简单手写实现
新 2019-09-01
code
interview
# 一、防抖节流
# 1.防抖
将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。
function debounce(fn, wait, immediate) {
let timer = null;
return function(...args) {
const context = this;
if (immediate && !timer) {
fn.apply(context, args);
timer = 1;
} else {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, wait);
}
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 2.节流
每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms执行一次即可。
function throttle(fn, wait, immediate) {
let timer = null;
let callnow = immediate;
return function(...args) {
const context = this;
if (callnow) {
fn.apply(context, args);
callnow = false;
}
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args);
timer = null;
}, wait);
}
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 二、call-apply-bind
# 1.call
Function.prototype.myCall = function(context) {
const args = [...arguments].slice(1);
context.fn = this;
let result = context.fn(...args);
delete context.fn;
return result;
};
1
2
3
4
5
6
7
2
3
4
5
6
7
# 2.apply
Function.prototype.myApply = function(context) {
context.fn = this;
let result = null;
if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
delete context.fn;
return result;
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 3.bind
Function.prototype.myBind = function(context) {
const _this = this;
const args = [...arguments].slice(1);
return function() {
_this.apply(context, args.concat(...arguments));
};
};
1
2
3
4
5
6
7
2
3
4
5
6
7
# 三、Promise 相关
# 1.Promise
const PENDING = "PENDING";
const RESOLVED = "RESOLVED";
const REJECTED = "REJECTED";
function MyPromise(fn) {
const _this = this;
_this.state = PENDING;
_this.value = null;
_this.resCbs = [];
_this.rejCbs = [];
function resolve(value) {
_this.state = RESOLVED;
_this.value = value;
_this.resCbs.forEach(cb => cb(value));
}
function reject(value) {
_this.state = REJECTED;
_this.value = value;
_this.rejCbs.forEach(cb => cb(value));
}
try {
fn(resolve, reject);
} catch (error) {
reject(error);
}
}
MyPromise.prototype.then = function(onRes, onRej) {
const _this = this;
onRes = typeof onRes === "function" ? onRes : v => v;
onRej =
typeof onRej === "function"
? onRej
: v => {
throw v;
};
if (_this.state == PENDING) {
_this.resCbs.push(onRes);
_this.rejCbs.push(onRej);
}
if (_this.state == RESOLVED) {
onRes(_this.value);
}
if (_this.state == REJECTED) {
onRej(_this.value);
}
};
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
37
38
39
40
41
42
43
44
45
46
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
37
38
39
40
41
42
43
44
45
46
# 2.Promise.all
Promise.all = function(ps) {
const len = ps.length;
return new Promise((res, rej) => {
if (len == 0) {
res([]);
} else {
let count = 0;
let resList = [];
for (let i = 0; i < len; i++) {
const p = ps[i];
Promise.resolve(p).then(
data => {
resList.push(data);
if (++count == len) {
res(resList);
}
},
err => rej(err)
);
}
}
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 3.Promise.prototype.finally
Promise.prototype.finally = function(cb) {
const P = this.constructor;
return this.then(
value => P.resolve(cb()).then(() => value),
reason =>
P.resolve(cb()).then(() => {
throw reason;
})
);
};
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 4.模拟超时
Promise.race([
axios.get(url), //调用后端接口
new Promise(function(resolve, reject) {
//5秒后执行,如果后端接口没有返回,则直接返回error
setTimeout(() => {
reject(new Error("request timeout")), 5000;
});
}).then(response => {
//成功返回后处理的逻辑
})
]);
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 5.all 中异常捕获
const a = new Promise(res => {
setTimeout(() => {
console.log("a");
res("a");
}, 1500);
});
const b = new Promise(res => {
setTimeout(() => {
console.log("b");
res("b");
}, 1000);
});
const c = new Promise((res, rej) => {
setTimeout(() => {
console.log("c");
rej("c-----");
}, 500);
});
// .catch(e => e);
let ps = [a, b, c];
Promise.all(
ps.map(p => {
p = p.then ? p : Promise.resolve(p);
return p.catch(err => err);
})
).then(d => {
console.log(d);
});
// 1. 单个 promise 中添加 catch
// 2. 或者 promise 数组批量添加 catch
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
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
# 四、观察者和发布订阅
观察者模式中主体和观察者是互相感知的,发布-订阅模式是借助第三方来实现调度的,发布者和订阅者之间互不感知
# 1.观察者
没有中间商赚差价
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。
class Subject {
constructor() {
this.observers = [];
}
add(observer) {
this.observers.push(observer);
}
notify(...args) {
this.observers.forEach(observer => observer.update(...args));
}
}
class Observer {
update(...args) {
console.log(...args);
}
}
let ob1 = new Observer();
let ob2 = new Observer();
let sub = new Subject();
sub.add(ob1);
sub.add(ob2);
sub.notify(1, 2, 3);
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
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
# 2.发布订阅
中间商赚差价
发布订阅模式相比观察者模式多了个事件通道,事件通道作为调度中心,管理事件的订阅和发布工作,彻底隔绝了订阅者和发布者的依赖关系。即订阅者在订阅事件的时候,只关注事件本身,而不关心谁会发布这个事件;发布者在发布事件的时候,只关注事件本身,而不关心谁订阅了这个事件。
// - 创建一个对象(缓存列表)
// - on方法用来把回调函数fn都加到缓存列表中
// - emit方法取到arguments里第一个当做key,根据key值去执行对应缓存列表中的函数
// - remove方法可以根据key值取消订阅
const event = {
list: {},
on(key, fn) {
if (!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(fn);
},
emit(key, ...args) {
const _this = this;
let fns = _this.list[key];
fns.forEach(fn => fn.apply(_this, args));
},
remove(key, fn) {
let fns = this.list[key];
if (!fn) {
this.list[key] = [];
} else {
fns.forEach((cb, i) => {
if (cb === fn) {
fns.splice(i, 1);
}
});
}
}
};
function cat() {
console.log("一起喵喵喵");
}
function dog() {
console.log("一起旺旺旺");
}
event.on("pet", data => {
console.log("接收数据 data: ", data);
});
event.on("pet", cat);
event.on("pet", dog);
event.remove("pet", dog);
event.emit("pet", ["二哈", "波斯猫"]);
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
37
38
39
40
41
42
43
44
45
46
47
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
37
38
39
40
41
42
43
44
45
46
47
# 五、flatten
# 1.递归
const flatten = arr =>
arr.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);
1
2
2
# 2.转字符串
const flatten = arr => {
return arr
.toString()
.split(",")
.map(item => +item);
};
// 只适于数组的元素都是数字
1
2
3
4
5
6
7
2
3
4
5
6
7
# 六、深拷贝
# 1.cloneDeep
function cloneDeep(target) {
if (typeof target == "object") {
let temp = Array.isArray(target) ? [] : {};
for (const key in target) {
temp[key] = cloneDeep(target[key]);
}
return temp;
} else {
return target;
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 七、new
- 新生成一个对象
- 链接到原型: obj.proto = Con.prototype
- 绑定this: apply
- 返回新对象(如果构造函数有自己 retrun 时,则返回该值)
function create() {
let obj = {};
let Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
let result = Constructor.apply(obj, arguments);
return result instanceof Object ? result : obj;
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 八、instanceof
能在实例的 原型对象链 中找到该构造函数的 prototype
属性所指向的 原型对象,就返回true。
function myInstanceof(left, right) {
let prototype = right.prototype;
left = left.__proto__;
while (true) {
if (left === null || left === undefined) return false;
if (prototype === left) return true;
left = left.__proto__;
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 九、Array.isArray
Array.myIsArray = function(o) {
return Object.prototype.toString.call(Object(o)) === "[object Array]";
};
1
2
3
2
3
阅读量: