深入理解javascript作用域系列第三篇——声明提升(hoisting)
前面的话
一般认为,javascript代码在执行时是由上到下一行一行执行的。但实际上这并不完全正确,主要是因为声明提升的存在。本文是深入理解javascript作用域系列第三篇——声明提升(hoisting)
变量声明提升
a = 2 ;
var a;
console.log( a );
直觉上,会认为是undefined,因为var a声明在a = 2;之后,可能变量被重新赋值了,因为会被赋予默认值undefined。但是,真正的输出结果是2
console.log( a ) ;
var a = 2 ;
鉴于上面的特点,可能会认为这个代码片段也会同样输出2。但,真正的输出结果是undefined
所有这些和观感相违背的原因是在于编译器的编译过程
第一篇介绍过作用域的内部原理。引擎会在解释javascript代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来
包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理
var a = 2 ;
这个代码片段实际上包括两个操作:var a 和 a = 2
第一个定义声明是在编译阶段由编译器进行的。第二个赋值操作会被留在原地等待引擎在执行阶段执行
//对变量a的声明提升到最上面后,再执行代码时,控制台输出2
var a;
a = 2 ;
console.log(a);
声明从它们在代码中出现的位置被“移动”到了最上面,这个过程就叫作提升(hoisting)
[注意]每个作用域都会进行提升操作
console.log(a);
var a = 0;
function fn(){
console.log(b);
var b = 1;
function test(){
console.log(c);
var c = 2;
}
test();
}
fn();
//变量声明提升后,变成下面这样
var a ;
console.log(a);
a = 0;
function fn(){
var b;
console.log(b);
b = 1;
function test(){
var c ;
console.log(c);
c = 2;
}
test();
}
fn();
函数声明提升
声明包括两种:变量声明和函数声明。不仅变量声明可以提升,函数声明也有提升操作
foo();
function foo(){
console.log(1);//
}
上面这个代码片段之所以能够在控制台输出1,就是因为foo()函数声明进行了提升,如下所示:
function foo(){
console.log(1);
}
foo();
函数声明会提升,但函数表达式却不会提升
foo();
var foo = function(){
console.log(1);//TypeError: foo is not a function
}
上面这段程序中的变量标识符foo被提升并分配给全局作用域,因此foo()不会导致ReferenceError。但是foo此时并没有赋值,foo()由于对undefined值进行函数调用而导致非法操作,因此会抛出TypeError异常
//变量提升后,代码如下所示:
var foo;
foo();
foo = function(){
console.log(1);
}
即使是具名的函数表达式也无法被提升
foo();//TypeError: foo is not a function
var foo = function bar(){
console.log(1);
};
//声明提升后,代码变为:
var foo;
foo();//TypeError: foo is not a function
foo = function bar(){
console.log(1);
};
[注意]函数表达式的名称只能在函数体内部使用,而不能在函数体外部使用
var bar;
var foo = function bar(){
console.log(1);
};
bar();//TypeError: bar is not a function
函数覆盖
函数声明和变量声明都会被提升。但是,函数声明会覆盖变量声明
var a;
function a(){}
console.log(a);//'function a(){}'
但是,如果变量存在赋值操作,则最终的值为变量的值
var a=1;
function a(){}
console.log(a);//
var a;
function a(){};
console.log(a);//'function a(){}'
a = 1;
console.log(a);//
[注意]变量的重复声明是无用的,但函数的重复声明会覆盖前面的声明(无论是变量还是函数声明)
【1】变量的重复声明无用
var a = 1;
var a;
console.log(a);//
【2】由于函数声明提升优先于变量声明提升,所以变量的声明无作用
var a;
function a(){
console.log(1);
}
a();//
【3】后面的函数声明会覆盖前面的函数声明
a();//
function a(){
console.log(1);
}
function a(){
console.log(2);
}
所以,应该避免在同一作用域中重复声明
深入理解javascript作用域系列第三篇——声明提升(hoisting)的更多相关文章
- 深入理解javascript作用域系列第三篇
前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javascript作用域系列第三篇——声明提升(hois ...
- 深入理解javascript函数系列第三篇——属性和方法
× 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...
- 深入理解javascript函数系列第三篇
前面的话 函数是javascript中特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本文是深入理解javascript函数 ...
- 深入理解javascript作用域系列第四篇——块作用域
× 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...
- 深入理解javascript作用域系列第四篇
前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀.简洁的 ...
- 深入理解javascript作用域系列第五篇——一张图理解执行环境和作用域
× 目录 [1]图示 [2]概念 [3]说明[4]总结 前面的话 对于执行环境(execution context)和作用域(scope)并不容易区分,甚至很多人认为它们就是一回事,只是高程和犀牛书关 ...
- 深入理解javascript作用域系列第五篇
前面的话 对于执行环境(execution context)和作用域(scope)并不容易区分,甚至很多人认为它们就是一回事,只是高程和犀牛书关于作用域的两种不同翻译而已.但实际上,它们并不相同,却相 ...
- 深入理解javascript对象系列第三篇——神秘的属性描述符
× 目录 [1]类型 [2]方法 [3]详述[4]状态 前面的话 对于操作系统中的文件,我们可以驾轻就熟将其设置为只读.隐藏.系统文件或普通文件.于对象来说,属性描述符提供类似的功能,用来描述对象的值 ...
- 深入理解javascript作用域系列第一篇——内部原理
× 目录 [1]编译 [2]执行 [3]查询[4]嵌套[5]异常[6]原理 前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域 ...
随机推荐
- BZOJ2888 资源运输(LCT启发式合并)
这道题目太神啦! 我们考虑他的每一次合并操作,为了维护两棵树合并后树的重心,我们只好一个一个的把节点加进去.那么这样一来看上去似乎就是一次操作O(nlogn),但是我们拥有数据结构的合并利器--启发式 ...
- CAS 4.0.0RC编译环境
CAS 4.0.0RC编译环境 Eclipse Java EE IDE for Web Developers. JDK1.7,注意用JDK1.8是会出现编译错误的. Maven 在编译出现test错误 ...
- c一些关键字
register:这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率.注意是尽可能,不是绝对. extern:可以置于变量或者函数前,以标示变量或者函数的定义 ...
- Xamarin.IOS之快速入门
欢迎大家加入以下开源社区 Xamarin-Cn:https://github.com/Xamarin-Cn Mvvmcross-Cn:https://github.com/Mvvmcross-Cn ...
- AIX 文件 打包 与 压缩 tar gzip compress 的使用
今天在Aix用tar -cvf 备份,打成tar包,占有硬盘空间过大,没有压缩比, 尝试使用tar -zcvf linux系统下可以用-z 命令 (z 用gzip来压缩/解压缩文件,加上该选项后可以 ...
- RCP:gef智能寻路算法(A star)
本路由继承自AbstactRouter,参数只有EditPart(编辑器内容控制器),gridLength(寻路用单元格大小),style(FLOYD,FLOYD_FLAT,FOUR_DIR). 字符 ...
- Wix 安装部署教程(九) --用WPF做安装界面
经常安装PC端的应用,特别是重装系统之后,大致分为两类.一类像QQ,搜狗输入法这样的.分三步走的:第一个页面可以自定义安装路径和软件许可.第二个页面显示安装进度条,第三个页面推荐其他应用.先不管人家怎 ...
- Programming Entity Framework CodeFirst--数据库约定和配置
这一章主要主要讲的是我们的模型如何映射到数据库,而不影响模型,以及不同的映射场景. 一.表名和列名 1.指定表名 [Table("PersonPhotos")] public cl ...
- 用“MEAN”技术栈开发web应用(一)AngularJs前端架构
前言 不知何时突然冒出“MEAN技术栈”这个新词,听起来很牛逼的样子,其实就是我们已经熟悉了的近两年在前端比较流行的技术,mongodb.express.angularjs.nodejs,由于这几项技 ...
- [问题解决]安装 SQL Server 无法开启NetFx3.5 的错误
谷歌了一下,该问题是由于系统中缺少.Net3.5相关特性造成的.需要手动安装一下3.5的环境 解决办法: Windows 徽标键+R打开运行窗口 输入Dism /online /enable-feat ...