天天看點

vue 數組轉集合_Vue源碼中值得學習的方法

作者:chinamasters 來源:segmentfault

最近在深入研究vue源碼,把學習過程中,看到的一些好玩的的函數方法收集起來做分享,希望對大家對深入學習js有所幫助。如果大家都能一眼看懂這些函數,說明技術還是不錯的哦。

vue 數組轉集合_Vue源碼中值得學習的方法

1. 資料類型判斷

Object.prototype.toString.call()傳回的資料格式為 [object Object]類型,然後用slice截取第8位到倒一位,得到結果為 Object

var _toString = Object.prototype.toString; function toRawType (value) {   return _toString.call(value).slice(8, -1) } 
           

運作結果測試

toRawType({}) //  Object  toRawType([])  // Array     toRawType(true) // Boolean toRawType(undefined) // Undefined toRawType(null) // Null toRawType(function(){}) // Function 
           

2. 利用閉包構造map緩存資料

vue中判斷我們寫的元件名是不是html内置标簽的時候,如果用數組類周遊那麼将要循環很多次擷取結果,如果把數組轉為對象,把标簽名設定為對象的key,那麼不用依次周遊查找,隻需要查找一次就能擷取結果,提高了查找效率。

function makeMap (str, expectsLowerCase) {     // 建構閉包集合map     var map = Object.create(null);     var list = str.split(',');     for (var i = 0; i < list.length; i++) {       map[list[i]] = true;     }     return expectsLowerCase       ? function (val) { return map[val.toLowerCase()]; }       : function (val) { return map[val]; } } // 利用閉包,每次判斷是否是内置标簽隻需調用isHTMLTag var isHTMLTag = makeMap('html,body,base,head,link,meta,style,title') console.log('res', isHTMLTag('body')) // true 
           

3. 二維數組扁平化

vue中_createElement格式化傳入的children的時候用到了simpleNormalizeChildren函數,原來是為了拍平數組,使二維數組扁平化,類似lodash中的flatten方法。

// 先看lodash中的flatten _.flatten([1, [2, [3, [4]], 5]]) // 得到結果為  [1, 2, [3, [4]], 5]  // vue中 function simpleNormalizeChildren (children) {   for (var i = 0; i < children.length; i++) {     if (Array.isArray(children[i])) {       return Array.prototype.concat.apply([], children)     }   }   return children }  // es6中 等價于 function simpleNormalizeChildren (children) {    return [].concat(...children) } 
           

4. 方法攔截

vue中利用Object.defineProperty收集依賴,進而觸發更新視圖,但是數組卻無法監測到資料的變化,但是為什麼數組在使用push pop等方法的時候可以觸發頁面更新呢,那是因為vue内部攔截了這些方法。

// 重寫push等方法,然後再把原型指回原方法   var ARRAY_METHOD = [ 'push', 'pop', 'shift', 'unshift', 'reverse',  'sort', 'splice' ];   var array_methods = Object.create(Array.prototype);   ARRAY_METHOD.forEach(method => {     array_methods[method] = function () {       // 攔截方法       console.log('調用的是攔截的 ' + method + ' 方法,進行依賴收集');       return Array.prototype[method].apply(this, arguments);     }   }); 
           

運作結果測試

var arr = [1,2,3] arr.__proto__ = array_methods // 改變arr的原型 arr.unshift(6) // 列印結果: 調用的是攔截的 unshift 方法,進行依賴收集 
           

5. 繼承的實作

vue中調用Vue.extend執行個體化元件,Vue.extend就是VueComponent構造函數,而VueComponent利用Object.create繼承Vue,是以在平常開發中Vue 和 Vue.extend差別不是很大。這邊主要學習用es5原生方法實作繼承的,當然了,es6中 class類直接用extends繼承。

// 繼承方法   function inheritPrototype(Son, Father) {    var prototype = Object.create(Father.prototype)    prototype.constructor = Son    // 把Father.prototype指派給 Son.prototype    Son.prototype = prototype  }  function Father(name) {    this.name = name    this.arr = [1,2,3]  }  Father.prototype.getName = function() {    console.log(this.name)  }  function Son(name, age) {    Father.call(this, name)    this.age = age  }  inheritPrototype(Son, Father)  Son.prototype.getAge = function() {    console.log(this.age)  } 
           

運作結果測試

var son1 = new Son("AAA", 23) son1.getName()            //AAA son1.getAge()             //23 son1.arr.push(4)           console.log(son1.arr)     //1,2,3,4  var son2 = new Son("BBB", 24) son2.getName()            //BBB son2.getAge()             //24 console.log(son2.arr)     //1,2,3 
           

6. 執行一次

once 方法相對比較簡單,直接利用閉包實作就好了

function once (fn) {   var called = false;   return function () {     if (!called) {       called = true;       fn.apply(this, arguments);     }   } } 
           

7. 淺拷貝

簡單的深拷貝我們可以用 JSON.stringify() 來實作,不過vue源碼中的looseEqual 淺拷貝寫的也很有意思,先類型判斷再遞歸調用,總體也不難,學一下思路。

function looseEqual (a, b) {   if (a === b) { return true }   var isObjectisObjectA = isObject(a);   var isObjectisObjectB = isObject(b);   if (isObjectA && isObjectB) {     try {       var isArrayA = Array.isArray(a);       var isArrayB = Array.isArray(b);       if (isArrayA && isArrayB) {         return a.length === b.length && a.every(function (e, i) {           return looseEqual(e, b[i])         })       } else if (!isArrayA && !isArrayB) {         var keysA = Object.keys(a);         var keysB = Object.keys(b);         return keysA.length === keysB.length && keysA.every(function (key) {           return looseEqual(a[key], b[key])         })       } else {         /* istanbul ignore next */         return false       }     } catch (e) {       /* istanbul ignore next */       return false     }   } else if (!isObjectA && !isObjectB) {     return String(a) === String(b)   } else {     return false   } } function isObject (obj) {   return obj !== null && typeof obj === 'object' } 
           

繼續閱讀