研究Vue源碼時(shí)發(fā)現(xiàn)的一些好玩函數(shù)

最近在深入研究vue源碼,把學(xué)習(xí)過(guò)程中,看到的一些好玩的的函數(shù)方法收集起來(lái)做分享,希望對(duì)大家對(duì)深入學(xué)習(xí)js有所幫助。如果大家都能一眼看懂這些函數(shù),說(shuō)明技術(shù)還是不錯(cuò)的哦。
1. 數(shù)據(jù)類(lèi)型判斷
Object.prototype.toString.call()返回的數(shù)據(jù)格式為?[object Object]類(lèi)型,然后用slice截取第8位到倒一位,得到結(jié)果為?Object
var?_toString?=?Object.prototype.toString;
function?toRawType?(value)?{
??return?_toString.call(value).slice(8,?-1)
}
運(yùn)行結(jié)果測(cè)試
toRawType({})?//??Object?
toRawType([])??//?Array????
toRawType(true)?//?Boolean
toRawType(undefined)?//?Undefined
toRawType(null)?//?Null
toRawType(function(){})?//?Function
2. 利用閉包構(gòu)造map緩存數(shù)據(jù)
vue中判斷我們寫(xiě)的組件名是不是html內(nèi)置標(biāo)簽的時(shí)候,如果用數(shù)組類(lèi)遍歷那么將要循環(huán)很多次獲取結(jié)果,如果把數(shù)組轉(zhuǎn)為對(duì)象,把標(biāo)簽名設(shè)置為對(duì)象的key,那么不用依次遍歷查找,只需要查找一次就能獲取結(jié)果,提高了查找效率。
function?makeMap?(str,?expectsLowerCase)?{
????//?構(gòu)建閉包集合map
????var?map?=?Object.create(null);
????var?list?=?str.split(',');
????for?(var?i?=?0;?i???????map[list[i]]?=?true;
????}
????return?expectsLowerCase
????????function?(val)?{?return?map[val.toLowerCase()];?}
??????:?function?(val)?{?return?map[val];?}
}
//?利用閉包,每次判斷是否是內(nèi)置標(biāo)簽只需調(diào)用isHTMLTag
var?isHTMLTag?=?makeMap('html,body,base,head,link,meta,style,title')
console.log('res',?isHTMLTag('body'))?//?true
3. 二維數(shù)組扁平化
vue中_createElement格式化傳入的children的時(shí)候用到了simpleNormalizeChildren函數(shù),原來(lái)是為了拍平數(shù)組,使二維數(shù)組扁平化,類(lèi)似lodash中的flatten方法。
//?先看lodash中的flatten
_.flatten([1,?[2,?[3,?[4]],?5]])
//?得到結(jié)果為??[1,?2,?[3,?[4]],?5]
//?vue中
function?simpleNormalizeChildren?(children)?{
??for?(var?i?=?0;?i?????if?(Array.isArray(children[i]))?{
??????return?Array.prototype.concat.apply([],?children)
????}
??}
??return?children
}
//?es6中?等價(jià)于
function?simpleNormalizeChildren?(children)?{
???return?[].concat(...children)
}
4. 方法攔截
vue中利用Object.defineProperty收集依賴(lài),從而觸發(fā)更新視圖,但是數(shù)組卻無(wú)法監(jiān)測(cè)到數(shù)據(jù)的變化,但是為什么數(shù)組在使用push?pop等方法的時(shí)候可以觸發(fā)頁(yè)面更新呢,那是因?yàn)関ue內(nèi)部攔截了這些方法。
?//?重寫(xiě)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('調(diào)用的是攔截的?'?+?method?+?'?方法,進(jìn)行依賴(lài)收集');
??????return?Array.prototype[method].apply(this,?arguments);
????}
??});
運(yùn)行結(jié)果測(cè)試
var?arr?=?[1,2,3]
arr.__proto__?=?array_methods?//?改變arr的原型
arr.unshift(6)?//?打印結(jié)果:?調(diào)用的是攔截的?unshift?方法,進(jìn)行依賴(lài)收集
5. 繼承的實(shí)現(xiàn)
vue中調(diào)用Vue.extend實(shí)例化組件,Vue.extend就是VueComponent構(gòu)造函數(shù),而VueComponent利用Object.create繼承Vue,所以在平常開(kāi)發(fā)中Vue?和?Vue.extend區(qū)別不是很大。這邊主要學(xué)習(xí)用es5原生方法實(shí)現(xiàn)繼承的,當(dāng)然了,es6中 class類(lèi)直接用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)
??}
運(yùn)行結(jié)果測(cè)試
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. 執(zhí)行一次
once?方法相對(duì)比較簡(jiǎn)單,直接利用閉包實(shí)現(xiàn)就好了
function?once?(fn)?{
??var?called?=?false;
??return?function?()?{
????if?(!called)?{
??????called?=?true;
??????fn.apply(this,?arguments);
????}
??}
}
7. 淺拷貝
簡(jiǎn)單的深拷貝我們可以用?JSON.stringify()?來(lái)實(shí)現(xiàn),不過(guò)vue源碼中的looseEqual?淺拷貝寫(xiě)的也很有意思,先類(lèi)型判斷再遞歸調(diào)用,總體也不難,學(xué)一下思路。
function?looseEqual?(a,?b)?{
??if?(a?===?b)?{?return?true?}
??var?isObjectA?=?isObject(a);
??var?isObjectB?=?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'
}
就先分享這些函數(shù),其他函數(shù),后面繼續(xù)補(bǔ)充,如有不對(duì)歡迎指正,謝謝!
專(zhuān)注分享當(dāng)下最實(shí)用的前端技術(shù)。關(guān)注前端達(dá)人,與達(dá)人一起學(xué)習(xí)進(jìn)步!
長(zhǎng)按關(guān)注"前端達(dá)人"

