JavaScript ES6类中的私有属性

d13*_*d13 410 javascript class private-members ecmascript-6 es2015

是否可以在ES6类中创建私有属性?

这是一个例子.我怎样才能阻止访问instance.property

class Something {
  constructor(){
    this.property = "test";
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"
Run Code Online (Sandbox Code Playgroud)

Met*_*win 260

简短的回答,不,没有ES6类私有属性的原生支持.

但是,您可以通过不将新属性附加到对象,但将它们保留在类构造函数中,并使用getter和setter来到达隐藏属性来模仿该行为.请注意,getter和setter会在类的每个新实例上重新定义.

ES6

class Person {
    constructor(name) {
        var _name = name
        this.setName = function(name) { _name = name; }
        this.getName = function() { return _name; }
    }
}
Run Code Online (Sandbox Code Playgroud)

ES5

function Person(name) {
    var _name = name
    this.setName = function(name) { _name = name; }
    this.getName = function() { return _name; }
}
Run Code Online (Sandbox Code Playgroud)

  • 这太奇怪了!在ES6中,你创造了比ES6之前更多的"封闭金字塔"!在构造函数中定义函数看起来比在上面的ES5示例中看起来更加丑陋. (9认同)
  • 所有这一切都是引入间接性的.现在,如何将`getName`和`setName`属性设为私有? (9认同)
  • 我最喜欢这个解决方案。我同意它不应该用于缩放,但它非常适合每个包含通常只实例化一次的类。 (3认同)
  • 此外,每次创建new时,您都要重新定义此类的每个组件. (2认同)
  • @aij 所以请说出一种不同的语言。你可以很容易地看到,他可以只注释掉 setter 或 getter 或两者,并且 `_name` 是真正私有的。 (2认同)

twh*_*whb 184

要扩展@ 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
Run Code Online (Sandbox Code Playgroud)

Scoped WeakMap

可以使用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
Run Code Online (Sandbox Code Playgroud)

此示例使用Object将一个WeakMap用于多个私有属性; 您也可以使用多个WeakMaps并使用它们age.set(this, 20),或者编写一个小包装并以另一种方式使用它,比如privateProps.set(this, 'age', 0).

从理论上讲,这种方法的隐私可以通过篡改全局WeakMap对象来破坏.也就是说,所有的JavaScript都可以通过错误的全局变量来破坏.我们的代码已经建立在假设不会发生这种情况的基础之上.

(这种方法也可以用Map,但WeakMap更好,因为Map除非你非常小心,否则会造成内存泄漏,为此目的,两者并没有其他区别.)

半答案:Scoped符号

符号是一种可以作为属性名称的原始值.您可以使用范围变量方法创建私有符号,然后将私有数据存储在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)`.
Run Code Online (Sandbox Code Playgroud)

半答案:下划线

旧的默认值,只需使用带下划线前缀的公共属性.虽然这不是一个私人财产,但这种惯例非常普遍,以至于它能够很好地传达读者应该将财产视为私有财产,这通常可以完成工作.为了换取这种失误,我们得到了一种更容易阅读,更容易打字和更快速的方法.

例:

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.
Run Code Online (Sandbox Code Playgroud)

结论

从ES2017开始,仍然没有完美的私有财产方式.各种方法都有利有弊.范围变量是真正的私有; 作用域WeakMaps非常私密,比范围变量更实用; 范围符号是相当私密和合理实用的; 下划线通常足够私密且非常实用.

  • 第一个示例代码段("范围变量")是一个完整的反模式 - 每个返回的对象将具有不同的类.不要那样做.如果需要特权方法,请在构造函数中创建它们. (7认同)
  • @Bergi @Kokodoko我编辑了范围变量方法稍微快一点,而不是破坏`instanceof`.我承认我认为这种方法只是为了完整性而包括在内,并且应该更多地考虑它实际上能够达到的程度. (2认同)
  • 很好的解释!我仍然感到惊讶的是,ES6 实际上让模拟私有变量变得更加困难,而在 ES5 中,您可以在函数中使用 var 和 this 来模拟私有和公共变量。 (2认同)
  • @Kokodoko如果你省去了这个类而只是把所有东西都放在函数中,你还必须恢复使用原型方法实现继承。到目前为止,在类上使用扩展是一种更简洁的方法,因此在函数内使用类是完全可以接受的。 (2认同)

Ali*_*ter 129

私有领域正在ECMA标准中实施.您现在可以使用babel 7和第3阶段预设开始使用它们.请参阅babel REPL示例.

class Something {
  #property;

  constructor(){
    this.#property = "test";
  }
}

const instance = new Something();
console.log(instance.property); //=> undefined
Run Code Online (Sandbox Code Playgroud)

  • 哇这太丑了。Hashtag 是有效字符。该财产不是真正私人的,还是?.. 我在 TypeScript 中检查过。私有成员不会以私有或只读方式(从外部)编译。刚刚声明为另一个(公共)财产。(ES5)。 (10认同)
  • 使用 `_` 将是一个重大改变,除非你的意思是 JS 根本不需要 _private_ 私有属性 (4认同)
  • 那埃斯林特呢?等号出现解析器错误。Babel正在工作,只是eslint无法解析这种新的js语法。 (3认同)
  • 你怎么用这个写_private methods_?我可以这样做:`#beep(){}`; 这是:`async #bzzzt(){}`? (2认同)

Ben*_*aum 115

更新:具有更好语法提案即将推出.欢迎捐款.


是的,对于对象的范围访问 - ES6引入了Symbols.

符号是唯一的,你不能从外部访问一个除了反射(如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
Run Code Online (Sandbox Code Playgroud)

  • @BenjaminGruenbaum:显然Symbols不再确保真正的隐私:http://stackoverflow.com/a/22280202/1282216 (40认同)
  • @trusktr通过thre键?不.通过符号?是.非常类似于如何在C#和Java等语言中使用反射来访问私有字段.访问修饰符不是关于安全性 - 它们是关于意图的清晰度. (28认同)
  • 另外,我觉得使用`private`和`protected`关键字会比`Symbol`或`Name`更清晰.我更喜欢点符号而不是括号表示法.我想继续使用点来做私事.`this.privateVar` (13认同)
  • 似乎使用Symbols类似于`const myPrivateMethod = Math.random(); Something.prototype [''+ myPrivateMethod] = function(){...} new Something()[''+ myPrivateMethod]();`.在传统的JavaScript意义上,这不是真正的隐私,它是默默无闻的.我认为"私有"JavaScript意味着使用闭包来封装变量.因此,这些变量无法通过反思获得. (9认同)
  • 你不能使用`Object.getOwnPropertySymbols`吗?;) (6认同)
  • 为什么不用私有数据加上`_`?如果无法执行,请至少公开一下意图.例如:`this._key ="123";` (4认同)

d13*_*d13 30

答案是不".但您可以创建对此属性的私有访问权限:

(在早期版本的ES6规范中,可以使用符号来确保隐私的建议是正确的,但不再是这种情况:https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604. html/sf/answers/1559614171/.有关符号和隐私的更长时间的讨论,请参阅:https://curiosity-driven.org/private-properties-in-javascript)

  • -1,这不能回答你的问题.(您也可以在ES5中使用IIFE的闭包).私有属性可通过大多数语言(Java,C#等)的反射来枚举.私有财产的目的是向其他职业者传达意图而不是强制执行安全. (6认同)

log*_*yth 29

在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));
  }
}
Run Code Online (Sandbox Code Playgroud)

