this ? 拿来吧你

248 阅读5分钟

👋👋大家好,我是Chuck,一个不那么正经却热爱前端的孩儿。

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

🚀🚀今天的文章的男一号是this,我将通过尽可能通俗易懂的方式能够让大家对this的理解更进一步。废话不多说,来看吧~💪

两个误解

关于this经常会有两个误解,罪魁祸首就是因为我们下意识地想要通过this这个词的字面意思去理解它,但可惜都是不太正确的。

指向自身?❌

第一个常见的错误是把它理解成指向函数自身。虽然说函数的确是一个对象,但是这么理解也是不对的。

假如我们此时有这样一个例子。一个foo函数,同时也有一个循环,总共循环5次,调用foo函数5次,此时foo函数有一个count属性(函数是一个对象),在其内部想要在每次调用的时候记录这个次数。

这样做行吗?当然不行。

为什么呢?foo函数上确实有一个count属性,但是这两个count属性却不一样?

怎么说呢?下面有个图可能会更直观一些:

foo.count访问的这个countfoo身上,而this.count访问的count却是在window对象上面(至于为什么,等我细细说)。两个count就不一样,混淆了他们两个,自然就不能达到预期的效果了。

指向函数作用域?也✅也❌

这种理解在某种情况下是正确的,但是在某种情况下又是错误的。

需要注意的是this在任何情况下都不指向词法作用域。某些情况下错误的做法也能达到预期的结果,但总归是不靠谱的,万一哪天捅出来个大篓子,就可以直接卷铺盖回家了。

所以,this到底是什么?

当一个函数被调用时,会创建一个执行上下文。执行上下文包含函数调用堆栈、函数的调用方式、传入的参数等一系列信息。

this则是执行上下文的一个属性。

如何寻找到正确的this

八股文真烦,说来说去,你倒是说怎么找this啊。

别急,这就来了。下面的方法通用且简单!

1. 找调用位置

什么叫调用位置呢?调用位置指的是函数在代码中被执行的位置!

怎么快速地寻找调用位置呢?我们可以借助调用栈,调用栈的第二个元素便是我们的调用位置。下面先给出一个小例子:

先不要看答案,自己想一下, 三个函数的调用位置在哪?

这里给出一下正确答案:

2. 应用绑定规则

绑定规则一共有4条,这里给大家完整地列出来,并附带优先级选择。

  • 默认绑定:独立函数调用
  • 隐式绑定:考虑上下文对象
  • 显式绑定: call、apply、bind
  • new
  1. 默认绑定 独立函数调用

什么叫独立函数调用呢?

bar() foo() baz()这就是独立函数调用,就是直接函数名调用,前面没有任何东西。

应用默认绑定的函数,this都会被绑定到 Window 上面。

值得注意的是,函数必须运行在非严格模式下,this才会被绑定到Window上。在严格模式下,默认绑定会讲this绑定到undefined.

  1. 隐式绑定 考虑上下文对象

所谓考虑上下文对象就是你调用函数的时候,函数前面有没有修饰的东西,比如来说:

此处需要注意的是,有一个隐式丢失的问题,来看下面代码!

这里是不是超出你的想象了,因为这里无论是bar还是obj.foo都是对函数foo的引用。因此这里直接调用bar就直接应用默认绑定规则。

  1. 显式绑定 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要绑定的对象。

  1. 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的时候发生什么了吗?

待我一一给你道来:

  1. 创建一个空对象.
  2. 将空对象链接[[prototype]]
  3. this绑定到空对象还是那个面,并将参数赋值给 this.
  4. 如果函数没有返回其他对象(通常我们的构造函数并不返回其他对象),此时将会返回 this.

好了,至此。this的四条绑定规则已经全部说完。当然了,这里只是粗略地说了一下。更多更细的内容,我在准备中啦。

其实这几天在掘金更文的时候,我一直在思考一个事情,长篇大论的技术文章大家是真的能看下去吗?至少我是不太能看下去,我在尽我的努力想给大家一种不一样的体验,但是目前来看的话,还没有找到一个很好的想法,慢慢来吧,终有一天会找到的,而且我有预感这一天很快就会到来。这个想法也会给我带来很大的影响!