源本科技 | 码上会

JavaScript this 关键字

2026/01/13
13
0

学习目标

  • 理解 this 在 JavaScript 中的动态绑定特性

  • 掌握 this 在不同调用上下文中的指向规则

  • 学会使用 callapplybind 显式控制 this 的值

  • 了解箭头函数中 this 的词法作用域行为

  • 能够准确预测和调试 this 相关的常见问题


this 基本概念

在 JavaScript 中,this 是一个动态关键字,它始终指向当前执行上下文中的对象。与许多编程语言不同,JavaScript 的 this 不是在定义时确定的,而是在函数被调用时决定的

const user = {
    name: "开发者社区",
    introduce() {
        return `欢迎来到 ${this.name}!`;
    }
};

console.log(user.introduce()); // 输出:欢迎来到 开发者社区!

在这个例子中,this 指向 user 对象,因此可以访问其 name 属性。


this 的主要绑定规则

1. 隐式绑定

当函数作为对象的方法被调用时,this 指向该对象。

const student = {
    name: "李明",
    grade: 10,
    getInfo() {
        console.log(`学生姓名:${this.name},年级:${this.grade}`);
    }
};

student.getInfo(); // 输出:学生姓名:李明,年级:10

规则:调用点左侧的对象决定了 this 的值

2. 默认绑定

当函数独立调用(不作为对象方法)时:

  • 非严格模式this 指向全局对象(浏览器中是 window,Node.js 中是 global

  • 严格模式thisundefined

// 非严格模式示例
var globalName = "全局变量";

function sayHello() {
    console.log(this.globalName); // 可能输出 "全局变量"
}

sayHello(); // 独立调用

// 严格模式示例
'use strict';
function strictFunc() {
    console.log(this); // undefined
}
strictFunc();

注意:在模块化或现代框架中,全局变量通常被避免,因此独立调用函数时 this 往往无法访问预期属性。

3. 显式绑定

通过 call()apply()bind() 手动指定 this 的值

使用 callapply

function checkEligibility() {
    if (this.age >= 18) {
        console.log(`${this.name} 可以考驾照`);
    } else {
        console.log(`${this.name} 还不能考驾照`);
    }
}

const person1 = { name: "王伟", age: 20 };
const person2 = { name: "刘芳", age: 16 };

checkEligibility.call(person1);   // 输出:王伟 可以考驾照
checkEligibility.call(person2);   // 输出:刘芳 还不能考驾照
  • call(obj, arg1, arg2, ...):参数逐个传递

  • apply(obj, [arg1, arg2, ...]):参数以数组形式传递

使用 bind

bind 返回一个新函数,其 this 被永久绑定:

const greet = function(greeting) {
    return `${greeting}, 我是 ${this.name}`;
};

const user = { name: "张涛" };
const boundGreet = greet.bind(user);

console.log(boundGreet("你好")); // 输出:你好, 我是 张涛

bind 常用于事件处理、回调函数等需要固定上下文的场景。

4. 箭头函数中的 this

箭头函数没有自己的 this,它会继承外层作用域的 this(词法作用域)。

const team = {
    name: "前端组",
    members: ["小李", "小王"],
    
    // 普通函数:this 指向 team
    listMembersNormal() {
        this.members.forEach(function(member) {
            // 这里的 this 指向全局对象(非严格模式)或 undefined(严格模式)
            console.log(`${member} 属于 ${this.name}`); // this.name 为 undefined
        });
    },
    
    // 箭头函数:this 继承自外层(即 team)
    listMembersArrow() {
        this.members.forEach((member) => {
            // this 仍指向 team
            console.log(`${member} 属于 ${this.name}`); // 正确输出
        });
    }
};

team.listMembersArrow(); 
// 输出:
// 小李 属于 前端组
// 小王 属于 前端组

箭头函数适用于不需要动态 this 的场景,如回调、工具函数等。

5. 构造函数中的 this

当函数通过 new 调用时,this 指向新创建的实例对象

function Product(name, price) {
    this.name = name;
    this.price = price;
    this.describe = function() {
        return `${this.name} 售价 ¥${this.price}`;
    };
}

const laptop = new Product("笔记本电脑", 5999);
console.log(laptop.describe()); // 输出:笔记本电脑 售价 ¥5999

在 ES6 类中同样适用:

class Book {
    constructor(title, author) {
        this.title = title;
        this.author = author;
    }
    getInfo() {
        return `"${this.title}" 作者:${this.author}`;
    }
}

const book = new Book("深入浅出 Node.js", "朴灵");
console.log(book.getInfo()); // 输出:"深入浅出 Node.js" 作者:朴灵

注意事项

1. 方法赋值导致 this 丢失

const obj = {
    value: 42,
    getValue() {
        return this.value;
    }
};

const fn = obj.getValue; // 仅复制函数引用
console.log(fn()); // undefined(因为 this 指向全局)

解决方案:使用 bind 绑定上下文

const safeFn = obj.getValue.bind(obj);
console.log(safeFn()); // 42

2. 回调函数中的 this 问题

const timer = {
    seconds: 0,
    start() {
        setInterval(function() {
            this.seconds++; // this 指向全局对象
            console.log(this.seconds);
        }, 1000);
    }
};

修复方式:

  • 使用箭头函数:

    start() {
        setInterval(() => {
            this.seconds++;
            console.log(this.seconds);
        }, 1000);
    }
  • 或保存 this 引用:

    start() {
        const self = this;
        setInterval(function() {
            self.seconds++;
            console.log(self.seconds);
        }, 1000);
    }

this 绑定的优先级

当多种规则同时存在时,JavaScript 按以下优先级确定 this

  1. new 绑定(最高)

  2. 显式绑定call / apply / bind

  3. 隐式绑定(对象方法调用)

  4. 默认绑定(最低)

function foo() {
    console.log(this.name);
}

const obj1 = { name: "对象1", foo: foo };
const obj2 = { name: "对象2" };

// 显式绑定优先于隐式绑定
obj1.foo.call(obj2); // 输出:对象2

重点总结

  • this 的值由函数调用方式决定,而非定义位置

  • 对象方法调用 → this 指向该对象(隐式绑定)

  • 独立函数调用 → this 指向全局对象或 undefined(默认绑定)

  • 使用 call/apply/bind 可强制指定 this(显式绑定)

  • 箭头函数没有自己的 this,继承外层作用域(词法绑定)

  • 构造函数中 this 指向新创建的实例

  • 理解绑定优先级有助于解决复杂场景下的 this 问题


思考题

  1. 为什么在严格模式下,独立调用的函数中 thisundefined?这种设计有什么好处?

  2. 编写一个通用的 logThis 函数,无论以何种方式调用,都能正确打印当前 this 的值,并解释其在不同调用方式下的输出结果。