通过Object.create()实现JavaScript原型链

10
var someObj = function() { }
var p = new someObj();

alert(someObj.prototype);   // This works
alert(p.prototype);         // UNDEFINED, but why?

someObj.prototype.model= "Nissan";
alert(p.model);             // This works! I understand the dynamic nature of prototypes, but doesn't that mean that p.prototype === someObj.prototype?

为什么会这样呢?由于“p”是“someObj”的一个实例,为什么原型是未定义的?我的意思是,当我向“someObj”原型添加属性时,它可以被“p”访问,那么为什么原型无法访问?
5个回答

11
重要的是,函数对象的prototype属性并不是一个对象的原型。它是通过new someObj创建的对象所分配的原型对象。在ES5之前,您无法直接访问对象的原型;从ES5开始,您可以通过Object.getPrototypeOf来访问它。
关于代码中alert(p.prototype); // UNDEFINED, but why?的原因是p对象没有名为“prototype”的属性。它有一个基础的原型,但这不是访问它的方式。
所有函数对象都具有一个称为prototype的属性,以便如果它们用作构造函数,我们可以定义由这些构造函数创建的对象的基础原型的属性。这可能会有所帮助:
function Foo() {
}
Foo.prototype.answer = 42;

console.log(Foo.prototype.answer); // "42"
var f = new Foo();
console.log(f.answer); // "42"

最后一行的作用如下:

  1. 获取对象 f
  2. f 是否有自己的名为 "answer" 的属性?
  3. 没有,f 有原型吗?
  4. 是的,原型是否有自己的名为 "answer" 的属性?
  5. 是的,返回该属性的值。

你在问题的标题中提到了Object.create。重要的是要理解,Object.create与构造函数是完全不同的。它被添加到语言中,这样如果你不喜欢使用构造函数,你就可以直接设置对象的原型来创建对象,而无需使用构造函数。


6

这是因为prototype是构造函数的属性,而不是它本身的属性。然而,prototype对象具有对构造函数的引用,所以您可以通过其constructor属性访问对象的prototype

function Foo() {}

Foo.prototype.foo = "bar";

var c = new Foo;

console.log( c.constructor === Foo );   // true
console.log( c.constructor.prototype ); // { foo: 'bar' }

然而,如果你覆盖了构造函数的初始prototype属性,则这种方法将不起作用:
function Foo() {}

// I overwrite the prototype property, so I lose the initial reference
// to the constructor.
Foo.prototype = {
  foo: "bar"
};

var c = new Foo;

console.log( c.constructor === Foo );    // false
console.log( c.constructor === Object ); // true
console.log( c.constructor.prototype );  // {}

因此,使用ES5引入的新的Object.getPrototypeOf方法会更好。

function Foo() {}

Foo.prototype = {
  foo: "bar"
};

var c = new Foo;

console.log( c.constructor === Foo );    // false
console.log( c.constructor === Object ); // true
console.log( c.constructor.prototype );  // {}
console.log( Object.getPrototypeOf(c) ); // { foo: 'bar' }

另一个解决方案是确保在原型上恢复 constructor 引用:
function Foo() {}

// Overwriting the initial prototype    
Foo.prototype = {
  constructor: Foo, // restore the constructor reference
  foo: "bar"
};

我发现了一篇非常有用的博客文章,结合这个答案,消除了关于整个原型混乱的许多困惑。http://blog.sklambert.com/javascript-prototype/ 图表在理解这个答案方面非常有帮助。希望这能帮助到某些人。 - Bruce

2

根据克劳德的回答,原型已经存在并且可以通过使用Object.getPrototypeOf(p)来访问。如果我的理解是正确的,那么属性和方法会被复制到新对象的实例中,但这并不意味着p === someObj.prototype。它意味着p.prototype === someObj.prototype。 - Taha Ahmad

1

psomeObj 的一个实例。原型属于构造函数。您可以使用 p.constructor.prototype 检索 p 的构造函数原型。


0
在JavaScript中,构造函数和实际上所有的函数都会获得一个原型属性。一个对象(也就是一组键值对)并没有原型属性。在你上面的例子中,
var someObj = function() { } // this is a function, so it has a prototype property
var p = new someObj(); // this is an instance object, so it doesn't

这就是为什么定义了someObj.prototype,但没有定义p.prototype的原因。

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