中午突然想起一个问题,如何实现私有属性,私有方法?先自己尝试动手实现。
类的私有域
通过闭包实现私有属性
首先想到的是通过创建闭包去维护这些私有属性。
1 | const Person = (function () { |
上述例子虽然实现了私有属性,私有方法,但是多了一个非必要的_id
属性,实例也可以访问这个属性,且实例对象被GC回收后,privatePropertys
也不会自动去删除这个实例对象对应的私有属性,存在内存泄漏的风险,当实例对象被回收时,需要人工维护privatePropertys
对象,删除该实例对应的属性。
privatePropertys
其实只是一个映射,所以我们可以使用WeakMap
来实现它,因为WeakMap
针对对象都是弱引用的,我们把实例对象作为WeakMap
的key,value存储实例对象的所有私有属性。
1 | const Person = (function () { |
WeakMap & WeakSet
Map和Set数据类型我在日常开发中已经开始使用,但是WeakMap和WeakSet基本没怎么用到过。
WeakMap,WeakSet,有一些共同的特性,所以我们可以放在一起讨论。
WeakSet只能以对象
作为集合的成员,WeakMap只接收对象
作为映射的key。WeakSet,WeakMap对这些对象都是弱引用,弱引用不会被计入垃圾回收机制里的引用
,所以当这些对象没有其他引用时,GC可以直接回收这些对象占用的内存。由于这个特性,WeakSet
的成员和WeakMap
的key都是不可枚举,因为你无法确定GC前后,其枚举出来的列表是否有变化,其是一个不确定的结果。
日常使用中,我很少会将一个对象
作为映射的key或者作为集合的成员,所以等到接触typescript之后才了解到类私有域,即#
修饰符。窥探tsc
编译后的代码,可以了解到ts
是通过WeakMap
实现了类的私有实例属性的。
类私有域
详细参考MDN上的介绍。
ECMAScript
针对类私有域的提案在2021年四月就已经到了Stage-4
阶段,即将在ECMAScript 2022
正式上线,也就是2022年6月份会正式发布。
而各家浏览器在更早之前就已经实现了类的私有域功能,chrome的最早一个实现类私有域的版本是73
,时间上可以追溯到2019年。我们可以在can i use上查看所有浏览器的情况。
现在大多数高版本的现代浏览器是实现了这个类私有域的提案-明年正式成为标准草案,低版本浏览器的目前全红。所以不太适合直接上生产,编译成ES2015
标准还是目前最稳妥的方案了。
总结
使用了Typescript的项目,我们直接使用类私有域的语法就可以实现私有化。
对于只运行在高版本浏览器上的项目,直接使用类私有域的语法即可,因为浏览器支持其大部分功能。
其他需要运行在低版本的项目,请使用polyfill的实现。
当各家浏览器标准愈发统一,且浏览器跟进实现标准的速度越来越快,可以预料到的是,交给前端处理的浏览器兼容性问题会越来越少,我们也不需要写那些polyfill的代码去向下兼容。