ES6中的const用法详解

一、const基本概念

1. const是什么?

  • const 用于声明常量(不可重新赋值的变量)

  • 具有块级作用域(与let相同)

  • 存在暂时性死区(TDZ)

  • 必须在声明时初始化

javascript 复制代码
// 基本用法
const PI = 3.14159;
console.log(PI); // 3.14159

// 必须初始化
const MAX_VALUE; // SyntaxError: Missing initializer in const declaration

// 不能重新赋值
PI = 3.14; // TypeError: Assignment to constant variable

2. const vs let vs var

javascript 复制代码
// 作用域对比
{
    var varVariable = 'var';
    let letVariable = 'let';
    const constVariable = 'const';
}

console.log(varVariable); // 'var' - 函数作用域
console.log(letVariable); // ReferenceError - 块级作用域
console.log(constVariable); // ReferenceError - 块级作用域

// 重复声明对比
var a = 1;
var a = 2; // 允许

let b = 1;
let b = 2; // SyntaxError: Identifier 'b' has already been declared

const c = 1;
const c = 2; // SyntaxError: Identifier 'c' has already been declared

// 变量提升对比
console.log(x); // undefined - var会提升
var x = 10;

console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 20;

console.log(z); // ReferenceError: Cannot access 'z' before initialization
const z = 30;

二、const的特殊行为

1. const与不可变性

javascript 复制代码
// const保证的是绑定(变量名与值的引用)不可变,不是值本身不可变

// 基本类型 - 值不可变
const num = 42;
num = 100; // TypeError: Assignment to constant variable

const str = 'hello';
str[0] = 'H'; // 静默失败,字符串不可变
console.log(str); // 'hello'

// 引用类型 - 引用不可变,但内容可变
const obj = { name: 'Alice' };
obj = {}; // TypeError: 不能重新赋值(改变引用)
obj.name = 'Bob'; // 允许:修改对象内容
obj.age = 25; // 允许:添加新属性
console.log(obj); // {name: 'Bob', age: 25}

const arr = [1, 2, 3];
arr = []; // TypeError: 不能重新赋值
arr.push(4); // 允许:修改数组内容
arr[0] = 100; // 允许:修改元素
console.log(arr); // [100, 2, 3, 4]

// 函数
const func = () => console.log('hello');
func = () => console.log('world'); // TypeError
func.prop = 'value'; // 允许:添加属性

2. 实现真正的不可变对象

javascript 复制代码
// 方法1:Object.freeze() - 浅冻结
const obj = Object.freeze({ name: 'Alice', address: { city: 'Beijing' } });

obj.name = 'Bob'; // 静默失败(严格模式下报错)
obj.age = 25; // 静默失败(严格模式下报错)

// 但是嵌套对象仍然可变
obj.address.city = 'Shanghai'; // 允许!
console.log(obj.address.city); // 'Shanghai'

// 方法2:深冻结
function deepFreeze(obj) {
    Object.keys(obj).forEach(key => {
        if (obj[key] && typeof obj[key] === 'object') {
            deepFreeze(obj[key]);
        }
    });
    return Object.freeze(obj);
}

const deepFrozen = deepFreeze({ 
    name: 'Alice', 
    address: { city: 'Beijing' } 
});

deepFrozen.address.city = 'Shanghai'; // 静默失败
console.log(deepFrozen.address.city); // 'Beijing'

// 方法3:使用Object.seal() - 密封
const sealed = Object.seal({ name: 'Alice' });
sealed.name = 'Bob'; // 允许:修改现有属性
sealed.age = 25; // 静默失败:不能添加新属性
delete sealed.name; // 静默失败:不能删除属性

三、const的实用场景

1. 配置和常量

javascript 复制代码
// 应用配置
const CONFIG = {
    API_URL: 'https://api.example.com',
    TIMEOUT: 5000,
    RETRY_ATTEMPTS: 3,
    FEATURES: {
        LOGGING: true,
        CACHE: false,
        ANALYTICS: true
    }
};

// 使用Object.freeze确保配置不被修改
Object.freeze(CONFIG);
Object.freeze(CONFIG.FEATURES);

// 数学常量
const MATH_CONSTANTS = Object.freeze({
    PI: 3.141592653589793,
    E: 2.718281828459045,
    PHI: 1.618033988749895,
    SQRT2: 1.4142135623730951
});

// 状态码
const HTTP_STATUS = Object.freeze({
    OK: 200,
    CREATED: 201,
    BAD_REQUEST: 400,
    UNAUTHORIZED: 401,
    FORBIDDEN: 403,
    NOT_FOUND: 404,
    INTERNAL_SERVER_ERROR: 500
});

2. 函数式编程

javascript 复制代码
// 纯函数中使用const
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

// 高阶函数
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);

// 柯里化
const curry = (fn) => {
    const arity = fn.length;
    return function curried(...args) {
        if (args.length >= arity) {
            return fn.apply(this, args);
        }
        return (...moreArgs) => curried.apply(this, args.concat(moreArgs));
    };
};

