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