如何在 ES6 中使用私有变量? [复制]

Posted

技术标签:

【中文标题】如何在 ES6 中使用私有变量? [复制]【英文标题】:How to work with private variables in ES6? [duplicate] 【发布时间】:2016-03-28 20:17:49 【问题描述】:

在 ES5 中,您可以模拟具有私有和公共变量的类,如下所示:

car.js

function Car() 
    // using var causes speed to be only available inside Car (private)
    var speed = 10;

    // public variable - still accessible outside Car
    this.model = "Batmobile";

    // public method
    this.init = function()

    

但是在 ES6 中,您不能再在构造函数之外声明 var,这实际上使得以 OOP 方式处理类变得更加困难!?

您可以使用 this 在构造函数中声明变量,但默认情况下会公开它们。这很奇怪,因为 ES6 DOES 有一个 get / set 关键字!

class Vehicle 
    constructor(make, year) 
        // the underscore is nice, but these are still public!
        this._make = make;
        this._year = year;
    

    // get and set can be handy, but would make more sense
    // if _make and _year were not accessible any other way!
    get make() 
        return this._make;
    

    get year() 
        return this._year;
    

【问题讨论】:

this._make = make;this.model = "Batmobile"; 的效果相同,你必须使用完全不同的技巧来定义私有变量,更多信息请参见What? Wait. Really? Oh no! (a post about ES6 classes and privacy)。 是的,但是 ES5 示例中的速度是私有的。如何在 ES6 中实现这一点?感谢您的链接!似乎 ES6 类在我们认为它们是真正的 OOP 类之前还有很长的路要走。 【参考方案1】:

ES6 标准没有提供定义私有变量的新方法。

事实上,新的 ES6 class 只是围绕常规基于原型的构造函数的语法糖。

getset 关键字提供了一种简化 ES5 自定义 getter 和 setter 定义的方法,这些自定义 getter 和 setter 以前使用 Object.defineProperty() 描述符定义

你能做的最好的就是将这些技术与Symbols 或WeakMaps 一起使用

下面的例子展示了使用 Wea​​kMap 来存储私有属性。

// myModule.js
const first_name = new WeakMap();

class myClass 
     constructor (firstName) 
          first_name.set(this, firstName);
     

     get name() 
          return first_name.get(this);
     


export default myClass;

我指的是由 David Vujic What? Wait. Really? Oh no! (a post about ES6 classes and privacy) 撰写的文章,其想法是使用 Wea​​kMaps。

【讨论】:

我想这可以工作,但我不确定这是否比 ES5 方式好得多。虽然我希望 ES6 能简化问题,但它似乎更令人费解。 @Kokodoko 这取决于你做什么,新标准提供了许多其他不错的功能,但不幸的是真正的 OOP 类不是其中之一。现在使用 ES6 模块,数据封装变得更加容易,javascript 肯定在朝着正确的方向发展。 是的,模块很好!看起来很奇怪,ES6 实际上删除了将 var 设为私有的简单技巧! (在我的第一个示例中,var speed=10)。换句话说,它们让封装的使用变得更难,而不是更容易。 @Kokodoko 实际上旧的 ES5 技巧仍然存在,我认为你理解错了。您可以在构造函数中定义变量,但丑陋的部分是您还必须在那里定义自定义 getter/setter,从而增加内存中实例的总指纹。请参阅Fiddle 是的,但就像我说的,现在你的构造函数中有函数和变量初始化。它有效,但我认为它违背了让类具有构造函数方法的想法。在其他语言中,构造函数只是一个在创建新实例时执行的函数。你甚至可以省略构造函数!在我看来,这个 ES5 示例更清晰:jsfiddle.net/35f9q7p6/2【参考方案2】:

2016 年 1 月更新 - 虽然我发现接受的答案中给出的方法是正确的,但我想说在 ES2015+ 中使用模块和符号是一种有效的信息隐藏技术(但 Class使用符号的属性将被隐藏,而不是严格私有的)。

通过结合使用 ES2015 模块(只会导出您声明为导出的内容)和 ES2015 symbols,可以实现有效的轻量级信息隐藏。 Symbol 是一种新的内置类型。每个新的符号值都是唯一的。因此可以用作对象的键。

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

