以前的文章 瓜皮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) } }
參考這段代碼 而後稍微改下就好了! ,這種方式,既沒有告訴你門牌號,也沒有找到門牌號的路徑,除非本身暴露出來。。有沒有以爲很贊呢。。