【前端面试小册】JS-第17节:实现 JSON.parse
第17节:实现 JSON.parse
一、核心概念
1.1 JSON.parse 的作用
JSON.parse() 用于将一个 JSON 字符串转换为 JavaScript 对象。
const jsonString = '{"name":"愚公上岸说","age":30}';
const obj = JSON.parse(jsonString);
console.log(obj); // { name: '愚公上岸说', age: 30 }
1.2 实现思路
实现 JSON.parse 主要有两种方式:
- 使用
Function构造函数 - 使用
eval函数
二、方法一:使用 Function 实现
2.1 基本实现
function myJsonParse(data) {
return new Function(`return ${data}`)();
}
// 测试数组
const arr = JSON.stringify([1, 2, 4]);
console.log(myJsonParse(arr)); // [1, 2, 4]
// 测试对象
const test = {
name: '愚公上岸说',
};
const newTest = JSON.stringify(test);
console.log(myJsonParse(newTest)); // {name: "愚公上岸说"}
2.2 原理说明
Function 构造函数:
new Function(code)创建一个新的函数对象- 传入的字符串会被当作函数体执行
- 返回函数执行的结果
执行流程:
graph TD
A[JSON 字符串] --> B[new Function return + 字符串]
B --> C[创建函数对象]
C --> D[执行函数]
D --> E[返回解析结果]
示例:
// JSON.stringify([1, 2, 4]) 返回 "[1,2,4]"
// new Function('return [1,2,4]') 创建函数
// 执行函数返回 [1, 2, 4]
2.3 优缺点
优点:
- ✅ 实现简单
- ✅ 性能较好
缺点:
- ❌ 存在安全风险(类似
eval) - ❌ 可能执行恶意代码
三、方法二:使用 eval 实现
3.1 普通实现
function myJsonParse(opt) {
return eval(`(${opt})`);
}
// 测试数组
const arr = JSON.stringify([1, 2, 4]);
console.log(myJsonParse(arr)); // [1, 2, 4]
// 测试对象
const test = {
name: '愚公上岸说',
};
const newTest = JSON.stringify(test);
console.log(myJsonParse(newTest)); // {name: "愚公上岸说"}
3.2 为什么需要括号?
// ❌ 错误:eval('{"name":"test"}') 会报错
// 因为 JavaScript 解析器会将 {} 当作代码块
// ✅ 正确:eval('({"name":"test"})') 返回对象
// 括号强制将内容解析为表达式
3.3 安全风险:XSS 攻击
问题:使用 eval,如果被解析的不是 JSON 对象而是一段带有攻击的 JS 代码,那么就会导致 XSS 漏洞。
// 恶意代码示例
const maliciousCode = 'alert("XSS攻击")';
eval(`(${maliciousCode})`); // 会执行恶意代码
攻击场景:
// 如果用户输入被直接 eval
const userInput = 'alert("XSS")';
myJsonParse(userInput); // 执行恶意代码
3.4 过滤 XSS 攻击实现
function myJsonParse(data) {
// 正则表达式用于验证 JSON 格式
const r1 = /^[\],:{}\s]*$/; // 匹配 JSON 结构字符
const r2 = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; // 匹配转义字符
const r3 = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; // 匹配值
const r4 = /(?:^|:|,)(?:\s*\[)+/g; // 匹配数组
// 是否是合法的 JSON
const isLegal = r1.test(
data
.replace(r2, '@') // 替换转义字符
.replace(r3, ']') // 替换值
.replace(r4, ''), // 替换数组标记
);
if (isLegal) {
return eval(`(${data})`);
}
throw new TypeError('请传入合法的 JSON');
}
3.5 正则表达式详解
r1: /^[\],:{}\s]*$/
- 匹配 JSON 结构字符:
],,,:,{,}, 空白字符 - 用于验证 JSON 的基本结构
r2: /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g
- 匹配转义字符:
\",\\,\/,\b,\f,\n,\r,\t,\uXXXX - 用于识别合法的转义序列
r3: /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g
- 匹配 JSON 值:
- 字符串:
"..." - 布尔值:
true,false - null:
null - 数字:
-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?
- 字符串:
r4: /(?:^|:|,)(?:\s*\[)+/g
- 匹配数组标记
- 用于识别数组结构
3.6 验证流程
graph TD
A[输入 JSON 字符串] --> B[替换转义字符]
B --> C[替换值]
C --> D[替换数组标记]
D --> E[验证结构字符]
E --> F{是否合法?}
F -->|是| G[使用 eval 解析]
F -->|否| H[抛出错误]
G --> I[返回解析结果]
四、方法对比
4.1 Function vs eval
| 特性 | Function | eval |
|---|---|---|
| 作用域 | 全局作用域 | 当前作用域 |
| 性能 | 较好 | 较差 |
| 安全性 | 相对安全 | 存在风险 |
| 使用场景 | 适合简单场景 | 需要额外验证 |
4.2 安全性对比
// Function:相对安全(但仍需谨慎)
function myJsonParse(data) {
return new Function(`return ${data}`)();
}
// eval:需要额外验证
function myJsonParse(data) {
// 需要验证 JSON 格式
if (isValidJSON(data)) {
return eval(`(${data})`);
}
throw new Error('Invalid JSON');
}
五、完整实现(推荐)
5.1 带验证的完整版本
function myJsonParse(data) {
// 参数验证
if (typeof data !== 'string') {
throw new TypeError('JSON.parse: expected string');
}
// 验证 JSON 格式
const isValid = validateJSON(data);
if (!isValid) {
throw new SyntaxError('JSON.parse: unexpected token');
}
// 使用 Function 解析(相对安全)
try {
return new Function(`return ${data}`)();
} catch (error) {
throw new SyntaxError(`JSON.parse: ${error.message}`);
}
}
function validateJSON(data) {
const r1 = /^[\],:{}\s]*$/;
const r2 = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
const r3 = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
const r4 = /(?:^|:|,)(?:\s*\[)+/g;
return r1.test(
data
.replace(r2, '@')
.replace(r3, ']')
.replace(r4, ''),
);
}
5.2 使用示例
// 正常使用
const obj = myJsonParse('{"name":"愚公上岸说","age":30}');
console.log(obj); // { name: '愚公上岸说', age: 30 }
// 错误处理
try {
myJsonParse('invalid json');
} catch (error) {
console.error(error.message); // JSON.parse: unexpected token
}
六、实际应用场景
6.1 数据解析
// 从服务器接收 JSON 字符串
const response = '{"users":[{"name":"Alice","age":25}]}';
const data = myJsonParse(response);
console.log(data.users[0].name); // Alice
6.2 配置解析
// 解析配置文件
const configStr = '{"apiUrl":"https://api.example.com","timeout":5000}';
const config = myJsonParse(configStr);
console.log(config.apiUrl); // https://api.example.com
6.3 数据转换
// 将 JSON 字符串转换为对象
const jsonArray = '[1,2,3,4,5]';
const array = myJsonParse(jsonArray);
console.log(array.reduce((a, b) => a + b)); // 15
七、注意事项
7.1 安全性
- 永远不要直接使用
eval解析用户输入 - 始终验证 JSON 格式
- 使用
Function相对安全,但仍需谨慎
7.2 性能
- 原生
JSON.parse性能最好 Function性能次之eval性能最差
7.3 兼容性
- 优先使用原生
JSON.parse - 自定义实现主要用于学习和理解原理
- 生产环境建议使用原生 API
八、面试要点总结
核心知识点
- 实现方式:
Function和eval - 安全性:需要验证 JSON 格式,防止 XSS 攻击
- 正则验证:使用正则表达式验证 JSON 格式
- 性能考虑:原生
JSON.parse性能最好
常见面试题
Q1: 如何实现 JSON.parse?
答:可以使用 Function 或 eval。Function 相对安全,eval 需要额外验证。实现时需要注意安全性,验证 JSON 格式,防止 XSS 攻击。
Q2: eval 和 Function 的区别?
答:
eval:在当前作用域执行,性能较差,存在安全风险Function:在全局作用域执行,性能较好,相对安全
Q3: 如何防止 XSS 攻击?
答:使用正则表达式验证 JSON 格式,确保输入是合法的 JSON 字符串,而不是恶意代码。
实战建议
- ✅ 生产环境优先使用原生
JSON.parse - ✅ 自定义实现主要用于学习和理解原理
- ✅ 如果必须自定义实现,务必添加格式验证
- ✅ 永远不要直接
eval用户输入
前端面试小册 文章被收录于专栏
每天更新3-4节,持续更新中... 目标:50天学完,上岸银行总行!
