理解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中的代码就是借助执行上下文才能正常运行
执行上下文的生命周期:
- 创建阶段
- 生成变量对象
- 创建arguments
- 注册函数声明
- 注册变量声明
- 确定作用域链
- 确定this指向
- 生成变量对象
- 执行阶段
- 变量赋值
- 函数的引用
- 执行其他代码
执行上下文的组成
执行上下文EC的组成主要是三个部分:
- VO variableobject 变量对象提供了当前执行代码所需的变量和函数
- ScopeChain 作用域链 用于保证JS中变量和函数的正确引用顺序
- this this指向
执行上下文主要包括两个部分: 全局上下文 | 函数上下文
全局上下文在每次运行时只有一个,而函数上下文则是每个函数执行时都会创建一个新的函数上下文
在全局代码的上下文执行环境中,变量对象就是全局对象,在JS浏览器端实现下,变量对象就是window对象,其次,全局对象创建阶段会将Math、String、Date等函数作为自生的函数,也会将全局变量作为自己的属性
什么时作用域链(ScopeChain)
函数的作用域规定了函数如何去查找变量,也就是确定当前执行代码对变量的访问权限;查找变量时,会先从当前上下文变量对象查找,如果没有找到,就会从父级函数上下文中查找,一直查找到全局上下文,没有找到就会抛出ReferenceError。这样的由多个上下文的变量对象构成的链表就是作用域链
执行上下文中有个内部属性[[scope]]来保存当前上下文的作用域链
如何确定this指向?
如果当前函数作为对象 方法调用或者通过bind()、 call()、 apply()等API委托调用,则将当前代码块的调用者信息存入当前执行上下文的this,否则,默认为全局对象调用

浙公网安备 33010602011771号