收集一些常见的手写题(上)

防抖

触发高频事件n秒内函数只执行一次,如果n秒内高频事件再次被触发,则重新计算时间

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
/**
* @desc 函数防抖
* @param {Function} func 函数
* @param {Number} wait 延迟执行毫秒数
* @param {Boolean} immediate true 表示立即执行,false 表示非立即执行
*/
function debounce(fn, daley, immediate){
let timer
return function () {
if (timer) clearTimeout(timer)
if (immediate) {
let run = !timer
timer = setTimeout(() => {
timer = null
// fn.apply(this, arguments) 立即执行完输入完再执行一次
}, daley)
if (run) {
fn.apply(this, arguments)
}
} else {
timer = setTimeout(() => {
fn.apply(this, arguments)
})
}
}
}


let box = document.getElementById('box')

function say(){
console.log('hi debounce')
}
box.addEventListener('input',debounce(say,1000,true))

节流

高频事件触发,但在n秒只会执行一次,节流会稀释函数的执行频率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function throttle(fn,daley){
let canRun = true
return function(){
if(!canRun) return
canRun = false
setTimeout(()=>{
fn.apply(this,arguments)
canRun = true
},daley)
}
}


let box = document.getElementById('box')

function say() {
console.log('hi throttle')
}
box.addEventListener('input', throttle(say, 1000))

深拷贝

  • 深拷贝:修改新变量的值不会影响原有变量的值。默认情况下基本数据类型(number,string,null,undefined,boolean)都是深拷贝。

  • 浅拷贝:修改新变量的值会影响原有的变量的值。默认情况下引用类型(object)都是浅拷贝。

ES6中的结构赋值Object.assign都是浅拷贝(一层的话看不出来)

1
2
3
4
5
6
7
8
9
10
11
function deepClone(obj){
if(obj == null) return obj; // == 有个默认的进制转化 null == undefined -> true
if(obj instanceof Date) return new Date(obj);
if(obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !='object') return obj; // 字符串
let newObj = new obj.constructor;
for(let key in obj){
newObj[key] = deepClone(obj[key]);
}
return newObj
}

不能解析new Date(), reg: /reg/, fn: function () {} 需要单独处理

发布订阅模式

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
let event = {
list: {},
on(key, fn) {
if (!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(fn);
},
emit() {
let key = [].shift.call(arguments),
fns = this.list[key];

if (!fns || fns.length === 0) {
return false;
}
fns.forEach(fn => {
fn.apply(this, arguments);
});
},
remove(key, fn) {
// 这回我们加入了取消订阅的方法
let fns = this.list[key];
// 如果缓存列表中没有函数,返回false
if (!fns) return false;
// 如果没有传对应函数的话
// 就会将key值对应缓存列表中的函数都清空掉
if (!fn) {
fns && (fns.length = 0);
} else {
// 遍历缓存列表,看看传入的fn与哪个函数相同
// 如果相同就直接从缓存列表中删掉即可
fns.forEach((cb, i) => {
console.log(cb,fn,cb === fn)
if (cb === fn) {
fns.splice(i, 1);
}
});
}
}
};

function cat() {
console.log('一起喵喵喵');
}
function dog() {
console.log('一起旺旺旺');
}

event.on('pet', data => {
console.log('接收数据');
console.log(data);
});
event.on('pet', cat);
event.on('pet', dog);
// 取消dog方法的订阅
event.remove('pet', dog);
// 发布
event.emit('pet', ['二哈', '波斯猫']);
/*
接收数据
[ '二哈', '波斯猫' ]
一起喵喵喵
*/