全局作用域
在全局范围内定义的变量和函数,拥有全局作用域,可以在任何地方访问
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可以重复声明,这是为什么呢?
- var 的历史原因:
• 在 JavaScript 的早期版本中,只有 var 用于变量声明,且 var 的作用域是函数作用域而不是块级作用域。这种设计导致了一些不太直观的行为,比如变量提升和可以重复声明。
• 允许重复声明是因为在同一个作用域中,使用 var 声明的变量会被提升到作用域的顶部,并且声明会被合并。重复声明变量不会引起错误,只会覆盖之前的声明。
var x = 10;
var x = 20; // 没有错误,x 的值被覆盖为 20
console.log(x); // 输出 20
- 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.