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

SailingCoder 2024-08-31 16:03:02 阅读 71

引言:最近,const定义的对象属性是否可以改变?” 这道面试题频频出现。这一问题让我们意识到,<code>let 和 const 不仅仅是简单的变量声明方式,其中的知识点和应用场景也值得我们深入探讨。

WX20231120-135736@2x.png

在这篇文章中,我们将全面解析 <code>let 和 const 的不同应用场景、堆栈和作用域详解、优缺点对比、技巧应用等。希望通过本文,你能对这两个关键字有更深入的理解,并能够在实际开发中灵活运用它们。

----借助目录阅读,效果更好。

基础概念与用法

在ES6(ECMAScript 2015)中,引入了 letconst 来替代传统的 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

我们接着再从堆栈和作用域方面,深入分析下。。

faf2b2119313b07e299d9d7405ff132897dd8c5a.webp

堆栈和作用域详解

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

堆栈内存管理

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

image.png

:用于存储对象(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 函数的局部变量 ab。该执行上下文被压入栈中。函数执行完毕后,执行上下文从栈中弹出,内存被释放。

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

作用域

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

全局作用域:在代码任何地方都可以访问的变量被称为全局变量。函数作用域:使用 var 声明的变量具有函数作用域,意味着它们只在声明它们的函数内部可见。块级作用域:使用 letconst 声明的变量具有块级作用域,意味着它们只在声明它们的块或子块内可见。

// 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 声明的变量会被提升,而 letconst 声明的变量不会被提升。详细内容请看文章【😤 踩了个小水坑】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,而 letconst 声明的变量不会被提升,因此在声明前使用会导致错误。

暂时性死区(Temporal Dead Zone, TDZ)

TDZ 是指在 letconst 声明的变量从作用域开始到初始化之间的时间段。在 TDZ 内访问这些变量会导致引用错误。这种现象的原因是,在变量声明之前的区域被称为暂时性死区。

{

// TDZ开始

// console.log(c); // 报错,c在TDZ中

let c = 10;

// TDZ结束

console.log(c); // 输出 10

}

WX20231120-135436@2x.png

优缺点对比:let、const、var

在JavaScript中,<code>let、constvar 各有其独特的特点和应用场景。通过比较它们的优缺点,可以帮助我们更好地选择合适的变量声明方式。

特性 let const var
作用域 块级作用域 块级作用域 函数作用域
重新赋值 允许 不允许 允许
提升(Hoisting) 不会,声明前使用会导致引用错误 不会,声明前使用会导致引用错误 会,被提升到函数或全局作用域顶部
暂时性死区(TDZ)
必须初始化
常见用途 循环变量、条件语句、块级作用域 常量、不可变对象、函数引用 全局变量、函数变量、在ES5及之前的代码中
使用安全性 高,避免变量提升和作用域污染 高,避免变量提升和作用域污染 低,易引起作用域污染和意外的变量重定义
内存管理 优,块级作用域帮助释放内存 优,块级作用域帮助释放内存 差,变量提升和全局作用域易导致内存泄漏
可维护性 高,代码结构清晰,易于维护 高,代码结构清晰,易于维护 低,易引起混乱和难以维护

可以看出,letconst 提供了块级作用域和更高的安全性,适用于现代 JavaScript 开发,而 var 由于其函数作用域和变量提升特性,已逐渐被淘汰。

高级技巧与实际应用

我们将探讨 letconst 在一些高级技巧和实际应用中的使用方法,包括模块化开发以及结合ES6+新特性的现代化应用。

模块化开发

模块化开发中,letconst 可以帮助避免全局命名空间的污染,提高代码的可维护性和重用性。

// 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);

WX20230807-141353@2x.png

常见误区和面试题解析

常见误区:你认为呢?

误以为 <code>const 声明的对象不可变忽略了 letconst 的块级作用域

面试题解析

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

const obj = { key: 'value' };

obj.key = 'newValue';

console.log(obj.key); // 输出 'newValue'

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

结论与展望

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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。