【问题标题】:Object is empty when a property to the prototype of function is added via Object.setPrototypeOf()当通过 Object.setPrototypeOf() 向函数原型添加属性时,对象为空
【发布时间】:2020-05-09 17:57:18
【问题描述】:

让我们考虑下面的代码,

let f = function () {
   this.a = 1;
   this.b = 2;
}

let o = new f(); 
f.prototype.c = 3;
console.log(Object.getPrototypeOf(o)); 

打印

[对象对象] { c: 3 }

但如果我使用 setPrototypeOf 而不是 f.prototype.c,则对象为空。

let f = function () {
   this.a = 1;
   this.b = 2;
}

let o = new f();   
Object.setPrototypeOf(f, {c: 3}); 
console.log(Object.getPrototypeOf(o));

打印

[对象对象] { ... }

但是如果我使用

let f = function () {
   this.a = 1;
   this.b = 2;
}

let o = new f(); 
Object.setPrototypeOf(f, {c: 3}); 
console.log(Object.getPrototypeOf(f)); 

打印

[对象对象] { c: 3 }

简而言之,问题是,当使用 Object.setPrototypeOf(o) 时,对象打印为空, 并且当使用 Object.setPrototypeOf(f) 时,对象会打印添加的属性。 在使用 f.prototype.c = 3 设置原型时,两个对象都可以访问它 原型和函数原型。

【问题讨论】:

  • 如果您有obj = {},然后是obj.foo = 1obj = {bar: 2},则差别不大。您要么修改现有对象,要么替换它。 o 保持对原始的引用。
  • 那么就这两种添加原型的方式(setPrototypeOf 和 .prototype)而言,有什么不同呢?
  • 我在答案中对此进行了扩展,但想象一下上面的代码有obj2 = obj。如果你这样做obj.foo = 1,你正在“改变”obj2,因为它实际上只是对同一对象的新引用。但是obj = {bar: 2} 将保持obj2 不变,因为现在objobj2 指向不同的对象。
  • 哇,我不知道 obj = {} 语法不会修改另一个对象,我知道 Object.assign() 和用于复制的扩展运算符,但从未读过这个保持对象不可变的方式,谢谢,现在让我再看看代码。我仍在尝试将其与这两种设置原型的方式联系起来。
  • 我遇到的另一件事是在执行 setPrototypeOf() 时,它替换了完整的原型属性,其中 .prototype 可以添加到它上面,在这里我可以将您关于引用和复制的解释联系起来

标签: javascript object prototype


【解决方案1】:

当你Object.setPrototypeOf(f, ... )时,链条实际上是3长:

o的原型是ff的原型是{c:3}

所以你的例子并不等同:

1) 在第一个示例中,您将属性c 直接添加到f 的实例将使用的原型中,因此o 的原型包含c,因为fo 的构造函数。

在最后两个示例中,您将c 添加到f 的原型中,因此原型f 是使用创建的。请记住,函数也只是对象,并且您正在设置用作构造函数的函数的原型。所以o 的原型包含f 的原型,其中包含c

2) 在第二个例子中,你得到PrototypeOf() o。 在第三个中,你得到PrototypeOf() f。因此只有第三个示例再次显示c

3) 如果您检查 chrome 中的元素,您会看到第二个示例的构造函数是 f,因为您询问了 o 的原型。

在第三个示例中,您将看到构造函数是 Object,因为您询问 f 的原型,它已设置为对象 {c},并从 o 跳过原型到 @987654348 @。

PS:我知道这可能是一个令人困惑的解释。

PPS:如果你想要继承,坚持 child.prototype = Object.create( parent.prototype ); child.constructor = child; 或 ES6 类 class child extends parent 可以使用对我个人而言不会造成混淆。

var first = function first() {
  this.value = 'first function';
};
// before .prototype.extra, first inherist from Function.
console.log( 'constructor of first before:', first.constructor );
first.prototype.extra = 'implemented with .prototype.extra';
// after .prototype.extra, first still inherits from Function, we did not change anything to first itself.
console.log( 'constructor of first after:', first.constructor );
// When first is used as a prototype, the instances will get "extra".
// Aka, everything that inherist from first.
var first_instance = new first();
console.log( 'extra on instance of first:', first_instance.extra );
// f itself does NOT have the extra property, only instances of first do.
console.log( 'extra on first itself:', first.extra );
console.log( '------' );
var second = function second() {
  this.value = 'second function';
};
// before setPrototypeOf, second inherist from Function, like above.
console.log( 'constructor of second before:', second.constructor );
Object.setPrototypeOf( second, { extra: 'implemented with .setPrototypeOf()' });
// after setPrototypeOf, second inherist from Object, we broke the default inheritance chain.
console.log( 'constructor of second after:', second.constructor );
// BY doing this, we effectively turned second into an object.
// It no longer is a function, so we cannot use function methods like .call() or .apply()
console.log( 'second is object, not function: function.apply', second.apply );
console.log( 'second is object, not function: object.hasOwnProperty', second.hasOwnProperty );
// When second is used as a prototype, the instances will not get "extra".
var second_instance = new second();
console.log( 'extra on instance of second:', second_instance.extra );
// second itself Does have the extra property, sine we assigned it on the prototype used by second. not when second is used as the prototype.
console.log( 'extra on second itself:', second.extra );

【讨论】:

  • 感谢您的解释,我会慢慢处理并回复您。 :)
  • 我对你的句子有点困惑,“在第一个例子中,你将属性 c 直接添加到 f 的实例将使用的原型中”我的假设是 setPrototypeOf(f) 和 f.prototype都会使用一些属性设置该函数的原型。
  • 是的,我将那行编辑了 5 次。 :/这是难以解释的部分。 fo 的原型。但是f 也有一个原型,因为它是一个对象。而 setPrototypeOf() 将设置f 的原型。所以创建f 的原型。然而,o 并不是从f 的原型创建的。 o 使用 f 作为它的原型。因此c 嵌套了两层。
  • 是的,但正如 setPrototypeOf() 的文档中所述,我们应该使用 Object.create() 或类来创建继承。我们使用这些东西的方式并不等同于彼此或继承。看看这个答案:stackoverflow.com/questions/45042777/… 以及它如何在原型 ( Dog.prototype ) 或实例 ( this ) 上使用 setPrototypeOf(),而不是在将成为构造函数 ( Dog ) 的函数上。这样做会删除到 Dog 的链接,这与我们通常在继承中想要的相反。
  • ff.prototype 的区别。所以在你的代码中,我们必须 setPrototypeOf() o.prototype 而不是 f 来获得正确的继承。因此我更喜欢o.prototype = Object.create( f.prototype ); 而不是Object.setPrototypeOf( o.prototype, f);。第一个让我更清楚地知道我们正在将一个原型复制到另一个原型,并且我们不太可能忘记在正确的位置添加 .prototype,因为 Object.setPrototypeOf( o, f); Object.setPrototypeOf( o.prototype, f);Object.setPrototypeOf( o, f.prototype); 都意味着什么不同。
猜你喜欢
  • 1970-01-01
  • 2017-03-31
  • 1970-01-01
  • 1970-01-01
  • 2016-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-19
相关资源
最近更新 更多