最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 03.我们不背诵API,只实现API。

    正文概述 掘金(tang丶有年)   2020-12-27   334

    为什么要实现API?

    • 一直是面试要考察的点
    • 加深对API的记忆和理解
    • 更能体现编程思维和能力

    jQery offset 方法实现

    使然 jQery 已经是"上古"神器了,但很多API都值得学习,也还是很经典。

    问:如何获取文档任意元素与文档顶部的距离? 思路:

    • 通过递归实现
    • 通过getBoundingClienetReact

    1.通过递归实现方案

    我们可以通过便利目标元素、目标元素父节点,父节点的父节点。。。依次溯源,并累加这些遍历过的节点相对于其最近祖先节点(其position属性是非static的)的偏移量,向上溯源直到document,即可得到累加结果

    实现如下:

    const offset = (ele) => {
      let result = {
        top: 0,
        left: 0,
      }
      const getOffset = (node, init) => {
        if (node.nodeType !== 1) {
          return
        }
        position = window.getComputedStyle(node)['position']
        if (typeof init === 'undefined' && position === 'static') {
          getOffset(node.parentNode)
          return
        }
        result.top = node.offsetTop + result.top - node.scrollTop
        result.left = node.offsetLeft + result.left - node.scrollLeft
        if (position === 'fixed') {
          return
        }
        getOffset(node.parentNode)
      }
      if (window.getComputedStyle(ele)['display'] === 'none') {
        return result
      }
      let position
      getOffset(ele, true)
      return result
    }
    

    上述代码不难理解,使用递归实现。

    这里只是粗略的启发示例,对于边界情况没有一一处理。但是却考察了递归的初级应用。

    2.通过getBoundingClientRect方法

    • 对于某一节点getBoundingClientRect方法,返回值是一个DOMRect类型的对象。这个对象表示一个矩形盒子,其中包含由left,top,right和bottom等只读属性。

    实现如下:

    const offset = (ele) => {
      let result = {
        top: 0,
        left: 0,
      }
      if (!ele.getClientRects().length) {
        return result
      }
      if (window.getComputedStyle(ele)['display'] === 'none') {
        return result
      }
      result = ele.getBoundingClientRect()
      var docElement = ele.ownerDocument.documentElement
      return {
        top: result.top + window.pageYOffset - docElement.clientTop,
        left: result.left + window.pageXOffset - docElement.clientLeft,
      }
    }
    

    数组reduce 方法的实现

    reduce 这个方法很好的体现了"函数式"理念;

    粗略实现如下:

      Array.prototype._reduce = function(fn, initVal) {
        let arr = this
        let base = typeof initVal === 'undefined' ? arr[0] : initVal 
        let starPoint = typeof initVal === 'undefined' ? 1 : 0
        arr.slice(starPoint).forEach(function(val, index){
        	base = fn(base, val, index+starPoint, arr)
        })
        return base
      }
    

    应用场景一:通过reduce实现runPromiseSsquence

    const runPromiseSsquence = (array,value) => array.reduce(
    	(promiseChain, currentFunction) => promiseChain.then(currentFunction),
        Promise.resolve(value)
    )
    

    runPromiseSsquence将会被一个每一项都返回一个Promise的数组调用,并且依次执行数组中的每一个Promise,参考下示例:

    const f1 = () =>
      new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('p1 running')
          resolve(1)
        }, 2000)
      })
    const f2 = () =>
      new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('p2 running')
          resolve(2)
        }, 1000)
      })
    const array = [f1, f2]
    const runPromiseSsquence = (array, value) =>
      array.reduce(
        (promiseChain, currentFunction) => promiseChain.then(currentFunction),
        Promise.resolve(value)
      )
    runPromiseSsquence(array, 'init')
    

    应用场景二:通过reduce实现pipe(柯里化函数)

    const pip = (...functions) => input => functions.reduce(
    	(acc, fn) => fn(acc),
        input
    )
    

    应用场景三:通过Koa only 模块源码认识reduce

    Koa only 模块示例:

    var o = {
    	a: 'a',
        b: 'b',
        c: 'c'
    }
    only(o,['a','b']) // {a: 'a', b: 'b'}
    

    only模块返回一个经过指定筛选属性的新对象,该模块的代码实现如下:

    var only = function (obj, keys) {
      obj = obj || {}
      if ('string' == typeof keys) keys = keys.split(/ +/)
      return keys.reduce(function (ret, key) {
        if (null == obj[key]) return ret
        ret[key] = obj[key]
        return ret
      }, {})
    }
    

    实现compose方法

    compose方法和前面提到的pipe方法一样,主要用于执行一串不定长度的任务(方法)

    let funcs = [func1, func2, func3, func4]
    let composeFunc = compose(...funcs)
    
    

    它跟pipe方法的差别在于调用顺序不体哦那个

    // compose
    func1(func2(func3(func4(args))))
    // pipe
    func4(func3(func2(func1(args))))
    

    实现compose方法的最简单方案式面向过程的:

    const compose = function (...args) {
      let length = args.length
      let count = length - 1
      let result
      return function f1(...args1) {
        result = args[count].apply(this, arg)
        if (count <= 0) {
          count = length - 1
          return result
        }
        count--
        return f1.call(null, result)
      }
    }
    

    利用reduce实现方案:

    const compose = (...args) =>
            args.reverse().reduce(reduceFunc, args.shift())
    

    利用Promise实现方案:

    const compose = (...args) => {
      let init = args.pop()
      return (...arg) => {
        args.reverse().reduce((sequence, func) => {
          sequence.then((result) => func.call(null, result)),
            Promise.resolve(init.apply(null, arg))
        })
      }
    }
    

    其实还可以使用lodash和Redux来实现compose方法的方案。

    函数式概念确实有些抽象,但是一旦顿悟,必然会感受到其中的优雅和简洁。


    起源地下载网 » 03.我们不背诵API,只实现API。

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元