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