1.什么是闭包
闭包概念:函数内部定义的函数,被返回了出去并在外部调用
形成条件;函数的返回值是函数
优点:实现封装,避免全局变量污染 在适当的时候,可以在内存中维护变量并缓存,提高执行效率。
缺点:函数的变量都保存在内存中,会有内存泄漏问题。在退出函数之前,将不使用的局部变量全部删除。
案例:
let bob=(function(){
let a=10;
let b=20;
function add() {
return a+b
}
function sub() {
return a-b
}
return {
add,
sub
}
})()
let resultAdd=bob.add();
let resultSub=bob.sub();
console.log(resultAdd); //执行结果为30
console.log(resultSub); //执行结果为-10
经典面试题:
var data=[];
for(var i=0;i<3;i++){
data[i]=function(){
console.log(i);
};
}
data[0]();
data[1]();
data[2]();
//输出
3
3
3
解释:这里的 i 是全局下的 i,共用一个作用域,当函数被执行的时候这时的 i=3,导致输出的结构都是3。
解决办法:1.利用闭包形成的互不干扰的私有作用域
var data=[];
for(var i=0;i<3;i++){
(
function(j){
data[j]=function(){
console.log(j);
}
}
)(i)
}
data[0]();
data[1]();
data[2]();
//输出
0
1
2
解决办法:2.利用let,具有块级作用域,形成的私有作用域不干扰
var data = [];
for (let i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}
data[0]();
data[1]();
data[2]()
//输出
0
1
2
2. call,apply,bind
2.1 call,apply,bind的用法
let dog={
name:'旺财',
sayName(){
console.log('我是',this.name);
},
eat(food,bone){
console.log('我喜欢吃 ',food,bone);
}
}
let cat={
name:'喵喵'
}
dog.sayName.call(cat); //输出: 我是 喵喵
dog.eat.call(cat,'猫粮','巧克力'); //输出: 我喜欢吃 猫粮 巧克力
//apply,只是传递参数不一样,[arg1,arg2]
dog.eat.apply(cat,['猫粮','巧克力']); //输出: 我喜欢吃 猫粮 巧克力
//bind会返回一个函数,单独调用
let fun=dog.eat.bind(cat,'猫粮','巧克力');
fun(); //输出:我喜欢吃 猫粮 巧克力
总结:相似处:都可以改变this指向,使用其他函数的方法
不同处:apply的参数是一个数组,bind有返回值
应用:子类可以继承父类的方法
function Animal(){
this.eat=function(){
console.log('eat something');
}
};
function Cat(){
//this 指向new出来的cat
Animal.call(this);
}
let cat=new Cat();
cat.eat(); //输出 eat something
3. 防抖与节流
3.1 防抖:一定时间内,频繁触发事件,只执行最后一次(在规定时间内每次触发都要重新计时)
案例:
<!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>Document</title>
</head>
<body style="height: 1000px;">
<input type="text">
</body>
<script>
let input=document.querySelector("input");
input.oninput=function(){
consloe.log(this.value);
</script>
</html>
如果此时输入字符串会出现什么情况?
它会把每次输入的字符都会发送到后端服务器,造成资源消耗,而我们每次要的只是最后一次的输入值
防抖案例:
<!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>Document</title>
</head>
<body style="height: 1000px;">
<input type="text">
</body>
<script>
let input=document.querySelector("input");
input.oninput=debounce(
function () {
console.log(this.value);
},800);
//封装成一个防抖函数,方便调用
/**
* fn 要执行的业务代码
* delay 延时时间,这段时间内就只能执行最后一次的触发事件
*/
function debounce(fn,delay){
//当第一次触发,则生成一个t
//在delay延时时间内再次触发,移除上一个t,并生成新的t
let t=null;
return function(){
if(t!==null){
clearTimeout(t);
}
t=setTimeout(() => {
//因为function是被input调用,因此用call可以改变this指向
//fn的原本指向是window全局对象
fn.call(this);
}, delay);
}
}
</script>
</html>
可以看出,通过防抖函数,可以实现与后台服务器的交互大大降低
3.2 节流:一定时间内,频繁触发事件,只执行第一次事件(在规定时间内只执行一次)
案例:
<!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>Document</title>
</head>
<body style="height: 1000px;">
<input type="text">
</body>
<script>
window.onscroll=function(){
console.log('scroll');
}
</script>
</html>
可以看到,当我们滑动的时候,函数是一直触发的,我们不需要这么高频率的触发。
节流案例:
<!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>Document</title>
</head>
<body style="height: 1000px;">
<input type="text">
</body>
<script>
window.onscroll=throttle(function () {
console.log('hello word');
},800)
/**
* fn 要执行的业务代码
* delay 延时时间,这段时间内就只能执行最后一次的触发事件
*/
function throttle(fn,delay) {
let flag=true;
return function(){
if(flag){
setTimeout(() => {
//改变this指向,指向引用的对象
fn.call(this);
//执行完这次,下一个delay时间内,继续设置定时器
flag=true;
}, delay);
}
//只要在delay时间内再次触发,就不能设置定时器
flag=false;
}
}
</script>
</html>
可以看到,在持续触发事件的过程中,函数不会立即执行,并且每 0.8s 执行一次,在停止触发事件后,
函数还会再执行一次。