网站首页 > 技术文章 正文
JavaScript 中的 this 关键字在不同情况下指的是什么,很多时候会有点复杂。幸运的是,有五个规则可以用来确定 this。尽管有些情况下这些规则不适用,但应该可以在绝大多数情况下解决我们的困惑。所以,让我们开始吧!
1. this通常由函数的 执行上下文 决定。执行上下文是指一个函数是 如何 被调用的。
2. 很重要的是一个函数每次被调用时,函数中的 this可能会指向不同的东西。
3. 如果现在还不能理解上面两点的含义,没有关系,本文结尾时在总结里还会提到。
1 全局对象
现在让我们在实践中学习。 打开 Chrome 开发者控制台(Windows:Ctrl + Shift + J)(Mac:Cmd + Option + J)并键入以下内容:
console.log(this);
我们得到了什么
// Window {...}
window对象!这是因为在全局范围内,this是指全局对象。在浏览器中,全局对象是 window对象。
为了帮助我们更好地理解为什么 this 是 window 对象,我们来深入了解一下。在控制台中,创建一个新变量并命名为 myName:
var myName = 'Brandon';
我们可以通过调用 myName 再次访问这个变量:
myName
但是你知道在全局范围内声明的每个变量都附加在“window”对象上吗?让我们测试一下:
window.myName
window.myName === myName
所以当我们在全局环境中运行 console.log(this) 的时候,我们知道这个 this指向全局对象。 由于浏览器中的全局对象是“window”对象,所以这样的输出结果是有道理的:
console.log(this)
现在让我们把这个 this 放在一个函数里。回想一下我们以前的定义:this 的值通常由函数 如何 被调用而确定。考虑到这一点,你期望这个函数返回什么?在浏览器控制台中,复制下面的代码,然后按回车键。
function test() {
关键字 this 再次指向了全局(window)对象。这是因为关键字 this不在一个已声明的对象内,因此默认指向全局(window)对象。这个概念现在可能有点难以理解,但随着进一步阅读理解会更加深入。有一件事要注意,如果使用严格模式,在上面的例子中,这个 this 将是 undefined。
2 已声明的对象
当关键字 this 在已声明的对象内部使用时,this 指向离被调用的方法 最近的父对象。 看看下面的代码,声明了对象 person,并在方法 full 中使用 this。
var person = {
person.full();
为了更好地说明 this 实际上是引用该对象的,请将以下代码复制到浏览器控制台中。它与上面的代码大致相同,只是执行了 console.log(this) ,所以我们可以看它到底返回什么。
var person = {
正如所看到的,控制台返回了 person 对象,证明 this 已经指向了 person。
还记得刚刚说过 this 指向离被调用方法 最近的父对象 吗?在我们有嵌套对象的情况下会发生什么?看下面的示例代码。我们有一个 person 对象,包含与之前相同的 first,last 和 full键。这一次,我们还嵌套了一个 personTwo 对象。personTwo 包含相同的三个键。
var person = {
当我们分别调用两个 full方法时会发生什么?我们来看看吧。
person.full();
person.personTwo.full();this 指向离被调用方法 最近的父对象 。当 person.full() 被调用时,函数里的 this 被绑定到person 对象。同时,当 person.personTwo.full() 被调用时,在 full 函数内,this 被绑定到personTwo 对象!
3 New 关键字
当使用 new关键字(一个构造函数)时,this 绑定到正在创建的那个新对象。
让我们来看一个例子:
function Car(make, model) {
你可能会猜测 this应该绑定到全局对象 - 如果我们不用关键字 new 的话,你就是正确的。 当我们使用 new 时,this 的值会指向一个空对象,在本例中是 myCar。
var myCar = new Car('Ford', 'Escape');
console.log(myCar);
为了理解以上的例子,你需要了解什么是 new 关键字,但这其实是另一个全新的话题。 所以现在,如果你不确定,就看到关键字 new,只要知道 this 指向的是一个全新的空白对象就可以了。
4 Call、Bind、Apply
最后,但同样很重要的是,我们可以使用 call(),bind() 和 apply() 来明确地设置 this 的值。 这三个是非常相似的,但重要的是了解其中的小区别。call()和 apply() 都会立即被调用。call()可以接受任意数量的参数:this 和之后其他参数。apply() 只需要两个参数:this 和一个包含附加参数的数组。
有木有跟上我的思路?下面这个例子应该会解释得更清楚。看下面的代码,我们正在尝试对几个数求和。将其复制到浏览器控制台并调用该函数。
function add(c, d) {
add(3,4);
add 函数输出 NaN(不是一个数字)。这是因为 this.a 和 this.b 未定义。他们不存在 而且不能将数字喝未定义的东西求和。
让我们添加一个新的对象。我们可以使用 call() 和 apply() 在我们的对象的作用域中调用该函数:
function add(c, d) {
var ten = {a: 1, b: 2};
add.call(ten, 3, 4);
add.apply(ten, [3,4]);
当我们使用 add.call() 时,第一个参数表示 this 被绑定到该参数。随后的参数被传递到我们调用的函数中。因此,在 add() 中,this.a 指的是 ten.a,this.b 指的是 ten.b,我们得到 1 + 2 + 3 + 4 的返回结果,也就是 10。
add.apply() 是类似的。第一个参数表示 this 被绑定到该参数。 后续的参数是要在函数中使用的参数组成的数组。bind() 又是什么呢?bind() 中的参数与 call() 相同,但是 bind() 不会立即被调用。相反,bind() 返回一个上下文 this 已经绑定为第一个参数的函数。因此,当我们最开始不知道运行时需要的所有参数时,就可以用 bind()。下面这个例子应该会解释得更清楚:
var small = {
var large = {
将上述内容复制到控制台。 然后调用:
small.go(2,3,4);
这里没什么新鲜的。但是,如果我们想要直接使用 large.a的值呢?我们可以使用 call()/apply():
small.go.call(large,2,3,4);
现在,如果我们不知道所有 3 个参数呢?我们可以使用 bind()
var bindTest = small.go.bind(large,2);
如果我们在控制台输出我们上面的变量 bindTest,我们可以看到真相
console.log(bindTest);
记住,bind 会返回一个已经 this 已经被绑定的函数!所以我们的 this 已经成功地绑定到我们的 large 对象上。我们也已经传递了第二个参数 2。 后来,当知道其它参数后时,我们可以接着将它们传递进函数:
bindTest(3,4);
为了更加清晰,这里将所有的代码放在一个代码块中。通看一遍,并将其复制到控制台,去真正了解发生了什么!
var small = {
5 箭头函数
这本身就是一个很大的话题,因此我写了一篇文章: 为初学者介绍箭头函数
总结
为自己鼓掌吧!在大多数情况下,你现在应该可以推断出 this 是指向什么了!记住以下几件事:
1.this 通常由函数的 执行上下文 决定。
2.在全局范围内,this 指的是全局对象(window 对象)。
3.当使用 new 关键字(一个构造函数)时,this 绑定到正在创建的新对象。
4.我们可以用 call(),bind() 和 apply() 来明确设置 this 的值。
5.箭头函数不影响 this —— this 的绑定由函数的 执行上下文 决定(即基于原始上下文)。
猜你喜欢
- 2024-10-16 JS中(a==1 && a==2 && a==3)可以在JavaScript中计算为“true”吗?
- 2024-10-16 JavaScript 的这个难点,毁掉了多少程序员
- 2024-10-16 前端开发之彻底搞懂this指向(前端this指向问题)
- 2024-10-16 JavaScript中的变量声明和作用域(一)
- 2024-10-16 Top 26 JavaScript面试问题和答案
- 2024-10-16 JS 经典实例知识点整理汇总【实践】
- 2024-10-16 苦恼于JavaScript中的reduce函数?五分钟讲透彻
- 2024-10-16 前端基础:JavaScript(前端基础题)
- 2024-10-16 一句话彻底理解JS中的回调(Callback)函数
- 2024-10-16 面试中被问到最多的 19 个 JavaScript 问题
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)