作用域:
在ES6中,添加了块级作用域,所以就有全局作用域、函数局部执行环境、块级作用域三类作用域。js代码的作用域以全局作用域为根,向下以树状的数据结构展开。
作用域查找变量的原则是,现在自身的作用域中从最顶部的代码到引用变量的地方,这个范围内进行查找,找不到的话只能向外面的作用域中查找,而不会向内部的作用域中查找。
未声明的变量:
在js中,调用未声明的变量,会直接报错。
var声明:
var的声明和查找,只承认全局作用域和函数局部执行环境,块级作用域会忽视。
用var重复声明变量的话,后声明的会覆盖之前声明的。
var声明的变量会在当前作用域中得到变量提升,相当于在作用域一开始就声明变量(但是不会初始化),因此在作用域的任何地方,甚至var声明的变量之前调用,不会报错,而是显示未初始化的undefined。
let声明:
与var和不同的是,let的声明和查找,全局作用域、函数局部执行环境和块级作用域都承认。
用let重复声明变量的话,只要之前声明过了的变量,无论用什么声明的,都会报错。
如果在let声明之前调用变量的话,报错的。但是,在它的上级作用域中也声明一个变量的话,并不会沿着作用域链去查找调用,还是报错。所以个人认为,let也存在变量提升,不过,提升后的作用与var不一样,let不会将变量置顶,而是形成一个“暂时性死区”(temporal dead zone,简称 TDZ),使得作用域顶部到let声明变量的范围内,不能使用该变量。
const声明:
const可以看作,在let的基础上,添加了一开始必须初始化,并且不能再改变变量的限制。
其他的,与let类似。
function声明:
function的声明和查找,与全局作用域、函数局部执行环境和块级作用域的关系比较松散混乱,所以就有点奇怪了=。=
在同一作用域的时候,function声明与var一样,会在当前作用域中得到变量提升。当时,与var不同的是,它会直接把初始化的函数内容也一起提升。
在同一作用域的时候,若是在一个作用域中同时进行两次函数声明的话,第二个函数会直接覆盖第一个。
严格模式中,在块级作用域中重复声明会报错,全局作用域中和函数执行作用域中则不会。
块级作用域内的块级作用域中声明函数是时:与let一样,承认块级作用域。
而在块级作用域与全局作用域混杂的时候。
全部块级内声明,在外部调用,像var一样:
一个在块级内声明,一个在外部声明:
这就非常有趣了,回到ES5,当var与function的声明重复时。
可以看到function的优先级使高于var,function会优先提升置顶,之后才是var。
所以,个人认为,在全局作用域下的块级作用域中的函数,对于全局作用域来说,是否看作var声明的函数变量?
函数执行作用域中,与全局作用域类似:
function在初始化的时候,只是一串静态的代码,无论内部引用什么变量,都不会有异常,只有在调用的时候,才会根据作用域链进行查找变量。
而内部变量查找的话,并不是从调用函数的地方开始,而是以声明的地方为起点,进入作用域链查找变量。(函数名可以视作使指向函数代码区的一个地址)。
小结:
以上代码都是在Chorme版本 83.0.4103.97(正式版本)浏览器上执行。