函数原型链的正确写法

7
以下程序的正确输出(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

请参考此链接:链接 - biziclop
4个回答

12

Function.prototype

根据ECMAScript语言规范

15.3.3.1 Function.prototype

Function.prototype的初始值是函数原型对象(第15.3.4节)。

15.3.4 函数原型对象的属性

函数原型对象本身也是一个函数对象(它的[[Class]]为"Function"),当被调用时,接受任何参数并返回undefined。函数原型对象内部[[Prototype]]属性的值是对象原型对象(第15.3.2.1节)。

这是一个“空体”的函数;如果它被调用,则仅返回undefined。函数原型对象本身没有valueOf属性,但是它从对象原型对象继承了valueOf属性。

我得到了以下输出:

  • Opera:function () { [native code] }
  • Chrome:function Empty() {}
  • IE7:function prototype() {[native code]}
  • FF3:function () { }

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]]和公共prototype之间的区别,就像你在早些时候问的那样!

Function.prototype.prototype == Object.prototype

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

Function.prototype.prototype.prototype

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

他们在这个问题上是一致的:

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

9
你在这里所做的并不是真正的原型链遍历 - 这个问题或许可以帮助你理解实际发生了什么。我没有查看ECMA规范,但是以下是我的看法:
  • Function 是函数对象的构造函数

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

  • Function.prototype.prototype 没有太多意义,但由于Function.prototype被实现为一个函数对象(可能可用作构造函数),因此它至少应该存在;使用Function.prototype作为构造函数创建的对象将继承其属性 - 但由于没有像这样做的理由,将其设置为nullundefined或空对象是合理的

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

希望这有所帮助 ;)

实际上只少了一步: > Function.prototype() - undefined > new Function.prototype() - Error 'Function.prototype' 不是一个构造函数 > Function.prototype.prototype - undefined - Bergi

4
直接回答你的问题:Mozilla是正确的。不仅因为Brendan Eich在Mozilla工作,而且因为这是唯一正确的方法。让我们详细看一下:
- 每个构造函数都是一个函数。 - 每个函数都有一个prototype属性用于构造对象。 - 原型是一个对象(字典)。各个对象将方法/属性委托给它们各自的原型。 - 在函数的情况下,原型对象是特殊的 - 它实现了特定于函数的方法和属性。此对象的类型/类未定义,并且不直接可用。 - 此原型对象不能是ObjectObject.prototype
让我详细说明最后一个语句。如果它是错误的,我们可以在用户代码中重新创建函数,如下所示:
// 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得到相同的结果。
总结:该函数的原型不是ObjectObject.prototype,而是一个非常特殊的对象。这是为什么我们不能在用户代码中重新创建一个函数的另一个原因。
编辑:有关原型的更多细节,请参见此答案

我有点困惑。为什么Fun()构造函数的结果会返回支持call()或apply()方法的东西?这些方法属于函数,但从构造函数返回的是一个普通的对象? - Helephant
如果您将原型设置为函数对象,则由构造函数创建的对象将具有这些属性: function Moo() { } Moo.prototype = function() { }; var moo1 = new Moo(); alert(moo1.apply) - Helephant
这个不起作用:var moo1 = new ...; moo1(1, "2", true); --- 拥有名为apply和call的方法对运算符没有帮助。JS不允许定义运算符和操作,并且没有合法的方式来继承/委托它们。我使用apply/call作为“可能”有效的示例,但实际上并不行。 - Eugene Lazutkin
很容易消除困惑并测试您的假设 - 只需运行我的示例并查看它是否适用于您。然后修改它以适应您的假设并再次运行。看看它在哪里起作用(如果有)。回报。 :-) - Eugene Lazutkin

2
我知道这篇文章有点旧了,但是我一直在网上搜索关于这个主题的信息,并想发表我所发现的内容。原型属性是为构造函数而设计的。它允许你分配将使用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

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