javscript中变量的作用域和提升
var a = 1;function foo() { if (!a) { var a = 10; } alert(a);}; foo();上面这段代码在运行时会产生什么结果?
初学者思路:
1.创建了全局变量 a,定义其值为 1
2.创建了函数 foo
3.在 foo 的函数体内,if 语句将不会执行,因为 !a 会将变量 a 转变成布尔的假值,也就是 false
4.跳过条件分支,alert 变量 a,最终的结果应该是输出 1,但正确答案却是10.
为什么是10呢?这是因为变量提升的原因造成的。
原因分析:
声明与定义
为了理解变量提升,我们先来看一个简单的情况:
var a = 1;
var a; 叫做 “声明变量”
var a = 1; 叫做 “定义变量”
声明:是指你声称某样东西的存在,比如一个变量或一个函数;但你没有说明这样东西到底是什么,仅仅是告诉解释器这样东西存在而已
定义:是指你指明了某样东西的具体实现,比如一个变量的值是多少,一个函数的函数体是什么,确切的表达了这样东西的意义。
总结一下:
var a; // 这是声明
a = 1; // 这是定义(赋值)
var a = 1; // 合二为一:声明变量的存在并赋值给它
重点来了:当你以为你只做了一件事情的时候(var a = 1),实际上解释器把这件事情分解成了两个步骤,一个是声明(var a),另一个是定义(a = 1)。
这和变量提升有何关系?
回到最开始的那个令人困惑的例子,我告诉你解释器是如何分析你的代码的:
var a;
a = 1;
function foo(){
var a; //关键点
if (!a) {
a = 10;
}
alert (a); //此时的a并非是函数体外的那个变量
}
如代码所示,在进入函数体后解释器声明了新的变量 a,而无论 if 语句的条件如何,都将为新的变量 a 赋值为 10。
作用域:
有人可能会问了:“为什么不是在 if 语句内声明变量 a?”
因为 JavaScript 没有块级作用域,只有函数作用域(Function Scoping),所以说不是看见一对花括号 {} 就代表产生了新的作用域。
当解析器读到 if 语句的时候,它发现此处有一个变量声明和赋值,于是解析器会将其声明提升至当前作用域的顶部(这是默认行为,并且无法更改),这个行为就叫做变量提升。
如果我就是想要 alert(a) 出那个 1 怎么办?
创建新的作用域
alert(a) 在执行的时候,会去寻找变量 a 的位置,它从当前作用域开始向上(或者说向外)一直查找到顶层作用域为止,若是找不到就报 undefined。
因为在 alert(a) 的同级作用域里,我们再次声明了本地变量 a,所以它报 10;所以我们可以把本地变量 a 的声明向下(或者说向内)移动,这样 alert(a) 就找不到它了。
记住:JavaScript 只有函数作用域!
var a = 1;function foo() { if (!a) { (function() { // 这是上一篇说到过的 IIFE,它会创建一个新的函数作用域 var a = 2; // 并且该作用域在 foo() 的内部,所以 alert 访问不到 }()); // 不过这个作用域可以访问上层作用域哦,这就叫:“闭包” }; alert(a);};foo();你或许在无数的 JavaScript
书籍和文章里读到过:“请始终保持作用域内所有变量的声明放置在作用域的顶部”,因为这样可以避免 变量提升
特性给你带来的困扰,在当前作用域内有哪些变量可以访问。但是,变量声明的提升并非 提升的全部。在 js中,有四种方式可以让命名进入到作用域中(按优先级):
1.语言定义的命名:比如 this 或者 arguments,它们在所有作用域内都有效且优先级最高,所以在任何地方你都不能把变量命名为 this 之类的,这样是没有意义的
2.形式参数:函数定义时声明的形式参数会作为变量被 hoisting 至该函数的作用域内。所以形式参数是本地的,不是外部的或者全局的。当然你可以在执行函数的时候把外部变量传进来,但是传进来之后就是本地的了
3.函数声明:函数体内部还可以声明函数,不过它们也都是本地的了
4.变量声明:这个优先级其实还是最低的,不过它们也都是最常用的
进一步理解声明和定义的区别:提升只提升了命名,没有提升定义
函数声明与函数表达式的差别
先看两个例子:
function test(){
foo();
}在第一个例子里,函数 foo
是一个声明,既然是声明就会被提升(我特意包裹了一个外层作用域,因为全局作用域需要你的想象,不是那么直观,但是道理是一样的),所以在执行
foo() 之前,作用域就知道函数 foo 的存在了。这叫做函数声明(Function
Declaration),函数声明会连通命名和函数体一起被提升至作用域顶部。
在第二个例子里,被提升的仅仅是变量名 foo,至于它的定义依然停留在原处。因此在执行 foo() 之前,作用域只知道 foo
的命名,不知道它到底是什么,所以执行会报错(通常会是:undefined is not a
function)。这叫做函数表达式(Function Expression),函数表达式只有命名会被提升,定义的函数体则不会。
javscript中变量的作用域和提升的更多相关文章
- 关于javascript中变量及函数的提升
javascript中变量以及函数的提升,在我们平时的项目中其实还是挺常用的,尤其是大型项目中,不知不觉就会顺手添加一些变量,而有时候自己的不小心就会酿成一些不必要错误,趁有时间整理一下自己对于js中 ...
- Python中变量的作用域(variable scope)
http://www.crifan.com/summary_python_variable_effective_scope/ 解释python中变量的作用域 示例: 1.代码版 #!/usr/bin/ ...
- C/C++——C++变量的作用域与生命周期,C语言中变量的作用域和生命周期
全局变量 作用域:全局作用域(全局变量只需在一个源文件中定义,就可以作用于所有的源文件.) 生命周期:程序运行期一直存在 引用方法:其他文件中要使用必须用extern 关键字声明要引用的全局变量. 内 ...
- 注意for循环中变量的作用域-乾颐堂
1 2 for e in collections: pass 在for 循环里, 最后一个对象e一直存在在上下文中.就是在循环外面,接下来对e的引用仍然有效. 这里有个问题容易被忽略,如果在循 ...
- 注意for循环中变量的作用域
for e in collections: pass 在for 循环里, 最后一个对象e一直存在在上下文中.就是在循环外面,接下来对e的引用仍然有效. 这里有个问题容易被忽略,如果在循环之前已经有一个 ...
- JavaScript 中变量、作用域和内存问题的学习
这是我学习JavaScript的第二篇文章,之前做过几年的Java开发,发现JavaScript虽然也是面向对象的语言但是确实有很多不同之处.就本篇博客,主要学习总结一下最近学习到的JavaScrip ...
- C语言中变量的作用域和生命周期
变量的类型: 1. 局部变量和全局变量 局部变量也称为内部变量. 局部变量是在函数内作定义说明的.其作用域仅限于函数内, 离开该函数后再 使用这种变量是非法的. 全局变量也称为外部变量,它是在函数外部 ...
- Delphi过程和函数中变量的作用域
变量的作用域是指变量能被某一子程序识别的范围. 全局变量和局部变量.全局变量是指在程序的type区定义的变量,而局部变量是在过程或函数的定义部分声明的变量.全局变量在整个程序中都有意义,局部变量只在它 ...
- if、while中变量的作用域问题
我们知道,函数.类会改变当前变量的作用域.if,while等分支循环结构会继承外部作用域,即外部变量对分支循环结构内部可见. 但是C语言不支持if,while等分支循环结构内部作用域对外可见,而PHP ...
随机推荐
- C语言提高 (4) 第四天 数组与数组作为参数时的数组指针
1昨日回顾 const int 和 int const是一样的 const char *p;值不变 char * const p; 指针不能变 编译器对参数的退化: 第三种模型: 三级指针 三级指针局 ...
- [tyvj 1061] Mobile Service (线性dp 滚动数组)
3月15日第一题! 题目限制 时间限制 内存限制 评测方式 题目来源 1000ms 131072KiB 标准比较器 Local 题目描述 一个公司有三个移动服务员.如果某个地方有一个请求,某个员工必须 ...
- ORM对象关系型映射的用法
ORM对象关系型映射的用法 -- Django模型 1.什么是ORM关系型映射 ORM 全拼Object-Relation Mapping. 中文意为 对象-关系映射. 主要实现模型对象到关系数据库数 ...
- 【CodeForces 271D】Good Substrings
[链接] 我是链接,点我呀:) [题意] [题解] 字典树 我们可以两重循环(i,j) 来枚举所有的子串 即i=1,j=1,2,3... i=2,j = 2,3,4,.. 于是我们在i变化的时候(就是 ...
- 【AIM Tech Round 5 (rated, Div. 1 + Div. 2) A】 Find Square
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 找到左上角.往下一直走,往右一直走走到B边界就好. 中点的话.直接输出中位数 [代码] #include <bits/stdc ...
- 说说sys_context函数
select SYS_CONTEXT('USERENV', 'TERMINAL') terminal, SYS_CONTEXT('USERENV', 'LANGUAGE') langua ...
- PL/SQL控制语句
本节要点: l 选择结构控制语句 if条件控制语句 Case语句 l 循环结构控制语句 基本loop循环 for循环 while循环 嵌套循环 PL/SQL既然是面向过程的编程语言,那么它就有针对 ...
- BA-siemens-TX-IO模块照片
西门子楼宇自控用到的P1模块是这样的 TX-16D模块是这样的 TX-8X模块是这样的: TX-6R模块是这样的: TX-IO总线连接模块( 此模块供电和通讯加起来共需三芯线):
- 洛谷 P1272 重建道路(树形DP)
P1272 重建道路 题目描述 一场可怕的地震后,人们用N个牲口棚(1≤N≤150,编号1..N)重建了农夫John的牧场.由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟 ...
- ISAM Indexed Sequential Access Method 索引顺序存取方法
ISAM Indexed Sequential Access Method 索引顺序存取方法 学习了:https://baike.baidu.com/item/ISAM/3013855 是IBM发展起 ...