__proto__ 和 prototype 的区别

8

据我所知,一个函数应该从其prototype对象继承属性,可以使用.prototype__proto__属性访问。

//my prototype Object
var myObj = {
    a: 1,
    b: 2
};

var myFunc = function () {};

// setting function's `prototype` property
myFunc.prototype = myObj;
alert(myFunc.a);
//returns undefined (Why???) I was expecting 1

但是当我尝试以下操作时,
//setting function __proto__ property
myFunc.__proto__ = myObj;
//returns 1
alert(myFunc.a);

为什么我设置myFunc.__proto__时它能工作,而设置myFunc.prototype时它就不起作用呢?

我参考了JavaScript中的__proto__ VS. prototype,但是还是无法理解。


2
http://es5.github.io/ 对 __proto__ 一无所知。这意味着它不是标准的一部分,你永远不应该使用它。 - zerkms
不,对象的原型(它继承自哪个)不是 .prototype。您是否阅读了 https://dev59.com/4mkw5IYBdhLWcg3wV5ID 的所有答案?您具体没有理解什么(我们很乐意改进它们)? - Bergi
那么,.prototype 有什么用途? - Flake
@Flake: 关于构造函数 - Bergi
1
这意味着“.prototype”被用作由该函数构造的对象的原型,但它与该函数自身的原型无关。 - Flake
3个回答

19

__proto__

你可以使用__proto__访问对象的内部[[Prototype]]属性。你可以将[[Prototype]]视为当前对象在继承层次结构中的实际父级。

prototype

这是一个特殊属性,当设置在(构造函数)函数对象上时,用于为从构造函数创建的实例建立继承链。例如:

function Foo() {}
Foo.prototype = {a: 1};

现在,当你创建一个类型为Foo的新对象时,新创建的对象的内部[[Prototype]]属性将引用Foo.prototype对象。您可以这样确认

console.assert((new Foo()).__proto__ === Foo.prototype);
在您的情况下,
myFunc.prototype = myObj;

你正在创建函数对象上的一个prototype属性,这仅在使用此函数(构造函数)创建新对象时才会使用。您可以将其视为新对象的模板。因此,当您执行myFunc.a时,JS引擎尝试在原型链中查找amyFunc及其父级中是否存在,如果没有找到,它就会返回undefined

但是,当您执行

myFunc.__proto__ = myObj;

你正在将myFunc的父级对象在原型链中设置为myObj。因此,当你执行 myFunc.a 时,JavaScript引擎首先在myFunc对象本身中查找a属性,但是在该对象中没有找到这个属性。然后,它尝试在其直接父级中查找,也就是myObj中查找。这就是为什么在这种情况下它返回1的原因。


注意:你可以使用以下函数更好地理解原型链。

function printPrototypeChain(object) {
    while (object !== null) {
        console.log(object);
        object = Object.getPrototypeOf(object);
    }
}

现在,让我们在将对象设置为函数对象的prototype属性时打印原型链。

function myFunc() {}
myFunc.prototype = {
    a: 1,
    b: 2
};
printPrototypeChain(myFunc);

输出结果将是:

[Function: myFunc]
[Function: Empty]
{}

这些对象中没有定义a,因此返回undefined。但在这种情况下,

function myFunc() {}
myFunc.__proto__ = {
    a: 1,
    b: 2
};
printPrototypeChain(myFunc);

原型链就像这样变成了这个样子

[Function: myFunc]
{ a: 1, b: 2 }
{}

并且a被发现在myFunc的直接父级中。因此,相应的值1被返回。

注意:不要在实际代码中使用__proto__,因为它仅为向后兼容保留在最新版本的JavaScript规范中。在这里了解更多信息。请改用Object.getPrototypeOfObject.setPrototypeOf


1
一个函数有两个原型……这很令人困惑!!! - Flake
1
当你执行myFunc.prototype = myObj;时,myObj可用于myFunc实例,而不是myFunc本身。 var x = new myFunc(); console.log(x.a); //返回1 - prgmrDev

1
你把构造函数myFuncmyFunc的实例混淆了。

如果你做这样的事情:

var o = new myFunc();
alert(o.a);

这将会警告1,因为omyFunc的实例,所以它的a属性来自于myFunc原型。

如果你设置了__proto__,你就会直接替换掉myFunc之前继承的Function原型,并用你的原型对象进行替换。事实上,在你这么做之后,任何你通常可以在函数上使用的方法,例如call,都不会再在myFunc中找到。

myFunc.__proto__ = myObj;
myFunc.call(null); // Will throw an error

0
所有的JS对象都有一个名为__proto__的系统属性。如果你尝试获取对象a的属性x的值,首先会在对象a的属性中搜索该属性。如果没有找到,则会在a.__proto__的属性中搜索。以此类推。
这就是为什么在你的例子中myFunc.a返回1的原因。
当你设置myFunc.prototype=myObj时,这意味着在未来创建myFunc实例时将使用myObj
var mf = new myFunc();

这并不意味着,如我上面提到的那样,myFunc 必须本身具有 a 属性。


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