Babel.js 无法在ES6类定义中定义原型属性

t0ybt7op  于 2022-12-08  发布在  Babel
关注(0)|答案(6)|浏览(184)

我尝试ES6语法,发现我不能在类定义中定义原型属性或示例属性,为什么禁止它?
我之前用的是MyClass.prototype.prop=1,尝试ES7的babel编译器如下,还是不能定义prototype属性。

class MyClass{
  prop=1;
  static sProp=1;
}

我不认为定义示例属性有任何危险,在我自己的浏览器游戏中有2种情况需要原型属性:
1.子类示例需要从基类继承相同的属性值:

var Building=function(){...}
Building.prototype.sight=350;
TerranBuilding.CommandCenter=...(CommandCenter extends Building)
TerranBuilding.Barracks=...(Barracks extends Building)

所以指挥中心和兵营都将有与350相同的建筑视线。

new CommandCenter().sight===new Barracks().sight//All buildings have same sight

1.缓冲效果覆盖原始属性并删除缓冲

Marine.prototype.speed=20
var unit=new Marine()
unit.speed===20//get unit.__proto__.speed 20
unit.speed=5//Buffer:slow down speed, unit.speed will override unit.__proto__.speed
delete unit.speed//Remove buffer
unit.speed===20//true, speed restore

所以我认为应该增加一个设置prototype属性的方法而不是完全禁止它,或者你能给予一些其他的解决方案来处理以上两种情况吗?

ktca8awb

ktca8awb1#

这两个都不会出现在类别原型上。
class Foo { bar = 1; }语法会将值指派给类别执行严修,以this.bar存取。
class Foo { static bar = 1; }语法会将值指派给类别建构函式,以Foo.bar存取。
在这种情况下,没有太多理由使用原型,它只会使谁真正拥有属性变得复杂,并且在几个不同的类中分配一个数字的开销非常小。
我建议使用类示例属性,并在任何需要的地方使用this.sight

dtcbnfnu

dtcbnfnu2#

更新答案(2022年4月)

就在我上次答复的两个月后,也就是2021年8月,TC-39委员会将静态块提案移至第4阶段。参见完整的已完成提案非正式列表here
对于那些希望获得Javascript中静态块的用例摘要的人,请阅读V8博客中2021年3月发布的初始出版物,在它们实现之后。
另请参阅MDN文件以取得静态初始化区块。
虽然大多数更新的浏览器现在都支持此功能,但如果您真的想支持Internet Explorer,请阅读下面的内容。

原始答案

下面是我在javascript中遵循的典型模式。
它反映了java使用的静态块风格。现在有一个Stage 3 Proposal为此开放,我希望它在不久的将来会被标准化(这与TC-39委员会第三阶段的提案期望一致)。

提案的外观

class MyClass {
    static {
        // Any code here is executed directly after the initialization
        // of MyClass. You can add prototype stuff here. The function
        // is called bound to `MyClass`.
    }
}

这可以在今天使用静态生命来完成
它们的功能完全相同。

class MyClass {
    // Using private properties is not required, it is just an option. Make
    // sure to use an arrow function so that `this` refers to `MyClass`,
    // Note that `MyClass` will still be in the functions closure.
    static #_ = (() => {
        // 'Almost' how functions are typically added. ES6 style
        // is always recommended over this.
        this.prototype.myFunc = function myFunc() {
            console.log(":D");
        };

        // ES6 would actually do this (approximately) so that the function is
        // non-enumerable in the prototype.
        Reflect.defineProperty(this.prototype, "myFunc", {
            // enumerable: false,  // defaults 'false'
            writable: true,
            configurable: true,

            // I'm intentionally not using the shorthand for the function
            // so that it is named 'myFunc'.
            value: function myFunc() {
                console.log(":D");
            }
        });

        // Note that all children of MyClass will refer to this exact
        // object if put in the prototype, i.e. not a copy of it.
        // Also, this property will be non-enumerable on the children
        // (but enumerable on the prototype itself unless you
        // use `defineProperty` as above).
        this.prototype.sharedProperty = { name: "Gerald" };
    })();
}
yvt65v4c

yvt65v4c3#

将属性添加到类体 * 内的原型 * 的最简单方法是使用原型赋值作为伪静态属性的“值”:

class MyClass {
    static _dummy = MyClass.prototype.prop1 = <expression1>
    static _dummy = MyClass.prototype.prop2 = <expression2>
    // or
    static _dummy = this.prototype.prop2 = <expression2>
}

(it不使用括号,因为=是右关联的,可以对每个原型赋值重复使用相同的伪属性)
如果你想对值进行更有趣的(多行)计算,初始化器可以是一个立即执行的函数表达式,在这种情况下,你基本上已经创建了一个static constructor,你可以把原型和类对象的所有初始化都放在其中。

g0czyy6m

g0czyy6m4#

我认为另一个答案没有理解这个问题的要点。拥有继承的全部要点是你可以决定何时何地重写某些东西。如果有人认为这是一个开销,你为什么要使用OOP语言呢?
我不知道为什么它是“禁止的”,但我可以分享一些想法。我很确定没有办法用'class'关键字定义一个 Prototype 属性。任何定义都将安装在“hasOwnProperty”上。一个巨大的挫折是在构造函数中,没有办法让任何父代的构造函数与一个被覆盖的属性交互。
就推理而言,它实际上是被另一个特性所驱逐的:您可以使用表达式将属性分配给***this***。

class A extends B { sight = this.getSight() * 3 }

当一个表达式出现借口时,它要么运行于 instance(用构造函数创建),要么运行于类声明(当创建 prototype 时)。
访问器和方法没有这个问题。它们在 * 原型定义时 * 定义,在 * 示例运行时 * 调用。
用表达式定义的属性(带有“=”)是表达式的返回值。它在定义后立即被免除-应该是 * 示例创建时间 *,否则 * 此 * 将不可用。
所以这与模式无关,这与表达式或继承有关,我更喜欢继承,表达式是如此的没有意义,当你可以直接把它们写进构造函数中。

class A extends B { constructor() { this.sight = this.getSight() * 3 }

使用装饰器是一个很好的解决办法。你可以用javascript中的原型做一些事情:

@B({sight:2}) class A {};

装饰者B是:

function(option) {return function(clazz) {clazz.prototype.sight = option.sight; return clazz}}
nbewdwxp

nbewdwxp5#

使用 *Class静态初始化块 *1:

class MyClass {
  static {
    this.prototype.prop = 1;
  }
}

console.log(MyClass.prototype.prop); // 1
const instance = new MyClass();
console.log(instance.__proto__.prop); // 1

this与静态块中的1:
静态区块内的this会指涉类别的建构函式对象。
静态区块内的程式码只会在评估类别初始化时执行一次。
注:Safari自2022年6月起不支持此功能。您可以在mdn web docs1上查看最新信息。
[1][https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_static_initialization_blocks](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_static_initialization_blocks)

0dxa2lsx

0dxa2lsx6#

class MyClass {
   constructor() {
    MyClass.prototype.prop2 = "Marry";
  }
}

const mc = new MyClass()
mc.__proto__ // { prop2: "Marry" }

相关问题