ES中私有变量的实现

181 阅读2分钟

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作用域下的变量

从而来模拟私有属性

但是使用闭包来模拟私有属性

优点:相比约定俗成的形式,使用闭包定义私有属性,在类外部无法访问和修改对应的私有属性

缺点:

  1. 为了实现IIFE,我们需要多使用一层函数作用域,会造成性能的损失,而且书写起来十分麻烦
  2. 私有属性本质上是位于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