apply、call、bind三者的区别—附:防抖、节流函数

apply、call、bind三者的区别

相同点
1.1 三者都能改变函数中的this指向
1.2 第一个参数都是是要改变的this指向的对象
不同点
2.1、第二个参数:apply传的是参数数组;call和bind传的是参数列表,且apply和call是一次性传入的,bind可以分多次传入
2.2、bind是返回绑定this之后的函数,需要调用。apply和call则是立即执行的。
光说总结怎么行呢,上例子:

基础数据:

	let name = = "lucky";
	const obj = {
    
    
	   	name: "martin",
	  	say: function (content = "") {
    
    
	   		console.log(this.name + content,"---",this);
		},
		hobby: function(content1="", content2="") {
    
    
        	console.log(`${
      
      this.name}喜欢${
      
      content1}${
      
      content2? '和':''}`+ content2,"---",this);
      	},
      	play: function(content1="", content2="") {
    
    
        	console.log(`${
      
      this.name}喜欢打${
      
      content1}${
      
      content2? '和':''}`+ content2,"---",this);
      	}
	};
	obj.say(); //martin,this指向obj对象
	

下面例子:三者都能改变函数中的this指向且第一个参数都是是要改变的this指向的对象。

	//这个指向obj, 也是因为是obj调用了这个方法
    obj.say("你好"); //martin你好 this指向obj对象
    
    // this指向window对象, 因为call改变了它的指向
    obj.say.call(null, "你好"); //lucy你好 
    obj.say.apply(null, "你好"); //lucy你好 
    obj.say.bind(null, "你好"); //lucy你好 
    

下面例子:apply传的是参数数组;call和bind传的是参数列表。

	obj.hobby.call(null, "吃","喝")//lucy喜欢吃和喝
    obj.hobby.apply(null, ["吃2","喝2"])//lucy喜欢吃和喝
    obj.hobby.bind(null, "吃3","喝3")lucy喜欢吃和喝

下面例子:call和apply是立即执行,bind是绑定后返回一个函数,需要调用后才会执行。

	obj.play.call(null, "乒乓球3","豆豆");//lucky喜欢打乒乓球3和豆豆
	obj.play.apply(null, ["乒乓球3","豆豆"]);//lucky喜欢打乒乓球3和豆豆

    let play2 = obj.play.bind(null, "羽毛球3","豆豆"); //此不会执行打印,需要作以下调用
    play2();//lucky喜欢打羽毛球3和豆豆
    

下面例子:bind可以多次传参:
1、可以在绑定时传参;
2、也可以在调用时传参;
3、还可以一部分在调用时传,一部分在绑定时传,需要注意的是绑定的参数是不会变的了,调用时传的参只能顺绑定的参后面传入;

	
	// 1、绑定时传参  [注] bind的时候如果绑定参数,调用的时候再传参也不会被改变
    let play3 = obj.play.bind(null, "王者1","豆豆");
    play3();//lucky喜欢打王者1和豆豆
    //下面打印就是因为绑定的时候参数也被绑定了,所以打印的是绑定时的内容 
    play3("王者2","豆豆");//lucky喜欢打王者1和豆豆
	
	// 2、调用时传参
	let play5 = obj.play.bind(null);
	play5("王者2","豆豆2");//lucky喜欢打王者2和豆豆2
	
	/* 
      3、调用的时候传参,已经被绑定的参数不变, 调用时传的参会从绑定的参之后直接顺位下去,如下所示:
      第一个参数已绑定,调用时的第一个参数直接变成第二个参数
    */
	let play4 = obj.play.bind(null,"王者3");
    play4("王者4","豆豆");//lucky喜欢打王者3和王者4 
    

下面是setTimeout相关知识。

	/* 
      setTimeout的第一个参数是将要执行的代码或者函数名
      obj.say,其实是传了一个方法进去,执行方法,此时是在全局执行上下文的环境中执行
      所以它指向window对象
    */
    setTimeout(obj.say, 0); //lucy,this指向window对象

    // 这个指向obj, 是因为是obj调用了这个方法(与下同)
    setTimeout(() => {
    
    
      obj.say();
    })
	//这个指向obj, 也是因为是obj调用了这个方法
    setTimeout(obj.say("你好"), 0); //martin你好 this指向obj对象
    

