大家好,今天我们来聊聊 JavaScript 中最让人又爱又恨的关键字------this
。它就像个善变的渣男,在不同的场合会变成不同的样子,让你捉摸不透。不过别担心,看完这篇指南,你就能轻松驾驭这个"渣男"了!
一、为什么需要this?------避免"传宗接代"的烦恼
想象一下,如果没有this
,我们的代码会变成什么样:
JavaScript
function identify(context){
return context.name.toUpperCase()
}
function speak(context){
var greating = 'hello , i am'+identify(context)
console.log(greating);
}
var me = {
name:'tom'
}
speak(me)
以上代码定义了 identify
和 speak
两个函数,我想正确的输出i am TOM
需要通过显示的传入content
这个上下文对象,虽然语言依然可以运行,但代码会变得臃肿、不优雅。这就像每次约会都要带着户口本证明"我是我",多麻烦啊!this
的出现就是为了解决这个问题:
JavaScript
function identify(){
return this.name.toUpperCase()
}
function speek(){
var greating = 'hello , i am'+identify.call(this)
console.log(greating);
}
var me = {
name:'tom'
}
speek.call(me)
this
就像个贴心的管家,自动帮你把当前对象准备好,让你的代码更加清爽。
二、this的"活动范围"------它能在哪里浪?
既然this
这么有用,那它到底能在哪些地方"浪"呢?让我们来看看这个"渣男"的活动范围:
1. 全局作用域------window是它的老家
在全局作用域中,this
就像个没出息的宅男,永远指向window
(浏览器环境)或global
(Node环境):
JavaScript
console.log(this === window) // 在浏览器中输出true
this.globalVar = '我在window上安家了'
console.log(window.globalVar) // "我在window上安家了"
这就像this
在老家 window 上盖了个房子,谁都能通过 window 找到它。
2. 函数作用域------它的"变脸"舞台
函数体内才是this
真正的表演舞台,它会根据调用方式上演"变脸"绝活:
javascript
function showThis() {
console.log(this)
}
// 1. 默认绑定:单身狗模式
showThis() // 浏览器中输出window
// 2. 隐式绑定:有主模式
const obj = { showThis: showThis }
obj.showThis() // 输出obj对象
// 3. 显式绑定:强扭的瓜模式
showThis.call({ name: '被强扭的瓜' }) // 输出{ name: '被强扭的瓜' }
// 4. new绑定:造物主模式
new showThis() // 输出新创建的对象
三、this的"变心"法则------四大绑定规则详解
现在,让我们深入探讨这个"渣男"的四大变心法则,看看它到底是如何在不同场合切换对象的!
1. 默认绑定------单身狗的自我修养
当函数孤独终老(独立调用)时,this
就会进入"单身狗"模式:
JavaScript
var a = 1
function foo() {
console.log(this.a)
}
foo() // 单身狗调用
输出结果
1
这里foo()
是直接调用,没有主人,所以this
指向全局对象window,找到了全局变量a
。
严格模式下的"社恐"表现
javascript
'use strict'
var a = 1
function foo() {
console.log(this)
}
foo()
输出结果
undefined
严格模式下单身狗连 window 都不认了,在严格模式下('use strict'
),单身狗模式的this
会变成undefined
,而不是window。
2. 隐式绑定------谁调用就认谁做主人
当函数被某个对象"领养"时,this
就会指向它的主人:
javascript
var a = 1
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
obj.foo()
输出结果
2
obj
调用了foo
,所以 this
指向 obj
,所以输出结果为 2 。
隐式绑定的"就近原则"
javascript
var a = 1
function foo() {
console.log(this.a)
}
var obj1 = {
a: 2,
foo: foo
}
var obj2 = {
a: 3,
obj1: obj1
}
obj2.obj1.foo()
输出结果
2
obj2
调用了obj1
,obj1
又调用了foo
,此时 this
指向最近的 obj1
,所以此时输出结果为 2 。
隐式丢失------被抛弃的痛
javascript
var a = 1
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
var bar = obj.foo // 只是赋值引用,没有调用
bar() // 单身狗调用,this指向window
输出结果
1
因为 obj
只是赋值引用foo
,而没有直接调用,此时单身狗调用 foo
(独立调用),this
指向window,所以输出结果为 1 。
3. 显式绑定------强扭的瓜最甜
如果你非要让this
指向特定对象,可以用call
、apply
或bind
:
call/apply------立即执行版
JavaScript
function foo(x, y) {
console.log(this.a, x + y) // 1 3
}
var obj = { a: 1 }
foo.call(obj, 1, 2)
foo.apply(obj, [1, 2])
输出结果
1 3
1 3
fn.call(obj,x,x,...)
显示的将fn
里面的this
绑定到obj
上,call
负责帮fn
接收参数。fn.apply(obj,[x,x,...])
显示的将fn
里面的this
绑定到obj
上,apply
负责帮fn
接收参数,参数必须以数据组盛放。
bind------慢热型绑定
javascript
function foo(x, y) {
console.log(this.a, x + y) // 1 3
}
var obj = { a: 1 }
var bar = foo.bind(obj)
bar(1, 2)
输出结果
1 3
fn.bind(obj,x,x,...)(x,x,...)
显示的将fn
里面的this
绑定到obj
上,bind
会返回一个新的函数,bind
和新的函数都可以负责帮fn
接收参数,参数零散的传入。
4. new绑定------造物主的权力
使用new
调用函数时,this
会指向新创建的对象:
javascript
function Person() {
this.name = '小明'
this.age = 18
}
const p1 = new Person()
console.log(p1.name)
JavaScript
小明
new的底层魔法
- 创建一个空对象
{}
- 把这个对象的
__proto__
指向构造函数的prototype
- 把
this
绑定到这个新对象 - 执行构造函数代码
- 如果构造函数没有返回对象,就返回这个新对象
箭头函数------佛系青年的禅修
因为箭头函数没有this
关键字,所以箭头函数体内的this
就像个闭关的和尚,永远继承外层非箭头函数的this
:
javascript
var a = 1
var obj = {
a: 2,
bar: function() {
const baz = () => {
console.log(this.a)
}
baz()
}
}
obj.bar()
输出结果
2
多层箭头函数的"禅定"
javascript
function a(){
let b = function(){
let c =()=>{
let d =()=>{
console.log(this);
}
d()
}
c()
}
b()
}
a()
输出结果
Object [global]
此时函数体内的this
继承外层非箭头函数的this
即函数a
的this
,又因为函数a
被独立调用,所以此时this
指向调用函数a
的window。
四、面试高频问题解析
问题1:以下代码输出什么?
javascript
var a = 1
function foo() {
var a = 2
function bar() {
var a = 3
console.log(this.a)
}
bar()
}
foo() // 输出1
解析 :bar()
是独立调用,默认绑定到window,所以输出全局变量a
的值1。
问题2:以下代码输出什么?
javascript
css
var a = 1
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
var obj2 = {
a: 3,
foo: obj.foo
}
obj2.foo()
输出结果
3
解析 :虽然foo
最初定义在obj
中,但最后是obj2
调用了它,所以this
指向obj2
。
终极绑定规则优先级
当多种规则同时适用时,优先级如下:
- new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
- 箭头函数的
this
不可被修改,优先级最高
记住这个顺口溜:
new 出来的最优先,
call/apply 靠边站,
对象调用排第三,
默认绑定最后面,
箭头函数最佛系,
外层 this 它继承。
掌握了这些规则,你就能准确预判这个"渣男"在各种情况下的行为了!下次面试被问到this
时,记得露出神秘的微笑,然后娓娓道来~
五、JavaScript中的this:一个"渣男"的自我修养总结
这个善变的this
就像个情场高手,在不同场合切换着不同的"人设":单身时(默认绑定)赖在 window
家不走;被对象"包养"时(隐式绑定)就认钱不认人;遇到强势的 call/apply/bind
(显式绑定)立马从良;被 new
召唤时( new 绑定)又变身造物主;而箭头函数则是个佛系青年,永远继承外层this
的"家产"。记住它的四大绑定法则和优先级:new
> 显式 > 隐式 > 默认,箭头函数永远随缘。下次面试被问到时,请优雅地抛出这个顺口溜,让面试官为你的"渣男"研究功力折服!毕竟,理解 this
的关键不在于它定义在哪,而在于它是如何被调用的------就像判断一个渣男,不要看他说什么,要看他怎么做!