深入理解JavaScript的this指针

yancy

深入理解JavaScriptthis指针

hello everybody,我又来了。上一篇文章中,我们讲解了JavaScript 的原型,有兴趣的同学可以通过传送门进入查看,今天我们来聊聊JavaScript中的this。一起探究下这个比较难理解的问题。话不多说,让我们进入正题吧👇👇👇

1.this指针具体是什么

首先开始第一个问题,想要了解this,那就得先知道它是什么东西。先来看一个例子。

this is a little dog. ---> 这是一只小狗

这句话可以让我们很清楚的明白this所指的是一只小狗。

灵光一闪,咦,好像明白了点儿什么。

不急,接着来看,

this.name = "二狗子"

通过这个句子,我们能很快的知道,这只小狗的名字叫二狗子。现在想象一下,将你的手指指向其他的小狗、小猫……

this is a cat.
this.name = "球球" // 这是一个猫,它叫球球

this is a other little dog. 
this.name = "狗剩子" // 这是另一只小狗,他的名字叫 狗剩子

// 作者内心是崩溃的,身为一个英语渣渣,这几句英语解释,简直要了老命。

看到这里的你是不是会感觉豁然开朗,我们的手指指向谁,this代表的就是谁。

哇哦,感觉打开了新世界的大门,this is so easy

重点来了!!!

this到底是什么呢?

它是一个代指,我们经常说在JavaScript中万物皆对象,那么,this就是代指这一个个对象,最重要的是看你(this)的手指指向谁

是不是需要巩固一下了?

this is lucy.
this is lilei.
this is liming.
this is me.
this is my brother.
this is a people.
…………………………

抛开那些烦人的英文吧,代码才是我的追求……

2.this指针的具体指向

想知道this具体代指的是哪个对象,最关键的还是要弄懂它的指向。

2.1 无闭包的函数

闭包的内容会在后续章节中讲解,敬请期待。

知识点1:

一般情况下,this代指的是调用它的那个对象(闭包和箭头函数除外,稍后讲解)

怎么理解这句话呢?小栗子上场

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
  function getName () {
    console.log(this === window) // true
  }

    // 调用
  getName()
</script>

这里定义了一个getName函数。调用的时候并没有指定是谁调用,那,它的this指向到了哪儿呢?

知识点2:

全局的方法和属性都是window对象的方法和属性,也就是说,这里的调用其实是 window.getName(),是 window调用的,也就是它的this指向是window

image

没理解?好吧,再看一个栗子

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
  let obj = {
    getName: getName
  }
  function getName () {
    console.log(this === window) // false
    console.log(this === obj) // true
  }
  obj.getName()
</script>

定义一个对象,将getName当做这个对象的方法,最后通过obj.getName()来执行,你会惊喜的发现,this已经指向了obj。原因就是:调用函数的对象是obj

image

2.2 有闭包的函数

众所周知,计时器也是闭包的一种,为了不让大家对代码有不理解的地方,这里我们使用计时器实现闭包。

还是熟悉的配方,还是熟悉的味道,还是熟悉的栗子。

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
  let obj = {
    getName: getName
  }
  function getName () {
    setTimeout(function xiaosan() {
      console.log(this === obj) // false
    }, 0)
  }
  obj.getName()
</script>

纳尼!!不是obj调用的吗?不是this会指向obj吗?究竟是谁,是谁让原本忠贞的爱情出现了裂痕?

别急别急,我们来分析一下,obj调动了getName函数。但是计时器中的xiaosan函数并不是由obj调用的,所以this肯定不会指向obj。那么,xiaosanthis指向了谁呢?我们将setTimeout改写为下面这种写法

setTimeout(function xiaosan() {
  console.log(this) // window
}, 0)

哦~原来window就是那个第三者。终于抓到你了。

见证奇迹的时刻,让我们为错位的爱情复位吧!

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
  let obj = {
    getName: getName
  }
  function getName () {
    setTimeout(function xiaosan() {
      console.log(this === obj) // true
    }.bind(this), 0)
  }
  obj.getName()
</script>

长舒一口气,终于圆满了。

知识点3:

闭包中的this指向为window。可以使用箭头函数、call、apply、bind等手法改变this指向。

2.3 箭头函数

首先介绍下箭头函数。官方文档 说到,箭头函数有以下几个特性:

  • 没有单独的this
  • 不能通过call、apply、bind方法改变this指向(因为没有😂)
  • 不绑定arguments
  • 不能使用 new 操作符 (因为没有this😂)
  • 没有prototype (具体介绍请看我上一篇文章)
  • 不能作为函数生成器,不能在箭头函数中使用yield

    • 除非是箭头函数内嵌套了一个普通函数,将yield用在普通函数中

