__proto__与Constructor.prototype有何不同?


163
function Gadget(name, color)
{
   this.name = name;
   this.color = color;
}

Gadget.prototype.rating = 3

var newtoy = new Gadget("webcam", "black")

newtoy.constructor.prototype.constructor.prototype.constructor.prototype 

它总是返回等级= 3的对象。

但是,如果我执行以下操作:

newtoy.__proto__.__proto__.__proto__

链条最终返回null

另外,在Internet Explorer中,如果没有__proto__属性,如何检查null ?


30
图形图将帮助您了解原型和原型之间的区别。您可以按照newtoy对象的原型链进行操作,然后您将了解为什么newtoy 的第3个原型为空。
2012年

从图中还可以清楚地看出,newtoy.prototype它不等于newtoy.constructor.prototype,因此newtoy.constructor.prototype将没有称为的属性rating。同样,newtoy.constructor.prototype.constructor.property也不会具有称为的属性rating
2012年

错别字在最后的评论:因此newtoy.constructor.prototype将具有称为等级的属性。同样,newtoy.constructor.prototype.constructor.property也将具有称为Rating的属性。


1
@Royi Namir我已经在github上上传了jsViz。这是演示站点。请不要介意实际的代码有多未维护(脏)。我从未感动过的超老项目。

Answers:


210

我最近一直在努力思考,最后想出了这个“地图”,我认为这可以充分说明此事

http://i.stack.imgur.com/KFzI3.png 在此处输入图片说明

我知道我不是第一个提出这个建议的人,但是找到它的原因更有趣:-)。无论如何,之后我发现了另一个我认为基本相同的图表:

JavaScript对象布局

对我来说,最令人惊讶的事情是发现它Object.__proto__指向Function.prototype,而不是Object.prototype,但是我确信有充分的理由:-)

我也将图像中提到的代码粘贴到这里,以便于任何人想要对其进行测试。请注意,一些属性已添加到对象中,以使一些跳转后的位置变得容易知道:

Object.O1='';
Object.prototype.Op1='';

Function.F1 = '';
Function.prototype.Fp1 = '';

Cat = function(){};
Cat.C1 = '';
Cat.prototype.Cp1 = '';

mycat = new Cat();
o = {};

// EDITED: using console.dir now instead of console.log
console.dir(mycat);
console.dir(o);

2
@utsaina太酷了。检出OP发布的代码的另一个图形表示。而且我认为我们的图表在技术细节方面是一致的。

43
Object.__proto__指向的原因Function.prototype是因为Object()它本身是实例化一个空对象的本机函数。因此,Object()是一种功能。您会发现所有其他主要的本机类型的__proto__属性都指向Function.prototypeObjectFunctionStringNumber,和Array所有继承函数原型。
2013年

@drodsou您的第二个链接很棒。请现在就检查一下;)mollypages.org/misc/js.mp不错的解释:D
abhisekp

@Swivel“因此,Object()是一个函数”-您是说Object是一个函数吗?没有()
giorgim

2
@GiorgiMoniava正确。Object本身是一个功能;执行callable的结果Object(即running的返回值Object())不是函数。
旋转

67

constructor是由prototype功能对象的属性指向的对象的预定义[[DontEnum]]属性,并且最初将指向功能对象本身。

__proto__ 等效于对象的内部[[Prototype]]属性,即其实际原型。

使用new运算符创建对象时,其内部[[Prototype]]属性将设置为构造函数的prototype属性所指向的对象。

这意味着.constructor它将评估为.__proto__.constructor,即用于创建对象的构造函数,并且据我们所知,protoype该函数的属性用于设置对象的[[Prototype]]。

随之而来的.constructor.prototype.constructor.constructor(只要这些属性没有被覆盖);请参阅此处以获取更详细的说明。

如果__proto__可用,您可以遍历对象的实际原型链。在普通的ECMAScript3中无法执行此操作,因为JavaScript并非为深度继承层次结构而设计。


3
“这里”链接是黄金标准。如果您需要完整的说明,请去那里。
Ricalsin

.constructor.prototype串连好抓。我也不清楚,虽然我没有看到那.constructor是相等的.__proto__.constructor。这只是意味着在构造函数和原型之间循环。
Johnny_D

30

JavaScript的原型继承是基于__proto__属性的,即每个对象都继承其__proto__属性所引用的对象的内容。

prototype属性仅对Function对象特别适用,并且仅在使用newoperator调用Functionas构造函数时才适用。在这种情况下,创建的对象__proto__将被设置为构造函数Function.prototype

这意味着添加到Function.prototype将自动反映所有__proto__引用的对象Function.prototype

Function.prototype用另一个对象替换构造函数的对象将不会更新__proto__任何现有对象的属性。

请注意,__proto__不应直接访问属性,而应使用Object.getPrototypeOf(object)

为了回答第一个问题,我创建了一个定制的__proto__prototype参考图,不幸的是stackoverflow不允许我添加“少于10个信誉”的图像。也许另一些时间。

[编辑]该图使用[[Prototype]]而不是__proto__因为ECMAScript规范是指内部对象的方式。希望您能解决所有问题。

