JSON.parse() 从入门到精通:把字符串变成可用数据的核心技术
一个让我印象深刻的bug
有次做项目,从后端拿到一段数据,直接就用 data.length 想获取长度,结果一直是 undefined。找了半小时才发现——我面对的根本不是数组,而是一串长得像数组的字符串。这就是没理解 JSON.parse() 的作用。
JSON.parse() 的核心作用就是:把那些看起来像JSON的字符串,变成真正能用的JavaScript对象、数组或原始值。
方法签名深度解析
JSON.parse(text[, reviver])
text 参数:必须严格遵守JSON格式
这个参数要求传入的是严格符合JSON规范的字符串。很多人在这里翻车,包括当年的我:
// 错误的写法——会报错
JSON.parse("{name: '代码号学习编程'}"); // 键名没有双引号
JSON.parse("{'name': 'code'}"); // 用了单引号
JSON.parse('{"name": "code",}'); // 多了一个逗号
// 正确的写法
JSON.parse('{"name": "代码号学习编程"}'); // 键名和字符串值都用双引号
JSON.parse('[1, 2, 3]'); // 数组格式
JSON.parse('"hello"'); // 字符串
JSON.parse('123'); // 数字
JSON.parse('true'); // 布尔值
个人经验:如果从接口获取数据解析报错,先打印出来看看,很字符串前后有不可见字符。用 trim() 处理一下能解决大部分问题。
reviver 参数:很少有人用的高级功能
这个可选参数是个函数,可以在返回之前对解析出来的每个值进行处理。我在处理日期字段时经常用到:
const jsonStr = '{"name":"张三","birthday":"1995-08-23","score":"89.5"}';
const user = JSON.parse(jsonStr, (key, value) => {
if (key === 'birthday') {
return new Date(value); // 把字符串转成Date对象
}
if (key === 'score') {
return Number(value); // 把字符串数字转成数值
}
return value;
});
console.log(user.birthday.getFullYear()); // 1995,可以调用Date方法了
console.log(typeof user.score); // "number",可以进行数学运算了
reviver 函数的执行顺序是从最内层开始向外层传递,这点要注意。
项目开发中的典型应用场景
场景1:处理API响应数据
// 模拟从服务器获取的JSON字符串
const apiResponse = `{
"code": 200,
"message": "success",
"data": {
"articles": [
{"id": 1, "title": "JSON入门指南", "views": 1250},
{"id": 2, "title": "前端面试题解析", "views": 3420}
],
"total": 2
}
}`;
// 解析后才能真正访问数据结构
const result = JSON.parse(apiResponse);
if (result.code === 200) {
result.data.articles.forEach(article => {
console.log(`《${article.title}》有 ${article.views} 次阅读`);
});
}
场景2:从localStorage读取数据
// 存储时用stringify,读取时一定要用parse
function loadUserPreferences() {
const saved = localStorage.getItem('userPrefs');
// 重要:处理空值情况
if (!saved) {
return { theme: 'light', fontSize: 14 }; // 返回默认值
}
try {
return JSON.parse(saved);
} catch (e) {
console.error('配置数据损坏,使用默认配置');
return { theme: 'light', fontSize: 14 };
}
}
const prefs = loadUserPreferences();
console.log(`当前主题:${prefs.theme}`); // 可以直接访问属性
场景3:深拷贝的快速实现
function deepClone(obj) {
// 这种写法有局限性(会丢失函数和undefined),但够快够简单
return JSON.parse(JSON.stringify(obj));
}
const original = {
name: "配置",
version: "1.0",
settings: {
volume: 80,
muted: false
}
};
const cloned = deepClone(original);
cloned.settings.volume = 50;
console.log(original.settings.volume); // 80,原对象没被修改
安全处理的实践
我早期写代码经常直接 JSON.parse(data),直到某次数据格式异常导致整个页面白屏。现在我都会加上保护:
function safeJSONParse(str, fallback = null) {
if (typeof str !== 'string') {
console.warn('safeJSONParse: 输入不是字符串');
return fallback;
}
try {
return JSON.parse(str);
} catch (error) {
console.error('JSON解析失败:', error.message);
// 可以上报错误到监控系统
// reportError(error);
return fallback;
}
}
// 使用示例
const userInput = '{"name":"代码号学习编程"}'; // 假设这是用户输入
const data = safeJSONParse(userInput, {});
console.log(data.name); // 即使解析失败也不会报错
课程知识要点
-
JSON.parse() 的核心功能:将符合JSON规范的字符串转换为对应的JavaScript对象
-
必须严格遵守JSON语法:键名和字符串值必须用双引号,不能有注释或尾随逗号
-
reviver参数的应用:可以在解析过程中对特定字段进行转换处理
-
解析结果类型:可以是对象、数组、字符串、数字、布尔值或null
-
错误处理的重要性:始终用try-catch包裹parse操作,防止程序崩溃
-
localStorage配合:读取存储数据后必须用parse才能得到可操作对象
-
浏览器兼容性:所有现在浏览器都支持,包括IE8以上版本
-
性能考虑:频繁解析大JSON会影响性能,考虑缓存解析结果
为什么不用eval解析JSON?
有些初学者会问:直接用 eval('(' + json + ')') 不也行吗?
我见过同事这么写过,但这是非常危险的做法。eval 会执行任何JavaScript代码,如果JSON字符串被注入恶意代码,整个应用就暴露在风险中。而 JSON.parse() 只解析JSON格式,更安全也更严格。而且 eval 性能也比 parse 差很多。
常见错误排查清单
当你用 JSON.parse() 报错时,按这个顺序检查:
-
检查字符串首尾:有没有多余空格或换行?用
trim()处理 -
检查引号:是不是用了双引号?单引号不行
-
检查键名:有没有用引号包起来?属性名必须加双引号
-
检查特殊字符:字符串里有没有控制字符?需要转义
-
检查数据类型:是不是真的传了字符串?传了对象进来
// 排查示例
function debugJSONParse(str) {
console.log('原始输入类型:', typeof str);
console.log('原始输入预览:', str.substring(0, 50));
try {
return JSON.parse(str);
} catch (e) {
console.error('解析失败位置:', e.message);
// 常见错误信息解析
if (e.message.includes('position')) {
const pos = e.message.match(/position (\d+)/).[1];
if (pos) {
console.log('错误位置附近:', str.substring(Number(pos)-10, Number(pos)+10));
}
}
throw e;
}
}
掌握了 JSON.parse(),你就掌握了把字符串数据"激活"的关键技能。无论API调用、本地存储,还是配置文件读取,都离不开它。