vue.js是通过这 Object.defineProperty来实现双向数据绑定的,神奇的方法
为JavaScript对象新增或者修改属性,有两种不同方式:直接使用=赋值或者使用Object.defineProperty()定义。如下:
// 示例1
var obj = {};
// 直接使用
obj.a = 123
// 使用Object.defineProperty定义
Object.defineProperty(obj, "b",
{
value: 2
});
console.log(obj) // 打印"{a: 1, b: 2}"
这么看两种方法都一样对吧,没啥区别,但是用Object.getOwnPropertyDescriptor()查看obj.a与obj.b的属性的描述描述符(property descriptor)时,会发现=与Object.defineProperty并不一样:
// 示例2
var obj = {};
obj.a = 1;
Object.defineProperty(obj, "b",
{
value: 2
});
console.log(Object.getOwnPropertyDescriptor(obj, "a")); // 打印"{value: 1, writable: true, enumerable: true, configurable: true}"
console.log(Object.getOwnPropertyDescriptor(obj, "b")); // 打印"{value: 2, writable: false, enumerable: false, configurable: false}"
Object.defineProperty 很简单,对吧,接收三个值,且都是必填的
第一个参数:目标对象
第二个参数: 需要定义的属性或者方法的名称
第三个参数:目标属性所拥有的特性(descriptor)描述符
前两个参数比较好理解,来看看第三个参数descriptor, 看下有哪些取值
- value: 属性的值
-
writable:如果为false,属性的值就不能被重写,只能为只读了(有道面试题ES5去写const 可以用这个思路)
-
configurable:总开关,一旦为false,就不能再设置他的(value,writable,configurable)
-
enumerable:是否能在for...in循环中遍历出来或在Object.keys中列举出来。
-
get (get 和set )不能与 value 或者是 writable 同时存在
-
set (get 和set )不能与 value 或者是 writable 同时存在
configurable
var a= {}
Object.defineProperty(a,"b",{
value:123
})
console.log(a.b);//123
上面这个简单的例子,我们可以只设置了value,并没有别的什么设置,但是它会默认帮我们添加上writable,configurable,enumerable,默认值是false,也就是上面的代码等同于下面的
var a= {}
Object.defineProperty(a,"b",{
value:123,
writable:false,
enumerable:false,
configurable:false
})
console.log(a.b);//123
首先看下总开关configurable, 第一次什么也没设置默认就是false,然后进行第二次设置会发生什么呢,没错,会报错。
var a= {}
Object.defineProperty(a,"b",{
configurable:false
})
Object.defineProperty(a,"b",{
configurable:true
})
//error: Uncaught TypeError: Cannot redefine property: b
所以这里的configurable如果后面需要用到,在第一次的时候就要设置好,不设置会默认帮你加上false。一旦设置成false ,就不能设置他的(value,writable,configurable)了。
writable
设置成false,就变成只读了。
var a = {};
Object.defineProperty(a, "b", {
value : 123,
writable : false });
console.log(a.b); // 打印 37
a.b = 25; // 没有错误抛出(在严格模式下会抛出,即使之前已经有相同的值)
console.log(a.b); // 打印 37, 赋值不起作用。
前面提到了一个面试题,在ES5 下实现 const,可以用到writable代码有点长,不想看可以跳过的
var __const = function __const(data, value) {
window.data = value // 把要定义的data挂载到window下,并赋值value
Object.defineProperty(window, data, { // 利用Object.defineProperty的能力劫持当前对象,并修改其属性描述符
enumerable: false,
configurable: false,
get: function () {
return value
},
set: function (data) {
if (data !== value) { // 当要对当前属性进行赋值时,则抛出错误!
throw new TypeError('Assignment to constant variable.')
} else {
return value
}
}
})
}
__const('a', 10)
console.log(a)
delete a
console.log(a)
// enumerable 属性下面有详细讲解
for (let item in window) { // 因为const定义的属性在global下也是不存在的,所以用到了enumerable: false来模拟这一功能
if (item === 'a') { // 因为不可枚举,所以不执行
console.log(window[item])
}
}
a = 20 // 报错
enumerable
属性特性 enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。
var a= {}
Object.defineProperty(a,"b",{
value:3445,
enumerable:true
})
console.log(Object.keys(a));// 打印["b"]
将enumerable改为false,
var a= {}
Object.defineProperty(a,"b",{
value:3445,
enumerable:false // 就这里改了,注意看呀~
})
console.log(Object.keys(a)); // 打印[]
set 和 get
在 descriptor 中不能 同时设置访问器 (get 和 set) 和 wriable 或 value,否则会错,就是说想用(get 和 set),就不能用(wriable 或 value中的任何一个)
set 是赋值用的,get 是取值用的
var a= {}
Object.defineProperty(a,"b",{
set:function(newValue){
console.log("你也可以赋值, 新值是"+newValue)
},
get:function(){
console.log("你取我的值")
return 2 // 注意这里,我硬编码返回2
}
})
a.b =1 // 打印 你要赋值给我,我的新值是1
console.log(a.b) //打印 你取我的值
//打印 2 注意这里,和我的硬编码相同的
简单来说,, 这个 “b” 赋值 或者 取值的时候会分别触发 set 和 get 对应的函数
这就是实现 observe的关键啊。。
先写这些拜了个拜~