如何在对象文字或类中定义生成器属性?



JavaScript在语法上支持对象字面值和类中的属性getter:

var stuff = {
    get things() { return ['thimbles', 'care', 'forks', 'hope']; },
};
class Foo {
    get things() { return ['thimbles', 'care', 'forks', 'hope']; }
};
// usage
[ ...stuff.things ]
var foo = new Foo;
for (let thing of foo.things) {
   // …
}

它还具有对生成器方法的语法支持:

var stuff = {
   *things() { yield* ['thimbles', 'care', 'forks', 'hope']; },
};
class Foo {
    *things() { return ['thimbles', 'care', 'forks', 'hope']; }
};
// usage
[ ...stuff.things() ]
var foo = new Foo;
for (let thing of foo.things()) {
   // …
}

如何将它们组合起来,为对象或类定义生成器属性 ?作为不工作的示例,以明显的方式组合get语法和generator方法语法会产生语法错误:

// generates "SyntaxError: Unexpected token '*'"
var stuff = {
    get* things() {
        yield* ['thimbles', 'care', 'forks', 'hope'];
    },
};
// generates "SyntaxError: Unexpected token '*'"
class Foo {
    get *things() {
        yield* ['thimbles', 'care', 'forks', 'hope'];
    }
};
// generates "SyntaxError: Unexpected identifier 'things'"
stuff = {
    *get things() {
        yield* ['thimbles', 'care', 'forks', 'hope'];
    },
};
// usage
[ ...stuff.things ];
var foo = new Foo;
for (let thing of foo.things) {
   // …
}

注意:虽然在示例中使用了静态数组,但实际用例将用于生成序列,例如DOM查询的结果;不使用发电机不是解决办法。类似地,虽然可以使用生成器方法代替属性,但计算属性的参数相同(例如,方法会泄漏抽象)。

查看最新的JavaScript语法(来自ES2022),对象字面量和类的getter和生成器方法都来自方法定义规则。派生:

  • ObjectLiteralPropertyDefinitionListPropertyDefinitionMethodDefinition
  • ClassDeclaration, ClassExpressionClassTailClassBodyClassElementListClassElementMethodDefinition

MethodDefinition, getter和生成器方法匹配不同的结果:

MethodDefinition:
    GeneratorMethod
    AsyncGeneratorMethod
    "get" ClassElementName "()" "{" FunctionBody "}"
    …

还有其他规则,但没有一个匹配getter或生成器。

GeneratorMethod进一步展开为:

GeneratorMethod:
    "*" ClassElementName "(" UniqueFormalParameters ")" "{"  GeneratorBody "}"

ClassElementName基本上是一个标识符,因此不会匹配任何标记getter或生成器的终端。

由此看来,在对象字面量或类中定义生成器属性在语法上是不可能的。可以使用Object.defineProperty:

向对象添加生成器属性
var stuff = {};
Object.defineProperty(stuff, 'things', {
    get: function*() { yield* ['thimbles', 'care', 'forks', 'hope']; },
    enumerable: true,
});
class Foo {
}
Object.defineProperty(Foo.prototype, 'things', {
    get: function*() { yield* ['thimbles', 'care', 'forks', 'hope']; },
    enumerable: true,
});

与意图更接近一点,生成器方法可以在对象文字或类中定义,然后转换为属性。

var stuff = {
    *things() {
        yield* ['thimbles', 'care', 'forks', 'hope'];
    },
};
Object.defineProperty(stuff, 'things', {
    get: stuff.things,
    enumerable: true,
});
[ ...stuff.things ];
class Foo {
    *things() {
        yield* ['thimbles', 'care', 'forks', 'hope'];
    }
}
Object.defineProperty(Foo.prototype, 'things', {
    get: Foo.prototype.things,
    enumerable: true,
});
var foo = new Foo;
for (let thing of foo.things) {
    // …
}

对于静态类属性,引用类本身的属性(例如Foo),而不是类的原型(例如Foo.prototype)。

最新更新