避免那些可恶的cannot read property of undefined 错误

更新日期: 2019-08-16阅读: 2.9k标签: 错误

Uncaught TypeError: Cannot read property 'foo' of undefined. 是一个我们在 JavaScript 开发中都遇到过的可怕错误。或许是某个 api 返回了意料外的空值,又或许是其它什么原因,这个错误是如此的普遍而广泛以至于我们无法判断。

我最近遇到了一个问题,某一环境变量出于某种原因没有被加载,导致各种各样的报错夹杂着这个错误摆在我面前。不论什么原因,放着这个错误不处理都会是灾难性的。所以我们该怎么从源头阻止这个问题发生呢?

让我们一起来找出解决方案。


工具

如果你已经在项目里用到一些工具库,很有可能库里已经有了预防这个问题发生的函数。lodash 里的 _.get(文档) 或者 Ramda 里的 R.path(文档)都能确保你安全使用对象。

如果你已经使用了工具库,那么这看起来已经是最简单的方法了。如果你没有使用工具库,继续读下去吧!


使用 && 短路

JavaScript 里有一个关于逻辑运算符的有趣事实就是它不总是返回布尔值。根据说明,『&& 或者 || 运算符的返回值并不一定是布尔值。而是两个操作表达式的其中之一。』

举个 && 运算符的例子,如果第一个表达式的布尔值是 false,那么该值就会被返回。否则,第二个表达式的值就会被使用。这说明表达式 0 && 1 会返回 0(一个 false 值),而表达式 2 && 3 会返回 3。如果多个 && 表达式连在一起,它们将会返回第一个 false 植或最后一个值。举个例子,1 && 2 && 3 && null && 4 会返回 null,而 1 && 2 && 3 会返回 3。

那么如何安全的获取嵌套对象内的属性呢?JavaScript 里的逻辑运算符会『短路』。在这个 && 的例子中,这表示表达式会在到达第一个假值时停下来。

复制代码在这个例子中,destroyAllHumans 不会被调用,因为 && 停止了所有在 false 之后的运算 这可以被用于安全地获取嵌套对象的属性。

除了简单,这个方法的一个主要优势就是在处理较少嵌套时十分简洁。然而,当访问深层的对象时,它会变得十分冗长。

对象嵌套的越深,它就变得越笨重。


『或单元』

Oliver Steele 提出这个方法并且在他发布的博客里探究了更多的细节,『单元第一章:或单元』我会试着在这里给出一个简要的解释。

与上面的短路例子类似,这个方法通过检查值是否为假来生效。如果值为假,它会尝试取得空对象的属性。在上面的例子中,favorites.reading 的值是 null,所以从一个空对象上获得books属性。这会返回一个 undefined 结果,所以0会被用于获取空数组中的成员。

这个方法相较于 && 方法的优势是它避免了属性名的重复。在深层嵌套的对象中,这会成为显著的优势。而主要的缺点在于可读性 — 这不是一个普通的模式,所以这或许需要阅读者花一点时间理解它是怎么运作的。

不幸的是,在 JavaScript 里,try...catch 声明不是表达式,它们不会像某些语言里那样计算值。这导致不能用一个简洁的 try 声明来作为设置变量的方法。 有一种选择就是在 try...catch 前定义一个 let 变量。

虽然这很冗长,但这对设置单一变量起作用(就是说,如果变量还没有吓跑你的话)然而,把它们写在一块就会出问题。

如果任意一个获取属性的尝试失败了,这会导致它们全部返回默认值。 一个可选的方法是用一个可复用的工具函数封装 try...catch。

通过一个函数包裹获取对象属性的行为,你可以延后『不安全』的代码,并且把它传入 try...catch。

这个方法的主要优势在于它十分自然地获取了属性。只要属性被封装在一个函数中,属性就可以被安全访问,同时可以为不存在的路径返回指定的默认值。


与默认对象合并

通过将对象与相近结构的『默认』对象合并,我们能确保获取属性的路径是安全的。

然而,需要注意并非单个属性,而是整个嵌套对象都会被覆写。

不!为了解决这点,我们需要类似地复制每一个嵌套对象。

好多了!

这种模式在这类插件或组件中很常见,它们接受一个包含默认值得大型可配置对象。

这种方式的一个额外好处就是通过编写一个默认对象,我们引入了文档来介绍这个对象。不幸的是,按照数据的大小和结构,复制每一个嵌套对象进行合并有可能造成污染。


未来:可选链式调用

目前 TC39 提案中有一个功能叫『可选链式调用』。这个新的运算符看起来像这样:

码?. 运算符通过短路方式运作:如果 ?. 运算符的左侧计算值为 null 或者 undefined,则整个表达式会返回 undefined 并且右侧不会被计算。 为了有一个自定义的默认值,我们可以使用 || 运算符以应对未定义的情况。


我们该使用哪一种方法?

答案或许你已经猜到了,正是那句老话『看情况而定』。如果可选链式调用已经被加到语言中并且获得了必要的浏览器支持,这或许是最好的选择。然而,如果你不来自未来,那么你有更多需要考虑的。你在使用工具库吗?你的对象嵌套有多深?你是否需要指定默认值?我们需要根据不同的场景采用不同的方法。

译者:Xcco
校对者:hanxiansen, Mirosalva
原文地址:Avoiding those dang cannot read property of undefined errors

链接: https://fly63.com/article/detial/4761

解决Cannot read property range of null 错误

vue工程npm run serve/start/dev启动时,node_modules文件报:Cannot read property range of null 错误,该问题是babel-eslint版本更新问题导致的;

HTTP 400 错误 - 请求无效 (Bad request)

在ajax请求后台数据时有时会报 HTTP 400 错误 - 请求无效 (Bad request);出现这个请求无效报错说明请求没有进入到后台服务里;原因:前端提交数据的字段名称或者是字段类型和后台的实体类不一致

js异步错误捕获

我们都知道 try catch 无法捕获 setTimeout 异步任务中的错误,那其中的原因是什么。以及异步代码在 js 中是特别常见的,我们该怎么做才比较?

不能执行已释放Script的代码

父页面初始化声明变量a为数组(数组对象是引用类型,赋值传递的是地址),创建iframe子页面后给父页面变量a赋值,赋值后销毁iframe子页面,再次调用变量a的时候就会抛出异常‘SCRIPT5011:不能执行已释放Script的代码’。

JS错误处理:前端JS/Vue/React/Iframe/跨域/Node

js错误的实质,也是发出一个事件,处理他,error实例对象message:错误提示信息,name:错误名称(非标准属性)宿主环境赋予

nodejs提示 cross-device link not permitted, rename 错误解决方法

文件上传的功能时候,调用fs.renameSync方法错误,这个提示是跨区重命名文件出现的权限问题。先从源文件拷贝到另外分区的目标文件,然后再unlink,就可以了。

Js中使用innerHTML的缺点是什么?

如果在JavaScript中使用innerHTML,缺点是:内容随处可见;不能像“追加到innerHTML”一样使用;innerHTML不提供验证,因此我们可能会在文档中插入有效的和破坏性的HTML并将其中断

Web前端开发,必须规避的8个错误点!

现在,有越来越多所谓的“教程”来帮助我们提高网站的易用性。我们收集了一些在Web开发中容易出错和被忽略的小问题,并且提供了参考的解决方案,以便于帮助Web开发者更好的完善网站。

web前端错误监控

为什么要做前端错误监控?1. 为了保证产品的质量2. 有些问题只存在于线上特定的环境3. 后端错误有监控,前端错误没有监控,前端错误分为两类: 即时运行错误和资源加载错误

自定义错误及扩展错误

当我们在进行开发的时候,通常需要属于我们自己的错误类来反映任务中可能出现的特殊情况。对于网络操作错误,我们需要 HttpError,对于数据库操作错误,我们需要 DbError,对于搜索操作错误,我们需要 NotFoundError,等等

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!