javascript -- this指向问题

思考题:下面代码输出结果为什么?如果想通过updateInfo函数来更新userInfo中的属性,应该如何修复如下代码?

let userInfo = {
	name: ‘flower’,
	age: 13,
	sex: ‘male’,
	updateInfo: function() {
		setTimeout(function() {
			this.name = ‘apple’
			this.age = 23
			this.sex = ‘female’
		}, 100)
	}	
}
userInfo.updateInfo()

想通过updateInfo函数来更新userInfo中的数据信息,应该如何修复这段代码?


在对象内部的方法中使用对象内部的属性是一个非常普遍的需求, 但我们可以发现在实际生产实践中,代码运行结果与我们常规预期不一致。

看如下代码:

var bar = {
    myName: 'time.geekbang.com',
    printName: function() {
        console.log(myName)
    }
}
function foo () {
    let myName: 'geek'
    return bar.printName
}

let myName = 'geekbang'
let _printName = foo()
_printName()
bar.printName()

// 执行结果
// geekbang
// geekbang

由于JavaScript语法的作用域是由词法作用域决定的,而词法作用域是由代码结构来确定的。

所以在printName函数里面使用的变量myName是属于全局作用域下面的,导致最终打印出来的值都是geekbang

一般按照常理来,调用bar.printName方法时,该方法内部变量myName应该使用bar对象中的,因为它们是一个整体。

在对象内部的方法中使用对象内部的属性是一个非常普遍的需求,但JavaScript的作用域机制并不支持这种操作,基于这个原因,产生了this机制。


执行上下文与this的联系与区别:

执行上下文包含了变量环境、词法环境、外部环境在这里插入图片描述
从图中可看出,this和执行上下文是相互绑定的,也就是说每个执行上下文中都有一个this。

而执行上下文主要分为三种----全局执行上下文、函数执行上下文和eval执行上下文。
故对应的this也只有三种 ----- 全局执行上下文、函数执行上下文和eval执行上下文

全局执行上下文中的this

在控制台输入console.log(this)来打印全局上下文中的this,最终输出的是window对象。在这里插入图片描述
全局执行上下文中的this指向了window对象。
这也是this和作用域链的唯一交点,作用域链的最低端包含了window对象,全局执行上下文中的this也指向了window对象。

函数执行上下文中的this
function foo() {
	console.log(this)
}
foo()

在这里插入图片描述
上图说明在默认情况下调用一个函数,其执行上下文中的this也是指向的window对象。

那么能不能设置函数执行上下文中的this指向其他对象呢?

有三种方法可以设置函数执行上下文中的this指向

1. 函数的call方法实现
let bar = {
	myName: ‘flower’,
	test1: 1
}
function foo() {
	this.myName = ‘apple’
}
foo.call(bar)
console.log(bar)
console.log(myName)


观察输出结果,发现foo函数内部的this已经指向了bar对象,因为打印bar的时候,bar的myName属性已经由flower变更为apple

同时在全局执行上下文中打印myName,报错该变量未定义

还可以使用bind和apply方法来设置函数执行上下文中的this,下篇文章分析apply、call、bind的区别

2. 通过对象调用方法设置
var myObj = {
	name: ‘flower’,
	showThis: function() {
		console.log(this)
	}
}
myObj.showThis()

在这里插入图片描述
使用对象来调用其内部的一个方法,该方法的this是指向对象本身的

var myObj = {
	name: ‘flower’,
	showThis: function() {
		this.name = ‘apple’
		console.log(this)
	}
}
var foo = myObj.showThis
foo()

在这里插入图片描述
通过上面两个例子的对比,可得出如下结论

  • 在全局环境中调用一个函数,函数内部this指向全局变量window
  • 通过一个对象来调用其内部的一个方法,该方法的只想上下文中的this指向该对象本身
3. 通过构造函数中设置
function CreateObj () {
	this.name = ‘flower’
}
var myObj = new CreateObj()

当执行new CreateObj()时,JavaScript引擎做了如下四件事

  • 首先创建了一个空对象 tempObj
  • 接着调用CreateObj.call方法,并将tmpObj作为call方法的参数,这样当CreateObj的执行上下文创建时,this就指向了tmpObj对象了
  • 然后执行CreateObj函数,此时CreateObj函数的执行上下文中this指向了TempObj对象
  • 最后返回tmpObj对象

代码演示过程如下:

var tempObj = {}
createObj.call(tempObj)
return tempObj
this的设计缺陷以及应对方案
var myObj = {
	name: ‘flower’,
	showThis: function() {
		console.log(this)
		function bar() {console.log(this)}
		bar()
	}
}
myObj.showThis()

直觉上,bar中的this应该和其外层showThis函数中的this是一致的,都指向myObj对象;所以bar函数中的this应该指向调用它的myObj对象。

那么实际上执行结果如何呢?看下图:
在这里插入图片描述
由上图可发现,函数bar中的this指向的是全局window对象,而函数showThis中的this指向的是myObj对象

this没有作用域的限制,这点跟变量不一样,所以函数不会从调用它的函数中继承this,会造成很多不符合直觉的代码,那么怎么解决这种问题呢?
两种方法

    1. 把this保存为一个self变量,再利用变量的作用域机制传递给嵌套函数
    var myObj = {
    	name: ‘flower’,
    	showThis: function() {
    		console.log(this)
    		var self = this
    		function bar() {
    			self.name = ‘apple’
    		}
    		bar()
    	}
    }
    myObj.showThis()
    console.log(myObj.name)
    console.log(window.name)
    

    执行结果如下:
    在这里插入图片描述

    1. 继续使用this,但把嵌套函数改为箭头函数,因为箭头函数没有自己的执行上下文,所以他会继承调用函数中的this
    var myObj = {
    	name: ‘flower’,
    	showThis: function() {
    		console.log(this)
    		var bar = () => {
    			this.name = ‘apple’
    			console.log(this)
    		}
    		bar()
    	}
    }
    myObj.showThis()
    console.log(myObj.name)
    console.log(window.name)
    

    执行结果如下:
    在这里插入图片描述
    普通函数中的this默认指向全局对象window
    如果要让函数执行上下文中的this指向某个对象,最好的方式是通过call方法来显示调用

总结

在使用this时,需要注意一下三点:

    1. 当函数作为对象的方法调用时,函数中的this就是指向该对象
    1. 当函数被正常调用时,在严格模式下,this值是undefined,非严格模式下this指向全局对象window
    1. 嵌套函数中的this不会继承外层函数的this值

因为箭头函数没有自己的执行上下文,所以箭头函数的this就是它外层函数的this

思考题:

let userInfo = {
	name: ‘flower’,
	age: 13,
	sex: ‘male’,
	updateInfo: function() {
		setTimeout(function() {
			this.name = ‘apple’
			this.age = 23
			this.sex = ‘female’
		}, 100)
	}	
}

想通过updateInfo函数来更新userInfo中的数据信息,应该如何修复这段代码?

答案:
方法一:将setTimeout中的函数改为箭头函数
方法二:在updateInfo函数中将this赋值给self变量,通过self更改userInfo的属性

let userInfo = {
	name: ‘flower’,
	age: 13,
	sex: ‘male’,
	updateInfo: function() {
		setTimeout(() => {
			this.name = ‘apple’
			this.age = 23
			this.sex = ‘female’
		}, 100)
	}	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值