对象的原型属性是一个对象吗?

3
我读过很多关于对象的原型属性的精彩文章和讨论(比如这篇),但是有一个重要问题。当我们观察"prototype"属性的行为时,我们会发现它实际上是另一个对象。我们可以看到它有自己的proto和构造函数属性,就像一个对象一样。
var myobj= function(){};
myobj.prototype.constructor== myobj  --> true

以下是这些问题的答案:

  1. "prototype" 本身是一个对象吗?
  2. 是的,"prototype" 本身是一个对象。

  3. 原型和它所关联的对象(在这个例子中是 myobj)之间的关系是什么?
  4. 原型是一个对象,包含可共享的方法和属性。当你创建一个对象时,它的内部 [[Prototype]] 指向原型对象,从而暴露了类的共享行为和属性给实例。

  5. 为什么 myobj.prototype.__proto__ 默认情况下是 "Object{ }",但它的构造函数是 myobj
  6. myobj.prototype.__proto__ 是由默认的 "Object{ }" 构造的,因为 myobj 的原型对象是从 "Object{ }" 继承而来的。但是,myobj.prototype.constructor 指向的是 myobj 的构造函数。

  7. 我们知道:"__proto__ 是用于解析方法等的查找链中实际使用的对象,prototype 是用于创建具有 new 关键字的对象时建立 __proto__ 的对象。" 但我们可以看到,这不仅是 prototype 的作用。我认为:原型就像一个包含对象的所有共享方法和属性的容器! 然后,当使用 new 创建对象的实例时,它的内部 [[Prototype]] 指向原型,从而暴露了类的共享行为和属性给实例!这是真的吗?
  8. 是的,这是正确的。

最后: 似乎当我们实例化一个类时,该实例的构造函数被设置为该对象原型的构造函数。 这个结论是正确的。

var b= new myobj();
 b.constructor== myobj.prototype.constructor --> true
 b.constructor == myobj --> true
 myobj.prototype={};
 b.constructor == myobj  --> false
 b.constructor== Object --> true
 myobj.prototype.constructor== Object --> true

4
第四条似乎不是一个问题?实际上它阐述了解决方案。 - Bergi
1
为了减少潜在的混淆:只有函数具有prototype属性,而不是所有对象。myobj是一个函数。 - Felix Kling
@ Bergi,问题的其余部分是“原型(Prototype)就像一个包含对象所有共享方法和属性的容器对象!然后,当使用new创建对象实例时,它的内部[[Prototype]]指向原型,从而将类的共享行为和属性暴露给实例!这是真的吗?”但由于stackoverflow的文本编辑器限制,我无法完成此操作。 - Amir Jalilifard
1
是的,prototype只是一个对象。这就是原型继承的工作方式:对象具有对另一个对象(它们的原型)的内部引用。有什么疑惑吗?我真的无法理解问题的核心。顺便说一下,b.constructor == myobj //--> false是不正确的。即使在myobj.prototype = {};之后,它仍然是true。你是否对constructor感到困惑?那只是每个prototype对象都有的预定义属性。做 console.dir(myobj.prototype),你会看到。 - Felix Kling
2
如果它们之间没有关系,原型对象如何知道它与哪个函数相关联。原型不需要知道它分配给哪个函数,也不关心。一个原型对象可以被分配给无限数量的构造函数,它们之间没有一对一的关系。 - Jesse Kernaghan
显示剩余12条评论
2个回答

