为什么要实现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方法的方案。
函数式概念确实有些抽象,但是一旦顿悟,必然会感受到其中的优雅和简洁。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!