本文转自:http://blog.csdn.net/waiterwaiter/article/details/50267787

最近也一直会用javascript,然后中间使用的一些组件,如Echarts 会有非常复杂的配置文件,而大部分配置可能都是一样的,所以想着写一份通用配置,然后,其他地方需要使用的时候,用这份配置深拷贝一份配置,然后在上面继续改。就如下:

const defaultOpt = {
key1: xxx,
key2: {
dd: ee
},
.....
}; // deepCopy为某个实现深拷贝的方法
const opt1 = deepCopy(defaultOpt);
opt1.....
const opt2 = deepCopy(defaultOpt);
opt2.....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

深拷贝和浅拷贝

这里也涉及到一个深拷贝和浅拷贝的概念。javascript中存储对象都是存地址的,所以浅拷贝是都指向同一块内存区块,而深拷贝则是另外开辟了一块区域。下面实例也可以看出这一点:

// 浅拷贝
const a = {t: 1, p: 'gg'};
const b = a;
b.t = 3;
console.log(a); // {t: 3, p: 'gg'}
console.log(b); // {t: 3, p: 'gg'} //深拷贝
const c = {t: 1, p: 'gg'};
const d = deepCopy(c);
d.t = 3;
console.log(c); // {t: 1, p: 'gg'}
console.log(d); // {t: 3, p: 'gg'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

可以明显看出,浅拷贝在改变其中一个值时,会导致其他也一起改变,而深拷贝不会。

Object.assign()

我需要的是深拷贝的方法,然后发现原来es6 中有Object.assign() 这个方法,感觉可以拿来用了。 贴一下两个官方例子:

// Cloning an object
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
  • 1
  • 2
  • 3
  • 4
// Merging objects
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 }; var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, target object itself is changed.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

是不是很完美,又可以clone又可以merge。在我这种情况下,我觉得我的代码量又可以减少了,比如:

