JavaScript 中 this 指向教程

1. this 指向的分析

基本概念

this 是 JavaScript 中的一个关键字,它的指向取决于函数的调用方式,而不是函数定义的位置。

代码示例

javascript 复制代码
// 定义函数
function foo() {
    console.log('foo函数', this);
}

// 方式一:直接使用
// foo();

// 方式二:通过对象调用
const obj = { name: 'meishiduoshuijiao' }
obj.aaa = foo

obj.aaa() // this -> obj 

关键点

  • 同一个函数在不同的调用方式下,this 的指向是不同的

  • 直接调用函数时,this 指向全局对象(浏览器中是 window

  • 通过对象调用函数时,this 指向该对象


2. this 绑定规则一:默认绑定

什么是默认绑定?

当函数独立调用时(没有任何修饰),会应用默认绑定规则,此时 this 指向全局对象(在浏览器中是 window,在 Node.js 中是 global)。

代码示例

javascript 复制代码
// 1.普通函数被独立的调用
function foo() {
    console.log("foo", this);
}

foo() // this -> window

// 2.函数定义在对象中,但是独立调用
var obj = {
    name: "msdsj",
    bar: function() {
        console.log("bar", this)
    }
}

var baz = obj.bar
baz()  // this -> window,因为是独立调用

// 3.高阶函数
function test(fn) {
    fn() // 独立调用
}

test(obj.bar) // this -> window

严格模式

在严格模式下("use strict"),独立函数调用时,this 指向 undefined,而不是全局对象。

javascript 复制代码
"use strict"
function foo() {
    console.log(this) // undefined
}
foo()

关键点

  • 独立函数调用时应用默认绑定

  • 即使函数定义在对象中,只要是独立调用,this 仍然指向全局对象

  • 通过高阶函数传递的函数,如果在内部独立调用,this 也指向全局对象

  • 严格模式下,默认绑定的 thisundefined


3. this 绑定规则二:隐式绑定

什么是隐式绑定?

当函数作为对象的方法被调用时,this 会隐式地绑定到该对象上。

代码示例

javascript 复制代码
// 隐式绑定
function foo() {
    console.log("foo", this)
}

var message = "hello world"

var obj = {
    bar: foo
}

obj.bar() // this -> obj

关键点

  • 当函数通过对象引用来调用时(如 obj.method()),this 绑定到这个对象

  • 调用位置使用对象的上下文来引用函数,所以 this 就是这个对象

  • 隐式绑定可能会丢失(见默认绑定的第二个例子)


4. this 绑定规则三:new 绑定

什么是 new 绑定?

使用 new 关键字调用函数时,会创建一个新对象,并将 this 绑定到这个新对象上。

new 关键字的执行过程

  1. 创建一个新的空对象

  2. this 指向这个空对象

  3. 执行函数体中的代码

  4. 如果函数没有显式返回非空对象,则默认返回这个新对象

代码示例

javascript 复制代码
function foo() {
    this.name = "msdsj"
    console.log("foo函数", this)
}

foo() // this -> window

new foo() // this -> 新创建的对象

关键点

  • 使用 new 调用函数时,会创建一个新对象

  • 这个新对象会绑定到函数调用的 this

  • 如果函数没有返回其他对象,new 表达式会自动返回这个新对象

  • 构造函数通常首字母大写(约定俗成)


5. this 绑定规则四:显式绑定

什么是显式绑定?

通过 call()apply()bind() 方法,可以显式地指定函数调用时 this 的指向。

代码示例

javascript 复制代码
var obj = {
    name: "msdsj"
}

function foo() {
    console.log("foo函数", this)
}

// 执行函数,并且函数中的this指向obj对象
// 方式一:通过对象调用(隐式绑定)
// obj.foo = foo
// obj.foo()

// 方式二:通过call显式绑定
foo.call(obj) // this -> obj
foo.call(123) // this -> Number {123}
foo.call("abc") // this -> String {"abc"}

关键点

  • call()apply() 可以在调用函数时显式指定 this 的值

  • 传入基本类型会被包装成对应的包装对象

  • 显式绑定比隐式绑定的优先级更高


6. 额外函数补充:apply 和 call

call 和 apply 的区别

两者功能相同,都是显式绑定 this,区别在于传参方式:

  • call(thisArg, arg1, arg2, ...):参数逐个列举

  • apply(thisArg, [argsArray]):参数以数组形式传递

代码示例

javascript 复制代码
function foo(name, age, height) {
    console.log("foo函数被调用", this)
    console.log("打印参数", name, age, height)
}

foo("msdsj", 18, 1.88) // 普通调用

// apply:第一个参数绑定this,第二个参数以数组形式传入
foo.apply("apply", ["msdsj", 18, 1.98])

// call:第一个参数绑定this,后续参数逐个传递
foo.call("call", "msdsjsleep", 18, 2.00)

使用场景

  • 当参数已经是数组形式时,使用 apply 更方便

  • 当参数需要逐个传递时,使用 call 更直观

  • 可以用于借用其他对象的方法


7. 额外函数补充:bind

什么是 bind?

bind() 方法创建一个新函数,这个新函数的 this 被永久绑定到 bind() 的第一个参数。

bind 与 call/apply 的区别

  • callapply 会立即执行函数

  • bind 返回一个新函数,不会立即执行

代码示例

javascript 复制代码
function foo(name, age, height, address) {
    console.log("foo", this)
    console.log("参数", name, age, height, address)
}

var obj = { name: "msdsj" }

// 1. bind函数的基本使用
var bar = foo.bind(obj)
bar() // this -> obj

// 2. bind函数的其他参数(预设参数/柯里化)
var bar = foo.bind(obj, "msdsj", 18, 1.80)
bar("江西省") // 前三个参数已预设,只需传入第四个参数

关键点

  • bind() 返回一个新函数,原函数不受影响

  • 可以预设部分参数(函数柯里化)

  • 一旦绑定,this 不可再被改变(即使使用 callapply


8. 内置函数的调用绑定

常见内置函数的 this 绑定

JavaScript 的许多内置函数都有自己的 this 绑定规则。

代码示例

javascript 复制代码
// 1. setTimeout
setTimeout(function() {
    console.log("定时器函数", this) // window
}, 1000)

// 2. DOM 事件监听
var btnEl = document.querySelector("button")
btnEl.onclick = function() {
    console.log("btn的点击:", this) // 指向按钮元素
}

// 3. 数组方法(forEach、map 等)
var names = ["arr1", "arr2", "arr3"]
names.forEach(function(item) {
    console.log("foreach", this)
}, "aaa") // 第二个参数可以指定this,否则为window/undefined

常见内置函数的 this 规则

函数类型 this 指向 说明
setTimeout/setInterval 全局对象 独立函数调用
DOM 事件监听器 触发事件的元素 浏览器内部绑定
forEach/map/filter 可选参数指定 第二个参数可指定 this
addEventListener 事件目标元素 浏览器自动绑定

关键点

  • 了解常用内置函数的 this 指向规则

  • 某些数组方法提供了可选参数来指定 this

  • 使用箭头函数可以避免 this 绑定的问题


9. this 绑定规则优先级

优先级排序

当多个绑定规则同时出现时,按以下优先级判断(从高到低):

  1. new 绑定 > 显式绑定

  2. 显式绑定(bind) > 显式绑定(call/apply)

  3. 显式绑定 > 隐式绑定

  4. 隐式绑定 > 默认绑定

代码示例

1. 显式绑定高于隐式绑定
javascript 复制代码
function foo() {
    console.log("foo", this)
}

var obj = { foo }
obj.foo.apply("abc") // this -> "abc",显式绑定优先

var bar = foo.bind("aaa")
var obj = {
    name: "msdsj",
    baz: bar
}
obj.baz() // this -> "aaa",bind的优先级更高
2. new 绑定高于隐式绑定'
javascript 复制代码
var obj = {
    name: "msdsj",
    foo: function() {
        console.log("foo", this)
    }
}

new obj.foo() // this -> 新创建的对象,而不是obj
3. new 绑定高于 bind
javascript 复制代码
function foo() {
    console.log("foo", this)
}

var bindFn = foo.bind("aaa")
new bindFn() // this -> 新创建的对象,而不是"aaa"

注意:new 不能和 call/apply 一起使用(语法错误)

4. bind 高于 apply/call
javascript 复制代码
function foo() {
    console.log("foo:", this)
}

var bindFn = foo.bind("aaa")
bindFn.apply("bbb") // this -> "aaa",bind后无法改变

优先级记忆口诀

new 最大,bind 次之,显式再次,隐式最末,独立最小


10. this 绑定之外的情况

特殊情况

有些情况下,this 的绑定会出现意想不到的结果。

代码示例

1. 传入 null 或 undefined
javascript 复制代码
function foo() {
    console.log("foo", this)
}

foo.apply("abc") // this -> String {"abc"}
foo.apply(null) // this -> window (非严格模式)
foo.apply(undefined) // this -> window (非严格模式)

在严格模式下:

javascript 复制代码
"use strict"
function foo() {
    console.log("foo", this)
}

foo.apply(null) // this -> null
foo.apply(undefined) // this -> undefined
2. 间接函数引用
javascript 复制代码
var obj1 = {
    name: "obj1",
    foo: function() {
        console.log("foo", this)
    }
}

var obj2 = {
    name: "obj2"
};

obj2.foo = obj1.foo;
obj2.foo() // this -> obj2

(obj2.foo = obj1.foo)() // this -> window
// 赋值表达式的返回值是函数本身,相当于独立函数调用

关键点

  • 传入 nullundefined 时,非严格模式下会绑定到全局对象

  • 赋值表达式 (obj2.foo = obj1.foo) 的结果是函数本身,调用时是独立调用

  • 这些特殊情况在实际开发中要注意避免


11. 箭头函数中的 this

箭头函数的特性

箭头函数不绑定 this,它会捕获其所在上下文的 this 值,作为自己的 this 值。

核心特点

  1. 箭头函数没有自己的 this

  2. this 由外层作用域决定

  3. 无法通过 call、apply、bind 改变 this

  4. 在定义时就确定了 this,而不是调用时

代码示例

1. 箭头函数没有自己的 this
javascript 复制代码
// 箭头函数会去上层作用域找this,也就是全局对象window
var bar = () => {
    console.log("bar", this) // window
}
bar()
2. 无法通过 apply 改变 this
javascript 复制代码
var bar = () => {
    console.log("bar", this) // 仍然是window
}
bar.apply("aaa")
3. this 的查找规则
javascript 复制代码
var obj = {
    name: "aaa",
    message: "hello world",
    foo: function() {
        // 普通函数,this指向obj
        var bar = () => {
            // 箭头函数,this从外层作用域(foo)继承
            console.log("bar", this) // this指向obj对象
        }
        return bar
    }
}

var fn = obj.foo()
fn.apply("bbb") // this仍然指向obj,无法改变

使用场景

✅ 适合使用箭头函数
  • 回调函数(如数组方法、定时器)

  • 需要保持外层 this 的场景

  • 简短的函数表达式

javascript 复制代码
// 示例:数组方法中
const obj = {
    name: 'test',
    arr: [1, 2, 3],
    print: function() {
        this.arr.forEach(item => {
            console.log(this.name, item) // 可以访问到obj.name
        })
    }
}

// 示例:定时器中
const person = {
    name: 'John',
    sayHi: function() {
        setTimeout(() => {
            console.log('Hi, ' + this.name) // 可以访问到person.name
        }, 1000)
    }
}
❌ 不适合使用箭头函数
  • 对象的方法

  • 需要动态 this 的场景

  • 构造函数

  • 需要使用 arguments 对象的函数

javascript 复制代码
// 错误示例:对象方法
const obj = {
    name: 'test',
    sayHi: () => {
        console.log(this.name) // this不指向obj
    }
}

// 正确示例:使用普通函数
const obj = {
    name: 'test',
    sayHi: function() {
        console.log(this.name) // this指向obj
    }
}

箭头函数总结

特性 普通函数 箭头函数
this 绑定 动态绑定,取决于调用方式 静态绑定,继承外层作用域
可否用 new 可以 不可以
arguments 没有
可否改变 this 可以(call/apply/bind) 不可以
适用场景 对象方法、构造函数 回调函数、保持外层this

总结

this 指向判断流程图

javascript 复制代码
判断函数调用时的this指向:

1. 是否用 new 调用?
   └─ 是:this 指向新创建的对象

2. 是否用 call/apply/bind 调用?
   └─ 是:this 指向指定的对象

3. 是否通过对象调用(obj.method())?
   └─ 是:this 指向该对象

4. 是否是箭头函数?
   └─ 是:this 继承外层作用域的this

5. 以上都不是(独立函数调用)
   └─ 非严格模式:this 指向 window
   └─ 严格模式:this 是 undefined

核心要点

  1. this 是在运行时绑定的,不是编写时绑定

  2. this 的绑定只取决于函数的调用方式

  3. 四大绑定规则:默认绑定、隐式绑定、显式绑定、new 绑定

  4. 优先级:new > bind > call/apply > 隐式 > 默认

  5. 箭头函数:不绑定 this,继承外层作用域

相关推荐
Apifox43 分钟前
如何通过抓包工具快速生成 Apifox 接口文档?
前端·后端·测试
A charmer43 分钟前
内存泄漏、死锁:定位排查工具+解决方案(C/C++ 实战指南)
c语言·开发语言·c++
苏打水com1 小时前
浏览器与HTTP核心考点全解析(字节高频)
前端·http
用户99045017780091 小时前
ruoyi集成camunda-前端篇
前端
wjs20241 小时前
HTML 基础
开发语言
Aerelin1 小时前
scrapy的介绍与使用
前端·爬虫·python·scrapy·js
BD_Marathon1 小时前
【JavaWeb】前端三大件——HTML简介
前端·html
pilaf19901 小时前
Rust练习题
开发语言·后端·rust
asdfg12589631 小时前
replace(/,/g, ‘‘);/\B(?=(\d{3})+(?!\d))/;千分位分隔
开发语言·前端·javascript