vue2 和 vue3 双向绑定原理
<h3>问题:</h3>
<p>我们都听过“数据绑定”这个词,其关键在于监听数据的变化,对于obj这个对象,怎么知道obj.name发生了改变呢?</p>
<pre><code class="language-javascript">var obj = { name: &quot;Andy&quot; }</code></pre>
<hr />
<h3>defineProperty:</h3>
<p>ES5 提供了 Object.defineProperty ,该方法可以在一个对象上定义新属性,或者修改现有属性</p>
<p><strong>语法:</strong>
Object.defineProperty(obj, prop, descriptor)</p>
<p><strong>参数:</strong>
obj: 要在其上定义属性的对象。
prop: 要定义或修改的属性的名称。
descriptor: 将被定义或修改的属性的描述符。</p>
<p><img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=939059eb475fd30d0b3da68f11588f18" alt="" /></p>
<hr />
<h3>getter 和 setter:</h3>
<pre><code class="language-javascript"> var obj = {}
var value = null
Object.defineProperty(obj, &quot;name&quot;, {
get: function () {
console.log('执行了get')
return value;
},
set: function (newValue) {
console.log('执行了set')
value = newValue;
}
})
obj.name = &quot;Andy&quot; // 执行了set
console.log(obj.name); // 执行了get // Andy</code></pre>
<p>封装一下:</p>
<pre><code class="language-javascript"> 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 = &quot;Andy&quot;; // 执行了set
enc.name = &quot;Curry&quot;; // 执行了set
console.log(enc.getList()); // [{ name: Andy }, { name: Curry }]</code></pre>
<p>重新封装一下:</p>
<pre><code class="language-javascript"> &lt;span id=&quot;container&quot;&gt;1&lt;/span&gt;
&lt;button id=&quot;button&quot;&gt;点击加 1&lt;/button&gt;
&lt;script&gt;
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, &quot;value&quot;, function (newvalue) {
document.getElementById('container').innerHTML = newvalue;
})
document.getElementById('button').addEventListener(&quot;click&quot;, function () {
obj.value++
});
&lt;/script&gt;</code></pre>
<hr />
<h3>Proxy:</h3>
<p>defineProperty 只能定义 get 和 set 行为,ES6 提供了 Proxy 构造函数,可以定义更多的行为</p>
<p><strong>语法:</strong>
var proxy = new Proxy(target, handler)</p>
<p>target 参数表示所要拦截的目标对象,handler参数用来定制拦截行为</p>
<pre><code class="language-javascript"> 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 = &quot;Andy&quot;; // 执行set
console.log(proxy.name); // 执行get // Andy</code></pre>
<p>除了 get 和 set ,proxy 可以拦截多达 13 种操作,比如 has(target, propKey),可以拦截 propKey in proxy 的操作,返回一个布尔值。</p>
<pre><code class="language-javascript"> // 使用 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</code></pre>
<p>apply 方法拦截函数的调用:</p>
<pre><code class="language-javascript"> 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</code></pre>
<p>ownKeys:可以拦截对象自身属性的读取操作</p>
<pre><code class="language-javascript"> let target = {
_name: 'Andy',
_age: '21',
address: 'XMU'
};
let handler = {
ownKeys(target) {
return Reflect.ownKeys(target).filter(key =&gt; key[0] !== '_');
}
};
let proxy = new Proxy(target, handler);
console.log(Object.keys(proxy)) // [&quot;address&quot;]
console.log(Object.values(proxy)) // [&quot;XMU&quot;]
console.log(Object.entries(proxy)) // [&quot;address&quot;, &quot;XMU&quot;]</code></pre>
<p>最后使用 proxy 写一下 watch :</p>
<pre><code class="language-javascript"> 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(&quot;click&quot;, function () {
newObj.value += 1
});</code></pre>