0%

手写代码

深克隆

  • 对于数组和对象做不同的处理
  • 为了解决循环引用的问题,缓存已经访问过的引用类型,如果已经存在,使用缓存值
  • Object.keys() 遍历的是对象自身的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function deepCopy(obj, cache = []) {
if (obj === null || typeof obj !== "object") {
return obj;
}
const hit = cache.filter((c) => c.original === obj)[0];
if (hit) {
return hit.copy;
}
const copy = Array.isArray(obj) ? [] : {};
cache.push({
original: obj,
copy,
});
Object.keys(obj).forEach((key) => {
copy[key] = deepCopy(obj[key], cache);
});
return copy;
}

let a = {
a: 10,
};
a.self = a;

let b = deepCopy(a);
console.log(b);

bind

  • 异常处理 如果调用者不是函数,抛出异常
  • 如果是构造调用,则返回构造调用的结果
  • 否则,使用 apply 改变函数绑定的 this
1
2
3
4
5
6
7
8
9
10
11
12
13
Function.prototype.mybind = function (context) {
if (typeof this !== "function") {
throw new Error("error");
}
let _this = this;
let arg = [...arguments].slice(1);
return function F() {
if (this instanceof F) {
return new _this(...arg, ...arguments);
}
return _this.apply(context, arg.concat(...arguments));
};
};

call/apply

  • 异常处理 如果调用者不是函数,抛出异常
  • 在 context 上添加 fn 为当前函数,进行调用得到结果,之后需要删除 context 上的 fn
  • 返回结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Function.prototype.mycall = function (context) {
if (typeof this !== "function") {
throw new TypeError("error");
}
context = context || window;
context.fn = this;
let result;
let args = [...arguments].slice(1);
result = context.fn(...args);
delete context.fn;
return result;
};

Function.prototype.myapply = function (context) {
if (typeof this !== "function") {
throw new TypeError("error");
}
context = context || window;
context.fn = this;
let result;

if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
delete context.fn;
return result;
};

console.log(Array.prototype.concat.mycall([], [1], [2]));
console.log(Array.prototype.concat.myapply([], [[1], [2]]));

防抖函数

  • 引入 immediate ,immediate 为 true,需要在 timer = null 时执行函数,而 immediate 为 false 时,需要在延时回调函数中执行函数,因此还需要在闭包中保存 context 和 传参信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function debounce(fn, wait = 50, immediate = true) {
let timer, context, args;
const later = () =>
setTimeout(() => {
timer = null;
if (!immediate) {
fn.apply(context, args);
context = args = null;
}
}, wait);

return function (...params) {
if (!timer) {
timer = later();
if (immediate) {
fn.apply(this, params);
} else {
context = this;
args = params;
}
} else {
clearTimeout(timer);
timer = later();
}
};
}

const dfn = debounce((arg) => console.log(arg), 500, true);

dfn(1);
dfn(2);
dfn(3);

setTimeout(() => {
dfn("aa");
}, 1000);

节流函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function throttle(fn, gutter) {
let prev = null;

return function (...params) {
let now = Date.now();
if (now - prev > gutter) {
fn.apply(this, params);
prev = now;
}
};
}

const tfn = throttle((arg) => console.log(arg), 5000);

setInterval(() => {
tfn("a");
}, 10);

数组扁平化

  • 简单的递归过程
1
2
3
4
const flattenDeep = (arr) =>
Array.isArray(arr)
? arr.reduce((prev, cur) => [...prev, ...flattenDeep(cur)], [])
: [arr];

实现 new

  • 创建一个对象,其 __proto__ 指向构造函数的原型
  • 调用该函数,函数的 this 绑定上一步创建的对象
  • 只要函数的返回值不为对象,返回之前创建的对象,否则返回函数返回的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function A() {
this.a = 10;
}

function New(func) {
let res = {};
if (func.prototype !== null) {
res.__proto__ = func.prototype;
}
let ret = func.apply(res, [...arguments].slice(1));
if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
return res;
}

console.log(New(A));

instanceOf

  • 沿着原型链循环判断
1
2
3
4
5
6
7
8
9
10
11
12
13
function instanceOf(left, right) {
let proto = left.__proto__;
let prototype = right.prototype;
while (true) {
if (proto === null) {
return false;
}
if (proto === prototype) {
return true;
}
proto = proto.__proto__;
}
}

柯里化

柯里化函数接收到足够参数后,就会执行原函数,那么我们如何去确定何时达到足够的参数呢?

  • 柯里化函数需要记住你已经给过他的参数,如果没给的话,则默认为一个空数组。
  • 接下来每次调用的时候,需要检查参数是否给够,如果够了,则执行 fn,没有的话则返回一个新的 curry 函数,将现有的参数塞给他。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let sum = (a, b, c, d) => a + b + c + d;

let curry = (fn, ...arr) => {
let len = fn.length;
return (...args) => {
const combineArgs = [...arr, ...args];
if (len <= combineArgs.length) {
return fn(...combineArgs);
} else {
return curry(fn, ...combineArgs);
}
};
};

let cfn = curry(sum);

console.log(cfn(1)(2)(3)(4));

eventbus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class EventBus {
all = new Map();
on(type, handler) {
const handlers = this.all.get(type);
const added = handlers && handlers.push(handler);
if (!added) {
this.all.set(type, [handler]);
}
}
off(type, handler) {
const handlers = this.all.get(type);
if (handlers) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
}

emit(type, evt) {
(this.all.get(type) || []).slice().map((handler) => {
handler(evt);
});
}
}