JavaScript中this指向规则及call与apply的区别详解

作者:异步漫步

时间:2026-10-29

浏览:328

本文详细解析JavaScript中this关键字的四种指向场景:对象方法调用、普通函数调用、构造器调用及call/apply调用。重点对比call与apply在参数传递上的差异,并通过代码示例演示如何利用它们改变this指向及借用数组方法,帮助开发者掌握核心机制。

文章目录

引言与参考资料

学习背景:

在早期的JavaScript开发中,this、call与apply机制常令人困惑,但其应用频率极高。为此,我专门投入一天时间深入研读并厘清这三者的逻辑。

参考资源涵盖《JavaScript设计模式与开发实践》(主)、《JavaScript高级程序设计》及《你不知道的JavaScript》(辅)。这三部著作对构建this、call、apply的知识体系至关重要。

JavaScript中this的指向规则

this关键字解析

本节首先聚焦于this关键字的行为机制。

依据《JavaScript设计模式与开发实践》的观点,理解this的关键在于把握其核心定义。

JavaScript中的this始终指向一个特定的对象。 在实际运行环境中,this的指向行为可归纳为以下四种场景:

  1. 场景一:作为对象的方法被调用
  2. 场景二:作为普通函数被调用
  3. 场景三:作为构造函数被调用
  4. 场景四:通过apply或call方法调用

下文将逐一剖析前三种场景,第四种场景(apply与call)将在后续章节详细展开。

1. 作为对象的方法调用

规则:当方法属于某个对象时,this指向该对象实例。 示例代码:


/**
 * 1.作为对象的方法调用
 *
 * 作为对象方法调用时,this指向该对象。
 */

var obj = {
 a: 1,
 getA: function() {
  console.log(this === obj);
  console.log(this.a);
 }
};

obj.getA(); // true , 1

2. 作为普通函数调用

规则:当函数独立运行时,this默认指向全局对象(在浏览器环境中为window)。 示例代码:


/**
 * 2.作为普通函数调用
 *
 * 不作为对象属性调用时,this必须指向一个对象。那就是全局对象。
 */

window.name = 'globalName';

var getName = function() {
 console.log(this.name);
};

getName(); // 'globalName'

var myObject = {
 name: "ObjectName",
 getName: function() {
  console.log(this.name)
 }
};

myObject.getName(); // 'ObjectName'

// 这里实质上是把function() {console.log(this.name)}
// 这句话赋值给了theName。thisName在全局对象中调用,自然读取的是全局对象的name值
var theName = myObject.getName;

theName(); // 'globalName'

3. 作为构造器调用

规则:当使用new关键字调用构造函数时,this指向新创建的实例对象。 示例代码:


/**
 * 3.作为构造器调用
 * 
 * 作为构造器调用时,this指向返回的这个对象。
 */

var myClass = function() {
 this.name = "Lxxyx";
};

var obj = new myClass();

console.log(obj.name); // Lxxyx
console.log(obj) // myClass {name: "Lxxyx"}

例外情况:若构造函数内部显式return一个对象,则this的指向失效,返回该对象。 若return非对象类型的数据,则正常返回新实例。


var myClass = function() {
 this.name = "Lxxyx";
 // 加入return时,则返回的是别的对象。this不起作用。
 return {
  name:"ReturnOthers"
 }
};

var obj = new myClass();
console.log(obj.name); // ReturnOthers

call与apply方法详解

4. Call与Apply机制

Call与Apply功能一致,均用于强制指定函数执行时this的指向。

Call与Apply的差异对比

Call:首个参数指定this指向,后续参数需逐个传入。 Apply:首个参数指定this指向,第二个参数为数组,用于批量传入所有参数。

若首个参数传null或undefined,this将指向调用上下文本身(在非严格模式下指向全局对象)。

1. 改变this指向

说明:这是call和apply最核心的应用场景,用于动态切换函数内部的this绑定。 示例代码:


var name = "GlobalName"

var func = function() {
 console.log(this.name)
};

func(); // "GlobalName"

var obj = {
 name: "Lxxyx",
 getName: function() {
  console.log(this.name)
 }
};

obj.getName.apply(window) // "GlobalName" 将this指向window
func.apply(obj) // "Lxxyx" 将this指向obj

2. 借用其他对象的方法

此处以立即执行函数(IIFE)作为引入:


(function(a, b) {
 console.log(arguments) // 1,2
 // 调用Array的原型方法
 Array.prototype.push.call(arguments, 3);
 console.log(arguments) // 1,2,3
})(1,2)

函数内部存在arguments属性,其类型为类数组对象。 由于类数组对象无法直接调用数组原型方法,需借助call或apply调用Array原型方法。 其原理直观:以push方法为例,在V8引擎源码中实现如下:


function ArrayPush() {
 var n = TO_UINT32(this.length); // 被push对象的长度
 var m = % _ArgumentsLength(); // push的参数个数
 for (var i = 0; i < m; i++) {
  this[i + n] = % _Arguments(i); // 复制元素
 }
 this.length = n + m; //修正length属性
 return this.length;
}

该方法仅依赖this指针,因此任何类数组对象均可通过此方式调用相关数组方法进行处理。

此部分逻辑较为复杂,加之作者自身水平限制,建议有条件者购买相关专著,或关注后续博客更新。

学习总结

通过本章节学习,深化了对JavaScript核心机制的理解。最显著的进步在于阅读优秀框架源码时,不再因this、call、apply及bind的交织使用而感到困惑,过程充满成就感。

下一阶段计划:深入探索日常开发中常用的CSS技术。鉴于JavaScript基础已夯实,HTML与CSS的同步提升同样不可或缺。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章