搜索
 找回密码
 立即注册

QQ鐧诲綍

鍙渶涓姝ワ紝蹇熷紑濮

扫一扫,访问微社区

js中模拟实现私有属性

查看: 1101| 评论: 0| 发布者: 孙雯雯

??? ??С
简介:今天在看《你不知道的js》这本书时,无意看到Object还有个方法叫做 getOwnPropertySymbols() ,用来获取对象的Symbol属性。记得之前看过一些文章说,可以使用Symbol来实现私有属性,如果能直接使用 getOwnPropertySy ...
今天在看《你不知道的js》这本书时,无意看到Object还有个方法叫做 getOwnPropertySymbols() ,用来获取对象的Symbol属性。记得之前看过一些文章说,可以使用Symbol来实现私有属性,如果能直接使用 getOwnPropertySymbols() 方法获取Symbol属性,那还是私有属性么?

今天复习整理一下关于js中创建私有属性的一些问题。

由于js并不是Java那种类式面向对象,因此即使es6添加了class支持,js根上还是基于原型的面向对象,不支持什么私有,公有属性的。

要想实现私有属性,基于现有的js,途径只有一个: 闭包。

至于什么是闭包,网上一大把,有兴趣也可以看看我之前整理的文章。

使用原始的闭包
const User = (function () { return class User{ constructor(name) { this.getName = function () { return name; } this.setName = function (name) { this.getName = function () { return name; } } } };})();

这种方式的确可以实现私有的属性,而且如果有子类继承,也可以如下写法:
const Boy = (function () { return class Boy extends User{ constructor(name){ super(name); this.getGender = function () { return 'boy'; } } }})();

但问题也有:
  • 代码组织混乱。由于对构造器函数形成一个闭包,因此所有的setter,getter函数都写在了构造器内。可以从上面的User的写法中看出,setter函数中,还要再定义一遍getter,这中混乱,不是一般能忍受的。如果业务逻辑中还有其他的更多操作,那么混乱程度一下子就上来了。
  • 闭包的内存开销不容小觑。

针对上面的问题,我们可不可以将name单独拿出来放到单独一个地方存储,优化一下代码的组织呢?比如下面这个:
const User = (function () { let privateName = null; return class User{ constructor(name) { privateName = name; } getName(){ return privateName; } setName(name) { privateName = name; } };})();

上面这个例子,貌似是可以的,这样代码也清晰了,也能实现私有属性。

但是,仔细分析一下,真的可以么?

如此的话,是不是所有的实例对象都共享一个 privateName 属性?后面的实例会覆盖前面实例的值。看下面:
const u1 = new User('coolcao');const u2 = new User('lili');console.log(u2.getName());console.log(u1.getName());// lili// lili

针对上面的问题,我们使用一个 privateName 保存私有属性,会被覆盖,那么我们如果使用一个数组,保存多个,然后针对每个实例,生成一个唯一的存取标识呢?

基于散列实现
const User = (function () { const privateData = {}; let i = 0; return class User { constructor(name){ this['_id'] = i ++; privateData[this['_id']] = {name: name}; } getName() { return privateData[this['_id']].name; } setName(name) { privateData[this['_id']].name = name; } };})();

我们使用 privateData 这个对象来保存所有实例的私有属性,针对每个实例,使用一个id进行标识,每实例化一个实例,该id会自动加1。然后将该id作为键值,将私有属性存入 privateData 对象。

嗯,这样看上去比直接在构造器中使用闭包要清晰多了,而且实例与实例之间也不冲突。

在es6之前,这可能是最合适的方案。虽然也会存在问题:

每个实例对象都会引用 privateData ,因此,还是由于闭包的问题,如果实例太多的话,内存是个问题。

WeakMap实现

幸好 es6 来了,带来了一个叫做 WeakMap 的东西,具体这东西是啥呢?可以看看阮老师的教程

简单说来,WeakMap键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。

好了,那么上面的这个强引用关系,我们可以使用 WeakMap 弱引用来代替:
const User = (function () { const privateData = new WeakMap(); class User { constructor(name) { privateData.set(this,{name:name}); } getName() { return privateData.get(this).name; } setName(name) { privateData.get(this).name = name; } }})();

如此的代码,干净清爽了许多,而且由于WeakMap是弱引用,如果没有其他引用和该键引用同一个对象,这个对象将会被当作垃圾回收掉。解决了内存泄露的问题。

好了,js模拟闭包,就这几个方式了,从这几个例子来看,都使用了自执行函数(IIFE),因此都会形成闭包。这也是我最开始说的,要想在js实现私有属性,只能使用闭包。

Symbol的问题

话说回来我当初的疑问,ES6的Symbol实现的私有属性有啥问题呢?

看下面例子:
const User = (function () { const NAME = Symbol('User#Name'); return class User{ constructor(name){ this[NAME] = name; } getName(){ return this[NAME]; } setName(name) { this[NAME] = name; } }})();

NAME是个Symbol,从外部并不能拿到NAME确切的值,好像是有点私有属性的意思。但是 有一个 Object.getOwnPropertySymbols() 方法可以拿到对象所有的Symbol属性,虽然我不知道具体存了个啥,但是能拿到这个标识,就可以修改属性值了:
const smbs = Object.getOwnPropertySymbols(user);for(let s of smbs) { user[s] = 'good';}console.log(user.getName());// good

