是否可以在ES6类中创建私有属性?
这是一个例子。
如何阻止访问instance.property
?
class Something {
constructor(){
this.property = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
答案 0 :(得分:238)
简短的回答,不,没有ES6类私有属性的原生支持。
但是你可以通过不将新属性附加到对象来模仿该行为,但是将它们保存在类构造函数中,并使用getter和setter来达到隐藏属性。请注意,getter和setter会在每个新类实例上重新定义。
ES6
class Person {
constructor(name) {
var _name = name
this.setName = function(name) { _name = name; }
this.getName = function() { return _name; }
}
}
ES5
function Person(name) {
var _name = name
this.setName = function(name) { _name = name; }
this.getName = function() { return _name; }
}
答案 1 :(得分:169)
扩展@ loganfsmyth的答案:
JavaScript中唯一真正的私有数据仍然是作用域变量。从内部访问的属性的意义上讲,您不能拥有与公共属性相同的私有属性,但您可以使用范围变量来存储私有数据。
这里的方法是使用构造函数的作用域(私有)来存储私有数据。对于有权访问此私有数据的方法,它们也必须在构造函数中创建,这意味着您要为每个实例重新创建它们。这是一种表现和记忆惩罚,但有些人认为惩罚是可以接受的。对于不需要访问私有数据的方法,可以通过像往常一样将它们添加到原型来避免惩罚。
示例:
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
WeakMap可用于避免先前方法的性能和内存损失。 WeakMaps将数据与对象(此处为实例)相关联,使得只能使用该WeakMap访问它。因此,我们使用范围变量方法创建私有WeakMap,然后使用WeakMap检索与this
关联的私有数据。这比范围变量方法更快,因为所有实例都可以共享一个WeakMap,因此您不需要重新创建方法只是为了让它们访问自己的WeakMaps。
示例:
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并像age.set(this, 20)
一样使用它们,或者编写一个小包装器并以另一种方式使用它,例如privateProps.set(this, 'age', 0)
。
理论上,通过篡改全局WeakMap
对象可以破坏这种方法的隐私。也就是说,所有的JavaScript都可以通过错误的全局变量来破坏。我们的代码已经建立在假设这不会发生的基础上。
(此方法也可以使用Map
完成,但WeakMap
更好,因为Map
会造成内存泄漏,除非您非常小心,为此目的,两者其他方面并不相同。)
符号是一种可以作为属性名称的原始值。您可以使用范围变量方法创建私有符号,然后将私有数据存储在this[mySymbol]
。
使用Object.getOwnPropertySymbols
可以破坏此方法的隐私,但有点尴尬。
示例:
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)`.
旧的默认值,只需使用带下划线前缀的公共属性。虽然这不是一个私人财产,但这种惯例非常普遍,以至于它能够很好地传达读者应该将财产视为私有财产,这通常可以完成工作。为了换取这种失误,我们采用了一种更易于阅读,更容易打字和更快速的方法。
示例:
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非常私密,比范围变量更实用;范围符号是相当私密和合理实用的;下划线通常足够私密且非常实用。
答案 2 :(得分:113)
更新:proposal with nicer syntax即将推出。欢迎提供捐助。
是的,对于对象的范围访问 - ES6 introduces Symbol
s。
符号是唯一的,你不能从外面访问一个除了反射(比如Java / C#中的私有),但任何有内部符号访问权限的人都可以使用它来进行密钥访问:
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
答案 3 :(得分:94)
正在ECMA standard中实现私有字段。您可以立即使用babel 7和阶段3预设开始使用它们。参见babel REPL example。
class Something {
#property;
constructor(){
this.#property = "test";
}
}
const instance = new Something();
console.log(instance.property); //=> undefined
答案 4 :(得分:29)
答案是"否"。但您可以创建对此属性的私有访问权限:
export
关键字公开。(在早期版本的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)
答案 5 :(得分:26)
在JS中获得真正隐私的唯一方法是通过范围界定,因此无法拥有只能在组件内部访问的this
成员的属性。在ES6中存储真正私有数据的最佳方法是使用WeakMap。
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是如此动态。有人还可以做
var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
// Store 'this', 'key', and 'value'
return oldSet.call(this, key, value);
};
在存储时捕获值,因此如果您需要格外小心,则需要捕获对.set
和.get
的本地引用,以明确使用而不是依赖于可覆盖的原型。
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));
}
}
答案 6 :(得分:21)
为了将来对其他观众的参考,我现在听说建议使用WeakMaps来保存私人数据。
这是一个更清晰,更有效的例子:
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}`)
}
}
}
}
答案 7 :(得分:11)
取决于whom you ask: - )
Maximally minimal classes proposal中没有包含private
属性修饰符,而current draft似乎已将其设为support for。
但是,可能有private names {{3}},它允许私有属性 - 它们也可能在类定义中使用。
答案 8 :(得分:9)
使用ES6模块(最初由@ d13提出)对我来说效果很好。它并不能完美地模仿私有财产,但至少你可以确信应该是私人的财产不会泄漏到你的班级之外。这是一个例子:
let _message = null;
const _greet = name => {
console.log('Hello ' + name);
};
export default class Something {
constructor(message) {
_message = message;
}
say() {
console.log(_message);
_greet('Bob');
}
};
然后消费代码可能如下所示:
import Something from './something.js';
const something = new Something('Sunny day!');
something.say();
something._message; // undefined
something._greet(); // exception
正如评论中概述的@DanyalAytekin所述,这些私有属性是静态的,因此在全局范围内。在与Singletons合作时,它们会很好用,但必须注意Transient对象。扩展上面的例子:
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
答案 9 :(得分:9)
完成@ d13以及@ johnny-oshika和@DanyalAytekin的评论:
我想在@ johnny-oshika提供的示例中,我们可以使用普通函数而不是箭头函数,然后.bind
使用当前对象加上_privates
对象作为curried参数:
<强> something.js 强>
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 强>
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
对象我能想到的一些缺点:
可在此处找到正在运行的代码段:http://www.webpackbin.com/NJgI5J8lZ
答案 10 :(得分:8)
是 - 你可以创建封装属性,但是它没有使用访问修饰符(public | private),至少没有使用ES6。
以下是一个简单的示例,说明如何使用ES6:
1使用class字
创建课程2在其内部构造函数使用let OR const保留字 - &gt;声明块范围变量由于它们是块范围,因此无法从外部访问(封装)
3要允许对这些变量进行某些访问控制(setter | getters),您可以使用以下语句在其构造函数中声明实例方法:this.methodName=function(){}
语法
"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;
}
}
}
现在让我们检查一下:
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
答案 11 :(得分:7)
如果你的IDE支持JSDoc(例如,Webstorm),我决定采用一种更实用的方法,而不是反对ES6中目前无法提供私有可见性这一事实。我们的想法是使用@private
tag。就开发而言,IDE将阻止您访问其类外部的任何私有成员。对我来说效果非常好,它对于隐藏内部方法非常有用,因此自动完成功能向我展示了该类实际上要暴露的内容。这是一个例子:
答案 12 :(得分:6)
<强> WeakMap 强>
Object.getOwnPropertySymbols
而导致软私密)首先,定义一个包装WeakMap的函数:
function Private() {
const map = new WeakMap();
return obj => {
let props = map.get(obj);
if (!props) {
props = {};
map.set(obj, props);
}
return props;
};
}
然后,在课堂外构建一个引用:
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不支持类,但在示例中它看起来更干净。
答案 13 :(得分:4)
事实上,可以使用符号和代理。您可以在类范围中使用符号并在代理中设置两个陷阱:一个用于类原型,以便Reflect.ownKeys(实例)或Object.getOwnPropertySymbols不提供符号,另一个用于构造函数本身因此,当调用new ClassName(attrs)
时,将拦截返回的实例并阻止自己的属性符号。
这是代码:
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 });
}
Reflect.ownKeys()
的工作原理如下:Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))
这就是我们需要为这些对象设置陷阱的原因。
答案 14 :(得分:4)
我在寻找&#34;班级私人数据&#34;的最佳做法时遇到过这篇文章。有人提到,一些模式会出现性能问题。
我根据在线书籍中的4种主要模式组合了一些jsperf测试&#34;探索ES6&#34;:
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中,性能最佳的模式是#34;私有数据通过构造函数环境&#34;和#34;通过命名约定的私人数据&#34;。对我来说,Safari在WeakMap上表现不错,但Chrome不太好。
我不知道内存的影响,但是&#34;构造函数环境的模式&#34;一些人警告说性能问题非常高效。
4种基本模式是:
通过构造函数环境的私有数据
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的私有数据
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();
通过命名惯例的私人数据
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的私人数据
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();
通过符号的私人数据
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();
答案 15 :(得分:4)
我个人喜欢bind operator ::
的提议,然后将其与提到的解决方案@ d13结合起来,但现在坚持使用@ {13的答案,你使用export
您的类的关键字,并将私有函数放在模块中。
还有一个更难解决的问题,这里没有提到更多功能方法,并允许它在类中拥有所有私有道具/方法。
<强> Private.js 强>
export const get = state => key => state[key];
export const set = state => (key,value) => { state[key] = value; }
<强> Test.js 强>
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
对它的评论将不胜感激。
答案 16 :(得分:4)
我相信这两个世界都是最好的&#39;在构造函数中使用闭包。有两种变化:
所有数据成员都是私有的
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);
}
};
&#13;
有些会员是私人的
注意:这无疑是丑陋的。如果您知道更好的解决方案,请编辑此回复。
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);
}
};
&#13;
答案 17 :(得分:4)
我认为Benjamin's answer可能是大多数情况下最好的,直到语言本身支持显式私有变量。
但是,如果出于某种原因需要阻止使用Object.getOwnPropertySymbols()
进行访问,我考虑使用的方法是附加一个唯一的,不可配置的,不可枚举的,不可写的属性,可以用作构造中每个对象的属性标识符(例如唯一Symbol
,如果您还没有其他独特属性,如id
)。然后使用该标识符保留每个对象的“私有”变量的映射。
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"
如果性能成为问题,这种方法相对于使用WeakMap
的潜在优势是faster access time。
答案 18 :(得分:3)
即使是打字稿也无法做到。来自documentation:
当成员被标记为私有时,无法从其包含的类外部访问它。例如:
class Animal { private name: string; constructor(theName: string) { this.name = theName; } } new Animal("Cat").name; // Error: 'name' is private;
但是在他们的playground上发现了这一点:
var Animal = (function () {
function Animal(theName) {
this.name = theName;
}
return Animal;
}());
console.log(new Animal("Cat").name);
所以他们的私人&#34;关键字无效。
答案 19 :(得分:3)
这次聚会来得很晚但我在搜索中遇到OP问题所以...... 是的,您可以通过在闭包中包装类声明来拥有私有属性
我在this codepen中有一个私有方法的例子。在下面的代码段中,Subscribable类有两个私有&#39;函数process
和processCallbacks
。可以以这种方式添加任何属性,并且通过使用闭包来保持它们是私有的。如果问题分离得很好,IMO隐私就是一种罕见的需求,当关闭整齐地完成工作时,通过添加更多语法,Javascript不需要变得臃肿。
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)}}}})();
我喜欢这种方法,因为它可以很好地区分问题并保持真正的私密性。唯一的缺点是需要使用&#39; self&#39; (或类似的东西)指的是这个&#39;在私人内容中。
答案 20 :(得分:3)
哦,这么多奇特的解决方案!我通常不关心隐私,因此使用“伪隐私” ,因为它是said here。但是,如果要照顾(如果对此有一些特殊要求),则在此示例中使用类似的内容:
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
函数(构造函数)Job
的另一种可能的实现:
function Job(name){
var impl = new jobImpl(name);
this.do = time => impl.do(time),
this.stop = time => impl.stop(time)
}
答案 21 :(得分:3)
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"
答案 22 :(得分:2)
此代码演示了私有和公共,静态和非静态,实例和类级别,变量,方法和属性。
https://codesandbox.io/s/class-demo-837bj
class Animal {
static count = 0 // class static public
static #ClassPriVar = 3 // class static private
constructor(kind) {
this.kind = kind // instance public property
Animal.count++
let InstancePriVar = 'InstancePriVar: ' + kind // instance private constructor-var
log(InstancePriVar)
Animal.#ClassPriVar += 3
this.adhoc = 'adhoc' // instance public property w/out constructor- parameter
}
#PawCount = 4 // instance private var
set Paws(newPawCount) {
// instance public prop
this.#PawCount = newPawCount
}
get Paws() {
// instance public prop
return this.#PawCount
}
get GetPriVar() {
// instance public prop
return Animal.#ClassPriVar
}
static get GetPriVarStat() {
// class public prop
return Animal.#ClassPriVar
}
PrintKind() {
// instance public method
log('kind: ' + this.kind)
}
ReturnKind() {
// instance public function
return this.kind
}
/* May be unsupported
get #PrivMeth(){ // instance private prop
return Animal.#ClassPriVar + ' Private Method'
}
static get #PrivMeth(){ // class private prop
return Animal.#ClassPriVar + ' Private Method'
}
*/
}
function log(str) {
console.log(str)
}
// TESTING
log(Animal.count) // static, avail w/out instance
log(Animal.GetPriVarStat) // static, avail w/out instance
let A = new Animal('Cat')
log(Animal.count + ': ' + A.kind)
log(A.GetPriVar)
A.PrintKind()
A.Paws = 6
log('Paws: ' + A.Paws)
log('ReturnKind: ' + A.ReturnKind())
log(A.adhoc)
let B = new Animal('Dog')
log(Animal.count + ': ' + B.kind)
log(B.GetPriVar)
log(A.GetPriVar) // returns same as B.GetPriVar. Acts like a class-level property, but called like an instance-level property. It's cuz non-stat fx requires instance.
log('class: ' + Animal.GetPriVarStat)
// undefined
log('instance: ' + B.GetPriVarStat) // static class fx
log(Animal.GetPriVar) // non-stat instance fx
log(A.InstancePriVar) // private
log(Animal.InstancePriVar) // private instance var
log('PawCount: ' + A.PawCount) // private. Use getter
/* log('PawCount: ' + A.#PawCount) // private. Use getter
log('PawCount: ' + Animal.#PawCount) // Instance and private. Use getter */
答案 23 :(得分:2)
请参阅this answer了解一个干净的&amp;简单&#39; class&#39;具有私有和公共界面的解决方案以及对合成的支持
答案 24 :(得分:2)
可以使用WeakMap
在类中使用私有方法。
WeakMap对象是键/值对的集合,其中键仅是对象,值可以是任意值。
键中的对象引用被保持较弱,这意味着如果不再有对该对象的其他引用,它们将成为垃圾回收(GC)的目标。
这是一个使用拥有数组的私有成员_items
创建Queue
data structure的示例。
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];
}
}
使用示例:
const c = new Queue();
c.enqueue("one");
c.enqueue("two");
c.enqueue("three");
c.enqueue("four");
c.enqueue("five");
console.log(c);
私有成员_items
被隐藏,无法在Queue
对象的属性或方法中看到:
但是,可以使用以下方式访问_items
对象中的私有成员Queue
:
const anArray = _items.get(this);
答案 25 :(得分:2)
您可以尝试此https://www.npmjs.com/package/private-members
此软件包将按实例保存成员。
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;
答案 26 :(得分:2)
我使用这种模式,它总是对我有用
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
答案 27 :(得分:2)
我找到了一个非常简单的解决方案,只需使用Object.freeze()
。当然问题是你以后不能向对象添加任何内容。
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
答案 28 :(得分:2)
实际上 可能
1.首先,创建类并在构造函数中返回被调用的_public
函数
2.在被调用的_public
函数中,传递this
引用(以获取对所有私有方法和道具的访问权),以及来自constructor
(将在new Names()
)中传递
3.在_public
函数范围内,还有Names
类,可以访问私有this
类的Names
(_this)引用
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);
}
答案 29 :(得分:2)
是完全可以,也很容易。这是通过在构造函数中返回原型对象图来公开您的私有变量和函数来完成的。这不是什么新鲜事,但需要一些js foo才能理解它的优雅。这种方式不使用全局范围或弱映射。它是一种内置于语言中的反射形式。取决于你如何利用这一点;可以强制执行一个中断调用堆栈的异常,或者将异常作为undefined
进行掩埋。这在下面进行了演示,可以阅读有关这些功能的更多信息here
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
&#13;
答案 30 :(得分:1)
此处, myThing 变量是私有的,是闭包的一部分:
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);
答案 31 :(得分:1)
另一种类似于最后发布的两种方式
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
答案 32 :(得分:1)
大多数答案要么说它不可能,要么你要求使用WeakMap或Symbol,这些ES6功能可能需要polyfill。不过还有另外一种方法!看看这个:
// 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
&#13;
我将此方法称为访问者模式。基本的想法是我们在闭包内部有一个闭包,一个键,我们创建了一个私有对象(在构造函数中)只有拥有键才能访问。
如果您有兴趣,可以在my article了解更多相关信息。使用此方法,您可以创建无法在闭包之外访问的每个对象属性。因此,您可以在构造函数或原型中使用它们,但不能在其他地方使用它们。我还没有看到这种方法在任何地方使用,但我认为它非常强大。
答案 33 :(得分:1)
我意识到这里有很多答案。我想分享我的解决方案,它确保在 ES6 类和旧版 JS 中使用真正的私有变量。
var MyClass = (function() {
var $ = new WeakMap();
function priv(self) {
var r = $.get(self);
if (!r) $.set(self, r={});
return r;
}
return class { /* use priv(this).prop inside your class */ }
}();
外部世界无法访问 $ 的事实确保了隐私。
当实例消失时,WeakMap 会释放数据。
这绝对适用于纯 Javascript,我相信它们适用于 ES6 类,但我还没有测试过 $ 在成员方法范围内是否可用。
答案 34 :(得分:0)
我开发了一个模块,可帮助您在 JavaScript类称为Capsulable。 (私有和受保护的静态)
如果您有兴趣,请在下面查看我的包裹。 https://github.com/hmmhmmhm/capsulable
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
答案 35 :(得分:0)
阅读先前的答案,我认为该示例可以总结上述解决方案
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8888');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
答案 36 :(得分:0)
我有一个可以解决问题的方法,它很简单...虽然性能不是有害的...但是它可以正常工作。
诀窍是,在建立私有属性和功能并且需要标准化/采用的解决方法之前,这是另一种解决方法...
class ClassPrivateProperties {
constructor(instance) {
const $this = instance;
let properties = {};
this.prop = (key, value = undefined) => {
if (!value) {
return properties[key];
} else {
properties[key] = value;
}
};
this.clear = instance => {
if ($this === instance) {
properties = {};
return true;
} else {
return false;
}
}
}
}
这是一个示例用法,它可能是以前的用法(同样,如果您使用上面的方法可以使其变得更好)
class Test {
constructor() {
this._privateProps = new ClassPrivateProperties(this);
}
property(key, value = undefined) {
if (!value) {
return this._privateProps.prop(key);
} else {
this._privateProps.prop(key, value);
}
}
clear() { return this._privateProps.clear(this); }
}
const test = new test;
test.property('myKey','some value here');
console.log(test.property('myKey'));
就像我提到的那样,这个问题并不是最好的,但它可以使财产真正私有。
答案 37 :(得分:0)
我们可以使用getter和setter来模拟类的私有属性。
class FootballClub {
constructor (cname, cstadium, ccurrentmanager) {
this.name = cname;
this._stadium = cstadium; // we will treat this prop as private and give getter and setter for this.
this.currmanager = ccurrentmanager;
}
get stadium( ) {
return this._stadium.toUpperCase();
}
}
let club = new FootballClub("Arsenal", "Emirates" , "Arteta")
console.log(club);
//FootballClub {
// name: 'Arsenal',
// _stadium: 'Emirates',
// currmanager: 'Arteta'
// }
console.log( club.stadium ); // EMIRATES
club.stadium = "Highbury"; // TypeError: Cannot set property stadium of #<FootballClub> which has only a getter
在上面的示例中,我们没有提供体育场的setter方法,因此我们无法为此设置新值。在下一个示例中,为体育场
添加了一个二传手class FootballClub {
constructor (cname, cstadium, ccurrentmanager) {
this.name = cname;
this._stadium = cstadium; // we will treat this prop as private and give getter and setter for this.
this.currmanager = ccurrentmanager;
}
get stadium( ) {
return this._stadium.toUpperCase();
}
set stadium(val) {
this._stadium = val;
}
}
let club = new FootballClub("Arsenal", "Emirates" , "Arteta")
console.log(club.stadium); // EMIRATES
club.stadium = "Emirates Stadium";
console.log(club.stadium); // EMIRATES STADIUM
答案 38 :(得分:-1)
我们知道,ES6类没有对私有属性的本地支持。
下面就是我所使用的(可能会有所帮助)。基本上我是在工厂内部包装一个类。
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)
}
我是一个初学者,很高兴听到这是一个不好的方法。