节流函数和防抖函数

下面是基础代码

<body>
	<div>
		<!-- 节流函数 -->
	    <button onclick="handleThrottle1(num1, num2, num3)">时间差节流函数</button>
	    <button onclick="handleThrottle(obj)">setTimeout节流函数</button>
	
	    <!-- 不用apply 会出现的问题 -->
	    <button onclick="foo.bar()">节流函数改变this指向</button>
	
	    <!-- 防抖函数 -->
	    <button onclick="handleDebounce(num1, num2, num3)">防抖函数</button>
	</div>
</body>

	//javascript 代码
	let num1 = 1;
	    num2 = 2;
	    num3 = 3;
	const obj = {
    
    
	   name: "cjj",
	   age: 18
	}
	

节流函数:让一个函数无法在短时间内连续调用,【在设置时间内只执行一次】防止用户频繁的行为导致程序奔溃,影响体验。具体看下面事例:

   // 方法一 时间差法 节流函数
   function throttle1(fn, wait) {
    
    
     let pre = new Date().getTime();
     return function(...args) {
    
    
       let now = new Date().getTime();
       if(now - pre >= wait) {
    
    
         
         fn.apply(this, args);//将this正确指向
         // fn(...args);
         console.log(this.obj,"----打印obj---")
         pre = new Date().getTime();
        
       }
     }
   }
   //需要节流的函数
   function myFn(arg1, arg2, arg3) {
    
    
     console.log("----打印成功----", arg1, arg2, arg3);
   }
   //触发事件
   const handleThrottle1 = throttle1(myFn,2000);
   
	
	// 方法二 setTimeout方法 节流函数
    function throttle(fn, wait) {
    
    
      let timer = null
      return function(...args) {
    
    
        if(!timer) {
    
    
          // fn(...args);
          fn.apply(this, ...args);//将this正确指向
          timer = setTimeout(()=> {
    
    
            timer = null
          }, wait)
        }
      }
    }
    //需要节流的函数
    function myObjFn(obj) {
    
    
      console.log(obj,"----打印---");
    }
    //触发事件
    const handleThrottle = throttle(myObjFn,2000);
    

防抖函数:事件延迟时间触发。在延迟的时间内,事件再次被触发将会重新计算延迟时间。通俗一点就是:事件只触发一下会延迟,多下就执行最后一次。具体看下面事例:

	//防抖函数
	function debounce(fn, wait) {
    
    
      let timer = null;
      return function (...args) {
    
    
        if(timer) {
    
    
          clearTimeout(timer);
        }
        timer = setTimeout(() => {
    
    
          fn(...args);
          time = null;
        }, wait)
      }
    }
    //需要防抖的函数
	function MF(arg1, arg2, arg3) {
    
    
      console.log("-----防抖----", arg1, arg2, arg3);
    }
    //触发事件
    const handleDebounce = debounce(MF, 1000);

    

在大部分情况下,不使用apply等方法,好像也能实现节流和防抖的效果,那我是不是可以直接调用方法而不改变this指向?其实有些情况,不使用apply等函数,它会出现this指向问题,所以还是有需要改变this指向的。下面我用一个例子说明:

	// 节流函数
	function throttle(fn, wait) {
    
    
      let timer = null
      return function(...args) {
    
    
        if(!timer) {
    
    
          // fn(...args);
          fn.apply(this, ...args);//将this正确指向
          timer = setTimeout(()=> {
    
    
            timer = null
          }, wait)
        }
      }
    }
	// 使用apply的理由
    class Foo {
    
    
      constructor() {
    
    
        this.a = "a";
        this.bar = throttle(this.bar, 500);
      }
      bar() {
    
    
      	/* 
      	  具体可修改上面throttle方法内的fn的注释,二选一即可
          如果节流函数使用 fn(...args) 下面打印的this是 undefined, this.a会报错
          如果节流函数使用 fn.apply(this, ...args); 下面打印的this是 实例化的对象foo, this.a 是 "a"
        */
        console.log(this,"----this----");
        console.log(this.a,"----a----");
      }
    }
    const foo = new Foo();
    

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_46653360/article/details/130305298