跟我学习javascript的var预解析与函数声明提升


Posted in Javascript onNovember 16, 2015

1、var 变量预编译

JavaScript 的语法和 C 、Java、C# 类似,统称为 C 类语法。有过 C 或 Java 编程经验的同学应该对“先声明、后使用”的规则很熟悉,如果使用未经声明的变量或函数,在编译阶段就会报错。然而,JavaScript 却能够在变量和函数被声明之前使用它们。下面我们就深入了解一下其中的玄机。

先来看一段代码:

(function() {
 console.log(noSuchVariable);//ReferenceError: noSuchVariable is not defined
})();

运行上面代码立马就报错,不过,这也正是我们期望的,因为 noSuchVariable 变量根本就没有定义过嘛!再来看看下面的代码:

(function() {
 console.log(declaredLater); //undefined
 var declaredLater = "Now it's defined!";
 console.log(declaredLater);// "Now it's defined!"
})();

首先,上面这段代码是正确的,没有任何问题。但是,为什么不报错了?declaredLater 变量是在调用语句后面定义的啊?为什么居然输出的是 undefined?

这其实是 JavaScript 解析器搞的鬼,解析器将当前作用域内声明的所有变量和函数都会放到作用域的开始处,但是,只有变量的声明被提前到作用域的开始处了,而赋值操作被保留在原处。上述代码对于解析器来说其实是如下这个样子滴:

(function() {
 var declaredLater; //声明被提前到作用域开始处了!
 console.log(declaredLater); // undefined
 declaredLater = "Now it's defined!"; //赋值操作还在原地!
 console.log(declaredLater);//"Now it's defined!"
})();

这就是为什么上述代码不报异常的原因!变量和函数经过“被提前”之后,declaredLater 变量其实就被放在了调用函数的前面,根据 JavaScript 语法的定义,已声明而未被赋值的变量会被自动赋值为 undefined ,所以,第一次打印 declaredLater 变量的值就是 undefined,后面我们对 declaredLater 变量进行了赋值操作,所以,第二次再打印变量就会输出Now it's defined!。

再来看一个例子:

var name = "Baggins";
(function () {
 console.log("Original name was " + name);// "Original name was undefined"
 var name = "Underhill";
 console.log("New name is " + name);// "New name is Underhill"
})();

上述代码中,我们先声明了一个变量 name ,我们的本意是希望在第一次打印 name 变量时能够输出全局范围内定义的 name 变量,然后再在函数中定义一个局部 name 变量覆盖全局变量,最后输出局部变量的值。可是第一次输出的结果和我们的预期完全不一致,原因就是我们定义的局部变量在其作用域内被“提前”了,也就是变成了如下形式:

var name = "Baggins";
(function () {
 var name; //注意:name 变量被提前了!
 console.log("Original name was " + name);// "Original name was undefined"
 name = "Underhill";
 console.log("New name is " + name);//"New name is Underhill"
})();

由于 JavaScript 具有这样的“怪癖”,所以建议大家将变量声明放在作用域的最上方,这样就能时刻提醒自己注意了。

2、函数声明“被提前”

前边说的是变量,接下来我们说说函数。

函数的“被提前”还要分两种情况,一种是函数声明,第二种是函数作为值赋值给变量,也即函数表达式。

先说第一种情况,上代码:

isItHoisted();//"Yes!"
function isItHoisted() { 
 console.log("Yes!");
}

如上所示,JavaScript 解释器允许你在函数声明之前使用,也就是说,函数声明并不仅仅是函数名“被提前”了,整个函数的定义也“被提前”了!所以上述代码能够正确执行。

再来看第二种情况:函数表达式形式。还是先上代码:

definitionHoisted();// "Definition hoisted!"
definitionNotHoisted();// TypeError: undefined is not a function
function definitionHoisted() { 
 console.log("Definition hoisted!");
}
var definitionNotHoisted = function () { 
 console.log("Definition not hoisted!");
};

我们做了一个对比,definitionHoisted 函数被妥妥的执行了,符合第一种类型;definitionNotHoisted 变量“被提前”了,但是他的赋值(也就是函数)并没有被提前,从这一点上来说,和前面我们所讲的变量“被提前”是完全一致的,并且,由于“被提前”的变量的默认值是 undefined ,所以报的错误属于“类型不匹配”,因为 undefined 不是函数,当然不能被调用。

总结
通过上面的讲解可以总结如下:

变量的声明被提前到作用域顶部,赋值保留在原地
函数声明整个“被提前”
函数表达式时,只有变量“被提前”了,函数没有“被提前”
3、var的副作用

隐式全局变量和明确定义的全局变量间有些小的差异,就是通过delete操作符让变量未定义的能力。

通过var创建的全局变量(任何函数之外的程序中创建)是不能被删除的。
无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的。
这表明,在技术上,隐式全局变量并不是真正的全局变量,但它们是全局对象的属性。属性是可以通过delete操作符删除的,而变量是不能的:

// 定义三个全局变量
var global_var = 1;
global_novar = 2;  // 反面教材
(function () {
 global_fromfunc = 3; // 反面教材
}());

// 试图删除
delete global_var;  // false
delete global_novar; // true
delete global_fromfunc; // true

