自定义工具函数库(一) 函数相关


自定义工具函数库(一) 函数相关

最终仓库:utils: 自定义工具库

之前在哔哩哔哩看的视频的笔记。整理了一下。

1.1 call 函数封装实现

原理:为传入的 obj 添加临时方法,然后去调用这个临时方法,这样子,这个方法的this就会指向调用它的对象了,最后还需要把临时方法删除掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// call函数封装实现
function call(fn, obj, ...args) {
if (obj === undefined || obj === null) {
// 如果call函数的第二个参数undefined(包括不传参)或null时,让obj等于全局对象
obj = globalThis; // 浏览器下globalThis是window,而node环境下则是global
}

// 为obj添加临时方法
obj.temp = fn;

// 调用temp方法,此时方法中的this就是指向obj
let result = obj.temp(...args);

// 删除temp方法
delete obj.temp;

return result;
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function add(a, b) {
console.log(this);
return a + b + this.c;
}

let obj = {
c: 3,
};

// 添加全局属性
window.c = 100;

console.log(call(add, obj, 1, 2)); // 6:1 + 2 + obj.c,此时add函数中的this是obj
console.log(obj); // {c: 3}

console.log(call(add, null, 1, 2)); // 103:1 + 2 + window.c,此时add函数中的this是window

1.2 apply 函数

原理:和 call函数一样,就只是第三个参数是数组,而不是多个参数而已,所以不需要使用扩展运算符 ...

1
2
3
4
5
6
7
8
9
10
11
12
13
// apply函数封装实现
function apply(fn, obj, args) {
if (obj === undefined || obj === null) {
obj = globalThis;
}
obj.temp = fn;

let result = obj.temp(...args);

delete obj.temp;

return result;
}

1.3 bind 函数

需要依赖自定义 call 函数或内置 call 函数

这个函数功能和 call函数一样,所以可以调用内置的 call函数来实现,当然也可以调用自定义版本的。

不同的是,返回是一个函数,而不是立即调用。而且在调用 bind时可以传参,调用返回的函数也可以传参,只是如果传两次参数,则只有第一次的参数会起作用

1
2
3
4
5
6
// bind函数封装实现
function bind(fn, obj, ...args1) {
return function (...args2) {
return fn.call(obj, ...args1, ...args2); // 如果传两次参数,则只有第一次的参数会起作用。如果只传一次,则那一次的参数就会起作用
};
}

测试用

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>函数封装实现</title>
<script src="./bind.js"></script>
</head>

<body>
<script>
function add(a, b) {
console.log(arguments);
return a + b + this.c;
}

let obj = {
c: 3,
};

// 添加全局属性
window.c = 100;

const fn1 = bind(add, obj, 3, 4);
console.log(fn1());

const fn2 = bind(add, obj, 3, 4);
console.log(fn2(5, 6));

const fn3 = bind(add, obj);
console.log(fn3(5, 6));

const fn4 = bind(add, null, 3, 4);
console.log(fn4());

// 内置版本
// const fn1 = add.bind(obj, 3, 4)
// console.log(fn1())

// const fn2 = add.bind(obj, 3, 4)
// console.log(fn2(5, 6))

// const fn3 = add.bind(obj)
// console.log(fn3(5, 6))

// const fn4 = add.bind(null, 3, 4)
// console.log(fn4())
</script>
</body>
</html>

1.4 函数节流和函数防抖

  • 事件频繁触发可能造成问题
    • 一些浏览器事件如 window.onresize window.mousedown等,触发频率高,会造成界面卡顿
    • 向后台发送请求,频繁触发的话,对服务器会造成不必要的麻烦

解决方案:通过函数节流和函数防抖限制事件处理函数的频繁调用


1.4.1 函数节流(throttle)

  • 在函数需要频繁触发时:函数执行一次后,经过设定的间隔后才可以执行第二次。

  • 适合多次时间按时间平均分配触发

场景:

  • resize 事件(窗口调整)
  • scroll 事件(页面滚动)
  • mousemove 事件(拖拽功能)
  • click 事件(疯狂点击点击)

语法: throttle(callback, wait)

功能:创建一个节流函数,在 wait 毫秒内最多执行 callback一次

实例:

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
// 函数节流
function throttle(callback, wait) {
// 定义开始时间
let start = 0;

// 返回结果是一个函数
return function (event) {
// 获取当前时间戳
let now = Date.now();

if (now - start >= wait) {
callback.call(this, event); // 满足条件,执行回调函数

// 修改开始时间
start = now;
}
};
}

// // 之前青训营时,月影老师教的版本:通过定义一个计时器,当计时器到期时,清除之前的计时器,而清除计时器的时候才可以再次调用回调函数
// function throttle(fn, time = 500) {
// let timer;
// return function (...args) {
// if (timer == null) {
// fn.apply(this, args);
// timer = setTimeout(() => {
// timer = null; /* 到期的话,清除之前的计时器 */
// }, time)
// }
// }
// }

测试:

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>函数封装实现</title>
<style>
body {
height: 2000px;
background-color: skyblue;
}
</style>
<script src="./throttle.js"></script>
</head>

<body>
<script>
// window.addEventListener('scroll', () => {
// console.log(this.scrollY) // 直接绑定滚动事件,一滚动,疯狂输出
// })

window.addEventListener(
"scroll",
throttle(function () {
console.log(this.scrollY); // 成功实现节流
}, 500)
);
</script>
</body>
</html>

1.4.2 函数防抖(debounce)

  • 在函数需要频繁触发时:在规定时间内,只让最后一次生效,前面的不生效
  • 适合多个事件一次相应的情况

场景:输入框实时搜索联想(keyup / input)

语法: debounce(callback, wait)

功能:创建一个防抖动函数,该函数会从上一次被触发后,延迟 wait毫秒后调用 callback

如果触发一次,还没过 wait毫秒,再次触发,那么又得重新计时,依此类推,直到延迟 wait毫秒后才调用 callback(即频繁触发时,只让最后一次生效)

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 函数防抖
function debounce(callback, time) {
let timer = null; // 定时器变量

return function (e) {
clearTimeout(timer); // 每一次新的触发都会把前一次的定时器给清除掉,直到没有新的触发且时间经过time毫秒后才调用callback

// 启动计时器
timer = setTimeout(() => {
callback.call(this, e);
}, time);
};
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>函数封装实现</title>
<script src="./debounce.js"></script>
</head>

<body>
<input type="text" />
<script>
const input = document.querySelector("input");

input.onkeydown = debounce((e) => {
console.log(e.keyCode);
}, 1000);
</script>
</body>
</html>

尚硅谷 Web 前端自定义工具函数库视频教程_哔哩哔哩_bilibili


文章作者: 赤蓝紫
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 赤蓝紫 !
评论
  目录