以前的文章 瓜皮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) } }
参考这段代码 而后稍微改下就好了! ,这种方式,既没有告诉你门牌号,也没有找到门牌号的路径,除非本身暴露出来。。有没有以为很赞呢。。