简体   繁体   English

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

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

From the API properties are defined like Function.name , and methods like Function.prototype.call() . 在API中,属性定义为Function.name ,方法定义为Function.prototype.call() But in the code I still invoke them in the same way, for example: 但是在代码中,我仍然以相同的方式调用它们,例如:

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

Ie I simply put a function name(Foo), then a method/property I want to use. 即,我只是简单地输入一个函数名称(Foo),然后输入一个我想使用的方法/属性。 Then why in API do I see properties defined on Function , and methods - on Function.prototype ? 那么,为什么在API中可以看到在Function定义的属性以及在Function.prototype上定义的方法?

Short answer: 简短答案:

Functions (in general) can be shared among instances, so they go in prototype . 函数(通常)可以在实例之间共享,因此它们都包含在prototype

Properties are per-instance, so go in the constructor. 属性是按实例的,因此请进入构造函数。

Placing methods in the prototype allows you to share them among all instances, instead of duplicating them for each one. 将方法放置在prototype可让您在所有实例之间共享它们,而不必为每个实例复制它们。

For example, the behavior of Function.prototype.call does not depend on which function is called. 例如, Function.prototype.call的行为不取决于调用哪个函数。 It only needs a reference to the function (received via this argument) in order to call it later. 它仅需要引用该函数(通过this参数接收),以便以后调用它。

However, intrinsic data like name must be stored in the function object itself. 但是,诸如name类的固有数据必须存储在函数对象本身中。 It can't be stored in the prototype because each function instance has its own name . 它不能存储在prototype因为每个函数实例都有其自己的name Well, it could be store as an internal [[Name]] property, and accessed via a getter and setter defined in the prototype , but the data would still need to be stored in the function. 好的,它可以存储为内部[[Name]]属性,并可以通过prototype定义的getter和setter进行访问,但是仍然需要将数据存储在函数中。

Note there are non-method properties defined in the prototype , eg constructor . 请注意, prototype定义了一些非方法属性,例如constructor

There are actually some argument for placing the default values of immutable values (or objects that are not mutated, by contract) in the prototype for common/expected properties. 实际上,有一些论点可以将不可变值默认值 (或按合同不变的未改变对象的默认值 )放置在原型中,用于常见/预期属性。

Re-assignment of the properties will always be in the 'current this' object (well, object on which the assignment occurs). 属性的重新分配将始终在“当前此”对象(好,发生分配的对象)中。 So if properties are assigned later - in the constructor or even after that - they will be 'promoted' to properties of the actual instance and not the prototype 1 . 因此,如果稍后在构造器中甚至之后分配属性,它们将被“提升”为实际实例的属性,而不是原型1

However, since the difference (if any) is very modest, and dependent upon situation, it is common practice just to dump all the property assignments in the constructor. 但是,由于差异(如果有的话)非常小,并且取决于情况,因此通常的做法是将所有属性分配转储到构造函数中。 Per-object properties would need to be set individually anyway, regardless of the approach used. 无论使用哪种方法,都必须分别设置每个对象的属性。

Sharing mutable properties in the [prototype] can get questionable as then the property (when mutated) acts akin to a static variable; 在[原型]中共享可变属性可能会引起问题,因为该属性(在发生突变时)的作用类似于静态变量; mutating shared objects should always be done with care. 更改共享对象时应始终小心。


1 The only observable difference as to 'where' default properties are assigned is if hasOwnProperty is used. 1关于分配“默认”属性的唯一可观察到的区别是是否使用hasOwnProperty

Interesting related-ish read: Should I put default values of attributes on the prototype to save space? 有趣的相关知识读物: 我应该在原型上放置属性的默认值以节省空间吗? (And yes, I know this disagrees with my vary first sentence.) (是的,我知道这与我的第一句话有所不同。)

You have a slight misconception here. 您在这里有点误解。 You can define properties and functions (methods) on the function itself. 您可以在函数本身上定义属性和函数(方法)。 You can define properties and functions that you want the object that the function construct on the prototype. 您可以定义想要在原型上构造函数的对象的属性和函数。 It's not the same thing. 不一样

For example, the .create() method of the base Object constructor is defined as as Object.create() but the .hasOwnProperty() method of object instances is defined as Object.prototype.hasOwnProperty() . 例如,基本Object构造函数的.create()方法定义为Object.create()但对象实例的.hasOwnProperty()方法定义为Object.prototype.hasOwnProperty()

For example, if you have an object mango created from the constructor Fruit() then: 例如,如果您有一个从构造函数Fruit()创建的对象mango ,则:

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

In particular, the this inside Fruit.isFruit() refers to the function Fruit() while the this inside Fruit.prototype.weight() in the above example refers to the object mango . 特别地, this内部Fruit.isFruit()是指在功能Fruit()this内部Fruit.prototype.weight()在上面的例子中是指在对象mango

If you are used to OO programming from other languages, the difference is between static and non-static class members. 如果您习惯于使用其他语言进行OO编程,则区别在于静态类成员和非静态类成员之间的区别。

prototype properties (such as the method Foo.prototype.call ) are accessible using instances of the object, while direct properties (such as Foo.name ) are accessible through the object itself (and not its instances) like static properties. prototype属性(例如Foo.prototype.call方法)可以使用对象的实例进行访问,而直接属性(例如Foo.name )可以通过对象本身(而不是其实例)进行访问,例如静态属性。

In your example there is a big difference between Foo.name and Foo.prototype.call , in order to use Foo.name you can call it directly, while in order to use Foo.prototype.call - you need to create an instance, and then it'll be available 在您的示例中, 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

Another thing you need to notice is that both name and call has a native definition in the Function object type in JavaScript - therefore when you call Foo.call() - you call the Function.prototype.call method (which is just like calling Foo(); , with one small difference that doesn't affect in this case) 您需要注意的另一件事是, namecall在JavaScript中的Function对象类型中都具有本机定义-因此,当您调用Foo.call() ,会调用Function.prototype.call方法(就像调用Foo();只有一个小差异,在这种情况下不会影响)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM