START
- 一直在使用
Object.assign()
方法去拷贝对象,但是一直知其然不知其所以然。今天研究一下,写一个笔记,记录一下。 - 如果不想看过多的叙述,可跳到最后总结部分.
官方说明文档
基本概念
-
Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
这里解释一下,属性 可枚举和不可枚举,由属性的
enumerable
值控制,例如
var o = {}; o.a = 1; // 等同于: Object.defineProperty(o, "a", { value: 1, writable: true, configurable: true, enumerable: true // 为true,即可枚举 });
详情参考 MDN object.defineProperty
- 简单来说,用户定义的属性都是可枚举的,而内置对象不可枚举。
- 一种情况除外:当属性的原型是继承于其它对象原型时,这时用户定义的属性就是不可枚举的
语法
- 用法
Object.assign(target, ...sources)
- 参数
target
目标对象。
sources
源对象。
- 用法示例
var one = { a: 1, b: 2 };
var two = { c: 3, d: 4 ,e:'swq' };
var three = { e: 5, f: 6, j: 7 };
var end1 = {};
var end2 = {};
console.log("objext.assign方法的返回值:", Object.assign(end1, one));
// 解释上面的代码,拷贝one给end1,并返回end1。
// 此时end1和返回值 都是 { a: 1, b: 2 }
Object.assign(end2, two, three);
// 先拷贝two给end2 end2 => { c: 3, d: 4 ,e:'swq' }
// 拷贝three给 end2 end2 => { c: 3, d: 4, e: 5, f: 6, j: 7 }
// three,end2两个都有e这个属性,后者覆盖前者。
console.log(end2);
// 此时 end2 就为{ c: 3, d: 4, e: 5, f: 6, j: 7 }
输出:
objext.assign方法的返回值: { a: 1, b: 2 }
{ c: 3, d: 4, e: 5, f: 6, j: 7 }
如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。
用我自己的话说,就是 第一个参数是目标对象,后续的参数是源对象,会将源对象的数据拷贝到目标对象,如果目标对象和源对象有相同属性,则目标对象的属性值会被源对象的属性值覆盖(后面的覆盖前面的)。
-
深浅拷贝
官方解释: 假如源值是一个对象的引用,它仅仅会复制其引用值。
个人解释:
- 当源对象只有一层的时候,是深拷贝
- 当源对象有多层,简单说就是一个对象中有一个属性的属性值为对象(数组也是对象),那么只会拷贝对象的引用值,这就是浅拷贝了。
- 官网的例子,全部读完,基本就可以理解了。
const log = console.log; function test() { 'use strict'; let obj1 = { a: 0 , b: { c: 0}}; let obj2 = Object.assign({}, obj1); log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}} obj1.a = 1; log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}} log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}} obj2.a = 2; log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}} log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}} obj2.b.c = 3; log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}} log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}} // Deep Clone obj1 = { a: 0 , b: { c: 0}}; let obj3 = JSON.parse(JSON.stringify(obj1)); obj1.a = 4; obj1.b.c = 4; log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}} } test();
- 异常会打断后续拷贝任务
举个简单的例子,
object.assign()
,定义的时候就只能用于可枚举的属性,当运行到,不可枚举的属性时,会报错,但是在不可枚举的属性之前拷贝的数据,是拷贝了的。
官方示例
const target = Object.defineProperty({}, "foo", {
value: 1,
writable: false
}); // target 的 foo 属性是个只读属性。
Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4});
// TypeError: "foo" is read-only
// 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。
console.log(target.bar); // 2,说明第一个源对象拷贝成功了。
console.log(target.foo2); // 3,说明第二个源对象的第一个属性也拷贝成功了。
console.log(target.foo); // 1,只读属性不能被覆盖,所以第二个源对象的第二个属性拷贝失败了。
console.log(target.foo3); // undefined,异常之后 assign 方法就退出了,第三个属性是不会被拷贝到的。
console.log(target.baz); // undefined,第三个源对象更是不会被拷贝到的。
但是我本地运行,后续的console.log() 因为前面的报错,都没打印。
总结
-
一般都是用它
object.assign()
,深拷贝对象用法示例
const target = { a: 1, b: 2 }; const source = { b: 4, c: 5 }; // target:目标对象 // source:源对象(可以有多个) const returnedTarget = Object.assign(target, source); console.log(target); // expected output: Object { a: 1, b: 4, c: 5 } console.log(returnedTarget); // expected output: Object { a: 1, b: 4, c: 5 }
注意
- 想要深拷贝的话,对象只能有一层,层次大于一,请使用其他方法深拷贝。
- 只能拷贝,可枚举类型的属性。
- 如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。
END
- 以上是番茄自己,结合MDN文档去总结的笔记,或许有些地方描述不恰当,参考即可。
网友评论