理解ES3 -- ES5中的声明提升

理解ES3 -- ES5中的声明提升

什么是声明提升?

声明提升就是函数中任何位置所声明的变量或函数,都会自动“提”到函数的最前面,就好像它们是在函数的开头声明的一样。

为什么会出现声明提升的现象?

Javascript引擎的工作方式是先解析代码,获取所有声明的变量和函数(这个就是生成变量对象 的过程,此过程导致了声明提升),然后再一行一行地运行。这就造成所有的变量、函数的声明语句在执行前就识别到内存中。并且,变量提升后,该变量的默认值为undefined,函数提升后,会将函数在堆中的首地址赋值给函数变量。

此外,变量提升并不是说真正的把var myname = '张三';这行代码的变量声明var myname在物理层面上提升到代码的开头处,而是在编译阶段被JavaScript引擎放入内存中。

变量对象是什么? 为什么要有变量对象?怎么实现的?

变量对象是JavaScript运行环境 执行上下文(execute context)的一个属性,变量对象提供了当前环境所需的变量和函数

在JavaScript代码执行的时候,遇到未声明的变量或者函数引用的时候,会抛出

ReferenceError: a is not defined

Javascript引擎就是通过变量对象(variable object)这个对象来判断一个变量是否是声明过的。

一个典型的变量对象应该由以下几个部分:

  • argument(形参)
  • Function: (直接指向函数)
  • variable声明,所有已声明的var变量,值为undefined

例:

function myName(){
    var name  = 'unset';
    var age  = -1;
    function setNameAndAge(arg1, arg2){
        this.name = arg1
        this.age = arg2
    }
}
var doge = new myName()
doge.setNameAndAge('doge', 12)

以上代码中,函数myName的函数上下文在JavaScript编译阶段生成的变量对象如下:

variableObject = {
    argument = {
        0: 'doge', 1: 12, caller: <who invite this function> ,length: 2
    },
    setNameAndAge: <function reference>,
    name: undefined,
    age: undefined
}

在变量对象中,函数声明优先于变量声明,且后声明的变量会覆盖先声明的变量

完成编译后,进入执行阶段,此时 变量对象转变成 活动对象(active onject)

什么是执行上下文?

执行上下文(Execution Context),简而言之,就是当前JavaScript代码运行时环境的抽象概念,Javascript中的代码就是借助执行上下文才能正常运行

执行上下文的生命周期:

  1. 创建阶段
    1. 生成变量对象
      1. 创建arguments
      2. 注册函数声明
      3. 注册变量声明
    2. 确定作用域链
    3. 确定this指向
  2. 执行阶段
    1. 变量赋值
    2. 函数的引用
    3. 执行其他代码

执行上下文的组成

执行上下文EC的组成主要是三个部分:

  • VO variableobject 变量对象提供了当前执行代码所需的变量和函数
  • ScopeChain 作用域链 用于保证JS中变量和函数的正确引用顺序
  • this this指向

执行上下文主要包括两个部分: 全局上下文 | 函数上下文

全局上下文在每次运行时只有一个,而函数上下文则是每个函数执行时都会创建一个新的函数上下文

在全局代码的上下文执行环境中,变量对象就是全局对象,在JS浏览器端实现下,变量对象就是window对象,其次,全局对象创建阶段会将Math、String、Date等函数作为自生的函数,也会将全局变量作为自己的属性

什么时作用域链(ScopeChain)

函数的作用域规定了函数如何去查找变量,也就是确定当前执行代码对变量的访问权限;查找变量时,会先从当前上下文变量对象查找,如果没有找到,就会从父级函数上下文中查找,一直查找到全局上下文,没有找到就会抛出ReferenceError。这样的由多个上下文的变量对象构成的链表就是作用域链

执行上下文中有个内部属性[[scope]]来保存当前上下文的作用域链

如何确定this指向?

如果当前函数作为对象 方法调用或者通过bind()、 call()、 apply()等API委托调用,则将当前代码块的调用者信息存入当前执行上下文的this,否则,默认为全局对象调用

posted @ 2021-12-06 15:41  KiShima  阅读(188)  评论(0)    收藏  举报