JavaScript知识点回顾(十八)——this(三)

101 阅读3分钟

这是我参与8月更文挑战的第十八天,活动详情查看:8月更文挑战

绑定规则

我们来看看在函数的执行过程中调用位置如何决定this的绑定对象。我们需要先找到调用位置,然后判断需要应用四条规则中的哪一条。首先让我们来看看这四条规则

显式绑定

在分析隐式绑定时,我们必须在一个对象内部包含一个指向函数的属性, 并通过这个属性间接引用函数,从而间接的把this绑定到这个对象上面。那如果我们不想在对象内部包含函数引用,而想在某个对象上强制调用函数,应该怎么操作呢?

JavaScript中的“所有”函数都偶有一些有用的特性,可以用来解决这个问题。想必有些同学已经猜到了,就是函数的call()和apply()方法。这两个方法的第一个参数是一个对象,是给this准备的,接着在调用函数时将其绑定到this。因为这种操作可以直接指定this的绑定对象,所以称之为显式绑定。

    function foo(){
        console.log(this.a);
    }
    var obj={
        a:2
    };
    foo.call(obj);  //2

通过foo.call(),我们在调用foo时强制把它的this绑定到了obj上面。这里值得一提的是,如果传入了一个原始值的参数(字符串、布尔、数字)来当做this的绑定对象,这个原始值会被转换成它的对象形式(即new String()、new Boolean()、new Number())。这个特性被称为装箱。可惜的是,显式绑定无法解决之前的绑定丢失问题。

硬绑定

但是显式绑定的一个变种可以解决这个问题。

    function foo(){
        console.log(this.a);
    }
    var obj = {
        a:2
    };
    var bar = function(){
        foo,call(obj);
    };
    bar();                 //2
    setTimeout(bar,100) ;  //2
    bar.this(window);      //2

上面的例子在函数bar()内部手动调用了foo.call(obj),因此强制把foo的this绑定到了obj。无论后面怎么调用函数bar,总会手动在obj上调用foo。这是一种显式的强制绑定,因此被称之为硬绑定。

因为硬绑定是一种非常常见的模式,所以早在ES5就提供了内置的方法Function.prototype.bind:

    function foo(something){
        console.log(this.a, something);
        returen this.a + something;
    }
    var obg = {
        a:2
    };
    var bar = foo.bind(obj);
    var b = bar(3);  // 2  3
    console.log(b);  //5

bind()会返回一个硬编码的新函数,它会把指定的参数设置为this的上下文并调用原始函数。

API调用的上下文

第三方库的许多函数,以及JavaScript语言和宿主环境中许多新的内置函数,都提供了一个可选的参数,通常被称为上下文,作用和bind()一样,确保回调函数使用指定的this。

    function foo(el){
        console.log(el,this.id);
    }
    var obj = {
        id :"awesome"
    };
    //调用foo()时把this绑定到obj
    [1,2,3].forEach(foo,obj); //1 awesome 2 awesome 3 awesome

这些函数实际上就是通过call()或者apply()实现了显式绑定。