为什么函数属性定义在对象本身上(例如Function.name),而方法定义在Function.prototype上?

Why do Function properties are defined on the object itself (e.g. Function.name), but methods - on Function.prototype?

本文关键字:Function 定义 name 方法 prototype 属性 函数 对象 为什么 例如      更新时间:2023-09-26

从API定义属性如Function.name,和方法如Function.prototype.call()。但在代码中,我仍然以相同的方式调用它们,例如:

function Foo() {console.log("inside Foo")}
console.log(Foo.name);  // Foo
Foo.call();             // inside Foo

。我只是简单地输入一个函数名(Foo),然后是我想要使用的方法/属性。那么为什么在API中,我看到Function上定义的属性和Function.prototype上定义的方法?

将方法放在prototype中允许您在所有实例中共享它们,而不是为每个实例复制它们。

例如,Function.prototype.call的行为不依赖于调用哪个函数。它只需要一个对函数的引用(通过this参数接收),以便以后调用它。

但是,像name这样的内部数据必须存储在函数对象本身中。它不能存储在prototype中,因为每个函数实例都有自己的name。它可以作为内部[[Name]]属性存储,并通过prototype中定义的getter和setter来访问,但是数据仍然需要存储在函数中。

注意prototype中定义了非方法属性,例如constructor

简短回答:

函数(通常)可以在实例之间共享,所以它们在prototype中。

prototype属性(例如方法Foo.prototype.call)可以使用对象的实例访问,而直接属性(例如Foo.name)可以通过对象本身(而不是其实例)访问,例如static属性。

在你的例子中,Foo.nameFoo.prototype.call之间有很大的区别,为了使用Foo.name,你可以直接调用它,而为了使用Foo.prototype.call,你需要创建一个实例,然后它就可用了

function Foo(){}
Foo.prototype.call = funciton(){console.log('I was called');}
Foo.name = 'My name is';
console.log(Foo.name); //My name is
var instance = new Foo();
instances.call(); //I was called

你需要注意的另一件事是,namecall在JavaScript中的Function对象类型中都有一个本地定义-因此,当你调用Foo.call()时,你调用Function.prototype.call方法(就像调用Foo();一样,有一个小的区别,在这种情况下没有影响)

对于将不可变值默认值(或根据约定不发生变化的对象)放置在通用/预期属性的原型中,实际上存在一些争论。

属性的重新赋值将始终在"当前this"对象中(好吧,发生赋值的对象)。因此,如果属性稍后被赋值——在构造函数中甚至之后——它们将被"提升"为实际实例的属性,而不是原型1

然而,由于差异(如果有的话)非常小,并且取决于情况,因此通常的做法是只是在构造函数中转储所有属性赋值。无论使用什么方法,每个对象的属性都需要单独设置。

在[prototype]中共享可变属性可能会受到质疑,因为该属性(当发生突变时)的行为类似于静态变量;对共享对象的修改应该非常小心。


1唯一可观察到的区别是'where'默认属性被分配,如果使用hasOwnProperty

有趣的相关阅读:我应该在原型上设置属性的默认值以节省空间吗?(是的,我知道这与我的第一句话不一致。)

这里你有一个小小的误解。您可以在函数本身上定义属性和函数(方法)。你可以定义你想要的属性和函数在原型上构造的对象。这不是一回事。

例如,基本Object构造函数的.create()方法被定义为Object.create(),而对象实例的.hasOwnProperty()方法被定义为Object.prototype.hasOwnProperty()

例如,如果从构造函数Fruit()创建了一个对象mango,那么:

mango.weight(); // method comes from Fruit.prototype.weight()
Fruit.isFruit(mango); // method comes from Fruit.isFruit()

具体来说,Fruit.isFruit()里面的this指的是函数Fruit(),上例中Fruit.prototype.weight()里面的this指的是对象mango

如果您习惯于从其他语言进行OO编程,则静态和非静态类成员之间的区别。