使用您的代码的快速示例:

vehicle.js

const s_make = Symbol();
const s_year = Symbol();

export class Vehicle 

  constructor(make, year) 
    this[s_make] = make;
    this[s_year] = year;
  

  get make() 
    return this[s_make];
  

  get year() 
    return this[s_year];
  

并使用模块vehicle.js

client.js

import Vehicle from './vehicle';
const vehicle1 = new Vehicle('Ford', 2015);
console.log(vehicle1.make); //Ford
console.log(vehicle1.year); // 2015

然而,符号虽然是独一无二的,但实际上并不是私有的,因为它们是通过 Object.getOwnPropertySymbols 等反射功能暴露出来的...

const vals = Object.getOwnPropertySymbols(vehicle1);
vehicle1[vals[0]] = 'Volkswagon';
vehicle1[vals[1]] = 2013;
console.log(vehicle1.make); // Volkswagon
console.log(vehicle1.year); // 2013

外卖消息 - 模块通常是隐藏某些内容的好方法,因为如果不导出,则无法在模块外部使用,并且与私有存储的符号一起用作键,然后类属性也可以隐藏(但是不一定是私人的)。类声明特别适合作为构造良好的模块(amd、commonjs 或 es6/2015)的一部分。

【讨论】:

Object.getOwnPropertySymbols(obj) 返回对象中使用的符号列表(就像Object.keys(obj) 一样)。那时,只需遍历列表即可。 Symbol 与保证完美隐私无关,更多的是保证每个引用都是唯一的,除非故意这样做,同时还要让它们远离循环和其他构造(同样,除非有意)。 @Norguard - 非常感谢您指出这一点。更新了我的答案 - 我坐在栅栏上,原始答案中的“信息隐藏”。 Babel 文档也说明了您提出的观点 - babeljs.io/docs/learn-es2015 感谢您对符号的非常清晰的解释!它与私有并不完全相同,但它肯定可以起到防止意外访问变量的目的。【参考方案3】:

但是在 ES6 中,你不能再在构造函数之外声明 vars

而且你不需要。您也没有在 ES5 构造函数中执行此操作。您可以将您的代码逐字翻译为

class Car 
    constructor() 
        // local variable
        var speed = 10;

        // public property
        this.model = "Batmobile";

        // public method
        this.init = () => 
            …
        ; // using an arrow function here simplifies things
    

【讨论】:

嗯,但是现在您在构造函数中定义了所有方法,与其他 OOP 语言相比,这似乎很奇怪。在构造函数之外,您无法以这种方式访问​​速度! @Kokodoko:当然,当您需要私有变量时,您会这样做。它与 ES5 中的完全相同。如果要使用原型(实际上性能差异可以忽略不计),则必须使用公共属性。 @Kokodoko: "但是现在你在构造函数内部定义了所有方法"这正是你在第一个示例 (ES5) 中所做的。 是的,但该示例并未包含在另一个函数中。这样,您似乎在滥用类构造函数来定义整个类。构造函数不应该是这样工作的,至少在任何其他 OOP 语言中都不应该这样。 @Kokodoko:“构造函数不应该这样工作”我同意。但是由于您已经在 ES5 中“违反”了该“规则”,因此您不妨在 ES6 中这样做。或者不做也不要试图让 JS 做它不支持的事情。【参考方案4】:

与 ES5 中的方法相同:在构造函数而不是原型中定义必须访问私有变量的方法,从而使它们成为特权方法。

否则就没有好的方法允许原型方法访问私有数据,但仍将其隐藏在外部。您可以尝试符号、弱图或握手,但 IMO 没有一个是完美的。请参阅accessing private member variables from prototype-defined functions 了解一些想法。

【讨论】:

我想这意味着仍然无法像在其他 OOP 语言中那样在 ES6 中编写类,学习起来有点令人失望......

以上是关于如何在 ES6 中使用私有变量? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥我可以在复制构造函数中访问私有变量?

如何在 Python 中使私有变量可访问? [复制]

Es6 的类(class)

JUnit 测试私有变量? [复制]

浅谈class私有变量

公共类,但私有成员变量? [复制]