我所理解的作用域,就像一个套一个的盒子;你在哪个盒子里,要么伸手拿自己盒子里的东西,要么从包裹你的盒子里拿东西;要想到你下面的盒子里去拿东西,是不被允许的。
// 首先,定义自己的log函数
const log = function() {
console.log.apply(console, arguments)
}
作用域,也称为运行环境。对于JS,运行在web浏览器上时,浏览器为其提供一个运行环境;而NodeJS也是为JS代码的执行提供了一个运行环境。
作用域有大小之分,大的称为全局作用域,小的称为局部作用域,再小的称为块级作用域;相对应的作用域中的变量,则称为全局变量、局部变量;暂时没听到块级变量这么一说。
作用域的大小之分,并不是为了比较谁大谁小;其不是相互独立的,可以理解成包含与被包含关系。局部作用域和块级作用域一定是包含在全局作用域之内;局部作用域之间可能有包含或被包含的关系,也可能是彼此独立的;而块级作用域与局部作用域类似。
全局作用域 -- 全局变量
一个变量因其所在作用域的不同,能够被访问的限制也是不同的;在局部作用域中,只能访问自己作用域内定义的变量,以及向上访问包含自己的父级作用域,或者延长的作用域。在全局作用域内定义一个全局变量,在整个环境中都可以访问的到,无论是是局部作用域中,还是在块级作用域中。比如:
// 例子1
// 定义全局变量i
var i = 0
// 在 局部作用域 中访问全局变量
const showme = function() {
log(i)
}
showme() // 0
// 在 块级作用域 中访问全局变量
for(; i < 2; i++) {
log(i) // 0 1
}
JS语句执行时,会先在当前自己的作用域中搜索变量,如上例中的i
;当在当前作用域中找不到的时候,会向上层去查找。在这里就是直接到全局作用域中找到了变量i
。
局部作用域 -- 局部变量
全局变量之下的变量,有局部和块级两个,不分上下级,只因声明的区域不同。局部变量一般是声明在函数内部,如上例中的showme
函数。一个函数的定义,就定义了一个局部作用域,其内声明的变量则只在这个作用域、及其下层作用域可以被访问的到。
// 例子2
// color为全局变量,在函数内部也可以访问
var color = 'blue'
var changeColor = function() {
// 注意:当变量不用var声明时,是一个全局变量,尽量不要这样使用
// anothorColor为changeColor函数内部变量,不能被外部所访问
var anothorColor = 'red'
var swapColor = function() {
// tempColor与anothorColor类似,是swapColor函数内部变量,不能被外层访问到
var tempColor = anothorColor
// 当访问anothorColor时,会先从本函数内部寻找,找不到,会到上层changeColor()寻找
anothorColor = color
// 当访问color时,在本层寻找不到,会逐级向上直到全局作用域
color = tempColor
}
return swapColor
}
// 由于anothorColor tempColor属于局部作用域中的局部变量,在全局环境中无法访问
log(anothorColor) // Uncaught ReferenceError: anothorColor is not defined
log(tempColor) // Uncaught ReferenceError: tempColor is not defined
// 调用changeColor()函数,获得swapColor()函数的引用
var swapColor = changeColor()
// 调用swapColor函数
swapColor() // "red"
// 上面两条语句合并为一条: changeColor()()
如上代码,一个全局变量color
,其后由changColor
函数定义了一个局部作用域A
;而在changColor的局部作用域A
之内,又由swapColor
函数定义了一个层级更低的局部作用域B
。在局部作用域中可以看到,变量的访问是一层一层逐级往上层查找的;找到了就结束查询;一直到全局作用于中都没有找到,则报错。
块级作用域 -- 块级变量
其实没有块级变量,是我自己喊的,方便称呼块级作用域中声明的变量。块级变量也是一个局部变量,但其不是在函数定义的局部作用域中;而是存在于由大括号{}
(或for()
圆括号)包裹的块级作用域中。块级作用域可以看作比局部变量更小的作用域;其访问的限制也是一样的,只可向上访问,不能向下访问。如例子1
中的变量i
。
既然一样,为什么还要有个块级作用域呢?块级作用域的存在确实方便了我们的代码编写,也帮我们避免了一些不小心的错误。如下:
// 例子3 ES6新增块级作用域,使用let声明变量
// 以前
if(2 == '2') {
var i = 10
}
log(i) //10
// ES6
if(2 == '2') {
let j = 10
}
log(j) // Uncaught ReferenceError: j is not defined
// 尤其for循环
for(var j = 0; j < 2; j++) {
log(j) // 0 1
}
log(j) // 2
// ES6
for (let k = 0; k < 2; k++) {
log(k) // 0 1
}
log(k) // VM473:4 Uncaught ReferenceError: k is not defined
网友评论