JavaScript深刻浅出第1课:箭头函数中的this到底是什么鬼?

JavaScript深刻浅出》系列javascript

普通函数与箭头函数

普通函数指的是用function定义的函数:java

var hello = function () {
    console.log("Hello, Fundebug!");
}
复制代码

箭头函数指的是用=>定义的函数:程序员

var hello = () => {
    console.log("Hello, Fundebug!");
}
复制代码

JavaScript箭头函数与普通函数不仅是写法上的区别,它们还有一些微妙的不一样点,其中一个不一样点就是this。es6

箭头函数没有本身的this值,箭头函数中所使用的this来自于函数做用域链。算法

这句话很简单,不过听着稍微有点莫名其妙,得从头提及。编程

this究竟是什么?

关于this的文章也够多了,有时候越描越黑,我就再也不添乱了,我只负责搬运一下MDN文档:this,感兴趣的能够仔细阅读一下,我摘录一些最重要的话就行了。小程序

A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.微信小程序

JavaScript是一门比较奇特的语言,它的this与其余语言不同,而且它的取值还取决于代码是否为严格模式("use strict")。浏览器

this的值是什么?微信

The JavaScript context object in which the current code is executing.

this就是代码执行时当前的context object。

Global context

In the global execution context (outside of any function), this refers to the global object whether in strict mode or not.

代码没有在任何函数中执行,而是在全局做用域中执行时,this的值就是global对象,对于浏览器来讲,this就是window。

这一条规则仍是比较容易接受的。

Function context

Inside a function, the value of this depends on how the function is called.

函数中的this值取决于这个函数是怎样被调用的,这一条规则就有点变态了,也是很容易出BUG的地方。

另外,this的值还与函数是否为严格模式("use strict")有关,这就很是的丧心病狂了...

你们若是好奇的话,出门左转看MDN文档,我多说无益,只说明一种简单的状况。

As an object method

When a function is called as a method of an object, its this is set to the object the method is called on.

当函数做为对象的方法被调用时,它的this值就是该对象。

var circle = {
    radius: 10,
    getRadius() {
        console.log(this.radius);
    }
};

circle.getRadius(); // 打印 10
复制代码

self = this?

当咱们须要在对象方法中嵌套一个内层函数时,this就会给咱们带来实际的困扰了,你们应该写过这样的代码:

// 使用临时变量self
var circle = {
    radius: 10,
    outerDiameter() {
        var self = this;
        var innerDiameter = function() {
            console.log(2 * self.radius);
        };
        innerDiameter();
    }
};

circle.outerDiameter(); // 打印20
复制代码

outerDiameter函数是circle对象的方法,所以其this值就是circle对象。

那咱们直接写this.radius多好啊,惋惜不能这么写,由于内层函数innerDiameter并不会继承外层函数outerDiameter的this值。outerDiameter函数的this值就是circle对象,this.radius等于10。

可是,innerDiameter函数的this值不是circle对象,那它究竟是啥?它是innerDiameter函数执行时当前的context object,这个context object又是啥?其实我也晕了,因此不妨测试一下:

// innerDiameter函数中的this是window
var circle = {
    radius: 10,
    outerDiameter() {
        var innerDiameter = function() {
            console.log(this === window);
        };
        innerDiameter();
    }
};

circle.outerDiameter(); // 打印true
复制代码

innerDiameter函数中的this是window,为啥是window这个不去管它,反正不是circle对象。

所以,若是咱们直接在innerDiameter函数中使用this的话,就出问题了:

// 使用普通函数
var circle = {
    radius: 10,
    outerDiameter() {
        var innerDiameter = function() {
            console.log(2 * this.radius);
        };
        innerDiameter();
    }
};

circle.outerDiameter(); // 打印NaN
复制代码

因而,咱们不得不使用一个临时变量self将外层函数outerDiameter的this值搬运到内层函数innerDiameter。

.bind(this)

咱们也可使用.bind(this)来规避this变来变去的问题:

// 使用.bind(this)
var circle = {
    radius: 10,
    outerDiameter() {
        var innerDiameter = function() {
            console.log(2 * this.radius);
        };
        innerDiameter = innerDiameter.bind(this);
        innerDiameter();
    }
};

circle.outerDiameter(); // 打印20
复制代码

可是,不管是使用临时变量self,仍是使用.bind(this),都不是什么很简洁的方式。

总之,普通函数的this取值多少有点奇怪,尤为当咱们采用面向对象的方式编程时,不少时候都须要用到this,大多数时候咱们都不会去使用.bind(this),而是使用临时变量self或者that来搬运this的取值,写起来固然不是很爽,并且一不当心就会写出BUG来。

正如MDN文档所说:

Until arrow functions, every new function defined its own this value based on how the function was called。This proved to be less than ideal with an object-oriented style of programming.

箭头函数

箭头函数的this取值,规则很是简单,由于this在箭头函数中,能够看作一个普通变量。

An arrow function does not have its own this. The this value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules.

箭头函数没有本身的this值,箭头函数中所使用的this都是来自函数做用域链,它的取值遵循普通普通变量同样的规则,在函数做用域链中一层一层往上找。

有了箭头函数,我只要遵照下面的规则,this的问题就能够基本上不用管了:

  • 对于须要使用object.method()方式调用的函数,使用普通函数定义,不要使用箭头函数。对象方法中所使用的this值有肯定的含义,指的就是object自己。
  • 其余状况下,所有使用箭头函数。
// 使用箭头函数
var circle = {
    radius: 10,
    outerDiameter() {
        var innerDiameter = () => {
            console.log(2 * this.radius);
        };
        innerDiameter();
    }
};

circle.outerDiameter(); // 打印20
复制代码

对于内层函数innerDiameter,它自己并无this值,其使用的this来自做用域链,来自更高层函数的做用域。innerDiameter的外层函数outerDiameter是普通函数,它是有this值的,它的this值就是circle对象。所以,innerDiameter函数中所使用的this来自outerDiameter函数,其值为circle对象。

结论

JavaScript是Brendan Eich花了10天时间设计出来的,所以各类莫名其妙的特性,this也算是其中一个奇葩。好在这些年ECMAScript标准发展很快也很稳定,每一年撸一个新的标准,多少能够弥补一下JS的先天不足。

箭头函数对于this取值规则的简化,其实也就是为了少给你们添乱,谁能记得住普通函数this取值的那么多条条框框啊。。。

另外,MDN文档绝对是一个宝藏,你们能够多看看。

关于JS,我打算开始写一个系列的博客,你们还有啥不太清楚的地方?不妨留言一下,我能够研究一下,而后再与你们分享一下。也你们欢迎添加个人我的微信(KiwenLau),我是Fundebug的技术负责人,一个对JS又爱又恨的程序员。

参考

关于Fundebug

Fundebug专一于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对一、微脉、青团社等众多品牌企业。欢迎你们免费试用

img

版权声明

转载时请注明做者 Fundebug 以及本文地址:blog.fundebug.com/2019/06/18/…

相关文章
相关标签/搜索