JavaScript的变量提升及作用域
JavaScript的变量提升及作用域
JavaScript的语法简直不要太灵活,灵活到导出是坑,甚至有人说JavaScript语言不够成熟。。
Code once,debug everywhere.
下面简单整理下常见的坑坑。
变量提升(hoisting)
javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面:
1 | var v = "hello"; |
倘若这是网页中的一部分,则第三行代码输出undefined
,若是在浏览器DevTools的console中运行的话,则是两条undefined
结果,第一条是console.log()
输出的,第二条是执行(闭包)函数时返回的默认值(没有定义返回值时)undefined
。
其实上边👆的代码等价于:👇
1 | var v = "hello"; |
下面给我们稍加改动下,,👇
1 | var v = "hello"; |
如果JavaScript有块作用域的话,应该会报错吧,,内容大概是第4行变量v已经被定义什么的。这样说不太明显,,看这个:👇
1 | for(var i = 0;i < 10; i++){ |
上边在for
循环中声明的变量i
应该只存在与for
循环的代码块中,但是却可以在代码块外访问,外边声明的变量默认是window对象的属性。由此可见javascript是没有块级作用域的。但函数是JavaScript中唯一拥有自身作用域的结构。就像文章开头的第一个代码块一样,再小改下👇:
1 | var v = "hello"; |
此时,闭包函数外是访问不到内部的变量v = "world"
的,,所以,outside输出的值为hello。若是不死心,,我们再改:👇
1 | var v = "hello"; |
还是关于函数内的块级作用域,,我再换种方式:👇
1 | var i = function(){ |
其实,我们还可以再改,,关于隐式声明的全局变量^1
1 | var i = function(){ |
》》》2017-12-19-补充《《《
1
2
3
4
5
6
7
8
9
10
11
var x = 0;
function f(){
var x = y = 1; // x在函数内部声明,y不是!
}
f();
console.log(x, y); // 0, 1
// x是全局变量。
// y是隐式声明的全局变量。
// 第4行可以近似理解为 y = 1;var x = y;
1 | var x = 0; |
需要注意的是,提升的不光有变量,函数声明也会提升,但是通过函数字面量定义的函数不会被提升,详见[JavaScript执行顺序]。(https://someoneiscoding.github.io/2017/12/02/JavaScript%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F/)
名字解析顺序
javascript中一个名字(name)以四种方式进入作用域(scope),其优先级顺序如下:
1、语言内置:所有的作用域中都有 this 和 arguments 关键字
2、形式参数:函数的参数在函数作用域中都是有效的
3、函数声明:形如function foo() {}
4、变量声明:如 var;
名字声明的优先级如上👆所示,也就是说如果一个变量的名字与函数的名字相同,那么函数的名字会覆盖变量的名字,无论其在代码中的顺序如何。但名字的初始化却是按其在代码中书写的顺序进行的,不受以上优先级的影响。看代码:
1 | (function(){ |