显然这可能很慢,而且绝对是丑陋的,但确实提供了隐私.

请记住,即使这不是完美的,因为Javascript是如此动态.有人还可以

var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
    // Store 'this', 'key', and 'value'
    return oldSet.call(this, key, value);
};
Run Code Online (Sandbox Code Playgroud)

在存储时捕获值,因此如果您想要格外小心,则需要捕获本地引用.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));
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 作为建议,您可以通过将对象用作值来避免每个属性使用一个弱映射.这样你也可以将map的`get`的数量减少到每个方法一个(例如`const _ = privates.get(this); console.log(_.privateProp1);`). (3认同)
  • 为此,访问该属性的代码将需要访问WeakMap对象,该对象通常位于模块内部并且无法访问 (2认同)

小智 22

为了将来对其他观众的参考,我现在听说建议使用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}`)
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这些属性是静态的. (20认同)
  • 我没有向你投票,但你的弱图示例是完全错误的. (8认同)
  • 即 - 您在所有类实例之间共享数据而不是每个实例 - 我可以至少修复它吗? (4认同)
  • 根据MDN,原始数据类型(如符号)不允许作为WeakMap键.[MDN WeakMap文档](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/WeakMap#Description) (2认同)

Ber*_*rgi 12

取决于你问谁 :-)

Maximally minimal classes提案中没有private包含任何属性修饰符,它似乎已成为当前草案.

但是,可能会支持 私有名称,它允许私有属性 - 它们也可能在类定义中使用.

  • 私人名称很可能不会进入ES6,尽管他们正在考虑ES7的某种私人形式. (3认同)

Joh*_*ika 10

使用ES6模块(最初由@ d13提出)对我来说效果很好.它并不能完美地模仿私有财产,但至少你可以确信应该是私人的财产不会泄漏到你的班级之外.这是一个例子:

something.js

let _message = null;
const _greet = name => {
  console.log('Hello ' + name);
};

export default class Something {
  constructor(message) {
    _message = message;
  }

  say() {
    console.log(_message);
    _greet('Bob');
  }
};
Run Code Online (Sandbox Code Playgroud)

然后消费代码可能如下所示:

import Something from './something.js';

const something = new Something('Sunny day!');
something.say();
something._message; // undefined
something._greet(); // exception
Run Code Online (Sandbox Code Playgroud)

更新(重要):

正如@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
Run Code Online (Sandbox Code Playgroud)

  • 适合`private static`. (4认同)

efi*_*les 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我能想到的好处:

  • 我们可以有私有方法(_greet_updateMessage只要我们不采取行动,像私有方法export的引用)
  • 虽然它们不在原型上,但上面提到的方法将节省内存,因为实例在类之外创建一次(而不是在构造函数中定义它们)
  • 因为我们在模块中,所以我们不会泄漏任何全局变量
  • 我们也可以使用绑定_privates对象拥有私有属性

我能想到的一些缺点:

可以在此处找到正在运行的代码段:http://www.webpackbin.com/NJgI5J8lZ


Nik*_*tin 8

是的 - 您可以创建封装属性,但是使用访问修饰符(public | private)至少不能使用ES6.

这是一个如何使用ES6完成的简单示例:

1使用词创建类

2在它的构造函数中使用let OR 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;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在让我们检查一下:

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
Run Code Online (Sandbox Code Playgroud)


Luc*_*iva 7

一种不同的"私人"方法

如果您的IDE支持JSDoc(例如,Webstorm),我决定采用一种更实用的方法,而不是与ES6中当前不可用的私有可见性这一事实作斗争.想法是使用@private标签.就开发而言,IDE将阻止您访问其类外部的任何私有成员.对我来说效果非常好,它对于隐藏内部方法非常有用,因此自动完成功能向我展示了该类实际上要暴露的内容.这是一个例子:

自动完成只显示公共内容

  • 问题是,我们不想通过编辑器访问私有变量,我们不想保护私有变量免受外部影响——这就是 public/private 所做的。如果您的代码完成,您可以从类外部访问(以及重要的想法:**覆盖**)这些变量。您的`@private` 注释无法阻止这些,它只是文档生成的**功能**,而您是 IDE。 (3认同)

Ser*_*gey 7

哦,这么多奇特的解决方案!我平时不关心隐私,所以我用“伪隐私”,因为它的在这里说。但是如果真的关心(如果有一些特殊要求),我会在这个例子中使用类似的东西:

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
Run Code Online (Sandbox Code Playgroud)

函数(构造函数)的另一种可能实现Job

function Job(name){
  var impl = new jobImpl(name);
  this.do = time => impl.do(time),
  this.stop = time => impl.stop(time)
}
Run Code Online (Sandbox Code Playgroud)


kev*_*ned 6

WeakMap

  • IE11支持(符号不是)
  • hard-private(使用符号的道具是软私人的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;
  };
}
Run Code Online (Sandbox Code Playgroud)

然后,在你的课外构建一个引用:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

注意:IE11不支持,但在示例中看起来更干净.


Mar*_*rkM 6

我在寻找“班级私有数据”的最佳实践时遇到了这篇文章。有人提到一些模式会有性能问题。

我根据在线书籍“探索 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种基本模式是:

通过构造函数环境的私有数据

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();
Run Code Online (Sandbox Code Playgroud)

通过构造函数环境 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();
Run Code Online (Sandbox Code Playgroud)

通过命名约定的私有数据

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();
Run Code Online (Sandbox Code Playgroud)

通过 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();
Run Code Online (Sandbox Code Playgroud)

通过符号的私有数据

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();
Run Code Online (Sandbox Code Playgroud)


Nan*_*ard 5

我认为Benjamin 的答案在大多数情况下可能是最好的,直到语言本身支持显式私有变量。

但是,如果由于某种原因您需要阻止访问 with Object.getOwnPropertySymbols(),我考虑使用的一种方法是附加一个唯一的、不可配置的、不可枚举的、不可写的属性,该属性可用作构造中每个对象的属性标识符(例如 unique Symbol,如果您还没有像 an 那样的其他独特属性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"
Run Code Online (Sandbox Code Playgroud)

如果性能成为问题,这种方法比使用 a 的潜在优势WeakMap更快的访问时间

  • 如果我错了,请纠正我,但是这个代码不会包含内存泄漏吗,因为即使对象已经被销毁, privateVars 仍然会存储对象的私有变量? (2认同)

Rob*_* F. 5

就我个人而言,我喜欢bind 运算符 的提议,::然后将其与提到的 @d13 解决方案结合起来,但现在坚持 @d13 的答案,在那里你export为你的类使用关键字并将私有函数放在模块中。

还有一个更难的解决方案,这里没有提到,下面是更实用的方法,并允许它在类中拥有所有私有道具/方法。

私有js

export const get = state => key => state[key];
export const set = state => (key,value) => { state[key] = value; }
Run Code Online (Sandbox Code Playgroud)

测试.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
Run Code Online (Sandbox Code Playgroud)

对此的评论将不胜感激。