【问题标题】:How does __proto__ differ from constructor.prototype?__proto__ 与 constructor.prototype 有何不同?
【发布时间】:2010-10-13 15:21:44
【问题描述】:
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?

【问题讨论】:

  • 这个graph diagram 将帮助您了解prototype 和proto 之间的区别。你可以跟随 newtoy 对象的 proto 链,然后你就会明白为什么 newtoy 的第三个 Proto 是空的。
  • 从图中也可以清楚地看出newtoy.prototype 不等于newtoy.constructor.prototype,因此newtoy.constructor.prototype 将没有名为rating 的属性。同样newtoy.constructor.prototype.constructor.property 也不会有名为rating 的属性。
  • 最后评论中的错字:因此newtoy.constructor.prototype 将具有名为 rating 的属性。同样,newtoy.constructor.prototype.constructor.property 也将具有称为 rating 的属性。
  • @Royi Namir 我已经在 github 上上传了jsViz。这是demo site。请不要介意实际代码是多么未维护(和肮脏)。这是我永远没有接触过的超级老项目。

标签: javascript inheritance prototype-programming


【解决方案1】:

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

JavaScript 中的对象

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

JavaScript 中的构造函数

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

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

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

原型链

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

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

查看这张图片(摘自blog):

每当您尝试访问对象中的属性时,JavaScript 都会在该对象中开始搜索它并继续查找其原型、原型的原型等等,直到遇到该属性或 __proto__ 持有值 @987654340 @。

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

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

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

var dict = Object.create(null);

这样的对象是比字面对象更好的映射(字典),这就是为什么这种模式有时被称为 dict 模式(dict 表示字典)。

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

【讨论】:

【解决方案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 属性仅适用于函数。

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.__proto__

虽然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

【讨论】:

    【解决方案3】:

    我最近一直在试图解决这个问题,最后想出了这个“地图”,我认为它可以充分说明这个问题

    http://i.stack.imgur.com/KFzI3.png

    我知道我不是第一个编造这个的人,但弄清楚它比找到它更有趣:-)。无论如何,在那之后我发现例如这是另一个我认为基本相同的图表:

    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);
    

    【讨论】:

    • @utsaina 非常酷。查看 OP 发布的代码的另一个 graphical representation。而且我认为我们的图表在技术细节方面是一致的。
    • 之所以Object.__proto__指向Function.prototype是因为Object()本身就是一个实例化空对象的native函数。因此,Object() 是一个函数。您会发现所有其他主要原生类型的__proto__ 属性都指向Function.prototypeObjectFunctionStringNumberArray都继承了Function原型。
    • @drodsou 你的第二个链接太棒了。现在请检查一下;)mollypages.org/misc/js.mp很好的解释:D
    • @Swivel " 因此,Object() 是一个函数" - 你的意思是说 Object 是一个函数吗?没有()
    • @GiorgiMoniava 正确。 Object本身就是一个函数;执行可调用Object的结果(即运行Object()的返回值)不是函数。
    【解决方案4】:

    我真的不知道为什么人们没有纠正你关于你理解的实际问题的地方。

    这会让你更容易发现问题

    让我们看看发生了什么:

    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__ 属性将设置为其constructor functionprototype 属性(1) 或“创建者”如果你喜欢 。

    2. 在 JS 中硬编码 — : Object.prototype.__proto__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
    

    更好?

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 我想你的意思是说__proto__属性。
      • 是的。我的意思是对象的 proto 属性。我希望这些信息有用。
      【解决方案6】:

      JavaScript 中的原型继承基于__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属性不存在于创建的对象中,而是继承自原型。

      【讨论】:

      • @xorcus 你能解释一下吗:new MyFunction() 创建一个对象实例,它的__proto__ 应该引用它的ctor 原型,即MyFunction.prototype. 那么为什么MyFunction.prototype.__proto__ 引用Object.prototype ?它应该(就像我的第一个样本一样)引用其 ctor 的原型,即 MyFunction.prototype(注意 MyFunction.prototypeMyfunction 的一个实例)
      • @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 对象并非如此
      【解决方案7】:

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

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

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

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

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

      我认为Object 类本身就是Function 的一个实例,这就是Object.__proto__ === Function.prototype 的原因。因此:Object 是 Eve,Function 是 Adam,Adam (Function) 用他的骨头 (Function.prototype) 创造了 Eve (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)?想一想:-)

      【讨论】:

      • 同意,除了最后一件事:function Empty() {}是什么,你指的是等于Function.prototype等?,你在chrome控制台中使用的代码是什么?
      • 我更正了您指出的最后一件事。它们在 Google Chrome 中的值为 function Empty() {}。我还添加了控制台输出。
      • 所有函数都是instanceof Function,因此,所有函数都继承自Function.prototype (_ _proto_ _)。就这么简单:)
      • 抱歉评论旧帖。但它们是由语言的发明者创造的吗?
      【解决方案8】:

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

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

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

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

      因此.constructor.prototype.constructor.constructor 相同(只要这些属性没有被覆盖);更详细的解释见here

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

      【讨论】:

      • “这里”链接是黄金标准。如果你想要完整的描述,去那里。
      • .constructor.prototype 链接很好。我也不清楚,虽然我没有看到.constructor 等于.__proto__.constructor。这仅仅意味着在构造函数和它的原型之间循环。
      猜你喜欢
      • 2016-10-29
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-18
      • 2019-08-20
      • 1970-01-01
      相关资源
      最近更新 更多