JavaScript 作用域

作用域决定"变量在哪些地方能被访问"。JS 的作用域分全局作用域、函数(局部)作用域和块级作用域。不理解作用域,就会遇到"变量找不到"或"变量被悄悄改坏"的 Bug。

作用域是什么

变量并不是任何地方都能用,它只在"自己的地盘"(作用域)里有效,就像房间里的物品只在房间里看得见:

function demo() {
  let x = 10;      // x 只在这个函数里有效
}
demo();
console.log(x);    // 报错:x 未定义(函数外访问不到)

全局作用域与局部作用域

  • 在函数外、代码最顶层声明的变量是全局变量,到处都能访问。
  • 在函数内声明的变量是局部变量,只在函数内(含嵌套函数)有效。
let globalNum = 1;       // 全局变量
function f() {
  let localNum = 2;      // 局部变量
  console.log(globalNum + localNum);  // 输出:3
}
f();
console.log(globalNum);   // 输出:1(全局可访问)
// console.log(localNum); // 报错:局部变量外部不可见

块级作用域:let 与 const

{ } 包起来的区域(如 if、for 的代码体)是"块"。let/const 声明的变量只在块内有效:

if (true) {
  let a = 1;     // 块级作用域:出块即失效
  console.log(a); // 输出:1
}
// console.log(a); // 报错:a 只在 if 块内有效

而 var 没有块级作用域,只有函数作用域——这是 var 容易出 Bug 的原因之一:

if (true) {
  var b = 2;     // var 无视块,会泄漏到外面
}
console.log(b);  // 输出:2(块外竟然能访问)

变量的查找:由内向外

访问变量时,JS 会先在当前作用域找,找不到就往外层作用域找,直到全局;都找不到才报错。内层的同名变量会"遮蔽"外层:

let name = "全局的小明";
function outer() {
  let name = "局部的小红";   // 遮蔽了全局的 name
  function inner() {
    console.log(name);      // 就近找到 outer 里的 name
  }
  inner();
}
outer();
// 输出:局部的小红

var 的坑:变量提升

用 var 声明的变量会被"提升"到作用域顶部,但赋值不提升,所以在声明前访问得到的是 undefined 而非报错:

function test() {
  console.log(v);   // 输出:undefined(var 被提升了)
  var v = 1;
}
test();

另外,忘记写关键字直接赋值(x = 5)会意外创建全局变量,务必避免;建议开启严格模式("use strict")来拦截这种错误。

小结

作用域分三级:全局、函数、块级。记住三条经验:能局部就不要全局;声明优先用 let/const 而非 var;变量查找由内向外、就近遮蔽。掌握作用域,Bug 能少一半。

笔记加载中…