apply, call, bind用法

作者: EdmundChen | 来源:发表于2017-01-17 11:42 被阅读110次

    在JavaScript 中,call、apply 和 bind 是 Function 对象自带的三个方法,这三个方法的主要作用是改变函数调用过程中的 this 指向

    apply(thisArgs[,args[]])

    • 第一个参数指定函数体内this对象的指向
      thisArgs 的取值有以下4种情况:
      • 不传,或者传null,undefined非严格模式下 函数中的 this 指向 window 对象。 严格模式不传undefined 函数中this指向undefined传入null函数中this指向null
      • 传递另一个函数的函数名,函数中的 this 指向这个函数的引用
      • 传递字符串、数值或布尔类型等基础类型,非严格模式函数中的 this 指向其对应的包装对象,如 StringNumberBoolean严格模式,指向传入的值
      • 传递一个对象,函数中的 this 指向这个对象
    • 第二个参数为一个带下标的集合,可以是数组或类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数
    var func = function(a, b, c) {
      console.log([a, b, c]); // [1,2,3]
    }
    func.apply(null, [1,2,3])
    

    apply 和 call 的唯一区别是第二个参数的传递方式不同,apply 的第二个参数必须是一个数组,而 call 允许传递一个参数列表。值得你注意的是,虽然 apply 接收的是一个参数数组,但在传递给调用函数时,却是以参数列表的形式传递,我们看个简单的例子:

    function b(x,y,z){
        console.log(x,y,z);
    }
     
    b.apply(null,[1,2,3]); // 1 2 3
    

    call(thisArgs [,args...])

    该方法可以传递一个thisArgs参数和一个参数列表,thisArgs 指定了函数在运行期的调用者,也就是函数中的 this 对象,而参数列表会被传入调用函数中。
    thisArgs 的取值有以下4种情况:

    • 不传,或者传null,undefined非严格模式下 函数中的 this 指向 window 对象。 严格模式不传undefined 函数中this指向undefined传入null函数中this指向null
    • 传递另一个函数的函数名,函数中的 this 指向这个函数的引用
    • 传递字符串、数值或布尔类型等基础类型,非严格模式函数中的 this 指向其对应的包装对象,如 StringNumberBoolean严格模式,指向传入的值
    • 传递一个对象,函数中的 this 指向这个对象
    function a(){
        console.log(this); //输出函数a中的this对象
    }
    function b(){} //定义函数b
     
    var obj = {name:'onepixel'}; //定义对象obj
    // 严格模式
    a.call(); //undefined
    a.call(null); //null
    a.call(undefined);//undefined
    a.call(1); //1
    a.call(''); // ''
    a.call(true); //true
    a.call(b);// function b(){}
    a.call(obj); //Object
     // 非严格模式
    a.call(); //window
    a.call(null); //window
    a.call(undefined);//window
    a.call(1); //Number
    a.call(''); //String
    a.call(true); //Boolean
    a.call(b);// function b(){}
    a.call(obj); //Object
    

    当使用call或则apply的时候,如果我们传入的第一个参数是null.函数体内的this会指向默认的宿主对象,在游览器中则是window

    var func = function( a, b, c ){
      alert ( this === window );    // 输出true
    };
    func.apply( null, [ 1, 2, 3 ] );
    

    但如果是在严格模式下,函数体内的this还是为null:

    var func = function( a, b, c ){    
      "use strict";    
      alert ( this === null );     // 输出true
    }
    func.apply( null, [ 1, 2, 3 ] );
    

    有时候我们使用call或者apply的目的不在于指定this指向,而是另有用途,比如借用其他对象的方法。
    那么我们可以传入null来代替某个具体的对象:
    Math.max.apply( null, [ 1, 2, 5, 3, 4 ] ) // 输出:5

    call和apply的用途

    确定函数内部this指向
    var obj1={
      name: '李小龙'
    }
    var obj2={
      name: '萧薰'
    }
    window.name = 'window'
    var getName = function(){
      console.log(this.name)
    };
    getName(); //输出:window
    getName.call(obj1); //输出:李小龙
    getName.call(obj2); //输出:萧薰
    

    当执行getName.call( obj1 )这句代码时,getName函数体内的this就指向obj1对象

    • this 错误的情况
    document.getElementById( 'div1' ).onclick = function(){    
      alert( this.id );            // 输出:div1    
      var func = function(){       
        alert ( this.id );        // 输出:undefined    
      }    
      func();
    };
    
    • 修正this
    document.getElementById( 'div1' ).onclick = function(){    
      var func = function(){        
        alert ( this.id );        // 输出:div1    
      }    
      func.call( this );
    };
    

    bind(thisArgs [,args...])

    bind是ES5 新增的一个方法,它的传参和call类似,但又和 call/apply 有着显著的不同,即调用 call 或 apply 都会自动执行对应的函数,而 bind 不会执行对应的函数,只是返回了对函数的引用。粗略一看,bind 似乎比call/apply 要落后一些,那ES5为什么还要引入bind 呢?

    其实,ES5引入 bind 的真正目的是为了弥补 call/apply 的不足,由于 call/apply 会对目标函数自动执行,从而导致它无法在事件绑定函数中使用,因为事件绑定函数不需要我们手动执行,它是在事件被触发时由JS 内部自动执行的。而 bind 在实现改变函数 this 的同时又不会自动执行目标函数,因此可以完美的解决上述问题,看一个例子就能明白:

    var obj = {name:'onepixel'};
    /**
     * 给document添加click事件监听,并绑定onClick函数
     * 通过bind方法设置onClick的this为obj,并传递参数p1,p2
     */
    document.addEventListener('click',onClick.bind(obj,'p1','p2'),false);
     
    //当点击网页时触发并执行
    function onClick(a,b){
        console.log(
                this.name, //onepixel
                a, //p1
                b  //p2
        )
    }
    

    参考JavaScript设计模式与开发实践一像素 博客园

    相关文章

      网友评论

      • smartphp:这个地方应该不是改变this指向。函数没有调用前是不属于任何对象的。应该是确定this的指向。这个建议不知是否合理?
        lvzhiyi: @smartphp 感觉改变更准确一点,构造函数里面的this本身指向实例化对象,实例对象使用apply就是改变this指向:一个栗子:

        function fruits() {}

        fruits.prototype = {
        color: "red",
        say: function() {
        console.log("My color is " + this.color);
        }
        }

        var apple = new fruits;
        apple.say(); //My color is red

        banana = {
        color: "yellow"
        }
        apple.say.call(banana); //My color is yellow
        apple.say.apply(banana); //My color is yellow
        EdmundChen:恩,合理

      本文标题:apply, call, bind用法

      本文链接:https://www.haomeiwen.com/subject/vnsmbttx.html