【问题标题】:Correct prototype chain for Function函数的正确原型链
【发布时间】:2008-12-20 10:33:40
【问题描述】:

以下程序的正确输出是什么(根据 ECMA 标准是正确的)?

function nl(x) { document.write(x + "<br>"); }
nl(Function.prototype);
nl(Function.prototype.prototype);
nl(Function.prototype.prototype == Object.prototype);
nl(Function.prototype.prototype.prototype);

Chrome 和 IE6 一致表示:

function Empty() {}
null for Chrome / undefined for IE6
false

然后崩溃。

Mozilla 输出:

function () { }
[object Object]
false
undefined

这些都是正确的吗?似乎 Mozilla 做得更好,但最好的输出是

function () { }
[object Object]
true
undefined

【问题讨论】:

  • 另见此链接:link

标签: javascript constructor prototype


【解决方案1】:

Function.prototype

来自ECMAScript Language Specification

15.3.3.1 Function.prototype

Function.prototype 的初始值为 函数原型对象(部分 15.3.4)。

15.3.4 函数原型对象的属性

函数 原型对象本身就是一个函数 对象(其 [[Class]] 是“功能”) 那,当被调用时,接受任何 参数并返回未定义。这 内部 [[Prototype]] 的值 函数原型的属性 object 是 Object 原型对象 (第 15.3.2.1 节)。

这是一个“空体”的函数; 如果它被调用,它只会返回 不明确的。函数原型 对象没有 valueOf 自己的财产;然而,它 继承 valueOf 属性 对象原型Object。

我得到这个输出:

  • Opera: function () { [原生代码] }
  • Chrome: 函数 Empty() {}
  • IE7: 函数原型() { [本机代码]}
  • FF3: 函数 () { }

Chrome 和 IE7 已经命名了它们的功能,Opera 和 IE7 告诉你它不会透露实现。他们都同意这一点:

nl(typeof Function.prototype); //function

比较一下:

nl(typeof Object.prototype); //object
nl(typeof Array.prototype); //object
nl(typeof String.prototype); // object

Function.prototype.prototype

我从 Opera 和 IE7 获得 undefined,从 Chrome 获得 null,从 FF3 获得 [object Object]。谁是对的?既然“函数原型对象本身就是一个函数对象”,它不应该是对自身的循环引用吗?为了避免循环引用,他们选择了不同的方式。我不知道是否有一个标准或者它是否取决于实现,但我认为 Object 是正确的。顺便说一句,在这里您可以看到内部 [[prototype]] 和公共原型之间的区别,就像您在之前的问题中所问的那样!

Function.prototype.prototype == Object.prototype

这是错误的,因为它不是同一个对象。见上文。

Function.prototype.prototype.prototype

只有 FF 会给你答案,因为他们的 Function.prototype.prototype 实现返回一个对象。

我同意您提出的输出看起来更合乎逻辑。

他们确实同意这一点:

nl(Object.prototype); // [object Object]
nl(Object.prototype.prototype); // undefined

