JavaScript箭头函数中的this详解

前言

箭头函数极大地简化了this的取值规则。

普通函数与箭头函数

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

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

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

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

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

箭头函数没有自己的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文档绝对是一个宝藏,大家可以多看看。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解JavaScript中关于this指向的4种情况

    对很多前端开发者来说,JavaScript语言的this指向是一个令人头疼的问题.先看下面这道测试题,如果你能实现并解释原因,那本文对你来说价值不大,可以直接略过. **开篇测试题:**尝试实现注释部分的Javascript代码,可在其他任何地方添加更多代码(如不能实现,说明一下不能实现的原因): let Obj = function (msg) { this.msg = msg this.shout = function () { alert(this.msg) } this.waitAndS

  • Javascript的this详解

    在理解javascript的this之前,首先先了解一下作用域. 作用域分为两种: 1.词法作用域:引擎在当前作用域或者嵌套的子作用域查找具有名称标识符的变量.(引擎如何查找和在哪查找.定义过程发生在代码书写阶段) 2.动态作用域:在运行时被动态确定的作用域. 词法作用域和动态作用域的区别是:词法作用域是在写代码或定义时确定的:动态作用域是在运行时确定的. this的绑定规则 this是在调用时被绑定,取决于函数的调用位置.由此可以知道,一般情况下(非严格模式下),this都会根据函数调用(调用

  • JavaScript this绑定过程深入详解

    本文实例形式详细分析了JavaScript this绑定过程.分享给大家供大家参考,具体如下: 在理解this 的绑定过程之前,首先要理解调用位置:调用位置就是函数在代码中被调用的位置(而不是声明的位置).只有仔细分析调用位置才能回答这个问题:这个this 到底引用的是什么?通常来说,寻找调用位置就是寻找"函数被调用的位置",但是做起来并没有这么简单,因为某些编程模式可能会隐藏真正的调用位置.最重要的是要分析调用栈(就是为了到达当前执行位置所调用的所有函数).我们关心的调用位置就在当前

  • javascript的this关键字详解

    this 的定义 表示当前执行代码的环境对象 因此可将 this 的剖析分为"全局环境" 和 "函数环境" 两种类型的环境对象 全局环境 console.log(this === window); // true var a = 10; console.log(this.a); // 10 函数环境 在函数内部,this 的取值取决于函数被调用时的运行环境. 这里涉及到内存里的数据结构相关的知识点,当我们定义以下字面量对象时会发生一系列的关联关系 var obj =

  • vue项目中在外部js文件中直接调用vue实例的方法比如说this

    一般我们都是在main.js中引入vue,然后在vue文件中直接使用this(this指向的是vue实例),但是在实际开发中,我们往往会引入外部的js文件使用this,这个this就会指向window,并不是我们期待的vue实例,那么就需要重新引入vue文件(import Vue from 'vue'),这样很麻烦.在目前项目中我使用的方法是mian.js导出vue实例,然后在需要使用的js中引入. 步骤一:main.js导出vue实例 var vue = new Vue({ el: '#app

  • JavaScript中this的全面解析及常见实例

    前言 this 关键字在 Javascript 中非常常见,但是很多开发者很难说清它到底指向什么.大部分人会从字面意思上去理解 this,认为 this 指向函数自身,实际上this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调 用时的各种条件.this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式. 总结: 函数被调用时发生 this 绑定,this 指向什么完全取决于函数在哪里被调用. 一.this 的绑定规则 this 一共有 4 中绑定规则,接下来一一介

  • JS匿名函数内部this指向问题详析

    前言 网上看到一句话,匿名函数的执行是具有全局性的,那怎么具有的全局性呢? this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象 1.案例中,第一个say打出来的是Alan,而第二个则是window var name = 'window' var person = { name :'Alan', sayOne:function () { console.log(this.name) }, sayTwo:functi

  • JavaScript中this用法学习笔记

    JavaScript这门语言中,最令人迷惑的地方有三个,闭包.this.原型.针对大多数人,可以利用词法作用域等避开this的坑,但是我们不能一直生活在舒适区,要敢于打破砂锅问到底,对我们来说也是一种提升. 一.一般对this关键字的误解: 1.this指向函数自身 2.this指向函数词法作用域 我们可以看以下一段代码: function test() { test.a = 1; this.a = 2; console.log(test.a); console.log(this.a); con

  • JavaScript箭头函数中的this详解

    前言 箭头函数极大地简化了this的取值规则. 普通函数与箭头函数 普通函数指的是用function定义的函数: var hello = function () { console.log("Hello, Fundebug!"); } 箭头函数指的是用=>定义的函数: var hello = () => { console.log("Hello, Fundebug!"); } JavaScript箭头函数与普通函数不只是写法上的区别,它们还有一些微妙的不

  • python中print()函数的“,”与java中System.out.print()函数中的“+”功能详解

    python中的print()函数和java中的System.out.print()函数都有着打印字符串的功能. python中: print("hello,world!") 输出结果为:hello,world! java中: System.out.print("hello,world!"); 输出结果为:hello,world! 我们可以看到,这两个函数的用法是一样的 print()函数还有这种用法: print("1+1=",1+1) 输出结

  • Python函数中的全局变量详解

    目录 1.什么是全局变量? 2.在函数外部定义的变量是全局变量. 3.在函数内部定义中添加global关键词后变成全局变量. 总结 1.什么是全局变量? 在Python中,全局变量指的是可以作用于函数内部和外部的变量. 在这里有两种情况:在函数的外部定义和内部定义添加global关键词变成全局变量. 2.在函数外部定义的变量是全局变量. 假设一个变量在函数的外部定义,那么这个函数就可以在函数的内部访问,也可以在函数的外部的访问. 示例:定义一个全局变量b,然后定义一个函数a,最后在该函数的内部和

  • 深入理解Javascript箭头函数中的this

    首先我们先看一段代码,这是一个实现倒数功能的类「Countdown」及其实例化的过程: function Countdown(seconds) { this._seconds = seconds; } Countdown.prototype._step = function() { console.log(this._seconds); if (this._seconds > 0) { this._seconds -= 1; } else { clearInterval(this._timer)

  • JS 箭头函数的this指向详解

    箭头函数是ES6中的新增特性,他没有自己的this,其this指向从外层代码库继承. 使用箭头函数时要注意一下几点: 箭头函数不能用作构造函数,用的话会抛出一个错误 无法使用arguments参数,如果要用的话就用rest 无法使用yield命令,所以箭头函数无法用作Generator函数 因为没有自己的this,所以没法通过bind.call.apply来改变this指向 但是这不代表箭头函数的this指向是静态的,我们可以通过改变它外层代码库的this指向来控制 箭头函数的this从外层代码

  • python lambda表达式在sort函数中的使用详解

    1.lambda表达式一般用法 语法: lamda argument:expression example: add = lambda x, y: x+y print(add(10, 20))<br data-filtered="filtered">>>> 30 2.lambda表达式在sort函数中的使用 假如a是一个由元组构成的列表,对该列表进行排序时,我们需要用到参数key,也就是关键词,如下面代码所示,lambda是一个匿名函数,是固定写法:x表示

  • JavaScript队列函数和异步执行详解

    编辑注:在Review别人的JavaScript代码时曾看到过类似的队列函数,不太理解,原来这个是为了保证函数按顺序调用.读了这篇文章之后,发现还可以用在异步执行等. 假设你有几个函数fn1.fn2和fn3需要按顺序调用,最简单的方式当然是: fn1(); fn2(); fn3(); 但有时候这些函数是运行时一个个添加进来的,调用的时候并不知道都有些什么函数:这个时候可以预先定义一个数组,添加函数的时候把函数push 进去,需要的时候从数组中按顺序一个个取出来,依次调用: var stack =

  • javaScript的函数对象的声明详解

    写作缘由: 平时再用js写函数的时候,一般都是以惯例 function fn () {} 的方式来声明一个函数,在阅读一些优秀插件的时候又不免见到 var fn = function () {} 这种函数的创建,究竟他们用起来有什么区别呢,今天就本着打破砂锅问到底的精神,好好来说说这个让人神魂颠倒的--函数声明. 函数声明 函数声明示例代码 复制代码 代码如下: function fn () {     console.log('fn 函数执行..');     // code.. } 这样我们

  • JSX在render函数中的应用详解

    一.JSX简介 const element = <h1>Hello, world!</h1>; JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式.JSX 可能会使人联想到模版语言,但它具有 JavaScript 的全部功能. Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用. 以下两种示例代码完全等效: const element = ( <h1 className="greeting"> H

  • JavaScript在XHTML中的用法详解

    编写XHTML代码的规则要比编写HTML要严格得多,类似下面的代码在HTML中是有效的,但在XHTML中则是无效的. [javascript] 复制代码 代码如下: <script type="text/javascript"> function compare(a, b) { if(a < b) { alert("a is less then b"); } else if(a > b) { alert("a is greater

随机推荐