全面解析this-理解this指向的原理

参考资料

  • 《你不知道的JavaScript》- this全面解析

this 是什么

  • this 是一个代词,代指一个对象
  • this 提供了一种更优雅的方式来隐式的传递一个对象引用,可以让代码更加简洁易于复用

调用位置

调用位置就是函数在代码中被调用的位置 (而不是声明的位置)。调用位置决定了 this 的绑定

比如下面代码:

js 复制代码
function baz() {
	// 当前调用栈是:baz
	// 因此,当前调用位置是全局作用域
	console.log("baz");
	bar(); // <-- bar 的调用位置
}
function bar() {
	// 当前调用栈是 baz -> bar
	// 因此,当前调用位置在 baz 中
	console.log("bar");
	foo(); // <-- foo 的调用位置
}
function foo() {
	// 当前调用栈是 baz -> bar -> foo // 因此,当前调用位置在 bar 中
	console.log("foo");
}
baz(); // <-- baz 的调用位置

可以使用浏览器的开发工具查看调用栈

this的绑定规则

this的指向有以下四条特性/规则

  • 默认绑定
  • 隐式绑定
  • 显式绑定
  • new 绑定

默认绑定

当函数被独立调用时,函数的 this 指向 window

独立调用就是像这样:foo() 的调用

比如

js 复制代码
function foo() {
	console.log(this.a);
}
var a = 2;
foo(); // 2

如果使用严格模式 (strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定到 undefined

隐式绑定

当函数引用有上下文对象 且被该对象调用时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象

js 复制代码
function foo() {
	console.log(this.a);
}
var obj = {
  a: 2,
  foo: foo
};
obj.foo(); // 2
// 这里就是隐式绑定,foo函数的this绑定到了obj上

需要注意的是,对象属性引用链中只有最顶层或者说最后一层会影响调用位置

比如

js 复制代码
function foo() {
	console.log(this.a);
}
var obj2 = {
	a: 42,
	foo: foo,
};
var obj1 = {
	a: 2,
	obj2: obj2,
};
obj1.obj2.foo(); // 42
// 相当于 obj2.foo(),因为只有最后一层会影响调用位置

隐式丢失

隐式绑定的函数可能会丢失绑定对象,也就是说它会应用默认绑定

隐式丢失的几种情况:

  1. 函数别名
js 复制代码
function foo() {
	console.log(this.a);
}
var obj = {
	a: 2,
	foo: foo,
};

// 注意这里的函数别名,会导致隐式绑定丢失,导致foo函数的this指向全局
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"
  1. 函数作为参数传入,并调用时
js 复制代码
function foo() {
	console.log(this.a);
}
function doFoo(fn) {
	// fn 其实引用的是 foo
	fn(); // <-- 调用位置!
}
var obj = {
	a: 2,
	foo: foo,
};
var a = "oops, global"; // a 是全局对象的属性
doFoo(obj.foo); // "oops, global"

参数传递其实就是一种隐式赋值 ,因此我们传入函数时也会被隐式赋值,所以结果和上一个例子一样

显式绑定

  • fn.call(obj, x, x, ...) 将fn函数的this指向obj,并调用,call的剩余参数是fn需要的参数
javascript 复制代码
function foo(aram) {
	console.log("foo", param.a); // foo 1

	function bar(x, y) {
		console.log("bar", this.a, x, y); // bar 1 2 3
	}
	bar.call(param, 2, 3);
}

foo({ a: 1 });
  • fn.apply(obj, [x, x, ...]) 将fn函数的this指向obj,并调用,apply的第二个参数是一个数组
javascript 复制代码
function foo(param) {
	console.log("foo", param.a); // foo 1

	function bar(x, y) {
		console.log("bar", this.a, x, y); // bar 1 2 3
	}
	bar.apply(param, [2, 3]);
}

foo({ a: 1 });
  • fn.bind(obj, x, x, ...)(x, x, ...) 将fn函数的this指向obj,bind会返回一个新的函数,新的函数也可以传递参数
javascript 复制代码
function foo(param) {
	console.log("foo", param.a); // foo 1

	function bar(x, y) {
		console.log("bar", this.a, x, y); // bar 1 2 3
	}
	return bar.bind(param, 2);
}

const bar = foo({ a: 1 });
bar(3);

new 绑定

new 绑定 - this会绑定到新创建的对象上

javascript 复制代码
function Person(name, age) {
  // this 指向新创建的对象
  this.name = name;
  this.age = age;

  // 通过new调用构造函数时,this指向新创建的对象
	// 直接调用构造函数时,应用默认绑定规则,this指向全局或undefined
  console.log(this);
}

// 使用 new 关键字调用构造函数
const person1 = new Person("张三", 25);
console.log(person1.name); // "张三"
console.log(person1.age);  // 25

// 不使用 new 调用,this 会指向全局对象(非严格模式)或 undefined(严格模式)
const person2 = Person("李四", 30); // this 不会指向新对象
console.log(person2); // undefined (因为没有显式返回)

箭头函数

  1. 箭头函数没有自己的this 指向,它需要继承外层函数的this指向
  2. 箭头函数即使是new也无法改变this指向,因此箭头函数不能用于编写构造函数
js 复制代码
var a = 1;
function foo() {
	var obj1 = {
		a: 2,
		bar: function () {
			console.log("bar", this.a);
			var obj2 = {
				a: 3,
				baz: () => {
					console.log("baz", this.a);
				},
			};
			// 箭头函数不会创建自己的 this,它会捕获外层函数的 this
			obj2.baz(); // baz 2
		},
	};
	console.log("foo", this.a);
	obj1.bar(); // bar 2
}
foo(); // foo 1
相关推荐
郭老二1 天前
【前端】vue3:编译步骤
前端
不在逃避q1 天前
Trae/Vs Code/Cursor命令行无法跑npm命令
前端·arcgis·npm
夏殇之殁1 天前
包中创建自定义列表项。 . 使用自定义列表项进行数据绑定 . 将天气预报数据保存到本地内存表,通过LiveBindings进行显示。 ...
服务器·前端·javascript
The Chosen One9851 天前
高进度算法模板速记(待完善)
java·前端·算法
陈随易1 天前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
新中地GIS开发老师1 天前
WebGIS开发学生作品|低空航天管理与航线规划系统
前端·javascript·webgis·三维gis开发
用户059540174461 天前
大模型对话记忆持久化踩坑实录:用 Playwright 自动化测了 300 次,终于揪出会话丢失的真凶
前端·css
丙氨酸長鏈1 天前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
kyriewen1 天前
面试官让我手写虚拟列表——AI生成的版本,快速滚动几下就白屏了
前端·javascript·面试
IT_陈寒1 天前
Vite热更新失效?你可能漏了这个配置
前端·人工智能·后端