ES6私有变量的实现

1. 约定前端

实现闭包

class Example {
 constructor() {
   this._private = 'private';
 }
 getName() {
   return this._private
 }
}

var ex = new Example();

console.log(ex.getName()); // private
console.log(ex._private); // private

优势函数

  • 写法简单
  • 调试方便
  • 兼容性好

缺点性能

  • 外部能够访问和修改
  • 语言没有配合的机制,如 for in 语句会将全部属性枚举出来
  • 命名冲突

2. 闭包学习

实现一优化

/**
 * 实现一
 */
class Example {
 constructor() {
  var _private = '';
  _private = 'private';
  this.getName = function() {return _private}
 }
}
 
var ex = new Example();
 
console.log(ex.getName()); // private
console.log(ex._private); // undefined

前端全栈学习交流圈:866109386,面向1-3经验年前端开发人员,帮助突破技术瓶颈,提高思惟能力,群内有大量PDF可供自取,更有干货实战项目视频进群免费领取。this

优势编码

  • 无命名冲突
  • 外部没法访问和修改

缺点调试

  • constructor 的逻辑变得复杂。构造函数应该只作对象初始化的事情,如今为了实现私有变量,必须包含部分方法的实现,代码组织上略不清晰。
  • 方法存在于实例,而非原型上,子类也没法使用 super 调用
  • 构建增长一点点开销

实现二code

/**
 * 实现二
 */
const Example = (function() {
 var _private = '';
 
 class Example {
  constructor() {
   _private = 'private';
  }
  getName() {
   return _private;
  }
 }
 
 return Example;
 
})();
 
var ex = new Example();
 
console.log(ex.getName()); // private
console.log(ex._private); // undefined

优势

  • 无命名冲突
  • 外部没法访问和修改

缺点

  • 写法有一点复杂
  • 构建增长一点点开销

3. Symbol

实现

const Example = (function() {
  var _private = Symbol('private');
 
  class Example {
    constructor() {
     this[_private] = 'private';
    }
    getName() {
     return this[_private];
    }
  }
 
  return Example;
})();
 
var ex = new Example();
 
console.log(ex.getName()); // private
console.log(ex.name); // undefined

前端全栈学习交流圈:866109386,面向1-3经验年前端开发人员,帮助突破技术瓶颈,提高思惟能力,群内有大量PDF可供自取,更有干货实战项目视频进群免费领取。

优势

  • 无命名冲突
  • 外部没法访问和修改
  • 无性能损失

缺点

  • 写法稍微复杂
  • 兼容性也还好

4. WeakMap

实现

/**
 * 实现一
 */
const _private = new WeakMap();
 
class Example {
 constructor() {
  _private.set(this, 'private');
 }
 getName() {
   return _private.get(this);
 }
}
 
var ex = new Example();
 
console.log(ex.getName()); // private
console.log(ex.name); // undefined

若是这样写,你可能以为封装性不够,你也能够这样写:

/**
 * 实现二
 */
const Example = (function() {
 var _private = new WeakMap(); // 私有成员存储容器
 
 class Example {
  constructor() {
   _private.set(this, 'private');
  }
  getName() {
    return _private.get(this);
  }
 }
 
 return Example;
})();
 
var ex = new Example();
 
console.log(ex.getName()); // private
console.log(ex.name); // undefined

优势

  • 无命名冲突
  • 外部没法访问和修改

缺点

  • 写法比较麻烦
  • 兼容性有点问题
  • 有必定性能代价

5. 最新提案

class Point {
 #x;
 #y;
 
 constructor(x, y) {
  this.#x = x;
  this.#y = y;
 }
 
 equals(point) {
  return this.#x === point.#x && this.#y === point.#y;
 }
}

那么为何不直接使用 private 字段呢?好比说这样:

class Foo {
 private value;
 
 equals(foo) {
  return this.value === foo.value;
 }
}

简单点来讲,就是嫌麻烦,固然也有性能上的考虑……

举个例子,若是咱们不使用 #,而是使用 private 关键字:

class Foo {
 private value = '1';
 
 equals(foo) {
  return this.value === foo.value;
 }
}
 
var foo1 = new Foo();
var foo2 = new Foo();
 
console.log(foo1.equals(foo2));

在这里咱们新建了两个实例,而后将 foo2 做为参数传入了 foo1 的实例方法中。

那么咱们能够获取 foo2.value 的值吗?若是咱们直接 foo2.value 确定是获取不到值的,毕竟是私有变量,但是 equals 是 Foo 的一个类方法,那么能够获取到的吗?

答案是能够的。

其实这点在其余语言,好比说 Java 和 C++ 中也是同样的,类的成员函数中能够访问同类型实例的私有变量,这是由于私有是为了实现“对外”的信息隐藏,在类本身内部,没有必要禁止私有变量的访问,你也能够理解为私有变量的限制是以类为单位,而不是以对象为单位,此外这样作也能够为使用者带来便利。

既然获取值是能够的,那么打印的结果应该为 true,可是若是咱们传入的值不是 Foo 的实例,而是一个其余对象呢?

var foo1 = new Foo();
 
console.log(foo1.equals({
 value: 2
}));

前端全栈学习交流圈:866109386,面向1-3经验年前端开发人员,帮助突破技术瓶颈,提高思惟能力,群内有大量PDF可供自取,更有干货实战项目视频进群免费领取。

固然这里代码也是能够正常运行的,可是对于编译器来讲,就有一点麻烦了,由于编译器不知道 value 究竟是 foo 的正常属性仍是私有属性,因此编译器须要作判断,先判断 foo 是否是 Foo 的实例,而后再接着获取值。

这也意味着每次属性访问都须要作这样一个判断,而引擎已经围绕属性访问作了高度优化,懒得改,并且还下降速度。

不过除了这个工做以外,还会有一些其余的内容须要考虑,好比说:

  • 你必须将私有的 key 编码进每一个词法环境
  • for in 能够遍历这些属性吗?
  • 私有属性和正常属性同名的时候,谁会屏蔽谁?
  • 怎么防止私有属性的名称不被探测出来。

关于使用 # 而不使用 private 更多的讨论能够参考这个Issue。

固然这些问题均可以被解决啦,就是麻烦了点。

而若是你选择 #,实现的方式将跟 JavaScript 对象属性彻底没有关系,将会使用 private slots 的方式以及使用一个新的 slot 查找语法,总之就是会比 private 的实现方式简单不少。

相关文章
相关标签/搜索