资讯专栏INFORMATION COLUMN

对象的合并及拷贝

econi / 2438人阅读

摘要:方法用于对象的合并,将所有自身的非继承的可枚举属性的值从一个或多个源对象拷贝到目标对象。比如,如果对象的属性是函数或对象,该属性会被过滤掉,导致拷贝时会缺少属性。利用递归对每一层都重新创建对象并赋值从而实现深拷贝

Object.assign()

Object.assign() 方法用于对象的合并,将所有自身的(非继承的)可枚举属性的值从一个或多个源对象拷贝到目标对象。返回目标对象。目标对象自身也会改变。

Object.assign(target, ...sources)

target: 目标对象。

sources: 源对象。

Object.assign() 合并拷贝属性的限制

只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false)。

Object.assign({b: "c"},
  Object.defineProperty({}, "invisible", {
    enumerable: false,
    value: "hello"
  })
)
// { b: "c" }
参数类型 1. 只有一个参数

如果只有一个参数,Object.assign() 会直接返回该参数。

let obj = {a: 1};
Object.assign(obj) === obj // true

如果该参数不是对象,则会先转成对象,然后返回。

typeof Object.assign(2) // "object"

由于 undefinednull 无法转成对象,所以如果它们作为参数,就会报错。

Object.assign(undefined) // 报错
Object.assign(null)      // 报错
2. 对象 + 非对象

非对象参数都会转成对象,如果无法转成对象,就会跳过,不会报错。

如果非对象参数为 undefinednull ,就会跳过,不会报错,返回的依旧是目标对象参数。

let obj = {a: 1};
Object.assign(obj, undefined) === obj    // true
Object.assign(obj, null) === obj         // true

如果非对象参数为其他类型的值(即数值、字符串和布尔值),也不会报错。但是,除了字符串会以数组形式拷贝入目标对象,其他值都不会产生效果。这是因为只有字符串的包装对象,会产生可枚举属性。

let v1 = "abc";
let v2 = true;
let v3 = 10;

let obj = Object.assign({}, v1, v2, v3);
console.log(obj)  // { "0": "a", "1": "b", "2": "c" }
3. 目标对象 + 源对象...
(1) 属性值为 nullundefined 的属性会正常合并

Object.assign() 不会跳过那些属性值为 nullundefined 的源对象。

var o1 = { a: null, b: 1};
var o2 = { c: undefined };
    
var obj = Object.assign({}, o1, o2);
obj   // {a: null, b: 1, c: undefined}
(2) 同名属性的替换

如果目标对象与源对象中的属性具有相同的键,则目标对象属性将被源中的属性覆盖。后来的源的属性将类似地覆盖早先的属性。

var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
obj    // { a: 1, b: 2, c: 3 }
(3) 浅拷贝

Object.assign() 方法实行的是浅拷贝,而不是深拷贝。拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。

var obj1 = { a: 0 , b: { c: 0 } };
var obj2 = Object.assign({}, obj1);
obj2   // { a: 0, b: { c: 0 } };

obj2.b.c = 3;
obj1   // { a: 0, b: { c: 3 } };
obj2   // { a: 0, b: { c: 3 } };
(4) 数组的处理

Object.assign() 可以用来处理数组,但是会把数组视为键值为数组下标的对象来合并,然而最终的返回形式也是数组。

Object.assign([1, 2, 3], [4, 5])  // [4, 5, 3]

Object.assign({0:1,1:2,2:3},{0:4,1:5})  // {0: 4, 1: 5, 2: 3}
(5) 存取器属性的处理

Object.assign() 如果遇到存取器定义的属性,会只拷贝值。

var obj = {
  foo: 1,
  get bar() { return 2; }
};

var copy = Object.assign({}, obj); 
copy  // { foo: 1, bar: 2 }

因此必须使用 Object.getOwnPropertyDescriptors() 方法配合 Object.defineProperties() 方法,就可以实现正确拷贝。但仅限于可拷贝 gettersetter ,对于属性的引用类型还是属于浅拷贝。

var obj = {
  foo: { a : 0 },
  get bar() { return 2; }
};
var target = Object.defineProperties({},
  Object.getOwnPropertyDescriptors(obj)
);
Object.getOwnPropertyDescriptor(target, "bar")
// { get : ƒ bar(),
   set : undefined,
   enumerable : true, 
   configurable : true }
   
obj.foo.a = 6
target.foo.a   // 6
常见用途 1. 为对象添加属性
class Point {
  constructor(x, y) {
    Object.assign(this, {x, y});
  }
}

上面方法通过 Object.assign() 方法,将 x 属性和 y 属性添加到 Point 类的对象实例。

2. 为对象添加方法
Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2) { ··· },
  anotherMethod() { ··· }
});

// 等同于下面的写法
SomeClass.prototype.someMethod = function (arg1, arg2) { ··· };
SomeClass.prototype.anotherMethod = function () { ··· };
3. 浅克隆对象
let obj = {a:5};
function clone(origin) {
  return Object.assign({}, origin);
}
let aaa = clone(obj);  // {a:5}

