有效避免 Cannot read properties of null/undefined 报错
前言
TypeError: Cannot read properties of null (reading 'xxxx')
TypeError: Cannot read properties of undefined (reading 'name')
这是 JavaScript 典型的运行时错误,不同于编译时错误,这往往是在程序正式运行后才会出现。所以我们很难提前预知,不论是新手小白,还是技术大牛,在整个开发生涯中,都不可避免的与其打交道。
那么,知其原理,学习对策,方能避而远之。
原理分析
JavaScript 中的类型分为两大类,两大类又各自包含多个子类。第一大类是原始类型,包含 Number
、String
、Boolean
、Null
、Undefined
、Symbol
、BigInt
。第二大类是对象类型,包含 Object
、Array
、Function
、Date
、RegExp
等多个内置对象类型。
原始类型本身是不可变的简单值,没有属性和方法。但如果尝试访问一个原始类型的属性或调用方法时,JavaScript 引擎会在后台临时创建一个对应的包装对象,使它看起来像是有属性或方法。这就是为什么 String
、Number
、Boolean
等原始类型存在.toString()
方法的原因。即便是访问原始类型不存在的属性时,也可以返回 undefined,不至于抛出错误。例如:
既然 null
和 undefined
在 JavaScript 中也被定义为原始类型,为何访问不存在属性时却抛出错误?因为 JS 引擎没有给他们提供包装对象。来看一下它们定义:
null
:表示“空值”或“空对象引用”,是一种明确赋值的状态,表示某个变量没有值。undefined
:表示“未定义”,是变量未被赋值的默认状态。
对于这样定义的类型,访问它们的属性是没有意义的,这是一种发生在编程中的错误行为,所以自然会抛出错误。
知识拓展
为什么 typeof null === "object" ?这是 JavaScript 设计上的一个历史遗留问题。在最初的实现中,JavaScript 使用低位二进制表示变量类型。对象的类型标志位是 000,而 null 恰好被表示为 000,因此被误识别为对象类型。为了向后兼容,这个问题被保留下来,没有修复。
错误原因及解决方案
出现 TypeError: Cannot read properties of null (reading 'xxxx')
报错的原因有很多,下面介绍几种常见的情况以及相对应的解决方案。
1. 变量未正确初始化
原因: 声明一个变量但是没有赋值时,其值默认为 undefined 。
解决方案
2. 异步数据加载未完成
原因: fetch 和 response.json() 是异步操作,需要等待它们完成之后 data 才能被赋值。
解决方案
3. 对象嵌套属性缺失
原因: 尝试访问嵌套对象的属性,但某个中间属性是 null 或 undefined。
解决方案
知识拓展
可选链操作符?.
是 JavaScript ES2020 中引入的一种语法,用于安全地访问嵌套对象的属性或调用方法,而无需显式检查中间对象是否为 null 或 undefined。当使用?.
访问一个属性或方法时,如果该操作链中任何一部分为 null 或 undefined,整个表达式会短路并返回 undefined,而不会抛出错误。
4. 函数返回值为 null 或 undefined
原因: 某些函数可能返回 null 或 undefined,而没有正确处理返回值。
解决方案
知识拓展
||(逻辑或运算符)与 ??(空值合并运算符)都是从左向右判断,并返回第一个真值,若没有真值,则返回最右侧的值(即便是假值); || 与 ?? 的区别是,|| 判断所有假值(false、''、0、NaN、null、underined),而 ?? 只会将 null 和 undefined 视为假值;
5. 数组索引越界
原因: 访问数组中不存在的索引位置。
解决方案。
总结
在 JavaScript 开发中,Cannot read properties of null/undefined
是一种常见但可以有效预防的错误。
通过以下策略,可以大大减少此类问题的发生:
- 变量初始化:声明变量时赋初始值,避免 undefined。
- 异步数据处理:正确处理异步数据加载,使用
then()
或async/await
。 - 可选链操作符
?.
:安全访问嵌套属性,避免中间节点为 null 或 undefined 时出错。 - 默认值
||
或??
:为可能为 null/undefined 的值设置默认值,确保逻辑安全。 - 范围检查:访问数组索引或对象属性前,确保目标存在。
养成良好的编码习惯,确保每个变量、属性或返回值都有合理的初始状态。善用现代 JavaScript 特性(如可选链、空值合并操作符等) 提高代码的安全性和可读性。便可从容应对这类错误!