JavaScript - The Good Parts:函数原型 vs 对象原型

20

刚读完《JavaScript: The Good Parts》这本书,非常好。但我对第33-34页上一个非常重要的主题——类型扩展感到困惑。它描述了在Function.prototype中创建一个新方法,因此当使用新方法调用时,所有函数都将具有该方法。很公平。但随后的示例显示该方法被用于数字和字符串上。我认为数字和字符串是对象,而不是函数。我错过了什么吗?

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

使用示例:

Number.method('integer', function() {
    return Math[this < 0 ? 'ceiling' : 'floor'](this);
});

document.writeln((-10 / 3).integer()); //-3

7
函数是对象。 - Matt Ball
2
@MattBall 我认为这不是 OP 所问的。相反,我认为他/她是在问 Number 如何从 Function 派生出来的。 - Kevin Ji
2
数字 一个函数。它不是从 Function 派生的,而是一个实例。 - Pointy
@Pointy,Object.getPrototypeOf(Number) == Function.prototype // true - JukkaP
@Pointy 但是这个 iNumber 实例是通过原型继承从 Function.prototype 派生而来的。 - JukkaP
显示剩余2条评论
4个回答

13
函数对象 vs 函数实例对象 首先,在 JavaScript 中,函数也是一个对象。我指的不是由 new() 构造函数创建的对象,而是函数本身。为避免混淆,我将这种对象称为函数对象,用 new() 构造函数创建的对象称为函数实例对象

_ proto _ 和 prototype 属性

在 JavaScript 中,任何函数对象都有两个属性:_ proto _prototype。此外,任何函数实例对象(使用 new 构造函数创建)都具有一个_ proto _属性。_ proto _ 定义了继承关系。 关于这方面的一些好资源可以在以下链接中找到:

http://zeekat.nl/articles/constructors-considered-mildly-confusing.html

如何定义继承?

如果对象 objA 和 objC 通过任意数量的 _ proto _ 相连,则 objA 继承了另一个对象 objC。因此,如果 objA 的_ proto _等于 objB,objB 的_ proto _等于 objC,则 objA 继承了 objB 和 objC,而 objB 继承了 objC。

什么是继承?

它意味着任何继承对象都可以使用继承对象的任何属性。

Function.prototype 是什么意思?

每个 函数对象_proto_ 属性都指向它的原型对象,也就是 Function.prototype 对象。这意味着每个 函数对象 都可以访问 Function.prototype 的属性,因为每个 函数对象 都继承了 Function.prototype 对象。这也意味着,如果在 Function.prototype 对象中添加了 method 属性,那么在 JavaScript 中所有可能的 函数对象 (包括字符串、数字等)都将可以使用它。

this.prototype[name] = func;

当从像数字、字符串等 函数对象 中调用 'method' 时,this 指向 函数对象。这意味着我们现在在 函数对象 中有一个名为 "name" 的新属性,并且它是一个函数 'func'。

函数对象的 prototype 属性有什么用?

函数对象prototype 属性是使用该函数的 new 构造函数创建的 函数实例对象_ proto _ 所引用的。

如果执行以下代码:

Number.method('integer', function () {...});

那么 Number.prototype 就有了定义在其中的 integer 方法。这意味着每个数字 函数实例对象,比如 new Number(2.4),都会从 Number.prototype 中 "继承" 这个新的属性 'integer',因为这个数字 函数实例对象_ proto _ 会被设置为 Number.prototype。


11

你向 Number 原型添加了一个方法,这样每个 Number 实例都可以访问它。换句话说,因为 Number 原型上有一个名为 "integer" 的属性,任何尝试从任何 Number 实例中访问该属性的操作都将成功。这就是将属性放在构造函数原型上的整个意义。

当 JavaScript 原始数字出现在 . 运算符的左侧时,语言会自动将其装箱为 Number 实例,以使方法调用有意义。

编辑 — 让我们看一下那个 "method" 函数是如何工作的。在调用中:

Number.method( "integer", function() { ... } )

发生了什么?在“method”函数内部,"name"参数是"integer"。然后将函数参数分配为this.prototype的属性。在“method”函数的调用中,“this”是Number构造函数。因此,“method”函数(位于Function原型上,并且可用于所有函数实例,例如Number构造函数)将给定的函数添加为所涉及构造函数的原型的属性。

为什么“method”属性在Number构造函数作为属性可见?因为Number构造函数本身就是一个函数。 "method"函数被创建为Function原型的属性,这意味着它对每个函数实例都可见-包括Number构造函数。


1
@svenyonson Number 是一个函数。在您的浏览器控制台中尝试输入以下内容:typeof Number - Matt Ball
@svenyonson,正如Matt Ball所说,Number构造函数本身就是一个函数实例。 - Pointy
@Pointy - 我现在明白了。(-10/3) 表达式需要调用 Number 构造函数。 - svenyonson
@Pointy,虽然上述所有内容都是正确的,但如果将“method”添加到Object而不是Function中,它也可以工作(我没有看到任何意外后果?)。这必须有效,因为函数也是对象。从可读性/理解性的角度来看,我建议将其添加到Object而不是Function中,这更有意义。 - svenyonson
@svenyonson 是的,但是 Date 构造函数不是一个 Date 实例。如果你将 "method" 放在 Object 原型上,那么它也会对 Date 实例可用。尝试调用 d.method("foo", function() { }); - Pointy
显示剩余6条评论

0

NumberObject 或其他内置类也都是构造函数,在 JavaScript 中,构造函数就是一个函数。在 ECMAScript 规范中,有两个重要的描述:

每个内置函数和每个内置构造函数都有 Function 原型对象,其为表达式 Function.prototype (15.3.4) 的初始值,作为其 [[Prototype]] 内部属性的值。

除非另有规定,否则每个内置原型对象都有 Object 原型对象,其为表达式 Object.prototype (15.2.4) 的初始值,作为其 [[Prototype]] 内部属性的值,但不包括 Object 原型对象本身。

因此,在控制台中,我们可以执行以下代码:

Number.prototype.__proto__.isPrototypeOf(Function)

结果是true

因此,让Number可以访问Function.prototype的方法是合理的。

同时,如果我们将Object.prototype扩展为Object.prototype.say = 'hello';,我们也可以通过Function.say访问属性say

因为Function.prototype.isPrototype(Object)true


0

这是否证明了你能够清楚地看到明显的区别呢? 在此输入图像描述

[**

在此输入图像描述 **]2


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