面试官:你来说说apply、call、bind之间的区别以及节流防抖

87 阅读4分钟

一、理解 apply、call、bind之间的区别(都是函数的方法)

说在前面

var name = '张三'
var age = 20
var obj = {
    Name:this.name,
    age:12,
    myFn:function(){
		console.log(this.name+'年龄'+this.age)
	}
}
var obj1 = {
    name:'李四',
    age:45
}
obj.myfn() //undefined 年龄 12

-- 关于this指向 :
		1、谁调用这个函数,this指向谁,this.age, obj调用 myFn ,所以this指向obj这个对象
		2this.name 中由于是全局调用name,所以this指向window

说了这么多,那么他们的作用的是什么呢????

  • apply()、call()、bind()三者都是改变this指向
  • apply和call会调用函数,bind不会调用函数
  • apply和bind传参一样,call传参的第二个参数需要是数组
obj.myFn.apply(obj1) //李四 年龄 45
obj.myFn.call(obj1) //李四 年龄 45
obj.myFn.bind(obj1)() //李四 年龄 45 //bind返回的事一个新的函数,必须调用他才会执行

如何理解 ? 可以这么理解,person有个方法fullName调用this.firstName和this.lastName这两个变量,this指向person这个对象,但它是没有这两个变量的。 apply可以改变调用apply方法的函数(这里指的就是person.fullName这个函数)的this指向,现在person.fullName.apply(person1)就让this指向person1了,fullName方法就可以访问到这两个值,就成功输出。

1.1、apply()、call()、bind()传参的区别
var name = '张三'
var age = 20
var obj = {
    Name:this.name,
    age:12,
    myFun:function(f,to){
		console.log(this.name+'年龄'+this.age+'来自'+f+'去往'+to)
	}
}
var db = {
    name:'李四',
    age:45
}
obj.myFun('北京','上海') //undefined年龄12来自北京去往上海
obj.myFun.call(db,'成都','上海');    // 李四年龄45来自成都去往上海
obj.myFun.apply(db,['成都','上海']);      // 李四年龄45来自成都去往上海
obj.myFun.bind(db,'成都','上海')();       // 李四年龄45来自成都去往上海
obj.myFun.bind(db,['成都','上海'])();   // 李四年龄45来自成都,上海去往undefined

从上面四个结果不难看出:

1、 call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象
2、call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔
3、apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,[‘成都’, …, ‘string’ ])。
4、bind 除了返回是函数以外,它 的参数和 call 一样。
5、三者的参数不限定是 string 类型,允许是各种类型,包括函数 、 object 等等!
1.2、应用
利用apply()求最大值
var arr =[2,6,8,3,4,9,7,23,56,889]; 
console.log(Math.max.apply(arr,arr)) //第一个arr表示让arr借用max这个方法,第二个arr表示传给max的数据
//apply()所执行的操作:1.执行Math.max(1,2,3,5,4) 2.把内部的this改成arr

Math.max.apply(arr,arr)//889 apply 方法 求最大值
Math.max(arr)//NAN 这种方法不对
Math.max(...arr)//889  扩展运算符求最大值

二、防抖和节流

  1. 防抖:所谓防抖就是限制用户在一定时间内只能点击一次,执行最后一次
  2. 节流:所谓防抖就是限制用户在一定时间内只能点击一次,执行第一次

2.1、防抖

场景:在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。这些需求都可以通过函数防抖动来实现。尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导致页面卡顿,不如将多次计算合并为一次计算,只在一个精确点做操作。 一般的防抖会有immediate选项,表示是否立即调用。

methods:{
    //实现一个防抖函数的封装
    debounce(fn,delay){
        let timer = null;
        let that = this;
        return function(){
			if(timer!=null){
                clearInertval(timer)
			}
			timer = setTimeout(()=>{
                fn.apply(that)
			},dalay)
		}
	}
}

全部代码(demo)

<!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>
    <input type="text">
    <script>
        var input = document.querySelector('input');//获取元素dom
        input.oninput = debounce(function() {
                console.log(this.value);
        },500)
        //fu是是搜索后要执行的事件
        //delay是延迟的时间
        function debounce(fn,delay){
            let timer = null;
            return function() {
                let that = this;
                if(timer!==null){
                    clearTimeout(timer)
                }
                timer = setTimeout(() => {
                    fn.apply(that)//改变this指向
                }, delay);
            }
        }
    </script>
</body>
</html>

基于vue实现 防抖函数

<template>
    <div>
        <el-input v-model="form.name" @input="search"/>
    </div> 
</template>
<script>
    export default {
		data(){
            form:{
                name:''
			},
            timer:null,
		}
	},
     methods:{
         //防抖逻辑
         debounce(fn,dalay){
             //let timer = null;
             let that = this;
             return function(){
                 if(that.timer!==null){
                     clearTimeout(that.timer)
				}
                 that.timer = setTimeout(()=>{
                     fn()
				},dalay)
			}
		},
         //实现搜索的逻辑,以及请求ajax
        searchInfo(){
            console.log(this.form.name)
		},
        //用户输入时触发
		search(){
            //执行防抖逻辑 的函数  注意:结尾加()来执行函数
            this.debounce(this.searchInfo,500)()
		}
	}
</script>
2.2、节流

防抖和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。

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

思路:每次触发事件时都判断当前是否有等待执行的延时函数。

methods:{
	throttle(fn,delay){
		let flag = true
        return function(){
            if(flag){
            setTimeout(()=>{
				fn.call(this)
                flag = true
           		},delay)
                flag = false
			}
        }
    }
}

全部代码(demo)

<!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>
    <div style="height:10000px"></div>
    <script>
        window.onscroll = throttle(()=>{
            console.log(1);
        },1000)

        function throttle(fn,delay){
            let flag = true
            return function(){
                if(flag){
                    setTimeout(()=>{
                        fn.call(this)
                        flag = true
                    },delay)
                }
                flag = false
            }
        }
    </script>
</body>
</html>