函数方法实现
狐七 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
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
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
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
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
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
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
2
3
4
5
6
7
8
9
10
11
12
13
# curry(柯里化)
解题思路
- 逐步接手参数然后进行存储
- 先不进行函数计算,延后执行到参数符合条件统一计算
- 会用到递归调用
- 如果不用箭头函数,要搞清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
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
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
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
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
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
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
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
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
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
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
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
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