前端基础 - 手写系列
磨刀不误砍柴工 🕵🕵🕵
# 实现 call、apply、bind
# call
function myCall(ctx = window, ...args) {
ctx = ctx || window;
// 为context 创建一个 Symbol(保证不会重名)属性,将当前函数赋值给这个属性
const fn = Symbol();
ctx[fn] = this;
// 处理参数,传入第一个参数后的其余参数
const res = ctx[fn](...args);
// 调用函数后即删除该Symbol属性
delete ctx[fn];
return res;
}
Function.prototype.call = function (context, ...args) {
var context = context || window;
context.fn = this;
var result = eval('context.fn(...args)');
delete context.fn
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# apply
function myApply(ctx = window, args) {
ctx = ctx || window;
const fn = Symbol();
ctx[fn] = this;
if (Array.isArray(args)) {
result = ctx[fn](...args);
} else {
result = ctx[fn]();
}
delete ctx[fn];
return res;
}
Function.prototype.apply = function (context, args) {
let context = context || window;
context.fn = this;
let result = eval('context.fn(...args)');
delete context.fn
return result;
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# bind
function myBind(ctx, ...args1) {
const _this = this;
return function F(...args2) {
if (this instanceof F) {
// 判断是否为构造函数调用,如果是则使用new调用当前函数
return new _this(...args1, ...args2)
} else {
// 如果不是,使用apply,将context和处理好的参数传入
return _this.apply(ctx, args1.concat(args2))
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 实现发布订阅、观察者模式
观察者模式与订阅发布模式的区别 (opens new window)
一句话总结: 发布-订阅模式是面向调度中心编程的,而观察者模式则是面向目标和观察者编程的。前者用于解耦发布者和订阅者,后者用于耦合目标和观察者
# 发布订阅
class PubSub {
constructor() {
this.subers = [];
}
sub(topic, callback) {
let callbacks = this.subers[topic];
if (!callbacks) {
this.subers[topic] = [callback];
} else {
callbacks.push(callback);
}
}
pub(topic, ...args) {
let callbacks = this.subers[topic];
callbacks.forEach(callback => callback(...args))
}
}
const aEvent = (msg) => {
console.log(msg + 'aaa');
}
const bEvent = (msg) => {
console.log(msg + 'bbb');
}
let pubsub = new PubSub();
pubsub.sub('a', aEvent)
pubsub.sub('b', bEvent)
pubsub.pub('a', 'A订阅者')
pubsub.pub('b', 'B订阅者')
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
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
# 观察者模式
class Sub {
constructor() {
this.obers = []
}
add(ob) {
this.obers.push(ob)
}
notify(...args) {
this.obers.forEach(ob => ob.update(...args))
}
}
class Ob {
update(...args) {
console.log(...args)
}
}
let ob1 = new Ob();
let ob2 = new Ob();
let sub = new Sub();
sub.add(ob1)
sub.add(ob2)
sub.notify('目标发生了变化')
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
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
# debounce
一段时间内只执行最后一次
function debounce(fn, delay=500) {
// timer 写在闭包中,因此防抖也是闭包的一个应用
let timer = null;
return function() {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay)
}
}
// 验证
input1.addEventListener('keyup', debounce(() => {
console.log(input1.value);
}), 600)
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(fn, delay = 100) {
let timer = null
return function() {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
clearTimeout(timer)
timer = null
}, delay)
}
}
div1.addEventListener(('drag', throttle(function (e) {
console.log(e.offsetX, e.offsetY)
})))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
最近更新时间: 2021/05/01 17:06:28
- 01
- 2023/02/08 00:00:00
- 02
- 2023/01/03 00:00:00
- 03
- 2022/12/21 00:00:00