变量的作用域

[ 执行环境和作用域?!] 变量的作用域,是指程序源代码中定义这个变量的区域,即,变量可以起作用的范围。

JavaScript 拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域。作用域貌似简单,实则复杂,由于作用域与 this 机制?!非常容易混淆,使得理解 用域的原理?!更为重要


词法作用域

[ 词法作用域和动态作用域?!] JavaScript 使用词法作用域,具体分为全局作用域和局部作用域(又叫函数作用域)。

function test(){
	var message = "hi";
}
test();
alert(message);  // 错误

如果省略var操作符,则会创建一个全局变量。

function test(){
	message = "hi";
}
test();
alert(message);  // "hi"


虽然省略var操作符可以定义全局变量,但并不推荐。
// 在局部作用域中定义的全局变量很难维护,而且如果有意的忽略var操作符,也会由于相应变量不会马上就有定义而导致不必要的混乱;
// 给未声明的变量赋值在严格模式下会导致抛出ReferenceError错误。
  • 全局作用域是最外围的一个执行环境,在 Web 浏览器中,全局执行环境被认为是 window 对象。

所有全局变量和函数都是 window 对象的属性和方法创建的。

全局变量拥有全局作用域,在 JS 代码中任何地方都有定义,全局作用域直到应用程序退出时才会被销毁。

  • 在函数内声明的变量,只在函数体内有定义,它们是局部变量,作用域是局部性的。

函数参数也是局部变量,它们只有在函数体内有定义。

函数作用域中所有代码执行完毕后,该作用域被销毁,保存其中的所有函数和变量定义,随之销毁。


[ 遮蔽效应 ] 如果函数内声明的局部变量或函数参数中带有的变量和全局变量重名,那么全局变量会被局部变量遮盖。

也就是说,在函数体内,局部变量的优先级高于同名的全局变量。

var scope = "global";
function checkscope(){
	var scope = "local";
	return scope;
}
checkscope();  // "local"

[ 声明提升?!] 一般认为,JS 代码是由上到下一行行执行;但实际上并不完全正确,主要是因为声明提升存在。

声明包括两种:变量声明和函数声明。因此,函数声明和变量声明都可以被提升,但函数声明会覆盖变量声明。

JS 函数(局部)作用域规定:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。

这意味着,变量在声明之前甚至已经可用。

JS 这个特性被非正式的称为:声明提升 — 函数中声明的所有变量(不涉及赋值),都被提前到函数体的顶部。

var scope = "global";
function f(){
	console.log(scope);  // undefined
	var scope = "local";
	console.log(scope);  // "local"
}

// 变量声明提升后,相当于下面代码:

var scope = "global";
function f(){
	var scope;
	console.log(scope);  // undefined
	scope = "local";
	console.log(scope);  // "local"
}

块作用域

尽管函数作用域是最常见的作用域单元,也是现行大多数 JavaScript 最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀、简洁的代码,如块作用域。

过去,JS 没有块作用域,var 声明时的声明提升、属性变量等行为让人困惑。

所谓块级作用域,是指任何一对花括号中的语句都属于一个块,在这之中定义的所有变量在代码块外是不可见的。

{
  var num = 7;
  // console.log(num);   // 7
}
  console.log(num);    // 7     在花括号外部也可以访问,所以,JS中是没有块作用域的

但,ES6新语法,可以帮我们更好地控制作用域,引入了一些块级作用域的绑定机制 ...

随着 ES6 的推广,“ 块作用域?!”也将用的越来越广泛。

建议:默认使用 const,只有确实需要改变变量的值时使用 let。