【前端面试小册】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 主要有两种方式:

  1. 使用 Function 构造函数
  2. 使用 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 安全性

  1. 永远不要直接使用 eval 解析用户输入
  2. 始终验证 JSON 格式
  3. 使用 Function 相对安全,但仍需谨慎

7.2 性能

  1. 原生 JSON.parse 性能最好
  2. Function 性能次之
  3. eval 性能最差

7.3 兼容性

  1. 优先使用原生 JSON.parse
  2. 自定义实现主要用于学习和理解原理
  3. 生产环境建议使用原生 API

八、面试要点总结

核心知识点

  1. 实现方式Functioneval
  2. 安全性:需要验证 JSON 格式,防止 XSS 攻击
  3. 正则验证:使用正则表达式验证 JSON 格式
  4. 性能考虑:原生 JSON.parse 性能最好

常见面试题

Q1: 如何实现 JSON.parse?

答:可以使用 FunctionevalFunction 相对安全,eval 需要额外验证。实现时需要注意安全性,验证 JSON 格式,防止 XSS 攻击。

Q2: eval 和 Function 的区别?

答:

  • eval:在当前作用域执行,性能较差,存在安全风险
  • Function:在全局作用域执行,性能较好,相对安全

Q3: 如何防止 XSS 攻击?

答:使用正则表达式验证 JSON 格式,确保输入是合法的 JSON 字符串,而不是恶意代码。

实战建议

  • ✅ 生产环境优先使用原生 JSON.parse
  • ✅ 自定义实现主要用于学习和理解原理
  • ✅ 如果必须自定义实现,务必添加格式验证
  • ✅ 永远不要直接 eval 用户输入
#前端面试##前端#
前端面试小册 文章被收录于专栏

每天更新3-4节,持续更新中... 目标:50天学完,上岸银行总行!

全部评论

相关推荐

点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务