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和生成器方法都来自方法定义规则。派生:
-
ObjectLiteral
→PropertyDefinitionList
→PropertyDefinition
→MethodDefinition
-
ClassDeclaration
,ClassExpression
→ClassTail
→ClassBody
→ClassElementList
→ClassElement
→MethodDefinition
从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
)。