前言

在阅读 《ECMAScript 6 入门》的时候,零散的看到有私有变量的实现,所以在此总结一篇。

1. 约定

实现

  1. class Example {
  2. constructor() {
  3. this._private = 'private';
  4. }
  5. getName() {
  6. return this._private
  7. }
  8. }
  9. var ex = new Example();
  10. console.log(ex.getName()); // private
  11. console.log(ex._private); // private

优点

  1. 写法简单
  2. 调试方便
  3. 兼容性好

缺点

  1. 外部可以访问和修改
  2. 语言没有配合的机制,如 for in 语句会将所有属性枚举出来
  3. 命名冲突

2. 闭包

实现一

  1. /**
  2. * 实现一
  3. */
  4. class Example {
  5. constructor() {
  6. var _private = '';
  7. _private = 'private';
  8. this.getName = function() {return _private}
  9. }
  10. }
  11. var ex = new Example();
  12. console.log(ex.getName()); // private
  13. console.log(ex._private); // undefined

优点

  1. 无命名冲突
  2. 外部无法访问和修改

缺点

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

实现二

  1. /**
  2. * 实现二
  3. */
  4. const Example = (function() {
  5. var _private = '';
  6. class Example {
  7. constructor() {
  8. _private = 'private';
  9. }
  10. getName() {
  11. return _private;
  12. }
  13. }
  14. return Example;
  15. })();
  16. var ex = new Example();
  17. console.log(ex.getName()); // private
  18. console.log(ex._private); // undefined

优点

  1. 无命名冲突
  2. 外部无法访问和修改

缺点

  1. 写法有一点复杂
  2. 构建增加一点点开销

3. Symbol

实现

  1. const Example = (function() {
  2. var _private = Symbol('private');
  3. class Example {
  4. constructor() {
  5. this[_private] = 'private';
  6. }
  7. getName() {
  8. return this[_private];
  9. }
  10. }
  11. return Example;
  12. })();
  13. var ex = new Example();
  14. console.log(ex.getName()); // private
  15. console.log(ex.name); // undefined

优点

  1. 无命名冲突
  2. 外部无法访问和修改
  3. 无性能损失

缺点

  1. 写法稍微复杂
  2. 兼容性也还好

4. WeakMap

实现

  1. /**
  2. * 实现一
  3. */
  4. const _private = new WeakMap();
  5. class Example {
  6. constructor() {
  7. _private.set(this, 'private');
  8. }
  9. getName() {
  10. return _private.get(this);
  11. }
  12. }
  13. var ex = new Example();
  14. console.log(ex.getName()); // private
  15. console.log(ex.name); // undefined

如果这样写,你可能觉得封装性不够,你也可以这样写:

  1. /**
  2. * 实现二
  3. */
  4. const Example = (function() {
  5. var _private = new WeakMap(); // 私有成员存储容器
  6. class Example {
  7. constructor() {
  8. _private.set(this, 'private');
  9. }
  10. getName() {
  11. return _private.get(this);
  12. }
  13. }
  14. return Example;
  15. })();
  16. var ex = new Example();
  17. console.log(ex.getName()); // private
  18. console.log(ex.name); // undefined

优点

  1. 无命名冲突
  2. 外部无法访问和修改

缺点

  1. 写法比较麻烦
  2. 兼容性有点问题
  3. 有一定性能代价

5. 最新提案

  1. class Point {
  2. #x;
  3. #y;
  4. constructor(x, y) {
  5. this.#x = x;
  6. this.#y = y;
  7. }
  8. equals(point) {
  9. return this.#x === point.#x && this.#y === point.#y;
  10. }
  11. }

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

  1. class Foo {
  2. private value;
  3. equals(foo) {
  4. return this.value === foo.value;
  5. }
  6. }

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

举个例子,如果我们不使用 #,而是使用 private 关键字:

  1. class Foo {
  2. private value = '1';
  3. equals(foo) {
  4. return this.value === foo.value;
  5. }
  6. }
  7. var foo1 = new Foo();
  8. var foo2 = new Foo();
  9. console.log(foo1.equals(foo2));

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

那么我们可以获取 foo2.value 的值吗?如果我们直接 foo2.value 肯定是获取不到值的,毕竟是私有变量,可是 equals 是 Foo 的一个类方法,那么可以获取到的吗?

答案是可以的。

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

既然获取值是可以的,那么打印的结果应该为 true,但是如果我们传入的值不是 Foo 的实例,而是一个其他对象呢?

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

当然这里代码也是可以正常运行的,但是对于编译器来说,就有一点麻烦了,因为编译器不知道 value 到底是 foo 的正常属性还是私有属性,所以编译器需要做判断,先判断 foo 是不是 Foo 的实例,然后再接着获取值。

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

不过除了这个工作之外,还会有一些其他的内容需要考虑,比如说:

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

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

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

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

原文链接
本文为云栖社区原创内容,未经允许不得转载。

