# 11 | this:从JavaScript执行上下文的视角讲清楚this 在[上篇文章](https://time.geekbang.org/column/article/127495)中,我们讲了词法作用域、作用域链以及闭包,并在最后思考题中留了下面这样一段代码: ``` var bar = { myName:"time.geekbang.com", printName: function () { console.log(myName) } } function foo() { let myName = "极客时间" return bar.printName } let myName = "极客邦" let _printName = foo() _printName() bar.printName() ``` 相信你已经知道了,在printName函数里面使用的变量myName是属于全局作用域下面的,所以最终打印出来的值都是“极客邦”。这是因为JavaScript语言的作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的。 不过按照常理来说,调用`bar.printName`方法时,该方法内部的变量myName应该使用bar对象中的,因为它们是一个整体,大多数面向对象语言都是这样设计的,比如我用C++改写了上面那段代码,如下所示: ``` #include using namespace std; class Bar{ public: char* myName; Bar(){ myName = "time.geekbang.com"; } void printName(){ cout<< myName <{ this.name = "极客邦" console.log(this) } bar() } } myObj.showThis() console.log(myObj.name) console.log(window.name) ``` 执行这段代码,你会发现它也输出了我们想要的结果,也就是箭头函数bar里面的this是指向myObj对象的。这是因为ES6中的箭头函数并不会创建其自身的执行上下文,所以箭头函数中的this取决于它的外部函数。 通过上面的讲解,你现在应该知道了this没有作用域的限制,这点和变量不一样,所以嵌套函数不会从调用它的函数中继承this,这样会造成很多不符合直觉的代码。要解决这个问题,你可以有两种思路: * 第一种是把this保存为一个self变量,再利用变量的作用域机制传递给嵌套函数。 * 第二种是继续使用this,但是要把嵌套函数改为箭头函数,因为箭头函数没有自己的执行上下文,所以它会继承调用函数中的this。 ### 2\. 普通函数中的this默认指向全局对象window 上面我们已经介绍过了,在默认情况下调用一个函数,其执行上下文中的this是默认指向全局对象window的。 不过这个设计也是一种缺陷,因为在实际工作中,我们并不希望函数执行上下文中的this默认指向全局对象,因为这样会打破数据的边界,造成一些误操作。如果要让函数执行上下文中的this指向某个对象,最好的方式是通过call方法来显示调用。 这个问题可以通过设置JavaScript的“严格模式”来解决。在严格模式下,默认执行一个函数,其函数的执行上下文中的this值是undefined,这就解决上面的问题了。 ## 总结 好了,今天就到这里,下面我们来回顾下今天的内容。 首先,在使用this时,为了避坑,你要谨记以下三点: 1. 当函数作为对象的方法调用时,函数中的this就是该对象; 2. 当函数被正常调用时,在严格模式下,this值是undefined,非严格模式下this指向的是全局对象window; 3. 嵌套函数中的this不会继承外层函数的this值。 最后,我们还提了一下箭头函数,因为箭头函数没有自己的执行上下文,所以箭头函数的this就是它外层函数的this。 这是我们“JavaScript执行机制”模块的最后一节了,五节下来,你应该已经发现我们将近一半的时间都是在谈JavaScript的各种缺陷,比如变量提升带来的问题、this带来问题等。我认为了解一门语言的缺陷并不是为了否定它,相反是为了能更加深入地了解它。我们在谈论缺陷的过程中,还结合JavaScript的工作流程分析了出现这些缺陷的原因,以及避开这些缺陷的方法。掌握了这些,相信你今后在使用JavaScript的过程中会更加得心应手。 ## 思考时间 你可以观察下面这段代码: ``` let userInfo = { name:"jack.ma", age:13, sex:male, updateInfo:function(){ //模拟xmlhttprequest请求延时 setTimeout(function(){ this.name = "pony.ma" this.age = 39 this.sex = female },100) } } userInfo.updateInfo() ``` 我想通过updateInfo来更新userInfo里面的数据信息,但是这段代码存在一些问题,你能修复这段代码吗? 欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程。感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给更多的朋友。