浅谈JS变量声明和函数声明提升
先来两个问题
很多时候,在直觉上,我们都会认为JS代码在执行时都是自上而下一行一行执行的,但是实际上,有一种情况会导致这个假设是错误的。
a = 2;
var a;
console.log(a);
按照传统眼光,console.log(a)输出的应该是undefined,因为var a在a = 2之后。但是,输出的是2。
再看第二段代码:
console.log(a);
var a = 2;
有人会想到第一段代码,然后回答undefined。还有人会认为a在使用前未被声明,因此抛出ReferenceError异常。遗憾的是,结果是undefined。
为什么呢?
从编译器的角度看问题
JS在编译阶段,编译器的一部分工作就是找到所有声明,并用合适的作用域将他们关联起来。对于一般人来说var a = 2仅仅是一个声明,但是,JS编译器会将该段代码拆为两段,即:var a和a = 2。var a这个定义声明会在编译阶段执行,而a = 2这个赋值声明会在原地等待传统意义上的从上到下的执行。
所以,在编译器的角度来看,第一段代码实际上是这样的:
var a; // 编译阶段执行
a = 2;
console.log(a);
所以,输出的是2。
类似的,第二个代码片段实际上是这样执行的:
var a;
console.log(a);
a = 2;
这样的话,很明显,输出的应该是undefined,因为只对a进行了定义声明,没有对a进行赋值声明。
从上面这两个例子可以看出,变量声明会从它们在代码中出现的位置被移动到当前作用域的最上方进行执行,这个过程叫做提升。
函数提升
下面,再来看一段代码
foo();
function foo () {
console.log(a);
var a = 2;
}
在这个例子中,输出undefined而不会报错,因为,函数变量也能提升。即,实际上像如下的情况运行。
function foo () {
var a;
console.log(a);
a = 2;
}
foo();
说到这里,你是不是认为提升很简单,只要把变量都放到当前作用域最上方执行就好了?
下面,我来说一种意外情况:函数表达式的提升情况。
函数表达式的提升情况
foo();
var foo = function bar () {
console.log(a);
var a = 2;
}
你是不是想说,这个例子不是和之前的那个差不多吗?输出的当然是undefined呀。但是,结果是,不输出,因为JS报了TypeError错误!
因为,函数表达式不会进行提升!
该例子的实际运行情况是这样的:
var foo;
foo();
foo = function bar () {
var a;
console.log(a);
a = 2;
}
由于执行时,在作用域中找得到foo(该作用域最上方声明了foo),所以不会报ReferenceError错误,但是,foo此时没有进行赋值(如果foo是一个函数声明而不是函数表达式,那么就会赋值),也就是说实际上foo()是对一个值为undefined的变量进行函数调用,所以,理所应当抛出TypeError异常。
值得一提的是,即使是具名的函数表达式,名称标识符在赋值之前也无法在所在作用域中使用,即:
foo(); // TypeError
bar(); // ReferenceError
var foo = function bar () {}
函数优先
函数声明和变量声明都会被提升,但是有一个值得注意的细节,那就是,函数会首先提升,然后才是变量!
看下面这一段代码:
foo();
var foo;
function foo () {
console.log(1);
}
foo = function () {
console.log(2);
}
这一段代码会输出1,原因就在于,函数优先。
这一段代码可以转换为以下形式:
function foo () {
console.log(1);
}
var foo; // 重复声明,被忽略
foo(); // 输出1
foo = function () {
console.log(2);
}
如果,在代码的结尾再执行一次foo函数,此时,输出的是1。
function foo () {
console.log(1);
}
var foo; // 重复声明,被忽略
foo(); // 输出1
foo = function () {
console.log(2);
}
foo(); // 输出2
因为,尽管重复的声明会被忽略了,但是后面的函数还是可以覆盖前面的函数。
明白了这个道理,你就可以理解下面这个问题了:
foo();
var a = true;
if (a) {
function foo () {
console.log("a");
}
} else {
function foo () {
console.log("b");
}
}
你猜这道题输出的结果是什么?是b!为什么?因为foo进行了两次的声明,但是,后一次函数覆盖了前一次的函数。所以调用foo时,永远调用的都是console.log("b")。
总结
1.所有声明(变量和函数)都会被移动到各自作用域的最顶端,这个过程被称为提升
2.函数表达式等各种赋值操作并不会被提升
3.函数优先原则
4.尽量避免产生提升问题
参考资料:You Dont't Know JS: SCope & Closures
浅谈JS变量声明和函数声明提升的更多相关文章
- 浅谈js变量作用域
变量的作用域也是前端面试题常考的一个问题,掌握下面几个规律可以帮你更好的理解js的作用域. 1.作用域优先级遵循就近原则,函数内部的作用域优先级大于外部 var a=456; var b=111; f ...
- 浅谈JS中的高级函数
在JavaScript中,函数的功能十分强大.它们是第一类对象,也可以作为另一个对象的方法,还可以作为参数传入另一个函数,不仅如此,还能被一个函数返回!可以说,在JS中,函数无处不在,无所不能,堪比孙 ...
- 浅谈JS中 var let const 变量声明
浅谈JS中 var let const 变量声明 用var来声明变量会出现的问题: 1. 允许重复的变量声明:导致数据被覆盖 2. 变量提升:怪异的数据访问.闭包问题 3. 全局变量挂载到全局对象:全 ...
- 浅谈JavaScript变量声明提升
前段时间阿里实习生内推,一面就被刷了,也是郁闷.今天系统给发通知,大致意思就是内推环节不足以了解彼此,还可以参加笔试,于是赶紧再投一次.官网流程显示笔试时间3月31日,时间快到了,开始刷题.网上搜了一 ...
- [js]变量声明、函数声明、函数定义式、形参之间的执行顺序
一.当函数声明和函数定义式(变量赋值)同名时 function ledi(){ alert('ledi1'); }; ledi(); var ledi = function (){ alert('le ...
- 浅谈JS中的闭包
浅谈JS中的闭包 在介绍闭包之前,我先介绍点JS的基础知识,下面的基础知识会充分的帮助你理解闭包.那么接下来先看下变量的作用域. 变量的作用域 变量共有两种,一种为全局变量,一种为局部变量.那么全局变 ...
- 浅谈JS严格模式
浅谈JS严格模式 简介 何为严格模式?严格模式(strict mode)即在严格的条件下运行,在严格模式下,很多正常情况下不会报错的问题语句,将会报错并阻止运行. 但是,严格模式可以显著提高代码的健壮 ...
- 浅谈JS中的!=、== 、!==、===的用法和区别 JS中Null与Undefined的区别 读取XML文件 获取路径的方式 C#中Cookie,Session,Application的用法与区别? c#反射 抽象工厂
浅谈JS中的!=.== .!==.===的用法和区别 var num = 1; var str = '1'; var test = 1; test == num //tr ...
- 浅谈 js 语句块与标签
原文:浅谈 js 语句块与标签 语句块是什么?其实就是用 {} 包裹的一些js代码而已,当然语句块不能独立作用域.可以详细参见这里<MDN block> 也许很多人第一印象 {} 不是对象 ...
随机推荐
- 浅显易懂的谈一谈python中的装饰器!!
hello大家好~~我是稀里糊涂林老冷,一天天稀里糊涂的. 前一段时间学习了装饰器,觉着这东西好高大上哇靠!!哈哈,一定要总结一下,方便以后自己查阅,也希望帮助其他伙伴们共同进步! 装饰器: 大家可以 ...
- linux 安装jdk以及nginx详细过程
一.安装jdk 1:首先下载jdk到本地,然后通过git 上传到linux服务器上 2:进入目录usr,并创建目录java,将jdk的压缩文件移动到该目录下 cd /usr mkdir java mv ...
- JavaScript 克隆
JavaScript 克隆 本次学习内容: 克隆:只克隆标签和属性,不克隆文本. 克隆的功能,如果不添加使用Ture,就只会克隆标签和属性,不会克隆文本. 克隆的参数全部是节点对象,不能是字符串 &l ...
- asp.net core 三 Nuget包管理
参考连接:http://www.cnblogs.com/netcore2/p/7412891.html 这里的说明,基本就是学习了别人的文章,自己做了个备份 asp.net c ...
- java Thread 接口学习
对于程序员来说 Thread应该都不会陌生,具体的接口调用不是本篇的重点.Thread的基本概念及接口的使用:java多线程 下面将更多的从底层实现角度讲一下Thread. Thread的声明如下: ...
- jQuery系列 第三章 jQuery框架操作CSS
第三章 jQuery框架操作CSS 3.1 jQuery框架的CSS方法 jQuery框架提供了css方法,我们通过调用该方法传递对应的参数,可以方便的来批量设置标签的CSS样式. 使用JavaScr ...
- UVALive - 3530:Martian Mining
dp 可以发现,对于(i,j),要么把它运上去,那么把它运到左边,枚举一下即可 #include<cstdio> #include<cstdlib> #include<a ...
- 【luoguP4006 清华集训2017】小Y和二叉树
题目描述 小 Y 是一个心灵手巧的 OIer,她有许多二叉树模型. 小 Y 的二叉树模型中,每个结点都具有一个编号,小 Y 把她最喜欢的一个二叉树模型挂在了墙上,树根在最上面,左右子树分别在树根的左下 ...
- ●UVA 11796 Dog Distance
题链: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...
- Codeforces Round #438 D. Huge Strings
Description You are given n strings s1, s2, ..., sn consisting of characters 0 and 1. m operations a ...