11 | this:从JavaScript执行上下文的视角讲清楚

2020-01-15  本文已影响0人  奋斗的小鸟cx

在对象内部的方法中使用对象内部的属性是一个非常普遍的需求

但是 JavaScript 的作用域机制并不支持这一点,基于这个需求,JavaScript 又搞出来另外一套 this 机制。

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 语言的作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的。
下来咱们就展开来介绍 this,不过在讲解之前,希望你能区分清楚 作用域链this 是两套不同的系统,它们之间基本没太多联系。在前期明确这点,可以避免你在学习 this 的过程中,和作用域产生一些不必要的关联。

JavaScript 中的 this 是什么

关于 this,我们还是得先从执行上下文说起。在前面几篇文章中,我们提到执行上下文中包含了变量环境、词法环境、外部环境,但其实还有一个 this 没有提及,具体你可以参考下图:

执行上下文中的 this
从图中可以看出, this 是和执行上下文绑定 的,也就是说每个执行上下文中都有一个 this。
执行上下文主要分为三种——全局执行上下文、函数执行上下文和 eval 执行上下文

1.当函数作为对象的方法调用时,函数中的 this 就是该对象

  1. 当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window
  2. 嵌套函数中的 this 不会继承外层函数的 this 值
  3. 最后,我们还提了一下箭头函数,因为箭头函数没有自己的执行上下文,所以箭头函数的 this 就是它外层函数的 this

三种方式来设置函数执行上下文中的 this 值。

1. 通过函数的 call 方法设置
2. 通过对象调用方法设置:使用对象来调用其内部的一个方法,该方法的 this 是指向对象本身的。
3. 通过构造函数中设置
function CreateObj(){
  this.name = "极客时间"
}
var myObj = new CreateObj()

其实,当执行 new CreateObj() 的时候,JavaScript 引擎做了如下四件事:首先创建了一个空对象 tempObj;接着调用 CreateObj.call 方法,并将 tempObj 作为 call 方法的参数,这样当 CreateObj 的执行上下文创建时,它的 this 就指向了 tempObj 对象;然后执行 CreateObj 函数,此时的 CreateObj 函数执行上下文中的 this 指向了 tempObj 对象;最后返回 tempObj 对象。
代码解释
var tempObj = {}
CreateObj.call(tempObj)
return tempObj

this 的设计缺陷以及应对方案

1. 嵌套函数中的 this 不会从外层函数中继承

var myObj = {
  name : "极客时间", 
  showThis: function(){
    console.log(this)
    function bar(){console.log(this)} //window
    bar()
  }
}
myObj.showThis()

函数 bar 中的 this 指向的是全局 window 对象,而函数 showThis 中的 this 指向的是 myObj 对象

解决问题

1声明一个变量 self 用来保存 this,然后在 bar 函数中使用 self,代码如下所示:

这个方法的的本质是把 this 体系转换为了作用域的体系。
第一种是把 this 保存为一个 self 变量,再利用变量的作用域机制传递给嵌套函数。


var myObj = {
  name : "极客时间", 
  showThis: function(){
    console.log(this)
    var self = this
    function bar(){
      self.name = "极客邦"
    }
    bar()
  }
}
myObj.showThis()
console.log(myObj.name)
console.log(window.name)
2你也可以使用 ES6 中的箭头函数来解决这个问题
这是因为 ES6 中的箭头函数并不会创建其自身的执行上下文,所以箭头函数中的 this 取决于它的外部函数。

第二种是继续使用 this,但是要把嵌套函数改为箭头函数,因为箭头函数没有自己的执行上下文,所以它会继承调用函数中的 this。

var myObj = {
  name : "极客时间", 
  showThis: function(){
    console.log(this)
    var bar = ()=>{
      this.name = "极客邦"
      console.log(this)
    }
    bar()
  }
}
myObj.showThis()
console.log(myObj.name)
console.log(window.name)
上一篇下一篇

猜你喜欢

热点阅读