【javascript】私有化变量

之前的文章 瓜皮csdn把之前正确的排版,给全部打乱了。。。

----

js中如何像java一样,将实例变量设置为私有呢? 因为没有类似的关键字private

方法一: 

在ES6之前,我们是通过闭包来完成封装的,看例子:

// 结构赋值和函数默认参数的使用
function initStudent({name = '', id = null, address =''} = {}) {
  let _name = name,
    _id = id,
    _address =address
  return {
    setName(name) {
      _name = name
    },
    getName() {
      return _name
    },
    setId(id) {
      _id = id
    },
    getId() {
      return _id
    },
    setAddress(address) {
      _address = addredss
    },
    getAddress(){
      return _address
    },
    toString() {
      return `student' name is ${_name}, address is ${_address} ,id is ${_id}`
    }
  }
}

 

这样的话,我们这样`var student = initStudent({id: '001', name: 'Kevin', address: 'somewhere'})`构造一个对象,由于闭包的关系,返回的对象关联了一个函数,这个函数引用了当前函数词法作用域外层也就是initStudent函数作用域的一个局部变量,尽管initStudent执行完毕但是由于存在这样一层引用,外层的作用域在内存中并没有释放掉,数据仍然存在内存中(如果不明白,简易去看下《js高程》的作用域链和闭包章节)。

这个对象 student 不能直接访问name属性,console.log(student.name) 的结果是 undefined

但是我们直接这样操作student.name = 'Kevin2', 成功了。。。然后 你比较student.name  和student.getName()是不同的。 这里我们优化一下代码:

// 结构赋值和函数默认参数的使用
function initStudent({name = '', id = null, address =''} = {}) {
  let _name = name,
    _id = id,
    _address =address
  let obj =
  {
    setName(name) {
      _name = name
    },
    getName() {
      return _name
    },
    setId(id) {
      _id = id
    },
    getId() {
      return _id
    },
    setAddress(address) {
      _address = addredss
    },
    getAddress(){
      return _address
    },
    toString() {
      return `student' name is ${_name}, address is ${_address} ,id is ${_id}`
    }
  }
  const getErrorFun =function(prop) {
    return function () {
      throw new Error(`you cannot set ${prop} value directly, use setMethod`)
    }
  }
  Object.defineProperties(obj , {
    'name': {
      set: getErrorFun('name')
    },
    'id': {
      set: getErrorFun('id')
    },
    'address': {
      set: getErrorFun('address')
    },    
  })
  return obj
}

 

这样就不能直接设定name等属性了,而且外部也不能直接访问和设定,必须通过get set方法去操作。如果像遍历对象的属性的话,可以自己在返回的对象里面增加一个*[Symbol.iterator]的方法,这个方法是一个生成器。

 

方法二

es6的Symbol

因为Symbol函数每一次调用返回的结果都是不同的,Symbol('x') === Symbol('x') 的值false, 可以理解为每次都生产了一个uuid,我们利用这个特性。我们修改上面的代码 如下:

// 用Symbol来封装
const _name = Symbol('name'),
  _id = Symbol('id'),
  _address = Symbol('address')

class Student {
  constructor({name = '', id = null, address =''} = {}) {
    this[_name] = name
    this[_id] = id
    this[_address] = address
  }
  get name(){
    return this[_name]
  }
  set name(name) {
    this[_name] = name
  }
  get address(){
    return this[_address]
  }
  set address(address) {
    this[_address] = address
  }
  get id(){
    return this[_id]
  }
  set id(id) {
    this[_id] = id
  }    
}

这里用了getter setter设置属性的存储函数和获取函数,拦截该属性的默认set和get行为【我犯了一个错误,上面闭包的方式实现也可以用这个方法,但是我当时想的是模拟java里面私有属性的公共方法】,如果非要使用getName setName这种形式,其实也行,我们改一下代码:

const _name = Symbol('name'),
  _id = Symbol('id'),
  _address = Symbol('address')

class Student {
  constructor({name = '', id = null, address =''} = {}) {
    this[_name] = name
    this[_id] = id
    this[_address] = address
  }
  get name(){
    return this[_name]
  }
  set name(name) {
    throw new Error('cannot set value directly, use setMethod')
  }
  getName(){
    return this[_name]    
  }
  setName(name){
    this[_name] = name
  }   
}

我们通过创建一个实例,var s =  new Student({name: 'kevin', id: 'iook', address: 'somewhere'}) , s.name报错, 其实我们也可以将默认的getter函数给屏蔽掉,只能通过s.getName去获取。这样也完成了我们想要的。

 

其实这种方法和第一种类似,第一种通过闭包隐藏访问途径, 第二种直接隐藏key的名字,有途径也没有用【但是实际上是还是可以通过Object.getOwnPropertySymbols(obj)来访问】。

 

方法三!!

用map来实现,wow,这段代码来自月大

const privateMap = new WeakMap()

const Point = class{
  constructor(x, y) {
    privateMap.set(this, {x, y}) 
  }
  get length(){
    let { x, y } = privateMap.get(this)
    return Math.sqrt(x ** 2 + y ** 2)
  }
}

参考这段代码 然后稍微改下就行了! ,这种方式,既没有告诉你门牌号,也没有找到门牌号的路径,除非自己暴露出来。。有没有觉得很赞呢。。

 

posted on 2018-07-23 22:25  狂奔的冬瓜  阅读(629)  评论(0编辑  收藏  举报