23函数进阶
# 函数进阶
# 一、变量提升给函数带来的问题
//报错,fn不是一个函数,
//原因是var变量提升在全局作用域添加了一个fn为undefined,
//所以undefined无法执行
fn();
var fn = function () {
console.log(123);
};
2
3
4
5
6
7
# 二、修改 this 指针
//把函数fn的指针修改为指向对象obj
function fn(num) {
console.log(this);
console.log(num);
}
let obj = {
name: "小明",
};
fn.call(obj, 123);
2
3
4
5
6
7
8
9
# call、apply、bind
[warning]call\apply\bind有什么区别?{% em type="red" %} 面试题 {% endem %}
1、
call和apply是返回值,bind是返回函数2、
call和bind是选项式参数、apply是数组参数
var obj = {
name:"小明"
}
function fn(a,b,c){
console.log(this)
console.log(a)
console.log(b)
console.log(c)
}
// fn.call(obj,1,2,3)
// fn.apply(obj,[1,2,3])
var f = fn.bind(obj,1,2,3)
f()
//把对象obj里面的fn2函数的指针修改为指向函数fn的this
function fn(){
obj.fn2.call(this)
}
let obj = {
fn2:function(){
console.log(this)
}
}
fn()
//案例
var name = "小明"
var obj = {
name:"小红",
obj2:{
name:"小绿",
fn:function(){
var a = function(){//虽然函数外层是obj2的作用域,但是因为函数默认指向了window,所以这里指的是window
console.log(this.name)//小明
}
a()
}
}
}
obj.obj2.fn()
var name = "小明"
var obj = {
name:"小红",
obj2:{
name:"小绿",
fn:function(){
var a = ()=>{//箭头函数让this绑定在当前作用域下,无法修改this指针
console.log(this.name)//小绿
}
a()
}
}
}
obj.obj2.fn()
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
[!cogs]
%accordion% 模块标题 %accordion%
<script>
var obj = {
name: "娜美"
}
function fn(a, b, c) {
console.log(this);
console.log(a);
console.log(b);
console.log(c);
}
fn.call(obj, 1, 2, 3) //返回值——是选择式参数
// fn.apply(obj,[1,2,3])//返回值——是选择式参数
// var f=fn.bind(obj,1,2,3)//返回函数——是数组参数
// f()
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
%/accordion%
%accordion% 模块标题 %accordion%
<script>
var obj = {
name: "娜美",
fn1: function () {
fn2.call(this) //修改他指针
},
}
function fn2() {
console.log(this);
}
obj.fn1()
</script>
2
3
4
5
6
7
8
9
10
11
12
13
%/accordion%
%accordion% 练习 %accordion%
<script>
var name = "娜美"
var obj = {
name: "妮可罗宾",
obj2: {
name: "大和",
fn: function () {
var a = function () {
// console.log(this);
console.log(this.name);//娜美
}
a()
},
},
}
obj.obj2.fn()
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
%/accordion%
%accordion% 函数tkis %accordion%
<script>
var name = "娜美"
var obj = {
name: "妮可罗宾",
obj2: {
name: "大和",
fn: function () {
var a = () => {
console.log(this.name); //大和
}
a()
},
},
}
obj.obj2.fn()
</script>
<script>
var name = "娜美"
var obj = {
name: "妮可罗宾",
obj2: {
name: "大和",
fn: function () {
var a = () => {
console.log(this.name); //大和
}
a.call() //就该这里
},
},
}
obj.obj2.fn()
</script>
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
%/accordion%
# 三、高阶函数
[info] 高阶函数
就是使函数抽象化,抽象就是隐藏更具体的实现细节,从更高的层次看待我们要解决的问题
// 给所有的数组添加一个自定义迭代方法
let arr = [1, 2, 3, 4, 5];
// arr.forEach(function(item,i){
// console.log(item)
// })
Array.prototype.myForEach = function (f) {
let arr = this;
for (let i = 0; i < arr.length; i++) {
f(arr[i], i);
}
};
arr.myForEach(function (item, i) {
console.log(i);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[!cogs]
%accordion% 高阶函数 %accordion%
[warning] 函数myForEach
给所有的数组都添加一个myForEach的方法
<script>
Array.prototype.myForEach = function (f) {
var arr = this
for (var i = 0; i < arr.length; i++) {
f(arr[i], i)
}
}
var arr = [1, 2, 3, 4, 5]
arr.myForEach(function (item, idx) {//idx索引是从零开始
// console.log(item);
console.log(idx);//0,1,2,3,4
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
%/accordion%
# 四、函数柯里化
[info] 柯里化
函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数。
目的是复用参数。
# 1、创建函数柯里化
function fn(a, b) {
return a + b;
}
fn(1, 2);
fn(1, 3);
//复用参数
function fn(a) {
return function (b) {
console.log(a + b);
};
}
let f = fn(1);
f(2);
f(3);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2、参数递归
//参数固定
function curry(f, args = []) {
//柯里化函数
return function () {
let arr = [...args, ...arguments];
if (arr.length < f.length) {
//如果add(1)(2)传的数量小于argSum的形参数量
return curry.call(this, f, arr); //就递归
} else {
return f.apply(this, arr); //如果数量相等,就返回累加的数组
}
};
}
function argSum(a, b, c, d) {
//累加实际参数,并且提供一个数值给柯里化函数进行比较
let arr = Array.from(arguments); //获取返回来的实际参数
let sum = arr.reduce(function (a, b) {
//累加
return a + b;
});
return sum;
}
let add = curry(argSum);
console.log(add(1)(2)(3)(4)); //10
console.log(add(1, 2)(3, 4)); //10
console.log(add(1, 2)(3)(4)); //10
console.log(add(1)(2)(3, 4)); //10
//参数不固定
function curry(f) {
//柯里化函数
let args = [];
return function cb(...arg) {
if (arg.length) {
args = [...args, ...arg];
return cb;
} else {
return f.apply(this, args);
}
};
}
function addArg(...arr) {
//累加
let sum = arr.reduce(function (a, b) {
return a + b;
});
return sum;
}
let add = curry(addArg);
console.log(add(1)(2)(3)(4)()); //10
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
[!cogs]
%accordion% 函数柯里化🚀 %accordion%
{% em type="red" %} 复用参数 {% endem %}
<script>
function fn(a) {
return function (b) {
return a + b
}
}
var f = fn(1)//固定值
console.log(f(2));//3
console.log(f(3));//4
</script>
2
3
4
5
6
7
8
9
10
%/accordion%
%accordion% 参数固定 %accordion%
<script>
function curry(fn,arr=[]){
return function(){
var arg=[...arguments]
var len=fn.length
var allArr=[...arg,...arr]
if(allArr.length<len){
return curry(fn,allArr)
}else{
return fn(...allArr)
}
}
}
var handler=curry(function(a,b,c,d){
var arr=[...arguments]
return arr.reduce(function(x,y){
return x+y
})
})
console.log(handler(1)(2,3)(4));
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
%/accordion%
%accordion% 参数不固定 %accordion%
<script>
function curry(fn, arg = []) {
return function () {
var allArr = [...arg, ...arguments];
if (arguments.length) {
return curry(fn, allArr);
} else {
return fn(...allArr);
}
};
}
var handler = curry(function () {
var arr = [...arguments];
return arr.reduce(function (x, y) {
return x + y;
});
});
console.log(handler(1)(2)(3)(4));
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
%/accordion%
# 五、节流函数和防抖函数
# 1、节流函数
[info] 节流函数
在某段时间内,不管触发了多少次回调,只认第一次触发,无视后面次数产生的触发,并在计时结束时给予响应。
var div = document.querySelector("div");
function throttle(fn, time) {
//节流函数
var last = 0;
return function () {
var now = new Date().getTime();
if (now - last >= time) {
last = now;
fn.apply(this, arguments);
}
};
}
var clickHandler = throttle(function () {
//具体逻辑
console.log("触发了");
}, 3000);
div.onclick = clickHandler;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[!cogs]
%accordion% 节点函数🚀 %accordion%
{% em type="red" %} 第一次触发,就执行,如再继续触发下去就等待3秒钟才执行。 {% endem %}
<button class="btn">按钮</button>
<script>
var btn = document.querySelector(".btn");
function throttle(fn, delayTime) {
var endTime = 0;
return function () {
var nowTime = +new Date();
if (nowTime - endTime > delayTime) {
fn();
endTime = nowTime;
}
};
}
var handler = throttle(function () {
console.log(123);
}, 3000);
btn.addEventListener("click", handler);
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
%/accordion%
# 2、防抖函数
[info] 防抖函数
在某段时间内,每次触发都会重新计时,直到最后一次触发超过了限定的时间。
var div = document.querySelector("div");
function debounce(fn, time) {
var timer;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(this, arguments);
}, time);
};
}
var clickHandler = debounce(function () {
console.log("触发了");
}, 3000);
div.onclick = clickHandler;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
%accordion% 防抖函数🚀 %accordion%
{% em type="red" %} 第一次触发等待3秒才执行,如频繁触发就从0开始直到你不按为止,待3秒才执行。 {% endem %}
<button class="btn">按钮</button>
<script>
var btn = document.querySelector(".btn")
function deBounce(fn, delayTime) {
var timer
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function () {
fn()
}, delayTime)
}
}
var handler = deBounce(function () {
console.log(123);
}, 3000)
btn.addEventListener("click", handler)
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
%/accordion%
# 3、优化版的防抖函数
[info] 优化版
防抖函数的问题,频繁的延迟会导致用户迟迟得不到响应。优化版的防抖函数本质上还是节流函数。
<button id="div"></button>
var div = document.querySelector("div");
function betterDebounce(fn, time) {
var timer;
var last = 0;
return function () {
var now = +new Date();
if (now - last < time) {
clearTimeout(timer);
timer = setTimeout(function () {
last = now;
fn.apply(this, arguments);
}, time);
} else {
clearTimeout(timer);
last = now;
fn.apply(this, arguments);
}
};
}
var clickHandler = betterDebounce(function () {
console.log("触发了");
}, 3000);
div.onclick = clickHandler;
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
%accordion% 优化版的防抖函数🚀 %accordion%
{% em type="red" %} 第一次触发就执行,如频繁触发就从0开始直到你不按为止,才执行 {% endem %}
<button class="btn">按钮</button>
<script>
var btn = document.querySelector(".btn")
function deBounce(fn, delayTime) {
var timer
var endTime = 0
return function () {
var nowTime = +new Date()
if (nowTime - endTime < delayTime) {
clearTimeout(timer)
endTime = nowTime //清除计时器
timer = setTimeout(function () {
fn()
}, delayTime)
} else {
clearTimeout(timer)
endTime = nowTime //清除计时器
fn()
}
}
}
var handler = deBounce(function () {
console.log(123);
}, 3000)
btn.addEventListener("click", handler)
</script>
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
%/accordion%