// 使用
const addCurried = curry((a, b) => a + b);
const add5 = addCurried(5);
console.log(add5(10)); // 15

3. 模块导出

javascript 复制代码
// module.js
export const VERSION = '1.0.0';

export const API_ENDPOINTS = Object.freeze({
    USERS: '/api/users',
    POSTS: '/api/posts',
    COMMENTS: '/api/comments'
});

export const createService = (config) => {
    const baseUrl = config.baseUrl;
    
    return {
        getUsers: () => fetch(`${baseUrl}${API_ENDPOINTS.USERS}`),
        getPosts: () => fetch(`${baseUrl}${API_ENDPOINTS.POSTS}`)
    };
};

// main.js
import { VERSION, API_ENDPOINTS, createService } from './module.js';

console.log(VERSION); // '1.0.0'
console.log(API_ENDPOINTS.USERS); // '/api/users'

const service = createService({ baseUrl: 'https://example.com' });

四、const与循环

1. for循环中的const

javascript 复制代码
// 传统for循环 - 不能使用const(因为i需要重新赋值)
for (let i = 0; i < 5; i++) { // 正确:使用let
    console.log(i);
}

for (const i = 0; i < 5; i++) { // TypeError: Assignment to constant variable
    console.log(i);
}

// for...in 循环 - 可以使用const
const obj = { a: 1, b: 2, c: 3 };
for (const key in obj) {
    console.log(key, obj[key]);
    // key = 'newKey'; // TypeError: 不能重新赋值
}

// for...of 循环 - 可以使用const
const arr = [10, 20, 30];
for (const value of arr) {
    console.log(value);
    // value = 100; // TypeError: 不能重新赋值
}

// 数组forEach - 可以使用const
arr.forEach((value, index) => {
    const squared = value * value; // 每次迭代创建新的const
    console.log(squared);
});

2. 迭代中的const模式

javascript 复制代码
// 每次迭代都创建新的const绑定
const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
    { id: 3, name: 'Charlie' }
];

// 每次迭代都是独立的const
for (const user of users) {
    // user是const,不能重新赋值,但可以修改其属性
    // user = {}; // TypeError
    user.active = true; // 允许:修改对象内容
    console.log(user);
}

// map方法中使用const
const userNames = users.map(user => {
    const { id, name } = user; // 解构赋值创建const
    return { id, name };
});

五、const的性能优化

1. const vs let的性能

javascript 复制代码
// 现代JavaScript引擎对const有优化
// const告诉引擎这个变量不会重新赋值,引擎可以做更多优化

// 示例:循环中使用const
const data = new Array(1000000).fill(0);

// 使用const(在for...of中)
function sumWithConst(data) {
    let total = 0;
    for (const value of data) {
        total += value;
    }
    return total;
}

// 使用let
function sumWithLet(data) {
    let total = 0;
    for (let i = 0; i < data.length; i++) {
        total += data[i];
    }
    return total;
}

// 实际上,现代引擎优化得很好,差异很小
// 但使用const可以提高代码可读性,表明变量的意图

2. 内存管理

javascript 复制代码
// const有助于避免意外的重新赋值,减少bug
function processItems(items) {
    // 使用const声明不会改变的变量
    const processedItems = items.map(item => ({
        ...item,
        processed: true
    }));
    
    const validItems = processedItems.filter(item => item.isValid);
    const totalCount = validItems.length;
    
    // 这些变量都不会被重新赋值,使用const很安全
    return { processedItems, validItems, totalCount };
}

// 如果使用let,后续可能意外修改
function buggyFunction(items) {
    let result = items.map(processItem);
    // ... 很多代码 ...
    result = {}; // 意外的重新赋值!
    return result;
}

六、const的高级用法

1. const与解构赋值

javascript 复制代码
// 数组解构
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [3, 4, 5]

// 对象解构
const person = { name: 'Alice', age: 30, city: 'Beijing' };
const { name, age } = person;
console.log(name, age); // Alice 30

// 重命名
const { name: personName, age: personAge } = person;

// 默认值
const { name: userName = 'Anonymous', country = 'China' } = person;
console.log(userName, country); // Alice China

// 嵌套解构
const company = {
    name: 'Tech Corp',
    address: {
        city: 'Shanghai',
        street: 'Main St'
    }
};

const { 
    name: companyName, 
    address: { 
        city: companyCity,
        street: companyStreet 
    } 
} = company;

console.log(companyName, companyCity); // Tech Corp Shanghai

2. const与模块模式