ES6 系列之私有变量的实现的更多相关文章

  1. ES6系列_3之变量的解构赋值

    ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构. 解构有三种类型: 1.数组的解构赋值 (1)简单的数组解构 以前,我们给变量赋值是直接单个指定值,比如: let a=0; ...

  2. es6系列-变量声明

    es6系列所有文章都是阅读阮一峰老师的<ES6标准入门>(第2版)所做的读书笔记.方便日后查阅相关基础知识. git地址: https://github.com/rainnaZR/es6- ...

  3. ES6 class类中定义私有变量

    ES6 class类中定义私有变量 class类的不足 看起来, es6 中 class 的出现拉近了 JS 和传统 OOP 语言的距离.但是,它仅仅是一个语法糖罢了,不能实现传统 OOP 语言一样的 ...

  4. 【原】iOS动态性(二):运行时runtime初探(强制获取并修改私有变量,强制增加及修改私有方法等)

    OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtime机制让我们可以在程序运行时动态修改类.对象中的所有属性.方法,就算是私有方法以及私有属性都是可以动 ...

  5. [js高手之路] es6系列教程 - 对象功能扩展详解

    第一:字面量对象的方法,支持缩写形式 //es6之前,这么写 var User = { name : 'ghostwu', showName : function(){ return this.nam ...

  6. iOS动态性 运行时runtime初探(强制获取并修改私有变量,强制增加及修改私有方法等)

    借助前辈的力量综合一下资料. OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtime机制让我们可以在程序运行时动态修改类.对象中的所有属性.方法,就算是 ...

  7. [js高手之路] es6系列教程 - 迭代器,生成器,for...of,entries,values,keys等详解

    接着上文[js高手之路] es6系列教程 - 迭代器与生成器详解继续. 在es6中引入了一个新的循环结构for ....of, 主要是用来循环可迭代的对象,那么什么是可迭代的对象呢? 可迭代的对象一般 ...

  8. es5 温故而知新 创建私有成员、私有变量、特权变量的方法

    其实js是不支持私有变量的.哪怕到es6的class语法.虽然有许多变相的方式.但非常冗余而不推崇. 这里介绍的实际上也不是class语法,而是普通的函数,并且利用IIFE(闭包)的方式来实现私有. ...

  9. 浅谈class私有变量

    class 的前世今生 在 es6 之前,虽然 JS 和 Java 同样都是 OOP (面向对象)语言,但是在 JS 中,只有对象而没有类的概念. 在 JS 中,生成实例对象的传统方法是通过构造函数, ...

随机推荐

  1. poi实现Excel文件的读取

    1.前端代码 $("#upload").on('click', function() { var formData = new FormData(); var name = $(& ...

  2. java下载Excel模板(工具类)

    一次文件下载记录 一次不成熟的文件下载操作记录,希望能对需要的人有所帮助. 1.前端代码 $("#downloadModel").click(function(){ var mod ...

  3. Parsing error was found in mapping #{}. Check syntax #{property|(expression), var1=value1, var2=val错误

    xml中有一个语法错误,整个都错了, 虽然只调用了select查询的方法,但是update中也有语法错误

  4. centos 6.5升级openssl

    1.下载升级版本 wget https://www.openssl.org/source/openssl-1.1.0i.tar.gz 2.安装 zlib zlib-devel yum -y insta ...

  5. (PMP)解题技巧和典型题目分析(0903-2班)

    1.计算题 ,5 2.概念题,少 3.情景题,很多 C B C D ------------------------------------------------------------------ ...

  6. APIView源码简单分析图

    APIView源码简单分析 !声明:下面这个dispatch分发方法不在是父类View里的dispatch了,APIView重新封装了这个dispatch.(整个核心就是initialize_requ ...

  7. 回头来学习wpf的FlowDocument

    学习了一段时间的electron,其实是一个神奇的开发方式,让人神往.但其打印和ocx或是activeX方面还是让我不大放心去使用.毕竟当前首要任务还是window的应用开发. 于是重新学习wpf的F ...

  8. ASCII、Unicode、UTF-8以及Python3编码问题

    编码问题,其实的确是个很烦人的问题,一开始觉得不需要看,到后来出现问题,真的是抓狂, 而像我们这些刚刚涉及到这些问题的小白来说,更是无从下手,所以查阅资料,总结理解下各个概念以及Python3的编码问 ...

  9. Calendar and GregorianCalendar

    1.GregorianCalendar是Calendar的一个具体子类,提供了世界上大多数国家/地区使用的标准日历系统 2.注意 (1)月份:1月到12月[0-11] (2)星期:周日到周六[1-7] ...

  10. 转发: windows如何管理内存

    (1)有三种方法:虚拟内存,内存映射文件,内存堆栈. 虚拟内存是将页文件加载到内存,适用于比较大的对象或结构: 内存映射文件是将磁盘上文件加载到内存,适用于大文件和单机的进程间内存共享: 堆栈就是动态 ...