es6类中如何声明私有变量和私有方法

新手上路,请多包涵

在 es5 中我们使用构造函数

function Person(name,gender){

    var initial ="";    // we use var key word to make variable private

    function getNameWithInitial(){ // this is the private method to get name with initial
        console.log(this);
        initial = this.gender ==="male"?"Mr. ":"Mrs. ";
        return initial + this.name;
    }

    this.name = name;
    this.gender = gender;
    this.getName = function(){
        return getNameWithInitial.call(this);
    }

}

var manas = new Person("Manas","male");

console.log(manas.getName());

我的问题是如何在 es6 类中声明私有变量和私有方法

原文由 manas 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 437
2 个回答

实现这一目标的一种方法是使用另一个 ES2015 功能,称为 modules

您可能已经熟悉 AMD 模块或 commonJS 模块(由 Nodejs 使用)。好吧,ES6 / ES2015 为 JS 带来了一个标准——我们称它们为 ES6 模块,但它们现在是 JS 语言的一部分。一旦有了模块,就可以对私有函数和对象变量进行信息隐藏。请记住,只有您“导出”的内容对客户端调用代码可见。

让我们完成您的示例代码。这是第一个剪辑:

人.js

  const getNameWithInitial = function () {
      let initial = this._gender === 'male' ?
      'Mr. ' :
      'Mrs. ';
      return initial + this._name;
    }

  export class Person {

    constructor(name, gender) {
        this._name = name;
        this._gender = gender;
      }

      get name() {
          return getNameWithInitial.call(this);
        }
    }
  }

客户端.js

 import {Person} from './person';
const manas = new Person('Manas', 'male');
console.log(manas.name);  // this calls what was your getName function

现在, getNameWithInitial 函数实际上是私有的,因为它没有被导出,所以 client.js 看不到它。

但是,对于 Person 类,我们仍然存在问题,因为它是导出的。目前你可以走到 manas 对象并做:

 manas._name = 'Joe'

使用 _name 等属性,我们可以组合模块和 符号。这是 ES6+/ES2015 提供的一种强大而轻量级的信息隐藏技术。

Symbol 是一种新的内置类型。每个新的 Symbol 值都是唯一的。因此可以用作对象的键。

如果客户端调用代码不知道用于访问该密钥的符号,则他们无法获取它,因为该符号未导出。

让我们看看我们修改后的代码,使用符号和模块来隐藏类属性。

人.js

 const s_name = Symbol();
const s_gender = Symbol();

const getNameWithInitial = function () {
  let initial = this[s_gender] === 'male' ?
    'Mr. ' :
    'Mrs. ';
  return initial + this[s_name];
}

export class Person {

  constructor(name, gender) {
    this[s_name] = name;
    this[s_gender] = gender;
  }

  get name() {
    return getNameWithInitial.call(this);
  }
}

所以,现在客户不能只做:

  manas._name = 'Joe'

因为 _name 没有被用作 name 值的键。

但是,符号是通过诸如 Object.getOwnPropertySymbols 之类的反射功能公开的,因此请注意,使用此技术它们并不是“完全”私有的。

 import {Person} from './person';
const manas = new Person('Manas', 'male');
const vals = Object.getOwnPropertySymbols(manas);
manas[vals[0]] = 'Joanne';
manas[vals[1]] = 'female';

外卖信息——模块通常是隐藏某些东西的好方法,因为如果不导出则不能在模块外部使用,并且与私有存储的符号一起用作键,那么类属性也可以隐藏(但不是严格私有的) ).今天使用模块可用于构建工具,例如。 webpack / browserify 和 babel。

原文由 arcseldon 发布,翻译遵循 CC BY-SA 3.0 许可协议

如果你想要一个类似于 ES5 的解决方案,那很简单; constructor 简单地变成了闭包,你添加任何应该记住私有状态的方法/对象。

如果不使用一些特权 getter,原型方法无法访问初始构造函数的闭包:

 class Person {

  constructor ({ name, age, deepDarkSecret }) {
    const bribe = () => console.log(`Give me money, or everybody will know about ${ redactRandom(deepDarkSecret) }`);

    Object.assign(this, { name, age }); // assigning publicly accessible values
    Object.assign(this, { bribe }); // assign "privileged" methods (functions with closure-reference to initial values)
  }

  recallSecret () {
    console.log("I'm just a prototyped method, so I know nothing about any secret, unless I use a privileged function...");
    this.bribe();
  }
}

如果你看看这给你带来了什么,你会注意到它与你的例子并没有太大的不同(只是使用“更干净”的花里胡哨;特别是在添加原型/静态方法时)。它仍然是下面的原型。

如果您有幸使用任何类型的模块/导出(ES6 是理想的),那么使用另一种数据类型, 您可以拥有真正的隐私,而不必自己清理

它看起来有点像黑客。它可能会变得不那么丑陋,并希望成为未来更清洁的东西的基础,但如果你想要基于实例的私有访问,即使是原型方法,那么制作 一个 WeakMap ,在里面您要从中导出课程的模块。

使用 this 作为您的密钥。与其创建具有闭包访问权限的特权函数,不如现在所有原型方法都具有对 weakmap 的闭包访问权限……

…缺点是任何时候你想要私有变量,你都必须在 WeakMap 中查找它们,而不是将它们从 this 中拉出来。

 const personalBaggage = new WeakMap();

class Person {
  constructor ({ name, age, darkestMoment }) {
    const privates = { name, age, darkestMoment };
    personalBaggage.add(this, privates);
  }

  recallDarkestHour () {
    const { darkestMoment } = personalBaggage.get(this);
    console.log(darkestMoment);
  }
}

export default Person;

只要您没有丢失 this 中的引用,这应该就可以了。

与使用 Symbol 不同,无论您多么努力,都无法获得对私有对象的引用。

如果您知道对象上的符号,则可以使用这些符号查找属性。

获取任何对象上的符号列表只是一个函数调用。 Symbol 不是要保密;它是关于保护事物免受命名冲突的影响,并定义可用于任何对象/函数但不会在循环中被拾取、不会被意外调用或覆盖等的通用符号。

使用 this 作为密钥将私有数据包存储在 weakmap 中,使您可以访问完全隐藏的数据集,该数据集在该模块之外是完全无法访问的。

更好的是,弱映射中的键/值不会阻止 GC 清理它们。

通常情况下,如果他们留在一个阵列中,他们会留在那里。如果对象最后一次被用作 weakmap 中的键,那么它会被收集并自动擦除 weakmap 值(在原生 ES6 中;内存奖励不能被 polyfill,但对象可以)。

原文由 Norguard 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题