JavaScript shallow clone and deep clone

JS shallow clone and deep clone

1 Related knowledge points

  1. Shallow cloning is to copy a reference in the stack memory and assign it to a new variable. In essence, the two points to the same address in the heap memory, and the content is the same. One of them changes and the other content changes.
  2. Deep cloning is to create a new empty object, open up a memory, and then copy all the data in the original object to completely cut off the connection between the two objects.
  3. Difference : The biggest difference between shallow cloning and deep cloning is the treatment of reference values, that is, after shallow cloning, you change and I change it , and after deep cloning, you change and I don't change it . (PS: The original value is treated the same)
  4. Original value (stack data stack): Number, Boolean (false/true), String, undefined, null
  5. Reference value (heap data heap): Array, Object, function ··· Date, RegExp

2 Shallow clone

In shallow cloning, the clone of the original value is okay, just the copy of the value, there will be no problems with you or me . But the cloning of the reference value will cause the problem that you change and I also change it, because the shallow clone is an address, that is, it points to the same space.

2.1 Shallow clone function

function clone(origin, target) {
    
    
    var target = target || {
    
    }; 
    //容错,即防止用户不传递目标参数。若用户传递了参数就用,若没传则拿一个空对象当目标
    for (var prop in origin) {
    
    
        target[prop] = origin[prop];
    }
    return target;
}

2.2 Application examples

function clone(origin, target) {
    
    
    var target = target || {
    
    }; 
    for (var prop in origin) {
    
    
        target[prop] = origin[prop];
    }
    return target;
}

var obj = {
    
    
    name: 'abc',
    age: '18',
    sex: 'male',
    card: ['a', 'b', 'c'],
    book: {
    
    
        name: 'ccc',
        sbook: {
    
    
            name: 'aaa'
        }
    }
};
var newobj = {
    
    };

clone(obj, newobj);

The running code is as follows:
Shallow cloning

3 Deep clone

After deep cloning, the problem of cloning the reference value will be the same as the original value. I will change it and you will not change it , because although the deep cloning is the same thing, it points to a different space. That is, after deep cloning, the values ​​are independent and do not affect each other.

3.1 Deep cloning step analysis

The objects that need to be deep cloned are as follows:

var obj = {
    
    
    name: 'abc', // 原始值
    age: '18', // 原始值
    sex: 'male',// 原始值
    card: ['a', 'b', 'c'], // 引用值
    book: {
    
      // 引用值
        name: 'ccc', // 原始值
        sbook: {
    
     // 引用值
            name: 'aaa'// 原始值
        }
    }
};

var obj1 = {
    
    };

(1) First, it is necessary to traverse the object to be cloned.
Method:for (var prop in origin){···}

for (var prop in origin) {
    
    
	···
}

(2) Determine in turn whether it is the original value.
Method:, typeof()if it is the original value, copy it directly; if it is the reference value (the value returned by typeof(···) is object), then perform the recursive operation. It should be noted that yes typeof(null) == 'object', so we have to rule out this situation.

if (origin[prop] !== "null" && typeof(origin[prop]) == 'object') {
    
    
	···
	// 递归
} else {
    
    
    target[prop] = origin[prop];
}

(3) determine an array or an object
method: toString(recommended), constructor, instanceof(the latter two involve minor problems his son domain, although probably not encountered large)

var toStr = Object.prototype.toString,
    arrStr = "[object Array]";
if (toStr.call(origin[prop]) == arrStr) {
    
    
	··· // 数组
} else {
    
    
	··· // 对象
}

(4) establishing a respective array or object
methods: create a new namesake empty array / object and the original object in an array or an object as a new original object, wherein the data is again copied to the target object of the same name empty array / Object inside. That is, recursively starts copying the data in the array or object, and recursively executes step (1). After the recursion is completed, the next data is cloned in turn.

var toStr = Object.prototype.toString,
    arrStr = "[object Array]";
