this指向解析


在这里插入图片描述

1. 调用位置

在理解this的绑定过程之前,首先是要理解调用位置:调用位置就是函数在代码中被调用的位置(不是声明的位置),只有明白调用位置,才能弄懂this到底引用的是声明。

function 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的调用位置

2. 绑定规则

1. 默认绑定

  • 最常用的函数调用类型:独立函数调用,也可以看作无法应用其他规则时的默认规则
function foo() {
	console.log(this.a)
}
var a = 2
foo() // 2

当调用foo()时,this.a被解析为全局变量a,因为函数调用时应用了默认绑定,因此this指向全局变量。

严格模式

如果使用严格模式,则不能将全局对象用于默认绑定,因此this为undefined:

"use strict"

function foo() {
	console.log(this.a)
}
var a = 2
foo() // TypeError: this is undefined;

注意:虽然this的绑定取决于调用位置,但只有foo()运行在非严格模式下,默认绑定才能绑定到全局对象;但在严格模式下调用则不影响默认绑定

function foo() {
	// 非严格模式
	console.log(this.a)
}
var a = 2
(function() {
	"use strict"
	foo()
})() // 2, 不影响默认绑定

2. 隐式绑定

第二种要考虑调用位置是否又上下文对象,或者说是否被某个对象拥有或包含

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

调用位置使用obj上下文来引用函数,可以说函数被调用时obj对象拥有或包含对象引用。当foo()被调用时,它的前面加上了对obj的引用。当函数引用又上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象,因为调用foo()时this被绑定到obj,因此this.a和obj.a一样。
对象属性引用链中只有上一层或最后一层在调用位置起作用

function foo() {
	console.log(this.a)
}
var obj2 = {
	a: 10,
	foo: foo
}
var obj1 = {
	a: 2,
	obj1: obj2
}
obj1.obj1.foo()

隐式丢失

最常见的问题是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局对象或undefined上。

function foo() {
	console.log(this.a)
}
var obj = {
	a: 2,
	foo : foo
}
var bar = obj.foo()
var a = 'global'
bar() // global

虽然bar是obj.foo的一个引用,但它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,应用了默认绑定。

还有一种就说传入回调函数时:

function foo() {
	console.log(this.a)
}
function foo1(fn) {
	fn()
}
var obj = {
	a: 2,
	foo: foo
}
var a = 'golbal'
foo(obj.foo) // folbal

参数传递就说一种隐式赋值,因此传入函数时也会被隐式赋值

3. 显示绑定

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

通过call,可以在调用foo时强制把它的this绑定到obj上,如果传入一个原始值来当作this绑定的对象,这个原始值会被转化成它的对象形式(呢哇String()等等)。通常被称为“装箱”,但显示绑定并不能解决丢失绑定的问题。

硬绑定

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

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

硬绑定的典型应用场景就说创建一个包裹函数,负责接收参数返回值:

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

另一种使用方法就是创建一个可以复用的赋值函数:

function foo(something) {
	console.log(this.a, something)
	return this.a + something
}
// 简单的辅助绑定函数
function bind(fn, obj) {
	return function() {
		return fn.apply(obj, arguments)
	}
}
var obj = {
	a: 2
}
var bar = bind(foo.obj)
var b = bar(3) // 2, 3
console.log(b) // 5

API调用的“上下文”

第三方库的许多函数,以及js语言和宿主环境中许多新的内置函数,都提供了一个可选的参数,通常被称为“上下文”,其作用和bind(…)一样,确保回调函数指向的this。

function() {
	console.log(el, this.id)
}
var obj = {
	id: 'aaa'
}
// 调用foo时把this绑定到obj
[1, 2, 3].forEach(foo, obj) // 1 aaa   2 aaa    3 aaa

4. new绑定

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

  1. 创建(构造)一个全新的对象
  2. 新对象会被执行[[Prototype]]连接
  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绑定行为的方法,称之为new绑定。

5. 箭头函数中的this指向

箭头函数不适用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

foo内部创建的箭头函数会捕获调用foo()的this,由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改。(new也不可以)

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

function foo() {
	setTimeout(() => {
		console.log(this.a)
	}, 1000)
}
var obj = {
	a: 2
}
foo.call(obj) // 2

3. 判断this的优先级

  1. 函数是否在new中调用,如果是的话this绑定的是新创建的对象
  2. 函数通过call,apply或硬绑定调用,this绑定的是指定的对象
  3. 函数在上下文对象中调用,绑定的是上下文对象
  4. 最后就是默认绑定,如果在严格模式下,绑定到undefined,否则绑定到全局对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值