这里有一些提示可以帮助您理解该图:

red    = JavaScript Function constructor and its prototype
violet = JavaScript Object constructor and its prototype
green  = user-created objects
         (first created using Object constructor or object literal {},
          second using user-defined constructor function)
blue   = user-defined function and its prototype
         (when you create a function, two objects are created in memory:
          the function and its prototype)

请注意,constructor属性在创建的对象中不存在,但是是从原型继承的。

在此处输入图片说明


@xorcus能否请您解释这一点:new MyFunction()创建它的对象实例__proto__应该是指它的构造函数的原型被MyFunction.prototype.那么,为什么MyFunction.prototype.__proto__referes来Object.prototype?它应该引用(就像我的第一个示例一样)其ctor的原型MyFunction.prototype(注意MyFunction.prototype是的实例Myfunction
Royi Namir

@Royi Namir:MyFunction.prototype .__ proto__引用Object.prototype,因为MyFunction.prototype是一个对象。Object.prototype被所有对象继承(通常,继承原型链在此终止)。我不同意MyFunction.prototype是MyFunction的实例。obj instanceof MyFunction <=> MyFunction.prototype.isPrototypeOf(obj)<=> MyFunction.prototype存在于obj原型链中。MyFunction.prototype对象不是这种情况
xorcus

14

Object是夏娃,Function也是亚当,亚当(Function)使用其骨骼(Function.prototype)创建了夏娃(Object)。那谁创造了亚当(Function)?-JavaScript语言的发明者:-)。

根据utsaina的回答,我想添加更多有用的信息。

对我来说,最令人惊讶的事情是发现它Object.__proto__ 指向Function.prototype,而不是Object.prototype,但是我确信有充分的理由:-)

不应该这样。 Object.__proto__不应指向Object.prototype。相反,实例Object oo.__proto__应指向Object.prototype

(请原谅我使用这些术语classinstanceJavaScript,但您知道的是:-)

我认为类Object本身是的一个实例Function,因此Object.__proto__ === Function.prototype。因此:Object是夏娃,Function也是亚当,亚当(Function)使用其骨骼(Function.prototype)创建夏娃(Object)。

此外,即使类Function本身也是其自身的实例Function,即Function.__proto__ === Function.prototype,这也是为什么Function === Function.constructor

此外,常规类Cat是的实例Function,即Cat.__proto__ === Function.prototype

发生上述情况的原因是,实际上,当我们使用JavaScript创建类时,我们只是在创建一个函数,该函数应该是的实例FunctionObjectFunction仅仅是特殊的,但他们仍然类,而Cat为普通班。

作为一个因素,在Google Chrome JavaScript引擎中,以下4个参数:

  • Function.prototype
  • Function.__proto__
  • Object.__proto__
  • Cat.__proto__

它们全部===(绝对等于)其他3个,其值为function Empty() {}

> Function.prototype
  function Empty() {}
> Function.__proto__
  function Empty() {}
> Object.__proto__
  function Empty() {}
> Cat.__proto__
  function Empty() {}
> Function.prototype === Function.__proto__
  true
> Function.__proto__ === Object.__proto__
  true
> Object.__proto__ === Cat.__proto__
  true

好。那么谁来创建特殊的function Empty() {}Function.prototype)?考虑一下:-)


同意这一点,除了最后一件事:function Empty() {}您指的是什么等于Function.prototype等?在chrome控制台中使用的代码是什么?
drodsou 2012年

2
我纠正了您指出的最后一件事。它们的价值在于function Empty() {}Google Chrome。我还添加了控制台输出。
李·彼得(Peter Lee)

所有函数都是Function的instanceof,因此,所有函数都_ _proto_ _从Function.prototype 继承()。就这么简单:)
xorcus 2015年

很抱歉在旧线程上发表评论。但是它们是由语言发明者创建的吗?
Patel Parth,

6

我真的不知道为什么人们没有在您理解的实际问题上对您进行纠正。

这将使您更容易发现问题

因此,让我们看看发生了什么:

var newtoy = new Gadget("webcam", "black")

newtoy 
  .constructor //newtoy's constructor function is newtoy ( the function itself)
    .prototype // the function has a prototype property.( all functions has)
      .constructor // constructor here is a **property** (why ? becuase you just did `prototype.constructor`... see the dot ? )  ! it is not(!) the constructor function  !!! this is where your mess begins. it points back to the constructor function itself ( newtoy function)
         .prototype // so again we are at line 3 of this code snippet
            .constructor //same as line 4 ...
                .prototype 
                 rating = 3

太好了,现在让我们来看一下 __proto__

在此之前,请记住两件事__proto__

  1. 使用new运算符创建对象时,如果愿意,其内部[[Prototype]]/ proto__属性将设置prototype为其constructor function或“创建者” 的property(1)。

  2. 在JS中进行硬编码-:Object.prototype.__proto__is null

让我们将这两点称为“ bill

