zoukankan      html  css  js  c++  java
  • call,apply,bind详解

    为什么要改变this指向?

    我们知道bind,call,apply的作用都是用来改变this指向的,那为什么要改变this指向呢?请看下面的例子:

    var name="lucy";
    let obj={
     name:"martin",
     say:function () {
     console.log(this.name);
     }
    };
    obj.say(); //martin,this指向obj对象
    setTimeout(obj.say,0); //lucy,this指向window对象
    

    可以观察到,正常情况下 say 方法中的 this 是指向调用它的 obj 对象的,而定时器 setTimeout 中的 say 方法中的 this 是指向window对象的(在浏览器中),这是因为 say 方法在定时器中是作为回调函数来执行的,因此回到主栈执行时是在全局执行上下文的环境中执行的,但我们需要的是 say 方法中 this 指向obj对象,因此我们需要修改 this 的指向。

    apply方法

    apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变thi指向一次。

    日常用法:改变this指向

    示例:

    回调函数绑定this指向:

    var name="martin";
    var obj={
     name:"lucy",
     say:function(year,place){
     console.log(this.name+" is "+year+" born from "+place);
     }
    };
    var say=obj.say;
    setTimeout(function(){
     say.apply(obj,["1996","China"])
    } ,0); //lucy is 1996 born from China,this改变指向了obj
    say("1996","China") //martin is 1996 born from China,this指向window,说明apply只是临时改变一次this指向
    

    小技巧:改变参数传入方式

    示例:

    求数组中的最大值:

    var arr=[1,10,5,8,3];
    console.log(Math.max.apply(null, arr)); //10
    

    其中Math.max函数的参数是以参数列表,如:Math.max(1,10,5,8,3)的形式传入的,因此我们没法直接把数组当做参数,但是apply方法可以将数组参数转换成列表参数传入,从而直接求数组的最大值。

    call方法

    call方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。

    示例:

    var arr=[1,10,5,8,3];
    console.log(Math.max.call(null,arr[0],arr[1],arr[2],arr[3],arr[4])); //10
    

    采纳以参数列表的形式传入,而apply以参数数组的形式传入。

    bind方法

    bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。

    示例:

    var arr=[1,10,5,8,12];
    var max=Math.max.bind(null,arr[0],arr[1],arr[2],arr[3])
    console.log(max(arr[4])); //12,分两次传参
    

    可以看出,bind方法可以分多次传参,最后函数运行时会把所有参数连接起来一起放入函数运行。

    实现bind方法(面试题):

    简易版

     Function.prototype.bind=function () {
     var _this=this;
     var context=arguments[0];
     var arg=[].slice.call(arguments,1);
     return function(){
     arg=[].concat.apply(arg,arguments);
     _this.apply(context,arg);
     }
     };
    

    完美版

    //实现bind方法
     Function.prototype.bind = function(oThis) {
     if (typeof this !== 'function') {
     // closest thing possible to the ECMAScript 5
     // internal IsCallable function
     throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
     }
     var aArgs = Array.prototype.slice.call(arguments, 1),
     fToBind = this,
     fNOP = function() {},
     fBound = function() {
     // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
     return fToBind.apply(this instanceof fBound
     ? this
     : oThis,
     // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
     aArgs.concat(Array.prototype.slice.call(arguments)));
     };
     // 维护原型关系
     if (this.prototype) {
     // 当执行Function.prototype.bind()时, this为Function.prototype 
     // this.prototype(即Function.prototype.prototype)为undefined
     fNOP.prototype = this.prototype; 
     }
     // 下行的代码使fBound.prototype是fNOP的实例,因此
     // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
     fBound.prototype = new fNOP();
     return fBound;
     };
     var arr=[1,11,5,8,12];
     var max=Math.max.bind(null,arr[0],arr[1],arr[2],arr[3]);
     console.log(max(arr[4])); //12
    

    apply,call,bind三者的区别

    • 三者都可以改变函数的this对象指向。
    • 三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window。
    • 三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入。
    • bind 是返回绑定this之后的函数,便于稍后调用;apply 、call 则是立即执行 。
  • 相关阅读:
    【推荐】com.alibaba方式xml转json,能将xml的所有属性方法,全部转化为json
    关于使用JAXB读取xml文档转换为java对象报错:类的两个属性具有相同名称com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsExc...
    java正则,将<a或者</a,尖括号后面的字母改成大写
    javaben转xml
    三次握手四次挥手
    RPC接口测试(六)RPC协议解析(重要!重要!重要!)
    os.popen(cmd) 与 os.system(cmd) 的区别
    jmeter测试webSocket接口
    Ubuntu20.04 体验和美化
    API可视化管理平台YApi
  • 原文地址:https://www.cnblogs.com/liutianzeng/p/11515230.html
Copyright © 2011-2022 走看看