因此,严格意义上说,Symbol其实并不能实现私有属性。
不过倒是可以将上面第二种基于散列的方式改为Symbol的方式:const User = (function () { const privateData = {}; return class User { constructor(name){ this._name = Symbol('name'); privateData[this._name] = {name: name}; } getName() { return privateData[this._name].name; } setName(name) { privateData[this._name].name = name; } };})();

这样对外暴露的只是 _name 的这个符号,外部还是无法直接访问每个实例的 privateData 中的值。但这个其实和第二种是一样的,闭包引起的问题还是无法解决。

展望未来

js的私有属性,目前处于 stage2 阶段,目前还未最终确定,不过我们可以先看一下模样:
class Point { #x; #y; constructor(x, y) { this.#x = x; this.#y = y; } equals(point) { return this.#x === point.#x && this.#y === point.#y; }}

语法如上,使用 # 定义私有属性,在类的内部可以直接 使用 this.#x 的形式引用。

目前使用#进行定义和访问私有属性,未来会不会使用 public,private等关键字不得而知。

目前使用#,可能的原因是,js没有静态类型系统。

参考
  • Private instance members with weakmaps in JavaScript
  • Private Properties(MDN)
  • JavaScript实现私有属性
  • JavaScript’s new #private class fields

原文链接:http://coolcao.com/2017/08/14/private-property-in-js/ 如有侵权请告知删除

本文仅代表作者个人观点,不代表SEO研究协会网官方发声,对观点有疑义请先联系作者本人进行修改,若内容非法请联系平台管理员。更多相关资讯,请到SEO研究协会网www.seoxiehui.cn学习互联网营销技术请到巨推学院www.jutuiedu.com。

鸡蛋

鲜花

握手

雷人

路过
已有 0 人参与

会员评论

推荐阅读

    2019-12-10 15:02
  • 作者:苏彦

    金融业抖音代运营公司为企业带来哪些服务?

    今年的短视频能够说是十分的火爆,展览的趋势好像要掩盖许多的小程序,并且还超越微信,那么今天咱们要谈的是抖音,在这样一个多短视频的平台上,抖音是最火爆的平台

  • 2019-12-10 13:49
  • 作者:苏彦

    零售百货抖音企业号代运营怎样玩才会让粉丝不断上涨

    同一件事让不同的人去做,就会有不同的结果,主要的不同在于做事的方式、考虑问题的角度和执行程度等等因素有关,就像玩抖音一样,有些人粉丝几百万甚至上千万,而有些人便是才十几个甚至上百个。

  • 2019-12-10 13:26
  • 作者:苏彦

    为什么抖音企业认证后播放量很少?巨推传媒告诉你真实答复

    抖音企业认证发展到现在,现已沉淀了不少用户,用户量一大,问题点也越来越多,特别是做了认证后说流量反而变少的声音在网络上四处蔓延。

  • 2019-12-10 13:22
  • 作者:白冰

    很好用的壁纸软件分享

    壁纸软件各种各样的壁纸都有,分辨率也是很高的,看不懂英文可以用谷歌浏览器来进行翻译哦获取方法:转发+关注+私信 私信回复“壁纸”即可获取【免责声明】本文仅代表作者或发布者个人观点,不代表SEO研究协会网(ww

  • 2019-12-10 13:20
  • 作者:chenxi689

    最大刷机软件关闭,安卓用户必备技能没用了吗?

    曾几何时,刷机是每个智能手机用户的必修科目之一,只要你希望手机有更多的个性化选项、更多的自定义功能,那么就得通过刷机来实现。于是各类手机论坛变得人头攒动,还催生了一系列刷机为主题的站点,也出现了专门帮

  • 2019-12-10 13:14
  • 作者:fanpeng1100

    如何注册公司,办理公司要多少钱

    公司注册是开始创业的第一步。一般来说,公司注册的流程包括:企业核名→提交材料→领取执照→刻章,就可以完成公司注册,进行开业了。但是,公司想要正式开始经营,还需要办理以下事项:银行开户→税务报到→申请税

  • 2019-12-10 12:43
  • 作者:潍溦

    7个优质电子书资源下载网站,再也不愁找不到想要电子书了

    写在前面上一期文章“除了Everything与Listary,Windows还有哪些值得信赖的搜索工具?”,一册笔记介绍了一款搜索软件“Recoll”,它最大的特点,是可以通过“文本”来搜索PDF文件。搜索工具都已经介绍了,索性再推

  • 2019-12-10 12:07
  • 作者:骚年

    “你的猫太胖了!”俄罗斯男子“偷天换日”,带超重肥猫上飞机被

    (央视财经《正点财经》)俄罗斯国际航空公司12日宣布,一名乘客在俄罗斯国际航空公司累积的全部里程作废,原因是这名乘客骗过值机人员,策划了一场“偷天换日”的行动,把一只体重“超标”的肥猫违规带进客舱。这位

  • 2019-12-10 11:08
  • 作者:披星戴月

    推荐给入门级的程序员,15款不一样的编程工具,易上手

    俗话说:巧妇难为五米之炊,程序员也要有好的软件,才会更高效率的提高自己的工作效率,那作为入门级别的程序员,几款趁手的编程软件是最需要的。除了几款基本的软件,其实还有很多很很酷的编程工具。接下来就为大家

  • 2019-12-10 10:39
  • 作者:Chris

    注意!这四类号码不能携号转网!有你吗?

    11月14日晚,三大运营商发布携号转网服务细则,并表示已于11月10日起进入试运行阶段。1349、174、170等号码不能携号转网,快来看看有你的吗?携号转网试运行 这些地区优先体验1、中国移动:11月10日启动携号转网系统

文章排行

TOP ARTICLES

返回顶部