Linux使用者、組與shell指令碼
阿新 • • 發佈:2022-04-09
vue原始碼createPatchFunction
export function createPatchFunction (backend) { // ... 一些輔助方法 return function patch (oldVnode, vnode, hydrating, removeOnly) { if (isUndef(vnode)) { if (isDef(oldVnode)) invokeDestroyHook(oldVnode) return } let isInitialPatch = false const insertedVnodeQueue = [] if (isUndef(oldVnode)) { // empty mount (likely as component), create new root element isInitialPatch = true createElm(vnode, insertedVnodeQueue) } else { const isRealElement = isDef(oldVnode.nodeType) if (!isRealElement && sameVnode(oldVnode, vnode)) { // patch existing root node patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly) } else { if (isRealElement) { // mounting to a real element // check if this is server-rendered content and if we can perform // a successful hydration. if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) { oldVnode.removeAttribute(SSR_ATTR) hydrating = true } if (isTrue(hydrating)) { if (hydrate(oldVnode, vnode, insertedVnodeQueue)) { invokeInsertHook(vnode, insertedVnodeQueue, true) return oldVnode } else if (process.env.NODE_ENV !== 'production') { warn( 'The client-side rendered virtual DOM tree is not matching ' + 'server-rendered content. This is likely caused by incorrect ' + 'HTML markup, for example nesting block-level elements inside ' + '<p>, or missing <tbody>. Bailing hydration and performing ' + 'full client-side render.' ) } } // either not server-rendered, or hydration failed. // create an empty node and replace it oldVnode = emptyNodeAt(oldVnode) } // replacing existing element const oldElm = oldVnode.elm const parentElm = nodeOps.parentNode(oldElm) // create new node createElm( vnode, insertedVnodeQueue, // extremely rare edge case: do not insert if old element is in a // leaving transition. Only happens when combining transition + // keep-alive + HOCs. (#4590) oldElm._leaveCb ? null : parentElm, nodeOps.nextSibling(oldElm) ) // update parent placeholder node element, recursively if (isDef(vnode.parent)) { let ancestor = vnode.parent const patchable = isPatchable(vnode) while (ancestor) { for (let i = 0; i < cbs.destroy.length; ++i) { cbs.destroy[i](ancestor) } ancestor.elm = vnode.elm if (patchable) { for (let i = 0; i < cbs.create.length; ++i) { cbs.create[i](emptyNode, ancestor) } // #6513 // invoke insert hooks that may have been merged by create hooks. // e.g. for directives that uses the "inserted" hook. const insert = ancestor.data.hook.insert if (insert.merged) { // start at index 1 to avoid re-invoking component mounted hook for (let i = 1; i < insert.fns.length; i++) { insert.fns[i]() } } } else { registerRef(ancestor) } ancestor = ancestor.parent } } // destroy old node if (isDef(parentElm)) { removeVnodes([oldVnode], 0, 0) } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode) } } } invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch) return vnode.elm } }
上面程式碼中,在函式createPatchFunction
中返回了patch
方法,這種程式設計方式屬於函式柯里化,也屬於高階函式的一種用法
什麼是函式柯里化
柯里化(curring)是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,並且接受餘下的引數且返回結果的新函式的技術。
上面的解釋來自於百度百科,這樣對於理解可能有些抽象,結合下面的程式碼會更好理解
// 普通的add函式 function add (x,y){ return x + y } // curring之後 function curringAdd (x) { return function (y) { return x + y } } add(1,2) // 3 curringAdd(1)(2) // 3
實際上就是把add
函式的x
和y
兩個引數變成了先用一個函式接收x
然後返回一個函式去處理y
引數。現在思路應該就比較清晰了,就是值傳遞給函式一部分引數來呼叫它,讓它返回一個函式去處理剩下的引數。
柯里化有什麼好處
- 引數複用
// 正常正則驗證字串reg.test(txt) // 函式封裝後 function check(reg, txt) { return reg.test(txt) } check(/\d+/g, 'test')// false check(/[a-z]+/g, 'test') // true // curring後 function curringCheck(reg) { return function(txt) { return reg.test(txt) } } var hasNumber = curringCheck(/\d+/g) var hasLetter = curringCheck(/[a-z]+/g) hasNumber('test1') // true hasNumber('testttt') // false hasLetter('234')// false
上面的示例是一個正則的校驗,正常來說直接呼叫check函式就可以了,但是如果我有很多地方都要校驗是否有數字,起始就是需要將第一個引數reg進行復用,這樣別的笛梵幹就能夠直接呼叫hasNumber,hasLetter等函式,讓引數能夠複用,呼叫起來也更方便。
2. 提前確認
var on = function(element, event, handler) {
if (document.addEventListener) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
} else {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
}
}
}
var on = (function() {
if (document.addEventListener) {
return function(element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
};
} else {
return function(element, event, handler) {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
}
};
}
})();
//換一種寫法可能比較好理解一點,上面就是把isSupport這個引數給先確定下來了
var on = function(isSupport, element, event, handler) {
isSupport = isSupport || document.addEventListener;
if (isSupport) {
return element.addEventListener(event, handler, false);
} else {
return element.attachEvent('on' + event, handler);
}
}
我們在做專案的過程中,封裝一些dom操作可以說再常見不過,上面第一種寫法也是比較常見,但是我們看看第二種寫法,它相對一第一種寫法就是自執行然後返回一個新的函式,這樣其實就是提前確定了會走哪一個方法,避免每次都進行判斷。
3. 延遲執行
Function.protorype.bind = function(context) {
const _this = this
const args = Array.prototype.slice.call(arguments,1)
return function(){
return _this.apply(context, args)
}
}
像我們js中經常使用的bind,實現的機制就死Curring
柯里化效能
- 存取arguments物件通常要比存取命名引數要慢一點
- 一些老版本的瀏覽器在arguments.length的實現上是相當慢的
- 使用
fn.apply(...)
和fn.call(...)
通常比直接呼叫fn(...)
稍微慢點 - 建立大量巢狀作用域和閉包函式會帶來花銷,無論是在記憶體上還是速度上
起始大部分應用中,主要的效能瓶頸是在操作DOM節點上,這js的效能損耗基本是可以忽略不計的,所以currying是可以直接放心使用的
經典面試題
實現一個add方法,使計算結果能夠滿足如下預期
add(1)(2)(3) = 6
add(1,2,3)(4) = 10
add(1)(2)(3)(4)(5) = 15
function add(){
const _args = Array.prototype.slice.call(arguments)
const _adder = function () {
_args.push(...arguments)
return _adder
}
_adder.toSting = function(){
return _args.reduce(function(a,b){
return a + b
})
}
return _adder
}