资讯专栏INFORMATION COLUMN

避免取值时出现Cannot read property 'xx' of unde

fantix / 2910人阅读

摘要:由于是以空函数为代理对象,我们可以将执行它,触发。中会遍历数组依次取值,如果发现无法继续取值则,跳出循环。

本文来自我的博客,欢迎大家去GitHub上star我的博客

我们在取值特别是链式取值的时候,常常会遇到Cannot read property "xx" of undefined的错误,如何避免这种情况的发生呢?这里有几种方法以供参考

使用成熟的库方法

这是最简单的一种手段:只用引入lodash,使用_.get方法;或者引入Ramda,使用R.path方法,我们就能规避出现上述错误的风险

尽管这种方法十分奏效且方便,但我还是希望你能看完其他方法

巧用&&和||

我们知道,在JavaScript中,使用&&或者||操作符,最后返回的值不一定是boolean类型的,比如下面这个例子:

console.log(undefined && "a"); //undefined
console.log("a" && "b"); //b
console.log(undefined || "a"); //a
console.log("a" || "b"); //a

&&:如果第一项是falsy(虚值,Boolean上下文中已认定可转换为‘假‘的值),则返回第一项,否则返回第二项

||:如果第一项是falsy,返回第二项,否则返回第一项

我们可以利用这种规则进行取值

我们先拟一个数据

const artcle = {
    authorInfo: {
        author: "Bowen"
    },
    artcleInfo: {
        title: "title",
        timeInfo: {
            publishTime: "today"
        }
    }
};

接下来利用&&和||进行安全取值:

console.log(artcle.authorInfo && artcle.authorInfo.author); //Bowen
console.log(artcle.timeInfo && artcle.timeInfo.publishTime); //undefined
console.log(artcle.artcleInfo &&
        artcle.artcleInfo.timeInfo &&
        artcle.artcleInfo.timeInfo.publishTime
); //today

console.log((artcle.authorInfo || {}).author); //Bowen
console.log((artcle.timeInfo || {}).publishTime); //undefined
console.log(((artcle.artcleInfo || {}).timeInfo || {}).publishTime); //today

不难看出,这两种方法都不算优雅,只适用短链式取值,一旦嵌套过深,使用&&需要写一长段代码,而使用||需要嵌套很多括号

利用解构赋值的默认值

我们可以利用es6的解构赋值,给属性一个默认值,避免出现错误。以上文的artcle数据为例,如下:

const { authorInfo: { author } = {} } = artcle;
console.log(author); //Bowen

上面这么做会暴露很多变量出来,我们可以简单地封装一个函数,如下:

const getAuthor = ({ authorInfo: { author } = {} } = {}) => author;
console.log(getAuthor(artcle)); //Bowen

这样做不会将变量暴露出来,同时getAuthor函数也能复用,优雅多了

利用try catch

既然在取值的过程中会出现错误,那我们自然可以利用try catch提前将错误捕获:

let author, publishTime;
try {
    author = artcle.authorInfo.author;
} catch (error) {
    author = null;
}
try {
    publishTime = artcle.timeInfo.publishTime;
} catch (error) {
    publishTime = null;
}
console.log(author); //Bowen
console.log(publishTime); //null

这个方法不好的地方在于:我们无法在一个try catch语句里进行多次取值,因为只要有任一错误,就会进入catch语句中去

我们可以写一个通用函数优化这一流程:

const getValue = (fn, defaultVaule) => {
    try {
        return fn();
    } catch (error) {
        return defaultVaule;
    }
};
const author = getValue(() => artcle.authorInfo.author);
const publishTime = getValue(() => artcle.timeInfo.publishTime);
console.log(author); //Bowen
console.log(publishTime); //undefined
利用proxy

这是我在网上看到的一个十分有意思的写法,利用了es6中的proxy完成的:

const pointer = function(obj, path = []) {
    return new Proxy(function() {}, {
        get: function(target, key) {
            return pointer(obj, path.concat(key));
        },
        apply: function(target, object, args) {
            let value = obj;
            for (let i = 0; i < path.length; i++) {
                if (value == null) {
                    break;
                }
                value = value[path[i]];
            }
            if (value === undefined) {
                value = args[0];
            }
            return value;
        }
    });
};
const proxyArtcle = pointer(artcle);
console.log(proxyArtcle.authorInfo.author()); //Bowen
console.log(proxyArtcle.publishTime()); //undefined

原理比较简单,我们可以看到,pointer方法返回的是一个以空函数为代理对象的Proxy实例,而在每次取值的时候会将key保存下来,以proxyArtcle.authorInfo.author为例,它其实等价于pointer(artcle, ["authorInfo", "author"])。由于是以空函数为代理对象,我们可以将执行它,触发apply。apply中会遍历path数组依次取值,如果发现无法继续取值则break,跳出循环。

如果你还没有学习proxy,可以花几分钟了解一下:proxy

这种方法在我看来已是比较优雅的解决方法,但由于proxy对浏览器和node版本有所限制,且不可能有polyfill,真正应用起来需要考虑太多

optional chaining

这是一个新特性,尚在提案阶段,具体可以看tc39/proposal-optional-chaining,不过已经有babel可以使用了:babel-plugin-proposal-optional-chaining

我们可以像下面一样使用这个特性:

console.log(artcle?.authorInfo?.author); //Bowen
console.log(artcle?.timeInfo?.publishTime) //undefined

这种方法已接近完美,我们可以期待它的真正落实

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101957.html

相关文章

  • antd报错Cannot read property &#039;filter&#039; of u

    摘要:技术栈问题描述在状态组件中书写下的其中涉及点击某处出现弹框的操作,在中有选框,点击取消或清空所有表单信息。点击选框内的各个选项,报错。在点击取消之后,清空的是所有组件的内容,再打开新的弹窗后,组件的状态并没有刷新。 技术栈: react + dva + antd 问题描述: 在状态组件中书写state下的columns,其中涉及点击某处出现弹框modal的操作,在modal中有sel...

    Tychio 评论0 收藏0
  • Vue “Cannot read property &#039;upgrade&#039; of u

    摘要:最近启动项目报了个错误,如下原因是在中使用了而在中已被删除了 最近启动vue-cli3.0项目报了个错误,如下showImg(https://segmentfault.com/img/bVbt7w8?w=2088&h=538);原因是在vue.config.js 中使用了 process.env.target 而在.env中 target已被删除了

    tolerious 评论0 收藏0
  • 解决Mac OS编译安装时出 cannot find openssl&#039;s <evp

    摘要:踩坑最近通过安装扩展时,提示以下错误根据提示,通过以下方式安装后,再次使用安装,仍然是提示同样的错误问题解决通过安装的路走不通,还是得通过简单暴力方式解决,使用源码包通过编译方式进行安装编译安装前先安装下载源代码包后,在终端进入源码目录,执 踩坑 最近通过pecl安装mongodb扩展时,提示以下错误 ...... configure: error: Cannot find OpenS...

    GHOST_349178 评论0 收藏0
  • 【Copy攻城狮日志】踩坑小程序之can&#039;t read property &#039;of

    摘要:根据当时的情境,是在微信开发者工具中删掉该小程序然后重新载入就解决了,大家给出的结论是微信小程序开发者工具的。 Created 2019-4-2 22:17:34 by huqiUpdated 2019-4-2 23:17:34 by huqishowImg(https://segmentfault.com/img/bVbqOLH?w=1526&h=818); ↑开局一张图,故事全靠编...

    hsluoyz 评论0 收藏0

发表评论

0条评论

fantix

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<