javascript作用域(var、let、const)

38 阅读5分钟

全局作用域

在全局范围内定义的变量和函数,拥有全局作用域,可以在任何地方访问

var globalVar = 'I am global';

function globalFunction() {
  console.log(globalVar);
}

函数作用域

在函数内部定义的变量和函数,拥有函数作用域,只能在函数内部访问

函数作用域在函数执行时创建,函数执行完毕后销毁

function myFunction() {
  var functionVar = 'I am inside a function';
  console.log(functionVar);
}

myFunction();
console.log(functionVar); // Uncaught ReferenceError: functionVar is not defined

块级作用域

块级作用域(Block Scope)是由花括号 {} 包围的代码块创建的作用域,常见于控制语句(如 if、for、while)、函数内部、代码块等

块级作用域在块执行时创建,块执行完毕后销毁。

{
  let blockVar = 'I am inside a block';
  console.log(blockVar);
}

console.log(blockVar); // Uncaught ReferenceError: blockVar is not defined

作用域链

当在当前作用域中查找变量时,如果找不到,会沿着作用域链向上查找,直到找到该变量或到达全局作用域。如果仍然找不到,则抛出 ReferenceError 错误。

var globalVar = 'I am global';

function outerFunction() {
  var outerVar = 'I am outside';

  function innerFunction() {
    var innerVar = 'I am inside';

    console.log(innerVar);    // 输出 'I am inside'
    console.log(outerVar);    // 输出 'I am outside'
    console.log(globalVar);   // 输出 'I am global'
  }

  innerFunction();
}

outerFunction();

变量提升

JavaScript 在代码执行前,会先扫描所有声明,并将变量和函数声明提升到当前作用域的顶部,但不会提升变量的赋值。

console.log(hoistedVar); // 输出 undefined
var hoistedVar = 'I am hoisted';

hoistedFunction(); // 输出 'I am a hoisted function'
function hoistedFunction() {
  console.log('I am a hoisted function');
}

上面的代码会被解释为:

var hoistedVar;
function hoistedFunction() {
  console.log('I am a hoisted function');
}

console.log(hoistedVar); // 输出 undefined
hoistedVar = 'I am hoisted';

hoistedFunction(); // 输出 'I am a hoisted function'

let和const同样具备变量提升,但与 var 不同的是,它们在被提升后不会被初始化为 undefined,并且在实际声明之前访问它们会导致 ReferenceError(“暂时性死区”(Temporal Dead Zone,TDZ))

【暂时性死区是指在代码块中,变量被声明(实际声明处)之前的部分。在这段时间内,尝试访问变量会抛出 ReferenceError。】

var变量提升:

console.log(varVariable); // 输出 undefined
var varVariable = 'I am var';
console.log(varVariable); // 输出 'I am var'

let和const变量提升:

console.log(letVariable); // Uncaught ReferenceError: Cannot access 'letVariable' before initialization
let letVariable = 'I am let';

console.log(constVariable); // Uncaught ReferenceError: Cannot access 'constVariable' before initialization
const constVariable = 'I am const';

let和const定义的变量在同一作用域中不可重复,但是var可以重复声明,这是为什么呢?

  1. var 的历史原因

• 在 JavaScript 的早期版本中,只有 var 用于变量声明,且 var 的作用域是函数作用域而不是块级作用域。这种设计导致了一些不太直观的行为,比如变量提升和可以重复声明。

• 允许重复声明是因为在同一个作用域中,使用 var 声明的变量会被提升到作用域的顶部,并且声明会被合并。重复声明变量不会引起错误,只会覆盖之前的声明。

var x = 10;
var x = 20; // 没有错误,x 的值被覆盖为 20
console.log(x); // 输出 20
  1. let const 的设计目的

• 为了引入更可靠的变量声明方式,ES6 引入了 let 和 const。它们具有块级作用域,并且在同一个作用域中不允许重复声明变量,从而避免了 var 可能带来的潜在错误。

• let 和 const 会在声明前存在“暂时性死区”(TDZ),在这个区域内访问这些变量会引发 ReferenceError,这使得变量声明更加明确和安全。

