This的四种绑定规则

本文主要介绍JavaScript中this的绑定规则。this在运行时绑定,取决于函数调用方式。包含默认、隐式、显式和new四种绑定,各绑定有优先级,new绑定最高,默认绑定最低。还提及绑定的例外情况,以及ES6箭头函数根据外层作用域决定this。

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

(一)四种绑定

this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调
用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
this是动态作用域,javascript变量函数使用是词法作用域!!!

1.默认绑定

this没有绑定指向时,默认绑定window。

function foo() {
	console.log( this.a );
}
var a = 2;
foo(); // 2
function foo(){
    console.log(this === window);
}
foo(); //true
function foo(num) {
	console.log( "foo: " + num );
	// 记录 foo 被调用的次数
	this.count++;
}
foo.count = 0;
var i;
for (i=0; i<10; i++) {
	if (i > 5) {
		foo( i );
	}
}
console.log( foo.count );//0
console.log( count );//非严格模式下,默认创建的undefined类型的全局变量,执行完++运算后变为NaN.
//在严格模式下直接报错ReferenceError

2.隐式绑定

隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。
例:使用对象调用函数进行绑定( obj.fun() ) ,执行obj.fun(),obj就是上下文环境。
!!隐式绑定可能丢失

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

隐式丢失
虽然 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"

3.显式绑定

使用 apply call bind 进行强制绑定。

function foo() {
	console.log( this.a );
}
var obj = {
	a:2
};
var a = 3;
foo.call( obj ); // 2
  • 硬绑定
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

//硬绑定的典型场景
//创建一个包裹函数,传入所有的参数并返回接收到的所有值
function foo(something) {
	console.log( this.a, something );
	return this.a + something;
}
var obj = {
	a:2
};
var bar = function() {
	return foo.apply( obj, arguments );
};
var b = bar( 3 ); // 2 3
console.log( b ); // 5

//系统内置
//由于硬绑定是一种非常常用的模式,所以在 ES5 中提供了内置的方法 Function.prototype.bind,它的用法如下:
function foo(something) {
	console.log( this.a, something );
	return this.a + something;
}
var obj = {
	a:2
};
var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3
console.log( b ); // 5
  • javascript内置函数的显式绑定
//forEach
function foo(el) {
	console.log( el, this.id );
}
var obj = {
	id: "awesome"
};
// 调用 foo(..) 时把 this 绑定到 obj
[1, 2, 3].forEach( foo, obj );
// 1 awesome 2 awesome 3 awesome

4.new绑定

使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

  1. 创建(或者说构造)一个全新的对象。
  2. 这个新对象会被执行 [[ 原型 ]] 连接。
  3. 这个新对象会绑定到函数调用的 this。
  4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
function foo(a) {
	this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2

(二)各个绑定的优先级

1.探索

//1.显式绑定和隐式绑定的优先级?
function foo() {
	console.log( this.a );
}
var obj1 = {
	a: 2,
	foo: foo
};
var obj2 = {
	a: 3,
	foo: foo
};
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call( obj2 ); // 3 可以看到显式绑定将隐式绑定的this给更改了
obj2.foo.call( obj1 ); // 2 可以看到显式绑定将隐式绑定的this给更改了
//隐式绑定和new绑定的优先级?
function foo(something) {
	this.a = something;
}
var obj1 = {
	foo: foo
};
obj1.foo( 2 );
console.log( obj1.a ); // 2
var bar = new obj1.foo( 4 );
console.log( obj1.a ); // 2
console.log( bar.a ); // 4 new绑定后创建了一个新对象对this进行绑定,new的优先级更高
//显式绑定和new绑定的优先级?
function foo(something) {
	this.a = something;
}
var obj1 = {};
var bar = foo.bind( obj1 );
bar( 2 );
console.log( obj1.a ); // 2
var baz = new bar(3);
console.log( obj1.a ); // 2
console.log( baz.a ); // 3 new能将this绑定到自身新建的对象上,new的优先级更高

2.结论

this绑定优先级:new绑定>显式绑定>隐式绑定>默认绑定

(三)绑定的例外情况

  • apply,call,bind上下文环境传入null,转为默认绑定
/**DMZ 对象比null更加安全,不会去创建全局对象而污染全局环境**/
function foo(a,b) {
	console.log( "a:" + a + ", b:" + b );
}
//DMZ 空对象
//Object.create(null) 和 {} 很 像, 但 是 并 不 会 创 建 Object.prototype 这个委托,所以它比 {}“更空”
var ø = Object.create( null );
// 把数组展开成参数
foo.apply( ø, [2, 3] ); // a:2, b:3
// 使用 bind(..) 进行柯里化
var bar = foo.bind( ø, 2 );
bar( 3 ); // a:2, b:3
console.log(a);//Uncaught ReferenceError: a is not defined
  • 间接引用
    对于默认绑定来说,决定 this 绑定对象的并不是调用位置是否处于严格模式,而是
    函数体是否处于严格模式。如果函数体处于严格模式,this 会被绑定到 undefined,否则
    this 会被绑定到全局对象。
function foo() {
	console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2 仅仅是把o.foo传递给p.foo,并不会把o对象传入
  • 软绑定

它会对指定的函数进行封装,首先检查调用时的 this,如果 this 绑定到全局对象或者 undefined,那就把指定的默认对象 obj 绑定到 this,否则不会修改 this。

//自定义原型链
if (!Function.prototype.softBind) {
	Function.prototype.softBind = function(obj) {
		var fn = this;
		// 捕获所有 curried 参数
		var curried = [].slice.call( arguments, 1 );
		var bound = function() {
			return fn.apply((!this || this === (window || global)) ? obj : this,
			curried.concat.apply( curried, arguments ));
		};
		bound.prototype = Object.create( fn.prototype );
		return bound;
	};
}
//应用原型链
function foo() {
	console.log("name: " + this.name);
}
var obj = { name: "obj" }, obj2 = { name: "obj2" }, obj3 = { name: "obj3" };
var fooOBJ = foo.softBind( obj );
fooOBJ(); // name:obj,进入方法后this为window <<==>> foo.apply(true? obj : this, {name:"obj"});
obj2.foo = foo.softBind(obj);
obj2.foo(); // name:obj2,进入方法后this为obj2 <<==>> foo.apply(false ? obj : this, {name:"obj"});
fooOBJ.call( obj3 ); // name:obj3,进入方法后this为obj3 <<==>> foo.apply(false ? obj : this, {name:"obj"});
setTimeout( obj2.foo, 10 );// name:obj <-- 应用了软绑定
//进入方法后this为window <<==>> foo.apply(true ? obj : this, {name:"obj"});

(四)ES6箭头函数

箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)作用域来决
定 this。

function foo() {
	// 返回一个箭头函数
	return (a) => {
		//this 继承自 foo()
		console.log( this.a );
	};
}
var obj1 = {
	a:2
};
var obj2 = {
	a:3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是 3 !
/**foo() 内部创建的箭头函数会捕获调用时 foo() 的 this。由于 foo() 的 this 绑定到 obj1,
bar(引用箭头函数)的 this 也会绑定到 obj1,箭头函数的绑定无法被修改。(new 也不
行!)**/

箭头函数最常用于回调函数中,例如事件处理器或者定时器:

function foo() {
	setTimeout(() => {
		// 这里的 this 在此法上继承自 foo()
		console.log( this.a );
	},100);
}
var obj = {
	a:2
};
foo.call( obj ); // 2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Funnee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值