jiangjiu / blog-md

前端/健身 思考与笔记~
https://github.com/jiangjiu/blog-md/issues
86 stars 6 forks source link

Object.assign浅拷贝和delete小坑 #28

Open jiangjiu opened 7 years ago

jiangjiu commented 7 years ago

Object.assign浅拷贝和delete小坑

最近看You-Dont-Know-JS看到es6第二章时,书上写到

另一些人可能喜欢用覆盖赋值的方式来完成这个任务。你可能会被ES6的Object.assign(..)工具(见第六章)所吸引,来首先克隆defaults中的属性然后使用从config中克隆的属性覆盖它,像这样:

    config = Object.assign( {}, defaults, config );

这看起来好多了,是吧?但是这里有一个重大问题!Object.assign(..)是浅拷贝,这意味着当它拷贝defaults.options时,它仅仅拷贝这个对象的引用,而不是深度克隆这个对象的属性到一个config.options对象。Object.assign(..)需要在你的对象树的每一层中实施才能得到你期望的深度克隆。

立马试了一下,立马掉进了delete小坑中[捂脸] 过程如下:

var defaults = {
    options: {
        remove: true,
        enable: false,
        instance: {}
    },
    log: {
        warn: true,
        error: true
    }
};

var obj = Object.assign({},defaults);

delete defaults.options;
console.log(obj.options) // 一切都在

当时有点蒙蔽,为什么删掉了的属性没有影响obj.options? 不是浅拷贝嘛! 以为书写错了。。。看了眼MDN,瞬间打脸&&加深学习:

跟一般人的认为的不一样的是,delete 操作符不会直接释放内存。其释放内存是通过打破引用间接完成的,可查看内存管理了解更多。

delete 操作符会从某个对象上移除指定属性。成功删除的时候回返回 true,否则返回 false。

果然,问题出在delete。而且MDN上还说了几种特殊情况:

  1. 如果属性不存在,也会返回true,但是没有任何操作
  2. 删除后这个属性会彻底消失,但是。。。如果对象原型链有同名属性,则会继承原型上的属性。这个倒是很好理解,删掉的是实例属性。
  3. var声明的全局对象没办法从window上删除, 同时函数表达式/函数声明也没办法从全局上删除。
  4. let const声明的变量则是可以从window上删除的。
  5. Non-configurable properties cannot be removed. This includes properties of built-in objects like Math, Array, Object and properties that are created as non-configurable with methods like Object.defineProperty().

参考

MDN上关于delete操作符的讲解

就你不懂JS:es6与未来