校招前端面試必問問題之一:vue
雙向綁定原理。javascript
wt?
我怎麼知道?不是會用就能夠了嘛?我管它怎麼實現。vue
雙向綁定是經過數據劫持實現的,經過劫持對象的 getter
和 setter
實現。Object.defineProperty
來劫持對象屬性的 setter
和 getter
操做,當觸發 getter
時收集依賴,當觸發 setter
時執行一些操做。今天咱們的主角,就是 defineProperty
,以及它的兄弟 proxy
。前端
defineProperty
?從名字上看,能夠拆分爲 define
和 property
,分別是 定義
和 屬性
的意思。因此 defineProperty
是用來定義一個屬性的。它接受的參數依次爲 obj
、prop
、descriptor
。vue
Object.defineProperty(obj, prop, descriptor)
複製代碼
其中第一和第二個參數比較簡單,obj
爲屬性所在的對象,prop
爲屬性名,常見寫法以下java
const o = {}
Object.defineProperty(o, 'key', {
value: 'value'
})
console.log(o) // { key: 'value' }
複製代碼
descriptor
: 翻譯過來爲 屬性描述符
,顧名思義,就是用來描述這個屬性的詳細信息,具體信息以下:es6
configurable
:當且僅當 configurable
爲 true
時,該屬性描述符才能被改變,同時該屬性也能從對應的對象上刪除,默認爲 false
const o = {}
Object.defineProperty(o, 'name', {
configurable: false,
value: 'name',
});
Object.defineProperty(o, 'age', {
configurable: true,
value: 23,
});
console.log(o); // { name: 'name', age: 23 }
delete o.name
delete o.age
console.log(o); // { name: 'name' } 其中 name 屬性沒法被刪除
複製代碼
enumerable
:當且僅當 enumerable
爲 true
時,該屬性才能出如今對象的枚舉屬性中,默認爲 false
const o = {}
Object.defineProperty(o, 'name', {
enumerable: false,
value: 'name',
});
Object.defineProperty(o, 'age', {
enumerable: true,
value: 23,
});
console.log(Object.keys(o)); // ["age"] 其中 name 不可被枚舉
複製代碼
value
:屬性的初始值,默認爲 undefined
const o = {}
Object.defineProperty(o, 'name', {
value: 'name',
});
Object.defineProperty(o, 'age', {});
console.log(o); // { name: 'name', age: undefined }
複製代碼
writable
:該屬性可否被賦值運算符改變,默認爲 false
const o = {}
Object.defineProperty(o, 'name', {
writable: false,
value: 'name',
});
Object.defineProperty(o, 'age', {
writable: true,
value: 23
});
console.log(o); // { name: 'name', age: 23 }
o.name = 'Jim';
o.age = 30;
console.log(o); // { name: 'name', age: 30 }
複製代碼
get
:存取描述符之一,給屬性提供一個 getter
方法,當訪問屬性時會被觸發。const o = {}
Object.defineProperty(o, 'name', {
get: () => {
console.log('get o.name')
return 'hello'
}
});
console.log(o.name) // 'get o.name' 'hello'
複製代碼
set
:存取描述符之一,給屬性提供一個 setter
方法,當給屬性賦值時被觸發。const o = {}
Object.defineProperty(o, 'name', {
get: function() {
console.log('getter');
return this._name
},
set: function(newVal) {
console.log('setter', newVal);
this._name = newVal
}
});
o.name = 10; // 'setter 10'
console.log(o.name); // 'getter' 10
複製代碼
proxy
proxy
譯爲 代理
,能夠攔截屬性的一些行爲來作一些特殊處理,下面爲一個簡單的例子面試
const _target = {
name: 'Jim',
}
const handler = {
get: (obj, prop) => obj[prop] || 'no value'
}
const target = new Proxy(_target, handler);
console.log(target.name, target.age); // 'Jim' 'no value'
複製代碼
proxy
能夠攔截十幾種行爲,下面進行了簡單羅列,具體使用方式請 點擊查看app
handler.get
:訪問屬性時觸發函數
handler.set
:屬性被賦值時觸發性能
handler.has
:攔截 in
操做,如 'name' in target
ui
handler.apply
:攔截函數調用,如 target(args)
handler.construct
:攔截 new
操做,如 new Target()
handler.deleteProperty
:攔截 delete
操做,如 delete obj.name
handler.defineProperty
:攔截 defineProperty
操做
defineProterty
和 proxy
的對比defineProterty
是 es5
的標準,proxy
是 es6
的標準;defineProterty
實現雙向數據綁定( vue2.x
採用的核心)proxy
實現雙向數據綁定( vue3.x
會採用)