【javascript】私有化变量

以前的文章 瓜皮csdn把以前正确的排版,给所有打乱了。。。javascript

----java

js中如何像java同样,将实例变量设置为私有呢? 由于没有相似的关键字privatees6

方法一: 

在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()是不一样的。 这里咱们优化一下代码:ui

// 结构赋值和函数默认参数的使用
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]的方法,这个方法是一个生成器。this

 

方法二

es6的Symbolspa

由于Symbol函数每一次调用返回的结果都是不一样的,Symbol('x') === Symbol('x') 的值false, 能够理解为每次都生产了一个uuid,咱们利用这个特性。咱们修改上面的代码 以下:code

// 用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)
  }
}

参考这段代码 而后稍微改下就好了! ,这种方式,既没有告诉你门牌号,也没有找到门牌号的路径,除非本身暴露出来。。有没有以为很赞呢。。

相关文章
相关标签/搜索