【JavaScript】this那些事

本文详细阐述了JavaScript中函数的this绑定规则,包括箭头函数的特殊性、默认绑定、隐式绑定、隐式丢失及显示绑定。重点讨论了如何在不同场景下理解和运用this,特别是硬绑定的概念,以及bind()方法的使用。通过对各种绑定形式的分析,有助于深入理解JavaScript中的作用域和对象上下文。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、箭头函数中的this

标准函数中this引用的是把函数当成方法调用的上下文对象。但是在箭头函数中this引用的是定义箭头函数的上下文,或者理解为它的父级作用域中的this。箭头函数中的this会保留定义该函数时的上下文

//标准函数中
window.color = 'red';
let o = {
	color:'blue';
};

function sayColor() {
	console.log(this.color);
}
sayColor();  //'red'  
//直接调用sayColor()函数,这里this指向window,this.color = window.color

o.sayColor = sayColor;  
//把sayColor赋值给o,相当于给o增加一个sayColor属性,属性为sayColor()函数方法

o.sayColor();  //'blue'  
//这里this指向对象o  this.color = o.color
//箭头函数中
window.color = 'red';
let o = {
	color:'blue';
};

let sayColor = () => console.log(this.color);  
//箭头函数在window上下文定义,this会保留为window上下文

sayColor();  //'red' 在window下调用

o.sayColor = sayColor;  
o.sayColor();  //'red'  
//this仍保留为定义箭头函数时的window上下文

二、 this的默认绑定

当一个函数没有明显调用对象的时候,即单纯作为独立函数调用的时候,将对函数的this使用默认绑定:绑定到全局的window对象。可以把这条规则看作无法应用其他规则时的默认规则。
凡是函数作为独立函数调用,无论它的位置在哪里,它的行为表现,都和直接在全局环境中调用无异

function foo() {
	console.log( this.a );
}
var a = 2;
foo(); // 2  
//foo()直接使用不带任何修饰的函数进行调用,所以只能使用默认绑定

注意:如果使用严格模式,那么全局对象将无法使用默认绑定,因此 this 会绑定到undefined。

三、隐式绑定

函数被一个对象“包含”的时候,我们称函数的this被隐式绑定到这个对象里面了。

隐式绑定下,作为对象属性的函数,对对象来说是独立的;定义在对象内部的函数只是**“恰好可以被这个对象调用**”而已。不应该认为这个函数“生来就是为了这个对象所调用的”

函数虽然被定义在对象的内部中,但它和“在对象外部声明函数,然后在对象内部通过属性名称的方式取得函数的引用”,这两种方式在性质上是等价的而不仅仅是效果上

function foo() {
	console.log( this.a );
}
var obj = {
	a: 2,
	foo: foo
};
obj.foo(); // 2 
//无论是直接在 obj 中定义还是先定义再添加为引用属性,这个函数严格来说都不属于 obj 对象

三、隐式丢失

被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 this 绑定到全局对象(或者 undefined 上,取决于是否是严格模式)。

function foo() {
	console.log( this.a );
}
var obj = {
	a: 2,
	foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"

注意!!这里,var bar = obj.foo; 虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()是一个不带任何修饰的函数调用,所以应用默认绑定。

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"

参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值

四、显示绑定

我们可以使用call(…)和apply(…)方法,在某个对象上强制调用函数

call的基本使用方式: fn.call(object,(参数1,参数2…))
fn是要调用的函数,object是this所绑定的对象。

注意:通过call()向函数传参时,必须把参数一个一个列出来

apply的基本使用方式:fn.apply(object,([Array]))
fn是要调用的函数,object是this所绑定的对象。第二个参数:可选,可以是Array的实例

call() 和 apply() 区别:call() 方法分别接受参数。apply() 方法接受数组形式的参数。如果要使用数组而不是参数列表,则 apply() 方法非常方便。

function foo() {
	console.log( this.a );
}
var obj = {
	a:2
};
foo.call( obj );  //2

使用call()和apply()的好处是可以将任意对象设置为任意函数的作用域,这样对象可以不用关心方法。但是,显示绑定仍然无法解决丢失绑定的问题。

但是显示绑定到一个变种——硬绑定可以解决这个问题

function foo() {
	console.log( this.a );
}
var obj = {
	a:2
};
var bar = function() {
	foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬绑定的 bar 不可能再修改它的 this
bar.call( window ); // 2

我们创建了函数bar(),并在它的内部手动调用了foo.call(obj),因此强制把 foo 的 this 绑定到了obj。无论之后如何调用函数 bar,它总会手动在 obj 上调用 foo。这种绑定是一种显式的强制绑定,因此我们称之为硬绑定。

出于同样目的,ES5中新定义了一个内置方法 bind(),bind()方法会创建一个新的函数实例,其this值会被绑定到传给bind()的对象

window.color = 'red';
var o = {
	color:'blue'
};

function sayColor() {
	console.log(this.color);
}
let objectSayColor = sayColor.bind(o);
objectSayColor();  //'blue'

在sayColor()上调用bind()并传入对象o创建了一个新函数objectSayColor()。objectSayColor()中的this值被设置为o。所以直接调用这个函数,即使是在全局作用域中调用,也会返回’blue’。

本文参考资料:
《你不知道的JavaScript(上卷)》
《JavaScript高级程序设计(第四版)》
函数中的this的四种绑定形式 — 大家准备好瓜子,我要讲故事啦~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值