数据绑定最基本的实现
实现一个方法,可以给 obj 所有的属性添加动态绑定事件,当属性值发生变化时会触发事件
let obj = {
key_1: 1,
key_2: 2
}
function func(key) {
console.log(key + ' 的值发生改变:' + this[key]);
}
bindData(obj, func);
obj.key_1 = 2; // 此时自动输出 "key_1 的值发生改变:2"
obj.key_2 = 1; // 此时自动输出 "key_2 的值发生改变:1"
实现bindData(obj, func)函数
function bindData(obj, func){
for(let key in obj){
let value = obj[key]
Object.defineProperty(obj,key,{
set:function(newValue){
if (newValue !== value) {
this.value = newValue
func.call(obj,key)
}
},
get:function(){
return this.value
}
})
}
}
就这么点代码折磨了我一个中午
踩过的几个坑:
1. 在遍历对象属性的时候用for(var key in obj)
这个地方是不能用var的,因为会导致你不管调用 obj.key_1
还是obj.key_2
他都只能打印出:
用
var
声明的那个key
会保留在上层作用域,你在调用 obj.key_1
触发func.call(obj,key)
的时候,里面的key
为循环遍历后最后赋给key的值,也就是本题中的key_2
2. this.value在赋值以前为undefined
我的第一版代码是这么写的(是错的)
function bindData(obj, func) {
for (let key in obj) {
Object.defineProperty(obj, key, {
set: function(newValue) {
if (this.value !== newValue) {
this.value = newValue;
func.call(obj, key);
}
},
get: function(){
return this.value;
}
})
}
}
这种写法会导致if (this.value !== newValue)
判断失效。因为这时的this.value
为undefined,所以无论属性值是否发生变化,func.call(obj,key)
都会被调用,如:
这显然不符合题目要求的“当属性值发生变化时会触发事件”。
针对这个问题,需要学习一下属性描述对象:
Object.defineProperty()
属性描述对象
来自MDN的定义
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
这个方法接受三个参数:
- obj:要在其上定义属性的对象。
- prop:要定义或修改的属性的名称。
- descriptor:将被定义或修改的属性描述符。
属性描述符:descriptor
属性描述符分为两种形式:数据描述符和存取描述符。
描述符必须是这两种形式之一;不能同时是两者。
-
数据描述符:一个具有值的属性,该值可能是可写的,也可能不是可写的。
它具有以下可选键值:
value:该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
writable:当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。 -
存取描述符:由getter-setter函数对描述的属性。
它具有以下可选键值:
get:取值函数。在对象调用属性的时候触发。
set:存值函数。在给属性赋值的时候触发。
以上这四个属性中,数据描述符的属性不能和存取描述符的属性同时存在:
如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。
除此之外,还有两个共同的属性:
- configurable:当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
- enumerable:当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。
以上为基本概念,结合这些基本概念本题中要注意的点有:
-
Object.defineProperty()
方法中,this指向定义该属性的对象(不考虑继承关系) - 用
Object.defineProperty()
修改属性之后,比如
var obj = {
key: 1,
}
Object.defineProperty(obj,'key',{
set:function(newValue){
this.value = newValue
},
get:function(){
return this.value
}
})
这时如果调用obj.key
返回的值为undefined:
因为
obj.key
会触发取值函数,但取值函数返回的this.value
为undefined。也就是说:修改属性的属性描述符过后,之前的属性值就不存在了。该属性是一个全新的属性。它的值取决于value(数据描述符)或者get函数的返回值(存取描述符)
但是上面的例子,只要修改过后,给属性赋一次值,this.value就有值了(不为undefined)。
网友评论