在ES6类中是否可以创建私有属性?
以下是一个例子。
如何防止访问instance.property
?
class Something {
constructor(){
this.property = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
在ES6类中是否可以创建私有属性?
以下是一个例子。
如何防止访问instance.property
?
class Something {
constructor(){
this.property = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
私有类特性 现在被大多数浏览器支持。
class Something {
#property;
constructor(){
this.#property = "test";
}
#privateMethod() {
return 'hello world';
}
getPrivateMessage() {
return this.#property;
}
}
const instance = new Something();
console.log(instance.property); //=> undefined
console.log(instance.privateMethod); //=> undefined
console.log(instance.getPrivateMessage()); //=> test
console.log(instance.#property); //=> Syntax error
#beep() {}
; 和这样做:async #bzzzt() {}
? - Константин Ван简短回答,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; }
}
getName
和setName
属性设为私有? - aij#
前缀并包含在其中,而不仅仅是在构造函数中。
MDN 文档
真正的私有属性终于在 ES2022 中被添加了。截至 2023 年 01 月 01 日,所有主流浏览器都已经支持私有属性(字段和方法)超过一年,但仍有 5-10% 的用户使用旧版浏览器 [Can I Use]。class Person {
#age
constructor(name) {
this.name = name; // this is public
this.#age = 20; // this is 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 name but not age
class Person {
constructor(name) {
let age = 20; // this is private
this.name = name; // this is public
this.greet = () => {
// here we can access both name and age
console.log(`name: ${this.name}, age: ${age}`);
};
}
anotherMethod() {
// here we can access name but not 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();
return 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}`);
}
};
})();
let joe = new Person('Joe');
joe.greet();
// here we can access name but not age
privateAge.set(this, 20)
那样使用它们,或者编写一个小包装器并以另一种方式使用它,比如privateProps.set(this, 'age', 0)
。WeakMap
对象可能会被篡改。话虽如此,所有JavaScript都可以被破坏。Map
完成,但WeakMap
更好,因为Map
会创建内存泄漏,除非你非常小心,而且对于这个目的,两者没有其他区别。)this[mySymbol]
中。Object.getOwnPropertySymbols
来突破,但有些棘手。let Person = (() => {
let ageKey = Symbol();
return 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]}`);
}
}
})();
let joe = new Person('Joe');
joe.greet();
// Here we can access joe's name and, with a little effort, age. We can’t
// access ageKey directly, but we can obtain it by listing all Symbol
// properties on `joe` with `Object.getOwnPropertySymbols(joe)`.
Object.defineProperty
将属性设置为不可枚举并不能阻止它被包含在Object.getOwnPropertySymbols
中。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.
instanceof
。我承认我之前认为这种方法只是为了完整性而包含的,应该更多地考虑它实际上能做到什么。 - twhb是的,有一个方法可以在对象中实现作用域访问 - ES6引入了Symbol
。
Symbol
是独一无二的,除非使用反射(类似于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
Object.getOwnPropertySymbols
吗? ;) - Qantas 94 Heavyprivate
和protected
关键词比使用Symbol
或Name
更加简洁明了。我更喜欢用点符号表示法而非方括号表示法。我希望继续使用点来表示私有成员,例如this.privateVar
。 - trusktrexport
关键字将其公开。(建议使用符号来确保隐私在ES6规范的早期版本中是正确的,但现在不再是这种情况:https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604.html和https://dev59.com/tmEi5IYBdhLWcg3wCoQp#22280202。有关符号和隐私的更长讨论,请参见:https://curiosity-driven.org/private-properties-in-javascript)
在JS中获得真正的隐私的唯一方法是通过作用域,因此没有办法拥有一个只能在组件内部访问的this
成员属性。使用WeakMap是在ES6中存储真正私有数据的最佳方式。
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));
}
}
const _ = privates.get(this); console.log(_.privateProp1);
)。 - Quentin Royconst myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"
这意味着你的属性是私有的还是公共的? - stackoverflow供其他人参考,我现在了解到建议使用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}`)
}
}
}
}
使用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在评论中所述,这些私有属性是静态的,因此具有全局范围。它们在使用单例模式时会很好地工作,但在处理瞬态对象时需要小心。延续上面的示例:
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
a.say(); // a
应该改为 b.say(); // b
。 - grokkylet _message = null
的方式,不太好,当多次调用构造函数时,会出现混乱。 - Littlee下面是一个简单的示例,展示如何使用ES6实现:
1 使用class关键字创建类
2 在构造函数内部使用let或const保留字声明块作用域变量 -> 因为它们是块级作用域,所以无法从外部访问(封装)
3 为了允许一些访问控制(setter|getter)到这些变量,可以在构造函数内部声明实例方法,使用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
new Something();
都会创建每个实例方法的副本,因为你的方法在构造函数中声明以访问这些私有变量。如果您创建了大量类的实例,则可能会导致大量内存消耗,从而影响性能。应该将方法声明在构造函数范围之外。我的评论更多地是解释你的解决方案缺点而不是批评。 - Freezystem
[Scopes]
对象中显示整个执行上下文。有些东西必须在浏览器范围之外编码。在我的测试中,没有一种方法可以向Chrome隐藏任何内容。 - thednp