关于javascript:深入理解JavaScript的this指针

27次阅读

共计 5155 个字符,预计需要花费 13 分钟才能阅读完成。

深刻了解 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

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

<!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

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

正文完
 0