👋👋大家好,我是Chuck,一个不那么正经却热爱前端的孩儿。
这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
🚀🚀今天的文章的男一号是this,我将通过尽可能通俗易懂的方式能够让大家对this的理解更进一步。废话不多说,来看吧~💪
两个误解
关于this经常会有两个误解,罪魁祸首就是因为我们下意识地想要通过this这个词的字面意思去理解它,但可惜都是不太正确的。
指向自身?❌
第一个常见的错误是把它理解成指向函数自身。虽然说函数的确是一个对象,但是这么理解也是不对的。
假如我们此时有这样一个例子。一个foo函数,同时也有一个循环,总共循环5次,调用foo函数5次,此时foo函数有一个count属性(函数是一个对象),在其内部想要在每次调用的时候记录这个次数。
这样做行吗?当然不行。
为什么呢?foo函数上确实有一个count属性,但是这两个count属性却不一样?
怎么说呢?下面有个图可能会更直观一些:
foo.count访问的这个count在foo身上,而this.count访问的count却是在window对象上面(至于为什么,等我细细说)。两个count就不一样,混淆了他们两个,自然就不能达到预期的效果了。
指向函数作用域?也✅也❌
这种理解在某种情况下是正确的,但是在某种情况下又是错误的。
需要注意的是this在任何情况下都不指向词法作用域。某些情况下错误的做法也能达到预期的结果,但总归是不靠谱的,万一哪天捅出来个大篓子,就可以直接卷铺盖回家了。
所以,this到底是什么?
当一个函数被调用时,会创建一个执行上下文。执行上下文包含函数调用堆栈、函数的调用方式、传入的参数等一系列信息。
而this则是执行上下文的一个属性。
如何寻找到正确的this
八股文真烦,说来说去,你倒是说怎么找this啊。
别急,这就来了。下面的方法通用且简单!
1. 找调用位置
什么叫调用位置呢?调用位置指的是函数在代码中被执行的位置!
怎么快速地寻找调用位置呢?我们可以借助调用栈,调用栈的第二个元素便是我们的调用位置。下面先给出一个小例子:
先不要看答案,自己想一下, 三个函数的调用位置在哪?
这里给出一下正确答案:
2. 应用绑定规则
绑定规则一共有4条,这里给大家完整地列出来,并附带优先级选择。
- 默认绑定:独立函数调用
- 隐式绑定:考虑上下文对象
- 显式绑定: call、apply、bind
- new
- 默认绑定 独立函数调用
什么叫独立函数调用呢?
bar() foo() baz()这就是独立函数调用,就是直接函数名调用,前面没有任何东西。
应用默认绑定的函数,this都会被绑定到 Window 上面。
值得注意的是,函数必须运行在非严格模式下,this才会被绑定到Window上。在严格模式下,默认绑定会讲this绑定到undefined.
- 隐式绑定 考虑上下文对象
所谓考虑上下文对象就是你调用函数的时候,函数前面有没有修饰的东西,比如来说:
此处需要注意的是,有一个隐式丢失的问题,来看下面代码!
这里是不是超出你的想象了,因为这里无论是bar还是obj.foo都是对函数foo的引用。因此这里直接调用bar就直接应用默认绑定规则。
- 显式绑定 call、apply、bind
既然this有这么复杂的规则,那么我可不可以手动的将它绑定到一个对象上呢,以防总是不小心的出错,当然可以了。call, apply, bind便可以这么做。
首先说: call, apply
二者的第一个参数是一样的,就是this要绑定的对象!
二者不同的地方是剩余的参数。
function foo(num1, num2) {
console.log(num1, num2);
}
var obj = {};
foo.call(obj, 1, 2); // 参数顺次往后挪动一个位置
foo.apply(obj, [1, 2]); // 参数都写在一个数组里面
再来说: bind
bind是绑定在 Function.prototype 上面的一个函数,接收一个参数,即this要绑定的对象。
- new
说到new就要说起构造函数了,构造函数的流程是什么样子的呢?我们先来搞一个构造函数:
function Person(name, age, height, weight) {
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
}
let chuck = new Person('Chuck', 18, 180, 120);
同学你可知道你使用new的时候发生什么了吗?
待我一一给你道来:
- 创建一个空对象.
- 将空对象链接[[prototype]]
- 将
this绑定到空对象还是那个面,并将参数赋值给this. - 如果函数没有返回其他对象(通常我们的构造函数并不返回其他对象),此时将会返回
this.
好了,至此。this的四条绑定规则已经全部说完。当然了,这里只是粗略地说了一下。更多更细的内容,我在准备中啦。
其实这几天在掘金更文的时候,我一直在思考一个事情,长篇大论的技术文章大家是真的能看下去吗?至少我是不太能看下去,我在尽我的努力想给大家一种不一样的体验,但是目前来看的话,还没有找到一个很好的想法,慢慢来吧,终有一天会找到的,而且我有预感这一天很快就会到来。这个想法也会给我带来很大的影响!