How to clone object in JavaScript?

An object is an entity which has properties and types. It is a complicated datatype where it can store various data type.

const employee = {
  name:'Anna',
  id:101,
  age:35,
  salary:2000
};

In JavaScript, objects are reference values. To clone objects we cannot use assignment operator (=). Using assignment operator (=)  we are creating only an alias for existing object.

What is Shallow copy?

Shallow copy or clone copies only the actual object. Nested objects are not copied. Since mutable and stored as reference, when assigning object to another variable, we are assigning objects memory address to variable. It is a one-level deep copy.

Using Spread operator:
const employee = {
  name:'Anna',
  id:101,
  age:35,
  salary:2000
};
const empclone = {
  ...employee  // clone object using spread 
};
//changing value in employee
employee.salary=3000;
console.log(empclone);
console.log(employee);
Output:

{name:’Anna’, id:101, age:35, salary:2000}

{name: ‘Anna’, id: 101, age: 35, salary: 3000 }

Here we have created a shallow copy of object.Any change in original object is not reflected in copied object. But if object consists of nested child objects, any change in child object reflects in copied object.

Example:

const data = { name: “Anna”, age: 26, hobbies: [“Jogging”, “Tennis”, “Gym”] }

const dataCopy = { …data }

data.hobbies[0] = “Swimming”

console.log(data.hobbies)

console.log(dataCopy.hobbies)

Output:

[ ‘Swimming’, ‘Tennis’, ‘Gym’ ]

[ ‘Swimming’, ‘Tennis’, ‘Gym’ ]

Here hobbies is nested object and changing its value gets reflected in copy object.

Using Object.assign():

It is an alternative to spread operator. We can copy values and properties from one or more source object to a new object.

Syntax:
const clone = Object.assign({}, object);
Example:
const employee = {
  name:'Anna',
  id:101,
  age:35,
  salary:2000
};
const empclone = Object.assign({}, employee); // copy using Object.assign()
console.log(empclone);
Output:

{name: ‘Anna’, id: 101, age: 35, salary: 2000 }

Here the second argument employee is copied to first argument {} of assign method.

This is also shallow copy like spread operator.

Deep Copy:

In JavaScript deep copy allows us to create a completely independent copy of original object. It has the same properties but does not share the same references as original object. Any change to one object will not reflect in another object. Deep copy is performed using the methods JSON.parse() and JSON.stringify().

Using JSON.parse():
Syntax:
const clone= JSON.parse(JSON.stringify(Objname))
Example:
const employee = {
  name:'Anna',
  id:101,
  age:35,
  salary:2000
};
const empclone = JSON.parse(JSON.stringify(employee));
console.log(empclone);
Output:

{ name: ‘Anna’, id: 101, age: 35, salary: 2000 }

Example:
// Deep Clone
obj = {
  a: 1,
  b: {
    c: 1
  }
};
let cloneObj = JSON.parse(JSON.stringify(obj));
obj.a = 2;
obj.b.c = 2;
console.log(JSON.stringify(obj)); // { a: 2, b: { c: 2}}
console.log(JSON.stringify(cloneObj)); // { a: 0, b: { c: 0}}
cloneObj.a = 4;
cloneObj.b.c = 4;
console.log(JSON.stringify(obj)); // { a: 2, b: { c: 2}}
console.log(JSON.stringify(cloneObj)); // { a: 4, b: { c: 4}}
Output:

{“a”:2,”b”:{“c”:2}}

{“a”:1,”b”:{“c”:1}}

{“a”:2,”b”:{“c”:2}}

{“a”:4,”b”:{“c”:4}}

As you can see from above example that copied object is independent but it can contain incorrect values in some cases. Another drawback is it works well only with primitive datatypes like numbers, strings and Boolean.

To overcome this we have structuredClone() function.

Using structuredClone():

It creates a deep clone of an object. It overcomes many short comings of JSON.parse().

Clone inifitely nested objects and arrays.

Copy circular references.

Clone a many data types like map, set, date, RegEx, etc.,

Transfer any transferable object.

Syntax:
structuredClone(value)
structuredClone(value, options)

value – Object to clone.

Options (optional)- object with properties as follows:

transfer- An array of transferable objects that will be moved rather than cloned to the returned object.

Return value- returns a deep copy of an object.

Supported platforms:
  • Chrome 98
  • Safari 137
  • Firefox 94
  • Node.js 17.0
  • Deno 1.14

On platforms that don’t support we can use polyfills.

Example:
const employee = {
  name:'Anna',
  id:101,
  age:35,
  salary:2000
};
const empclone = structuredClone(employee);
console.log(empclone);
Output:

{name: ‘Anna’, id: 101, age: 35, salary: 2000}

Points to note:
To perform shallow copy:
  • The spread operator
  • The Object.assign() method.
To perform deep copy:
  • The JSON parsing approach
  • The structuredClone() method.

Arrow function in JavaScript

Arrow functions expression is an alternative to traditional function expression. They are similar to lambda functions in python. These functions are often used in passing a function as a parameter to higher order functions. It is one of the features introduced in ES6.

The function ,

// function expression
let x = function(x, y) {
   return x * y;
}

Can be written as

// using arrow functions
let x = (x, y) => x * y;
Syntax:
let myFunction = (arg1, arg2, ...argN) => {
    statement(s)
}

myFunction – name of function

arg1,arg2,…argN – arguments of function

statement(s) – function body

Example:

The below example illustrates arrow functions with

  • No arguments
  • Single argument
  • Function expression
  • Function body
No arguments:
// with no argument
let noarg = () => console.log('Abaython');
noarg(); // Abaython
One argument:
// with one argument

let onearg = x => console.log(x);

onearg('Abaython'); // Abaython
As Expression:
//as expression
let num = 50;
let func = (num > 20) ?
  () => console.log('Greater than 20') :
  () => console.log('Less than 20');
func(); // Greater than 20
Function Body:
let sum = (a, b) => {

    let result = a + b;

    return result;

}

let result1 = sum(10,20);

console.log(result1); // 30
Output:

Abaython

Abaython

Greater than 20

30

Arrow functions and this keyword:

In a regular function this keyword refers to the function where it is called. But arrow functions does not have this and it refers to its parents scope whenever called.

function Employee() {
    this.name = 'Anna',
    this.age = 25,
    this.sayName = function () {

        console.log(this.name);
        let Func = () => {
            console.log(this.name);
        }

        Func();
    }
}
const x = new Employee();
x.sayName();
Output:

Anna

Anna

Func() is defined within arrow. Inside the arrow this keyword refers to parents scope and returns this.name as Anna.

Argument bindings:

Arrow functions don’t have argument bindings.

let x = () => {
    console.log(arguments);
}
x(10, 20); // error
Output:

Reference error

To solve this use spread syntax.

let x = (...n) => {
    console.log(n);
}
x(10, 20); // [10, 20]
Output:

[10, 20]

Limitations:
  • Arrow functions does not have bindings to this, arguments or super and should be used as methods.
  • Calling arrow functions with new throws type error. They cannot be used as constructor.
  • They cannot use yield within their body and cannot be created as generator function.
  •  You should not use arrow functions to create methods inside objects.
Key differences with Regular function:
  • No prototype object for arrow functions
  • Duplicate-named parameters are not allowed.
  • Does not have argument object.
When to use them?

Arrow functions can be used when we need to bind this to the context and not to the function itself. It is a clear and concise way to write callbacks without exposing the scope that should be hidden.

In the coming article will try to ellaborate the difference with regular functions.