【讨论】:

    【解决方案2】:

    您在这里所做的并不是真正走原型链 - this question 可能会帮助您了解实际发生的情况。我没有费心检查 ECMA 规范,但这是我对这个问题的看法:

    • Function是函数对象的构造函数

    • Function.prototype 是所有函数对象继承的原型 - 它可能包含像 callapply 这样的属性对所有 Function 实例通用;您检查的实现是一致的,因为它本身是作为函数对象实现的(正如 some 指出的,ECMA 规范要求这样做)

    • Function.prototype.prototype 并没有多大意义,但由于 Function.prototype 是作为函数对象实现的(可以使用作为构造函数),它至少应该存在;使用 Function.prototype 作为构造函数创建的对象将继承其属性 - 但由于不应该做这样疯狂的事情,将其设置为 null, undefined或者空对象是合理的

    • Function.prototype.prototype.prototype 很可能未定义:正如我们之前所见,Function.prototype.prototype strong> 应该是没有属性的东西(nullundefined 或空对象)并且绝对不是函数对象;因此,它的 prototype 属性应该是 undefined 甚至在尝试访问时可能会抛出错误

    希望这会有所帮助;)

    【讨论】:

    • 其实少了一步:&gt; Function.prototype() - undefined&gt; new Function.prototype() - Error 'Function.prototype' is not a constructor&gt; Function.prototype.prototype - undefined
    【解决方案3】:

    直接回答您的问题:Mozilla 是对的。不仅因为 Brendan Eich 为 Mozilla 工作,而且因为这是唯一正确的方法。让我们来看看细节:

    • 每个构造函数都是一个函数。
    • 每个函数都有prototype 属性用于构造对象。
    • 原型是一个对象(字典)。各个对象将方法/属性委托给各自的原型。
    • 就函数而言,原型对象是特殊的——它实现了特定于函数的方法和属性。此对象的类型/类未定义,也无法直接使用。
    • 这个原型对象不能是Object 也不能是Object.prototype

    让我详细说明最后一个陈述。如果为 false,我们可以像这样在用户代码中重新创建函数:

    // we re-creating the function!
    
    // our function constructor
    var Fun = function(){ /*...*/ };
    
    // let's chain the prototype
    Fun.prototype = new Object();
    
    // do we have a function now? let's fund out
    var fun = new Fun();
    console.log(fun.length);  // undefined
    fun.call(null);           // fail
    fun.apply({}, [1, 2, 3]); // fail
    // nope
    

    我们可以观察到new Object() 没有定义新的方法或属性,可以尝试直接使用Object.prototype 得到相同的结果。

    总结:函数原型不是Object也不是Object.prototype。这是一个非常特殊的物体。这是我们无法在用户代码中重新创建函数的另一个原因。

    编辑:有关原型的更多详细信息,请查看this answer

    【讨论】:

    • 我有点困惑。为什么 Fun() 构造函数的结果会返回支持 call() 或 apply() 方法的东西?这些方法属于函数,但构造函数返回的只是一个普通的旧对象?
    • 如果将原型设置为函数对象,则构造函数创建的对象将具有以下属性: function Moo() { } Moo.prototype = function() { }; var moo1 = new Moo();警报(moo1.apply)
    • 这不起作用:var moo1 = new ...; moo1(1,“2”,真); --- 拥有名为 apply 和 call 的方法对操作员没有帮助。 JS 不允许定义操作符和操作,并且没有合法的方式来继承/委托它们。我使用 apply/call 作为“可能”工作但不工作的示例。
    • 消除困惑并检验您的假设很容易——只需运行我的示例,看看它是否适合您。然后修改它以适应您的假设并再次运行。看看它在哪里工作(如果有的话)。回来报告。 :-)
    【解决方案4】:

    我知道这篇文章有点老了,但我一直在网上搜索有关此主题的信息,并认为我会发布我发现的内容。原型属性用于构造函数。它允许您使用 new 关键字分配您将创建的对象的原型对象。

    JavaScript 中的每个对象都有一个原型对象,但许多实现不允许您直接访问它或允许您在对象创建后设置它。在 FireFox 中,您可以通过“__proto__”属性访问此对象。

    下面我有一个使用“__proto__”属性的代码版本。函数原型链上的部分与您认为的应该是相符的。

    function nl(z) { document.write(z + "<br>"); }
    
    x = {};
    
    nl(x["__proto__"]);
    nl(x["__proto__"] === Object.prototype);
    
    nl("");
    
    nl(nl.prototype.constructor);
    nl(nl["__proto__"].constructor);
    nl(nl["__proto__"] === nl.prototype);
    
    nl("");
    
    nl(nl["__proto__"]);
    nl(nl["__proto__"] === Function.prototype);
    nl(nl["__proto__"]["__proto__"] === Object.prototype);
    nl(nl["__proto__"]["__proto__"]["__proto__"]);
    
    nl("");
    
    nl(Function["__proto__"]);
    nl(Function["__proto__"]["__proto__"]);
    nl(Function["__proto__"]["__proto__"] === Object.prototype);
    nl(Function["__proto__"]["__proto__"]["__proto__"]);
    

    Firefox 中的输出是:

    [object Object]
    true
    
    function nl(z) { document.write(z + "
    "); }
    function Function() { [native code] }
    false
    
    function () { }
    true
    true
    null
    
    function () { }
    [object Object]
    true
    null
    

    【讨论】:

      猜你喜欢
      • 2012-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多