一、什么是浅拷贝和深拷贝
- 浅拷贝:
- 浅拷贝就是指对象地址的复制,并没有开辟新的栈,即:复制的结果是原对象与浅拷贝的对象指向同一个地址。
- 修改其中一个对象的属性,另外一个对象的这个属性也会跟随着改变
- 深拷贝:
- 深拷贝就是值开辟了新的栈,原对象与深拷贝的对象不是指向同一个地址。
- 修改一个对象的属性,另外一个对象并不会随之发生改变
二、浅拷贝的实现
2.1 直接赋值
let oObj = {
name: 'jm',
age: 18,
hobbies: ['打篮球', '游泳']
}
// 直接赋值,浅拷贝
let copy = oObj
copy.hobbies.push('跳舞')
console.log(oObj)
console.log(copy)
2.2 Object.assign()
Object.assign()
:实现的只是浅拷贝
let oObj = {
name: 'jm',
age: 18,
hobbies: ['打篮球', '游泳']
}
// Object.assign() 浅拷贝
let copy = Object.assign({}, oObj)
copy.hobbies.push('跳舞')
console.log(oObj)
console.log(copy)
三、深拷贝的实现
3.1 递归实现
let oObj = {
name: 'jm',
age: 18,
hobbies: ['打篮球', '游泳'],
friend: {
name: 'lili',
age: 18
}
}
let cObj = {
workPlace: ['佛山', '广州']
}
// 测试是否已成功深拷贝
const result = deepCopy(oObj, cObj)
result.hobbies.push('学习')
console.log(result)
console.log(oObj)
/**
* 深拷贝(递归实现)- 将原对象复制到目标对象上
* @param originObj 原对象
* @param copyObj 目标对象
* @return {*|{}}
*/
function deepCopy (originObj, targetObj) {
let cObj = targetObj || {}
for (var i in originObj) {
const item = originObj[i]
// 引用类型
if (typeof item === 'object') {
// 对象
if (Object.prototype.toString.call(item) === '[object Object]') {
cObj[i] = {}
}
// 数组
else if (Object.prototype.toString.call(item) === '[object Array]') {
cObj[i] = []
}
// 递归
deepCopy(originObj[i], cObj[i])
}
// 基本类型
else {
cObj[i] = item
}
}
return cObj
}
3.1 通过JSON解析解决
let oObj = {
name: 'jm',
age: 18,
hobbies: ['打篮球', '游泳'],
friend: {
name: 'lili',
age: 18
}
}
// 通过JSON解析,也可以实现深拷贝
let copy = JSON.parse(JSON.stringify(oObj))
copy.hobbies.push('跳舞')
console.log(oObj)
console.log(copy)
- 但是,如果值中出现
undefined
,那么将会解析“失败”- demo
let oObj = {
name: 'jm',
age: 18,
hobbies: ['打篮球', '游泳'],
friend: undefined
}
// 通过JSON解析,也可以实现深拷贝
let copy = JSON.parse(JSON.stringify(oObj))
copy.hobbies.push('跳舞')
console.log(oObj)
console.log(copy)
- 从上图结果可知道,原对象中的
friend
设置为undefined
,经过一系列的JSON
解析后,你会发现深拷贝后的结果中friend
这个属性没了!
四、jQuery.extend()
4.1 源码
- 以下是jquery-3.2.1的源码
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[ 0 ] || {},
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
// Skip the boolean and the target
target = arguments[ i ] || {};
i++;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
target = {};
}
// Extend jQuery itself if only one argument is passed
if ( i === length ) {
target = this;
i--;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( ( options = arguments[ i ] ) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = Array.isArray( copy ) ) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && Array.isArray( src ) ? src : [];
} else {
clone = src && jQuery.isPlainObject( src ) ? src : {};
}
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};
4.2 只传入一个对象
- 只传入一个对象,本实例返回的是
window
对象,但也包含了fruits1
对象里有的属性和值- demo
var fruits1 = {
apple: 0,
banana: { weight: 52, price: 100 },
cherry: 97
};
const fruits = extend(fruits1)
console.log(fruits)
4.3 浅拷贝
let fruits1 = {
apple: 0,
banana: { weight: 52, price: 100 },
cherry: 97
};
let fruits2 = {
orange: {},
watermelon: {
weight: 100,
price: 10
}
}
// 浅拷贝
extend(fruits1, fruits2)
fruits1.orange.place = 'blue'
console.log(fruits1, fruits2)
4.4 深拷贝
let fruits1 = {
apple: 0,
banana: { weight: 52, price: 100 },
cherry: 97
};
let fruits2 = {
orange: {},
watermelon: {
weight: 100,
price: 10
}
}
// 深拷贝
extend(true, fruits1, fruits2)
fruits1.orange.place = 'blue'
console.log(fruits1, fruits2)
4.4 深拷贝 - 有相同属性
let fruits1 = {
apple: 0,
banana: { weight: 52, price: 100 },
cherry: 97
};
let fruits2 = {
apple: {
weight: 100,
price: 10
},
banana: {
weight: 80
}
}
// 深拷贝
extend(true, fruits1, fruits2)
console.log(fruits1)
console.log(fruits2)
4.6 分析
jQuery
的深拷贝和浅拷贝的封装的函数有以下几大特点(考虑十分全面):
- 开启一个变量:用来规定是深拷贝还是浅拷贝
- 在开始深拷贝或者浅拷贝前,都会做一大堆的判断,比如:判断这个是不是对象(非null),判断这个对象是不是数组等等
- 整个深拷贝主要用了递归的思想,实在妙!