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