1. 声明不可变性
1.1 基本类型的不可变性
js
复制代码
// 基本类型声明后不能修改
const name = 'John';
name = 'Jane'; // TypeError: Assignment to constant variable
const age = 25;
age = 26; // TypeError: Assignment to constant variable
const isValid = true;
isValid = false; // TypeError: Assignment to constant variable
1.2 引用类型的可变性
js
复制代码
// 对象的属性可以修改
const person = {
name: 'John',
age: 25
};
person.name = 'Jane'; // 正常工作
person.age = 26; // 正常工作
person = {}; // TypeError: Assignment to constant variable
// 数组的元素可以修改
const numbers = [1, 2, 3];
numbers.push(4); // 正常工作
numbers[0] = 0; // 正常工作
numbers = []; // TypeError: Assignment to constant variable
2. 声明必须赋值
2.1 正确的声明方式
js
复制代码
// ✅ 声明时必须初始化
const name = 'John';
const age = 25;
const person = { name: 'John' };
2.2 错误的声明方式
js
复制代码
// ❌ 不能只声明不赋值
const name; // SyntaxError: Missing initializer in const declaration
// ❌ 不能先声明后赋值
const age;
age = 25; // SyntaxError: Missing initializer in const declaration
3. 不允许重复定义
3.1 同一作用域重复声明
js
复制代码
// ❌ 同一作用域不能重复声明
const name = 'John';
const name = 'Jane'; // SyntaxError: Identifier 'name' has already been declared
// ❌ 与其他声明方式混用也不行
const age = 25;
var age = 26; // SyntaxError: Identifier 'age' has already been declared
let age = 26; // SyntaxError: Identifier 'age' has already been declared
3.2 不同作用域的声明
js
复制代码
// ✅ 不同作用域可以声明同名常量
const x = 1;
if (true) {
const x = 2; // 正常工作
console.log(x); // 2
}
console.log(x); // 1
function example() {
const x = 3; // 正常工作
console.log(x); // 3
}
4. 不具有变量提升
4.1 变量提升问题
js
复制代码
// ❌ 在声明前访问会报错
console.log(name); // ReferenceError: Cannot access 'name' before initialization
const name = 'John';
// ❌ 在函数中也是一样
function example() {
console.log(age); // ReferenceError: Cannot access 'age' before initialization
const age = 25;
}
5. 暂时性死区(TDZ)
5.1 基本概念
js
复制代码
// 从作用域开始到变量声明前的区域称为暂时性死区
{
console.log(name); // ReferenceError: Cannot access 'name' before initialization
const name = 'John';
}
5.2 复杂场景中的 TDZ
js
复制代码
// 函数参数中的 TDZ
function example(x = y, y = 1) {
return [x, y];
}
example(); // ReferenceError: Cannot access 'y' before initialization
// 条件语句中的 TDZ
if (true) {
console.log(value); // ReferenceError
const value = 123;
}
6. 不与顶层对象挂钩
6.1 与全局对象的关系
js
复制代码
// 浏览器环境
const name = 'John';
console.log(window.name); // undefined
// Node.js 环境
const age = 25;
console.log(global.age); // undefined
7. const 定义的数据修改限制
7.1 使用 Object.freeze() 实现真正的不可变
js
复制代码
// 使用 Object.freeze() 冻结对象
const person = Object.freeze({
name: 'John',
age: 25,
address: {
city: 'New York'
}
});
person.name = 'Jane'; // 静默失败或在严格模式下报错
person.age = 26; // 静默失败或在严格模式下报错
person.address.city = 'LA'; // 仍然可以修改(浅冻结)
// 深度冻结函数
function deepFreeze(obj) {
Object.keys(obj).forEach(prop => {
if (typeof obj[prop] === 'object' && obj[prop] !== null) {
deepFreeze(obj[prop]);
}
});
return Object.freeze(obj);
}
// 使用深度冻结
const config = deepFreeze({
api: {
url: 'https://api.example.com',
key: '123456'
},
settings: {
timeout: 1000
}
});
config.api.url = 'new-url'; // 无法修改
config.settings.timeout = 2000; // 无法修改
7.2 使用 const 的最佳实践
js
复制代码
// ✅ 用于不变的配置
const CONFIG = {
API_URL: 'https://api.example.com',
MAX_RETRIES: 3,
TIMEOUT: 5000
};
// ✅ 用于引用类型时,明确表达意图
const userSettings = Object.freeze({
theme: 'dark',
notifications: true
});
// ✅ 用于函数声明
const calculateTotal = (items) => {
return items.reduce((sum, item) => sum + item.price, 0);
};
// ❌ 避免对需要修改的数据使用 const
const userList = []; // 如果需要修改数组,应该使用 let
8. 实际应用场景
8.1 模块常量
js
复制代码
// constants.js
export const API_CONFIG = Object.freeze({
BASE_URL: 'https://api.example.com',
TIMEOUT: 5000,
HEADERS: {
'Content-Type': 'application/json'
}
});
// 使用常量
import { API_CONFIG } from './constants';
fetch(\`\${API_CONFIG.BASE_URL}/users\`, {
headers: API_CONFIG.HEADERS,
timeout: API_CONFIG.TIMEOUT
});
8.2 React/Vue 组件中的使用
js
复制代码
// React 组件
const DEFAULT_PROPS = Object.freeze({
theme: 'light',
size: 'medium'
});
const MyComponent = (props) => {
const finalProps = { ...DEFAULT_PROPS, ...props };
return <div className={finalProps.theme}>{/* ... */}</div>;
};
// Vue 组件
const VALIDATION_RULES = Object.freeze({
required: true,
minLength: 3,
maxLength: 20
});
export default {
data() {
return {
rules: VALIDATION_RULES
};
}
};