变量与函数提升(ES5之前)
JS预解析
-
预解析
==> js是一个解释型语言,就是在代码执行之前,要先对代码进行通读和解释,然后再执行 ==> js代码在运行的是时候,会经历两个阶段:解释代码和执行代码
-
解释代码
==> 解释代码在执行代码之前,也叫预解析 ==> 需要预解析的内容有两个 1 声明式函数 ==> 在内存中声明一个变量名式函数名,并且这个名字的内容式一个函数 2 var关键字 ==> 在内存中声明有一个变量 ==> 赋值式函数会按照var关键字的规则进行解析 ==> 函数是js里面的一等公民
变量声明
- JavaScript中,使用一个变量之前,先用var关键字声明它,变量如果没有赋值,则初始化值为undefined
- 声明变量时,如果不写var,则会被定义为全局变量,而且并不是在函数内部写了变量,这个变量就属于这个函数的作用域;必须用var来声明,这个变量才会属于这个作用域
作用域
-
全局作用域
==> 全局作用域是最大的作用域 ==> 在全局中定义的变量可以在任何地方使用 ==> 在页面打开的时候,浏览器会自动给我们生成一个全局作用域window ==> 这个作用域一直存在,页面关闭就消失了
-
局部作用域
==> 局部作用域就是在全局作用域下面开辟出来的一个相对较小的作用域 ==> 在局部作用域中定义的变量只能在这个局部作用域使用 ==> 在js中只有函数能生成一个局部作用域,别的都不行 ==> 每个函数,都是一个局部作用域
变量的使用规则
-
访问规则
==> 访问:获取一个变量的值 ==> 规则 首先:在自己作用域内查找,如果有,就直接获取值使用 如果没有,就去上一级作用域查找,如果有,直接获取值使用 如果没有,就继续去上一级作用域查找,以此类推 如果一直到全局作用域都没有这个变量,那么就会直接报错(变量 is not defined) ==> 变量的访问规则也叫做作用域的查找机制 ==> 只能向上找,不能向下找
-
赋值规则
==> 赋值:先找到这个变量,再给他赋值 ==> 规则: 首先:在自己作用域内查找,如果有,就直接赋值 如果没有,就去上一级作用域查找,如果有,直接赋值 如果没有,就继续去上一级作用域查找,以此类推 如果一直到全局作用域都没有这个变量,那么就会把这个变量定义成全局变量,然后再赋值
变量提升
-
var关键字声明的变量,无论实际声明的位置在何处,都会被视为声明在函数的顶部(如果声明不在任意函数内,则视为在全局作用域的顶部)
-
JavaScript引擎的工作方式是,先预解析代码, 获取所有被声明的变量和函数声明,然后再一行一行地运行,这就使所有变量声明的语句,都会被提升到代码的头部,这就是变量提升
console.log(a); //undefined var a = 1; console.log(a); // 1
上面代码首先在控制台显示未声明的变量,本来是错误的做法但是没有报错.因为JS的变量提升.真正运行的是以下代码
var a ; console.log(a); a = 1; console.log(a);
在ES5中,js只有两种形式的作用域:全局作用域和函数作用域
全局作用域其实是全局对象的作用域,任意地方都可以访问到(如果没有被函数作用域覆盖)var i = 2; //全局变量,全局作用域 function outer(){ //定义外部函数 console.log(i); //访问全局变量 2 function inner(){ //定义内部函数 console.log(i); //访问全局函数 2 } inner(); } outer(); console.log(i); //2
函数内部定义变量时,如果不写var,比如下面的x = 8,则会被定义为全局变量,作用域为全局作用域,在声明语句后的任何位置都可以访问,否则为局部变量,则作用域为函数作用域.
function getResult(){ //定义外部函数 var c = 4; //定义局部变量 function open(){ //定义内部函数 x = 8; var y = 9; console.log(c); // 访问函数外部的局部变量 4 } open(); console.log(x); //console.log(y); //局部变量.访问不了,报错未定义 } getResult(); console.log(x); //console.log(y); //局部变量.访问不了,报错未定义
函数提升
-
函数的定义:
函数表达式 var f1 = function(){ alert();} //匿名方式
声明式函数 function f2(){ alert();} //命名方式 先于函数执行代码被解析器解析
1)函数表达式中的函数声明不会被提升,变量会被提升,如上面的举例代码
2)声明式函数中,函数同名,函数全部提升,后者覆盖前者
var p = 1; function add(num){ return num = num + 1; } y = add(p); function add(num){ return num= num+ 2; } z = add(p); console.log(y); //3 console.log(z); //3
3)如果有变量和函数同名,则会忽略掉变量,只提升函数
console.log(foo); // foo(){ } function foo(){ } var foo = 2; console.log(foo); // 2
总结
-
我们习惯将var a = 2;看做是一个声明,而实际上javascript引擎并不这么认为。它将var a和a = 2看做是两个单独的声明,第一个是编译阶段的任务,而第二个则是执行阶段的任务。
-
这意味着无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理,可以将这个过程形象地想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程被称为提升。