当前位置: 首页 > news >正文

JavaScript 中的 this 及 this 指向的改变方法

在 JavaScript 的世界里,this是一个既强大又容易让人困惑的概念。它的指向在不同的函数调用场景下会动态变化,而call()、apply()和bind()这三个方法则为我们提供了精确控制this指向的能力。本文将从基础概念出发,结合具体案例,带大家深入理解这些核心知识点。

一、JavaScript 中 this 的本质

1. 作为普通函数调用(独立调用)​

当函数作为独立函数调用时(即没有任何对象前缀),this指向全局对象(浏览器环境为window,Node 环境为global)。在严格模式下("use strict"),this会被绑定为undefined。

function sayHello() {console.log(this); // 普通调用时指向window(非严格模式)
}
sayHello(); // window

2. 作为对象方法调用​

当函数作为对象的方法被调用时(即通过对象。方法 () 的形式),this指向该对象本身:

const person = {name: 'Alice',greet: function() {console.log(`Hello, ${this.name}`); // 指向person对象}
};
person.greet(); // Hello, Alice

3. 作为构造函数调用​

使用new关键字调用函数时,会创建一个新对象,此时this指向这个新创建的实例对象:

function Person(name) {this.name = name; // 指向新创建的实例
}
const alice = new Person('Alice');
console.log(alice.name); // Alice

4. 箭头函数中的 this​

箭头函数不具备自身的this,它的this继承自外层作用域(词法作用域),且无法通过call()、apply()、bind()改变:

⑴.箭头函数的 this 继承外层作用域

箭头函数的 this 值在函数 定义时 就确定,继承自外层(词法作用域)的 this,而非运行时的调用上下文。

测试代码 1:全局作用域

// 全局作用域(非严格模式)
const globalArrow = () => {console.log(this); // 输出:window(浏览器环境)或 global(Node.js)
};globalArrow();// 严格模式下
(function() {"use strict";const strictArrow = () => {console.log(this); // 输出:undefined(因为外层严格模式的 this 是 undefined)};strictArrow();
})();

测试代码 2:对象方法中的 this

const obj = {name: '普通函数',regularMethod: function() {console.log(this.name); // 输出:"普通函数"(this 指向 obj)},arrowMethod: () => {console.log(this.name); // 输出:undefined(或全局对象的 name,如果存在)}
};obj.regularMethod();  // 普通函数:this 指向 obj
obj.arrowMethod();    // 箭头函数:this 继承自外层(全局或严格模式下的 undefined)

⑵.无法通过 bind()call()apply() 改变 this

箭头函数的 this固定 的,无法通过 bindcallapply 改变。

测试代码 3:尝试绑定 this

const arrowFunc = () => {console.log(this); // 始终继承定义时的 this
};const obj = { name: '尝试绑定' };// 尝试绑定 this 到 obj
const boundFunc = arrowFunc.bind(obj);
boundFunc(); // 输出:全局对象(或 undefined,取决于定义时的外层 this)// 使用 call/apply 也无效
arrowFunc.call(obj); // 输出:全局对象

⑶.构造函数中的箭头函数

箭头函数 不能作为构造函数 使用,因为没有自己的 this,且没有 prototype

测试代码 4:构造函数错误

// 错误示例:尝试用箭头函数作为构造函数
const Person = (name) => {this.name = name; // 报错!箭头函数没有 this
};new Person('Alice'); // 抛出错误:this is undefined

⑷.嵌套函数中的 this 传递

箭头函数的 this 继承自 外层最近的非箭头函数this

测试代码 5:嵌套函数中的 this

function outer() {this.message = '外层函数';const innerRegular = function() {console.log(this.message); // 输出:undefined(因为 innerRegular 的 this 默认指向全局或严格模式下的 undefined)};const innerArrow = () => {console.log(this.message); // 输出:"外层函数"(继承 outer 的 this)};innerRegular();   // 普通函数:this 未绑定innerArrow();     // 箭头函数:this 继承自 outer
}outer();

二、改变 this 指向的三种方法​

1. call () 方法 - 显式指定 this 并立即执行​

call() 方法是 JavaScript 中函数对象的一个内置方法,它允许你调用一个函数,并指定这个函数内部 this 的值,同时还可以传递参数给这个函数。

语法

function.call(thisArg, arg1, arg2, ...)
函数名.call(此值指向对象, 参数1, 参数2, …)
  • 函数名:调用 call 方法的函数。
  • 此值指向对象:调用函数时,函数内部 this 关键字所指向的值。若该参数为 null 或者 undefined,那么 this 会指向全局对象(在浏览器环境里是 window 对象)。
  • 参数 1, 参数 2, …:属于可选参数,是传递给函数的参数列表。

案例

案例一:改变 this 的指向

const person = {name: 'Alice',greet: function() {console.log(`Hello, my name is ${this.name}.`);}
};const anotherPerson = {name: 'Bob'
};// 使用 call 方法改变 greet 函数的 this 指向
person.greet.call(anotherPerson);

在这个例子中,person 对象有一个 greet 方法,该方法使用 this.name 来输出信息。通过 call 方法,我们将 greet 方法内部的 this 指向了 anotherPerson 对象,所以输出的是 Bob 的名字。

案例二:传递参数

function add(a, b) {return a + b;
}// 使用 call 方法调用 add 函数并传递参数
const result = add.call(null, 3, 5);
console.log(result);

在这个例子中,add 函数是一个简单的加法函数。由于 add 函数不依赖于 this,所以第一个参数可以传入 null。然后我们通过 call 方法传递了两个参数 3 和 5,最终得到结果 8

案例三:类数组对象使用数组方法

const arrayLike = {0: 'apple',1: 'banana',2: 'cherry',length: 3
};// 使用 call 方法让类数组对象使用数组的 forEach 方法
Array.prototype.forEach.call(arrayLike, function(item) {console.log(item);
});

在这个例子中,arrayLike 是一个类数组对象,它没有数组的方法。通过 call 方法,我们让 arrayLike 对象使用了数组的 forEach 方法,从而可以遍历它的元素。

2.apply () 方法

apply() 方法也是 JavaScript 中函数对象的一个内置方法,与 call() 方法类似,都可以用来调用函数并指定函数内部 this 的值,不同之处在于 apply() 方法接收参数的方式是一个数组或类数组对象。

语法

function.apply(thisArg, [argsArray])

参数解释

  • function:要调用 apply 方法的函数。
  • thisArg:在调用函数时,this 关键字会指向这个值。如果该参数为 null 或 undefined,在非严格模式下,this 会指向全局对象(在浏览器环境中是 window 对象);在严格模式下,this 的值为 null 或 undefined
  • argsArray:一个包含要传递给函数的参数的数组或类数组对象。如果不需要传递参数,可以传入一个空数组 []

案例

案例一:改变 this 的指向

const person = {name: 'Alice',greet: function() {console.log(`Hello, my name is ${this.name}.`);}
};const anotherPerson = {name: 'Bob'
};// 使用 apply 方法改变 greet 函数的 this 指向
person.greet.apply(anotherPerson);

在这个例子中,person 对象有一个 greet 方法,通过 apply 方法将 greet 方法内部的 this 指向了 anotherPerson 对象,所以输出的是 Bob 的名字。

案例二:传递参数

function add(a, b) {return a + b;
}// 使用 apply 方法调用 add 函数并传递参数
const result = add.apply(null, [3, 5]);
console.log(result); 

这里 add 函数是一个简单的加法函数,因为 add 函数不依赖于 this,所以第一个参数传入 null。通过 apply 方法传递了一个包含 3 和 5 的数组作为参数,最终得到结果 8

案例三:求数组中的最大值

const numbers = [5, 10, 3, 8, 15];// 使用 Math.max.apply 求数组中的最大值
const max = Math.max.apply(null, numbers);
console.log(max); 

Math.max() 函数用于求多个数中的最大值,但它接收的是多个独立的参数,而不是一个数组。通过 apply 方法,将 numbers 数组作为参数传递给 Math.max() 函数,就可以求出数组中的最大值 15

3. bind () 方法

bind()方法会创建一个新函数,当这个新函数被调用时,this会被绑定为bind()方法的第一个参数。

语法

function.bind(thisArg, arg1, arg2, ...)
函数名.bind(此值指向对象, 参数1, 参数2, …)

参数解释

  • 函数名:调用 bind 方法的原函数。
  • 此值指向对象:在调用新创建的函数时,函数内部 this 关键字会指向该值。若传入 null 或 undefined,在非严格模式下,this 指向全局对象(浏览器环境中是 window 对象);严格模式下,this 就是 null 或 undefined
  • 参数 1, 参数 2, …:可选参数,是在调用新创建的函数时预先传入的参数。

案例

案例一:改变 this 的指向

const person = {name: 'Alice',greet: function() {console.log(`Hello, my name is ${this.name}.`);}
};const anotherPerson = {name: 'Bob'
};// 使用 bind 方法创建一个新函数,改变 greet 函数的 this 指向
const newGreet = person.greet.bind(anotherPerson);
newGreet();

在这个例子里,person 对象有 greet 方法,借助 bind 方法创建了一个新函数 newGreet,并把 greet 方法内部的 this 指向了 anotherPerson 对象,调用 newGreet 时输出的就是 Bob 的名字。

案例二:预先传入参数

function multiply(a, b) {return a * b;
}// 使用 bind 方法创建一个新函数,预先传入第一个参数
const double = multiply.bind(null, 2);
console.log(double(5)); 

这里的 multiply 函数用于计算两个数的乘积。通过 bind 方法创建了新函数 double,预先传入参数 2 作为第一个参数。调用 double(5) 时,相当于调用 multiply(2, 5),最终结果是 10

案例三:在事件处理程序中使用

const button = document.createElement('button');
button.textContent = 'Click me';const obj = {message: 'Button clicked!',handleClick: function() {console.log(this.message);}
};// 使用 bind 方法确保 handleClick 中的 this 指向 obj
button.addEventListener('click', obj.handleClick.bind(obj));
document.body.appendChild(button);

在这个例子中,创建了一个按钮元素,obj 对象有 handleClick 方法。通过 bind 方法将 handleClick 方法的 this 指向 obj,这样在点击按钮触发事件时,就能正确输出 Button clicked!

4.bind() 与 call()apply() 的区别

  • call() 和 apply() 是直接调用函数,同时可以指定 this 值和参数。
  • bind() 并不直接调用函数,而是创建一个新的函数,这个新函数在被调用时,会使用指定的 this 值和预先传入的参数。

http://www.xdnf.cn/news/86959.html

相关文章:

  • 基于SpringBoot的中华诗词文化分享平台-项目分享
  • HarmonyOS:1.7
  • Cursor 设置规则
  • CentOS7 环境配置
  • metasploit(2)生成dll木马
  • 给vue-admin-template菜单栏 sidebar-item 添加消息提示
  • 嵌入式面试高频笔试题目解析
  • Vue3-全知识点
  • Java求多点位之间的共点
  • TockOS,一种新安全软件架构的RTOS介绍
  • C# 事件知识文档
  • Cyber SpaceGuidance网安学习指南见解
  • 零基础学Python——第八章:实战项目(1-3)
  • 46.[前端开发-JavaScript高级]Day11-生成器-async和await-事件循环-队列
  • YOLO算法的革命性升级:深度解析Repulsion损失函数在目标检测中的创新应用
  • 18.1基于Linux的INPUT子系统实验(知识)_csdn
  • A2A + MCP:构建实用人工智能系统的超强组合
  • 记录:扩展欧几里得算法
  • Spark2 之 memorypool
  • Lua 第7部分 输入输出
  • this._uid:Vue 内部为每个组件实例分配的唯一 ID
  • 基于DeepSeek的文献分析系统
  • 模型 螃蟹效应
  • 详解Windows(七)——更新管理
  • uView的u-modal不显示问题
  • 若依框架二次开发——若依 Vue3 版本前端样式优化指南
  • Spark-streaming(一)
  • 第 1.4 节: G1 人形机器人足球项目定义与课程路线
  • LSTM如何解决梯度消失问题
  • uv包管理器如何安装依赖?