web-dev-qa-db-ja.com

javascriptにヌル結合(エルビス)演算子または安全なナビゲーション演算子はありますか?

例で説明します。

エルビス演算子(?:)

「エルビス演算子」は、Javaの三項演算子を短縮したものです。これが便利な例の1つは、式がfalseまたはnullに解決される場合に「適切なデフォルト」値を返すことです。簡単な例は次のようになります。

def gender = user.male ? "male" : "female"  //traditional ternary operator usage

def displayName = user.name ?: "Anonymous"  //more compact Elvis operator

安全なナビゲーション演算子(?。)

安全なナビゲーション演算子は、NullPointerExceptionを回避するために使用されます。通常、オブジェクトへの参照がある場合、オブジェクトのメソッドまたはプロパティにアクセスする前に、それがnullでないことを確認する必要があります。これを避けるために、安全なナビゲーション演算子は、次のように例外をスローするのではなく、単にnullを返します。

def user = User.find( "admin" )           //this might be null if 'admin' does not exist
def streetName = user?.address?.street    //streetName will be null if user or user.address is null - no NPE thrown
174
tiagomac

Elvis演算子の代わりに論理「OR」演算子を使用できます。

たとえばdisplayname = user.name || "Anonymous"

ただし、現在JavaScriptには他の機能はありません。代替構文が必要な場合は、 CoffeeScript を参照することをお勧めします。あなたが探しているものに似ているいくつかの速記があります。

たとえば、実在の演算子

Zip = lottery.drawWinner?().address?.zipcode

関数のショートカット

()->  // equivalent to function(){}

セクシーな関数呼び出し

func 'arg1','arg2' // equivalent to func('arg1','arg2')

複数行のコメントとクラスもあります。明らかにこれをjavascriptにコンパイルするか、<script type='text/coffeescript>'としてページに挿入する必要がありますが、多くの機能が追加されます:)。 <script type='text/coffeescript'>の使用は、実際には開発用であり、実稼働用ではありません。

124
Lime

次は安全なナビゲーション演算子と同等ですが、もう少し長くなります。

var streetName = user && user.address && user.address.street;

streetNameは、user.address.streetまたはundefinedの値になります。

デフォルトを他の何かにしたい場合は、上記のショートカットと組み合わせるか、以下を指定できます。

var streetName = (user && user.address && user.address.street) || "Unknown Street";
96
samjudson

Javascriptの 論理OR演算子短絡 であり、「Elvis」演算子を置き換えることができます。

var displayName = user.name || "Anonymous";

ただし、私の知る限り、?.演算子に相当するものはありません。

79

私は時折、次のイディオムが役立つことを発見しました。

a?.b.?c

次のように書き換えることができます。

((a||{}).b||{}).c

これは、nullまたはundefinedの場合のように例外をスローするのではなく、オブジェクトの不明な属性を取得するとundefinedが返されるという事実を利用します。

54
James_pic

lodash _.get() は、_.get(user, 'name')のようにここで役立ち、_.get(o, 'a[0].b.c', 'default-value')のようなより複雑なタスクだと思います

22
tony_k

未だに。多分すぐに。現在、ドラフト仕様があります。

https://github.com/tc39/proposal-optional-chaining

https://tc39.github.io/proposal-optional-chaining/

ただし、現時点では、 lodash get(object, path [,defaultValue]) または dlv delve(obj, keypath) を使用します

14
Jack Tuck

前者の場合、||を使用できます。 Javascriptの「論理OR」演算子は、単純に既定のtrue値とfalse値を返すのではなく、trueの場合は左の引数を返し、それ以外の場合は右の引数を評価して返すという規則に従います。真理値のみに関心がある場合は同じになりますが、foo || bar || baz真の値を含むfoo、bar、またはbazの左端の1つを返すことも意味します。

ただし、falseとnullを区別できるものは見つかりません。0と空の文字列はfalse値なので、valueが正当に0またはvalue || defaultになる可能性のある""コンストラクトの使用は避けてください。 。

11
hobbs

エルビス演算子に相当する簡単なものを次に示します。

function elvis(object, path) {
    return path ? path.split('.').reduce(function (nestedObject, key) {
        return nestedObject && nestedObject[key];
    }, object) : object;
}

> var o = { a: { b: 2 }, c: 3 };
> elvis(o)

{ a: { b: 2 }, c: 3 }

> elvis(o, 'a');

{ b: 2 }

> elvis(o, 'a.b');

2

> elvis(o, 'x');

undefined
5
cdmckay

これは、一般にヌル合体演算子として知られています。 Javascriptにはありません。

5
doctorless

私はそのための解決策を持っています、あなた自身のニーズに合わせて調整します、私のライブラリの1つからの抜粋:

    elvisStructureSeparator: '.',

    // An Elvis operator replacement. See:
    // http://coffeescript.org/ --> The Existential Operator
    // http://fantom.org/doc/docLang/Expressions.html#safeInvoke
    //
    // The fn parameter has a SPECIAL SYNTAX. E.g.
    // some.structure['with a selector like this'].value transforms to
    // 'some.structure.with a selector like this.value' as an fn parameter.
    //
    // Configurable with tulebox.elvisStructureSeparator.
    //
    // Usage examples: 
    // tulebox.elvis(scope, 'arbitrary.path.to.a.function', fnParamA, fnParamB, fnParamC);
    // tulebox.elvis(this, 'currentNode.favicon.filename');
    elvis: function (scope, fn) {
        tulebox.dbg('tulebox.elvis(' + scope + ', ' + fn + ', args...)');

        var implicitMsg = '....implicit value: undefined ';

        if (arguments.length < 2) {
            tulebox.dbg(implicitMsg + '(1)');
            return undefined;
        }

        // prepare args
        var args = [].slice.call(arguments, 2);
        if (scope === null || fn === null || scope === undefined || fn === undefined 
            || typeof fn !== 'string') {
            tulebox.dbg(implicitMsg + '(2)');
            return undefined;   
        }

        // check levels
        var levels = fn.split(tulebox.elvisStructureSeparator);
        if (levels.length < 1) {
            tulebox.dbg(implicitMsg + '(3)');
            return undefined;
        }

        var lastLevel = scope;

        for (var i = 0; i < levels.length; i++) {
            if (lastLevel[levels[i]] === undefined) {
                tulebox.dbg(implicitMsg + '(4)');
                return undefined;
            }
            lastLevel = lastLevel[levels[i]];
        }

        // real return value
        if (typeof lastLevel === 'function') {
            var ret = lastLevel.apply(scope, args);
            tulebox.dbg('....function value: ' + ret);
            return ret;
        } else {
            tulebox.dbg('....direct value: ' + lastLevel);
            return lastLevel;
        }
    },

魅力のように機能します。少ない痛みをお楽しみください!

3
balazstth

次のように言って、ほぼ同じ効果を達成できます。

var displayName = user.name || "Anonymous";
3
Justin Ethier

あなたはあなた自身を転がすことができます:

function resolve(objectToGetValueFrom, stringOfDotSeparatedParameters) {
    var returnObject = objectToGetValueFrom,
        parameters = stringOfDotSeparatedParameters.split('.'),
        i,
        parameter;

    for (i = 0; i < parameters.length; i++) {
        parameter = parameters[i];

        returnObject = returnObject[parameter];

        if (returnObject === undefined) {
            break;
        }
    }
    return returnObject;
};

そして、次のように使用します:

var result = resolve(obj, 'a.b.c.d'); 

* a、b、c、またはdのいずれかが未定義の場合、結果は未定義です。

2
Pylinux

非常に遅い段階で、現在はステージ2でオプションのチェーンの提案[1]があり、Babelプラグイン[2]が利用可能です。現在、私が知っているどのブラウザにもありません。

  1. https://github.com/tc39/proposal-optional-chaining
  2. https://www.npmjs.com/package/@babel/plugin-proposal-optional-chaining
1
Tracker1

はい、foo?.barは最終的にJSでネイティブに動作します! ????これには@babel/plugin-proposal-optional-chainingをインストールする必要があります。

これを有効にする設定は次のとおりです。

// package.json

{
  "name": "optional-chaining-test",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "parcel index.html --open",
    "build": "parcel build index.html"
  },
  "devDependencies": {
    "@babel/plugin-proposal-optional-chaining": "7.2.0",
    "@babel/core": "7.2.0",
    "@babel/preset-env": "^7.5.5",
    "parcel-bundler": "^1.6.1"
  }
}
// .babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "debug": true
      }
    ]
  ],
  "plugins": [
    "@babel/plugin-proposal-optional-chaining"
  ]
}
// index.js

const foo = { bar: "hello" }
console.log(foo?.bar);  // it works
0
gazdagergo

これは、ミックスインを使用する安全なナビゲーションオペレーターにとって興味深いソリューションでした。

http://jsfiddle.net/avernet/npcmv/

  // Assume you have the following data structure
  var companies = {
      orbeon: {
          cfo: "Erik",
          cto: "Alex"
      }
  };

  // Extend Underscore.js
  _.mixin({ 
      // Safe navigation
      attr: function(obj, name) { return obj == null ? obj : obj[name]; },
      // So we can chain console.log
      log: function(obj) { console.log(obj); }
  });

  // Shortcut, 'cause I'm lazy
  var C = _(companies).chain();

  // Simple case: returns Erik
  C.attr("orbeon").attr("cfo").log();
  // Simple case too, no CEO in Orbeon, returns undefined
  C.attr("orbeon").attr("ceo").log();
  // IBM unknown, but doesn't lead to an error, returns undefined
  C.attr("ibm").attr("ceo").log();
0
Dean Hiller

この記事を読んで( https://www.beyondjava.net/elvis-operator-aka-safe-navigation-javascript-TypeScript )、プロキシを使用してソリューションを変更しました。

function safe(obj) {
    return new Proxy(obj, {
        get: function(target, name) {
            const result = target[name];
            if (!!result) {
                return (result instanceof Object)? safe(result) : result;
            }
            return safe.nullObj;
        },
    });
}

safe.nullObj = safe({});
safe.safeGet= function(obj, expression) {
    let safeObj = safe(obj);
    let safeResult = expression(safeObj);

    if (safeResult === safe.nullObj) {
        return undefined;
    }
    return safeResult;
}

次のように呼び出します。

safe.safeGet(example, (x) => x.foo.woo)

Nullに遭遇する式またはそのパスに沿って未定義の式の結果は未定義になります。 wildに進み、Objectプロトタイプを変更できます!

Object.prototype.getSafe = function (expression) {
    return safe.safeGet(this, expression);
};

example.getSafe((x) => x.foo.woo);
0
Sam

これは私にとって長い間問題でした。エルビスのオペレーターか何かを得たら、簡単に移行できるソリューションを考え出さなければなりませんでした。

これは私が使用するものです。配列とオブジェクトの両方で機能します

これをtools.jsファイルまたは何かに入れます

// this will create the object/array if null
Object.prototype.__ = function (prop) {
    if (this[prop] === undefined)
        this[prop] = typeof prop == 'number' ? [] : {}
    return this[prop]
};

// this will just check if object/array is null
Object.prototype._ = function (prop) {
    return this[prop] === undefined ? {} : this[prop]
};

使用例:

let student = {
    classes: [
        'math',
        'whatev'
    ],
    scores: {
        math: 9,
        whatev: 20
    },
    loans: [
        200,
        { 'hey': 'sup' },
        500,
        300,
        8000,
        3000000
    ]
}

// use one underscore to test

console.log(student._('classes')._(0)) // math
console.log(student._('classes')._(3)) // {}
console.log(student._('sports')._(3)._('injuries')) // {}
console.log(student._('scores')._('whatev')) // 20
console.log(student._('blabla')._('whatev')) // {}
console.log(student._('loans')._(2)) // 500 
console.log(student._('loans')._(1)._('hey')) // sup
console.log(student._('loans')._(6)._('hey')) // {} 

// use two underscores to create if null

student.__('loans').__(6)['test'] = 'whatev'

console.log(student.__('loans').__(6).__('test')) // whatev

コードが少し判読できなくなることはわかっていますが、シンプルなワンライナーソリューションであり、うまく機能します。私はそれが誰かを助けることを願っています:)

0
Neut