zoukankan      html  css  js  c++  java
  • 侯策《前端开发核心知识进阶》读书笔记——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]

    将多维数组转化为一维:

    let arr = [[0, 1], [2, 3], [4,[5,6,7]]]
    const newArr = function(arr){
       return arr.reduce((pre,cur)=>pre.concat(Array.isArray(cur)?newArr(cur):cur),[])
    }
    console.log(newArr(arr)); //[0, 1, 2, 3, 4, 5, 6, 7]

    对象里的属性求和:

    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

  • 相关阅读:
    终于和Java碰面了
    Servlet学习-MVC开发模式
    Servlet学习-数据库的操作
    Servlet学习-sendRedirect()和forward()
    Servlet学习-request
    Servlet学习-乱码解决方案
    Servlet学习-response
    Servlet学习-页面设置不缓存
    软件构造 第一章第二节 软件开发的质量属性
    软件构造 第二章 第一节 软件生命周期和版本控制
  • 原文地址:https://www.cnblogs.com/fmyao/p/12796277.html
Copyright © 2011-2022 走看看