跟我学习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 相关文章推荐
Javascript 同时提交多个Web表单的方法
Feb 19 Javascript
JavaScript实现在标题栏上显示当前日期的方法
Mar 19 Javascript
每天一篇javascript学习小结(String对象)
Nov 18 Javascript
使用CSS+JavaScript或纯js实现半透明遮罩效果的实例分享
May 09 Javascript
分分钟玩转Vue.js组件(二)
Mar 01 Javascript
Mongoose中document与object的区别示例详解
Sep 18 Javascript
微信小程序request请求后台接口php的实例详解
Sep 20 Javascript
JavaScript作用域、闭包、对象与原型链概念及用法实例总结
Aug 20 Javascript
jQuery Datatables 动态列+跨列合并实现代码
Jan 30 jQuery
js 解析 JSON 数据简单示例
Apr 21 Javascript
vue插件--仿微信小程序showModel实现模态提示窗功能
Aug 19 Javascript
js实现拖拽与碰撞检测
Sep 18 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
php简单的会话类代码
2011/08/08 PHP
使用PHP编写的SVN类
2013/07/18 PHP
PHP生成Gif图片验证码
2013/10/27 PHP
php简单图像创建入门实例
2015/06/10 PHP
PHP实现腾讯与百度坐标转换
2017/08/05 PHP
xtree.js 代码
2007/03/13 Javascript
JavaScript中的getMilliseconds()方法使用详解
2015/06/10 Javascript
使用JQuery FancyBox插件实现图片展示特效
2015/11/16 Javascript
JS实现设置ff与ie元素绝对位置的方法
2016/03/08 Javascript
vue使用v-if v-show页面闪烁,div闪现的解决方法
2018/10/12 Javascript
js中的面向对象之对象常见创建方法详解
2019/12/16 Javascript
JavaScript中的this原理及6种常见使用场景详解
2020/02/14 Javascript
python uuid模块使用实例
2015/04/08 Python
使用PyCharm配合部署Python的Django框架的配置纪实
2015/11/19 Python
利用Django-environ如何区分不同环境
2018/08/26 Python
python提取具有某种特定字符串的行数据方法
2018/12/11 Python
python实现连续图文识别
2018/12/18 Python
Python使用itchat模块实现群聊转发,自动回复功能示例
2019/08/26 Python
Python 3.10 的首个 PEP 诞生,内置类型 zip() 迎来新特性(推荐)
2020/07/03 Python
Opencv 图片的OCR识别的实战示例
2021/03/02 Python
Giuseppe Zanotti美国官方网站:将鞋履视为高级时装般精心制作
2018/02/06 全球购物
捷克浴室和厨房设备购物网站:SIKO
2018/08/11 全球购物
Pharmacy Online中文直邮网站:澳洲大型药房
2020/06/27 全球购物
你经历的项目中的SCM配置项主要有哪些?什么是配置项?
2013/11/04 面试题
前台文员个人求职信范文
2014/01/05 职场文书
讲文明树新风公益广告宣传方案
2014/02/25 职场文书
党员思想汇报材料
2014/12/19 职场文书
工作失误检讨书范文
2015/01/26 职场文书
教师师德表现自我评价
2015/03/05 职场文书
大专护理专业自荐信
2015/03/25 职场文书
2015年收银员个人工作总结
2015/04/01 职场文书
物业前台接待岗位职责
2015/04/03 职场文书
公司停电通知
2015/04/15 职场文书
工作年限证明模板
2015/06/15 职场文书
2019年度政务公开考核工作总结模板
2019/11/11 职场文书
Oracle 触发器trigger使用案例
2022/02/24 Oracle