ECMAScript在浏览器端运行被称为JavaScript
在浏览器端JavaScript是一种描述型脚本语言,不需要编译成中间语言,而是由浏览器按代码块顺序进行动态地解析与执行。
关于作用域
ES6之前,只有全局作用域和函数作用域;ES6引入了块级作用域。作用域是代码执行过程中的变量、函数或者对象的可访问区域。
- 全局作用域:当定义变量的地方没有被 function 包括则是全局变量
- 函数作用域:在函数内部定义的变量就是处于函数作用域中
- 块状作用域:类似于 if、switch 条件选择或者 for、while 这样的循环体即是所谓的块级作用域
关于变量的生命周期
变量的生命周期包含着变量声明、变量初始化、以及变量赋值三个步骤;其中声明步骤会在作用域中注册变量,初始化步骤负责为变量分配内存并且创建作用域绑定,此时变量会被初始化为 undefined,最后的分配步骤则会将开发者指定的值分配给该变量。
关于预编译
JavaScript代码不需要编译成一个其它可执行的文件,但是js代码在执行之前会有一个准备过程,这个准备过程叫作预编译。
预编译的过程是不可见的,也不会产生额外的文件。
预编译期间会主要完成两件事情:
- 按顺序扫码代码块,如果发现语法错误,停止工作并报错;
- 若没有语法错误,做声明变量和初始化变量的操作即所谓的变量提升操作;
先看一道简单的题目
<script type="text/javascript">
console.log(a);
var a = 666;
console.log(a);
</script>
这道题目很简单,但是却很能说明问题。
提升操作
- 使用var声明的变量的提升:把所有带有var关键字的变量名,提升到当前代码块的开始,并设初值为undefined。
- 函数提升:把所有用声明式定义的函数,提升到当前代码块的开始,注意是做函数整体的提升。
具体细节如下
只提升加var的变量,没有var不提升
如图,没有var,变量名没有提升,相当于没有声明变量a就直接使用它,会出现引用类型错误。
if结构中的变量提升
如图所示,变量提升一定是发生在预编译期(提升之后才会进入代码的执行期),而变量的提升与if条件语句是否成立无关。只要有var就会提升。
for循环中的变量提升
同样会做变量提升。
函数内部的变量提升
函数内部定义的变量,不可能提上到函数的外部。如图会直接报错。
函数内部的变量只会体升到函数的开始位置。
只提升声明式定义的函数
定义函数有两种方式:
- 声明式:
function f(){}
- 函数表达式:
var f = function(){}
函数提升是整个函数体提升,变量提升是提升的变量名。
如上图所示,通过表达式定义的函数提升,报了一个TypeError的错误;是因为预编译期将var关键字提升,也就是说这里的f就是一个普通变量,它的初值被赋为undefined;当代码执行到第11行
f();
时就相当于是undefined();
,所以肯定会报类型错误。
函数与变量同时提升
当函数名与变量名同名,且同时提升,以函数为准。
理由是函数是一等公民,地位高于变量。
提升不能跨script标签
开始那道题的答案
ES6中的变量提升以及暂时性死区
在 ES6 中以 let 与 const 关键字声明的变量会在作用域头部被初始化,不过这些变量仅允许在实际声明之后使用。在作用域头部与变量实际声明处之间的区域就称为所谓的暂时死区(Temporal Dead Zone),TDZ 能够避免传统的提升引发的潜在问题。
小结
以上,就是关于JavaScript变量提升的知识小结。