引言:
定义私有对象,或者私有属性对于开发工具库是比较重要的知识,在各种使用场景你不知道会出现什么情况它可能会篡改你不想让用户操作的对象和属性,因此安全的定义私有对象能保证代码库的稳定性。
定义一个简单的私有对象
通常情况下我们会通过匿名函数自执行
的方式(闭包
)获得一个私有对象,这样用户就不能随意篡改重要元数据了。
同时实现了一个 get 方法示意其提供 api 的方式。
js
const o = (function(){
const obj = {
foo:'foo',
bar:'bar',
}
return {
get(key){
return obj[key]
}
}
})();
export default o;
我们看到 obj 已经被很好的保护起来了。
意想不到的篡改
但是真的无法篡改了吗?未必!
正常情况下我们无论如何也无法篡改 obj
了,因为我们只能通过 get
方法获得 obj 的属性
,而无法获取 obj 本身
,要是可以 通过 obj 的属性获取到 obj 本身 就好了,但是 obj
上只存在 foo
和 bar
两个属性,显然无从下手,如果硬要做点什么,那么可以通过什么来做到呢?
有了! 原型
和原型链
,我们可以通过修改原型来扩展方法和属性,obj
是一个对象,通过原型链我们可以知道他的原型是 Object.prototype
这么一来就好办了,要是给 Object.prototype
扩展一个获取自己的属性不就好了嘛~,Object.defineProperty
要上场了,属性Vue2
的小伙伴应该知道要怎么做了吧,接下来我们实现一下。
扩展一个 self 属性吧,作用就是返回自己。
js
Object.defineProperty(Object.prototype,'self',{
get(){
return this
}
})
接下来我们尝试篡改 obj
可以看到成功了。
预防
比较弱的预防是获取属性的时候判断是是不是自身拥有的属性
只返回自身属性
js
const o = (function(){
const obj = {
foo:'foo',
bar:'bar',
}
return {
get(key){
if(obj.hasOwnProperty(key)){
return obj[key]
}
}
}
})();
export default o;
更好的方式容易想到我们只需要打断原型链或者在创建的时候不继承原型就行了
打断原型链
js
const o = (function(){
const obj = {
foo:'foo',
bar:'bar',
}
// 打断原型链
obj.__proto__ = undefined
return {
get(key){
return obj[key]
}
}
})();
export default o;
不继承原型
Object.create(null) 创建的对象可以去除一些不干净的东西。
js
const o = (function(){
const obj = Object.create(null)
Object.assign(obj,{
foo:'foo',
bar:'bar',
})
return {
get(key){
return obj[key]
}
}
})();
export default o;
私有属性
这个我们搞快点:
1、命名规范下划线
这个太弱了
js
class Foo {
__foo='foo'
}
不推荐
2、Symbol + 闭包
js
const foo = Symbol('foo')
class Foo {
[foo]='foo'
}
export default Foo
也比较弱,不是很推荐。
3、标准语法'#'
js
class Foo {
#foo='foo'
}
export default Foo
这个很完美了但是有浏览器兼容问题但未来不是问题,推荐。
4、自定义映射
js
const foo = new WeakMap()
class Foo {
constructor(){
foo.set(this, "foo");
}
}
export default Foo
如果再多一个私有属性呢,那就定义两个 WeakMap
js
const foo = new WeakMap()
const bar = new WeakMap()
class Foo {
constructor(){
foo.set(this, "foo");
bar.set(this, "bar")
}
}
export default Foo
等价于
js
class Foo {
#foo='foo'
#bar='bar'
}
export default Foo
如果是实例属性不是类属性可以
js
const map = new WeakMap()
map.set('attributeName', 'value')
这种没有兼容性问题,当然后续兼容性会更好或者你有babel的话也可以直接使用最新的语法
csharp
#attributeName
this.#attributeName
(完)