目录
前言
在JavaScript中,this是理解函数上下文的关键。本文将深入剖析this的奥秘,揭示其在不同场景下的行为,文章最后会给出几道面试题,测试你的掌握能力。
环境说明准备
环境,我们就Node 环境,来讲我们下面的代码 大家可以直接用 node file
或者 nodemon file
来运行代码,推荐使用nodemon
避免重复启动, 如果还没有,可以使用下面命令进行安装
npm i -g nodemon
干货开始
node中this的坑
结论: node中的
this
跟globalThis
并不是同一个 对象 , node 中的this
指向的是module.exports
globalThis
是一个全局对象,它在所有 JavaScript 环境中都可用,用于引用全局作用域。在不同的 JavaScript 环境中,全局对象可能有不同的名称,例如:
- 在浏览器中,全局对象是
window
。- 在 Node.js 中,全局对象是
global
。- 在 Web Workers 中,全局对象是
self
。
console.log('全局this: ',this);
console.log('this 等于 module.exports :', this === module.exports);
console.log('全局globalThis:', globalThis);
this常用场景
全局中的this
console.log(this); //{}
this.num = 10;
console.log(this.num); //10
console.log(globalThis.num); //undefined
函数中的this
function fn(){
this.num = 10;
}
//执行函数
fn();
console.log(this); // {}
console.log(this.num); //undefined
console.log(globalThis.num); //10
构造函数中的this
function Fn(){
this.num = 998;
}
let fn = new Fn();
console.log(fn.num); //998
console.log(globalThis.num); //undefined
改变this的三种方式
this
关键字在 JavaScript 中用于指向函数执行的上下文对象。改变this
的值通常使用call
、apply
和bind
这三个方法。
-
call():
- 立即调用函数。
- 接受参数列表,参数逐个传递。
-
apply():
- 立即调用函数。
- 接受一个参数数组。
-
bind():
- 不立即调用函数,而是返回一个新的函数。
- 可以预先设定参数。
特性 | call() | apply() | bind() |
---|---|---|---|
调用方式 | 立即调用 | 立即调用 | 返回新函数 |
参数传递 | 逐个传递 | 数组形式传递 | 可以预设参数 |
返回值 | 函数的返回值 | 函数的返回值 | 新函数引用 |
用法示例 | func.call(obj, arg1, arg2) | func.apply(obj, [arg1, arg2]) | const newFunc = func.bind(obj, arg1, arg2) |
箭头函数
箭头函数是ECMAScript 6(ES6)中引入的一种新的函数定义方式,它提供了一种更简洁的语法来编写函数。主要有以下几个特点:
-
简洁的语法:箭头函数的语法比传统的函数表达式更简洁,不需要使用
function
关键字。 -
没有独立的
this
:箭头函数没有自己的this
上下文,它会捕获其所在上下文的this
值,作为自己的this
。 -
没有
arguments
对象:箭头函数中没有arguments
对象,如果需要访问函数的参数,可以使用剩余参数(rest parameters)语法。 -
不可以使用
yield
关键字:箭头函数不能用作生成器(generator)函数,即不能使用yield
关键字。 -
隐式返回:当箭头函数的函数体只有单个表达式时,可以省略花括号和
return
关键字,表达式的结果将被隐式返回。
// 传统函数表达式
var traditionalFunc = function(x) {
return x * 2;
};
// 箭头函数
const arrowFunc = (x) => x * 2;
// 隐式返回的箭头函数
const double = x => x * 2;
// 使用剩余参数的箭头函数
const sum = (...numbers) => numbers.reduce((acc, current) => acc + current, 0);
// 没有参数的箭头函数
const sayHello = () => console.log("Hello!");
// 使用对象字面量的箭头函数
const getPerson = () => ({ name: "Alice", age: 25 });
箭头函数 This
结论
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
- 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
- this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this
代码示例
对象箭头函数
let bob1 = {
_name: "Bob",
printFriends:() => {
//箭头函数根本没有自己的this,导致内部的this就是外层代码块的this
console.log(this); // {}
}
}
console.log(bob1.printFriends());
// 外部对象this的改变 ,感觉this被改变了,但是记住它是没有this的
let friends = ['jack']
let bob = {
_name: "Bob",
printFriends() {
let that = this
friends.forEach(f => {
console.log(this === that,'that');
console.log('is bob?',this === bob,);
console.log('is globalThis?',this === globalThis,);
console.log(this + " knows " + f)
});
}
};
console.log(bob.printFriends());
console.log('-----');
let printFriends = bob.printFriends
console.log(printFriends());
请问下面的代码之中有几个this?
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
面试题测试:
答案我在放评论区,鼓励你先在评论区写下答案在进行核对
-
题目: 以下函数调用中,
this
指向什么?function test() { return this; } const obj = { test: test }; console.log(test()); // ? console.log(obj.test()); // ?
-
题目: 使用
call
方法改变this
的指向,并调用函数。function sayName() { console.log("Name: " + this.name); } const person = { name: "John" }; sayName.call(person); // ?
-
题目: 使用
apply
方法实现与call
相同的效果。function sayAge(age) { console.log("Age: " + this.age); } const person = { age: 30 }; sayAge.apply(person, [30]); // ?
-
题目: 使用
bind
方法创建一个新函数,其this
指向特定对象。function greet(greeting) { console.log(greeting + ", " + this.user); } const user = { user: "Jane" }; const boundGreet = greet.bind(user, "Hello"); boundGreet(); // ?
-
题目: 箭头函数中的
this
是如何工作的?const obj = { name: "Alice", greet: () => { setTimeout(() => { console.log("Hi, " + this.name); }, 1000); } }; obj.greet(); // ?
-
题目: 构造函数中的
this
是如何工作的?function Person(name) { this.name = name; this.greet = function() { console.log("Hello, " + this.name); }; } const person = new Person("Bob"); person.greet(); // ?