JS中的 变量提升
首先纠正下,文章标题里的 “变量提升” 名词是随大流叫法,“变量提升” 改为 “标识符提升” 更准确。因为变量一般指使用 var 声明的标识符,JS 里使用 function 声明的标识符也存在提升(Hoisting)。
JS 存在变量提升,这个的设计其实是低劣的,或者是语言实现时的一个副作用。它允许变量不声明就可以访问,或声明在后使用在前。新手对于此则很迷惑,甚至许多使用JS多年老手也比较迷惑。但在 ES6 加入 let/const 后,变量Hoisting 就不存在了。
一、 变量未声明,直接使用
1
2
3
4
|
function test() { alert(notDefined); } test(); // ? |
报错是自然的
二. 变量声明在末尾
1
2
3
4
5
|
function test() { alert(declaredButNotAssigned); // undefined var declaredButNotAssigned; } test(); |
输出 undefined, 结果比上例有所改善,没有报错,代码可以运行,但变量值可能不是程序员所期望的。
三、 变量声明在末尾,同时给变量赋值
1
2
3
4
5
|
function test() { alert(declaredAndAssigned); // undefined var declaredAndAssigned = 1; } test(); |
结果和 二 相同, 很明显,并不会因为赋值了就输出 1。
二、三 都发生了变量提升(Hoisting),简单定义
变量提升: 在指定作用域里,从代码顺序上看是变量先使用后声明,但运行时变量的 “可访问性” 提升到当前作用域的顶部,其值为 undefined ,没有 “可用性”。
通常为了让大家理解起来容易些,把 三 拆成如下
1
2
3
4
5
|
function test() { var declaredAndAssigned; alert(declaredAndAssigned); // undefined declaredAndAssigned = 1; } |
即把声明和赋值分为两句。
这里强调 “代码顺序” 和 “运行顺序”,是因为多数时候我们写的代码都是顺序执行的,即 “代码顺序” 和 “运行顺序” 是一致的。这也符合人的大脑的思维过程。比如有过 C语言 经验的程序员
1
2
3
4
5
|
#include <stdio.h> int main() { int x = 1; printf( "%d, " , x); // 1 } |
两句代码,先声明整数型 x, 再输出。代码顺序和运行顺序是一致的,即正常运行。
如果顺序反过来
1
2
3
4
5
|
#include <stdio.h> int main() { printf( "%d, " , x); // error int x = 1; } |
此时,编译都不能通过了。但JS里可以反过来写,见二、三。
因此,有类 C语言 经验的程序员,都很清楚变量需要 先声明后使用,不然会报错。而到了JS里,有 变量提升 现象,可以 先使用后声明,C 的经验用到 JS 里迷惑便出现了。
四、 函数表达式也存在变量提升
1
2
3
4
5
|
function test() { alert(func); // undefined var func = function () {}; } test(); |
但如果想使用这个 func,则无可能
1
2
3
4
5
6
|
function test() { alert(func); // undefined func(); // 报异常 var func = function () {}; } test(); |
结果func 是 undefined,调用 func 则会报异常。 在上面的定义中提到了 可访问性 和 可用性 对应如下语句。
可访问性:alert(func),输出 undefined,可以运行,可以访问 func。
可用性: func(), 报异常,不能正常调用 func,表示无可用性。
二、三、四 都是使用 var 声明的变量,JS 里函数声明也会存在提升,只是这个 “变量” 比较特殊,它是一个 function 类型(可以作为函数、方法或构造器)。它的名字(标识符)也会提升到当前作用域的顶部。
五、函数声明的名也会提升到当前作用域顶部
1
2
3
4
5
6
7
8
|
function test() { alert(f1); // function f1(); // "called" function f1() { alert( 'called' ); } } test(); |
我们看到,声明 f1 在代码最末,f1 使用在前,alert(f1) 和 f1() 都正常执行,表示 可访问性 和 可用性 都有了。
前面说了,变量提升(Hoisting)没什么用,属于语言的低劣设计,好的习惯还是 “先声明后使用”。这个特性也会出现在不少大公司面试题里
题1:
1
2
3
4
5
6
7
8
9
|
// 写出以下代码的运行结果 var a = 1; function fn() { if (!a) { var a = 2; } alert(a); // ? } fn(); |
题2:
1
2
3
4
5
6
7
8
9
|
// 写出以下代码的运行结果 var a = 1; function fn() { a = 2; return ; function a() {} } fn(); alert(a); // ? |
但这一切随着 ES6 的 let/const 到来结束了,ES里除全局变量外,其它都使用 let/const,var 替换成 let 后变量提升就不复存在了。
1
2
3
4
5
6
7
8
9
10
|
function test() { alert(declaredButNotAssigned1); // 报异常 alert(declaredButNotAssigned2); // 报异常 alert(func); // 报异常 let declaredButNotAssigned1; let declaredButNotAssigned2 = true ; let func = function () {}; } test(); |
这强制程序员养成好的习惯,变量需要“先声明再使用”,否则报错。
以下摘自MDN的关于let不在发生变量提升的描述
In ECMAScript 6,
let
does not hoist the variable to the top of the block. If you reference a variable in a block before thelet
declaration for that variable is encountered, this results in aReferenceError
, because the variable is in a "temporal dead zone" from the start of the block until the declaration is processed.
用 let 声明变量后,typeof 却不再安全了
1
2
3
4
|
if (condition) { alert( typeof num); // Error! let num = 100; } |
以前可以用 typeof == 'undefined',来判断是否引入了某lib,比如jQuery
1
2
3
4
|
// 判断jQuery是否引入了 if ( typeof $ !== 'undefined' ) { // do something }... |
jQuery没有引入,$ 没有声明,这句也不会报错而影响到下面的代码执行,但如果是 let 声明的就会报错了。
转自:http://www.cnblogs.com/snandy/p/4552078.html
JS中的 变量提升的更多相关文章
- js中的变量提升(hoisting)
来看如下代码: function HelloJS(){ var array = [1,2,3,4,5]; for(var i in array){ } alert(i); } HelloJS(); a ...
- js中的变量提升(Hoisting)
<script> function test(){ console.log(a); console.log(foo()); var a=1; function foo(){ return ...
- js中的变量提升和函数提升
从上周开始,我所在的学习小组正式开始了angular的学习,angular是全面支持es6的,所以语法上和以前的angular有了很大的不同,比如变量声明时就抛弃了var,而选择了let和const: ...
- js中的变量提升与函数提升
先看看一个简单的代码 var str='Hello World'; alert(str);//弹出 Hello World 再看一段代码: var v='Hello World'; (function ...
- js中的变量提升
var v='Hello World'; (function(){ alert(v); var v='I love you'; })() 会出现alert出来的是undefined,原因是因为在函数域 ...
- js 中的变量声明提前总结
一.var 声明 ES6之前,js 中声明变量基本上用 var 关键字: 1.如果访问未声明的变量,会报错:ReferenceError 2.声明了未赋值,值为 undefined,跟前面的报错是两回 ...
- Js中执行变量中的命令语句,也就是所谓的宏替换(很实用的例子)
Js中执行变量中的命令语句,也就是所谓的宏替换(很实用的例子) 由其做动态编程时非常有用,必须符合js中的语法,用eval能够执行. var aaa="alert('这是变量中的语句')&q ...
- Javascript开发技巧(JS中的变量、运算符、分支结构、循环结构)
一.Js简介和入门 继续跟进JS开发的相关教程. <!-- [使用JS的三种方式] 1.HTML标签中内嵌JS(不提倡使用): 示例:<button onclick="javas ...
- JavaScript 基础——使用js的三种方式,js中的变量,js中的输出语句,js中的运算符;js中的分支结构
JavaScript 1.是什么:基于浏览器 基于(面向)对象 事件驱动 脚本语言 2.作用:表单验证,减轻服务器压力 添加野面动画效果 动态更改页面内容 Ajax网络请求 () 3.组成部分:ECM ...
随机推荐
- 招聘一个靠谱的 iOS
近一年内陆续面试了不少人了,从面试者到面试官的转变让我对 iOS 招聘有了更多的感受.经过了前段时间的一大波面试,我们终于找到了志同道合的小伙伴,面试也暂时告一段落了.总结下面试人过程中的感受,你也可 ...
- aws在线技术峰会笔记-基于AWS的Devops最佳实践
AWS CodeCommit AWS CodePipeline 可以和github集成 可以支持蓝绿部署 微服务架构, API Gateway进行转发
- iOS AFNetworking中cookie的读取与设置
参考: http://blog.csdn.net/zhaoxy_thu/article/details/20532879 实际上AFNetworking中并没有专门针对cookie封装的代码,但是由于 ...
- 点击datagrid弹出ldhdialog,点击弹出框的按钮,关闭且刷新datagrid
datagrid里的js这么写 //点击添加按钮触发 function superadd(title,addurl,gname,width,height) { gridname=gname; crea ...
- android,NDK android.mk相关
1.c++ try...catch的支持 需要在Android.mk 中添加 LOCAL_CPPFLAGS += -fexceptions,或者在Application.mk中添加APP_CPPFLA ...
- 【转】 void与void*详解
void关键字的使用规则: 1. 如果函数没有返回值,那么应声明为void类型: 2. 如果函数无参数,那么应声明其参数为void: 3. 如果函数的参数可以是任意类型指针,那么应声明其参数为void ...
- 【转】数据预处理之独热编码(One-Hot Encoding)
原文链接:http://blog.csdn.net/dulingtingzi/article/details/51374487 问题由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. ...
- python 中分支结构(switch)
可通过字典调用:{1:case1,2:case2}.get(x,lambda *args,**key:)() # 编写一个计算器 # -*- coding=utf-8 -*- def jia(x,y) ...
- ios批量打包
最近我们接到了新的需求,需要打出类似xx001-xx100共100个这样的ipa渠道包,不需要签名.(这批ipa包后续会用企业证书签名,不会影响AppStore的) 这些包所有的功能.内容都是一样的, ...
- 介绍开源的.net通信框架NetworkComms框架之八 UDP通信
原文网址: http://www.cnblogs.com/csdev Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是 ...