什麼是資料雙向綁定?
vue是一個mvvm架構,即資料雙向綁定,即當資料發生變化的時候,視圖也就發生變化,當視圖發生變化的時候,資料也會跟着同步變化。這也算是vue的精髓之處了
為什麼要實作資料的雙向綁定?
在vue中,如果使用vuex,實際上資料還是單向的,之是以說是資料雙向綁定,這是用的UI控件來說,對于我們處理表單,vue的雙向資料綁定用起來就特别舒服了。
即兩者并不互斥, 在全局性資料流使用單項,友善跟蹤; 局部性資料流使用雙向,簡單易操作。
Object.definedProperty方法
Object.definedProperty方法可以在一個對象上直接定義一個新的屬性、或修改一個對象已經存在的屬性,最終傳回這個對象。
vue.js的雙向資料綁定就是通過Object.defineProperty方法實作的,俗稱屬性攔截器。
文法
Object.defineProperty(obj, prop, descriptor)
參數
-
需要定義屬性的對象。obj
-
需被定義或修改的屬性名。prop
-
需被定義或修改的屬性的描述符。descriptor
傳回值
函數将傳回傳遞給他的obj對象本身。即第一個參數obj
描述符(descriptor)說明
該方法允許開發者精确的對對象屬性的定義和修改。通過正常指派進行屬性添加而建構的屬性會被枚舉器方法(如for…in循環或Object.keys方法)擷取,進而導緻屬性值被外部方法改變或删除。而Object.defineProperty()可以避免以上描述的情況,預設的,通過Object.defineProperty()添加的屬性是預設不可改變的。
對象裡目前存在的屬性描述符有兩種主要形式:
資料描述符(data descriptor)和通路器描述符(accessor descriptor)。
資料描述符就是一個包含屬性的值,并說明這個值可讀或不可讀的對象;通路器描述符就是包含該屬性的一對getter-setter方法的對象。
一個完整的屬性描述(descriptor)必須是這兩者之一,并且不可以兩者都有。
資料描述符和通路器描述符各自都是對象,他們必須包含以下鍵值對:
configurable
是否可以删除目标屬性或是否可以再次修改屬性的特性(writable, configurable, enumerable)。設定為true可以被删除或可以重新設定特性;設定為false,不能被可以被删除或不可以重新設定特性。預設為false。
這個屬性起到兩個作用:
- 目标屬性是否可以使用delete删除
- 目标屬性是否可以再次設定特性
//-----------------測試目标屬性是否能被删除------------------------
var obj = {}
//第一種情況:configurable設定為false,不能被删除。
Object.defineProperty(obj,"newKey",{
value:"hello",
writable:false,
enumerable:false,
configurable:false
});
//删除屬性
delete obj.newKey;
console.log( obj.newKey ); //hello
//第二種情況:configurable設定為true,可以被删除。
Object.defineProperty(obj,"newKey",{
value:"hello",
writable:false,
enumerable:false,
configurable:true
});
//删除屬性
delete obj.newKey;
console.log( obj.newKey ); //undefined
//-----------------測試是否可以再次修改特性------------------------
var obj = {}
//第一種情況:configurable設定為false,不能再次修改特性。
Object.defineProperty(obj,"newKey",{
value:"hello",
writable:false,
enumerable:false,
configurable:false
});
//重新修改特性
Object.defineProperty(obj,"newKey",{
value:"hello",
writable:true,
enumerable:true,
configurable:true
});
console.log( obj.newKey ); //報錯:Uncaught TypeError: Cannot redefine property: newKey
//第二種情況:configurable設定為true,可以再次修改特性。
Object.defineProperty(obj,"newKey",{
value:"hello",
writable:false,
enumerable:false,
configurable:true
});
//重新修改特性
Object.defineProperty(obj,"newKey",{
value:"hello",
writable:true,
enumerable:true,
configurable:true
});
console.log( obj.newKey ); //hello
除了可以給新定義的屬性設定特性,也可以給已有的屬性設定特性
//定義對象的時候添加的屬性,是可删除、可重寫、可枚舉的。
var obj = {
test:"hello"
}
//改寫值
obj.test = 'change value';
console.log( obj.test ); //'change value'
Object.defineProperty(obj,"test",{
writable:false
})
//再次改寫值
obj.test = 'change value again';
console.log( obj.test ); //依然是:'change value'
提示:一旦使用Object.defineProperty給對象添加屬性,那麼如果不設定屬性的特性,那麼configurable、enumerable、writable這些值都為預設的false
var obj = {};
//定義的新屬性後,這個屬性的特性中configurable,enumerable,writable都為預設的值false
//這就導緻了neykey這個是不能重寫、不能枚舉、不能再次設定特性
//
Object.defineProperty(obj,'newKey',{
});
//設定值
obj.newKey = 'hello';
console.log(obj.newKey); //undefined
//枚舉
for( var attr in obj ){
console.log(attr);
}
enumerable
此屬性是否可以被枚舉(使用for…in或Object.keys())。設定為true可以被枚舉;設定為false,不能被枚舉。預設為false。
var obj = {}
//第一種情況:enumerable設定為false,不能被枚舉。
Object.defineProperty(obj,"newKey",{
value:"hello",
writable:false,
enumerable:false
});
//枚舉對象的屬性
for( var attr in obj ){
console.log( attr );
}
//第二種情況:enumerable設定為true,可以被枚舉。
Object.defineProperty(obj,"newKey",{
value:"hello",
writable:false,
enumerable:true
});
//枚舉對象的屬性
for( var attr in obj ){
console.log( attr ); //newKey
}
資料描述符可以包含以下可選鍵值對:
value
設定屬性的值,可以是任何JavaScript值類型(number,object,function等類型)。預設為undefined。
var obj = {}
//第一種情況:不設定value屬性
Object.defineProperty(obj,"newKey",{
});
console.log( obj.newKey ); //undefined
------------------------------
//第二種情況:設定value屬性
Object.defineProperty(obj,"newKey",{
value:"hello"
});
console.log( obj.newKey ); //hello
writable
屬性的值是否可以被重寫。設定為true可以被重寫;設定為false,不能被重寫。預設為false。
var obj = {}
//第一種情況:writable設定為false,不能重寫。
Object.defineProperty(obj,"newKey",{
value:"hello",
writable:false
});
//更改newKey的值
obj.newKey = "change value";
console.log( obj.newKey ); //hello
//第二種情況:writable設定為true,可以重寫
Object.defineProperty(obj,"newKey",{
value:"hello",
writable:true
});
//更改newKey的值
obj.newKey = "change value";
console.log( obj.newKey ); //change value
通路器描述符可以包含以下可選鍵值對:
get
getter 是一種獲得屬性值的方法,若屬性沒有getter方法則為undefined。該方法的傳回為屬性的值。預設為undefined。
set
setter是一種設定屬性值的方法。,若屬性沒有setter方法則為undefined。該方法接收唯一的參數,作為屬性的新值。預設為undefined。
在特性中使用get/set屬性來定義對應的方法。
var obj = {};
var initValue = 'hello';
Object.defineProperty(obj,"newKey",{
get:function (){
//當擷取值的時候觸發的函數
return initValue;
},
set:function (value){
//當設定值的時候觸發的函數,設定的新值通過參數value拿到
initValue = value;
}
});
//擷取值
console.log( obj.newKey ); //hello
//設定值
obj.newKey = 'change value';
console.log( obj.newKey ); //change value
注意:當使用了getter或setter方法,不允許使用writable和value這兩個屬性 ; 如果不設定方法,則get和set的預設值為undefined
相容性
在ie8下隻能在DOM對象上使用,嘗試在原生的對象使用 Object.defineProperty()會報錯。
簡單的資料雙向綁定實作方法
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>forvue</title>
</head>
<body>
<input type="text" id="textInput">
輸入:<span id="textSpan"></span>
<script>
var obj = {},
textInput = document.querySelector('#textInput'),
textSpan = document.querySelector('#textSpan');
Object.defineProperty(obj, 'foo', {
set: function (newValue) {
textInput.value = newValue;
textSpan.innerHTML = newValue;
}
});
textInput.addEventListener('keyup', function (e) {
obj.foo = e.target.value;
});
</script>
</body>
</html>
最終效果圖:

可以看到,實作一個簡單的資料雙向綁定還是不難的: 使用Object.defineProperty()來定義屬性的set函數,屬性被指派的時候,修改Input的value值以及span中的innerHTML;然後監聽input的keyup事件,修改對象的屬性值,即可實作這樣的一個簡單的資料雙向綁定。
小結:
VUE實作雙向資料綁定的原理就是利用了 Object.defineProperty() 這個方法重新定義了對象擷取屬性值(get)和設定屬性值(set)的操作來實作的。它接收三個參數,要操作的對象,要定義或修改的對象屬性名,屬性描述符。重點就是最後的屬性描述符。屬性描述符是一個對象,主要有兩種形式:資料描述符和存取描述符。這兩種對象隻能選擇一種使用,不能混合兩種描述符的屬性同時使用。上面說的get和set就是屬于存取描述符對象的屬性。如若被問到VUE雙向綁定的原理?答:VUE實作雙向資料綁定的原理就是利用了 Object.defineProperty() 這個方法重新定義了對象擷取屬性值(get)和設定屬性值(set)的操作來實作的。