作用域
作用域是管理变量可用性的一个重要概念。作用域在基本闭包处,定义了全局变量和局部变量的概念。
作用域 - The Scope
在深入理解作用域之前,先通过一个简单的例子看看是如何工作的。
const message = 'hello'
console.log(message) // hello你可以轻松访问message变量。
如果将message变量放在if语句内,比如:
if (true) {
const message = 'hello'
}
console.log(message) // ReferenceError: message is not defined为什么?
if 代码块为消息变量创建一个作用域,而消息变量只能在这个作用域内访问。

在更高的级别上,变量的可访问性受到它们被创建的作用域的限制。您可以自由访问在其作用域内定义的变量。但是在它的作用域之外,变量是不可访问的。
块级作用域 - Block Scope
if (true) {
// if 块级作用域
const message = 'hello'
console.log(message) // hello
}
console.log(message) // ReferenceError: message is not defined第一个console.log输出hello,因为message变量在if块级作用域内。
第二个console.log输出ReferenceError: message is not defined,因为message变量在if块级作用域之外。
if、for、while、do、switch、with、function 以及catch创建块级作用域。比如:
for (const color of ['green', 'red', 'blue']) {
// "for" block scope
const message = 'Hi';
console.log(color); // 'green', 'red', 'blue'
console.log(message); // 'Hi', 'Hi', 'Hi'
}
console.log(color); // throws ReferenceError
console.log(message); // throws ReferenceError
while (/* condition */) {
// "while" block scope
const message = 'Hi';
console.log(message); // 'Hi'
}
console.log(message); // => throws ReferenceErrorvar 不是块级作用域
var 关键字不是块级作用域。比如:
if (true) {
// "if" block scope
var count = 0
console.log(count) // 0
}
console.log(count) // 0使用var定义的变量,会产生变量提升,即变量会在它们被访问之前被创建。在除function外的所有块级作用域中,var 关键字都是不可访问的。
函数作用域 - Function Scope
function run() {
// "run" function scope
var message = 'Run Forrest, run!'
console.log(message) // Run Forrest, run!
}
run()
console.log(message) // ReferenceError: message is not definedrun()函数定义了一个块级作用域,它包含了message变量,message变量可以在run()函数内访问,但是不能在run()函数之外访问。
同样let和const也是块级作用域。可以在run()函数内访问, 但是不能在run()函数之外访问。
function run() {
// "run" function scope
const two = 2
let count = 0
function run2() {}
console.log(two) // 2
console.log(count) // 0
console.log(run2) // function
}
run()
console.log(two) // throws ReferenceError
console.log(count) // throws ReferenceError
console.log(run2) // throws ReferenceError模块作用域 - Module Scope
ES2015 同样支持模块作用域。模块作用域是一个特殊的块级作用域,它可以在模块内部访问,但是不能在模块之外访问。
// circle.js
const PI = 3.14
console.log(pi) // 3.14circle.js模块定义了一个块级作用域,它包含了PI变量。但是未导出PI变量,因此不能在模块之外访问。
import './circle'
consoel.log(pi) // throws ReferenceError嵌套作用域 - Scopes can be nested
作用域可以嵌套。比如:
function run() {
// "run" function scope
const message = 'Run, Forrest, Run!'
if (true) {
// "if" code block scope
const friend = 'Bubba'
console.log(message) // 'Run, Forrest, Run!'
}
console.log(friend) // throws ReferenceError
}
run()if代码块作用域嵌套在run()函数作用域内。if代码块作用域内部可以访问message变量,但是不能if外部访问friend变量。

全局作用域 - Global Scope
全局作用域是一个特殊的作用域,它可以在整个程序的所有位置访问。<script>标签内的代码块是全局作用域。
;<script src="myScript.js"></script>
// myScript.js
// 全局作用域
let counter = 1在前面的代码片段中,counter 是一个全局变量。这个变量可以从网页的 JavaScript 的任何地方访问。
全局作用域是一种机制,它允许 JavaScript 的主机(浏览器、节点)为应用程序提供特定于主机的函数作为全局变量。
例如,window 和 document 是浏览器提供的全局变量。在 Node 环境中,可以将进程对象作为全局变量访问。
词法作用域 - Lexical Scope
定义两个函数,将函数innerFunc嵌套在函数outerFunc内部。
function outerFunc() {
// the outer scope
let outerVar = 'I am from outside!'
function innerFunc() {
// the inner scope
console.log(outerVar) // 'I am from outside!'
}
return innerFunc
}
const inner = outerFunc()
inner()inner()函数执行时,innerFunc()函数定义了一个块级作用域,它包含了outerVar变量。
实际上是执行了outerFunc函数内的innerFunc()函数。outerFunc函数返回函数,形成了一个闭包。
所以outerFunc外部可以访问innerFunc()内部的outerVar变量。
变量隔离 - Variables isolation
您可以在不同的作用域中重用公共变量名(count、index、current、value、etc) ,而不会发生冲突。
foo()和bar()函数作用域有它们自己的。
function foo() {
// "foo" function scope
let count = 0
console.log(count) // 0
}
function bar() {
// "bar" function scope
let count = 1
console.log(count) // 1
}
foo()
bar()