JavaScript中的this:一个"朝三暮四"的渣男指南

大家好,今天我们来聊聊 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指向特定对象,可以用callapplybind

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的底层魔法

  1. 创建一个空对象{}
  2. 把这个对象的__proto__指向构造函数的prototype
  3. this绑定到这个新对象
  4. 执行构造函数代码
  5. 如果构造函数没有返回对象,就返回这个新对象

箭头函数------佛系青年的禅修

因为箭头函数没有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即函数athis,又因为函数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

终极绑定规则优先级

当多种规则同时适用时,优先级如下:

  1. new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
  2. 箭头函数的this不可被修改,优先级最高

记住这个顺口溜:

new 出来的最优先,

call/apply 靠边站,

对象调用排第三,

默认绑定最后面,

箭头函数最佛系,

外层 this 它继承。

掌握了这些规则,你就能准确预判这个"渣男"在各种情况下的行为了!下次面试被问到this时,记得露出神秘的微笑,然后娓娓道来~

五、JavaScript中的this:一个"渣男"的自我修养总结

这个善变的this就像个情场高手,在不同场合切换着不同的"人设":单身时(默认绑定)赖在 window 家不走;被对象"包养"时(隐式绑定)就认钱不认人;遇到强势的 call/apply/bind (显式绑定)立马从良;被 new 召唤时( new 绑定)又变身造物主;而箭头函数则是个佛系青年,永远继承外层this的"家产"。记住它的四大绑定法则和优先级:new > 显式 > 隐式 > 默认,箭头函数永远随缘。下次面试被问到时,请优雅地抛出这个顺口溜,让面试官为你的"渣男"研究功力折服!毕竟,理解 this 的关键不在于它定义在哪,而在于它是如何被调用的------就像判断一个渣男,不要看他说什么,要看他怎么做!

相关推荐
前端呆猿28 分钟前
小程序性能优化全攻略:提升用户体验的关键策略
前端·性能优化·小程序
y东施效颦1 小时前
uni-app 配置华为离线推送流程
前端·vue.js·uni-app
老神在在0014 小时前
SpringMVC1
java·前端·学习·spring
萌萌哒草头将军7 小时前
🚀🚀🚀React Router 现在支持 SRC 了!!!
javascript·react.js·preact
薛定谔的算法7 小时前
# 从0到1构建React项目:一个仓库展示应用的架构实践
前端·react.js
Tina学编程8 小时前
HTML基础P1 | HTML基本元素
服务器·前端·html
一只小风华~9 小时前
Web前端:JavaScript和CSS实现的基础登录验证功能
前端
90后的晨仔9 小时前
Vue Router 入门指南:从零开始实现前端路由管理
前端·vue.js
LotteChar9 小时前
WebStorm vs VSCode:前端圈的「豆腐脑甜咸之争」
前端·vscode·webstorm
90后的晨仔9 小时前
零基础快速搭建 Vue 3 开发环境(附官方推荐方法)
前端·vue.js