前述
在了解js中this指向之前先简单的阐述一下脚本执行过程和执行上下文的概念
js脚本执行过程
当js引擎执行脚本代码之前会先进行一个预编译阶段,然后创建全局执行上下文入栈(上下文调用栈),然后执行全局代码,执行到函数调用的时候会为该函数创建一个函数执行上下文然后入栈,然后执行函数代码,以此类推。当函数代码执行完毕会将这个函数执行上下文出栈,最后全局代码执行完毕,全局执行上下文出栈,这是一个完整的脚本执行执行过程。
什么是执行上下文
执行上下文是一个抽象的概念,它定义了代码被执行时的环境,其中包含this指向,执行上下文可以分为三种类型:全局执行上下文、函数执行上下文和eval 执行上下文(不做了解)。
- 全局执行上下文中的this指向window,严格模式下this是undefined
- 函数执行上下文中的this指向是在函数被调用的时候确定的
this指向的几种情况
1、函数调用
console.log(this) // 指向windowfunction fn() {console.log(this) // 函数调用的时候this指向window}const obj = { name: 'obj', fn(){console.log(this)}}function fn1(callback) {callback() // 函数调用的时候this指向window}fn1(fn)fn1(obj.fn)
函数调用指向window
2、对象调用
function fn() {console.log(this)}const obj = {_fn() {console.log(this) fn() // 函数中的this指向window},_fn1() {function _fn2() {console.log(this)}_fn2() // 函数中的this指向window},_fn3: fn}fn() // 函数中的this指向windowobj._fn() // obj调用_fn方法,函数中的this指向objobj._fn1()obj._fn3() // obj调用_f3方法,函数中的this指向objconst fn3 = obj._fnfn3() // 非对象调用,函数中的this指向windowfunction Animal(name) {this.name = name}Animal.prototype.getName = function() {console.log(this)}const cat = new Animal('cat')cat.getName() // 构造函数中原型对象中的this指向cat对象
函数被对象调用,this指向调用它的对象, 构造函数中的this指向实例对象
(Js es6以前类的创建以及原型链-CSDN博客中有new 构造函数的执行过程)
3、箭头函数中的this
const obj = {fn() {const fn1 = () => {console.log(this) // 指向window}fn1() // 函数中的this指向obj},fn2: () => {console.log(this)}}obj.fn()obj.fn2() // 函数中的this指向window
会发现,在obj的fn方法里声明fn1箭头函数并执行,this指向obj,将fn2方法改为箭头函数,this指向window,这是因为箭头函数并没有自己的this,它会捕获外部上下文中的this值。
代码解释
- obj.fn调用:上下文被创建,其this指向调用它的对象也就是obj,fn1执行,由于fn1是在fn方法函数里定义的,所以它的this会捕获外部上下文中的this,也就是fn函数上下文中的this(obj)
- obj.fn2调用:由于fn2是一个箭头函数,当fn2被执行的时候,它里面的this捕获外层上文中的this,由于最外层是全局代码环境,所以它里面的this是全局执行上下中的this(window)
4、call、apply、bind改变this指向
const obj = {_fn(){console.log(this)},_fn1(){console.log(this)}}const obj1= {name: 'obj1'}const fn3 = obj._fnfn3.call(obj1) // 改变函数中的this将其指向obj1,并执行函数fn3.apply(obj1) // 改变函数中的this将其指向obj1,并执行函数obj._fn1.call(obj1) // 改变函数方法中的this将其指向obj1,并执行方法const fn4 = fn3.bind(obj1) fn4() // 将函数中的this指向obj1, 并返回一个新的函数
call、apply、bind能够指定函数中的this指向,call和apply的区别就是传参的方式不同
function fn(param1, param2, param3, param4) {}fn.call(obj, param1_value, param2_value, param3_value, param4_value)fn.bind(obj, [param1_value, param2_value, param3_value, param4_value])
箭头函数无法更改this指向
5、回调函数中的this
const obj = {fn() {setTimeout(function(){console.log(this) // window})setTimeout(() => {console.log(this) // obj})}}
回调(非箭头)函数中的this指向取决于执行这个回调函数的函数想让它指向谁,比如定时器就让他指向window,但是可以通过call、apply、bind改变回调函数中的this指向。
function callback_fn() {console.log(this)}const obj = {callback_fn() {console.log(this)}}function fn(callback) {const inner_obj = {name: 'inner_obj'}callback.call(inner_obj) // 回调函数中的this指向obj}fn(callback_fn)fn(obj.callback_fn)