问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

【前端大忌】不要小看 let 和 const,这里面也博大精深

创作时间:
作者:
@小白创作中心

【前端大忌】不要小看 let 和 const,这里面也博大精深

引用
CSDN
1.
https://m.blog.csdn.net/qq_37834631/article/details/140289373

在JavaScript开发中,let和const是两个非常重要的关键字,它们不仅影响代码的可读性和可维护性,还关系到内存管理和性能优化。本文将从基础概念、应用场景、堆栈和作用域详解、优缺点对比等多个维度,深入探讨let和const的使用技巧和最佳实践。

在这篇文章中,我们将全面解析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中,变量的声明和使用不仅仅是简单的赋值和调用,而是与内存管理、作用域链以及执行上下文等多个复杂机制密切相关。为了更好地理解let和const的工作原理,我们需要深入了解堆栈内存管理、作用域和变量提升以及暂时性死区(TDZ)的概念。

堆栈内存管理

JavaScript 中的变量储存在堆(Heap)和栈(Stack)中。基本类型(例如数字和布尔值)存储在栈中,而引用类型(例如对象和数组)存储在堆中。

  • 堆:用于存储对象(Object)和大块的数据。
  • 栈:用于存储基本数据类型(如字符串、数字等)和执行上下文(Execution Context)。
const x = 10; // 基本类型,存储在栈中
const y = { value: 20 }; // 引用类型,y存储在栈中,实际对象存储在堆中

在 JavaScript 中,每当调用函数时,都会创建一个新的执行上下文并被压入栈顶。当函数执行完毕时,执行上下文从栈顶弹出,释放其所占用的内存空间。让我们通过一个示例来理解这一过程:

function foo() {
  let a = 20;
  const b = 30;
  console.log(a + b);
}
foo();

在上述代码中,当调用foo函数时:

  1. JavaScript 引擎创建一个新的执行上下文,其中包含foo函数的局部变量a和b。
  2. 该执行上下文被压入栈中。
  3. 函数执行完毕后,执行上下文从栈中弹出,内存被释放。

这个过程确保了变量在其作用域范围内可访问,并在函数执行完毕后释放内存,避免内存泄漏。

作用域

作用域决定了变量的可访问性。在 JavaScript 中,作用域有三种类型:全局作用域、函数作用域和块级作用域。

  1. 全局作用域:在代码任何地方都可以访问的变量被称为全局变量。
  2. 函数作用域:使用var声明的变量具有函数作用域,意味着它们只在声明它们的函数内部可见。
  3. 块级作用域:使用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中,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);

常见误区和面试题解析

常见误区

  • 误以为const声明的对象不可变
  • 忽略了let和const的块级作用域

面试题解析

题目:const定义的对象属性是否可以改变?

![](https://wy-static.wenxiaobai.com/chat-rag-image/11230342650284671744)

const obj = { key: 'value' };
obj.key = 'newValue';
console.log(obj.key); // 输出 'newValue'

解析:const声明的对象引用不可变,但对象的属性是可变的。

结论与展望

通过本文的阅读,希望能对let和const在不同应用场景下的优劣势有了更深入的了解,并能在实际开发中应用自如。请在评论区分享你的使用经验或者提出你的疑问,期待与你的互动!

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号