JavaScript如何优雅地告别❌Cannot read properties of undefined,Optional类体验分享


theme: channing-cyan highlight: atom-one-dark

文章同步在公众号:萌萌哒草头将军,欢迎关注!

相信很多开发者在开发中经常遇到的一个报错:

Uncaught TypeError: Cannot read properties of undefined

作为一名熟练的前端攻城狮,你一眼就能这是由于读取一个对象属性时,对象为undefined时导致的。

要想避免,我们需要做下判断。

let a = fetch(url, params)
a?.result?.map(console.log)

或者你使用try catch结构来捕获异常。

try {
    let a = fetch(url, params)
    a.result.map(console.log)
} catch (err) {
    console.log(err)
}

当然,我们还有很多其他的选择,这里就不一一列举了

接下来我介绍一种我最新学到的方案——Java的Optional类。

Optional类是Java中解决空指针异常(NullPointerException)的一种方案。(它和Cannot read properties of undefined是一样的错误),我们看看没有Optional类的情况

String str = null;
// System.out.println(appendTest.toString());
// 上面会触发:NullPointerException,下面是常见的应对方法
if (str !== null) {
    System.out.println(str.toString());
} else {
    System.out.println("str is null!!!");
}

但是使用Optional就优雅的多了

String str = null;
// 生成Optional类,ofNullablebiao 生成可以为null的对象
Optional optional = Optional.ofNullable(name);

生成Optional类还有of方法,不同的是不能为null,否则调用ifPresent依然会报错NullPointerException

ifPresent: 如果存在就打印它,否则不进行任何操作

optional.ifPresent(str -> System.out.println(str));
// 等价于
optional.ifPresent(System.out::println);

orElseGet:如果存在就返回它,否则执行后面的表达式

optional.orElseGet(() -> System.out.println("str is null!!"));

除此之外还有很多有意思的接口,比如我们熟悉的:map、flatmap、filter等,

需要注意的是,因为Optional是一个容器类,它的方法返回的仍然是Optional对象,所以你可以继续对返回的Optional对象进行链式调用。例如:

import java.util.Optional;

public class Person {
// something
}

public class Main {
public static void main(String args) {
// 创建一个可能为 null 的 Person 对象
Person person = new Person(“John Doe”, 30);

    // 使用 Optional 对象进行链式调用
    Optional<person> optionalPerson = Optional.ofNullable(person)
            .filter(p -&gt; p.getAge() &gt;= 18)
            .map(p -&gt; new Person(p.getName().toUpperCase(), p.getAge())
            .flatMap(p -&gt; Optional.of(new Person(p.getName(), p.getAge() + 1)));  

    // 输出结果
    optionalPerson.ifPresent(p -&gt; System.out.println("Name: " + p.getName() + ", Age: " + p.getAge()));
}

}

更多信息可以看下面这张截图

这些方法像极了javascript的数组方法,但是他们的区别是,在Java中,Optional类主要用于包装单个对象,以表示一个可能存在或可能不存在的值。它并不直接支持包装数组。

让我们回到javascript,这种方案似乎可以很好的解决前端的问题。好奇的搜索GitHub,这种方案有很多javascript版本的实现。

例如:https://github.com/JasonStorey/Optional.js

const Optional = require('optional-js');

// Define some simple operations
const getUserId =
username => username === ‘root’ ? 1234 : 0;

const verify =
userId => userId === 1234;

const login =
userId => console.log('Logging in as : ’ + userId);

// Declare a potentially undefined value
const username = process.argv[2];

// Wrap username in an Optional, and build a pipeline using our operations
Optional.ofNullable(username)
.map(getUserId)
.filter(verify)
.ifPresent(login);

这看起来妙极了!!!

那么这种方案是怎么实现的呢?关键是函数返回值依然是个Optional对象。下面我们简单的实现一下这个过程.

class Optional {
  constructor(value) {
    this.value = value;
  }

static of(value) {
return new Optional(value);
}

isPresent() {
return this.value !== undefined && this.value !== null;
}

map(mapper) {
if (this.isPresent()) {
const mappedValue = mapper(this.value);
return Optional.of(mappedValue);
}
return Optional.ofNullable(null);
}

flatMap(mapper) {
if (this.isPresent()) {
const mappedValue = mapper(this.value);
if (mappedValue instanceof Optional) {
return mappedValue;
}
return Optional.ofNullable(mappedValue);
}
return Optional.ofNullable(null);
}

orElse(defaultValue) {
return this.isPresent() ? this.value : defaultValue;
}

static ofNullable(value) {
return Optional.of(value);
}
}

function getNameLength(person) {
return Optional.ofNullable(person)
.map(p => p.name)
.map(name => name.length)
.orElse(0);
}

const person = { name: ‘John Doe’ };
console.log(getNameLength(person)); // 输出:8

const emptyPerson = null;
console.log(getNameLength(emptyPerson)); // 输出:0

你觉得这个方案可以吗?相信大家读到这里心中已经有答案了吧


这是一个从 https://juejin.cn/post/7367164214081339442 下的原始话题分离的讨论话题