JavaScript之call,apply,bind

472 阅读3分钟

call()、apply()、bind()都是函数对象的一个方法,目的是改变函数中this的指向

一. 作用

通常的函数有两种,一种是独立的函数,另一种是作为对象的一个方法。

作为对象方法的时候,函数中的this指的是调用它的对象。如果是独立函数,通常不用使用this。

call和apply是为了动态改变this而出现的,当一个object没有某个方法,但是其他对象有,我们可以借助call或apply用其它对象的方法来操作。

例如

var cat={
   food:"fish",
   eat: function(){
      alert("I eat "+this.food);
   }
}
var dog = {food:"bone"};
cat.eat.call(dog);

翻译成通俗的方式来理解就是:猫可以吃鱼,现在有一只狗也想吃,那么猫.吃鱼.call(狗),这样狗就可以吃到了。

二. 参数说明

第一个参数:指定将要调用此函数的对象,即this的指向,如果没有,则默认为全局对象window

第二个参数:被调用函数的传参

三. 区别

  1. call传参需要一个个传,apply直接传一个数组
func.call(this, arg1, arg2); 
func.apply(this, [arg1, arg2]);

当你的参数是明确知道数量时,用 call,而不确定的时候,用 apply,然后把参数 push 进数组传递进去。

当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数。

  1. call和apply直接执行函数,bind需要再调用一次

bind是用来绑定this指向,返回一个原函数被绑定this后的新函数

例如:

var obj = {
   x: 81,
}
var foo = {
   getX: function(){
       return this.x;
   }
}
console.log(foo.getX.bind(obj)());//81
console.log(foo.getX.call(obj));  //81
console.log(foo.getX.apply(obj));  //81
  1. bind的第二个参数不传时,可以调用的时候传。如果传了,调用时就不用传,如果调用时也传了,则不生效。

例如:

var person = {
    name:"tt",
    age:24,
    sayHello:function(age){
        console.log(this.name);
        console.log(age);
    }
};
var son = {
    name:"cc"
};
var boundFunc = person.sayHello.bind(son);
boundFunc(25); // cc  25
var boundFunc = person.sayHello.bind(son,25);
boundFunc(); // cc  25
var boundFunc = person.sayHello.bind(son,25);
boundFunc(30); // cc  25

四. 应用

1. 实现继承

function Animal(type) {
    this.type = type;
}
function Bird(type, color) {
    Animal.call(this, type);
    this.color = color;
}
var bird = new Bird('bird', 'green');
console.log(bird); // Bird { type: 'bird', color: 'green' }

2. 合并数组

var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
Array.prototype.push.apply(arr1, arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]

3. 获取数组的最大值,最小值

var num = [1,3,5,7,2,-10,11];
var maxNum = Math.max.apply(null, num);
var minNum = Math.min.apply(null, num);
console.log(maxNum); //11
console.log(minNum); //-10

4. 将伪数组转换为数组

var Arr = {0:'a',1:'b',length:2};
var arr1 = Array.prototype.slice.call(Arr);
console.log(arr1[0]); //a
var arr2 = [].slice.call(Arr);
console.log(arr2[0]); //a
arr1.push("c");
console.log(arr1); //["a", "b", "c"]

Array.prototype.slice.call() 是什么?

Array.prototype.slice.call(arguments)能将具有length属性的对象转成数组,就是arguments.toArray().slice();

slice有两个用法,一个是String.slice,返回字符串,一个是Array.slice,返回数组。

推测一下slice的实现:

Array.prototype.slice = function(start,end){
  var result = new Array();
  start = start || 0;
  end = end || this.length; //this指向调用的对象,当用了call后,能够改变this的指向,也就是指向传进来的对象
  for(var i = start; i < end; i++){
      result.push(this[i]);
  }
  return result;
}

5. 保存this 变量

// 正常情况下使用变量保存 this 值
var foo = {
    bar : 1,
    eventBind: function(){
        var _this = this ;
        $('.someClass').on('click',function(event) {
            console.log(_this.bar);     //1
        });
    }
}

// 使用 bind 进行函数绑定
var foo = {
    bar : 1,
    eventBind: function(){
        $('.someClass').on('click',function(event) {
            console.log(this.bar);      //1
        }.bind(this));
    }
}