// 测试该删除
typeof global_var;  // "number"
typeof global_novar; // "undefined"
typeof global_fromfunc; // "undefined"

在ES5严格模式下,未声明的变量(如在前面的代码片段中的两个反面教材)工作时会抛出一个错误。

4、单var形式声明变量

在函数顶部使用单var语句是比较有用的一种形式,其好处在于:

提供了一个单一的地方去寻找功能所需要的所有局部变量
防止变量在定义之前使用的逻辑错误
少代码(类型啊传值啊单线完成)
单var形式长得就像下面这个样子:

function func() {
 var a = 1,
  b = 2,
  sum = a + b,
  myobject = {},
  i,
  j;
 // function body...
}

您可以使用一个var语句声明多个变量,并以逗号分隔。像这种初始化变量同时初始化值的做法是很好的。这样子可以防止逻辑错误(所有未初始化但声明的变量的初始值是undefined)和增加代码的可读性。在你看到代码后,你可以根据初始化的值知道这些变量大致的用途。

以上就是针对javascript的var预解析与函数声明提升的学习内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
js表格分页实现代码
Sep 18 Javascript
招聘网站基于jQuery实现自动刷新简历
May 10 Javascript
JavaScript模拟可展开、拖动与关闭的聊天窗口实例
May 12 Javascript
jQuery删除节点用法示例(remove方法)
Sep 08 Javascript
Jquery针对tr td的一些实用操作方法(必看篇)
Oct 05 Javascript
利用React-router+Webpack快速构建react程序
Oct 27 Javascript
解决vue-cli中stylus无法使用的问题方法
Jun 19 Javascript
EasyUI实现下拉框多选功能
Nov 07 Javascript
使用js实现一个简单的滚动条过程解析
Sep 10 Javascript
Vue利用localStorage本地缓存使页面刷新验证码不清零功能的实现
Sep 04 Javascript
原生JavaScript实现贪吃蛇游戏
Nov 04 Javascript
uniapp 微信小程序 自定义tabBar 导航
Apr 22 Javascript
跟我学习javascript的全局变量
Nov 16 #Javascript
浅析JavaScript访问对象属性和方法及区别
Nov 16 #Javascript
跟我学习javascript的基本类型和引用类型
Nov 16 #Javascript
简单实现限制uploadify上传个数
Nov 16 #Javascript
跟我学习javascript的隐式强制转换
Nov 16 #Javascript
跟我学习javascript的浮点数精度
Nov 16 #Javascript
跟我学习javascript的严格模式
Nov 16 #Javascript
You might like
全国FM电台频率大全 - 20 广西省
2020/03/11 无线电
PHP关联链接常用代码
2012/11/05 PHP
php $_SERVER windows系统与linux系统下的区别说明
2014/02/14 PHP
[原创]PHP实现逐行删除文件右侧空格的方法
2015/12/25 PHP
[原创]PHP字符串中插入子字符串方法总结
2016/05/06 PHP
PHP微信开发之有道翻译
2016/06/23 PHP
PHP isset()及empty()用法区别详解
2020/08/29 PHP
ExtJs Excel导出并下载IIS服务器端遇到的问题
2011/09/16 Javascript
深入理解Javascript动态方法调用与参数修改的问题
2013/12/10 Javascript
一个简单的动态加载js和css的jquery代码
2014/09/01 Javascript
jQuery中Ajax的load方法详解
2015/01/14 Javascript
ES6新特征数字、数组、字符串
2016/10/01 Javascript
vue.js指令v-for使用及索引获取
2016/11/03 Javascript
nodejs个人博客开发第一步 准备工作
2017/04/12 NodeJs
vue.js实例todoList项目
2017/07/07 Javascript
JS实现的数组去除重复数据算法小结
2017/11/17 Javascript
使用JS判断移动端手机横竖屏状态
2018/07/30 Javascript
vue中activated的用法
2021/01/03 Vue.js
Nodejs 数组的队列以及forEach的应用详解
2021/02/25 NodeJs
[01:46]TI4西雅图DOTA2前线报道 中国选手抱团调时差
2014/07/08 DOTA
Python利用多进程将大量数据放入有限内存的教程
2015/04/01 Python
独特的python循环语句
2016/11/20 Python
python中的字符串内部换行方法
2018/07/19 Python
详解Python with/as使用说明
2018/12/13 Python
Python 最大概率法进行汉语切分的方法
2018/12/14 Python
python使用参数对嵌套字典进行取值的方法
2019/04/26 Python
Python 数据的累加与统计的示例代码
2020/08/03 Python
浅谈three.js中的needsUpdate的应用
2012/11/12 HTML / CSS
Bally巴利英国官网:经典瑞士鞋履、手袋及配饰奢侈品牌
2018/05/07 全球购物
美国职棒大联盟的官方手套、球和头盔:Rawlings
2020/02/15 全球购物
Linux管理员面试经常问道的相关命令
2013/04/29 面试题
房地产融资计划书
2014/01/10 职场文书
九年级数学教学反思
2014/02/02 职场文书
普通大学毕业生自荐信范文
2014/02/23 职场文书
超市周年庆活动方案
2014/08/16 职场文书
2014年医院工作总结
2014/11/20 职场文书