看到这里突然发现,箭头函数实在是太惨了,这么多不能干的。既然这么多限制,那还用箭头函数干什么?

好处很多,这里我们只介绍与本节课相关的,箭头函数可以“自动绑定”this

举个栗子看下:

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
  let obj = {
    getName: () => {
      console.log(this === obj) // false
    },
    getAge: function () {
      console.log(this === obj) // true
    }
  }

  obj.getName()
  obj.getAge()
</script>

同样的两个方法调用,第一个返回的竟然是false。这是为什么?明明我就是使用obj调用的。别急,我们来打印下this,看它指向了哪儿。

getName: () => {
  console.log(this) // window
},

知识点4:

箭头函数不会创建自己的this,只会从作用域链的上一层继承this

这里的作用域上一层为window对象,所以,this指向了window

我们再来看看闭包中的箭头函数

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
  let obj = {
    getName: getName
  }
  function getName () {
    setTimeout(() => {
      console.log(this === obj) // true
    }, 0)
  }
  obj.getName()
</script>

刚才我们这个打印是false,现在我们把xiaosan函数改成箭头函数,惊喜的发现,this指向了obj。是因为getName函数的上一层thisobj,继承了过来。

3.修改this指针的指向

手动改变this指针,可以帮助我们更明确的知道自己要执行的方法。改变指针的方法有三种。

  • call
  • apply
  • bind

3.1 call、apply

这两兄弟性格相近,那就放到一块儿来介绍吧。

使用call()apply()扩充作用域最大的好处就是, 对象不需要与方法有任何耦合关系,

它们的作用就是在函数的体内设置this的值。也就是我们说的改变this的指向。怎么改变的呢?

😏小栗子又要出场了。

call 的小栗子
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
  let obj = {
    name: '一个对象',
    getName: function (a,b,c) {
      globalGetName.call(this,a,b,c)
    }
  }
  function globalGetName (a,b,c) {
    console.log(this.name,a,b,c) // 一个对象 xiaosan xiaosi xiaowu
  }
  obj.getName("xiaosan", "xiaosi", "xiaowu")
</script>

知识点5:

call的特点

  • this绑定到当前环境中,
  • 参数逐个传入。
apply 的小栗子
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
  let obj = {
    name: '一个对象',
    getName: function (a,b,c) {
      globalGetName.apply(this,[a,b,c])
    }
  }
  function globalGetName (a,b,c) {
    console.log(this.name,a,b,c) // 一个对象 xiaosan xiaosi xiaowu
  }
  obj.getName("xiaosan", "xiaosi", "xiaowu")
</script>

知识点6:

apply的特点

  • this绑定到当前环境中,
  • 传入一个参数列表,如 [a,b,c]

3.2 bind

在调用bind方法之后,会将bind中传入的第一个参数作为this绑定到当前函数上。之后的参数会逐个传入到函数中。

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
  let obj = {
    name: '一个对象'
  }
  function getName (a) {
    console.log(this.name, a)
  }
  getName.bind(obj, '111')() // 一个对象 111
</script>

可以看到,我们通过getName.bindobj传入,作为getNamethis。那么在执行getName的时候,this代指的就是obj对象。那么this.name === obj.name。将第二个参数通过入参的方式传入到getName上。

知识点7:

bind的特点

  • 不会自动执行,通过bind绑定this后,需要手动执行函数。如getName.bind(obj, '111')()
  • 传参方式和call相同

首先,恭喜能看到这里的你,说明我的文章也不是这么让人厌烦。接下来我会通过一个面试题来具体讲解this指针的变换。准备好了吗? Let’s go……

4.面试题说明

var foo = 'fooBar'
var obj = {
  foo: "bar",
  func: function () {
    var self = this;
    console.log(this.foo); // 1
    console.log(self.foo); // 2
    (function () {
      console.log(this.foo); // 3
      console.log(self.foo); // 4
    }());
  }
};
obj.func();

输出:

  1. bar
  2. bar
  3. fooBar
  4. Bar
解析:
  • 当执行obj.func的时候,this指向了obj。此时 1 和 2都能正常的方位 obj.foo
  • 3因为在一个闭包中,this指向了window,所以会找到全局的foo = ‘fooBar’
  • 4因为缓存了this,所以self代指的还是obj这个对象,self.foo === obj.foo
阅读 382

你若盛开,清风自来

12 声望
1 粉丝
0 条评论
你知道吗?

你若盛开,清风自来

12 声望
1 粉丝
文章目录
宣传栏