5
  1. 是的,原型对象就是一个对象。它会和函数一起隐式构建。
  2. 我不会称之为“父级”。他们唯一的关系是myobj函数确实有一个.prototype属性,其值为原型对象。
  3. 原型对象确实继承了Object.prototype。它确实拥有一个自己的.constructor属性,值为myobj函数。(它只是一个属性
  4. 是的,这是正确的。.__proto__是对内部的[[Prototype]]字段的非标准访问器。因此,它们经常被用作同义词。
  5. 不是的,在构建实例时,没有设置.constructor属性。所有发生的事情就是新实例从原型对象继承,并且原型对象具有该constructor属性。请注意,你的示例中的myobj.prototype = {};对先前构建的实例没有影响。

混淆的是:“原型”是一个独立的对象,函数与该对象相关。函数如何指向其“原型”,而myobj.proto== myobj.prototype。如果我们有两个不同的对象,它们如何相互关联?想象一下,我们有一个独立的对象,但不幸的是JS将此对象显示为函数的属性!那么,JS如何将函数和其原型相关联?我们知道唯一的方法是使用内部[Prototype]],但似乎是反过来的! - Amir Jalilifard
1
是的,它们是独立的对象,仅通过.prototype.constructor属性相互引用。它们之间没有继承关系myobj.__proto__ == myobj.prototypefalse。相反, myobj 是一个函数,继承自 Function.prototype,而 myobj.prototype 是一个普通的对象,继承自 Object.prototype - Bergi
如果它们是两个不同的对象,原型对象如何知道它与哪个函数相关联? - Amir Jalilifard
就像我之前所说的,它有一个名为.constructor的属性,其值是一个函数。然而,它并不需要知道这一点。 - Bergi

1
我知道已经有一个被接受的答案,但是似乎对于原型是什么、构造函数是什么以及它们如何相互作用存在一些混淆。
以这个对象为例:
var foo = {
  bar : 'hello',
  sayBar : function(){
    alert(this.bar);
  }
};

你可以直接使用它,就像你期望的那样。你可以重新赋值foo.bar并使用sayBar方法而不会有任何问题:
foo.bar = 'Goodbye';
foo.sayBar(); //alerts(Goodbye);

现在假设您需要一堆对象,它们都需要一个名为bar的属性,但具有不同的值。这时,您可以创建一个构造函数来创建唯一的实例:
var fooClass = function(newbar){
  this.bar = newbar;
};

可以这样调用:
var myFoo = new fooClass('baz');
console.log(myFoo.bar); //logs 'baz'

这可以在幕后分解成以下几个部分:
var fooClass = function(newbar){
  //**IF 'new' is declared ** create an instance object, for now it has no declared prototype other than Object's base prototype
  //That instance of the prototype is now scoped to 'this'

  //it sets the 'constructor' of the instance behind the scenes      
  //this.constuctor = [our constructor function]

  // now we can assign values to our instance and invoke it's methods

  this.bar = newbar;
  // our instance now has a key 'bar' that has the value of newbar


  // so our instance now looks like this:
  //
  // { 
  //  bar : [newbar]
  // }


  // return our instance object { bar : [newbar] }
};

这就是原型对象发挥作用的地方。让我们将foo对象用作fooClass原型:
var fooClass = function(newbar){
  this.bar = newbar;
};

fooClass.prototype = foo;

现在,正如您所期望的那样,您可以使用foo的方法:
var myFoo = new fooClass('baz');
myFoo.sayBar(); //alerts baz

这是构造函数现在正在执行的操作:
var fooClass = function(newbar){
  //**IF 'new' is declared ** create an instance object, which uses foo as it's prototype
  //That instance of the prototype is now scoped to 'this'

  //it sets the 'constructor' of the instance behind the scenes      
  //this.constuctor = [our constructor function]

  // now we can assign values to our instance and invoke it's methods

  this.bar = newbar;
  // we have now overridden foo's default foo.bar with our instance bar


  // so our instance now looks like this:
  //
  // { 
  //  bar : [newbar], //overridden in constructor
  //
  //  //with access to foo's methods through the prototype chain
  //  sayBar : function(){
  //    alert(this.bar);
  //  }
  // }


  // return our instance object
};

没有什么神奇的事情发生,构造函数所做的只是创建并返回一个对象实例,而.prototype =所做的只是为该对象设置原型。

如果我忘记使用 'new' 会发生什么?

如果在使用构造函数时不使用new,则不会创建或返回新的对象实例。这就是为什么通常要检查当前实例是否具有正确的构造函数:

var fooClass = function(newbar){
  if(!(this instanceof fooClass)){ //if this constructor != fooClass
    return new fooClass(newbar); //refire constructor with 'new'
  }

  //...
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接