无论vue还是react,新型设计框架都趋向去数据驱动,而不是直接操作dom。 在看一些实现数据双向绑定的源码中, 我们总能看到数据劫持,将对象属性变成访问器属性;那么这时就要了解什么是访问器属性了。
那么什么是访问器属性?访问器属性被设计出来是作何使用的?
ES5中定义了两种属性,分别是数据属性和访问器属性
数据属性和访问器属性,分别都有四个属性,为了表示特性是内部值,规范(ECMA-262)就把他们放到了两对方括号里了。
数据属性
- [[Configurable]]: 表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
- [[Enumerable]]: 表示能否通过 for-in 循环返回属性。
- [[Writable]]: 表示能否修改属性的值。
- [[Value]]: 包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值时,把新值保存在这个位置。默认值是 undefined。 示例:
const obj = { name: '哈哈' }
console.log(obj.name) // '哈哈'
obj.name = '大葆'
console.log(obj.name) // '大葆'
// 再看Math
console.log(Math.PI) // 3.1415926……
Math.PI = 1111
console.log(Math.PI)
这可以看到同样是Object的实例,obj对象的属性可以被改写,而Math对象的属性却不能被改写。原因为何? 我们通过打印其内部属性一探究竟
Object.getOwnPropertyDescriptor('obj','name')
// { configurable: true, enumerable: true, value: '大葆', writable: true}
Object.getOwnPropertyDescriptor('Math','PI')
// { configurable: false, enumerable: false, value: 3.141592653589793, writable: false}
根据getOwnPropertyDescriptor打印可以看出,Math.PI里面的几个特性都是被false掉的。但是是否可以重置Math的四个数据属性呢,答案是否定的,会报“Cannot redefine property”错误。
但是自己声明的确实是可以重置的哦
const obj = { name: '哈哈' }
console.log(obj.name) // '哈哈'
obj.name = '大葆'
console.log(obj.name) // '大葆'
Object.defineProperty(obj,"name",{
writable: false,
})
obj.name = '大葆变成二宝吗'
// 等同于
// Object.defineProperty(obj,"name",{
// value: '大葆变成二宝吗',
// })
console.log(obj.name) // 大葆 此时并不能再被重写
访问器属性
访问器属性就不包含是否可写和value值,取而代之的是读取和重写的get和set函数。 在读取访问器属性时,会调用getter函数,在写入访问器属性时,又会调用setter函数并传入新值(这样的话就可以用于劫持监听数据什么时候被读取或者有更新了)
访问器属性的4特性
-
[[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。
-
[[Enumerable]]:表示能否通过 for-in 循环返回属性。
-
[[Get]]:在读取属性时调用的函数。默认值为 undefined。
-
[[Set]]:在写入属性时调用的函数。默认值为 undefined。
访问器属性和数据属性的区别
访问器属性并不能直接被定义,必须使用Object.defineProperty()来定义
var book = {
name: '寻梦环游记',
pages: 423
}
Object.defineProperty(book, 'pages', {
get() {
this.pages
},
set(newVal) {
if (newVal !== this.pages) {
this.pages = newVal
console.log('更新了')
} else {
console.log('没变化')
return
}
}
})
可以看到访问器属性的提供,给数据变化的响应监听提供了途径