上一篇的末尾,筆者簡單介紹了Object.defineProperty
在數組監控方面的不足以及其替代品Proxy
。可是對於前者總感受還少點什麼,emmmmm...好像是demo,因而筆者精心準備了一下。因此本篇會主要會分紅兩大塊:一是講述如何彌補Object.defineProperty
先天不足的狀況下實現對數組的精準監控,二是着重用Proxy
實現雙向數據綁定。node
首先呢,對於Object.defineProperty
在數組監控方面的不足,咱們不只要知其然,更要知其因此然。所以先用栗子來證明下這個不足
。上代碼:
let data = { list: [] } Object.keys(data).forEach(function (key) { let value = data[key]; Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { return value; }, set(newValue) { console.log(`Setting`); value = newValue; return true; } }) }) data.list.push(1); //---> A // data.list = [1, 2, 3]; //---> B console.log(data.list);
事先咱們須要明確下:因此對數組的監控,就是能監測到數組元素的 增長或者刪除。所以按照‘理論’,上述代碼若是向list
中增長元素的時候,理應會有打印Setting
(筆者已經標記了最後的A
行 和B
行)
在只註釋 B
行狀況下看運行結果:git
/usr/local/bin/node --inspect-brk=17809 demo.js Debugger listening on ws://127.0.0.1:17809/48911764-8533-4c95-a501-20384c924f6a Debugger attached. Array(1) [1]
能夠看出,雖然咱們成功得向數組裏增長了一個元素,可是並無打印出 Setting
,所以就是說並無監測到數組的變化.es6
在只註釋 A
行狀況下看運行結果:segmentfault
/usr/local/bin/node --inspect-brk=27303 demo.js Debugger listening on ws://127.0.0.1:27303/2fac4f06-e775-4485-b70b-b2660a98c2b8 Debugger attached. Setting Array(3) [1, 2, 3]
咱們成功得向數組裏增長了一個元素也沒有打印出 Setting
,說明數組的變化被監測到了.源碼數組
因此咱們能夠大膽得猜想:當監控 數組 數據對象的時候,實質上就是監控數組的 地址,地址不變也就不會被監測到,因此咱們向list
裏 push 元素的時候並無觸發打印;當咱們直接替換list
對象的時候就觸發了打印。因此這就是Object.defineProperty
在數組監控方面的不足。
下面就用例子介紹下如何彌補這方面的不足(固然,也是Vue的處理方式)app
其核心思想就是
覆寫
數組對象中的方法,在調用數組方法的同時能觸發回調。下面是核心代碼:
let arrayMethod = Object.create(Array.prototype); ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) { Object.defineProperty(arrayMethod, method, { enumerable: true, configurable: true, value: function () { let args = [...arguments] Array.prototype[method].apply(this, args); console.log(`operation: ${method}`) dep.notify(); } }) }); [Array Object].__proto__ = arrayMethod;
同時這樣作的好處是,僅僅是數據中的數組對象的原型被修改掉了,並不會影響到全局的數組對象。
而後筆者也作了一個例子,以下:框架
經過向 list
中添加隨機數或者刪除隨機數,能同步渲染到頁面上,源碼在這函數
Proxy
意爲代理
,通俗來講就是在 目標數據對象 外圍設置一層 攔截。
舉個形象的栗子:有一個倉庫
,一開始你們能夠任意得存放貨物,後來老闆請了一個倉庫管家
,全部人想要存放貨物的必需要通過管家的手。並且老闆還給管家制定了一套管理標準
,管家對倉庫的管理必須嚴格按照這套標準。
根據這個栗子,羅列了三個關鍵詞:倉庫
,倉庫管家
,管理標準
,下面用一段簡單的代碼來表述下, 若是想全面得學習Proxy, 請參考阮一峯老師的博客:學習
let dataProxy = new Proxy(data, { set() { }, get() { } })
那麼代碼與栗子的對於關係就出來了:this
倉庫 --- data
倉庫管家 --- Proxy / dataProxy
管理標準 --- {set(),get()}
並且能夠注意到 Proxy 實際上是個構造函數,全部咱們對原數據對象的操做都得經過構造函數new
出來的對象 dataProxy
。就好像全部貨物的存取都得經過管家同樣
而後接下來筆者用 Proxy 寫了一個栗子,用來 重現 上面對數組的監控,核心代碼以下:
//數據源 let vm = { list: [1, 2, 3, 4] } let vmProxy = new Proxy(vm.list, { set(target, prop, value) { console.log(`Setting: ${value}`); Reflect.set(target, prop, value); dep.notify(); return true; } })
咱們能夠注意到咱們並無直接將 vm
用Proxy進行「包裝」,而是將vm.list
進行「包裝」。由於筆者在學習的時候發現若是直接用 vm
的話,當咱們向 list
中添加元素的時候並不會被監測到,筆者猜想的緣由和 Object.defineProperty
同樣,栗子呈上。若是有知道緣由的小夥伴,請多多指教。
篇幅有限,這個例子的代碼直接上連接了,有興趣的小夥伴能夠直接down下來看。
數組說完了,剩下就普通類型的數據就是按照正常套路走就對了。另外,筆者此次吸收上篇博客的教訓,將代碼寫得更詳盡,實現方式更貼近框架級別。用Proxy實現數據的雙向綁定,栗子的功能和上篇博客是同樣的,只是內容更加飽滿。該栗子中筆者簡單實現了一個簡單版的類Vue的框架 View
,包含Html文檔的解析、Watcher的構造、數據的綁定等,代碼簡單不復雜,因此這裏就不贅述了,我以爲千言萬語不敵一段代碼,那麼相關的代碼就此呈上。
感謝小夥伴的捧場!我會堅持寫博客,分享本身的見聞和工做中遇到的坑,共同窗習,共同進步