A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© webman 初级黑马   /  2017-9-3 15:12  /  1730 人查看  /  2 人回复  /   4 人收藏 转载请遵从CC协议 禁止商业使用本文

你被JavaScript中的关键字this迷惑了吗?它一开始迷惑了所有初学者,所以你不用担心,不止是你被迷惑了。 但是这不意味着你可以永远不用理解this。它在JavaScript和教程中使用的非常多,以至于你迟早需要明白this是什么。一旦你理解了它,你就会意识到它比你想象的要简单得多。 在本文的结尾,你会为揭秘this。你会知道它是什么,它有什么用以及如何使用它。

所以,this是什么?

this 是一个值随着调用方式变化的关键字。这里有this 呈现不同值的六种方式。它们分别是:
  • 全局环境下的this

  • 对象构造函数中的this

  • 对象方法中的this

  • 简单函数中的this

  • 箭头函数中的this

  • 事件监听器中的this


首先你可能会好奇在每种条件下this是什么以及为什么有必要去改变它的值。



全局环境下的This

当this在任何方法之外调用,在全局环境下,this默认为浏览器的Window对象。

[JavaScript] 纯文本查看 复制代码
`console.log(this) // Window`

这里默认为浏览器的Window对象。

通常你不会在全局环境下用this,所以它的值在这里不太重要。我们接下来去看另一种环境下。

对象构造函数中的This

当你使用new关键字创建一个对象的实例,this指向该对象。

[JavaScript] 纯文本查看 复制代码
function Human (age) {[/size][/font][/color][/p]this.age = age
}
let greg = new Human(22)
let thomas = new Human(24)
console.log(greg) // this.age = 22
console.log(thomas) // this.age = 24


向对象实例

你可以看到greg是Human对象的一个实例。现在,无论何时你引用greg,你不会意外地获取到thomas。所以,设置this为对象的一个实例是非常有意义的。

接下来看一个紧密相关的环境 – 对象方法中的this。

###对象方法中的This

方法是关联在对象上的函数的别称, 如下: (注意: 这里的方法是ES6对象中定义方法的简写方式. 点击这里 如果你不确定它如何使用).

[JavaScript] 纯文本查看 复制代码
let o = {
// A method
aMethod () {}
}

任意方法中的this 指向对象本身。

[JavaScript] 纯文本查看 复制代码
let o = {
sayThis () {
console.log(this)
}
}

o.sayThis() // o

这里指向对象

既然 this 指向对象,你可以在方法中获得对象的实例,如下:

[JavaScript] 纯文本查看 复制代码
function Human (name) {
return {
name,
getName() {
return this.name
}
}
}
const zell = new Human('Zell')
const vincy = new Human('Vincy')
console.log(zell.getName()) // Zell

在接下来的两部分, 你可以看到this的值的改变使你可以获得正确的对象实例, 这是面向对象编程的基础。面向对象编程是接下来某天讨论的话题。

让我们介绍下面的部分

普通函数中的This

大家熟知普通函数; 如下面的这个。以相同方式定义的匿名函数也被看做普通函数.

[JavaScript] 纯文本查看 复制代码
function hello () {
// say hello!
}

在浏览器中, 普通函数中this 总是被看做Window 对象。这在你调用对象中的方法的时候同样适用()。

[JavaScript] 纯文本查看 复制代码
function simpleFunction () {
console.log(this)
}

const o = {
sayThis () {
simpleFunction()
}
}
simpleFunction() // Window
o.sayThis() // Window

不幸的是, this的值在这里的改变是初学者不希望看到的。他们认为this 和对象方法中的是一致的。我也迷惑于此。

去了解原因,考虑下面的代码 ,this.speakLeet 方法在之后的setTimeout 函数中执行.

[JavaScript] 纯文本查看 复制代码
const o = {
doSomethingLater () {
setTimeout(function() {
this.speakLeet() // Error
}, 1000)
},
speakLeet() {
console.log(`1337 15 4W350M3`)
}
}

不幸的是,上述代码执行错误。错误的原因是在 setTimeout 函数中this 被设定为 Window对象 。Window 没有 speakLeet 方法。

解决方式是创建一个变量存储this的引用。这个变量通常叫做self 或 that。

[JavaScript] 纯文本查看 复制代码
const o = {
doSomethingLater () {
const self = this
setTimeout(function() {
self.speakLeet()
}, 1000)
},
speakLeet() {
console.log(`1337 15 4W350M3`)
}
}

第二种解决方式是使用ES6中的箭头函数,也就是我们下面要讨论的部分。

箭头函数中的This

箭头函数中的this总是它定义时所指向的环境 (在它的定义环境)。 所以, 你在对象方法中使用箭头函数, this的执行环境总是指向对象, 而不是 Window。

