ES中私有变量的实现
约定俗成
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
class Person {
constructor(name) {
this._name = name
}
get name() {
return this._name
}
set name(v) {
this._name = v
}
}
const per = new Person('Klaus')
这是最早的一种定义类的私有属性的方式,也是最为简单易懂的一种方式
我们一般将私有的成员属性名定义成以下划线开头的变量
并新建一个变量,使用该新建变量的访问器去操作私有变量
但是这种形式的问题也是非常明显的
在这里的_name并不是真正意义上的私有化,只是形式上的
console.log(per) // => Person { _name: 'Klaus' }
闭包
const Person = (() => {
let _name = ''
return class {
constructor(name) {
_name = name
}
get name() {
return _name
}
}
})()
const per = new Person('Klaus')
console.log(per) // => {}
console.log(per.name) // => Klaus
可以使用IIFE创建一个函数作用域,随后返回我们所需要的类
在运行环境执行示例代码的时候,因为返回的类的作用域链上存在IIFE的AO对象
所以我们可以将私有属性定义在IIFE的AO对象中,也就是定义成IIFE作用域下的变量
从而来模拟私有属性
但是使用闭包来模拟私有属性
优点:相比约定俗成的形式,使用闭包定义私有属性,在类外部无法访问和修改对应的私有属性
缺点:
- 为了实现IIFE,我们需要多使用一层函数作用域,会造成性能的损失,而且书写起来十分麻烦
- 私有属性本质上是位于IIFE这个函数的作用域中,并不是被添加到对象的实例上的
Symbol
const Person = (() => {
let _name = Symbol('name')
return class {
constructor(name) {
this[_name] = name
}
get name() {
return this[_name]
}
}
})()
const per = new Person('Klaus')
console.log(per) // => { [Symbol(name)]: 'Klaus' }
console.log(per.name) // => Klaus
使用Symbol来模拟私有属性,可以很好的解决使用闭包模拟私有属性的时候,私有属性实际本不是添加在实例对象上的问题
但是使用Symbol来模拟私有属性,依旧需要在类的外层包裹一层函数作用域,书写起来依旧有点麻烦
最新提案
class Person {
// 私有属性需要在这里声明
#name
constructor(name) {
this.#name = name
}
get name() {
return this.#name
}
}
const per = new Person('Klaus')
console.log(per) // => Person {}
console.log(per.name) // => Klaus