將屬性添加到對象,或修改現有屬性的特性。框架
var obj = {},newVal; // Add an accessor property to the object. Object.defineProperty(obj, "name", { set: function (x) { console.log("in property set accessor" + newVal); this.firstName= x; }, get: function () { console.log("in property get accessor" + newVal); return this.firstName; }, enumerable: true, configurable: true });
Vue.js當數據有變化時,經過defineProperty的set方法去通知notify()訂閱者subscribers有新的值修改異步
function defineReactive(obj, key, val, doNotObserve) { var dep = new Dep(); var property = Object.getOwnPropertyDescriptor(obj, key); if (property && property.configurable === false) { return; } // cater for pre-defined getter/setters var getter = property && property.get; var setter = property && property.set; // if doNotObserve is true, only use the child value observer // if it already exists, and do not attempt to create it. // this allows freezing a large object from the root and // avoid unnecessary observation inside v-for fragments. var childOb = doNotObserve ? isObject(val) && val.__ob__ : observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { var value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); if (childOb) { childOb.dep.depend(); } if (isArray(value)) { for (var e, i = 0, l = value.length; i < l; i++) { e = value[i]; e && e.__ob__ && e.__ob__.dep.depend(); } } } return value; }, set: function reactiveSetter(newVal) { var value = getter ? getter.call(obj) : val; if (newVal === value) { return; } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = doNotObserve ? isObject(newVal) && newVal.__ob__ : observe(newVal); dep.notify(); } }); }
2.MutationObserver函數,HTML5新特性之Mutation Observerasync
Mutation Observer(變更觀察器)是監視DOM變更的接口。當DOM對象樹發生任何變更時,Mutation Observer會獲得通知。ide
要概念上,它很接近事件。能夠理解爲,當DOM發生變更會觸發Mutation Observer事件。可是,它與事件有一個本質不一樣:事件是同步觸發,也就是說DOM發生變更馬上會觸發相應的事件;Mutation Observer則是異步觸發,DOM發生變更之後,並不會立刻觸發,而是要等到當前全部DOM操做都結束後才觸發。
這樣設計是爲了應付DOM變更頻繁的狀況。舉例來講,若是在文檔中連續插入1000個段落(p元素),會連續觸發1000個插入事件,執行每一個事件的回調函數,這極可能形成瀏覽器的卡頓;而Mutation Observer徹底不一樣,只在1000個段落都插入結束後纔會觸發,並且只觸發一次。
Mutation Observer有如下特色:
- 它等待全部腳本任務完成後,纔會運行,即採用異步方式
- 它把DOM變更記錄封裝成一個數組進行處理,而不是一條條地個別處理DOM變更。
- 它便可以觀察發生在DOM節點的全部變更,也能夠觀察某一類變更
/** * Defer a task to execute it asynchronously. Ideally this * should be executed as a microtask, so we leverage * MutationObserver if it's available, and fallback to * setTimeout(0). * * @param {Function} cb * @param {Object} ctx */ var nextTick = (function () { var callbacks = []; var pending = false; var timerFunc; function nextTickHandler() { pending = false; var copies = callbacks.slice(0); callbacks = []; for (var i = 0; i < copies.length; i++) { copies[i](); } } /* istanbul ignore if */ if (typeof MutationObserver !== 'undefined') { var counter = 1; var observer = new MutationObserver(nextTickHandler); var textNode = document.createTextNode(counter); observer.observe(textNode, { characterData: true }); timerFunc = function () { counter = (counter + 1) % 2; textNode.data = counter; }; } else { // webpack attempts to inject a shim for setImmediate // if it is used as a global, so we have to work around that to // avoid bundling unnecessary code. var context = inBrowser ? window : typeof global !== 'undefined' ? global : {}; timerFunc = context.setImmediate || setTimeout; } return function (cb, ctx) { var func = ctx ? function () { cb.call(ctx); } : cb; callbacks.push(func); if (pending) return; pending = true; timerFunc(nextTickHandler, 0); }; })();
異步更新隊列,Vue.js 默認異步更新 DOM。每當觀察到數據變化時,Vue 就開始一個隊列,將同一事件循環內全部的數據變化緩存起來。若是一個 watcher 被屢次觸發,只會推入一次到隊列中。等到下一次事件循環,Vue 將清空隊列,只進行必要的 DOM 更新。在內部異步隊列優先使用MutationObserver
,若是不支持則使用 setTimeout(fn, 0)
。經過MutationObserver, 實現了異步更新隊列