上文中 speakLeet 可以在箭头函数中这样调用:

[JavaScript] 纯文本查看 复制代码
const o = {
doSomethingLater () {
setTimeout(() => this.speakLeet(), 1000)
},
speakLeet() {
console.log(`1337 15 4W350M3`)
}
}

第三种在任意函数内改变this值的方式是使用bind, call 或 apply方法。在下文中我们会介绍bind,下次介绍call 和 apply。但是首先我们先介绍最后一部分——事件监听器

事件监听器中的Thisthis被设定为事件监听器中绑定事件的对象:

[JavaScript] 纯文本查看 复制代码
let button = document.querySelector('button')
button.addEventListener('click', function() {
console.log(this) // button
})

当创建更复杂的组件时,您可能会发现自己在方法中创建事件监听器。

[JavaScript] 纯文本查看 复制代码
function LeetSpeaker (elem) {
return {
listenClick () {
elem.addEventListener('click', function () {
// Do something here
})
}
}
}

由于this 指的是事件监听器中的元素,如果需要调用另一个方法,则需要通过该方法所属对象进行调用。

[JavaScript] 纯文本查看 复制代码
function LeetSpeaker (elem) {
return {
listenClick () {
const self = this
elem.addEventListener('click', function () {
self.speakLeet()
})
},
speakLeet() { console.log(`1337 15 4W350M3`) }
}
}

你也可以使用箭头函数. 你可以通过 event.currentTarget来获得该元素的引用.

[JavaScript] 纯文本查看 复制代码
function LeetSpeaker (elem) {
return {
listenClick () {
elem.addEventListener('click', (e) => {
console.log(e.currentTarget) // elem
this.speakLeet()
})
},
speakLeet() { console.log(`1337 15 4W350M3`) }
}
}

这些匿名方法都不足以帮你移除事件监听器。

为了移除事件监听器,命名函数作为第二个参数:

[JavaScript] 纯文本查看 复制代码
function someFunction () {
console.log('do something')

// Removes the event listener.
document.removeEventListener('click', someFunction)
}
document.addEventListener('click', someFunction)

如果你需要在事件监听器中引用该对象, 你需要使用 bind手动创建this的执行环境.

[JavaScript] 纯文本查看 复制代码
function LeetSpeaker (elem) {[/size][/font][/color][/p]return {
listenClick () {
this.listener = this.speakLeet.bind(this)
elem.addEventListener('click', this.listener)
},
speakLeet(e) {
const elem = e.currentTarget
console.log(`1337 15 4W350M3`)
elem.removeEventListener('click', this.listener)
}
}
}


如果你没理解bind你可能对上述代码迷惑。所以,在解释为什么之前,先解释一下bind。

##通过bind改变this bind是一种在每个函数中都存在的方法。它允许你改变this上下文。此方法接受任意数量的参数并返回绑定函数。

[JavaScript] 纯文本查看 复制代码
const sayThis = _ => console.log(this)
const boundFunc = sayThis.bind(/* arguments...*/)

传入bind的第一个参数在绑定函数中变成this。创建了绑定函数后,可以随时调用它:

[JavaScript] 纯文本查看 复制代码
const sayThis = _ => console.log(this)
const boundFunc = sayThis.bind({hippy: 'hipster'})
boundFunc()

通过bind改变this

传递给bind 的其他参数将作为参数传递给原始函数。

[JavaScript] 纯文本查看 复制代码
const sayParams = (...args) => console.log(...args)
const boundFunc = sayParams.bind(null, 1, 2, 3, 4, 5)
boundFunc()

传递给bind的其他参数变成函数中的arguments

这就是你需要知道的关于bind的全部内容。

现在,让我们回顾一下移除事件监听器的代码,并剖析发生了什么:

[JavaScript] 纯文本查看 复制代码
function LeetSpeaker (elem) {
return {
listenClick () {
// Binds this.speakLeet with a reference to the instance.
// Sets bound function to this.listener, so we can remove it later.
this.listener = this.speakLeet.bind(this)
elem.addEventListener('click', this.listener)
},

speakLeet(e) {
console.log(`1337 15 4W350M3`)

// Gets the element so we can remove the event listener.
const elem = e.currentTarget

// Removes the event listener.
elem.removeEventListener('click', this.listener)
}
}
}


总结:

this是JavaScript中的关键词。它出现在许多JavaScript框架中,所以您必须知道它是做什么的。

在这篇文章中,您学习了六种不同的上下文,其中this具有不同的值。您还学会了如何用bind之类的函数来改变这个“上下文”。此外,您还学会了正确地删除事件侦听器。

这就是你需要知道的。只要掌握这篇文章所教的概念,你就不会再感到困惑了。


2 个回复

倒序浏览
厉害了,赞一个
回复 使用道具 举报
优秀,奈斯
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马