Why implement an API?

  • That’s always the point of the interview
  • Deepen your memory and understanding of apis
  • More can reflect the programming thinking and ability

JQery offset method implementation

JQery is a relic of the past, but many of the apis are worth learning and still classic.

Q: How do I get the distance between any element of a document and the top of the document? Ideas:

  • We do it recursively
  • Through getBoundingClienetReact

1. Implement the solution through recursion

We can facilitate the target element, the target element’s parent node, the parent node’s parent node… Trace these traversed nodes in turn, and add up the offset relative to their nearest ancestor node (whose position property is non-static), trace up to document, to obtain the summation result

The implementation is as follows:

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 }Copy the code

The above code is not difficult to understand and is implemented recursively.

  • If node.nodeType is of type other than Element(1), the node is jumped.
  • If the position attribute of the related node is static, it is not included in the calculation and enters the recursion of the next node (its parent node).
  • If the display attribute of the related attribute is nonde, 0 should be returned as the result.

This is only a rough heuristic example, not a one-by-one treatment of boundary cases. But we looked at the elementary application of recursion.

2. Use the getBoundingClientRect method

  • For a node getBoundingClientRect method, the return value is an object of type DOMRect. This object represents a rectangular box containing read-only properties such as left,top,right, and bottom.

The implementation is as follows:

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, } }Copy the code
  • Ele.ownerdocument stands for document, including two nodes, documentElement;
  • ClientTop in docelement. clientTop represents the width of the top border of the element, excluding margins and inner margins
  • The getBoundingClientRect method is used for simple geometry, boundary CSS handling, and compatibility handling

Implementation of array Reduce method

Reduce is a good embodiment of the “functional” concept;

The reduce method gives an execution function and accumulates each value of the array from left to right to produce a result

Rough implementation is as follows:

  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
  }
Copy the code

Application Scenario 1: Use Reduce to implement runPromiseSsquence

const runPromiseSsquence = (array,value) => array.reduce(
	(promiseChain, currentFunction) => promiseChain.then(currentFunction),
    Promise.resolve(value)
)
Copy the code

RunPromiseSsquence will be called by an array that returns a Promise for each item and executes each Promise in turn, as shown in the following example:

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')
Copy the code

Application Scenario 2: Implementing PIPE (Currization Function) using Reduce

const pip = (... functions) => input => functions.reduce( (acc, fn) => fn(acc), input )Copy the code

Application scenario 3: Learn about Reduce through the source code of the Koa Only module

Example of Koa Only module:

var o = {
	a: 'a',
    b: 'b',
    c: 'c'
}
only(o,['a','b']) // {a: 'a', b: 'b'}
Copy the code

The only module returns a new object with the specified filter attribute. The code for this module is as follows:

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
  }, {})
}
Copy the code

Implementing the Compose method

The compose method, like the pipe method mentioned earlier, is primarily used to perform a list of tasks of an indefinite length (methods)

let funcs = [func1, func2, func3, func4] let composeFunc = compose(... funcs)Copy the code
  • Perform composeFunc (args)
  • Equivalent to the implementation func1(func2(func3(func4(args))))

It differs from the PIPE method in that it is called in a different order

// compose
func1(func2(func3(func4(args))))
// pipe
func4(func3(func2(func1(args))))
Copy the code

The simplest solution for implementing the compose method is process-oriented:

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) } }Copy the code

Reduce implementation scheme:

const compose = (... args) => args.reverse().reduce(reduceFunc, args.shift())Copy the code

Use Promise to implement the scheme:

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)) }) } }Copy the code

You can also use Lodash and Redux to implement the Compose method solution.

The functional concept is a bit abstract, but once you understand it, you can’t help but feel its elegance and simplicity.

This article is from: Advance of Core Knowledge of Front-end Development: From Laying a Solid Foundation to Breaking the Bottleneck