【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)
  }
}

參考這段代碼 而後稍微改下就好了! ,這種方式,既沒有告訴你門牌號,也沒有找到門牌號的路徑,除非本身暴露出來。。有沒有以爲很贊呢。。

相關文章
相關標籤/搜索