JS手写API实现

tech2022-10-04  102

API实现

jQuery offset 实现递归实现通过 getBoundingClientRect API 实现 数组 reduce 方法的相关实现概念reduce的用法简单用法:计算数组中每个元素出现的次数:数组去重:将二维数组转化为一维:将多维数组转化为一维: reduce 实现 runPromiseInSequencereduce 实现 pipe实现一个reduce compose 实现的几种方案compose 概念面向过程的实现方式reduce的实现方式Promise的实现方式 本文总结于: 侯策《前端开发核心知识进阶》

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)