Private properties in JavaScript ES6 classes
是否可以在ES6类中创建私有属性?
这是一个例子。
如何阻止访问
1 2 3 4 5 6 7 8 | class Something { constructor(){ this.property ="test"; } } var instance = new Something(); console.log(instance.property); //=>"test" |
简短的回答,不,没有ES6类私有属性的原生支持。
但是,您可以通过不将新属性附加到对象,但将它们保留在类构造函数中,并使用getter和setter来到达隐藏属性来模仿该行为。请注意,getter和setter会在类的每个新实例上重新定义。
ES6
1 2 3 4 5 6 7 | class Person { constructor(name) { var _name = name this.setName = function(name) { _name = name; } this.getName = function() { return _name; } } } |
ES5
1 2 3 4 5 | function Person(name) { var _name = name this.setName = function(name) { _name = name; } this.getName = function() { return _name; } } |
要扩展@ loganfsmyth的答案:
JavaScript中唯一真正的私有数据仍然是作用域变量。在内部访问的属性方面,您不能拥有与公共属性相同的私有属性,但您可以使用范围变量来存储私有数据。
范围变量
这里的方法是使用私有的构造函数的范围来存储私有数据。对于可以访问此私有数据的方法,它们也必须在构造函数中创建,这意味着您将使用每个实例重新创建它们。这是一种表现和记忆惩罚,但有些人认为惩罚是可以接受的。对于不需要访问私有数据的方法,可以通过像往常一样将它们添加到原型来避免惩罚。
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function Person(name) { let age = 20; // this is private this.name = name; // this is public this.greet = function () { // here we can access both name and age console.log(`name: ${this.name}, age: ${age}`); }; } let joe = new Person('Joe'); joe.greet(); // here we can access name but not age |
Scoped WeakMap
可以使用WeakMap来避免先前方法的性能和内存损失。 WeakMaps将数据与对象(此处为实例)相关联,使得只能使用该WeakMap访问它。因此,我们使用范围变量方法创建私有WeakMap,然后使用该WeakMap检索与
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | let Person = (function () { let privateProps = new WeakMap(); class Person { constructor(name) { this.name = name; // this is public privateProps.set(this, {age: 20}); // this is private } greet() { // Here we can access both name and age console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`); } } return Person; })(); let joe = new Person('Joe'); joe.greet(); // here we can access joe's name but not age |
此示例使用Object将一个WeakMap用于多个私有属性;你也可以使用多个WeakMaps并像
理论上,通过篡改全局
(这种方法也可以用
半答案:Scoped符号
符号是一种可以作为属性名称的原始值。您可以使用范围变量方法创建私有Symbol,然后将私有数据存储在
使用
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | let Person = (function () { let ageKey = Symbol(); class Person { constructor(name) { this.name = name; // this is public this[ageKey] = 20; // this is intended to be private } greet() { // Here we can access both name and age console.log(`name: ${this.name}, age: ${this[ageKey]}`); } } return Person; })(); let joe = new Person('Joe'); joe.greet(); // Here we can access joe's name and, with a little effort, age. ageKey is // not in scope, but we can obtain it by listing all Symbol properties on // joe with `Object.getOwnPropertySymbols(joe)`. |
半答案:下划线
旧的默认值,只需使用带下划线前缀的公共属性。虽然这不是一个私人财产,但这种惯例非常普遍,以至于它能够很好地传达读者应该将财产视为私有财产,这通常可以完成工作。为了换取这种失误,我们得到了一种更容易阅读,更容易打字和更快速的方法。
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Person { constructor(name) { this.name = name; // this is public this._age = 20; // this is intended to be private } greet() { // Here we can access both name and age console.log(`name: ${this.name}, age: ${this._age}`); } } let joe = new Person('Joe'); joe.greet(); // Here we can access both joe's name and age. But we know we aren't // supposed to access his age, which just might stop us. |
结论
从ES2017开始,仍然没有完美的私有财产方式。各种方法都有利有弊。范围变量是真正的私有;作用域WeakMaps非常私密,比范围变量更实用;范围符号是相当私密和合理实用的;下划线通常足够私密且非常实用。
更新:具有更好语法的提案即将推出。欢迎捐款。
是的,对于对象中的作用域访问 - ES6引入了
符号是唯一的,你不能从外部访问一个除了反射(如Java / C#中的私有),但任何有权访问内部符号的人都可以使用它来进行密钥访问:
1 2 3 4 5 6 7 8 9 10 | var property = Symbol(); class Something { constructor(){ this[property] ="test"; } } var instance = new Something(); console.log(instance.property); //=> undefined, can only access with access to the Symbol |
私有领域正在ECMA标准中实施。您现在可以使用babel 7和第3阶段预设开始使用它们。请参阅babel REPL示例。
1 2 3 4 5 6 7 8 9 10 | class Something { #property; constructor(){ this.#property ="test"; } } const instance = new Something(); console.log(instance.property); //=> undefined |
答案是不"。但您可以创建对此属性的私有访问权限:
-
使用模块。除非使用
export 关键字公开,否则模块中的所有内容都是私有的。 - 在模块内部,使用函数闭包:http://www.kirupa.com/html5/closures_in_javascript.htm
(在早期版本的ES6规范中,可以使用符号来确保隐私的建议是正确的,但不再是这种情况:https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604。 html和https://stackoverflow.com/a/22280202/1282216。有关符号和隐私的更长时间的讨论,请参阅:https://curiosity-driven.org/private-properties-in-javascript)
在JS中获得真正隐私的唯一方法是通过作用域,因此无法获得只能在组件内部访问的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const privateProp1 = new WeakMap(); const privateProp2 = new WeakMap(); class SomeClass { constructor() { privateProp1.set(this,"I am Private1"); privateProp2.set(this,"I am Private2"); this.publicVar ="I am public"; this.publicMethod = () => { console.log(privateProp1.get(this), privateProp2.get(this)) }; } printPrivate() { console.log(privateProp1.get(this)); } } |
显然这可能很慢,而且绝对是丑陋的,但确实提供了隐私。
请记住,即使这不是完美的,因为Javascript是如此动态。有人还可以
1 2 3 4 5 | var oldSet = WeakMap.prototype.set; WeakMap.prototype.set = function(key, value){ // Store 'this', 'key', and 'value' return oldSet.call(this, key, value); }; |
在存储时捕获值,因此如果您想要格外小心,则需要捕获对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | const {set: WMSet, get: WMGet} = WeakMap.prototype; const privateProp1 = new WeakMap(); const privateProp2 = new WeakMap(); class SomeClass { constructor() { WMSet.call(privateProp1, this,"I am Private1"); WMSet.call(privateProp2, this,"I am Private2"); this.publicVar ="I am public"; this.publicMethod = () => { console.log(WMGet.call(privateProp1, this), WMGet.call(privateProp2, this)) }; } printPrivate() { console.log(WMGet.call(privateProp1, this)); } } |
为了将来对其他观众的参考,我现在听说建议使用WeakMaps来保存私人数据。
这是一个更清晰,更有效的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | function storePrivateProperties(a, b, c, d) { let privateData = new WeakMap; // unique object as key, weak map can only accept object as key, when key is no longer referened, garbage collector claims the key-value let keyA = {}, keyB = {}, keyC = {}, keyD = {}; privateData.set(keyA, a); privateData.set(keyB, b); privateData.set(keyC, c); privateData.set(keyD, d); return { logPrivateKey(key) { switch(key) { case"a": console.log(privateData.get(keyA)); break; case"b": console.log(privateData.get(keyB)); break; case"c": console.log(privateData.get(keyC)); break; case"d": console.log(privateData.set(keyD)); break; default: console.log(`There is no value for ${key}`) } } } } |
取决于你问谁:-)
没有
但是,可能会支持私有名称,它允许私有属性 - 它们也可能在类定义中使用。
使用ES6模块(最初由@ d13提出)对我来说效果很好。它并不能完美地模仿私有财产,但至少你可以确信应该是私人的财产不会泄漏到你的班级之外。这是一个例子:
something.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | let _message = null; const _greet = name => { console.log('Hello ' + name); }; export default class Something { constructor(message) { _message = message; } say() { console.log(_message); _greet('Bob'); } }; |
然后消费代码可能如下所示:
1 2 3 4 5 6 | import Something from './something.js'; const something = new Something('Sunny day!'); something.say(); something._message; // undefined something._greet(); // exception |
更新(重要):
正如@DanyalAytekin在评论中概述的那样,这些私有属性是静态的,因此在全局范围内。在与Singletons合作时,它们会很好用,但必须注意Transient对象。扩展上面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import Something from './something.js'; import Something2 from './something.js'; const a = new Something('a'); a.say(); // a const b = new Something('b'); b.say(); // b const c = new Something2('c'); c.say(); // c a.say(); // c b.say(); // c c.say(); // c |
完成@ d13以及@ johnny-oshika和@DanyalAytekin的评论:
我想在@ johnny-oshika提供的示例中,我们可以使用普通函数而不是箭头函数,然后使用当前对象
something.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function _greet(_privates) { return 'Hello ' + _privates.message; } function _updateMessage(_privates, newMessage) { _privates.message = newMessage; } export default class Something { constructor(message) { const _privates = { message }; this.say = _greet.bind(this, _privates); this.updateMessage = _updateMessage.bind(this, _privates); } } |
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import Something from './something.js'; const something = new Something('Sunny day!'); const message1 = something.say(); something.updateMessage('Cloudy day!'); const message2 = something.say(); console.log(message1 === 'Hello Sunny day!'); // true console.log(message2 === 'Hello Cloudy day!'); // true // the followings are not public console.log(something._greet === undefined); // true console.log(something._privates === undefined); // true console.log(something._updateMessage === undefined); // true // another instance which doesn't share the _privates const something2 = new Something('another Sunny day!'); const message3 = something2.say(); console.log(message3 === 'Hello another Sunny day!'); // true |
我能想到的好处:
-
我们可以有私有方法(
_greet 和_updateMessage 就像私有方法一样,只要我们不export 引用) - 虽然它们不在原型上,但上面提到的方法将节省内存,因为实例在类之外创建一次(而不是在构造函数中定义它们)
- 因为我们在模块中,所以我们不会泄漏任何全局变量
-
我们也可以使用绑定的
_privates 对象拥有私有属性
我能想到的一些缺点:
- 不太直观
- 类语法和旧学校模式的混合使用(对象绑定,模块/函数范围变量)
- 硬绑定 - 我们无法重新绑定公共方法(虽然我们可以通过使用软绑定来改进它(https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26% 20object%20prototypes / ch2.md#软化结合))
可以在此处找到正在运行的代码段:http://www.webpackbin.com/NJgI5J8lZ
是的 - 您可以创建封装属性,但是使用访问修饰符(public | private)至少不能使用ES6。
这是一个如何使用ES6完成的简单示例:
1使用类词创建类
2在它的构造函数中使用let OR const保留字来声明块范围的变量 - >因为它们是块范围,所以它们不能从外部访问(封装)
3要允许对这些变量进行某些访问控制(setter | getters),您可以使用以下命令在其构造函数中声明实例方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | "use strict"; class Something{ constructor(){ //private property let property="test"; //private final (immutable) property const property2="test2"; //public getter this.getProperty2=function(){ return property2; } //public getter this.getProperty=function(){ return property; } //public setter this.setProperty=function(prop){ property=prop; } } } |
现在让我们检查一下:
1 2 3 4 5 | var s=new Something(); console.log(typeof s.property);//undefined s.setProperty("another");//set to encapsulated `property` console.log(s.getProperty());//get encapsulated `property` value console.log(s.getProperty2());//get encapsulated immutable `property2` value |
一种不同的"私人"方法
如果你的IDE支持JSDoc(例如,Webstorm),我决定采用一种更实用的方法,而不是反对ES6中目前无法提供私有可见性这一事实。我们的想法是使用
WeakMap
- IE11支持(符号不是)
-
hard-private(由于
Object.getOwnPropertySymbols ,使用符号的道具是软私密的) - 可以看起来非常干净(不像需要构造函数中的所有道具和方法的闭包)
首先,定义一个包装WeakMap的函数:
1 2 3 4 5 6 7 8 9 10 11 | function Private() { const map = new WeakMap(); return obj => { let props = map.get(obj); if (!props) { props = {}; map.set(obj, props); } return props; }; } |
然后,在你的课外构建一个引用:
1 2 3 4 5 6 7 8 9 10 11 12 | const p = new Private(); class Person { constructor(name, age) { this.name = name; p(this).age = age; // it's easy to set a private variable } getAge() { return p(this).age; // and get a private variable } } |
注意:IE11不支持类,但在示例中看起来更干净。
我认为Benjamin的答案可能是大多数情况下最好的,直到语言本身支持显式私有变量。
但是,如果由于某种原因你需要阻止使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | const privateVars = {}; class Something { constructor(){ Object.defineProperty(this, '_sym', { configurable: false, enumerable: false, writable: false, value: Symbol() }); var myPrivateVars = { privateProperty:"I'm hidden" }; privateVars[this._sym] = myPrivateVars; this.property ="I'm public"; } getPrivateProperty() { return privateVars[this._sym].privateProperty; } // A clean up method of some kind is necessary since the // variables won't be cleaned up from memory automatically // when the object is garbage collected destroy() { delete privateVars[this._sym]; } } var instance = new Something(); console.log(instance.property); //=>"I'm public" console.log(instance.privateProperty); //=> undefined console.log(instance.getPrivateProperty()); //=>"I'm hidden" |
如果性能成为一个问题,这种方法相对于使用
我相信使用构造函数内部的闭包可以获得"两个世界中最好的"。有两种变化:
所有数据成员都是私有的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function myFunc() { console.log('Value of x: ' + this.x); this.myPrivateFunc(); } function myPrivateFunc() { console.log('Enhanced value of x: ' + (this.x + 1)); } class Test { constructor() { let internal = { x : 2, }; internal.myPrivateFunc = myPrivateFunc.bind(internal); this.myFunc = myFunc.bind(internal); } }; |
有些成员是私人的
注意:这无疑是丑陋的。如果您知道更好的解决方案,请编辑此回复。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | function myFunc(priv, pub) { pub.y = 3; // The Test object now gets a member 'y' with value 3. console.log('Value of x: ' + priv.x); this.myPrivateFunc(); } function myPrivateFunc() { pub.z = 5; // The Test object now gets a member 'z' with value 3. console.log('Enhanced value of x: ' + (priv.x + 1)); } class Test { constructor() { let self = this; let internal = { x : 2, }; internal.myPrivateFunc = myPrivateFunc.bind(null, internal, self); this.myFunc = myFunc.bind(null, internal, self); } }; |
事实上,可以使用符号和代理。您可以在类范围中使用符号并在代理中设置两个陷阱:一个用于类原型,以便Reflect.ownKeys(实例)或Object.getOwnPropertySymbols不提供符号,另一个用于构造函数本身因此,当调用
这是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | const Human = (function() { const pet = Symbol(); const greet = Symbol(); const Human = privatizeSymbolsInFn(function(name) { this.name = name; // public this[pet] = 'dog'; // private }); Human.prototype = privatizeSymbolsInObj({ [greet]() { // private return 'Hi there!'; }, revealSecrets() { console.log(this[greet]() + ` The pet is a ${this[pet]}`); } }); return Human; })(); const bob = new Human('Bob'); console.assert(bob instanceof Human); console.assert(Reflect.ownKeys(bob).length === 1) // only ['name'] console.assert(Reflect.ownKeys(Human.prototype).length === 1 ) // only ['revealSecrets'] // Setting up the traps inside proxies: function privatizeSymbolsInObj(target) { return new Proxy(target, { ownKeys: Object.getOwnPropertyNames }); } function privatizeSymbolsInFn(Class) { function construct(TargetClass, argsList) { const instance = new TargetClass(...argsList); return privatizeSymbolsInObj(instance); } return new Proxy(Class, { construct }); } |
我个人喜欢绑定运算符
还有一个更难以解决的问题,这里没有提到更多的功能方法,并允许它拥有类中的所有私有道具/方法。
Private.js
1 2 | export const get = state => key => state[key]; export const set = state => (key,value) => { state[key] = value; } |
Test.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import { get, set } from './utils/Private' export default class Test { constructor(initialState = {}) { const _set = this.set = set(initialState); const _get = this.get = get(initialState); this.set('privateMethod', () => _get('propValue')); } showProp() { return this.get('privateMethod')(); } } let one = new Test({ propValue: 5}); let two = new Test({ propValue: 8}); two.showProp(); // 8 one.showProp(); // 5 |
对它的评论将不胜感激。
我在寻找"课堂私人数据"的最佳实践时遇到过这篇文章。有人提到,一些模式会出现性能问题。
我根据在线书"探索ES6"中的4个主要模式组合了几个jsperf测试:
http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes
测试可以在这里找到:
https://jsperf.com/private-data-for-classes
在Chrome 63.0.3239 / Mac OS X 10.11.6中,效果最佳的模式是"通过构造函数环境的私有数据"和"通过命名约定的私有数据"。对我来说,Safari在WeakMap上表现不错,但Chrome不太好。
我不知道内存的影响,但有些人警告过的"构造函数环境"的模式是性能问题非常高效。
4种基本模式是:
私有数据通过构造函数环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class Countdown { constructor(counter, action) { Object.assign(this, { dec() { if (counter < 1) return; counter--; if (counter === 0) { action(); } } }); } } const c = new Countdown(2, () => {}); c.dec(); c.dec(); |
私有数据通过构造函数环境2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Countdown { constructor(counter, action) { this.dec = function dec() { if (counter < 1) return; counter--; if (counter === 0) { action(); } } } } const c = new Countdown(2, () => {}); c.dec(); c.dec(); |
私有数据通过命名约定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class Countdown { constructor(counter, action) { this._counter = counter; this._action = action; } dec() { if (this._counter < 1) return; this._counter--; if (this._counter === 0) { this._action(); } } } const c = new Countdown(2, () => {}); c.dec(); c.dec(); |
私有数据通过WeakMaps
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | const _counter = new WeakMap(); const _action = new WeakMap(); class Countdown { constructor(counter, action) { _counter.set(this, counter); _action.set(this, action); } dec() { let counter = _counter.get(this); if (counter < 1) return; counter--; _counter.set(this, counter); if (counter === 0) { _action.get(this)(); } } } const c = new Countdown(2, () => {}); c.dec(); c.dec(); |
私人数据通过符号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | const _counter = Symbol('counter'); const _action = Symbol('action'); class Countdown { constructor(counter, action) { this[_counter] = counter; this[_action] = action; } dec() { if (this[_counter] < 1) return; this[_counter]--; if (this[_counter] === 0) { this[_action](); } } } const c = new Countdown(2, () => {}); c.dec(); c.dec(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Something { constructor(){ var _property ="test"; Object.defineProperty(this,"property", { get: function(){ return _property} }); } } var instance = new Something(); console.log(instance.property); //=>"test" instance.property ="can read from outside, but can't write"; console.log(instance.property); //=>"test" |
这次聚会来得很晚,但我在搜索中遇到OP问题所以......
是的,您可以通过将类声明包装在闭包中来拥有私有属性
这个代码集中有一个私有方法的例子。在下面的代码段中,Subscribable类有两个"私有"函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | const Subscribable = (function(){ const process = (self, eventName, args) => { self.processing.set(eventName, setTimeout(() => processCallbacks(self, eventName, args)))}; const processCallbacks = (self, eventName, args) => { if (self.callingBack.get(eventName).length > 0){ const [nextCallback, ...callingBack] = self.callingBack.get(eventName); self.callingBack.set(eventName, callingBack); process(self, eventName, args); nextCallback(...args)} else { delete self.processing.delete(eventName)}}; return class { constructor(){ this.callingBack = new Map(); this.processing = new Map(); this.toCallbacks = new Map()} subscribe(eventName, callback){ const callbacks = this.unsubscribe(eventName, callback); this.toCallbacks.set(eventName, [...callbacks, callback]); return () => this.unsubscribe(eventName, callback)} // callable to unsubscribe for convenience unsubscribe(eventName, callback){ let callbacks = this.toCallbacks.get(eventName) || []; callbacks = callbacks.filter(subscribedCallback => subscribedCallback !== callback); if (callbacks.length > 0) { this.toCallbacks.set(eventName, callbacks)} else { this.toCallbacks.delete(eventName)} return callbacks} emit(eventName, ...args){ this.callingBack.set(eventName, this.toCallbacks.get(eventName) || []); if (!this.processing.has(eventName)){ process(this, eventName, args)}}}})(); |
我喜欢这种方法,因为它可以很好地区分问题并保持真正的私密性。唯一的缺点是需要使用"自我"(或类似的东西)在私人内容中引用"此"。
哦,这么多奇特的解决方案!我通常不关心隐私所以我使用"伪隐私",因为它在这里说。但是,如果关心(如果有一些特殊要求)我使用类似于这个例子中的东西:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | class jobImpl{ // public constructor(name){ this.name = name; } // public do(time){ console.log(`${this.name} started at ${time}`); this.prepare(); this.execute(); } //public stop(time){ this.finish(); console.log(`${this.name} finished at ${time}`); } // private prepare(){ console.log('prepare..'); } // private execute(){ console.log('execute..'); } // private finish(){ console.log('finish..'); } } function Job(name){ var impl = new jobImpl(name); return { do: time => impl.do(time), stop: time => impl.stop(time) }; } // Test: // create class"Job" var j = new Job("Digging a ditch"); // call public members.. j.do("08:00am"); j.stop("06:00pm"); // try to call private members or fields.. console.log(j.name); // undefined j.execute(); // error |
函数(构造函数)
1 2 3 4 5 | function Job(name){ var impl = new jobImpl(name); this.do = time => impl.do(time), this.stop = time => impl.stop(time) } |
即使是打字稿也无法做到。从他们的文件:
When a member is marked private, it cannot be accessed from outside of its containing class. For example:
1
2
3
4
5
6 class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // Error: 'name' is private;
但在他们的操场上发现,这给出了:
1 2 3 4 5 6 7 | var Animal = (function () { function Animal(theName) { this.name = theName; } return Animal; }()); console.log(new Animal("Cat").name); |
所以他们的"私人"关键字是无效的。
我找到了一个非常简单的解决方案,只需使用
1 2 3 4 5 6 7 8 9 10 | class Cat { constructor(name ,age) { this.name = name this.age = age Object.freeze(this) } } let cat = new Cat('Garfield', 5) cat.age = 6 // doesn't work, even throws an error in strict mode |
您可以试试这个https://www.npmjs.com/package/private-members
该包将按实例保存成员。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | const pvt = require('private-members'); const _ = pvt(); let Exemplo = (function () { function Exemplo() { _(this).msg ="Minha Mensagem"; } _().mensagem = function() { return _(this).msg; } Exemplo.prototype.showMsg = function () { let msg = _(this).mensagem(); console.log(msg); }; return Exemplo; })(); module.exports = Exemplo; |
我使用这种模式,它总是对我有用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | class Test { constructor(data) { class Public { constructor(prv) { // public function (must be in constructor on order to access"prv" variable) connectToDb(ip) { prv._db(ip, prv._err); } } // public function w/o access to"prv" variable log() { console.log("I'm logging"); } } // private variables this._data = data; this._err = function(ip) { console.log("could not connect to"+ip); } } // private function _db(ip, err) { if(!!ip) { console.log("connected to"+ip+", sending data '"+this.data+"'"); return true; } else err(ip); } } var test = new Test(10), ip ="185.167.210.49"; test.connectToDb(ip); // true test.log(); // I'm logging test._err(ip); // undefined test._db(ip, function() { console.log("You have got hacked!"); }); // undefined |
实际上这是可能的。
1.首先,创建类并在构造函数中返回被调用的
2.在被调用的
3.在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | class Names { constructor() { this.privateProperty = 'John'; return _public(this, arguments); } privateMethod() { } } const names = new Names(1,2,3); console.log(names.somePublicMethod); //[Function] console.log(names.publicProperty); //'Jasmine' console.log(names.privateMethod); //undefined console.log(names.privateProperty); //undefind function _public(_this, _arguments) { class Names { constructor() { this.publicProperty = 'Jasmine'; _this.privateProperty; //"John"; _this.privateMethod; //[Function] } somePublicMethod() { _this.privateProperty; //"John"; _this.privateMethod; //[Function] } } return new Names(..._arguments); } |
是完全可以,也很容易。这是通过在构造函数中返回原型对象图来公开您的私有变量和函数来完成的。这不是什么新鲜事,但需要一些js foo才能理解它的优雅。这种方式不使用全局范围或弱映射。它是一种内置于语言中的反射形式。取决于你如何利用这一点;可以强制执行一个中断调用堆栈的异常,或者将异常作为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | class Clazz { constructor() { var _level = 1 function _private(x) { return _level * x; } return { level: _level, public: this.private, public2: function(x) { return _private(x); }, public3: function(x) { return _private(x) * this.public(x); }, }; } private(x) { return x * x; } } var clazz = new Clazz(); console.log(clazz._level); //undefined console.log(clazz._private); // undefined console.log(clazz.level); // 1 console.log(clazz.public(1)); //2 console.log(clazz.public2(2)); //2 console.log(clazz.public3(3)); //27 console.log(clazz.private(0)); //error |
看到这个答案,一个干净的&amp;简单的"类"解决方案,具有私有和公共界面,并支持合成
另一种类似于最后两个发布的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | class Example { constructor(foo) { // privates const self = this; this.foo = foo; // public interface return self.public; } public = { // empty data nodata: { data: [] }, // noop noop: () => {}, } // everything else private bar = 10 } const test = new Example('FOO'); console.log(test.foo); // undefined console.log(test.noop); // { data: [] } console.log(test.bar); // undefined |
大多数答案要么说这是不可能的,要么你要求使用WeakMap或Symbol,这些ES6功能可能需要polyfill。然而,还有另一种方式!看看这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | // 1. Create closure var SomeClass = function() { // 2. Create `key` inside a closure var key = {}; // Function to create private storage var private = function() { var obj = {}; // return Function to access private storage using `key` return function(testkey) { if(key === testkey) return obj; // If `key` is wrong, then storage cannot be accessed console.error('Cannot access private properties'); return undefined; }; }; var SomeClass = function() { // 3. Create private storage this._ = private(); // 4. Access private storage using the `key` this._(key).priv_prop = 200; }; SomeClass.prototype.test = function() { console.log(this._(key).priv_prop); // Using property from prototype }; return SomeClass; }(); // Can access private property from within prototype var instance = new SomeClass(); instance.test(); // `200` logged // Cannot access private property from outside of the closure var wrong_key = {}; instance._(wrong_key); // undefined; error logged |
我将此方法称为访问器模式。基本的想法是我们有一个闭包,闭包内的一个键,我们创建一个私有对象(在构造函数中),只有拥有密钥才能访问它。
如果您有兴趣,可以在我的文章中阅读更多相关信息。使用此方法,您可以创建无法在闭包之外访问的每个对象属性。因此,您可以在构造函数或原型中使用它们,但不能在其他地方使用它们。我没有看到这种方法在任何地方使用,但我认为它真的很强大。
使用
根据MDN网站文档:
The WeakMap object is a collection of key/value pairs in which the keys are objects only and the values can be arbitrary values.
The object references in the keys are held weakly, meaning that they are a target of garbage collection (GC) if there is no other reference to the object anymore.
这是使用包含数组的私有成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | const _items = new WeakMap(); class Queue { constructor() { _items.set(this, []); } enqueue( item) { _items.get(this).push(item); } get count() { return _items.get(this).length; } peek() { const anArray = _items.get(this); if( anArray.length == 0) throw new Error('There are no items in array!'); if( anArray.length > 0) return anArray[0]; } dequeue() { const anArray = _items.get(this); if( anArray.length == 0) throw new Error('There are no items in array!'); if( anArray.length > 0) return anArray.splice(0, 1)[0]; } } |
使用示例:
1 2 3 4 5 6 7 | const c = new Queue(); c.enqueue("one"); c.enqueue("two"); c.enqueue("three"); c.enqueue("four"); c.enqueue("five"); console.log(c); |
私有成员
但是,可以使用以下方式访问
1 | const anArray = _items.get(this); |
这里,myThing变量是私有的,是闭包的一部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Person { constructor() { var myThing ="Hello World"; return { thing: myThing, sayThing: this.sayThing } } sayThing() { console.log(this.thing); } } var person = new Person(); console.log(person); |
阅读上一个答案,我认为这个例子可以总结出上述解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | const friend = Symbol('friend'); const ClassName = ((hidden, hiddenShared = 0) => { class ClassName { constructor(hiddenPropertyValue, prop){ this[hidden] = hiddenPropertyValue * ++hiddenShared; this.prop = prop } get hidden(){ console.log('getting hidden'); return this[hidden]; } set [friend](v){ console.log('setting hiddenShared'); hiddenShared = v; } get counter(){ console.log('getting hiddenShared'); return hiddenShared; } get privileged(){ console.log('calling privileged method'); return privileged.bind(this); } } function privileged(value){ return this[hidden] + value; } return ClassName; })(Symbol('hidden'), 0); const OtherClass = (() => class OtherClass extends ClassName { constructor(v){ super(v, 100); this[friend] = this.counter - 1; } })(); |
我开发了一个模块,可以帮助您使用访问限制
JavaScript类叫做Capsulable。 (私有和受保护的静态)
如果您有兴趣,请查看我的包裹。
https://github.com/hmmhmmhm/capsulable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const Capsulable = require('capsulable') const Field = Capsulable() class A { constructor(_field){ // Configure data fields. Field(this, _field) // The code below provides access to // the data fields when creating // functions within the class. Field(this).private Field(this).protected Field(this).protectedStatic } } module.exports = A |
我们知道ES6类的私有属性没有原生支持。
以下是我使用的(可能会有帮助)。基本上我是在工厂内包装一个班级。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function Animal(name) { const privateData = 'NO experiments on animals have been done!'; class Animal { constructor(_name) { this.name = _name; } getName() { return this.name } getDisclamer() { return `${privateData} Including ${this.name}` } } return new Animal(name) } |
我是一个初学者,很高兴听到这是一个糟糕的方法。