Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

如何定义「复制对象」?

  看到这个标题是不是又想吐槽博主自己都没有对象为何又来瞎扯复制对象这种事情了?然而这确实是一个值得扯的问题。虽然以前写过一篇「对象的可复制性」但感觉还是坑。现在有了 Object.assign 就把「复制对象」这件事做了个标准定义,我们应该遵循规范。
  当初我并没有把不可枚举属性从复制的概念中排除,只是觉得原型链不属于该复制的部分而已,其实还是一直对对象复制的概念耿耿于怀。后来看了 Object.assign 的定义才了然。
  通常,我们会不分青红皂白地使用 for in 来复制对象,比如这样 <script>
var A = function() {
  this.x = 1;
  Object.defineProperty(this, 'z', { value: 3 });
};
A.prototype.y = 2;

var a = new A();

var b = {};
for(var i in a) b[i] = a[i];

document.write(JSON.stringify(b)); // {"x":1,"y":2}
</script>
  这么复制会有一些问题。首先是 for in 会访问原型链,这样就会复制到一些本不属于这个对象的属性。然后就是赋值运算符仅仅是在对象上执行 [[Get]] 和 [[Set]] 抽象操作,并不是复制属性。也就是说,如果是一个访问器属性就会执行后得到一个值。但真正的复制应该是把访问器也一起复制过去才对。Object.assign 就是解决了这些问题的一个操作。
<script>
var A = function() {
  this.x = 1;
  Object.defineProperty(this, 'z', { value: 3 });
};
A.prototype.y = 2;

var a = new A();

var b = Object.assign({}, a);

document.write(JSON.stringify(b)); // {"x":1}
</script>
  以上就是「复制对象」的基本概念。但是这个「对象」是狭义的(并不要求继承于 Object)。值得注意的是 Object.assign 不会复制对象的 [[Prototype]] 内部属性以及其它内部属性,所以如果对一个数组调用 Object.assign 也只是把数组上的可枚举属性复制过来而已。
  但实际上用于存储的基本对象也分为很多类型(内部类型可以由 @@toStringTag 来区分),因此在复制数组时应该让目标对象也是一个数组才可以正确复制,比如:
<script>
var a = Object.assign({}, [1, 2, 3]);
var b = Object.assign([], [1, 2, 3]);
document.write(JSON.stringify(a)); // {"0":1,"1":2,"2":3}
document.write(JSON.stringify(b)); // [1,2,3]
</script>
  如果还想纠结其它对象的复制,比如、Number、RegExp、Date,之类的东西那就自己折腾去吧。对这些对象的复制操作根本就没有定义。即使我们把对象的 @@toStringTag 搞对也没意义,因为规范没有要求复制它们的内部属性,如 [[NumberData]]、[[RegExpMatcher]] 之类的,这后面都属于未定义行为。于是我就不想继续扯蛋了,再扯下去还不如好好找对象。
网名:
3.80.55.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^