javascript 复制代码
// 创建不可变的单例
const Singleton = (function() {
    let instance;
    
    function createInstance() {
        return {
            data: [],
            add(item) {
                this.data.push(item);
            },
            get() {
                return [...this.data]; // 返回副本,保护数据
            }
        };
    }
    
    return {
        getInstance() {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    };
})();

const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true

七、const的最佳实践

1. 代码规范建议

javascript 复制代码
// 默认使用const,只有在需要重新赋值时才使用let

// 好:使用const
const userName = 'Alice';
const userAge = 30;
const isActive = true;

// 只在需要重新赋值时使用let
let counter = 0;
counter = 1; // 需要重新赋值

// 循环变量通常用let
for (let i = 0; i < 10; i++) {
    const item = items[i]; // 循环体内使用const
    // ...
}

// 避免使用var
// var outdated = '不要使用'; // 避免

2. 命名约定

javascript 复制代码
// 常规变量 - 驼峰命名
const userName = 'Alice';
const maxRetryCount = 3;

// 全局常量 - 全大写加下划线
const MAX_SIZE = 100;
const API_BASE_URL = 'https://api.example.com';
const DEFAULT_TIMEOUT = 5000;

// 枚举类型 - 使用对象冻结
const COLOR = Object.freeze({
    RED: '#FF0000',
    GREEN: '#00FF00',
    BLUE: '#0000FF'
});

// 配置对象 - 明确不可变
const APP_CONFIG = Object.freeze({
    version: '1.0.0',
    environment: 'production',
    features: Object.freeze({
        analytics: true,
        caching: false
    })
});

3. 常见错误和陷阱

javascript 复制代码
// 陷阱1:认为const对象完全不可变
const user = { name: 'Alice' };
user.name = 'Bob'; // 允许!只是不能重新赋值user

// 解决方案:使用Object.freeze
const frozenUser = Object.freeze({ name: 'Alice' });
frozenUser.name = 'Bob'; // 静默失败或严格模式报错

// 陷阱2:const在循环中的使用
for (const i = 0; i < 5; i++) { // TypeError
    // ...
}

// 正确:使用let
for (let i = 0; i < 5; i++) {
    const item = items[i]; // 在循环体内使用const
}

// 陷阱3:const与立即执行函数
const result = (function() {
    const value = 42;
    return value;
})();
// value在这里不可访问 - 正确

// 陷阱4:const与异步代码
const fetchData = async () => {
    const response = await fetch('/api/data'); // 正确
    const data = await response.json(); // 正确
    return data;
};

// 如果有重新赋值需求,使用let
const processItems = async (items) => {
    let result = [];
    for (const item of items) {
        const processed = await processItem(item);
        result = result.concat(processed); // 需要重新赋值
    }
    return result;
};

八、const在TypeScript中的使用

javascript 复制代码
// TypeScript中的const有额外类型推断

// 基本类型
const num = 42; // 类型推断为 42(字面量类型),而不是number
const str = 'hello'; // 类型为 "hello",而不是string

// 对象类型
const obj = { x: 10, y: 20 }; // 类型为 { x: number; y: number; }

// 数组类型
const arr = [1, 2, 3]; // 类型为 number[]

// as const断言 - 创建完全不可变的类型
const user = {
    name: 'Alice',
    age: 30
} as const;
// 类型为 { readonly name: "Alice"; readonly age: 30; }

const numbers = [1, 2, 3] as const;
// 类型为 readonly [1, 2, 3]

// 使用const enum(编译时常量)
const enum Direction {
    Up = 'UP',
    Down = 'DOWN',
    Left = 'LEFT',
    Right = 'RIGHT'
}

const direction = Direction.Up;
// 编译后:const direction = "UP" /* Direction.Up */;

总结

const的核心要点:

  1. 绑定不可变:变量名与值/引用的绑定不可变

  2. 必须初始化:声明时必须赋值

  3. 块级作用域:只在当前块内有效

  4. 暂时性死区:声明前不可访问

  5. 支持闭包:可被内部函数引用

使用建议:

  1. 默认使用const,需要重新赋值时才用let

  2. 避免使用var,使用let/const替代

  3. 使用Object.freeze保护对象内容

  4. 命名约定:全大写用于全局常量

  5. 结合解构赋值使用const更安全

适用场景:

  • 配置值和常量

  • 导入的模块和函数

  • 循环中的迭代变量(for...of, for...in)

  • 函数式编程中的纯函数

  • 任何不需要重新赋值的变量

const是编写可维护、可预测JavaScript代码的重要工具,它通过限制变量的可变性来提高代码质量。

相关推荐
Van_captain19 小时前
React Native for OpenHarmony Toast 轻提示组件:自动消失的操作反馈
javascript·开源·harmonyos
Van_captain19 小时前
React Native for OpenHarmony Modal 模态框组件:阻断式交互的设计与实现
javascript·开源·harmonyos
xkxnq19 小时前
第一阶段:Vue 基础入门(第 14天)
前端·javascript·vue.js
前端小臻19 小时前
列举react中类组件和函数组件常用到的方法
前端·javascript·react.js
研☆香19 小时前
html css js文件开发规范
javascript·css·html
赵民勇19 小时前
JavaScript中的this详解(ES5/ES6)
前端·javascript·es6
wayne21419 小时前
React Native 状态管理方案全梳理:Redux、Zustand、React Query 如何选
javascript·react native·react.js
我的golang之路果然有问题19 小时前
Mac 上的 Vue 安装和配置记录
前端·javascript·vue.js·笔记·macos
赵民勇19 小时前
JavaScript中的Mixin模式详解
javascript·ecmascript