跨越时代 發表於 2019-6-20 08:32:00

JavaScript箭头函数中的this详解

<p>前言</p>
<p>箭头函数极大地简化了this的取值规则。</p>
<p>普通函数与箭头函数</p>
<p>普通函数指的是用function定义的函数:</p>
<pre class="brush:js;">var hello = function () {
console.log("Hello, Fundebug!");
}</pre>
<p>箭头函数指的是用=&gt;定义的函数:</p>
<pre class="brush:js;">var hello = () =&gt; {
console.log("Hello, Fundebug!");
}</pre>
<p>JavaScript箭头函数与普通函数不只是写法上的区别,它们还有一些微妙的不同点,其中一个不同点就是this。</p>
<p>箭头函数没有自己的this值,箭头函数中所使用的this来自于函数作用域链。</p>
<p>这句话很简单,不过听着稍微有点莫名其妙,得从头说起。</p>
<p>this到底是什么?</p>
<p>关于this的文章也够多了,有时候越描越黑,我就不再添乱了,我只负责搬运一下MDN文档:this,感兴趣的可以仔细阅读一下,我摘录一些最重要的话就好了。</p>
<blockquote>
<p>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.</p>
</blockquote>
<p>JavaScript是一门比较奇特的语言,它的this与其他语言不一样,并且它的取值还取决于代码是否为严格模式("use strict")。</p>
<p>this的值是什么?</p>
<blockquote>
<p>The JavaScript context object in which the current code is executing.</p>
</blockquote>
<p>this就是代码执行时当前的context object。</p>
<p>Global context</p>
<blockquote>
<p>In the global execution context (outside of any function), this refers to the global object whether in strict mode or not.</p>
</blockquote>
<p>代码没有在任何函数中执行,而是在全局作用域中执行时,this的值就是global对象,对于浏览器来说,this就是window。</p>
<p>这一条规则还是比较容易接受的。</p>
<p>Function context</p>
<blockquote>
<p>Inside a function, the value of this depends on how the function is called.</p>
</blockquote>
<p>函数中的this值取决于这个函数是怎样被调用的,这一条规则就有点变态了,也是很容易出BUG的地方。</p>
<p>另外,this的值还与函数是否为严格模式("use strict")有关,这就非常的丧心病狂了...</p>
<p>大家如果好奇的话,出门左转看MDN文档,我多说无益,只说明一种简单的情况。</p>
<p>As an object method</p>
<blockquote>
<p>When a function is called as a method of an object, its this is set to the object the method is called on.</p>
</blockquote>
<p>当函数作为对象的方法被调用时,它的this值就是该对象。</p>
<pre class="brush:js;">var circle = {
radius: 10,
getRadius() {
console.log(this.radius);
}
};
circle.getRadius(); // 打印 10</pre>
<p>self = this?</p>
<p>当我们需要在对象方法中嵌套一个内层函数时,this就会给我们带来实际的困扰了,大家应该写过这样的代码:</p>
<pre class="brush:js;">// 使用临时变量self
var circle = {
radius: 10,
outerDiameter() {
var self = this;
var innerDiameter = function() {
console.log(2 * self.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印20</pre>
<p>outerDiameter函数是circle对象的方法,因此其this值就是circle对象。</p>
<p>那我们直接写this.radius多好啊,可惜不能这么写,因为内层函数innerDiameter并不会继承外层函数outerDiameter的this值。outerDiameter函数的this值就是circle对象,this.radius等于10。</p>
<p>但是,innerDiameter函数的this值不是circle对象,那它到底是啥?它是innerDiameter函数执行时当前的context object,这个context object又是啥?其实我也晕了,所以不妨测试一下:</p>
<pre class="brush:js;">// innerDiameter函数中的this是window
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function() {
console.log(this === window);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印true</pre>
<p>innerDiameter函数中的this是window,为啥是window这个不去管它,反正不是circle对象。</p>
<p>因此,如果我们直接在innerDiameter函数中使用this的话,就出问题了:</p>
<pre class="brush:js;">// 使用普通函数
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function() {
console.log(2 * this.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印NaN</pre>
<p>于是,我们不得不使用一个临时变量self将外层函数outerDiameter的this值搬运到内层函数innerDiameter。</p>
<p>.bind(this)</p>
<p>我们也可以使用.bind(this)来规避this变来变去的问题:</p>
<pre class="brush:js;">// 使用.bind(this)
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function() {
console.log(2 * this.radius);
};
innerDiameter = innerDiameter.bind(this);
innerDiameter();
}
};
circle.outerDiameter(); // 打印20</pre>
<p>但是,无论是使用临时变量self,还是使用.bind(this),都不是什么很简洁的方式。</p>
<p>总之,普通函数的this取值多少有点奇怪,尤其当我们采用面向对象的方式编程时,很多时候都需要用到this,大多数时候我们都不会去使用.bind(this),而是使用临时变量self或者that来搬运this的取值,写起来当然不是很爽,而且一不小心就会写出BUG来。</p>
<p>正如MDN文档所说:</p>
<blockquote>
<p>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.</p>
</blockquote>
<p>箭头函数</p>
<p>箭头函数的this取值,规则非常简单,因为this在箭头函数中,可以看做一个普通变量。</p>
<blockquote>
<p>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.</p>
</blockquote>
<p>箭头函数没有自己的this值,箭头函数中所使用的this都是来自函数作用域链,它的取值遵循普通普通变量一样的规则,在函数作用域链中一层一层往上找。</p>
<p>有了箭头函数,我只要遵守下面的规则,this的问题就可以基本上不用管了:</p>
<ul class=" list-paddingleft-2">
<li>
<p>对于需要使用object.method()方式调用的函数,使用普通函数定义,不要使用箭头函数。对象方法中所使用的this值有确定的含义,指的就是object本身。</p>
</li>
<li>
<p>其他情况下,全部使用箭头函数。</p>
</li>
</ul>
<pre class="brush:js;">// 使用箭头函数
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = () =&gt; {
console.log(2 * this.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印20</pre>
<p>对于内层函数innerDiameter,它本身并没有this值,其使用的this来自作用域链,来自更高层函数的作用域。innerDiameter的外层函数outerDiameter是普通函数,它是有this值的,它的this值就是circle对象。因此,innerDiameter函数中所使用的this来自outerDiameter函数,其值为circle对象。</p>
<p>结论</p>
<p>JavaScript是Brendan Eich花了10天时间设计出来的,因此各种莫名其妙的特性,this也算是其中一个奇葩。好在这些年ECMAScript标准发展很快也很稳定,每年撸一个新的标准,多少可以弥补一下JS的先天不足。</p>
<p>箭头函数对于this取值规则的简化,其实也就是为了少给大家添乱,谁能记得住普通函数this取值的那么多条条框框啊。。。</p>
<p>另外,MDN文档绝对是一个宝藏,大家可以多看看。</p>
<h4>您可能感兴趣的文章:</h4>
<ul class=" list-paddingleft-2">
<li>
<p>javascript的this关键字详解</p>
</li>
<li>
<p>JavaScript中this的全面解析及常见实例</p>
</li>
<li>
<p>JS匿名函数内部this指向问题详析</p>
</li>
<li>
<p>vue项目中在外部js文件中直接调用vue实例的方法比如说this</p>
</li>
<li>
<p>详解JavaScript中关于this指向的4种情况</p>
</li>
<li>
<p>Javascript的this详解</p>
</li>
<li>
<p>JavaScript中this用法学习笔记</p>
</li>
<li>
<p>JavaScript this绑定过程深入详解</p>
</li>
</ul>
<p>文章同步发布:&nbsp;https://www.geek-share.com/detail/2771907768.html</p><br><br>
来源:https://www.cnblogs.com/xxcn/p/11056495.html
頁: [1]
查看完整版本: JavaScript箭头函数中的this详解