const defaultOpt = {
title: 'hello',
name: 'oo',
type: 'line'
};
// 原来可能需要这样
const opt1 = deepCopy(a);
opt1.title = 'opt1';
opt1.type = 'bar';
opt1.extra = 'extra'; // 额外增加配置
// 现在只要这样
const opt2 = Object.assign({}, a, {
title: 'opt2',
type: 'bar',
extra: 'extra'
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

不过,很快,问题出现了,那就是

merge和我想象的不一样

且看例子:

const defaultOpt = {
title: {
text: 'hello world',
subtext: 'It\'s my world.'
}
}; const opt = Object.assign({}, defaultOpt, {
title: {
subtext: 'Yes, your world.'
}
}); console.log(opt); // 预期结果
{
title: {
text: 'hello world',
subtext: 'Yes, your world.'
}
}
// 实际结果
{
title: {
subtext: 'Yes, your world.'
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

原本想的是它只会覆盖subtext ,然而其实它直接覆盖了整个title ,这个让我比较郁闷,相当于它只merge根属性,下面的就不做处理了。 代码只能重构成相对麻烦一点的:

const defaultOpt = {
title: {
text: 'hello world',
subtext: 'It\'s my world.'
}
}; const opt = Object.assign({}, defaultOpt);
opt.title.subtext = 'Yes, your world.'; console.log(opt);
// 结果正常
{
title: {
text: 'hello world',
subtext: 'Yes, your world.'
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这样用虽然麻烦一点,但是也还好,可以用了。不过。。。很快,又出现问题了,如下:

const defaultOpt = {
title: {
text: 'hello world',
subtext: 'It\'s my world.'
}
}; const opt1 = Object.assign({}, defaultOpt);
const opt2 = Object.assign({}, defaultOpt);
opt2.title.subtext = 'Yes, your world.'; console.log('opt1:');
console.log(opt1);
console.log('opt2:');
console.log(opt2); // 结果
opt1:
{
title: {
text: 'hello world',
subtext: 'Yes, your world.'
}
}
opt2:
{
title: {
text: 'hello world',
subtext: 'Yes, your world.'
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

上面结果发现两个配置变得一模一样,而其实我们并没有去更改opt1subtext ,只是改了opt2 的。 这说明一点:在title 这一层只是简单的浅拷贝 ,而没有继续深入的深拷贝。 这里不经让我怀疑这个接口到底是怎么实现的,它到底是不是和我所想的一样。 翻了一下官方文档,发现它写得一个Polyfill ,代码我加了点注释如下:

if (!Object.assign) {
// 定义assign方法
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) { // assign方法的第一个参数
'use strict';
// 第一个参数为空,则抛错
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
} var to = Object(target);
// 遍历剩余所有参数
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
// 参数为空,则跳过,继续下一个
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource); // 获取改参数的所有key值,并遍历
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
// 如果不为空且可枚举,则直接浅拷贝赋值
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

上面的代码可以直接说明它只对顶层属性做了赋值,完全没有继续做递归之类的把所有下一层的属性做深拷贝。

总结

Object.assign() 只是一级属性复制,比浅拷贝多深拷贝了一层而已。用的时候,还是要注意这个问题的。

发现一个可以简单实现深拷贝的方法,当然,有一定限制,如下:

const obj1 = JSON.parse(JSON.stringify(obj));
  • 1

思路就是将一个对象转成json字符串,然后又将字符串转回对象。

本文转自:http://blog.csdn.net/waiterwaiter/article/details/50267787

[转]javascript之Object.assign()痛点的更多相关文章

  1. javascript系列--Object.assign实现浅拷贝的原理以及实现

    一.前言 之前在前面一篇学习了赋值,浅拷贝和深拷贝.介绍了这三者的相关知识和区别. 传送门:https://www.mwcxs.top/page/592.html 本文会介绍浅拷贝Object.ass ...

  2. es6 javascript对象方法Object.assign()

    es6 javascript对象方法Object.assign() 2016年12月01日 16:42:34 阅读数:38583 1  基本用法 Object.assign方法用于对象的合并,将源对象 ...

  3. JavaScript 复制对象【Object.assign方法无法实现深复制】

    在JavaScript这门语言中,数据类型分为两大类:基本数据类型和复杂数据类型.基本数据类型包括Number.Boolean.String.Null.String.Symbol(ES6 新增),而复 ...

  4. javascript学习总结之Object.assign()方法详解

    最近再写ES6的文章时候发现自己对Object.assign()方法不太了解,之前也没有接触过所以就就查阅了相关的资料,为了自己以后肯能会用到以及对知识进行巩固,所以在这里记录下自己学习的点点滴滴,毕 ...

  5. [Javascript] Object.assign()

    Best Pratices for Object.assign: http://www.cnblogs.com/Answer1215/p/5096746.html Object.assign() ca ...

  6. es6 javascript对象方法Object.assign() 对象的合并复制等

    Object.assign方法用于对象的合并,将源对象( source )的所有可枚举属性,复制到目标对象( target ). 详细使用稳步到前辈: http://blog.csdn.net/qq_ ...

  7. ecma 2018, javascript spread syntax behaves like Object.assign

    as the subject. It is only supported in Chrome version 60+, so, first check the version, or just use ...

  8. [Javascript] Combine Objects with Object.assign and Lodash merge

    Learn how to use Object.assign to combine multiple objects together. This pattern is helpful when wr ...

  9. 微信不支持Object.assign

    微信不支持Object.assign,让我Vue怎么用QAQ... 解决方法: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Refe ...

随机推荐

  1. Redis连接异常

    1. 以指定配置文件启动Redis,否则配置的东西不生效 redis-cli shutdown 关闭 $ redis-server ./redis.conf 2.连接异常 redis.conf 配置文 ...

  2. 用注册表禁止windows添加新用户

    运行 regedt32.exe 打开你的注册表,里面有一个目录树:打开其中目录 HKEY_LOCAL_MACHINE再打开其中目录 SAM再打开其中目录 SAM再打开其中目录 Domains再打开其中 ...

  3. 初识WebAPI

    (一)Web API简介: 目前使用Web服务的三种主流的方式是:远程过程调用(RPC),面向服务架构(SOA)以及表征性状态转移(REST),其中REST模式的Web服务与复杂的SOA和RPC对比来 ...

  4. C#扩展一个现有的类

    做个记录,写个示例 using System; class Rubbish { public void Say() { Console.Write("Hello"); } } st ...

  5. 文本比较算法Ⅱ——Needleman/Wunsch算法的C++实现【求最长公共子串(不需要连续)】

    算法见:http://www.cnblogs.com/grenet/archive/2010/06/03/1750454.html 求最长公共子串(不需要连续) #include <stdio. ...

  6. Django + DRF + Elasticsearch 实现搜索功能

    django使用haystack来调用Elasticsearch搜索引擎  如何使用django来调用Elasticsearch实现全文的搜索 Haystack为Django提供了模块化的搜索.它的特 ...

  7. MySQL(增删改查补充)

    SQL语句数据行操作补充             create table tb12(                 id int auto_increment primary key,       ...

  8. SpringBoot入门之事件监听

    spring boot在启动过程中增加事件监听机制,为用户功能拓展提供极大的便利,sptingboot支持的事件类型有以下五种: ApplicationStartingEvent Applicatio ...

  9. Java并发工具类之同步屏障CyclicBarrier

    CyclicBarrier的字面意思是可以循环使用的Barrier,它要做的事情是让一个线程到达一个Barrier的时候被阻塞,直到最后一个线程到达Barrier,屏障才会放开,所有被Barrier拦 ...

  10. Part15 – 前端之jQuery

    本节内容 jQuery 一.jQuery jQuery是对DOM的封装 jQuery 中文在线文档:http://jquery.cuishifeng.cn/ 模块(Python) <--> ...