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