this 深度解析

this 深度解析

这是《你不知道的JavaScript》第三节,深入了解 this 的含义,以及如何判断 this 的指向。

this 是什么

首先我们要知道 this 到底是什么。我认为,this 更像是一个关键字,他最终可能会指向某个对象(也有可能指向undefined)。在函数中,this 只在函数调用的时候才确定其最终指向的对象,当我们在函数中对 this 进行操作的时候,实际是操作 this 指向的变量。

这里会有一些误区:
误区1:this 指向函数本身,这种想法是错的,虽然从 this 的字面含义的确有这层意思。
误区2:this 指函数的作用域,这是初学者比较容易弄混 this 和作用域的概念。诚然在某种情况下,this 可能会指向作用域,但这不代表 this 是函数的作用域。
用简单的示例你就能明白

function a() {
	let name = 'this'
	console.log(this.name)
	this.age = 20
	console.log(a.age)
}
a()
// log:
// undefined
// undefined

如果 this 指向函数,那么 this.age = 20age 最终应该会挂载到 a.age 中。如果 this 指向函数作用域,那么 this.name 应该可以访问到作用域中定义的 name 值。但实际都无法获取到。

this 指向

那么 this 实际指向谁,只需遵循下面几个原则,你就能很快判断 this 指向谁

  • 隐式绑定
  • 显式绑定
  • new
  • 都没有,则是默认绑定

一般情况下,如果不满足前三种绑定,那么就是默认绑定,我们一个个来说。

隐式绑定

隐式绑定最简单的判断就是是否有调用者,如:obj.log();其中 log 是执行的函数,obj 是log的调用者。这种情况下,log 函数执行时 this 指向 obj

这里你需要注意,不一定非要将 log 函数定义在 obj 内。只要满足 log 的调用者是 obj 即可。这是我们可能会忽略的地方。如:

let b = {
	name: 'bbbb',
	log: function() {
		console.log(this.name)
	}
}

let obj = {
	name: 'aaaa',
	log: _log
}

obj.log() // aaaa

你不需要太过注重函数定义的位置(一般情况下),因为决定 log 函数中 this 指向的是他实际的调用者。

显式调用

显式调用是指使用callapplybind等方法强制改变函数执行时 this 的指向。这里简单演示一下

function log() {
	console.log(this.name)
}
let a = {
	name: 'a'
}
log.call(a) // a 直接执行,第二个参数后接收变量,作为参数传入log
log.apply(a) // a 直接执行,第二个参数是一个数组,数组最终会展开传入log
log.bind(a)() //a 返回一个新的函数,第二个参数后接收变量,作为参数传入log

这里不详细讨论这些方法的用法,这不是我们的重点。

如何隐式和显式一起调用,那么最终 this 指向谁呢?

let a = {
	name: 'a',
	log() {
		console.log(this.name)
	}
}
let b = {name: 'b'}
a.log.call(b) // ?

// 实际最终打印 b

显式调用的优先级高于隐式调用

new

在我们使用构造函数 new 一个对象的时候,函数最终会返回一个对象,而函数内对 this 的操作会最终执行到这个对象。
这实际是因为,构造函数在 new 的时候,会新建一个空的对象,并将函数的 this 指向这个新建的对象,所以函数内对 this 的操作,实际就是对这个新的对象的操作。构造函数在执行的时候实际可以指定需要指向的对象,但一般用的不多,这里不做讨论。

function Foo(name) {
	this.name = name
}
let foo = new Foo('foo')

// {name: 'foo'}

默认绑定

如果不满足上面三种情况,那么函数中 this 会进行默认绑定到 window(非严格模式下),如果是严格模式下,会绑定 undefined

function log() {
	console.log(this)
}
log() // Window

// 严格模式
'use strict'
function log() {
	console.log(this)
}
log() // undefined

箭头函数

对于箭头函数,他内部的this指向,他并不适用于用上面四种情况来判断。因为严格意义来说箭头函数内部并没有this。若在箭头函数内调用 thisthis 最终指向其定义该箭头函数时父级作用域的 this。类似词法作用域,所以箭头函数的 this 在定义该箭头函数的时候就已经确定,且无法改变。所以在使用箭头函数的时候需要注意其 this 的指向。

let log = null
let obj = {
	far() {
		log = () => {
			// 箭头函数 this 指向父级 this
			console.log(this)
		}
	}
}
obj.far() // 当 far 执行的时候定义了log箭头函数,且此时 far 函数 this 指向 obj
log() // log 是一个箭头函数,其 this 指向父级函数的this,也就是 obj

箭头函数的 this 这种继承的效果,可以让我们更好的处理某些函数中需要使用特定 this 对象的情况。但这同时也会引发一些问题,如 this 强制绑定,这是需要注意的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值