自从世界从 ES5 转到 ES6, 为了让 JavaScript 代码库更加美观, ES 语法 (不仅是语法) 发生了巨大的变化. 尽管其他所有语法都有所改进, 但有一件令开发人员困扰的事情是, 如何在类中声明私有变量. 但不幸的是, 在 ES6 中没有专门的语法.
没有! ES6 中没有专门的语法来声明私有变量. 不过有一个提案 https://github.com/tc39/proposal-private-fields .
新提案(还没有实现)
- class MyClass{
- #private1;
- #private2;
- getPrivate1(){
- return this.private1;
- }
- }
上面的语法是提交给 TC39 的一个提案, 还没被批准, 并且在不久的将来肯定也不能用, 但我们希望在未来的一些 ES 版本中可以有这种语法.
声明私有变量的最佳方式
你可以创建模块, 而模块中的所有东西都是私有的, 直到以及除非你使用 exports 公开它.
- let private1 = new WeakMap();
- let private2 = new WeakMap();
- class MyClass {
- constructor() {
- this.setPrivate1("something");
- private2.set(this, "something else");
- }
- getPrivate1() {
- return private1.get(this);//"something"
- }
- getPrivate2() {
- return private2.get(this);//"something else"
- }
- setPrivate1(val) {
- private1.set(this, val);
- }
- }
- module.exports = MyClass;
为什么不用 let private1 = "something"; 这种方式
为什么我们不声明一个变量 private1, 然后将它所需的值赋给它呢? 原因是, 变量 private1 是一个会在 MyClass 的所有实例中共享的单一变量. 所以只要有任何一个实例修改了 private1, 那么同样的变化就会反映到其它实例上.
为什么是用 WeakMap 而不是 Map?
Map 和 WeakMap 之间的区别在于: 对于 WeakMap 来说, 如果键对象准备好被垃圾回收, 就会自动删除值; 而对于 Map 来说, 它会一直维持一个对键对象的引用, 从而会导致内存泄漏.
声明私有变量的其它方式
还有其它几种方式来声明私有变量, 每种方式各有其优缺点. 其中的两种方式描述如下.
命名约定
多年来人们一直在私有变量的名称中使用下划线. 如果你根据这个约定, 并且信任其他开发人员的话, 就可以用这种方法. 不过, 这并不能确保数据的安全性, 因为任何人如果想的话, 他可以用或者甚至修改该数据. 如果你正在开发一个库, 那么这种方法强烈不推荐.
- class MyClass {
- constructor() {
- this._private1 = "something";
- this.public = "something else";
- }
- }
- Object.assign
通过用 Object.assign, 你也能使用私有变量, 并且能确保数据安全. 但是这种方法的问题是, 要读写该私有变量的方法不能是原型方法. 这些方法与私有变量一起, 也必须写在构造器内. 这不仅让它对于其他开发人员来说很难去读懂, 而且也是一种低效率的声明那个函数的方式; 因为这些函数会被在该类的每个实例中重复(不是共享).
- class MyClass{
- constructor(){
- var private1 = "something";
- Object.assign(this, {
- getPrivate(){
- return private1;
- },
- setPrivate(val){
- private1 = val;
- }
- });
- }
- anotherMethod(){
- console.log(private1); //ERROR
- }
- }
结论
有些人可能会推荐用 ES6 Symbol 来创建私有变量. 几个月前这种方式还是有效的, 不过 ES6 已经修改了其草案, 现在 Symbol 键从一个对象的外部也可以访问了. 所以遵循这种方式没有意义. 不过, 最佳实践是把你的程序分为模块, 并且用我展示的第一个方式来声明私有变量. 使用这种方式能确保数据安全, 该类中所有方法将都能访问该数据. 我所看到的唯一缺点是, 它给了你一种骇客式的感觉, 因为与命名约定方式中相比, 私有变量与类没有太大关系.
相关帖子
[ES6 Map vs WeakMap vs plain Objects - Describing the differences][55]
[Clone an object in vanilla JS - multiple ways][56]
[Understanding Ember Objects, computed properties and observers - Ember.js Tutorial part 4][57]
来源: https://juejin.im/entry/5ae29713518825672205ef3e