在 ES6 中引入的 JavaScriptclass
语法中(静态属性可以从类访问,实例属性可以从实例访问):
- 静态字段属于类;
- 静态方法属于类; 实例
- 方法属于实例的原型;
this
属性属于实例。
因此,我希望实例字段将属于实例的原型,例如实例方法。但事实并非如此,它们得到了特殊处理并属于实例,例如this
属性。
例如,类
class A {
static x; // static field
static f() {} // static method
y; // instance field (special treatment)
g() {} // instance method
constructor() {this.z = 0} // this property
}
给出结果
> Object.getOwnPropertyNames(A)
[ 'length', 'name', 'prototype', 'f', 'x' ]
> Object.getOwnPropertyNames(A.prototype)
[ 'constructor', 'g' ]
> Object.getOwnPropertyNames(new A)
[ 'y', 'z' ]
为什么实例字段属于实例而不是其原型?
我看到这种特殊处理的一个缺点是,它可以防止人们简单地在实例的原型中声明非函数属性,迫使人们在类声明后执行此操作(或诉诸静态初始化块):
> A.prototype.y2 = 0;
0
> Object.getOwnPropertyNames(A.prototype)
[ 'constructor', 'g', 'y2' ]
另一个缺点是,如果要在实例上声明属性,则已经有this
,因此它复制了现有的语言功能。
在添加公共实例字段之前,这些属性始终属于实例(除了一些相当模糊的用法)。添加后,他们继续将这些属性存储在实例中。
在 Axel Rauschmayer 博士本文的第 2.1 节中,它显示了添加公共实例字段的动机。
有时,构造函数中有一个赋值,用于创建实例属性,但不受构造函数中任何其他数据(如参数)的影响
class MyClass {
constructor() {
this.counter = 0;
}
}
在这种情况下,您可以使用字段将计数器的创建移出构造函数:
class MyClass {
counter = 0;
constructor() {
}
}
因此,添加公共实例可以简化构造函数。它还清楚地表明,属性的初始值不依赖于构造函数的参数。(它还可能有助于可读性并使其更容易文档)
还有一个语义上的差异,因为类字段使用[[DefineProperty]]
语义(具有与Object.defineProperty
匹配的行为),因此对于公共字段,不会调用超类中的setter。
比较:
class A {
set prop(value) {
console.log('SETTER: '+value);
}
}
class B extends A {
constructor() {
super();
this.prop = 123; // (setter will be called, inherited from A)
}
}
console.log(new B); //does not contain a 'prop' field because there is a setter
跟
class A {
set prop(value) {
console.log('SETTER: '+value);
}
}
class B extends A {
prop = 123; //setter from A is not called
}
console.log(new B);
至于为什么这些属性属于实例而不是原型(也适用于class
es 在 JS 中成为事物之前的日子):
如果原型中有一个属性,一旦你分配了(比如说),instance1.property1 = 3
,原型中的值将不再使用(属性阴影);在实例中创建一个新属性,并且原型中的值不会更改。因此,在原型中放置值并不是很有用。
相比之下,方法通常不会重新分配,在实例之间共享很有用。instance1.method1(...)
和instance2.method1(...)
调用具有不同this
的相同函数。(所以这些不是彼此的别名,因为它们具有不同的效果)。
class
语法只是存在事物的语法糖:
prototype
- 一个对象,在实例之间共享内容,主要是方法和getset描述符constructor
- 使用预定义原型制作对象的函数- 使用
constructor
作为命名空间来保留相关内容 - 领域?什么领域?你只有一个带有原型的对象,用它做任何你想 做的事情
class
语法包括所有这些...除了原型上的常量 - 它们有非常奇怪的行为(实际上是属性初始值设定项),没有人真正使用过它们