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

时间:2016-01-19 05:48:29

标签: javascript node.js ecmascript-6

在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类中声明私有变量和私有方法

4 个答案:

答案 0 :(得分:19)

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

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

让我们完成您的示例代码。这是第一次切割:

<强> person.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);
        }
    }
  }

<强> client.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._name = 'Joe'

使用 _name 等属性,我们可以合并模块和symbols。这是ES6 + / ES2015提供的功能强大且轻量级的信息隐藏技术。

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

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

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

<强> person.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 未被用作名称值的键。

但是,符号是通过反射功能(如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。

答案 1 :(得分:5)

如果您想要与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();
  }
}

如果你看看这个网络是什么,你会注意到它与你的例子没什么不同(只是使用&#34;更清洁&#34;花里胡哨;特别是在添加原型时/ static methods)。它仍然是原型。

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

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

使用this作为密钥。 现在所有原型方法都有对弱映射的闭包访问,而不是制作具有闭包访问权限的特权函数 ...缺点是,只要你想要私有变量,就必须在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为键的弱地图中,可以访问完全隐藏的数据集,这在该模块之外是完全无法访问的。
更糟糕的是,弱图中的键/值不会阻止GC清理它们。
通常情况下,如果他们被留在阵列中,他们会留在那里。如果使用对象的最后一个位置作为弱映射中的键,则会收集它并自动擦除弱映射值(在本机ES6中;内存奖励不能被填充,但对象可以)。 / p>

答案 2 :(得分:0)

这是一个关于这个主题的非常好的帖子。在你的问题之前,我对在ES6中做这件事并不了解,但在回顾之后看起来真的很酷。因此,您创建一个索引到该函数的符号。

这是他们坐下的代码。

var Person = (function() {
    var nameSymbol = Symbol('name');

    function Person(name) {
        this[nameSymbol] = name;
    }

    Person.prototype.getName = function() {
        return this[nameSymbol];
    };

    return Person;
}());

var p = new Person('John');

Private properties in JavaScript

答案 3 :(得分:0)

您可以使用fit_transform完成此操作。

Symbol