1. 程式人生 > 其它 >vue原始碼中值得學習的方法

vue原始碼中值得學習的方法

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

1. 資料型別判斷

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

var_toString=Object.prototype.toString;
functiontoRawType(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,那麼不用依次遍歷查詢,只需要查詢一次就能獲取結果,提高了查詢效率。

functionmakeMap(str,expectsLowerCase){
//構建閉包集合map
varmap=Object.create(null);
varlist=str.split(',');
for(vari=0;i<list.length;i++){
map[list[i]]=true;
}
returnexpectsLowerCase
?function(val){returnmap[val.toLowerCase()];}
:function(val){returnmap[val];}
}
//利用閉包,每次判斷是否是內建標籤只需呼叫isHTMLTag
varisHTMLTag=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中
functionsimpleNormalizeChildren(children){
for(vari=0;i<children.length;i++){
if(Array.isArray(children[i])){
returnArray.prototype.concat.apply([],children)
}
}
returnchildren
}

//es6中等價於
functionsimpleNormalizeChildren(children){
return[].concat(...children)
}

4. 方法攔截

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

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

執行結果測試

vararr=[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繼承。

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

執行結果測試

varson1=newSon("AAA",23)
son1.getName()//AAA
son1.getAge()//23
son1.arr.push(4)
console.log(son1.arr)//1,2,3,4

varson2=newSon("BBB",24)
son2.getName()//BBB
son2.getAge()//24
console.log(son2.arr)//1,2,3

6. 執行一次

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

functiononce(fn){
varcalled=false;
returnfunction(){
if(!called){
called=true;
fn.apply(this,arguments);
}
}
}

7. 淺拷貝

簡單的深拷貝我們可以用 JSON.stringify() 來實現,不過vue原始碼中的looseEqual 淺拷貝寫的也很有意思,先型別判斷再遞迴呼叫,總體也不難,學一下思路。

functionlooseEqual(a,b){
if(a===b){returntrue}
varisObjectA=isObject(a);
varisObjectB=isObject(b);
if(isObjectA&&isObjectB){
try{
varisArrayA=Array.isArray(a);
varisArrayB=Array.isArray(b);
if(isArrayA&&isArrayB){
returna.length===b.length&&a.every(function(e,i){
returnlooseEqual(e,b[i])
})
}elseif(!isArrayA&&!isArrayB){
varkeysA=Object.keys(a);
varkeysB=Object.keys(b);
returnkeysA.length===keysB.length&&keysA.every(function(key){
returnlooseEqual(a[key],b[key])
})
}else{
/*istanbulignorenext*/
returnfalse
}
}catch(e){
/*istanbulignorenext*/
returnfalse
}
}elseif(!isObjectA&&!isObjectB){
returnString(a)===String(b)
}else{
returnfalse
}
}
functionisObject(obj){
returnobj!==null&&typeofobj==='object'
}