• 函数柯里化


    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函数的xy两个参数变成了先用一个函数接收x然后返回一个函数去处理y参数。现在思路应该就比较清晰了,就是值传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

    柯里化有什么好处

    1. 参数复用
    // 正常正则验证字符串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
    }
    
  • 相关阅读:
    python学习笔记4核心类型字典
    python学习笔记5核心类型元组和文件及其他
    python学习笔记11异常总结
    python学习笔记14类总结
    python学习笔记17常用函数总结整理
    python学习笔记1核心类型数字
    python学习笔记3核心类型列表
    python学习笔记16各种模块和开放工具收集整理
    源码、反码、补码
    素数
  • 原文地址:https://www.cnblogs.com/dehenliu/p/16137731.html
Copyright © 2020-2023  润新知