不过,采用这种方法克隆,只能克隆原始对象自身的值,不能克隆它继承的值。如果想要保持继承链,可以采用下面的代码。

function clone(origin) {
  let originProto = Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);
}
4. 合并多个对象
let merge = (target, ...sources) => Object.assign(target, ...sources);

如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。

let merge = (...sources) => Object.assign({}, ...sources);
5. 为属性指定默认值
const DEFAULTS = {
  a: 0,
  b: "ccc"
};

function copy(options) {
  options = Object.assign({}, DEFAULTS, options);
  // ...
}

注意,由于存在浅拷贝的问题,DEFAULTS对象和options对象的所有属性的值,最好都是简单类型,不要指向另一个对象。否则,DEFAULTS对象的该属性很可能不起作用。

参考链接:Object.assign()

深拷贝 1. JSON.parse(JSON.stringify(obj))
var obj1 = { a: 0 , b: { c: 0}};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj1.b.c = 4;
obj2    // { a: 0, b: { c: 0}}

但由于 JSON 的局限性,该方法也不是万能的。比如,如果对象的属性是 undefined、函数、symbolXML 对象,该属性会被 JSON.stringify() 过滤掉,导致拷贝时会缺少属性。

let obj = {
  name:"dora",
  sayHello:function(){ console.log("Hello World"); }
}

let cloneObj = JSON.parse(JSON.stringify(obj));
console.log(cloneObj); // {name: "dora"}
2. 利用递归对每一层都重新创建对象并赋值从而实现深拷贝
function deepClone(source){
  let targetObj = source.constructor === Array ? [] : {}; 
  for(let keys in source){
    if(source.hasOwnProperty(keys)){
      if(source[keys] && typeof source[keys] === "object"){
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      }else{
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}

let obj = { a: { b: 1, c: 2 }, sayHello: function(){ console.log("Hello World"); } }
let cloneObj = deepClone(obj);

obj.a.b = 4
obj       // {a:{b: 4, c: 2},sayHello:ƒ ()}
cloneObj  // {a:{b: 1, c: 2},sayHello:ƒ ()}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101125.html

相关文章

  • Python实用技法第33篇:字符串连接合并

    摘要:上一篇文章实用技法第篇对齐文本字符串下一篇文章问题我们想将许多小字符串合并成一个大的字符串。示例如下对于不必要的字符串连接操作也要引起重视。有时候在技术上并非必需的时候,程序员们也会忘乎所以地使用字符串连接操作。 上一篇文章: Python实用技法第32篇:对齐文本字符串下一篇文章:  问题 我们想将许多小字符串合并成一个大的字符串。  解决方案 如果想要合并的字符串在一个序列或可迭代...

    JayChen 评论0 收藏0
  • js浅拷贝拷贝几种方法

    摘要:但这两种拷贝有一个问题就是只能赋值一层,假设我们有如下数据结构臧三小明小芳我们会发现打印出的结果如下上图可看出的结果均变了,这并不是我们想要的结果,所以我们要用到深拷贝。 一个项目开发中经常会用到需要复制一个对象或者数组,但是却不能改变原始对象,所以就要用到拷贝,拷贝又分深拷贝和浅拷贝,今天列举一下几种拷贝形式。 一、浅拷贝 (1) Object.assign() Object.ass...

    wing324 评论0 收藏0
  • Object 属性与方法全接触

    摘要:一切对象都是的实例,一切函数都是的实例,是构造函数,函数是的实例,是对象,对象是的实例,可以说与是一对密不可分的兄弟,让我们一起解开与的神秘面纱,本章主要了解相关知识,下章再来看构造函数可以创建一个对象包装器中所有对象都来自所有对象从继承方 一切对象都是 Object 的实例,一切函数都是 Function 的实例,Object 是构造函数,函数是 Function 的实例,Funct...

    YJNldm 评论0 收藏0
  • 浅探js深拷贝和浅拷贝

    摘要:接下来就让我们更细致的探究中的深浅拷贝。总结以上对深拷贝和浅拷贝做了简单的介绍,在深拷贝的实现上也只介绍了最简单的实现形式,并未考虑复杂情况以及相应优化,想要对深拷贝有更深入的了解,需要大家花时间去深入研究,或者可以关注我后续文章的动态。 对象和数组的拷贝对我来说一直都是一个比较模糊的概念,一直有点一知半解,但是在实际工作中又偶尔会涉及到,有时候还会一不小心掉坑里,不知道大家有没有同样...

    habren 评论0 收藏0
  • javascript拷贝VS浅拷贝

    摘要:深拷贝浅拷贝本文主要对深拷贝浅拷贝的解释及实现做一下简单记录。之所以会有深拷贝与浅拷贝之分,是因为不同数据类型的数据在内存中的存储区域不一样。但注意,只能做一层属性的浅拷贝。 深拷贝VS浅拷贝 本文主要对深拷贝&浅拷贝的解释及实现做一下简单记录。原文链接,欢迎star。 之所以会有深拷贝与浅拷贝之分,是因为不同数据类型的数据在内存中的存储区域不一样。 堆和栈是计算机中划分出来用来存储的...

    Nekron 评论0 收藏0

发表评论

0条评论

econi

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<