函数方法实现

3/13/2022 programming

# 原生方法实现

# apply

apply改变this指向 fn.apply(context)

Function.prototype.apply = Function.prototype.apply || function(context, ...args) {
    // 判断参数
    if (typeof args === 'undefined' || args === null) args = [];
    if (typeof context === 'undefined' || context === null) context = window;
    // 在context上定义一个属性applyKey接收要执行的函数
    context.applyKey = this;
    // 利用上下文调用方式让this指向context
    const result = context.applyKey(...args);
    // 将多余的属性删除
    delete context.applyKey;
    // 返回运行结果
    return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# bind

bind 函数改变函数中的this指向,返回一个函数,调用返回的函数传入其他参数

Function.prototype.bind = Function.prototype.bind || function(context) {
    const _this = this;
    const args = Array.prototype.slice.call(arguments, 1);
    return function () {
        const finalArgs = args.concat(Array.prototype.slice.call(arguments));
        return _this.apply(context, finalArgs);
    }
}
1
2
3
4
5
6
7
8

# instanceof

instanceof(a,b) 判断a在不在b的原型链上

function instanceFunc(L,R) {
    // 如果是原始类型直接返回false
    if(typeof L !=== 'object') return false;
    while(true) {
        // 如果原型链查找完成直接返回false
        if(L.__proto__ === null) return false;
        // 如果在原型链中找到了,返回true
        if(L.__proto__ === R.prototype) return true;
        // 在原型链继续查找
        L = L.__proto__;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# new

function newFnc (...args) {
    const constructor = args.shift()
    const obj = Object.create(constructor.prototype)
    const result = constructor.apply(obj, args)
    return (typeof result === 'object' && result !== null) ? result : obj
}
1
2
3
4
5
6

# memorize(缓存函数)

将函数返回值进行缓存

const memorize = fn => {
    let cacheMap = {}
    return function(...args) {
        const cacheKey = args.join('_')
        if (cacheKey in cacheMap) {
            return cacheMap[cacheKey]
        } else {
            return cacheMap[cacheKey] = fn.apply(this || {}, args)
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11

# debounce(防抖)

段时间内多次调用合并成一次

/**
 * isImmediate是防抖延迟开始就先调用一次
 */
const debounce = (func, time, isImmediate) => {
    let timer = null
    return function () {
        clearTimeout(timer)
        // 立即调用的第一次进入
        if (isImmediate && timer === null) {
            func.call(this, ...arguments)
            timer = 0
            return;
        }
        timer = setTimeout(() => {
            func.call(this, ...arguments)
            timer = null
        }, time)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# throttle(节流)

短时间内控制以固定频率执行函数

function throttle(handler, delay) {
    let flag = true
    return function () {
        if (flag) {
            handler.call(null,...arguments)
            flag = false
            setTimeout(()=>{
                flag = true
            },delay)
        }
    }
 }

1
2
3
4
5
6
7
8
9
10
11
12
13

# curry(柯里化)

解题思路

  1. 逐步接手参数然后进行存储
  2. 先不进行函数计算,延后执行到参数符合条件统一计算
  3. 会用到递归调用
  4. 如果不用箭头函数,要搞清this的指向
const curry = fn => {
    return tempFn = (...args1) => {
        if (arg1.length >= fn.length) {
            return fn(...arg1)
        } else {
            return (...arg2) => tempFn(...arg1, ...arg2)
        }
    }
}
1
2
3
4
5
6
7
8

# unCurry(反柯里化)

  • 该方法是希望被其他对象所调用方法fn
  • unCurry方法执行后会返回一个新函数,该函数的第一个参数是预期要执行方法的对象obj,后面是参数
function unCurry(fn) {
    return function () {
        var obj = [].shift.call(arguments)
        return fn.apply(obj, arguments)
    }
}
1
2
3
4
5

# partial(偏函数)

将多参数函数转换成具有固定参数的函数 n = n / k * k

const partial = (fn, ...rest) => (...args) => fn(...rest, ...args)

const partial = (fn, ...args) => fn.bind(null, ...args)
1
2
3

# compose

compose(f,g,h)(a) = f(g(h(a)))

const compose = (...fns) => {
    const len = fns.length;
    return function(...args) {
        let index = 0;
        // 先运算出第一个结果,this指向调用的函数
        let result = len ? fns.reverse()[index].apply(this, args) : args[0];
        // 循环运算出其他的结果
        while(++index < len) {
            result = fns[index].call(this,result);
        }
        return result;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# pipe

pipe(f,g,h)(a) = h(g(f(a)))

const pipe = (...fns) => input => fns.reduce((result, val) => val(result), input)
1

# runPromisesInSeries

runPromisesInSeries(promiseArr, value)

const runPromisesInSeries = (promiseArr, value) => promiseArr.reduce((result, promiseFnc) => promiseFnc.then(result), Promise.then(value))
1

# deepClone

function cloneDeep(obj, map = new Map()) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    if (map.has(obj)) {
        return map.get(obj);
    }
    const output = Array.isArray(obj) ? [] : {};
    map.set(obj, output);
    const keys = [...Object.getOwnPropertySymbols(obj), ...Object.keys(obj)];
    for (const key of keys) {
        const val = obj[key];
        output[key] = cloneDeep(val, map);
    }
    return output;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# chunk

返回新的数组,规则如下,按照给定的数值进行数组拆分。

chunk(['a', 'b', 'c', 'd'], 2)   // => [['a', 'b'], ['c', 'd']]
chunk(['a', 'b', 'c', 'd'], 3)   // => [['a', 'b', 'c'], ['d']]
chunk(['a', 'b', 'c', 'd'], 5)   // => [['a', 'b', 'c', 'd']]
chunk(['a', 'b', 'c', 'd'], 0)   // => []
1
2
3
4
function chunk(arr, num) {
    if (num === 0) return [];
    if (Array.isArray(arr) && typeof num === "number") {
        let result = [];
        let i = 0;
        while (i < arr.length) {
            result.push(arr.slice(i, i + num));
            i += num;
        }
        return result;
    } else {
        console.log("params type error");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 变异题

# 1. 实现 (5).add(3).minus(2) = 5 + 3 - 2

Number.prototype.add = function(num) {
    return this.valueOf() + num
}
Number.prototype.minus = function(num) {
    return this.valueOf() - num
}

(5).add(3).minus(2) // 6
1
2
3
4
5
6
7
8

# 2. 实现一个curry函数,完成下面的输出

给基本函数join,实现一个curry函数,让输出的curriedJoin完成下面的功能

const join = (a, b, c) => {
   return `${a}_${b}_${c}`
}

const curriedJoin = curry(join)

curriedJoin(1, 2, 3) // '1_2_3'

curriedJoin(1)(2, 3) // '1_2_3'

curriedJoin(1, 2)(3) // '1_2_3'
1
2
3
4
5
6
7
8
9
10
11
查看答案
/**
 * @param { (...args: any[]) => any } fn
 * @returns { (...args: any[]) => any }
 */
function curry(fn) {
  let len = fn.length
  return cur = (...args) => {
      if (args.length >= len) {
        return fn.call(this, ...args)
      } else {
        return (...args1) => cur(...args, ...args1)
      }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 3. 实现一个有占位符的curry,完成下面的输出

有一个基本函数join,_是占位符,最后输出的是没有占位符的。

const join = (a, b, c) => {
   return `${a}_${b}_${c}`
}

const curriedJoin = curry(join)
const _ = curry.placeholder

curriedJoin(1, 2, 3) // '1_2_3'

curriedJoin(_, 2)(1, 3) // '1_2_3'

curriedJoin(_, _, _)(1)(_, 3)(2) // '1_2_3'
1
2
3
4
5
6
7
8
9
10
11
12
查看答案
/**
 * @param { (...args: any[]) => any } fn
 * @returns { (...args: any[]) => any }
 */
function curry(fn) {
  let len = fn.length
  return cur = (...args) => {
    // 如果len之前的没有占位符就直接输出
    if (args.length >= len && !args.slice(0, len).includes(curry.placeholder)) {
      return fn.call(this, ...args)
    } else {
      return (...args1) => {
        // 遍历map判断占位符就用新的参数替换
        let newArg = args.map(arg => arg === curry.placeholder && args1.length ? args1.shift() : arg)
        // 新参数在前,旧参数在后
        return cur(...newArg, ...args1)
      }
    }
  }
}

curry.placeholder = Symbol()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 4. 请实现一个 add 函数 TODO

满足以下功能:
add(1);
// 1
add(1)(2);
// 3
add(1)(2)(3);
// 6
add(1)(2, 3);
// 6
add(1, 2)(3);
// 6
add(1, 2, 3);
// 6

更新时间: 2022-04-10 13:23