vue2 和 vue3 双向绑定原理
问题:
我们都听过“数据绑定”这个词,其关键在于监听数据的变化,对于obj这个对象,怎么知道obj.name发生了改变呢?
var obj = { name: "Andy" }
defineProperty:
ES5 提供了 Object.defineProperty ,该方法可以在一个对象上定义新属性,或者修改现有属性
语法: Object.defineProperty(obj, prop, descriptor)
参数: obj: 要在其上定义属性的对象。 prop: 要定义或修改的属性的名称。 descriptor: 将被定义或修改的属性的描述符。
getter 和 setter:
var obj = {}
var value = null
Object.defineProperty(obj, "name", {
get: function () {
console.log('执行了get')
return value;
},
set: function (newValue) {
console.log('执行了set')
value = newValue;
}
})
obj.name = "Andy" // 执行了set
console.log(obj.name); // 执行了get // Andy
封装一下:
function Encapsulation() {
var value = null
var namelist = []
Object.defineProperty(this, 'name', {
get: function () {
console.log('执行了get')
return value;
},
set: function (value) {
console.log('执行了set')
value = value;
namelist.push({
name: value
})
}
})
this.getList = function () {
return namelist;
}
}
var enc = new Encapsulation();
enc.name; // 执行了get
enc.name = "Andy"; // 执行了set
enc.name = "Curry"; // 执行了set
console.log(enc.getList()); // [{ name: Andy }, { name: Curry }]
重新封装一下:
<span id="container">1</span>
<button id="button">点击加 1</button>
<script>
function watch(obj, name, func) {
var value = obj[name];
Object.defineProperty(obj, name, {
get: function () {
return value;
},
set: function (newValue) {
value = newValue;
func(value)
}
});
}
var obj = {
value: 1
}
watch(obj, "value", function (newvalue) {
document.getElementById('container').innerHTML = newvalue;
})
document.getElementById('button').addEventListener("click", function () {
obj.value++
});
</script>
Proxy:
defineProperty 只能定义 get 和 set 行为,ES6 提供了 Proxy 构造函数,可以定义更多的行为
语法: var proxy = new Proxy(target, handler)
target 参数表示所要拦截的目标对象,handler参数用来定制拦截行为
var proxy = new Proxy({}, {
get: function (obj, prop) {
console.log('执行get')
return obj[prop];
},
set: function (obj, prop, value) {
console.log('执行set')
obj[prop] = value;
}
});
proxy.name = "Andy"; // 执行set
console.log(proxy.name); // 执行get // Andy
除了 get 和 set ,proxy 可以拦截多达 13 种操作,比如 has(target, propKey),可以拦截 propKey in proxy 的操作,返回一个布尔值。
// 使用 has 方法隐藏某些属性,不被 in 运算符发现
var handler = {
has(target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
};
var target = {
_prop: 'foo',
prop: 'foo'
};
var proxy = new Proxy(target, handler);
console.log('_prop' in proxy); // false
apply 方法拦截函数的调用:
var target = function () {
return '1+1=2';
};
var handler = {
apply: function () {
return '1+1=3';
}
};
var p = new Proxy(target, handler);
console.log(p()) // 1+1=3
ownKeys:可以拦截对象自身属性的读取操作
let target = {
_name: 'Andy',
_age: '21',
address: 'XMU'
};
let handler = {
ownKeys(target) {
return Reflect.ownKeys(target).filter(key => key[0] !== '_');
}
};
let proxy = new Proxy(target, handler);
console.log(Object.keys(proxy)) // ["address"]
console.log(Object.values(proxy)) // ["XMU"]
console.log(Object.entries(proxy)) // ["address", "XMU"]
最后使用 proxy 写一下 watch :
function watch(target, func) {
var proxy = new Proxy(target, {
get: function (target, prop) {
return target[prop];
},
set: function (target, prop, value) {
target[prop] = value;
func(prop, value);
}
});
return proxy;
}
var obj = {
value: 1
}
var newObj = watch(obj, function (key, newvalue) {
if (key == 'value') document.getElementById('container').innerHTML = newvalue;
})
document.getElementById('button').addEventListener("click", function () {
newObj.value += 1
});