8

一、this关键字小测试

ES6箭头函数体中的this指向哪里?

在回答这个问题之前先来揣揣你对this关键字的了解程度:
(让我们回到ES6之前)
题:

var obj = {
    a: function() {
        console.log(this);
    }
}

var b = obj.a;
b();

问:打印出的this的值?
再来几个选项:

  1. window
  2. obj
  3. document
  4. obj.a

答案是window(可以在控制台运行一下)

什么!什么情况!发生了什么?

一句话解释:
一般的,this指向函数运行(调用)时所在的执行环境【《JavaScript高级程序设计》4.2节执行环境及作用域】的(变量)对象(简单地,this指向函数的调用者)

分析代码:

var b = obj.a;    // ==>相当于
var b = function() {
    console.log(this);
}

函数调用时(b();)其所在的执行环境是全局环境,所以this指向全局变量对象window

二、ES6箭头函数

如果你觉得以上这些都知道了(我会!我会!)那么就继续吧~~

(来到ES6箭头函数)

例1【阮一峰《ECMAScript 6 入门》-7.函数的扩展:箭头函数

我将阮老师的例子代码修改了一下:
(普通函数)

function foo() {
    console.log("id1:", this.id);
    console.log("this1:", this);
    setTimeout(function() {
        console.log("id2:", this.id);
        console.log("this2:", this);
    }, 0);
}

var id = 21;

foo();

// Chrome
// id1: 21
// this1: window
// id2: 21
// this2: window

foo.call({id: 42});

// Chrome
// id1: 42
// this1: {id: 42}
// id2: 21
// this2: window

注意:超时调用(setTimeout回调)的代码都是在全局作用域环境中执行的,因此(setTimeout回调)函数中this的值在非严格模式下指向window对象,在严格模式下是undefined

例如

var obj = {
    console.log(this);
    setTimeout(function() {
        console.log(this);
    }, 0);
}

obj.a();

// Chrome
// {a: f}
// window
回到例1

我们使用foo函数的call方法改变了foo函数调用时函数体内this的指向({id: 42}这个对象),但setTimeout回调函数中的this依旧指向window对象(因为在全局环境中运行)。

接下来再将例1改写一下,将foo函数中的setTimeout回调函数改成箭头函数的形式:

例2
(箭头函数)

function foo() {
    console.log("id1:", this.id);
    console.log("this1:", this);
    setTimeout(() => {
        console.log("id2:", this.id);
        console.log("this2:", this);
    }, 0);
}

var id = 21;

foo();

// Chrome
// id1: 21
// this1: window
// id2: 21
// this2: window

foo.call({id: 42});

// Chrome
// id1: 42
// this1: {id: 42}
// id2: 42
// this2: {id: 42}

foo();的输出结果没有变化,但foo.call({id: 42});的输出结果改变了。

到底发生了什么?

在这里直接给出结论:
箭头函数根本没有自己的this,导致内部的this指向了外层代码的this这个指向在定义时就已经确定而不会在调用时指向其执行环境的(变量)对象

注意:因为箭头函数内部的this是指向外层代码块的this(最近的this,例2中的foo函数)的,所以我们可以通过改变外层代码块的this的指向从而改变箭头函数中this的指向(例2中使用了foo函数的call方法)。

重新解释例2

因为箭头函数(setTimeout回调)没有自己的this,导致其内部的this引用了外层代码块的this,即foo函数的this

(要注意:在定义阶段(调用函数前),foo函数的this的值并不确定【《JavaScript高级程序设计》第三版5.5.4函数内部属性】,但箭头函数的this自定义阶段开始就指向foo函数的this了)

又因为使用call方法改变了foo函数运行(调用)时其函数体内this的指向(重新指向对象{id: 42})从而使箭头函数中this的指向发生变化,最后输出例2所示结果。

三、例子总结

到这里,我想小伙伴们应该是比较清楚了,那么不妨去看看阮一峰老师的《ECMAScript 6 入门》函数的扩展一节箭头函数部分的一个问题示例:“请问下面的代码中有几个this(滑稽)

加深印象

例3【阮一峰《ECMAScript 6 入门》-19.Class基本语法:this的指向

class Logger {
  constructor() {
    this.printName = (name = 'there') => {
      this.print(`Hello ${name}`);
    };
  }

  // ...
}

箭头函数中的this指向constructor构造方法内部的this,由于此时constructor中的this尚未获得值,当通过new命令生成对象实例时,将会自动调用constructor方法,constructorthis才能指向该实例对象,在此过程中,箭头函数中的this一直引用着constructor中的this,当constructorthis发生变化,箭头函数的this也会一并发生变化。

若有错误,请指出批评!!

lostexin
47 声望2 粉丝

千里之行始于足下