call AND apply

作为前端,call和apply再熟悉不过,今天我们就来谈谈这两个东西~

1. call和apply的区别

call和apply的用途是一样的,只是它们接收参数的方式不同

  • apply 接受两个参数,第一个指定了函数体内this的指向,第二个参数为带下标的集合,可以是数组或类数组
  • call 同样接受两个参数,第一个为函数体内this的指向,从第二个参数开始往后,参数依次被传入函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var func1 = function(a,b,c){
    console.log( [a,b,c] );
    }

    func1.apply( null,[1,2,3] ) //[1,2,3]
    func1.apply( null,[1,2,3,4] ) //[1,2,3]
    func1.apply( null,[1,2] ) //[1,2,undefined]

    var func2 = function( a,b,c ){
    console.log( [a,b,c] );
    };
    func2.call( null,1,2,3 ) //[1,2,3]
    func2.call( null,1,2,3,4 ) //[1,2,3]
    func2.call( null,1,2 ) //[1,2,undefined]

JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时,用 call,而不确定的时候,用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来便利所有的参数。

call和apply的第一个参数为null时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 在使用call或apply时,如果第一个参数传入的是null,函数体的this就会指向默认的宿主对象,在浏览器中是window
var func1 = function( a, b, c){
console.log( this === window );
}

func1.apply( null, [1,2,3] ) //true

// 在严格模式下this还是指向null
var func2 = function( a ,b ,c ){
"use strict"
console.log(this);
};
func2.apply( null, [1,2,3] ); //null
func2.apply( this, [1,2,3] ); //window
func2.apply( [1,2,3] ); //[1,2,3]

2. call和apply的用途

  • 改变this指向

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    var obj1 = {
    name : 'zhu'
    };
    var obj2 = {
    name : 'wang'
    };
    window.name = 'mo';

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

    getName(); //mo
    getName.call( obj1 ); //zhu
    getName.call( obj2 ); //wang

    document.getElementById( 'div1' ).onclick = function(){
    var func = function(){
    console.log( this.id );
    }
    func.call( this );
    }
  • Function.prototype.bind

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    Function.prototype.bind = function( obj ){
    var self = this; //保存原函数
    return function(){ //返回新函数
    return self.apply( obj, arguments) //修改新函数里的this,使它指向传进来的对象
    }
    }
    var obj1 = {
    name : 'zhu'
    }
    var func = function(){
    console.log( this.name )
    }.bind( obj1 );

    func();

    Function.prototype.bind = function(){
    var self = this;
    var obj = [].shift.apply( arguments ); //找出需要绑定的上下文 obj2
    var args = [].slice.apply( arguments ); //[1,2]
    return function(){
    return self.apply(obj,[].concat.call( args, [].slice.apply( arguments ) ) )
    }
    }

    var obj2 = {
    name : 'sun'
    };

    var func5 = function( a , b , c , d ){
    console.log( this.name );
    console.log( [a,b,c,d] );

    }.bind( obj2, 1,2 );

    func5( 4, 5 );

3. 借用其他对象的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//1) 借用构造函数,实现一些类似继承的效果
var A = function(name){
this.name = name;
};

var B = function(){ //B如何使用apply来继承A原型里面的方法??
A.apply( this,arguments );
};

B.prototype.getName = function(){
console.log( this.name );
};

var b = new B( 'chen' );
b.getName(); //chen

//2) 借用[].prototype上的方法:
(function(){
Array.prototype.push.call( arguments, 3 );
console.log( arguments ); //[1,2,3]
})( 1,2 )


(function(){
[].prototype.push.call( arguments, 3 );
console.log( arguments ); //ERROR
})( 1,2 )

通过这次学习,发现了一下问题:

  • [].shift.apply( arguments )这里直接将this指向arguments,等价于arguments.shift();
  • Array.prototype.push.call()和[].prototype.push.call()的区别???
    []没有prototype属性,构造函数才有这个属性,一般对象是没有的。要用也应该是[].push.call(…)。要说区别的话,[].push方法就是从Array.prototype继承来的,所以是等价的。不过前者浪费了一个[]对象,更耗一点内存。

    1
    console.log( [] instanceof Array )	//true
  • B如何使用apply来继承A原型里面的方法??

文章目录
  1. 1. 1. call和apply的区别
  2. 2. 2. call和apply的用途
  3. 3. 3. 借用其他对象的方法: