【前端大忌】不要小看 let 和 const,这里面也博大精深
SailingCoder 2024-08-31 16:03:02 阅读 71
引言:最近,“const定义的对象属性是否可以改变?” 这道面试题频频出现。这一问题让我们意识到,<code>let 和 const
不仅仅是简单的变量声明方式,其中的知识点和应用场景也值得我们深入探讨。
在这篇文章中,我们将全面解析 <code>let 和 const
的不同应用场景、堆栈和作用域详解、优缺点对比、技巧应用等。希望通过本文,你能对这两个关键字有更深入的理解,并能够在实际开发中灵活运用它们。
----借助目录阅读,效果更好。
基础概念与用法
在ES6(ECMAScript 2015)中,引入了 let
和 const
来替代传统的 var
进行变量声明。这两个关键字具有块级作用域,并且避免了 var
声明带来的许多问题。
let
:用于声明可变的变量。const
:用于声明不可变的变量,但要注意的是,这里的“不变”是指变量绑定的值的引用不变,而不是值本身不变。
// 使用 let 声明变量
let a = 10;
a = 20; // 允许重新赋值
// 使用 const 声明变量
const b = 10;
// b = 20; // 不允许重新赋值,会报错
// const 声明对象
const obj = { key: 'value' };
obj.key = 'newValue'; // 允许修改对象属性
console.log(obj.key); // 输出 'newValue'
不同应用场景的深入分析
let
适用于需要在代码块内部重复赋值的变量声明,例如循环计数器、临时变量等。如下,let
确保了 i
的作用域仅限于循环内部,避免了 var
可能导致的全局变量污染问题。
// 循环中的应用
for (let i = 0; i < 10; i++) {
console.log(i);
}
// 条件语句中的应用
let flag = true;
if (flag) {
let message = 'Hello, world!';
console.log(message); // 输出 'Hello, world!'
}
// console.log(message); // 报错,message 不在作用域内
const
适用于声明那些在初始化后不会再被重新赋值的变量,例如常量、函数和对象。
// 1. 声明常量
const PI = 3.14159;
// PI = 3.14; // 报错,不允许重新赋值
// 2. 声明函数
const greet = function() {
console.log('Hello, world!');
}
greet();
// 3. 声明对象
const user = {
name: 'Alice',
age: 25
};
user.age = 26; // 允许修改对象属性
console.log(user.age); // 输出 26
我们接着再从堆栈和作用域方面,深入分析下。。
堆栈和作用域详解
在JavaScript中,变量的声明和使用不仅仅是简单的赋值和调用,而是与内存管理、作用域链以及执行上下文等多个复杂机制密切相关。为了更好地理解 <code>let 和 const
的工作原理,我们需要深入了解堆栈内存管理、作用域和变量提升以及暂时性死区(TDZ)的概念。
堆栈内存管理
JavaScript 中的变量储存在堆(Heap)和栈(Stack)中。基本类型(例如数字和布尔值)存储在栈中,而引用类型(例如对象和数组)存储在堆中。
堆:用于存储对象(Object)和大块的数据。栈:用于存储基本数据类型(如字符串、数字等)和执行上下文(Execution Context)。
<code>const x = 10; // 基本类型,存储在栈中
const y = { value: 20 }; // 引用类型,y存储在栈中,实际对象存储在堆中
在 JavaScript 中,每当调用函数时,都会创建一个新的执行上下文并被压入栈顶。当函数执行完毕时,执行上下文从栈顶弹出,释放其所占用的内存空间。让我们通过一个示例来理解这一过程:
function foo() {
let a = 20;
const b = 30;
console.log(a + b);
}
foo();
在上述代码中,当调用 foo
函数时:
JavaScript 引擎创建一个新的执行上下文,其中包含 foo
函数的局部变量 a
和 b
。该执行上下文被压入栈中。函数执行完毕后,执行上下文从栈中弹出,内存被释放。
这个过程确保了变量在其作用域范围内可访问,并在函数执行完毕后释放内存,避免内存泄漏。
作用域
作用域决定了变量的可访问性。在 JavaScript 中,作用域有三种类型:全局作用域、函数作用域和块级作用域。
全局作用域:在代码任何地方都可以访问的变量被称为全局变量。函数作用域:使用 var
声明的变量具有函数作用域,意味着它们只在声明它们的函数内部可见。块级作用域:使用 let
和 const
声明的变量具有块级作用域,意味着它们只在声明它们的块或子块内可见。
// 1. 全局作用域
var globalVar = "全局作用域";
function globalScopeExample() {
console.log(globalVar); // 输出 "全局作用域"
}
globalScopeExample();
// 2. 函数作用域
function functionScopeExample() {
var functionVar = "函数作用域";
console.log(functionVar); // 输出 "函数作用域"
}
functionScopeExample();
// console.log(functionVar); // 报错,functionVar 不在作用域内
// 3. 块级作用域
if (true) {
let blockLet = "块级作用域 let";
const blockConst = "块级作用域 const";
console.log(blockLet); // 输出 "块级作用域 let"
console.log(blockConst); // 输出 "块级作用域 const"
}
// console.log(blockLet); // 报错,ReferenceError: blockLet is not defined
// console.log(blockConst); // 报错,ReferenceError: blockConst is not defined
变量提升(Hoisting)
变量提升是指 JavaScript 在执行代码时会将变量声明提升到作用域的顶部。使用 var
声明的变量会被提升,而 let
和 const
声明的变量不会被提升。详细内容请看文章【😤 踩了个小水坑】JavaScript不仅有变量声明,还有变量提升
console.log(varVar); // 输出 undefined
var varVar = "小明";
// console.log(letVar); // 报错,Cannot access 'letVar' before initialization
let letVar = "小薇";
// console.log(constVar); // 报错,Cannot access 'constVar' before initialization
const constVar = "小刚";
var
声明的 varVar
变量被提升,因此在 console.log(varVar)
时输出 undefined
,而 let
和 const
声明的变量不会被提升,因此在声明前使用会导致错误。
暂时性死区(Temporal Dead Zone, TDZ)
TDZ 是指在 let
和 const
声明的变量从作用域开始到初始化之间的时间段。在 TDZ 内访问这些变量会导致引用错误。这种现象的原因是,在变量声明之前的区域被称为暂时性死区。
{
// TDZ开始
// console.log(c); // 报错,c在TDZ中
let c = 10;
// TDZ结束
console.log(c); // 输出 10
}
优缺点对比:let、const、var
在JavaScript中,<code>let、const
和 var
各有其独特的特点和应用场景。通过比较它们的优缺点,可以帮助我们更好地选择合适的变量声明方式。
特性 | let | const | var |
---|---|---|---|
作用域 | 块级作用域 | 块级作用域 | 函数作用域 |
重新赋值 | 允许 | 不允许 | 允许 |
提升(Hoisting) | 不会,声明前使用会导致引用错误 | 不会,声明前使用会导致引用错误 | 会,被提升到函数或全局作用域顶部 |
暂时性死区(TDZ) | 是 | 是 | 否 |
必须初始化 | 否 | 是 | 否 |
常见用途 | 循环变量、条件语句、块级作用域 | 常量、不可变对象、函数引用 | 全局变量、函数变量、在ES5及之前的代码中 |
使用安全性 | 高,避免变量提升和作用域污染 | 高,避免变量提升和作用域污染 | 低,易引起作用域污染和意外的变量重定义 |
内存管理 | 优,块级作用域帮助释放内存 | 优,块级作用域帮助释放内存 | 差,变量提升和全局作用域易导致内存泄漏 |
可维护性 | 高,代码结构清晰,易于维护 | 高,代码结构清晰,易于维护 | 低,易引起混乱和难以维护 |
可以看出,let
和 const
提供了块级作用域和更高的安全性,适用于现代 JavaScript 开发,而 var
由于其函数作用域和变量提升特性,已逐渐被淘汰。
高级技巧与实际应用
我们将探讨 let
和 const
在一些高级技巧和实际应用中的使用方法,包括模块化开发以及结合ES6+新特性的现代化应用。
模块化开发
模块化开发中,let
和 const
可以帮助避免全局命名空间的污染,提高代码的可维护性和重用性。
// module.js
export const PI = 3.14;
// main.js
import { PI } from './module.js';
console.log(PI); // 输出 3.14
在这个例子中,PI
常量通过 const
在模块间传递和使用,保证了值的不可变性和代码的清晰性。
解构赋值
解构赋值是一种便捷的方式,可以从数组或对象中提取值并赋给变量。
const user = {
name: 'Alice',
age: 25,
address: {
city: 'Wonderland'
}
};
// 对象解构赋值
const { name, age, address: { city } } = user;
console.log(name, age, city); // 输出 'Alice 25 Wonderland'
// 数组解构赋值
const colors = ['red', 'green', 'blue'];
const [first, second, third] = colors;
console.log(first, second, third); // 输出 'red green blue'
模板字符串
模板字符串使用反引号(`
)包围,允许在字符串中嵌入表达式。
const name = 'Alice';
const age = 25;
// 模板字符串
const message = `My name is ${ name} and I am ${ age} years old.`;
console.log(message); // 输出 'My name is Alice and I am 25 years old.'
// 多行字符串
const multiline = `
This is a
multiline
string.
`;
console.log(multiline);
常见误区和面试题解析
常见误区:你认为呢?
误以为 <code>const 声明的对象不可变忽略了 let
和 const
的块级作用域
面试题解析
题目:const
定义的对象属性是否可以改变?
const obj = { key: 'value' };
obj.key = 'newValue';
console.log(obj.key); // 输出 'newValue'
解析: const
声明的对象引用不可变,但对象的属性是可变的。
结论与展望
通过本文的阅读,希望能对 let
和 const
在不同应用场景下的优劣势有了更深入的了解,并能在实际开发中应用自如。请在评论区分享你的使用经验或者提出你的疑问,期待与你的互动!
下一篇: WebGL入门(019):WebGLSync 简介、使用方法、示例代码
本文标签
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。