JavaScript中的this ——《你不知道的JavaScript上》

this到底是什么

this是在运行时进行绑定的,不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明位置没有任何关系,只取决于函数的调用方式
当一个函数被调用时,会创建一个活动记录(执行上下文),这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在函数执行的过程中用到。

调用位置

寻找函数的调用位置最重要的是分析调用栈(为了到达当前执行位置所调用的所有函数)。调用位置就在当前正在之前的函数的前一个调用中。调用位置决定了 this 的绑定对象。

function a() {
	// 当前调用栈是:a
	// 因此,当前调用位置是全局作用域
	console.log('a');
	b(); // <-- b的调用位置
}

function b() {
	// 当前调用栈是a -> b
	// 因此,当前调用位置是在a中
	console.log('b');
	c(); // <-- c的调用位置
}

function c() {
	// 当前调用栈是 c -> b -> a
	// 因此,当前调用位置在b中
	console.log('c');
}

a(); // <-- a的调用位置
绑定规则
  1. 默认绑定
function f() {
	console.log(this.a);
}
var a = 2;
f(); // 2

声明在全局作用域中的变量a就是全局对象的一个同名属性,我们调用f()的时候,因为函数调用时应用了this的默认绑定,因此this.a被解析成了全局变量athis指向了全局对象
!!!如果使用严格模式,全局对象无法使用默认绑定,this会绑定到undefined

  1. 隐式绑定
    需要考虑的另外一条规则是:调用位置是否有上下文对象,或者是否被某个对象拥有或者包含。
function f() {
	console.log(this.a);
}
var obj = {
	a: 2,
	f: f
};
obj.f(); // 2

函数被调用时obj对象“拥有”f函数。
当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象,因为调用f()this被绑定到obj,所以this.aobj.a是一样的。

对象属性引用链中只有最后一层会影响调用位置:

function f() {
	console.log(this.a);
}
var obj2 = {
	a: 42,
	f: f
};
var obj1 = {
	a: 2,
	obj2: obj2
};
obj1.obj2.f(); // 42
  • 隐式丢失
    一个常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,会用默认绑定,导致把this绑定到全局对象或者undefined上,取决于是否是严格模式
function f() { 
	console.log( this.a );
}
var obj = { 
	a: 2,
	f: f
};
var bar = obj.f; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性 
bar(); // "oops, global" 其实是一个不带任何修饰的函数调用,应用了默认绑定
  1. 显示绑定
    隐式绑定必须在对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,把this 间接绑定到这个对象上。
function foo() {
	console.log(this.a);
}
var obj = {
	a: 2
}
var a = 666;
foo(); // 666
foo.call(obj); // 2	调用foo时强制把它的this绑定到obj上
  • 硬绑定
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

使用bind

function foo(something) {
	console.log(this.a, something);
	return this.a + something;
}
var obj = {
	a: 2
};
var bar = foo.bing(obj);
var b = bar(3); // 2 3
console.log(b); // 5

bind(..)会返回一个硬编码的新函数,它会把参数设置为this的上下文并调用原始函数。
4. new绑定
JavaScript中的“构造函数”只是一些使用new 操作符时被调用的函数,并不属于某个类,也不会实例化一个类,只是被new操作符调用的普通函数。
包括内置对象函数在内的所有函数都可以用new来调用,这种函数调用被称为构造函数调用:实际上并不存在“构造函数”,只有对于函数的“构造调用
使用new来调用函数,或着说发送构造函数调用时,会自动执行以下操作:

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

使用new调用foo(..)时,会构造一个新对象并绑定到foo(..)调用中的this上。new是最后一种可以影响函数调用时this绑定行为的方法。

优先级
  1. 函数是否在new中调用(new绑定)?如果是this绑定的是新创建的对象
var bar = new foo();
  1. 函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是,this绑定的是指定的对象。
var bar = foo.call(obj2);
  1. 函数是否在某个上下文对象中调用(隐式绑定)?如果是,this绑定的是那个上下文对象。
var bar = obj1.foo();
  1. 如果都不是,使用默认绑定,如果在严格模式下,就绑定到undefined,否则就绑定全局对象
var bar = foo();
结论
  1. new调用?绑定到新创建的对象。
  2. callapply(或bind)调用?绑定到指定对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:严格模式下绑定undefined,否则绑定到全局对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值