摘要:數組則在對象監聽之外額外在數組對象上的原型鏈上加一層原型對象來攔截掉等方法然后在執行預設的回調函數最后本文有什么不完善的地方或者流程圖有待改進的地方敬請斧正。
前言
隨著前端交互復雜度的提升,各類框架如angular,react,vue等也層出不窮,這些框架一個比較重要的技術點就是數據綁定。數據的監聽有較多的實現方案,本文將粗略的描述一番,并對其中一個兼容性較好的深入分析。
實現方案簡介目前對象的監聽可行的方案:
臟檢查: 需要遍歷scope對象樹里的$watch數組,使用不當容易造成性能問題
ES5 object.defineproperty: 除ie8部分支持 其他基本都完全支持
ES7 object.observe : 已經移除(緣由)出ES7草案
gecko object.watch :目前只有基于gecko的瀏覽器如火狐支持,官方建議僅供調試用
ES6 Proxy: 目前支持較差,babel也暫不支持轉化
ES5現代瀏覽器基本都支持了,OK,本文將介紹目前支持度最好的object.defineproperty 的Setters 和 Getters方式
object.defineproperty介紹 簡潔的介紹它屬于es5規范,有兩種定義屬性:
一種是 數據屬性 包含Writable,Enumerable,Configurable
一種是 訪問器屬性 包含get 和set
數據屬性的例子
obj.key="static"; //等效于 Object.defineProperty(obj, "key", { enumerable: true, configurable: true, writable: true, value: "static" });
訪問器屬性例子
var obj = { temperature:"test" }; var temperature=""; Object.defineProperty(obj, "temperature", { get: function() { return temperature+"-----after"; }, set: function(value) { temperature = value; } }) obj.temperature="Test"; //Test-----after console.log(obj.temperature);詳細的介紹
火狐開發者
實現監聽的思路將需要監聽對象/數組 obj和回調函數callback傳入構造函數,this.callback = callback 存儲回調函數
遍歷對象/數組obj,通過Object.defineProperty將屬性全部定義一遍。在set函數里面添加callback函數,設置val值。get函數返回val。
判斷對應的obj[key]是否為對象,是則進入第二步,否則繼續遍歷
遍歷結束之后判斷該對象是否為數組,是則對操作數組函數如push,pop,shift,unshift等進行封裝,操作數組前調用callback函數
數組的封裝比較復雜的是數組的封裝,結構如下:
新建一個對象newProto,繼承Array的原型,并在newProto上面封裝push,pop等數組操作方法,再將傳入的array對象的原型設置為newProto。
在獲取數據變化的同時,定位該變化數據在原始根對象的位置,以數組表示如:
如[ "a", "dd", "ffffd" ] 表示對象obj.a.dd.ffffd的屬性改變
實現:每個遍歷對象屬性都通過path.slice(0)的方式復制入參數組path,生成新數組tpath,給tpath數組push對應的對象屬性key,最后在執行set的回調函數時候將tpath當參數傳入
watch.js
/** * * @param obj 需要監聽的對象或數組 * @param callback 當對應屬性變化的時候觸發的回調函數 * @constructor */ function Watch(obj, callback) { this.callback = callback; //監聽_obj對象 判斷是否為對象,如果是數組,則對數組對應的原型進行封裝 //path代表相應屬性在原始對象的位置,以數組表示. 如[ "a", "dd", "ffffd" ] 表示對象obj.a.dd.ffffd的屬性改變 this.observe = function (_obj, path) { var type=Object.prototype.toString.call(_obj); if (type== "[object Object]"||type== "[object Array]") { this.observeObj(_obj, path); if (type == "[object Array]") { this.cloneArray(_obj, path); } } }; //遍歷對象obj,設置set,get屬性,set屬性能觸發callback函數,并將val的值改為newVal //遍歷結束后再次調用observe函數 判斷val是否為對象,如果是則在對val進行遍歷設置set,get this.observeObj = function (obj, path) { var t = this; Object.keys(obj).forEach(function (prop) { var val = obj[prop]; var tpath = path.slice(0); tpath.push(prop); Object.defineProperty(obj, prop, { get: function () { return val; }, set: function (newVal) { t.callback(tpath, newVal, val); val = newVal; } }); t.observe(val, tpath); }); }; //通過對特定數組的原型中間放一個newProto原型,該原型繼承于Array的原型,但是對push,pop等數組操作屬性進行封裝 this.cloneArray = function (a_array, path) { var ORP = ["push", "pop", "shift", "unshift", "splice", "sort", "reverse"]; var arrayProto = Array.prototype; var newProto = Object.create(arrayProto); var t = this; ORP.forEach(function (prop) { Object.defineProperty(newProto, prop, { value: function (newVal) { path.push(prop); t.callback(path, newVal); arrayProto[prop].apply(a_array, arguments); }, enumerable: false, configurable: true, writable: true }); }); a_array.__proto__ = newProto; }; //開始監聽obj對象,初始path為[] this.observe(obj, []); }
index.html
完整代碼地址
流程圖具體流程的復雜度基于監聽對象的深度,所以下圖只對父對象做流程分析
通過定義對象內部屬性的setter和getter方法,對將要變化的屬性進行攔截代理,在變化前執行預設的回調函數來達到對象監聽的目的。
數組則在對象監聽之外額外在數組對象上的原型鏈上加一層原型對象,來攔截掉push,pop等方法,然后在執行預設的回調函數
最后本文有什么不完善的地方,或者流程圖有待改進的地方,敬請斧正。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87908.html
摘要:事件的監聽與事件的觸發事件一事件機制的實現中大部分的模塊,都繼承自模塊。從另一個角度來看,事件偵聽器模式也是一種事件鉤子的機制,利用事件鉤子導出內部數據或狀態給外部調用者。的核心就是事件發射與事件監聽器功能的封裝。 nodejs事件的監聽與事件的觸發 nodejs事件(Events)showImg(https://segmentfault.com/img/bV0Sqi?w=692&h=...
摘要:我的個人博客地址資源地址非父子組件傳值,事件總線的使用方式我的博客地址如果您對我的博客內容有疑惑或質疑的地方,請在下方評論區留言,或郵件給我,共同學習進步。 歡迎訪問我的個人博客:http://www.xiaolongwu.cn 前言 先說一下什么是事件總線,其實就是訂閱發布者模式; 比如有一個bus對象,這個對象上有兩個方法,一個是on(監聽,也就是訂閱),一個是emit(觸發,也就...
摘要:之前的文章里有說,在中,流是許許多多原生對象的父類,角色可謂十分重要。效率更高的從數組中去除一個元素。不過這個所提供的功能過于多了,它支持去除自定義數量的元素,還支持向數組中添加自定義的元素。 之前的文章里有說,在 Node.js 中,流(stream)是許許多多原生對象的父類,角色可謂十分重要。但是,當我們沿著族譜往上看時,會發現 EventEmitter 類是流(stream)類的...
摘要:取消事件的默認行為。阻止事件的派發包括了捕獲和冒泡阻止同一個事件的其他監聽函數被調用。 事件模型 DOM0 級事件模型 -沒有事件流,這種方式兼容所有瀏覽器 // 方式一 將事件直接通過屬性綁定在元素上 / 方式二 獲取到頁面元素后,通過 onclick 等事件,將觸發的方法指定為元素的事件 var btn = document.getElementById(btn) btn....
閱讀 3591·2023-04-26 01:43
閱讀 2977·2021-10-14 09:42
閱讀 5445·2021-09-30 09:59
閱讀 2177·2021-09-04 16:40
閱讀 1212·2019-08-30 15:52
閱讀 828·2019-08-29 17:09
閱讀 2000·2019-08-26 13:37
閱讀 3436·2019-08-26 10:20