跟我学习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 相关文章推荐
关于jQuery新的事件绑定机制on()的使用技巧
Apr 26 Javascript
jquery插件tooltipv顶部淡入淡出效果使用示例
Dec 05 Javascript
JavaScript中的typeof操作符用法实例
Apr 05 Javascript
js中判断变量类型函数typeof的用法总结
Aug 09 Javascript
JavaScript随机生成颜色的方法
Oct 15 Javascript
JS实现根据用户输入分钟进行倒计时功能
Nov 14 Javascript
vue2.0 axios前后端数据处理实例代码
Jun 30 Javascript
vue页面使用阿里oss上传功能的实例(二)
Aug 09 Javascript
解决vue router组件状态刷新消失的问题
Aug 01 Javascript
wx-charts 微信小程序图表插件的具体使用
Aug 18 Javascript
AntV F2和vue-cli构建移动端可视化视图过程详解
Oct 08 Javascript
vue使用wavesurfer.js解决音频可视化播放问题
Apr 04 Vue.js
跟我学习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
用Flash图形化数据(二)
2006/10/09 PHP
PHP利用APC模块实现大文件上传进度条的方法
2015/10/29 PHP
详解PHP如何更好的利用PHPstorm的自动提示
2017/08/18 PHP
基于jquery实现的一个选择中国大学的弹框 (数据、步骤、代码)
2012/07/26 Javascript
鼠标经过tr时,改变tr当前背景颜色
2014/01/13 Javascript
用js通过url传参把数据从一个页面传到另一个页面
2014/09/01 Javascript
轻松实现Bootstrap图片轮播
2020/04/20 Javascript
手机端图片缩放旋转全屏查看PhotoSwipe.js插件实现
2016/08/25 Javascript
Vue2递归组件实现树形菜单
2017/04/10 Javascript
用nodejs实现json和jsonp服务的方法
2017/08/25 NodeJs
浅谈vuex之mutation和action的基本使用
2017/08/29 Javascript
新版vue-cli模板下本地开发环境使用node服务器跨域的方法
2018/04/03 Javascript
微信小程序实现弹出菜单
2018/07/19 Javascript
vue中v-for通过动态绑定class实现触发效果
2018/12/06 Javascript
jQuery使用$.extend(true,object1, object2);实现深拷贝对象的方法分析
2019/03/06 jQuery
Jquery让form表单异步提交代码实现
2019/11/14 jQuery
解决vuex刷新数据消失问题
2020/11/12 Javascript
python标准算法实现数组全排列的方法
2015/03/17 Python
详解Python多线程Selenium跨浏览器测试
2017/04/01 Python
Python只用40行代码编写的计算器实例
2017/05/10 Python
Python安装Numpy和matplotlib的方法(推荐)
2017/11/02 Python
python交互式图形编程实例(一)
2017/11/17 Python
用Python实现KNN分类算法
2017/12/22 Python
实例讲解Python中浮点型的基本内容
2019/02/11 Python
python实现一行输入多个值和一行输出多个值的例子
2019/07/16 Python
如何在sublime编辑器中安装python
2020/05/20 Python
python和opencv构建运动检测器的实现
2021/03/03 Python
当x.equals(y)等于true时,x.hashCode()与y.hashCode()可以不相等,这句话对不对
2015/05/02 面试题
业务部主管岗位职责
2014/01/29 职场文书
2014年工作总结与下年工作计划
2014/11/27 职场文书
2014年教研员工作总结
2014/12/23 职场文书
教师党员承诺书2015
2015/01/21 职场文书
Springboot如何使用logback实现多环境配置?
2021/06/16 Java/Android
Vue3.0中Ref与Reactive的区别示例详析
2021/07/07 Vue.js
python分分钟绘制精美地图海报
2022/02/15 Python
python中pymysql包操作数据库方法
2022/04/19 Python