newtoy
     .__proto__ // When `newtoy` was created , Js put __proto__'s value equal to the value of the cunstructor's prototype value. which is `Gadget.prototype`.
       .__proto__ // Ok so now our starting point is `Gadget.prototype`. so  regarding "bill" who is the constructor function now? watch out !! it's a simple object ! a regular object ! prototype is a regular object!! so who is the constructor function of that object ? Right , it's the `function Object(){...}`.  Ok .( continuing "bill" ) does it has a `prototype` property ? sure. all function has. it's `Object.prototype`. just remember that when Gadget.prototype was created , it's internal `__proto__` was refered to `Object.prototype` becuase as "bill" says :"..will be set to the `prototype` property of   its `constructor function`"
          .__proto__ // Ok so now our satrting point is `Object.prototype`. STOP. read bullet 2.Object.prototype.__proto__ is null by definition. when Object.prototype ( as an object) was created , they SET THE __PROTO__ AS NULL HARDCODED

更好?



2

如果所有这些数字都是压倒性的,那么让我们看一下这些属性的含义。

STH.prototype

创建新函数时,将并行创建一个空对象,并使用[[Prototype]]链将其链接到该函数。要访问此对象,我们使用 prototype函数的属性。

function Gadget() {}
// in background, new object has been created
// we can access it with Gadget.prototype
// it looks somewhat like {constructor: Gadget}

请记住,prototype属性仅可用于功能。

构造函数

上面提到的原型对象除了-以外没有其他属性constructor。此属性表示创建原型对象的函数。

var toy = new Gadget();

创建Gadget函数时,我们也创建了一个对象,就像{constructor: Gadget}-一样Gadget.prototype。作为constructor指创建的对象原型的函数,toy.constructor表示Gadget功能。我们写toy.constructor.prototype,我们{constructor: Gadget}又得到了。

因此,存在一个恶性循环:您可以使用toy.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype,并且永远都是Gadget.prototype

toy
.constructor    // Gadget
.prototype    // {constructor: Gadget}
.constructor    // Gadget
.prototype    // {constructor: Gadget}
// ...

STH .__ proto__

虽然prototype是函数特有的属性,__proto__但它位于中时可用于所有对象Object.prototype。它指的是可以创建对象的函数原型。

[].__proto__ === Array.prototype
// true

({}).__proto === Object.prototype
// true

在这里toy.__proto__Gadget.prototype。正如Gadget.prototypeobject({})和使用Object函数创建对象一样(请参见上面的示例),我们得到Object.prototype。这是JavaScript中的高级对象,__proto__只能指示null

toy
.__proto__    // Gadget.prototype (object looking like {constructor: Gadget})
.__proto__    // Object.prototype (topmost object in JS)
.__proto__    // null - Object.prototype is the end of any chain

0

简短的答案:__proto__是对prototype创建对象的构造函数的属性的引用。

JavaScript中的对象

JavaScript对象是用于零个或多个属性集合的内置类型。属性是保存其他对象,原始值或函数的容器。

JavaScript中的构造函数

函数是常规对象([[Call]]以ECMA-262术语实现),具有可调用的附加功能,但在JavaScript中扮演另一个角色:如果通过运算符调用,则它们将成为构造函数(对象的工厂new。因此,构造函数是其他语言类的粗略模拟。

每个JavaScript函数实际上都是Function内置函数对象的一个实例,该对象具有一个特殊的属性,该属性prototype用于实现基于原型的继承和共享属性。构造函数创建的每个对象都有对其构造函数值的隐式引用(称为原型__proto__prototype

构造函数prototype是构建对象的一种蓝图,因为构造函数创建的每个对象都会继承对其的引用prototype

原型链

对象通过内部属性[[Prototype]]或指定其原型__proto__。两个对象之间的原型关系与继承有关:每个对象可以将另一个对象作为其原型。原型可能就是null价值。

通过__proto__属性连接的对象链称为原型链。当引用一个对象中的属性时,该引用就是在包含该名称的属性的原型链中第一个对象中遇到的属性。原型链的行为就像是单个对象一样。

看到这张图片(摘自这个博客):

proto.jpg

每当您尝试访问对象中的属性时,JavaScript都会在该对象中开始对其进行搜索,然后继续其原型,原型的原型等等,直到遇到该属性或是否__proto__拥有该值null

这种使用原型链的继承通常称为委托,以避免使用类链与其他语言的混淆。

几乎所有对象都是的实例Object,因为Object.prototype在它们的原型链中是最后一个。但Object.prototype不是Object因为Object.prototype.__proto__持有价值的一个实例null

您还可以使用如下null原型创建对象:

var dict = Object.create(null);

这样的对象比文字对象是更好的映射(字典),这就是为什么有时将这种模式称为dict模式(字典的dict)的原因。

注意:使用创建的文字对象{}是的实例,Object因为({}).__proto__是的引用Object.prototype


请引用您正在使用的引号和工件的来源。图片似乎来自giamir.com/pseudoclasses-and-prototypal-inheritance-in-JS,您对此拥有版权吗?
Bergi

@Bergi我引用了图片的来源。最我使用来自JS标准或MDN要么萃取引号
eigenslacker
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.