由ES范例学JavaScript(三):深切明白this

一. this的泉源

this是JavaScript的关键字,它最初应该是从Java、C++等面向对象的语言中自创来的。

比方,在Java中没有函数只要要领,this只能用在类的成员要领或组织要领中,示意当前实例对象。所以在Java中this的寄义很明白,在其他语言中也相似。

然则到了JavaScript中,this变得复杂了起来:不仅函数内能够用,在一切函数外(全局高低文中)也能够用;函数中的this的寄义在函数声明时没法肯定,要到运行期才肯定,而且与挪用函数的体式格局有关;代码是不是是严厉形式也会影响this的取值。

二. this究竟是什么

在ES5.1中,有一个所谓实行高低文(Execution Context,EC)的观点,简朴的说就是JS引擎的实行进入到某块代码地区时,为该代码地区竖立的高低文对象,主要用来纪录该地区中声明的变量、函数等。

EC有三个主要组成部分:VELEThisBinding。前两个是词法环境,临时不管。第三个 ThisBinding就是指在该代码地区的this的值。

可见,this是跟某块代码地区关联的。而在JS中,代码地区有三种:

  • global代码

  • function代码

  • eval代码

此文中主要议论前两种。

三. 全局代码地区中的this

全局代码地区是一切函数以外的地区。在此地区中的this就是指全局对象window(在Node.js中是global)。

示例:

var num = 123;
console.log(this.num); // 输出123

参考http://es5.github.io/#x10.4.1.1

四. 函数代码地区中的this

函数代码地区是指某个函数内的代码,然则不包含它所嵌套的函数内的代码。从我们能够看出:

this是与包裹它的且离它近来的函数相干的,this既不能穿透到外部的函数,也不能穿透进内部的函数。

示例:

btn.addListener('click', function() {
    var that = this;
    dosth(function() {
        console.log(that.name);
    });
});

一般每一个函数中的this是差别的,内部函数能够援用外部函数的局部变量,然则不能直接援用外部函数的this。经由过程将外部函数的this赋值给一个局部变量能够处理这个题目。

函数内的this的详细函数比较复杂,主要与挪用这个函数的体式格局有关。主要包含以下状况:

1. 直接挪用时

示例:

var num = 123;

function fn() {
    console.log(this.num);
}

function fn2() {
    "use strict"
    console.log(this.num);
}

fn(); // 输出123
fn2(); // 报错

直接挪用函数时,假如是在严厉形式下,this会被设为undefined;假如是在非严厉形式下,this会被设为全局对象window

2. 作为要领挪用时

示例:

var student = {
    name: 'Tom',
    sayName: function() {
        console.log(this.name);
    };
};

student.sayName(); // 输出Tom

作为要领挪用时,this指要领所属的对象。

参考http://es5.github.io/#x10.4.3http://es5.github.io/#x11.2.3

3. call和apply要领:挪用时指定this

除了上述两种牢固的状况外,Javascript供应了一种能够为所欲为地根据需要变动函数中this要领。即运用函数对象的callapply要领来挪用函数,明显这类体式格局给编程带来了极大的天真性。

示例:

function fn() {
    var args = Array. prototype. slice.call(arguments, 1);
    console.log(args);
}

fn(1, 2, 3); // 输出[2, 3]

这类要领经常使用的场景就是:把一个对象的要领”借”给另一个具有相似构造的对象运用。

4. bind要领:从新绑定函数的this

与call和apply差别,bind要领是在挪用前就把函数内的this绑定了,而且一旦绑定就不能再转变。实际上bind要领返回了一个原函数的新版本。

示例:

function fn() {
    console.log(this.age);
}

var fn2 = fn.bind({age: 18});
fn2() // 输出18
fn2.call({age: 25}) // 输出18

经由过程bind获得的函数,不管用哪一种体式格局挪用,它的this都是雷同的。

参考http://es5.github.io/#x15.3.4.5

小结:函数中的this是由挪用函数的体式格局决议的。同一个函数,挪用它的体式格局差别,那末它内部的this就可能差别。换句话说,this是动态决议的。

5. 组织函数中的this

当组织函数经由过程new操纵符来挪用时,this示意正在建立的对象。

示例:

function Person(name, age) {
    this.name = name;
    this.age = age;
}

var jerry = new Person('Jerry', 12);

console.log(jerry.age); // 输出12

正因为这个缘由,我们能够在组织函数中经由过程this给实例增加属性。

参考http://es5.github.io/#x11.2.2

6. 回调函数的this

回调函数也只不过是函数的一种,实际上这类状况已包含在了前面提到的状况中。然则因为回调函数的挪用者每每不是我们本身,而是回调函数的接收者,即某个库或框架、以至是JS运行时环境。这样一来,回调函数在中的this是什么就与对方的挪用体式格局有关了,因而变得比较复杂,所以零丁拿出来议论一下。

状况1:没有明白作用对象的状况下,一般this为全局对象

比方setTimeout函数的回调函数,它的this就是全局对象。你假如愿望本身指定this,能够经由过程bind函数等要领。

状况2:某个事宜的监听器回调函数,一般this就是事宜源对象

比方:

button.addEventListener('click', fn)

fn的中的this就是事宜源button对象。

状况3:某些API会特地供应一个参数,用来指定回调函数中的this

比方,我们能够从新设想一个能够指定this的setTimeout:

function setTimeoutExt(cb, period, thisArg) {
    setTimeout(function() {
        cb.call(thisArg);
    }, period);
}

别的,在ExtJS中也大批运用了能够指定this的接口。

五. eval中的this

(代补充)

六. 从新审阅

this,除了面向对象语言中通用的那两种状况(要领和组织函数)外,在JavaScript 中还供应了更多的运用体式格局,虽然这让JS中的this变得相对难以控制,然则它使得JS越发雄厚越发天真。我们能够把this算作函数的一个特别的隐含的参数,这个参数代表函数正在操纵的主体。

注:时候比较急急,有些处所没有太深切,代码实例也比较简朴。有时机继承完美。

    原文作者:manxisuo
    原文地址: https://segmentfault.com/a/1190000004306888
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