实现ES6的const
通过Object.defineProperty模拟实现ES6中const关键字的常量定义功能
问题
在ES6之前,JavaScript没有原生的常量定义方式。本题要求手动实现一个类似ES6 const 的功能,使得定义的变量:
- 不能被重新赋值
- 必须在声明时初始化
- 具有块级作用域(可选实现)
解答
/**
* 实现const关键字功能
* @param {string} key - 常量名
* @param {*} value - 常量值
* @param {object} target - 目标对象,默认为window
*/
function _const(key, value, target = window) {
// 检查是否已经定义过该常量
if (target.hasOwnProperty(key)) {
throw new TypeError(`Identifier '${key}' has already been declared`);
}
// 检查是否提供了初始值
if (value === undefined) {
throw new SyntaxError('Missing initializer in const declaration');
}
// 使用Object.defineProperty定义不可修改的属性
Object.defineProperty(target, key, {
value: value,
writable: false, // 不可写
enumerable: true, // 可枚举
configurable: false // 不可配置(不可删除)
});
}
// 方案二:使用Proxy实现更严格的拦截
function createConstContext() {
const constMap = new Map();
return {
define(key, value) {
if (constMap.has(key)) {
throw new TypeError(`Identifier '${key}' has already been declared`);
}
if (value === undefined) {
throw new SyntaxError('Missing initializer in const declaration');
}
constMap.set(key, value);
},
get(key) {
if (!constMap.has(key)) {
throw new ReferenceError(`${key} is not defined`);
}
return constMap.get(key);
},
set(key, value) {
if (constMap.has(key)) {
throw new TypeError('Assignment to constant variable');
}
throw new ReferenceError(`${key} is not defined`);
}
};
}
使用示例
// 示例1:基础使用
_const('PI', 3.14159);
console.log(PI); // 3.14159
// 尝试重新赋值(会静默失败或在严格模式下报错)
PI = 3.14;
console.log(PI); // 3.14159(值未改变)
// 示例2:重复声明会报错
try {
_const('PI', 3.14);
} catch (e) {
console.error(e.message); // Identifier 'PI' has already been declared
}
// 示例3:未初始化会报错
try {
_const('MAX_SIZE');
} catch (e) {
console.error(e.message); // Missing initializer in const declaration
}
// 示例4:使用Proxy方案
const constCtx = createConstContext();
constCtx.define('API_URL', 'https://api.example.com');
console.log(constCtx.get('API_URL')); // https://api.example.com
try {
constCtx.set('API_URL', 'https://new-api.com');
} catch (e) {
console.error(e.message); // Assignment to constant variable
}
// 示例5:定义对象常量
_const('CONFIG', { port: 3000, host: 'localhost' });
console.log(CONFIG.port); // 3000
// 注意:对象内部属性仍可修改(与真实const行为一致)
CONFIG.port = 8080;
console.log(CONFIG.port); // 8080
// 但不能重新赋值整个对象
CONFIG = {}; // 静默失败或报错
关键点
-
Object.defineProperty:API,通过设置
writable: false和configurable: false实现不可修改和不可删除 -
属性描述符配置:
writable: false- 防止值被修改configurable: false- 防止属性被删除或重新配置enumerable: true- 保持可枚举性
-
初始化检查:const必须在声明时赋值,需要检查value是否为undefined
-
重复声明检查:使用
hasOwnProperty检查属性是否已存在,防止重复声明 -
浅层不可变:与真实const一样,只保证变量引用不可变,对象内部属性仍可修改。如需深度冻结,可使用
Object.freeze() -
作用域限制:简单实现挂载在window上,实际const具有块级作用域,完整实现需要配合作用域管理
-
Proxy方案优势:可以实现更精确的错误提示和访问控制,更接近真实const的行为
目录