JS手写API实现


本文总结于: 侯策《前端开发核心知识进阶》

jQuery offset 实现

offset() 方法返回或设置匹配元素相对于文档的偏移。

递归实现

const offset = ele => {
   let result = {
          top: 0,
          left: 0
      }
      /*
      * nodeType 属性返回以数字值返回指定节点的节点类型。
      * 如果节点是元素节点,则 nodeType 属性将返回 1。
      * 如果节点是属性节点,则 nodeType 属性将返回 2。
      * 如果节点 node.nodeType 类型不是 Element(1),则跳出;
      * 如果相关节点的 position 属性为 static,则不计入计算,进入下一个节点(其父节点)的递归。
      * 如果相关属性的 display 属性为 none,则应该直接返回 0 作为结果。
      */
      const getOffset = (node) => {
          if (node.nodeType !== 1) {
              return
          }

          position = window.getComputedStyle(node)['position']

          if (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)
      }

      // 当前 DOM 节点的 display === 'none' 时, 直接返回 {top: 0, left: 0}
      if (window.getComputedStyle(ele)['display'] === 'none') {
          return result
      }

      let position

      getOffset(ele)

      return result

  }
  let box = document.getElementById('box2')
  let result = offset(box);
  console.log(result)

通过 getBoundingClientRect API 实现

const offset = ele => {
    let result = {
        top: 0,
        left: 0
    }
    // 当前为 IE11 以下,直接返回 {top: 0, left: 0}
    if (!ele.getClientRects().length) {
        return result
    }

    // 当前 DOM 节点的 display === 'none' 时,直接返回 {top: 0, left: 0}
    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 方法的相关实现

概念

arr.reduce(callback,[initialValue])

callback (执行数组中每个值的函数,包含四个参数)

1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
2、currentValue (数组中当前被处理的元素)
3、index (当前元素在数组中的索引)
4、array (调用 reduce 的数组)

initialValue (作为第一次调用 callback 的第一个参数。)
如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。

reduce的用法

简单用法:

var  arr = [1, 2, 3, 4];
var sum = arr.reduce((x,y)=>x+y)
var mul = arr.reduce((x,y)=>x*y)
console.log( sum ); //求和,10
console.log( mul ); //求乘积,24

计算数组中每个元素出现的次数:

let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

let nameNum = names.reduce((pre,cur)=>{
  if(cur in pre){
    pre[cur]++
  }else{
    pre[cur] = 1 
  }
  return pre
},{})
console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}

数组去重:

let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((pre,cur)=>{
    if(!pre.includes(cur)){
      return pre.concat(cur)
    }else{
      return pre
    }
},[])
console.log(newArr);// [1, 2, 3, 4]

将二维数组转化为一维:

let arr = [[0, 1], [2, 3], [4, 5]]
let newArr = arr.reduce((pre,cur)=>{
    return pre.concat(cur)
},[])
console.log(newArr); // [0, 1, 2, 3, 4, 5]

将多维数组转化为一维:

var result = [
    {
        subject: 'math',
        score: 10
    },
    {
        subject: 'chinese',
        score: 20
    },
    {
        subject: 'english',
        score: 30
    }
];

var sum = result.reduce(function(prev, cur) {
    return cur.score + prev;
}, 0);
console.log(sum) //60

reduce 实现 runPromiseInSequence

const f1 = () => {
    console.log('p1 running')
}

const f2 = () => {
    console.log('p2 running')
}
const array = [f1, f2]

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

runPromiseInSequence(array, 'init')

reduce 实现 pipe

function pipe(src, ...fns){
    return fns.reduce(function(fn1, fn2){
        return fn2(fn1)
    }, src);
}

实现一个reduce

if (!Array.prototype.reduce) {
  Object.defineProperty(Array.prototype, 'reduce', {
    value: function(callback /*, initialValue*/) {
      if (this === null) {
        throw new TypeError( 'Array.prototype.reduce ' + 
          'called on null or undefined' )
      }
      if (typeof callback !== 'function') {
        throw new TypeError( callback +
          ' is not a function')
      }

      var o = Object(this)
      var len = o.length >>> 0

      var k = 0
      var value

      if (arguments.length >= 2) {
        value = arguments[1]
      } else {
        while (k < len && !(k in o)) {
          k++
        }

        if (k >= len) {
          throw new TypeError( 'Reduce of empty array ' +
            'with no initial value' )
        }
        value = o[k++]
      }

      while (k < len) {
        if (k in o) {
          value = callback(value, o[k], k, o)
        }

        k++
      }

      return value
    }
  })
}

其中x>>>0,保证x有意义(为数字类型),且为正整数,在有效的数组范围内(0 ~ 0xFFFFFFFF),且在无意义的情况下缺省值为0
forEach实现:

Array.prototype.reduce = Array.prototype.reduce || function(func, initialValue) {
    var arr = this
    var base = typeof initialValue === 'undefined' ? arr[0] : initialValue
    var startPoint = typeof initialValue === 'undefined' ? 1 : 0
    arr.slice(startPoint)
        .forEach(function(val, index) {
            base = func(base, val, index + startPoint, arr)
        })
    return base
}

compose 实现的几种方案

compose 概念

compose 的参数是函数数组,返回的也是一个函数

  • compose 的参数是任意长度的,所有的参数都是函数,执行方向是自右向左的,因此初始函数一定放到参数的最右面
  • compose 执行后返回的函数可以接收参数,这个参数将作为初始函数的参数,所以初始函数的参数是多元的,初始函数的返回结果将作为下一个函数的参数,以此类推。因此除了初始函数之外,其他函数的接收值是一元的
  • compose 其实和前面提到的 pipe 一样,就是执行一连串不定长度的任务(方法) ,实际上,compose 和 pipe 的差别只在于调用顺序的不同:
// compose
fn1(fn2(fn3(fn4(args))))

// pipe
fn4(fn3(fn2(fn1(args))))

面向过程的实现方式

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

reduce的实现方式

const reduceFunc = (f, g) => (...arg) => g.call(this, f.apply(this, arg))
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)))
}

参考资料:

https://blog.csdn.net/zwkkkk1/article/details/80229923

https://www.jianshu.com/p/e375ba1cfc47

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值