详解ES6系列之私有变量的实现


Posted in Javascript onNovember 21, 2018

前言

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

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

优点

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

缺点

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

实现二

/**
 * 实现二
 */
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

优点

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

缺点

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

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
}));

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

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

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

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

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

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
基于jquery的跨域调用文件
Nov 19 Javascript
javascript showModalDialog 内跳转页面的问题
Nov 25 Javascript
contains和compareDocumentPosition 方法来确定是否HTML节点间的关系
Sep 13 Javascript
jquery左边浮动到一定位置时显示返回顶部按钮
Jun 05 Javascript
jQuery实现进度条效果代码
Dec 17 Javascript
jQuery中的siblings用法实例分析
Dec 24 Javascript
javascript嵌套函数和在函数内调用外部函数的区别分析
Jan 31 Javascript
js根据手机客户端浏览器类型,判断跳转官网/手机网站多个实例代码
Apr 30 Javascript
jQuery原理系列-常用Dom操作详解
Jun 07 Javascript
JS实现评价的星星功能
Aug 20 Javascript
Vue实现用户自定义字段显示数据的方法
Aug 28 Javascript
Vue在chrome44偶现点击子元素事件无法冒泡的解决方法
Dec 15 Javascript
jQuery实现上下滚动公告栏详细代码
Nov 21 #jQuery
Vue.js 中 axios 跨域访问错误问题及解决方法
Nov 21 #Javascript
vue  directive定义全局和局部指令及指令简写
Nov 20 #Javascript
详解Vue.js在页面加载时执行某个方法
Nov 20 #Javascript
element vue validate验证名称重复 输入框与后台重复验证 特殊字符 字符长度 及注意事项小结【实例代码】
Nov 20 #Javascript
jQuery 同时获取多个标签的指定内容并储存为数组
Nov 20 #jQuery
一步步教你利用Docker设置Node.js
Nov 20 #Javascript
You might like
如何使用脚本模仿登陆过程
2006/11/22 PHP
基于initPHP的框架介绍
2013/04/18 PHP
浅析51个PHP处理字符串的函数
2013/08/02 PHP
浅析虚拟主机服务器php fsockopen函数被禁用的解决办法
2013/08/07 PHP
php短信接口代码
2016/05/13 PHP
使用户点击后退按钮使效三行代码
2007/07/07 Javascript
推荐40款强大的 jQuery 导航插件和教程(上篇)
2012/09/14 Javascript
使用jQuery设置disabled属性与移除disabled属性
2014/08/21 Javascript
javascript实现带下拉子菜单的导航菜单效果
2015/05/14 Javascript
更靠谱的H5横竖屏检测方法(js代码)
2016/09/13 Javascript
js获取Get值的方法
2016/09/29 Javascript
Bootstrap 3.x打印预览背景色与文字显示异常的解决
2016/11/06 Javascript
jQuery简单自定义图片轮播插件及用法示例
2016/11/21 Javascript
jquery表单验证插件validation使用方法详解
2017/01/20 Javascript
Node.JS中事件轮询(Event Loop)的解析
2017/02/25 Javascript
JS表单提交验证、input(type=number) 去三角 刷新验证码
2017/06/21 Javascript
vue 页面加载进度条组件实例
2018/02/05 Javascript
Vue实现自定义下拉菜单功能
2018/07/16 Javascript
微信小程序开发之map地图组件定位并手动修改位置偏差
2019/08/17 Javascript
JQuery常用简单动画操作方法回顾与总结
2019/12/07 jQuery
javascript 原型与原型链的理解及应用实例分析
2020/02/10 Javascript
全面解析js中的原型,原型对象,原型链
2021/01/25 Javascript
[58:18]2018DOTA2亚洲邀请赛3月29日 小组赛B组 iG VS Mineski
2018/03/30 DOTA
[47:42]完美世界DOTA2联赛PWL S2 GXR vs Ink 第一场 11.19
2020/11/20 DOTA
Python的迭代器和生成器使用实例
2015/01/14 Python
用python处理MS Word的实例讲解
2018/05/08 Python
Python判断以什么结尾以什么开头的实例
2018/10/27 Python
使用python itchat包爬取微信好友头像形成矩形头像集的方法
2019/02/21 Python
Python 循环终止语句的三种方法小结
2019/06/24 Python
python 8种必备的gui库
2020/08/27 Python
MoviePy常用剪辑类及Python视频剪辑自动化
2020/12/18 Python
python文件路径操作方法总结
2020/12/21 Python
俄罗斯家居用品购物网站:Евродом
2020/11/21 全球购物
《小鹰学飞》教学反思
2014/04/23 职场文书
海南召开党的群众路线教育实践活动总结大会新闻稿
2014/10/21 职场文书
2015年学校党支部工作总结
2015/04/01 职场文书