let y = 10;
let y = 20; // SyntaxError: Identifier 'y' has already been declared

战术性温习:

var num = 10;
console.log('a:', num);
var num;
console.log('b', num);
function test() {
  console.log('c', num);
  var num = 20;
  console.log('d', num);
}
test();
console.log('e', num);

输出:

a: 10
b: 10
c: undefined
d: 20
e: 10

解析后:

var num; // 变量提升,初始化为 undefined
function test() {
  var num; // 变量提升,初始化为 undefined
  console.log('c', num); // 输出 undefined
  num = 20;
  console.log('d', num); // 输出 20
}

num = 10;
console.log('a:', num); // 输出 'a: 10'
console.log('b', num); // 输出 'b: 10'
test(); // 执行函数 test
console.log('e', num); // 输出 'e: 10'

你觉得学习效果如何,敢不敢...


总结

var 声明的变量会被提升到函数或全局作用域的顶部,并初始化为 undefined。这意味着你可以在声明之前访问这些变量。

let const 声明的变量也会被提升到块级作用域的顶部,但在实际声明之前访问它们会导致 ReferenceError。这种行为被称为“暂时性死区”(TDZ)。

练一练

试题1:

console.log('a:', foo);
var foo = 'hello';
console.log('b:', foo);
function test1() {
  console.log('c:', foo);
  var foo = 'world';
  console.log('d:', foo);
}
test1();
console.log('e:', foo);

试题2:

var bar = 1;
function test2() {
  console.log('a:', bar);
  var bar = 2;
  function inner() {
    console.log('b:', bar);
  }
  inner();
  console.log('c:', bar);
}
test2();
console.log('d:', bar);

试题3:

var x = 10;
function test3() {
  console.log('a:', x);
  if (false) {
    var x = 20;
  }
  console.log('b:', x);
}
test3();
console.log('c:', x);

试题4:

console.log('a:', z);
var z = 'global';
function test4() {
  console.log('b:', z);
  var z = 'local';
  function innerTest() {
    console.log('c:', z);
    var z = 'inner';
    console.log('d:', z);
  }
  innerTest();
  console.log('e:', z);
}
test4();
console.log('f:', z);

习题解析

试题1:

console.log('a:', foo); // 输出 'a: undefined'
var foo = 'hello';
console.log('b:', foo); // 输出 'b: hello'
function test1() {
  console.log('c:', foo); // 输出 'c: undefined'
  var foo = 'world';
  console.log('d:', foo); // 输出 'd: world'
}
test1();
console.log('e:', foo); // 输出 'e: hello'

试题2:

var bar = 1;
function test2() {
  console.log('a:', bar); // 输出 'a: undefined'
  var bar = 2;
  function inner() {
    console.log('b:', bar); // 输出 'b: 2'
  }
  inner();
  console.log('c:', bar); // 输出 'c: 2'
}
test2();
console.log('d:', bar); // 输出 'd: 1'

试题3:

var x = 10;
function test3() {
  console.log('a:', x); // 输出 'a: undefined'
  if (false) {
    var x = 20;
  }
  console.log('b:', x); // 输出 'b: undefined'
}
test3();
console.log('c:', x); // 输出 'c: 10'

// 对于var来说只有全局作用域和函数作用域,并没有块级作用域一说

试题4:

console.log('a:', z); // 输出 'a: undefined'
var z = 'global';
function test4() {
  console.log('b:', z); // 输出 'b: undefined'
  var z = 'local';
  function innerTest() {
    console.log('c:', z); // 输出 'c: undefined'
    var z = 'inner';
    console.log('d:', z); // 输出 'd: inner'
  }
  innerTest();
  console.log('e:', z); // 输出 'e: local'
}
test4();
console.log('f:', z); // 输出 'f: global'

最后,温馨两问:

1、let和const变量只在当前作用域生效吗,作用域链对其无效?

2、var、let、const的区别?


编程的世界总是充满未知,期待与你在技术的海洋中再相遇
I just hope my code makes more sense than my life.