什么是雙向綁定
首先來說一下單向綁定,單向綁定就是把 Model 綁定到 View,當(dāng)我們用 JS 代碼更新 Model 時(shí),View 就會(huì)自動(dòng)更新。
有單向綁定,就有雙向綁定。如果用戶更新了 View,Model 的數(shù)據(jù)也自動(dòng)被更新了,這種情況就是雙向綁定。
object.defineProperty
Object.defineProperty 是 ES5 中雙向綁定的方法,下面開始介紹它的語法
Object.defineProperty(obj, prop, descriptor);
// Object.defineProperty(對(duì)象, 屬性, 描述符)
/* descriptor 對(duì)象屬性的詳細(xì)介紹 */
/* 描述符 */
value: "" // 屬性值 默認(rèn) ''
weitable: true // 是否可寫 默認(rèn)值 false
configurable: true //是否可配置 默認(rèn)值 false
enumerable: true // 是否可枚舉 默認(rèn)值 false
/* 存取描述符 */
set: function(){} // 屬性訪問器 進(jìn)行寫操作時(shí)調(diào)用該方法
get: function(){} // 屬性訪問器 進(jìn)行讀操作時(shí)調(diào)用該方法
描述符介紹
我們在正常聲明一個(gè)對(duì)象,都是可寫,可配置,可枚舉的。例如:
let a = {
name:'ccc'
}
a.name = "xxx" // weitable可寫
for(let i in a){
console.log(i) // 輸出 name 說明 enumerable 可枚舉
}
delete a.name // 返回 true configurable 可配置
使用 Object.defineProperty
var a = {
}
Object.defineProperty(a,"name",{
value: 'ccc',
})
a.name = "xxx"; // a.name 返回的還是 ccc
delete a.name // 返回 false
for(var i in a){
console.log(i) //返回 undefined
}
因?yàn)槲彝ㄟ^ Object.defineProperty 設(shè)置的屬性和屬性值,后面的 weitable(寫入)、configurable(配置)、enumerable(枚舉) 都是默認(rèn)值 false,所以不可寫入,不可配置,不可枚舉的。
var a = {
}
Object.defineProperty(a,"name",{
value: 'ccc',
weitable: true
configurable: true,
enumerable: true
})
a.name = "xxx"; // a.name 返回 xxx
delete a.name // 返回 true
for(var i in a){
console.log(i) //返回 name
}
在設(shè)置 weitable(寫入)、configurable(配置)、enumerable(枚舉)這三個(gè)屬性為 true 之后,就可以進(jìn)行寫入、配置、枚舉操作。
存取描述符
我們下一下面的例子
var a = {
}
Object.defineProperty(a,"name",{
// 在設(shè)置 get 和 set 之后 value weitable 這兩個(gè)屬性就不能設(shè)置了
// 在設(shè)置 get 和 set 之后 value weitable 不能共存
configurable: true,
enumerable: true,
get: function (){
console.log('執(zhí)行g(shù)et了');
},
set: function(){
console.log('執(zhí)行set了');
}
})
a.name = "ccc"; // 輸出 執(zhí)行 set 了
console.log(a.name); // 輸出 執(zhí)行 get 了
在上面代碼中 a.name 被賦值 ccc,被 set 方法監(jiān)聽到執(zhí)行 console.log('執(zhí)行g(shù)et了')。之后又訪問 a.name 被 set 方法監(jiān)聽到執(zhí)行 console.log('執(zhí)行set了')。
var val;
var a = {
}
Object.defineProperty(a,"name",{
// 在設(shè)置 get 和 set 之后 value weitable 這兩個(gè)屬性就不能設(shè)置了
// 在設(shè)置 get 和 set 之后 value weitable 不能共存
configurable: true,
enumerable: true,
get: function (){
console.log('執(zhí)行g(shù)et了');
return val;
},
set: function(newVal){
console.log('執(zhí)行set了');
val = newVal;
console.log(val);
}
})
a.name = "ccc"; // 輸出 執(zhí)行 set 了 并且執(zhí)行 set 里面 console.log(val) 是 ccc;
console.log(a.name); // 輸出 執(zhí)行 get 了 打印 a.name 是 ccc
簡單的模擬一下雙向綁定
VUE2.0 雙向數(shù)據(jù)綁定核心功能由 Observe、Compile、Watcher 三部分實(shí)現(xiàn)。其中 Observer 的部分功能由 Object.defineProperty 實(shí)現(xiàn)。(Observer 監(jiān)測數(shù)據(jù)變化進(jìn)行相應(yīng)回調(diào))。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id='inputDemo'/>
<div id="txt"></div>
</body>
<script>
var data = {
value: 'ccc',
name:{
nameData: "ttt"
}
}
inputDemo.oninput = function(){
data.name.nameData = this.value;
}
function upData (){
txt.innerText = data.name.nameData;
}
upData();
// 監(jiān)控對(duì)象的某個(gè)屬性是否發(fā)生改變
function Observer (obj) {
// 判斷 obj 是否存在或類型不是對(duì)象 如果是直接返回 obj
if(!obj || typeof obj != 'object') return obj;
// 通過 keys 拿到 obj 的屬性,之后在循環(huán)一個(gè)一個(gè)拿出來
Object.keys(obj).forEach(function(ele){
// 執(zhí)行 defineReactive(監(jiān)聽的對(duì)象, 監(jiān)聽對(duì)象的屬性, 屬性值)
defineReactive(obj,ele,obj[ele]);
})
}
function defineReactive (obj,ele,value) {
// 執(zhí)行遞歸 value 里面還有沒有深層的對(duì)象
Observer(value);
// 開始監(jiān)聽
Object.defineProperty(obj,ele,{
get(){
console.log("執(zhí)行了get")
return value;
},
set(newVal){
console.log("執(zhí)行了set");
if(newVal == value) return
value = newVal;
upData();
}
});
}
// 調(diào)用監(jiān)聽的方法
Observer(data);
data.value = 'xxx';
</script>
</html>
通過這種方式寫就可以實(shí)現(xiàn) input 和 div 實(shí)現(xiàn)一個(gè)雙向綁定。
數(shù)組雙向綁定
Object.defineProper 不支持?jǐn)?shù)組的監(jiān)聽,可以用 array.prototype 原型對(duì)象
var arr = [];
var push = Array.prototype.push
function upData () {
console.log('數(shù)組更新了')
}
var arrFun = ["pop","shift","unshift","splice ","splice", 'push']
Object.defineProperty(Array.prototype, arrFun[5], {
value: (function(){
return function (){
push.apply(arr, [].slice.call(arguments));
}
})()
});
arr[arrFun[5]](1,2);
Object.defineProperty 除了支持監(jiān)聽數(shù)組之外,還對(duì) Observer 之后新增的對(duì)象也不支持監(jiān)聽
var data = {
value: 'ccc',
name:{
nameData: "ttt"
}
}
// 監(jiān)控對(duì)象的某個(gè)屬性是否發(fā)生改變
function Observer (obj) {
// ...
}
function defineReactive (obj,ele,value) {
// ...
}
// 調(diào)用監(jiān)聽的方法
Observer(data);
data.a = 10; // Object.defineProperty 對(duì)這個(gè)屬性 不支持監(jiān)聽
// 因?yàn)樵?Observer() 之后