深入浅出this、apply、call、bind

this的概念

this是Javascript语言的一个关键字。this对象是在运行时基于函数的执行环境绑定的。 它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用.随着函数使用场合的不同,this的值会发生变化。但是有一个 总的原则,那就是this指的是,调用函数的那个对象. 谁调用它,this就指向谁。

注意 :当我们使用严格模式(strict mode)的时候,this 在全局函数中和匿名函数中的值是未定义的(undefined),不指向任何一个对象。一个函数执行的时候,它就获得了 this 这个属性。

  1. 全局作用域中使用this:在全局作用域中,当代码在浏览器中执行的时候,所有的全局变量和函数都被定义在 window 对象上。因此,当我们在全局函数中使用 this 的时候,它会指向全局 window 对象并且拥有它的值(除非在严格模式下)。
  2. 我们应该发现当上下文发生变化的时候,换句话说就是当我们在别的对象中调用了本对象内定义的方法的时候,this 关键字就不再指向定义 this 时的那个对象了,而是指向了调用了那个 this 所在方法的对象。
  3. 在匿名函数内部的 this 不能获得外层函数 this 的值,所以当没有使用严格模式的时候,它就被绑定在了全局 window 对象上了。在内层函数中维持 this 的值的方法:在常见的单体模式中,通常我们会使用_this , that , self 等保存 this,这样我们可以在改变了上下文之后继续引用到它 。

this的基本用法

  1. 纯粹的函数调用:这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global.

    1
    2
    3
    4
    5
    6
    var x = 1;
    function test(){
        this.x = 0;
    }
    test();
    alert(x); //0
  2. 作为对象方法的调用:函数还可以作为某个对象的方法调用,这时this就指这个上级对象

    1
    2
    3
    4
    5
    6
    7
    function test(){
        alert(this.x);
      }
    var o = {};
    o.x = 1;
    o.m = test;
    o.m(); // 1
  3. 作为构造函数调用:所谓构造函数,就是通过这个函数生成一个新对象(object)。这时,this就指这个新对象。

    1
    2
    3
    4
    5
    function test(){
        this.x = 1;
      }
    var o = new test();
    alert(o.x); // 1
  4. apply调用:apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var x = 0;
    function test(){
        alert(this.x);
      }
    var o={};
    o.x = 1;
    o.m = test;
    o.m.apply(); //0
    o.m.apply(o); //1

apply和call的作用以及区别

JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。当一个 object 没有某个方法,但是其他的有,我们可以借助call或apply用其它对象的方法来操作。

作用:call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。

区别:apply和call功能一样,作用是绑定this指针,设定函数中this的上下文环境。第一个参数都是要传入给当前对象的对象,你想指定的上下文,可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象)。但对第二个参数:call()接受的是一个参数列表,而 apply()方法接受的是一个包含多个参数的数组(或类数组对象arg)。

1
2
3
4
5
6
7
8
9
10
11
12
/* 其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),
* call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。
* 当参数数量不确定时,函数内部也可以通过 arguments 这个伪数组来遍历所有的参数。
*/

func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])

//number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。

var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers), //458
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

bind的作用

bind() 方法与 apply 和 call 很相似,也是可以改变函数体内 this 的指向。bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。 在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。 区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。但IE9(包括IE9)以上的才支持bind

1
2
3
4
5
6
7
8
9
10
11
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

bind、call、apply三者的区别

  1. apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
  2. apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
  3. apply 、 call 、bind 三者都可以利用后续参数传参,apply 传递的参数必须是用数组进行包装的,而 call 和 bind 则是将要传递的参数一一列出;
  4. bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

用apply或call模拟bind

1
2
3
4
5
6
7
8
9
10
Function.prototype.Bind = function(context){
var self = this,
// 获取到bind第二个参数(中的所有参数),bind参数是一一列出的。
args = Array.prototype.slice.call(arguments,1);
// 返回一个新的函数
return function(){
// 将相关参数赋给这个bind所在方法,并将执环境赋给context
return self.apply(context,args);
};
};

参考: