JavaScript模拟java.lang.Optional?

时间:2015-09-01 07:17:31

标签: javascript java null undefined optional

我正在寻找一个客户端JavaScript库,它允许我使用某种类型的Option类型编写类似于我在其他语言中可以执行的代码,例如java.lang.Optional

我的目标是避免在客户端代码中进行null / undefined检查,并使API更加明确。

以下是我希望能够编写的API:

var dictionary = {
    key1: 'value1',
    key2: 'value2'
}

function getValue(key){
    var value = dictionary[key];
    if(value !== null && value !== undefined) 
         return Optional.of(value);
    else 
         return Optional.empty();
}

这是客户端代码:

var value = getValue('key3');
if(value.isPresent())
     console.log('got a value: ' + value.get().toUpperCase());
else
     console.log('no value found!');

或者有时候:

var value = getValue('unknown key').orElse('default value');

如果我在get()值上调用Optional.empty(),则应该抛出某种Error。 如果我在Optional.of()null上致电undefined,它也应该投放。

3 个答案:

答案 0 :(得分:3)

鉴于java.lang.Optional source,编写一个可选实现本身不应该那么难。

虽然你必须考虑一些差异,ieJavascript区分undefinednull,你没有像Generics这样的概念,并且某些人认为map的Java实现为{{3 }}。然而,它很容易实现。

但是,我自己完成了一项实施(broken),这符合我的需求,如果您愿意,可以使用它,但它可能无法满足您的所有要求(即{ {1}}不会在undefined或null上抛出异常,但of()会这样做

使用我的代码,您的示例就像

get()

顺便说一下。像这样的代码

function getValue(key){
    return Optional.of(dictionary[key]);
}

var value = getValue('key3');
value.ifPresent(function() {
     console.log('got a value: ' + value.get().toUpperCase());
 });

对于Optional是无效的用例,因为您可以轻松替换它将null / undefined检查。但是,如果您需要if(value.isPresent()) console.log('got a value: ' + value.get().toUpperCase()); else console.log('no value found!'); filtermapflatMap等方法,您将从中受益更多。

答案 1 :(得分:3)

<块引用>

对于那些在 2021 年仍然对此感兴趣的人。

tl;博士:

  • 始终使用 ES Optional chain。它在 JavaScript 方式中更具语义和艺术性(也许使用 Babel)。喜欢:

    obj.val?.prop || defaultValue
    obj.val?.[expr]
    obj.arr?.[index]
    obj.func?.(args)
    
  • 使用 Optional 类时,TypeScript 中的其他包可能支持,如果您只想深入访问对象属性,则 > 有点容易。

了解更多信息:

我尝试使用 TypeScript 创建一个大致 Optional 的类:

import isNil from "../is/isNil";
// isNil = val => val === null || val === undefined

class Optional<T> {
  value = null;
  constructor(value: T) {
    this.value = value;
  }

  static EMPTY = new Optional(null);

  static empty(): Optional<unknown> {
    return Optional.EMPTY;
  }

  static of<U>(value: U): Optional<U> {
    return new Optional(value);
  }

  isPresent(): boolean {
    return !isNil(this.value);
  }

  filter<T>(predicate: (value: T) => Boolean): Optional<T> {
    if (!this.isPresent()) {
      return this;
    }
    return predicate(this.value) ? this : Optional.EMPTY;
  }

  map<T, U>(mapper: (value: T) => U): Optional<U> {
    if (!this.isPresent()) {
      return this;
    }
    return Optional.of(mapper(this.value));
  }

  flatMap<T, U>(mapper: (value: T) => Optional<U>): Optional<U> {
    if (!this.isPresent()) {
      return this;
    }
    const mapped = mapper(this.value);
    if (isNil(mapped)) {
      throw new Error("flatMap will map the value not to null or undefined.");
    }
    return mapped;
  }

  orElse<T>(other: T): T {
    return isNil(this.value) ? other : this.value;
  }
}

在这里你可以看到我想使用Optional访问属性的时候,它会写成如下:

Optional.of(dictionary)
.map((obj)=> {
  const descriptors = Object.getOwnPropertyDescriptors(obj)
  // ⚠️ when you use like this, it may also cause an error.
  return descriptors.key1.value
})
.orElse('defaultValue')

使用 Optional chain 会像这样:

dictionary?.key1 || 'defaultValue'

答案 2 :(得分:0)

对于某些使用情况,一个零到一个元素的数组非常合适。

这很简单,具有用于创建和查询的简洁语法,还为您提供了内置的filter()和map(),这是我使用Optional的主要原因:

    const one = ["foo"];
    const none = [];

    console.log("one.isPresent: " + !!one.length);
    console.log("none.isPresent: " + !!none.length);

    console.log("one.get: " + one[0]);

    // Caveat: this 'orElse' will fail for values that are present but falsy:
    console.log("one.orElse('fallback'): " + (one[0] || 'fallback') );
    console.log("none.orElse('fallback'): " + (none[0] || 'fallback'));

    console.log("Fluent chaining: "+ (
        none
            .filter(s => s.length > 2)
            .map(lodash.upperCase)
        [0] || "too short"
    ));

如果您愿意修改原型,则可以根据需要添加更强大的orElse和语法糖。