摘要:,而且每種框架雙向數(shù)據(jù)綁定的實現(xiàn)方式都不太一致,比如內(nèi)部使用的是臟檢查,而內(nèi)部實現(xiàn)方式的本質(zhì)是設(shè)置屬性訪問器。在中也有類似的概念,不過不叫魔術(shù)方法,而是叫做訪問器。
緣起
前幾天在看一些流行的迷你mvvm框架(比如avalon.js、 vue.js 這種較輕的框架,而非Angularjs、Emberjs這種較重的框架)的實現(xiàn)。現(xiàn)代流行的mvvm框架一般都會將數(shù)據(jù)雙向綁定(two-ways data binding)做掉,作為框架自身的一個賣點( Ember.js 貌似是不支持?jǐn)?shù)據(jù)雙向綁定的。),而且每種框架雙向數(shù)據(jù)綁定的實現(xiàn)方式都不太一致,比如Anguarjs內(nèi)部使用的是 臟檢查,而avalon.js內(nèi)部實現(xiàn)方式的本質(zhì)是設(shè)置 屬性訪問器 。
這里不打算具體的討論各個框架對雙向數(shù)據(jù)綁定的具體實現(xiàn),僅說一下前端實現(xiàn)雙向數(shù)據(jù)綁定的幾種常用方法,并著重講一下avalon.js實現(xiàn)雙向數(shù)據(jù)綁定的技術(shù)選型。
雙向數(shù)據(jù)綁定的常規(guī)實現(xiàn)方式
首先我們來說一下何為前端的 雙向數(shù)據(jù)綁定 。簡單的來說,就是框架的控制器層(這里的控制器層是一個泛指,可以理解為控制view行為和聯(lián)系model層的中間件)和UI展示層(view層)建立一個雙向的數(shù)據(jù)通道。當(dāng)這兩層中的任何一方發(fā)生變化時,另一層將會立即(或者看起來是 立即)自動作出相應(yīng)的變化。
一般來說要實現(xiàn)這種雙向數(shù)據(jù)綁定關(guān)系(控制器層與展示層的關(guān)聯(lián)過程),在前端目前會有三種方式,
臟檢查
觀察機制
封裝屬性訪問器
臟檢查
我們說Angularjs(這里特指AngularJS 1.x.x版本,不代表AngularJS 2.x.x版本)雙向數(shù)據(jù)綁定的技術(shù)實現(xiàn)是臟檢查,大致的原理就是,Angularjs內(nèi)部會維護(hù)一個序列,將所有需要監(jiān)控的屬性放在這個序列中,當(dāng)發(fā)生某些特定事件時(注意,這里并不是定時的而是由某些特殊事件觸發(fā)的),Angularjs會調(diào)用 $digest 方法,這個方法內(nèi)部做的邏輯就是遍歷所有的watcher,對被監(jiān)控的屬性做對比,對比其在方法調(diào)用前后屬性值有沒有發(fā)生變化,如果發(fā)生變化,則調(diào)用對應(yīng)的handler。網(wǎng)上有許多剖析Angularjs雙向數(shù)據(jù)綁定實現(xiàn)原理的文章,比如 這篇 ,再比如 這篇 ,等等。
這種方式的缺點很明顯,遍歷輪訓(xùn)watcher是非常消耗性能的,特別是當(dāng)單頁的監(jiān)控數(shù)量達(dá)到一個數(shù)量級的時候。
觀察機制
博主之前有一篇轉(zhuǎn)載翻譯的文章, Object.observe()帶來的數(shù)據(jù)綁定變革 ,說的就是使用ECMAScript7中的 Object.observe 方法對對象(或者其屬性)進(jìn)行監(jiān)控觀察,一旦其發(fā)生變化時,將會執(zhí)行相應(yīng)的handler。
這是目前監(jiān)控屬性數(shù)據(jù)變更最完美的一種方法,語言(瀏覽器)原生支持,沒有什么比這個更好了。唯一的遺憾就是目前支持廣度還不行,有待全面推廣。
封裝屬性訪問器
在php中有 魔術(shù)方法 這樣一種概念,比如php中的 __get() 和 __set() 方法。在javascript中也有類似的概念,不過不叫魔術(shù)方法,而是叫做訪問器。我們來看個示例代碼,
var data = { name: "erik", getName: function() { return this.name; }, setName: function(name) { this.name = name; } };
從上面的代碼中我們可以管中窺豹,比如 data 中的 getName() 和 setName() 方法,我們可以簡單的將其看成 data.name 的訪問器(或者叫做 存取器 )。
其實,針對上述的代碼,更加嚴(yán)格一點的話,不允許直接訪問 data.name 屬性,所有對 data.name 的讀寫都必須通過 data.getName() 和 data.setName() 方法。所以,想象一下,一旦某個屬性不允許對其進(jìn)行直接讀寫,而必須是通過訪問器進(jìn)行讀寫時,那么我當(dāng)然通過重寫屬性的訪問器方法來做一些額外的情,比如屬性值變更監(jiān)控。使用屬性訪問器來做數(shù)據(jù)雙向綁定的原理就是在此。
這種方法當(dāng)然也有弊端,最突出的就是每添加一個屬性監(jiān)控,都必須為這個屬性添加對應(yīng)訪問器方法,否則這個屬性的變更就無法捕獲。
Object.defineProperty 方法
國產(chǎn)mvvm框架avalon.js實現(xiàn)數(shù)據(jù)雙向綁定的原理就是屬性訪問器。不過它當(dāng)然不會像上述示例代碼一樣原始。它使用了ECMAScript5.1(ECMA-262)中定義的標(biāo)準(zhǔn)屬性 Object.defineProperty 方法。針對國內(nèi)行情,部分還不支持 Object.defineProperty 低級瀏覽器采用VBScript作了完美兼容,不像其他的mvvm框架已經(jīng)逐漸放棄對低端瀏覽器的支持。
我們先來MDN上對 Object.defineProperty 方法的定義,
The Object.defineProperty() method defines a new property directly on an object, or modifies an existing property on an object, and returns the object.
意義很明確, Object.defineProperty 方法提供了一種直接的方式來定義對象屬性或者修改已有對象屬性。其方法原型如下,
Object.defineProperty(obj, prop, descriptor)
其中,
obj ,待修改的對象 prop ,帶修改的屬性名稱 descriptor ,待修改屬性的相關(guān)描述 descriptor 要求傳入一個對象,其默認(rèn)值如下, * @{param} descriptor */ }//歡迎加入全棧開發(fā)交流圈一起學(xué)習(xí)交流:582735936 ]//面向1-3年前端人員 } //幫助突破技術(shù)瓶頸,提升思維能力 { configurable: false, enumerable: false, writable: false, value: null, set: undefined, get: undefined }configurable ,屬性是否可配置。可配置的含義包括:是否可以刪除屬性( delete ),是否可以修改屬性的 writable 、 enumerable 、 configurable 屬性。 enumerable ,屬性是否可枚舉。可枚舉的含義包括:是否可以通過 for...in 遍歷到,是否可以通過 Object.keys() 方法獲取屬性名稱。 writable ,屬性是否可重寫。可重寫的含義包括:是否可以對屬性進(jìn)行重新賦值。 value ,屬性的默認(rèn)值。 set ,屬性的重寫器(暫且這么叫)。一旦屬性被重新賦值,此方法被自動調(diào)用。 get ,屬性的讀取器(暫且這么叫)。一旦屬性被訪問讀取,此方法被自動調(diào)用。
下面來一段示例代碼,
var o = {}; Object.defineProperty(o, "name", { value: "erik" }); console.log(Object.getOwnPropertyDescriptor(o, "name")); // Object {value: "erik", writable: false, enumerable: false, configurable: false} Object.defineProperty(o, "age", { value: 26, configurable: true, writable: true }); }//歡迎加入全棧開發(fā)交流圈一起學(xué)習(xí)交流:582735936 ]//面向1-3年前端人員 } //幫助突破技術(shù)瓶頸,提升思維能力 console.log(o.age); // 26 o.age = 18; console.log(o.age); // 18. 因為age屬性是可重寫的 console.log(Object.keys(o)); // []. name和age屬性都不是可枚舉的 Object.defineProperty(o, "sex", { value: "male", writable: false }); o.sex = "female"; // 這里的賦值其實是不起作用的 console.log(o.sex); // "male"; delete o.sex; // false, 屬性刪除的動作也是無效的
經(jīng)過上述的示例,正常情況下 Object.definePropert() 的使用都是比較簡單的。
不過還是有一點需要額外注意一下, Object.defineProperty() 方法設(shè)置屬性時,屬性不能同時聲明訪問器屬性( set 和 get )和 writable 或者 value 屬性。 意思就是,某個屬性設(shè)置了writable 或者 value 屬性,那么這個屬性就不能聲明 get 和 set 了,反之亦然。
因為 Object.defineProperty() 在聲明一個屬性時,不允許同一個屬性出現(xiàn)兩種以上存取訪問控制。
示例代碼,
var o = {}, myName = "erik"; Object.defineProperty(o, "name", { value: myName, set: function(name) { myName = name; }, get: function() { return myName; } });
上面的代碼看起來貌似是沒有什么問題,但是真正執(zhí)行時會報錯,報錯如下,
TypeError: Invalid property. A property cannot both have accessors and be writable or have a value, #
因為這里的 name 屬性同時聲明了 value 特性和 set 及 get 特性,這兩者提供了兩種對 name 屬性的讀寫控制。這里如果不聲明 value 特性,而是聲明 writable 特性,結(jié)果也是一樣的,同樣會報錯。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/99491.html
摘要:,而且每種框架雙向數(shù)據(jù)綁定的實現(xiàn)方式都不太一致,比如內(nèi)部使用的是臟檢查,而內(nèi)部實現(xiàn)方式的本質(zhì)是設(shè)置屬性訪問器。在中也有類似的概念,不過不叫魔術(shù)方法,而是叫做訪問器。 緣起前幾天在看一些流行的迷你mvvm框架(比如avalon.js、 vue.js 這種較輕的框架,而非Angularjs、Emberjs這種較重的框架)的實現(xiàn)。現(xiàn)代流行的mvvm框架一般都會將數(shù)據(jù)雙向綁定(two-ways...
摘要:的數(shù)據(jù)劫持版本內(nèi)部使用了來實現(xiàn)數(shù)據(jù)與視圖的雙向綁定,體現(xiàn)在對數(shù)據(jù)的讀寫處理過程中。這樣就形成了數(shù)據(jù)的雙向綁定。 MVVM由以下三個內(nèi)容組成 View:視圖模板 Model:數(shù)據(jù)模型 ViewModel:作為橋梁負(fù)責(zé)溝通View和Model,自動渲染模板 在JQuery時期,如果需要刷新UI時,需要先取到對應(yīng)的DOM再更新UI,這樣數(shù)據(jù)和業(yè)務(wù)的邏輯就和頁面有強耦合。 在MVVM中,U...
摘要:接下來要看看這個訂閱者的具體實現(xiàn)了實現(xiàn)訂閱者作為和之間通信的橋梁,主要做的事情是在自身實例化時往屬性訂閱器里面添加自己自身必須有一個方法待屬性變動通知時,能調(diào)用自身的方法,并觸發(fā)中綁定的回調(diào),則功成身退。 本文能幫你做什么?1、了解vue的雙向數(shù)據(jù)綁定原理以及核心代碼模塊2、緩解好奇心的同時了解如何實現(xiàn)雙向綁定為了便于說明原理與實現(xiàn),本文相關(guān)代碼主要摘自vue源碼, 并進(jìn)行了簡化改造,...
閱讀 1847·2021-11-22 15:25
閱讀 3912·2021-11-17 09:33
閱讀 2507·2021-10-12 10:12
閱讀 1802·2021-10-09 09:44
閱讀 3235·2021-10-08 10:04
閱讀 1313·2021-09-29 09:35
閱讀 1947·2019-08-30 12:57
閱讀 1303·2019-08-29 16:22