浅谈Js中的this指向问题


作为一个初级前端开发者,对this指向非常容易混淆,且在面试中经常会问到的一个点,下面是我对this指向的一些浅薄的看法。
在介绍this指向前我们先说说执行上下文,变量对象。

1.什么是执行上下文

函数在运行的时候,会创建一个执行环境,这个执行环境叫做执行上下文,JavaScript会以栈的方式处理它们,这个栈就是函数调用栈
执行环境有全局执行环境,函数执行环境和eval三种,每个执行环境又包含变量对象/活动对象,作用域链,this的值三种

2.什么是变量对象

在执行上下文中,会创建一个叫做变量对象的特殊对象,基础数据类型往往都保存在变量对象中。换句话说,我们所声明的所有变量都会保存在变量对象中,它包括以下内容

1.函数中的所有参数
2.当前上下文中的所有函数声明
3.当前上下文中的所有变量声明
我们了解,在函数被调用执行后,产生一个变量对象,从而this的指向也确定了,我总结了一个重要的结论:当前函数的this是在函数被调用执行的时候才确定。如果当前的执行上下文处于函数调用栈的栈顶,那么变量会变成活动对象,同时this的指向也确定了

This指向

全局上下文

无论是在严格模式还是在非严格模式,在全局执行环境中this都是指向全局对象的,在浏览器中,通常是window;在Node环境中,则是globalThis.

函数上下文

在函数里,this的值取决于函数的调用方式。如果被对象调用,则指向这个对象,如果在函数中没有被调用,则this指向全局对象window或者undefined(严格模式)

(1)函数中的this是取决于函数的调用方式才确定的
在严格模式下

如果调用者是独立调用,那么该函数内部的this指向undefinded

function fn(){
    'use strict'
    console.log(this);
}

function fn(){
    'use strict'
    console.log(this);
}
fn(); // fn是调用者,为独立调用,this指向undefinded
在非严格模式下
  • 如果调用者是独立调用,当this指向undefinded时,它会自动指向全局对象。
function fn(){
    console.log(this);
}
fn(); // fn是调用者,独立调用,
//this指向undefinded时会自动指向全局对象window
  • 如果调用者是被某个对象所拥有,那么调用改函数时内部this指向该对象
function fn(){
    console.log(this);
}
window.fn();// fn是调用者,被window所拥有,this指向window对象
var fn = {
a: 20,
b: function() {
console.log(this.a) // 20
}
}
fn.b();

划重点:this指向一个即将被执行的对象,这个对象在执行上下文中执行,上述代码中,b()函数被执行,它对应的执行上下文是fn()

this的几种绑定

1.默认绑定:函数调用无任何调用前缀的情景,它没有被上一级的对象所调用,且在非严格模式下,那么this指向全局对象window,在严格模式下this指向undefined

function a(){
	var user = 'zhangsan';
	console.log(this)		// window
}
function a(){
	"use strict"	   	// 全局模式下,默认绑定的this指向undefined
	var user = 'zhangsan';
	console.log(this)		// undefined
}
a();		

2、隐式绑定 如果一个函数中有this,这个函数被上一级的对象所调用,那么this就会隐式绑定到这个对象上。

var o = {
	user:"zhangsan";
	fn:function(){
		console.log(this.user)	// zhangsan
}
}
o.fn();

如果一个函数中有this,这个函数包含多级对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象。

var o = {
	a: 10,
	b: {
		a: 12,
		fn: function() {
		console.log(this.a) //12
		}
	}
}

3、隐式丢失 在特定情况下会存在隐式绑定丢失的问题,最常见的就是作为参数传递及变量赋值,如下:

	var o = {
				a: 10,
				b: {
					fn: function() {
						console.log(this.a) // window
					}
				}
			}
			var j = o.b.fn();
			j();

fn()函数作为参数传给变量j,只是单独的传递了一个函数,this并没有跟函数绑在一起,所以this丢失,这里指向了window。
这里this指向的是window,this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的。虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行,所以最终指向的是window。
4、this的显式绑定
显示绑定是指我们通过call/apply/bind 显示改变this指向,相比隐式绑定,我们能清楚感知this指向的变化。
当函数调用call/apply时,call的第一个参数是为确定函数内部this的指向,后面的参数则是函数所执行时需要的参数,一个一个的传递;apply的第一个参数与call相同,为函数内部this指向,函数执行所需要的参数以数组的形式传递,作为apply的第二个参数。
在这里插入图片描述fn(10,10):this指向window对象,此时this.a = 10,结果为:30
fn.call(obj,10,10): 通过call,fn()函数改变this的指向为obj对象,那么this.a == obj.a = 20,fn为其执行上下文,所以结果为:40
5、 new的绑定
new一个对象有几个过程,本质上是对应函数的this指向new出来的新对象,对象的隐式原型指向函数的显式原型,形成一条原型链,对象继承函数的属性和方法。
6. this绑定优先级
显式绑定 > 隐式绑定 > 默认绑定
new绑定 > 隐式绑定 > 默认绑定

拓展

1.call、apply与bind都用于改变this绑定,但call、apply在改变this指向的同时立即执行函数,但是bind在改变this后是返回一个全新的boundFcuntion绑定函数,这也是为什么上方例子中bind后还加了一对括号 ()的原因。
2.bind属于硬绑定,返回的 boundFunction 的 this 指向无法再次通过bind、apply或 call 修改;call与apply的绑定只适用当前调用,调用完就没了,下次要用还得再次绑。
3.call与apply功能完全相同,唯一不同的是call方法传递函数调用形参是以散列形式,而apply方法的形参是一个数组。在传参的情况下,call的性能要高于apply,因为apply在执行时还要多一步解析数组。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值