面试官:既然您熟悉JS,那您能简单说说JS中的this指向问题吗?
我:好的,正常浏览器环境中,this是指向window对象,node环境中指向global对象,对象中,谁调用它,this就指向了谁,箭头函数中,指向外层对象,大概是这样。
面试官:那继续详细说说?
我:这还能怎么详细?已经够详细了。
面试官:嗯,似乎也确实,一般确实没必要考的更深了。
正文
JavaScript 中的 “this” 是一个令人迷惑但又十分重要的概念。
它的行为可能会随着代码的不同而变化,因此深入理解 “this” 对于成为一名优秀的 JavaScript 开发者至关重要。
本文将带您深入探讨 “this” 的工作原理以及如何在实际应用中正确使用它。
基础概念
在 JavaScript 中,”this” 表示当前执行代码的对象。
但是,它的具体指向取决于代码的上下文。
在全局作用域下,”this” 指向全局对象(在浏览器中通常是 “window” 对象)。在函数内部,”this” 的值取决于函数的调用方式。
为什么要有this
为了让对象中的函数有能力访问对象自己的属性,this可以显著的提升代码质量,减少上下文参数的传递,例如:
1 | const obj = { |
- 第一二段代码对比,为了让对象中的函数有能力访问对象自己的属性。
- 第二三段代码对比,this可以显著的提升代码质量,减少上下文参数的传递。
全局上下文下的 “this”
在全局作用域下,”this” 指向全局对象。
这意味着当在全局作用域下调用 “this” 时,它将指向 “window” 对象(或 在node环境中,指向”global” 对象,取决于执行环境)。
1 | console.log(this === window); // 输出 true |
用了严格模式 “use strict”,严格模式下无法再意外创建全局变量,所以 this 不为 window 而为 undefined。
注意:babel 转成 ES6 的,babel 会自动给 js 文件上加上严格模式。
1 |
|
函数上下文中的 “this”
在函数内部,”this” 的值取决于函数的调用方式。
如果函数是作为对象的方法调用的,则 “this” 将指向调用该方法的对象。
这就是那句经典的回答:“谁调用了它,它就指向谁”。
1 | const obj = { |
如果函数中的 this 是被上一级的对象所调用的,那么 this 指向的就是上一级的对象,如下示例。
1 | const animal = { |
构造函数中的 “this”
在构造函数中,”this” 用于引用将要创建的对象实例。
当使用 “new” 关键字调用构造函数时,”this” 将指向新创建的对象。
1 | function Person(name) { |
箭头函数中的 “this”
与常规函数不同,箭头函数中的 “this” 指向定义时的外部上下文,而不是调用时的上下文。
这使得箭头函数在访问外部作用域的 “this” 值时非常方便。
严格模式对箭头函数没有效果。
1 |
|
说明:箭头函数没有this这个机制,写在箭头函数中的this也是它外层非箭头函数的this
this 的绑定规则
在 JavaScript 中,”this” 的值是在函数执行时确定的,根据调用函数的方式不同,”this” 可以绑定到不同的值上。
下面是几种常见的绑定规则:
1. 默认绑定
当一个函数独立调用时,并且没有任何修饰符,”this” 将默认绑定到全局对象(在浏览器中通常是 “window” 对象)上。
1 | function greet() { |
在全局,通过var
声明的变量相当于是在window
对象上添加了一个属性,即 window.name
,因此window.name == this.name
。
2. 隐式绑定
当函数被作为对象的方法调用时,”this” 将隐式绑定到调用该方法的对象上。
1 | const obj = { |
3. 隐式丢失
隐式丢失:当一个函数被多个对象链式调用时,函数的this指向就近的那个对象(就近原则)
1 | const obj1 = { |
这里,根据就近原则,输出Bob
4. 显示绑定
通过调用函数的 call、apply 或 bind 方法,可以显示地指定函数内部的 “this” 值。
- call 方法
call
方法允许您显式调用函数,并且可以指定函数内部的 this
值。
它接受一个参数列表,在调用函数时,将传递给函数作为参数使用。
1 | function greet(greeting) { |
在上面的示例中,我们使用 call
方法将函数 greet
的 this
绑定到对象 obj
上,并且传递了一个额外的参数 "Hello"
。
- apply 方法
apply
方法与 call
方法类似,但是它接受一个参数数组而不是参数列表。
这意味着您可以将参数作为数组传递给函数。
1 | function greet(greeting, punctuation) { |
在这个示例中,我们使用 apply
方法将函数 greet
的 this
绑定到对象 obj
上,并传递了一个包含两个参数的数组 ["Hi", "!"]
。
- bind 方法
bind
方法创建一个新的函数,并将指定的 this
值绑定到函数内部。
与 call
和 apply
不同,bind
并不会立即调用函数,而是返回一个新的函数,您可以稍后调用它。
1 | function greet(greeting) { |
在上述示例中,我们使用 bind
方法创建了一个新的函数 boundGreet
,并将其 this
绑定到对象 obj
上。
然后,我们可以随时调用 boundGreet
函数,它将使用预先绑定的 this
值。
上述三个方法也可不传参数使用。
5. new 绑定
当一个函数被使用 “new” 关键字调用时,它会创建一个新的对象,并将该对象绑定到函数内部的 “this” 上。
1 | function Person(name) { |
结语
“this” 是 JavaScript 中一个重要且经常被误解的概念。
通过深入理解 “this” 的工作原理,并根据不同的上下文正确使用它,您可以写出更加清晰、健壮的 JavaScript 代码。
希望本文能够帮助您更好地理解 “this”,并在实际项目中更加灵活地运用它。