使用Object.create设置__proto__而不是prototype

5

我对使用object.create实现原型继承相对于经典的js方式还比较陌生。

至少在Chrome浏览器中,我惊奇地发现以下代码:

var baseObject = {
test : function(){
    console.log('Child');
}
}

var newObject = Object.create(baseObject);
newObject.test = function(){
console.log('Parent');
this.__proto__.test();
}
console.log(newObject);

newObject.test();

生成这个(模拟Web工具中的输出):

Object {test: function, test: function} 
    test: function (){
    __proto__: Object
        test: function (){
        __proto__: Object
Parent
Child 

因此你可以看到,它不是设置原型而是仅使用“__proto__”,我认为这种做法是不鼓励的。从我的代码中可以看出,我能够正确继承并调用父对象,但只能使用“__proto__”。使用“prototype”会导致错误(未定义)。

这里发生了什么?我认为对象的创建会设置“prototype”,因为这是标准(或者我曾经这样认为)。为什么它填充并让我使用“__proto__”?

3个回答

5

不建议使用__proto__来设置原型继承,因为它是非标准的。这并不意味着你不能使用Object.create()来创建一个具有特定原型对象的新对象。

默认情况下,对象没有.prototype属性。你可能会将其与函数的.prototype对象混淆。

因此,如果我有这样一个函数:

function Foo() {

}

Foo 函数对象有一个 .prototype 属性,它引用一个对象,该对象将在调用构造函数创建任何对象时用作 __proto__

var f = new Foo();

现在f是一个对象,它的原型链上有Foo.prototype。可以使用Object.getPrototypeOf()来验证此内容;

Object.getPrototypeOf(f) === Foo.prototype; // true

Object.create 方法能够让你设置相同的原型链,但不需要使用构造函数。因此下面这段代码是等效的:

var f2 = Object.create(Foo.prototype);

现在我们有一个对象,其设置方式与原始的f对象相同。
Object.getPrototypeOf(f2) === Foo.prototype;            // true
Object.getPrototypeOf(f2) === Object.getPrototypeOf(f); // true

所以这只是一种不同的方式,最终做的事情是相同的。对象和其原型链之间的关系是一种内部关系。非标准的__proto__只是暴露了该关系。


非常有趣。你是对的,我忘记了原型只适用于函数。因此,至少在Chrome中,__proto__的使用以某种非标准的方式复制了计划对象的原型? - nahelm
@nahelm:有点类似。__proto__ 允许你给一个已存在的对象设置新的原型链。构造函数和 Object.create 只能在对象创建时设置它。从那时起,它就是永久的。事实上,如果你从构造函数创建一个对象,然后给构造函数一个新的 .prototype 对象,那么你已经创建的对象仍然引用原始的对象。它永远不会改变。 - the system
我想我终于明白了。我困惑的部分是我期望我的Chrome网页工具显示我正在创建的对象的原型。我还因为某些愚蠢的原因认为使用new关键字允许您使用原型关键字(例如您的示例中的f.prototype),但这是不正确的。当我执行您的Foo示例并向其原型添加一个函数时,当我console.log(f)时,该函数也会显示在__proto__对象中。因此,在这种情况下,__proto__公开了通常隐藏的原型链,并且如您所说是永久的。 - nahelm
@nahelm:没错,完全正确。有点不幸的是,JavaScript 最初使用这些构造函数来设置原型链,这真的相当令人困惑。理解的关键部分是原型继承涉及的是对象之间的隐藏链接,该链接在查找属性时使用。 Object.create() 是一种更直接创建该链接的方法。 Object.getPrototypeOf() 允许您获取链中的下一个对象,而 __proto__ 则允许您获取或替换它。 - the system

0

在这种情况下,由于原始对象是一个对象,构造函数的原型是对象。因此constructor.prototype != __proto__ - Travis J

0

Object.create() 设置了特定对象实例的原型。prototype 是构造函数上的一个属性,它是每个通过使用该构造函数和new操作符创建的实例自动分配为[[Prototype]]的对象。

__proto__ 是访问特定实例的[[Prototype]]的非标准方式。您还可以调用Object.getPrototypeOf(this)来以标准方式访问原型。


1
可能需要将“prototype”更改为[[Prototype]],以澄清当您谈论内部属性时的含义。 - Matt Ball

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