为什么实例字段属于实例而不是其原型?



在 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);


至于为什么这些属性属于实例而不是原型(也适用于classes 在 JS 中成为事物之前的日子):

如果原型中有一个属性,一旦你分配了(比如说),instance1.property1 = 3,原型中的值将不再使用(属性阴影);在实例中创建一个新属性,并且原型中的值不会更改。因此,在原型中放置值并不是很有用。

相比之下,方法通常不会重新分配,在实例之间共享很有用。instance1.method1(...)instance2.method1(...)调用具有不同this的相同函数。(所以这些不是彼此的别名,因为它们具有不同的效果)。

class语法只是存在事物的语法糖:

  1. prototype- 一个对象,在实例之间共享内容,主要是方法和getset描述符
  2. constructor- 使用预定义原型制作对象的函数
  3. 使用constructor作为命名空间来保留相关内容
  4. 领域?什么领域?你只有一个带有原型的对象,用它做任何你想
  5. 做的事情

class语法包括所有这些...除了原型上的常量 - 它们有非常奇怪的行为(实际上是属性初始值设定项),没有人真正使用过它们

相关内容

  • 没有找到相关文章

最新更新