利用Object.defineProperty实现简单的input双向绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>practice</title>
</head>
<body>
<input id="input" type="text">
<div id="box"></div>
<script>
let input = document.getElementById("input");
let box = document.getElementById("box");
let value = {};
let name;
Object.defineProperty(value,"name",{
get:()=>{
return name;
},
set:(val)=>{
console.log(val);
name = val;
box.innerHTML = val;
if(input.value!=val){
input.value = val
}
}
})
input.addEventListener('input',(e)=>{
console.log(e.target.value);
value.name = e.target.value;
})
value.name = "wmm";
</script>
</body>
</html>
最终效果
原理是,通过Object.defineProperty对对象的getter和setter进行拦截,在setter的时候对数据进行绑定处理,从而监听数据实时变化。
vue2.0中也是利用Object.defineProperty实现的双向绑定
但是后面es6新增了Proxy代理,相比较Object.defineProperty而言,Proxy可以一次性监听所有对象的属性,而Object.defineProperty则需要通过遍历才可以实现这一点,如下
let value = {
age: "18",
name: "wmm",
};
let name = value.name;
let age = value.age;
Object.keys(value).forEach((key) => {
Object.defineProperty(value, key, {
get: () => {
return key == 'age' ? age : name
},
set: (val) => {
console.log(val);
if(key=='age'){
age = val;
}else if(key=='name'){
name = val;
}
}
})
})
console.log(value.name);
value.name = "wmm123";
console.log(value.name);
而且Object.defineProperty无法监听新增加的属性,也无法相应数组的相关方法如push等,在vue中通过重写数组原型链的方式,来监听push、shift、pop等操作;
const arrayProto = Array.prototype;
const newArrayProto = Object.create(arrayProto);
const methods = ['pop', 'shift', 'unshift', 'sort', 'reverse', 'splice', 'push'];
methods.forEach(method => {
newArrayProto[method] = function () {
arrayProto[method].call(this, ...arguments);
}
Object.defineProperty(newArrayProto, method, {
get() {
},
set() {
}
})
})
既然proxy这么好,下面放个用法
-
get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
-
set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
let value = {
age: "18",
name: "wmm",
};
// let name = value.name;
// let age = value.age;
value = new Proxy(value, {
// target:目标对象 property:属性名
get(target, property, recevier) {
console.log("target", target);
console.log("property", property);
return Reflect.get(target, property, recevier);
},
// obj:目标对象 prop:属性名 value:属性值
set(obj, prop, value,recevier) {
console.log("obj", obj);
console.log("prop", prop);
console.log("value", value);
return Reflect.set(obj, prop , value, recevier);
}
})
value.name = "wmm122"
console.log(value);
console.log(value.name);
console.log(value);