if (toStr.call(origin[prop]) == arrStr) {
    
    
	target[prop] = [];
} else {
    
    
	target[prop] = {
    
    };
}
newobj = {
    
    
    name: 'abc',
    age: '18',
    sex: 'male',
    card: [] 
    // 建立一个新的同名空数组,并把obj的card数据当成一个原始对象,再次拷贝到obj1的card里面
    // 即 递归开始拷贝数组或对象里面的数据,递归执行第(1)步
    // 执行完数组card拷贝之后,开始同理拷贝下一个对象book···
}

3.2 Deep clone function

function deepClone(origin, target) {
    
    
    var target = target || {
    
    },
        toStr = Object.prototype.toString,
        arrStr = "[object Array]";
    for (var prop in origin) {
    
    
        if (origin.hasOwnProperty(prop)) {
    
    
            if (origin[prop] !== "null" && typeof(origin[prop]) == 'object') {
    
    
                if (toStr.call(origin[prop]) == arrStr) {
    
    
                    target[prop] = [];
                } else {
    
    
                    target[prop] = {
    
    };
                }
                deepClone(origin[prop], target[prop]);

            } else {
    
    
                target[prop] = origin[prop];
            }
        }
    }
    return target;
}

The simplified code using the ternary operator is as follows:

// 使用三目运算符简化后
function deepClone(origin, target) {
    
    
    var target = (target || {
    
    }),
        toStr = Object.prototype.toString,
        arrStr = "[object Array]";
    for (var prop in origin) {
    
    
        if (origin.hasOwnProperty(prop)) {
    
    
            if (origin[prop] !== "null" && typeof (origin[prop]) == 'object') {
    
    
                target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {
    
    };
                deepClone(origin[prop], target[prop]);
            } else {
    
    
                target[prop] = origin[prop];
            }
        }
    }
    return target;
}

3.3 Application examples

function deepClone(origin, target) {
    
    
    var target = (target || {
    
    }),
        toStr = Object.prototype.toString,
        arrStr = "[object Array]";
    for (var prop in origin) {
    
    
        if (origin.hasOwnProperty(prop)) {
    
    
            if (origin[prop] !== "null" && typeof (origin[prop]) == 'object') {
    
    
                target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {
    
    };
                deepClone(origin[prop], target[prop]);
            } else {
    
    
                target[prop] = origin[prop];
            }
        }
    }
    return target;
}

var obj = {
    
    
    name: 'abc',
    age: '18',
    sex: 'male',
    card: ['a', 'b', 'c'],
    book: {
    
    
        name: 'ccc',
        sbook: {
    
    
            name: 'aaa'
        }
    }
};
var newobj = {
    
    };

deepClone(obj, newobj);

The running code is as follows:
Deep clone

3.4 hasOwnProperty

The hasOwnProperty() method returns a Boolean value indicating whether the object has the specified property (that is, whether it has the specified key).

  1. grammar:obj.hasOwnProperty(prop)
  2. Parameters: The name of the property to be detected in string form, or Symbol.
  3. Return value: Boolean value used to determine whether an object contains the specified attribute.
  4. Description: All inherited Objectobjects will inherit hasOwnPropertymethods. This method can be used to detect whether an object has specific own properties; inunlike operators, this method ignores those properties inherited from the prototype chain.
  5. Usage:
    a. Use hasOwnPropertymethod to determine whether the attribute exists
    b. Distinguish self attribute and inherited attribute
    c. Traverse all self attributes of an object
    d. Use hasOwnPropertyas attribute name
  6. For specific knowledge points, please refer to Object.prototype.hasOwnProperty()

If the prototype property is written in the object, but you don't want it to be displayed during traversal, you can use 对象名.hasOwnProperty(属性名)to determine whether it is its own property, if it is its own, the return value is true, if it is not its own prototype property, the return value is false. Examples are as follows:

var obj = {
    
    
	name: 'ABC',
	age: '18',
	sex: 'male',
	__proto__: {
    
    
		heart: 'happy'
	}
}
for (var prop in obj) {
    
    
	// 配套使用,起到一个过滤的作用,不把原型链上的数据弄出来
	if (obj.hasOwnProperty(prop)) {
    
    
		console.log(obj[prop]);// ABC 18 male
	}
}

Personal notes, everyone is welcome to exchange and discuss!

Guess you like

Origin blog.csdn.net/Yuki_yuhan/article/details/108297341