【浏览器工作原理】5. 从执行上下文角度理解javaScript的this机制

129 阅读3分钟

引言

在实际开发过程中,JavaScript的this还是比较常见的,但是由于分不清楚this的指向,导致出现很多难以理解的bug。本文就从JavaScript执行上下文角度来说明白this,希望再也不担心this带来的问题了。

为什么出现this

对于陌生的事情,了解其背后的原因非常有助于理解。在对象内部的方法使用对象内部的属性是非常常见的需求,但是JavaScript并不支持,因此JavaScript提出this机制来解决作用域机制的缺陷。

this是什么

之前文章提到过执行上下文中包含了变量环境、词法环境、外部环境,其实还有一个this。this是和执行上下文绑定的,每个执行上下文都有一个this。

所以this根据执行上下文的不同分为三种:全局执行上下文中的this、函数的this和eval中的this。

  • 全局执行上下文:this指向window对象(非严格模式)
  • 函数执行上下文:分4种情况
  1. 默认情况:this指向window对象
function foo(){
  console.log(this); //window
}
foo()
  1. 通过函数的call/apply/bind方法:this指向绑定的对象

let bar = {
  myName : "hi",
  test1 : 1
}
function foo(){
  this.myName = "hello"
}
foo.call(bar)
console.log(bar) // hi
console.log(myName) //undefined
  1. 通过对象调用:this指向该对象
var myObj = {
  name : "hello", 
  showThis: function(){
    console.log(this)
  }
}
myObj.showThis()

其实是因为JavaScript引擎在执行myObj.showThis()时转化为了 myObj.showThis.call(myObj),再来看个例子

var myObj = {
  name : "hi",
  showThis: function(){
    this.name = "hello"
    console.log(this) // window对象
  }
}
var foo = myObj.showThis;
foo()

通过例子可知:

  • 在全局环境中调用函数,函数内部的this指向全局变量window
  • 通过一个对象来调用其内部的一个方法,this指向对象本身
  1. 通过构造函数:指向新对象
function CreateObj(){
  this.name = "hi"
}
var myObj = new CreateObj()

当执行new CreateObj时,JavaScript引擎做了如下三件事:

  1. 创建一个空对象tempOjb;
  2. 调用CreateObj.call方法,并将tempObj作为call方法的参数,this就指向了tempObj
  3. 执行crateobj函数,返回tempObj对象
  • eval函数上下文:eval 只在被直接调用时,this指向才是当前作用域

\

this有哪些缺陷

我们都知道this在使用过程中存在不少坑,下面说几个比较常见的:

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

举个例子


var myObj = {
  name : "hi", 
  showThis: function(){
    console.log(this); // myObj
    function bar(){console.log(this)} //window
    bar()
  }
}
myObj.showThis()

通过上面例子可知this没有作用域限制。

解决方法:

  • 再showThis函数中声明一个变量self用来保存this,本质上把this机制转化为作用域机制。
  • 使用es6中的箭头函数
  1. 普通函数中的this默认指向全局对象window 解决方法:
  • 通过call方法显示调用
  • 设置JavaScript的严格模式

总结

本文通过上下文角度说明了JavaScript中的this机制,可见明白了来龙去脉,this指向也没那么难理解了。