JavaScript 私人会员 - 没有第 3 阶段预设
JavaScript private members - Without stage 3 presets
我们在 Java 和许多其他 Object 面向编程语言中有私有成员。因此,问题的标题很明确:我们如何在 JavaScript 类 中拥有私有成员?
让我们考虑以下示例:
// PrivateMemberOwner.js
export default class PrivateMemberOwner {
iAmPrivate () {
console.log("I can not be accessed from Foo class' children!");
}
iAmPublic () {
console.log("Anyone can call me from anywhere!");
}
}
然后在我的 PrivateMemberOwnerExtension.js
:
import PrivateMemberOwner from "./PrivateMemberOwner.js";
export default class PrivateMemberOwnerExtension extends PrivateMemberOwner {
consumer () {
// This one should work just fine:
Foo.iAmPublic();
// This one should throw some sort of error
Foo.iAmPrivate();
}
}
正确的处理方法是什么?
在我看来,在JavaScript中拥有私有成员应该满足这些条件:
- 能够在
PrivateMemberOwner
class 内调用 iAmPrivate
。 (如问题所述)
- 无法在 class 定义之外的任何地方调用
iAmPrivate
。 (至少不能不小心调用)
- 该机制应利用 JavaScript 的原型继承功能以消耗更少的内存。
话虽如此,我想到了 ES6 中引入的新 Symbol
类型。
Symbols are a primitive type introduced in ECMAScript 6, joining the existing primitive types: strings, numbers, booleans, null
, and undefined
.
(来自 Understanding ECMAScript 6 - 作者 Nicholas C. Zakas)
所以,让我们来看看代码:
// PrivateMemberOwner.js
const iAmPrivate = Symbol("iAmPrivate");
export default class PrivateMemberOwner {
[iAmPrivate] (message) {
console.log("I can not be accessed from Foo class' children!", message);
}
iAmPublic () {
console.log("Anyone can call me from anywhere!");
}
iAmPrivateUser () {
this[iAmPrivate]("I can access 'iAmPrivate' just fine.");
}
}
这样,我们可以让iAmPrivateUser
方法访问iAmPrivate
方法,让PrivateMemberOwner
class的children无法访问它。 (至少是偶然访问到的)
备注
如上所述,这类成员不是完全私有的,[需要一些努力]可以像这样访问成员:
// ./PrivateReader.js
import PrivateMemberOwner from "./PrivateMemberOwner.js";
const privateMemberOwner = new PrivateMemberOwner();
const privateMemberOwnerSymbols = Object.getOwnPropertySymbols(privateMemberOwner);
const iWasPrivate = privateMemberOwner[privateMemberOwnerSymbols[0]];
iWasPrivate.call(privateMemberOwner, 'I just gained your access!');
私有属性也可以用同样的方法实现:
const barSymbol = Symbol('bar');
class Foo {
constructor () {
this[barSymbol] = null;
}
getBar () {
return this[barSymbol];
}
setBar (value) {
this[barSymbol] = value;
}
}
我们在 Java 和许多其他 Object 面向编程语言中有私有成员。因此,问题的标题很明确:我们如何在 JavaScript 类 中拥有私有成员?
让我们考虑以下示例:
// PrivateMemberOwner.js
export default class PrivateMemberOwner {
iAmPrivate () {
console.log("I can not be accessed from Foo class' children!");
}
iAmPublic () {
console.log("Anyone can call me from anywhere!");
}
}
然后在我的 PrivateMemberOwnerExtension.js
:
import PrivateMemberOwner from "./PrivateMemberOwner.js";
export default class PrivateMemberOwnerExtension extends PrivateMemberOwner {
consumer () {
// This one should work just fine:
Foo.iAmPublic();
// This one should throw some sort of error
Foo.iAmPrivate();
}
}
正确的处理方法是什么?
在我看来,在JavaScript中拥有私有成员应该满足这些条件:
- 能够在
PrivateMemberOwner
class 内调用iAmPrivate
。 (如问题所述) - 无法在 class 定义之外的任何地方调用
iAmPrivate
。 (至少不能不小心调用) - 该机制应利用 JavaScript 的原型继承功能以消耗更少的内存。
话虽如此,我想到了 ES6 中引入的新 Symbol
类型。
Symbols are a primitive type introduced in ECMAScript 6, joining the existing primitive types: strings, numbers, booleans,
null
, andundefined
.
(来自 Understanding ECMAScript 6 - 作者 Nicholas C. Zakas)
所以,让我们来看看代码:
// PrivateMemberOwner.js
const iAmPrivate = Symbol("iAmPrivate");
export default class PrivateMemberOwner {
[iAmPrivate] (message) {
console.log("I can not be accessed from Foo class' children!", message);
}
iAmPublic () {
console.log("Anyone can call me from anywhere!");
}
iAmPrivateUser () {
this[iAmPrivate]("I can access 'iAmPrivate' just fine.");
}
}
这样,我们可以让iAmPrivateUser
方法访问iAmPrivate
方法,让PrivateMemberOwner
class的children无法访问它。 (至少是偶然访问到的)
备注
如上所述,这类成员不是完全私有的,[需要一些努力]可以像这样访问成员:
// ./PrivateReader.js import PrivateMemberOwner from "./PrivateMemberOwner.js"; const privateMemberOwner = new PrivateMemberOwner(); const privateMemberOwnerSymbols = Object.getOwnPropertySymbols(privateMemberOwner); const iWasPrivate = privateMemberOwner[privateMemberOwnerSymbols[0]]; iWasPrivate.call(privateMemberOwner, 'I just gained your access!');
私有属性也可以用同样的方法实现:
const barSymbol = Symbol('bar'); class Foo { constructor () { this[barSymbol] = null; } getBar () { return this[barSymbol]; } setBar (value) { this[barSymbol] = value; } }