对象的引用赋值
js中一旦遇到引用数据类型,就会开辟一块堆内存,将引用数据类型的值进行储存,并给这块堆内存分配一个16进制地址。
1 2 3 4
| const info = {name:"forward",age:24}; const obj = info; obj.age=23; console.log(info.age);
|
浅拷贝
- 浅拷贝:将第一层的数据数据完全拷贝过来,如果有引用类型,引用类型指向的是一个16进制地址,也会拷贝过来(即:拷贝的引用类型和原数据指向同一个内存地址)
- (对象)如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址
- (数组)如果数组元素(arr[index])是基本类型,就会拷贝一份,互不影响,而如果是对象或者数组,就会只拷贝对象和数组的引用,这样我们无论在新旧数组进行了修改,两者都会发生变化
- 即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址
- 常见的浅拷贝:
- Object.assign()
- concat()
- slice() 返回一个新的数组对象;原始数组不会被改变。
- …拓展运算符
- Lodash库
_.clone(value)
例如:
1 2 3 4 5 6 7
| const info = { name: "forward", age: 24 };
const obj = Object.assign({},info); console.log(obj); obj.age=23; console.log(info.age);
|
将上述代码进行简单修改,加一个对象参数;这个对象参数会开辟一块堆内存(16进制地址);浅拷贝会将第一层的数据数据完全拷贝过来,引用类型指向的是一个16进制地址,也会拷贝过来(即:拷贝的引用类型指向同一个内存地址);
1 2 3 4 5
| const info = { name: "forward", age: 24,friend:{name:"zhangsan",age:23} }; const obj = Object.assign({},info); obj.friend.name="lisi"; console.log(info.friend.name);
|
Lodash JS库
Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库;用_.clone(xxx)
实现浅拷贝;详见:Lodash 中文文档
1 2 3 4
| const objects = [{ 'a': 1 }, { 'b': 2 }]; const shallow = _.clone(objects); console.log(shallow[0] === objects[0]);
|
数组的浅拷贝
1 2 3 4 5 6 7
| const arr = ['old', 1, true, null, undefined]; const arr2 = arr.concat(); const arr3 = arr.slice(); const arr4 = [...arr]; arr2[0]="new"; console.log(arr); console.log(arr2);
|
深拷贝
- 深拷贝是指完全生成了一个新的对象,里面所有的东西都是新的,即使嵌套了对象,两者也相互分离,修改一个对象的属性,也不会影响另一个。
JSON.parse(JSON.string(xxx))
1 2 3 4
| const info = { name: "forward", age: 24,friend:{name:"zhangsan",age:23} }; const obj = JSON.parse(JSON.stringify(info)) obj.friend.name="lisi"; console.log(info.friend.name);
|
此方法缺点:JSON.stringify(..) 在对象中遇到 undefined 、 function 和 symbol 时会自动将其忽略, 在 数组中则会返回 null (以保证单元位置不变)。详见:JSON.stringify 深拷贝的弊端
1 2 3 4
| console.log(JSON.stringify(undefined)); console.log(JSON.stringify( function(){})); console.log(JSON.stringify( [1,undefined,function(){},4])); console.log(JSON.stringify({ undefined,a:2, b:function(){}}));
|
Lodash JS库
Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库;用_.cloneDeep(xxx)
实现深拷贝;详见:Lodash 中文文档
1 2 3 4 5
| const objects = [{ 'a': 1 }, { 'b': 2 }]; const deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]);
|
浅拷贝+递归
见下文手写浅(深)拷贝
structuredClone()
在 JS 中深拷贝一个对象,我们一般会使用 lodash 的 deepClone 等方法。现在一个新的原生函数可以完美胜任这个任务:
structuredClone();这个函数几乎完美适配所有类型,甚至包括: Error,Date,Blob 等等。更棒的是而且现在主流的浏览器都兼容这个函数;
- 在哪里可以使用structuredClone()?
1 2 3 4 5 6 7 8
| const obj = { bar: ["ES6", "NEW API", "DeepClone"] } const obj2 = structuredClone(obj); obj.bar.push("add"); obj2.bar.pop(); console.log(obj.bar); console.log(obj2.bar);
|
手写浅(深)拷贝
object.hasOwnProperty(propertyName)
- 用来检测属性是否为对象的私有属性,如果是,返回true,否则返回false; 参数propertyName指要检测的属性名;
- 所以说不会检测原型链上的公有属性;
- 说简单点,它能帮你指向你当前循环的对象,而过滤掉原型链上其它对象,因为在工作中我们很难保证其他人是否会修改原型链,这样做会更为保险;
hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(处理对象属性而不遍历原型链);
Object.hasOwn()
如果指定的对象自身有指定的属性,则静态方法 Object.hasOwn()
返回 true
。如果属性是继承的或者不存在,该方法返回 false
;备注: Object.hasOwn()
旨在取代 Object.hasOwnProperty()
。
object.hasOwnProperty(propertyName)
1 2 3 4 5 6 7 8 9 10 11 12
| const object1 = {}; object1.property1 = 42;
console.log(object1.hasOwnProperty('property1'));
console.log(object1.hasOwnProperty('toString'));
console.log(object1.hasOwnProperty('hasOwnProperty'));
|
Object.hasOwn()
1 2 3 4 5 6 7 8 9 10 11 12
| const object1 = { prop: 'exists' };
console.log(Object.hasOwn(object1, 'prop'));
console.log(Object.hasOwn(object1, 'toString'));
console.log(Object.hasOwn(object1, 'undeclaredPropertyValue'));
|
浅拷贝
遍历对象,然后把属性和属性值都放在一个新的对象不就好了~
1 2 3 4 5 6 7 8 9 10 11 12 13
| var shallowCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj; }
|
深拷贝
那如何实现一个深拷贝呢?说起来也好简单,我们在拷贝的时候判断一下属性值的类型,如果是对象,我们递归调用深拷贝函数不就好了~
1 2 3 4 5 6 7 8 9 10
| var deepCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj; }
|
总结
|
和原数据是否指向同一对象 |
第一层数据为基本数据类型 |
原数据中包含子对象 |
赋值 |
是 |
改变会使原数据一同改变 |
改变会使原数据一同改变 |
浅拷贝 |
否 |
改变不会使原数据一同改变 |
改变会使原数据一同改变 |
深拷贝 |
否 |
改变不会使原数据一同改变 |
改变不会使原数据一同改变 |
参考文献
JS中对象深拷贝:structuredClone()
Object.assign()的使用
JavaScript专题之深浅拷贝
详细解析赋值、浅拷贝和深拷贝的区别
hasOwnProperty() 方法详解