__proto__与constructor.prototype有何不同?

时间:2009-03-16 14:55:54

标签: javascript inheritance prototype-programming

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 

它总是返回rating = 3的对象。

但如果我这样做:

newtoy.__proto__.__proto__.__proto__

链条最终返回null

同样在Internet Explorer中,如果没有__proto__属性,我将如何检查null?

8 个答案:

答案 0 :(得分:196)

我最近一直试图绕过这个,最后想出了这个“地图”,我认为这个问题充分说明了这个问题

http://i.stack.imgur.com/KFzI3.png enter image description here

我知道我不是第一个提出这个问题的人,但更有意思的是找出它:-)。无论如何,在那之后我发现了我认为另一张图基本相同:

Javascript object layout

对我来说最令人惊讶的事情是发现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);

答案 1 :(得分:66)

constructor是函数对象的prototype属性所指向的对象的预定义[[DontEnum]]属性,最初将指向函数对象本身。

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

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

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

以下.constructor.prototype.constructor.constructor相同(只要这些属性没有被覆盖);有关更详细的说明,请参阅here

如果__proto__可用,您可以遍历对象的实际原型链。在简单的ECMAScript3中无法做到这一点,因为JavaScript不是为深度继承层次结构而设计的。

答案 2 :(得分:30)

JavaScript中的Prototypal Inheritance基于__proto__属性,在某种意义上,每个对象都继承了__proto__属性引用的对象的内容。

prototype属性仅适用于Function个对象,且仅在使用new运算符调用Function作为构造函数时才有效。在这种情况下,创建的对象的__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属性在创建的对象中不存在,但是从原型继承。

enter image description here

答案 3 :(得分:13)

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

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

  

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

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

(请原谅我在JavaScript中使用classinstance这两个词,但你知道: - )

我认为课程Object本身就是Function的一个实例,这就是Object.__proto__ === Function.prototype的原因。因此:Object是夏娃,而Function是亚当,亚当(Function)使用他的骨骼(Function.prototype)创建夏娃(Object)。

此外,即使班级Function本身也是Function本身的一个实例,即Function.__proto__ === Function.prototype,这也是Function === Function.constructor

的原因

此外,常规课程CatFunction的实例,即Cat.__proto__ === Function.prototype

上面的原因是,当我们在JavaScript中创建一个类时,实际上,我们只是创建一个函数,它应该是Function的一个实例。 ObjectFunction只是特殊的,但它们仍然是类,而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)?想一想: - )

答案 4 :(得分: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属性(1)如果您愿意,可以constructor function或“创作者”。

  2. 在JS中硬编码 - :Object.prototype.__proto__null

  3. 我们将这两点称为“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
    

    更好?

答案 5 :(得分:2)

每个函数都创建它的原型。 当我们使用该函数构造函数创建对象时,我的对象的 __ proto __ 属性将开始指向该函数的原型。

答案 6 :(得分:2)

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

JavaScript中的对象

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

JavaScript中的构造函数

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

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

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

原型链

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

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

查看此图片(摘自blog):

proto.jpg

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

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

几乎所有对象都是Object的实例,因为Object.prototype在其原型链中位于最后。但是Object.prototype不是Object的实例,因为Object.prototype.__proto__拥有值null

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

var dict = Object.create(null);

这样的对象比文字对象是更好的地图(词典),这就是为什么有时将这种模式称为 dict 模式(对于字典是 dict )的原因。 / p>

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

答案 7 :(得分:0)

如果所有这些数字都是压倒性的,那么让我们来看看这些属性是什么意思。

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属性仅适用于函数。

STH.constructor

上面提到的原型对象没有属性,只有一个 - 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 .__原__

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

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

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

此处toy.__proto__Gadget.prototype。由于Gadget.prototype是一个对象({})并且使用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