zoukankan      html  css  js  c++  java
  • JavaScript中的call 和apply的用途以及区别

    apply 接受两个参数,第一个参数指定了函数体内this 对象的指向,第二个参数为一个带下
    标的集合,这个集合可以为数组,也可以为类数组,apply 方法把这个集合中的元素作为参数传
    递给被调用的函数:

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

    在这段代码中,参数 1、2、3 被放在数组中一起传入func 函数,它们分别对应func 参数列
    表中的a、b、c。

    call 传入的参数数量不固定,跟apply 相同的是,第一个参数也是代表函数体内的this 指向,
    从第二个参数开始往后,每个参数被依次传入函数:

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

    当调用一个函数时,JavaScript 的解释器并不会计较形参和实参在数量、类型以及顺序上的

    区别,JavaScript 的参数在内部就是用一个数组来表示的。从这个意义上说,apply 比call 的使用

    率更高,我们不必关心具体有多少参数被传入函数,只要用apply 一股脑地推过去就可以了。

    call 是包装在apply 上面的一颗语法糖,如果我们明确地知道函数接受多少个参数,而且想

    一目了然地表达形参和实参的对应关系,那么也可以用call 来传送参数。

    call和apply的用途

    1. 改变this 指向
    call 和apply 最常见的用途是改变函数内部的this 指向,我们来看个例子:

    var obj1 = {
        name: 'sven'
    };
    var obj2 = {
        name: 'anne'
    };
    window.name = 'window';
    var getName = function(){
        alert ( this.name );
    };
    getName(); // 输出: window
    getName.call( obj1 ); // 输出: sven
    getName.call( obj2 ); // 输出: anne

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

    var getName = function(){
    alert ( this.name );
    };

    实际上相当于:
    var getName = function(){
    alert ( obj1.name ); // 输出: sven
    };

    在实际开发中,经常会遇到this 指向被不经意改变的场景,比如有一个div 节点,div 节点
    的onclick 事件中的this 本来是指向这个div 的:

    document.getElementById( 'div1' ).onclick = function(){
        alert( this.id ); // 输出:div1
    };

    假如该事件函数中有一个内部函数func,在事件内部调用func 函数时,func 函数体内的this
    就指向了window,而不是我们预期的div,见如下代码:

    document.getElementById( 'div1' ).onclick = function(){
        alert( this.id ); // 输出:div1
        var func = function(){
            alert ( this.id ); // 输出:undefined
        }
        func();
    };

    这时候我们用call 来修正func 函数内的this,使其依然指向div:

    document.getElementById( 'div1' ).onclick = function(){
        var func = function(){
           alert ( this.id ); // 输出:div1
        }
        func.call( this );
    };

    2. Function.prototype.bind

    大部分高级浏览器都实现了内置的Function.prototype.bind,用来指定函数内部的this 指向,
    即使没有原生的Function.prototype.bind 实现,我们来模拟一个也不是难事,代码如下:

    Function.prototype.bind = function( context ){
    var self = this; // 保存原函数
    return function(){ // 返回一个新的函数
            return self.apply( context, arguments ); // 执行新的函数的时候,会     把之前传入的context
        // 当作新函数体内的this
        }
    };
    var obj = {
        name: 'sven'
    };
    var func = function(){
        alert ( this.name ); // 输出:sven
    }.bind( obj);
    func();

    我们通过Function.prototype.bind 来“包装”func 函数,并且传入一个对象context 当作参
    数,这个context 对象就是我们想修正的this 对象。

    在Function.prototype.bind 的内部实现中,我们先把func 函数的引用保存起来,然后返回一
    个新的函数。当我们在将来执行func 函数时,实际上先执行的是这个刚刚返回的新函数。在新
    函数内部,self.apply( context, arguments )这句代码才是执行原来的func 函数,并且指定context
    对象为func 函数体内的this。

    这是一个简化版的Function.prototype.bind 实现,通常我们还会把它实现得稍微复杂一点,
    使得可以往func 函数中预先填入一些参数:

    Function.prototype.bind = function(){
        var self = this, // 保存原函数
        context = [].shift.call( arguments ), // 需要绑定的this 上下文
        args = [].slice.call( arguments ); // 剩余的参数转成数组
        return function(){ // 返回一个新的函数
            return self.apply( context, [].concat.call( args, [].slice.call(    arguments ) ) );
            // 执行新的函数的时候,会把之前传入的context 当作新函数体内的this
           // 并且组合两次分别传入的参数,作为新函数的参数
        }
    };
    var obj = {
        name: 'sven'
    };
    var func = function( a, b, c, d ){
        alert ( this.name ); // 输出:sven
        alert ( [ a, b, c, d ] ) // 输出:[ 1, 2, 3, 4 ]
    }.bind( obj, 1, 2 );
    func( 3, 4 );



  • 相关阅读:
    CodeForces Gym 100935G Board Game DFS
    CodeForces 493D Vasya and Chess 简单博弈
    CodeForces Gym 100935D Enormous Carpet 快速幂取模
    CodeForces Gym 100935E Pairs
    CodeForces Gym 100935C OCR (水
    CodeForces Gym 100935B Weird Cryptography
    HDU-敌兵布阵
    HDU-Minimum Inversion Number(最小逆序数)
    七月馒头
    非常可乐
  • 原文地址:https://www.cnblogs.com/wswq/p/6273165.html
Copyright © 2011-2022 走看看