JavaScriptにnull結合(Elvis)演算子または安全なナビゲーション演算子はありますか?


210

例を挙げて説明します。

エルビスオペレーター(?:)

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

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

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

安全なナビゲーションオペレーター(?。)

Safe Navigationオペレーターは、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

9
「Elvisオペレーター」はC#に存在しますが、ヌル合体オペレーターと呼ばれます(はるかにエキサイティングではありません):-)
Cameron

別の構文が必要な場合は、cofeescriptを確認できます
Lime

この質問はちょっと混乱しています... 3つの異なる演算子を混同していますか?:(質問で綴られた三項演算子、おそらくタイプミス)、?? (JavaScriptに存在するnull合体)および?。(Elvis)JavaScriptには存在しません。回答では、この区別が明確にされていません。
JoelFan 2015年

2
@JoelFanは??、JavaScriptでの適切なnullコアレッセンス()に関するドキュメントへのリンクを提供できますか?私がこれまでに見つけたすべてのことは、JSには「偽の」合体(を使用||)しかないことを示唆しています。
Charles Wood

1
まあ、JSが文字通り持っていたと言うつもりはなかったのですか?しかし、それはヌル合体を持っていた...しかし、それでも私はちょっと間違っていました。そうは言っても、||を使用する多くのJSコードを見てきました。誤った落とし穴にもかかわらず、ヌル合体として
JoelFan

回答:


139

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'>は実際には開発のみを目的としており、本番環境では使用されません。


14
左が未定義である場合にのみ右のオペランドを選択し、それが定義されていて誤っている場合は選択しないようにするため、ほとんどの場合、論理または必要なものではありません。
user2451227 2014

それは私の間違い<script type='coffee/script>'ですか、それとも本当にですか?
JCCM 2015

2
CoffeeScriptホームページではを使用してい<script type="text/coffeescript">ます。
エリアスザマリア2016

19
これは質問に答えるものですが、ほぼ完全にjavascriptではなくcoffeescriptに関するものであり、OPに関係のないcoffeescriptの利点について説明することの半分以上です。コーヒースクリプトの他の利点と同じくらい素晴らしいので、質問に関連するものだけに要約することをお勧めします。
jinglesthula

4
バナナに行きますか?確かにuser2451227の異議(現在は4票)は無効です。これは、式/左オペランドが定義されていて、誤っている場合、3項の中間オペランド(つまり、Elvis演算子を持つ右オペランド)が等しく選択されないためです。どちらの場合も、次に行く必要がありますx === undefined
マイクげっ歯類

115

次は、少し長いですが、安全なナビゲーションオペレーターと同等だと思います。

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

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

デフォルトにしたい場合は、上記のショートカットと組み合わせるか、次のように指定します。

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

7
加えて、ヌル伝播とヌル合体の両方の素晴らしい例として1つ!
ジェイウィック

1
これが機能するのは、nullになるか未定義になるかがわからないという点です
Dave Cousineau

82

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

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

しかし、私の知る限り、あなたの?.オペレーターに相当するものはありません。


13
+1、||そのように使用できることを忘れていました。式があるときに、これがないだけで合体することに注意してくださいnull、だけでなく、それは未定義だ、false0、または空の文字列。
Cameron

@Cameron、確かに、それは質問で述べられており、質問者の意図のようです。""または0予期しないかもしれませんが、:)
フレデリックハミディ

72

私は時々次のイディオムが便利だと思いました:

a?.b?.c

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

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

これは、nullorのundefinedように例外をスローするのではなく、オブジェクトで不明な属性を取得するとundefinedが返されるという事実を利用しているため、ナビゲートする前にnullとundefinedを空のオブジェクトで置き換えます。


14
まあ、それは読みにくいですが、それはその冗長な&&方法よりも優れています。+1。
悲鳴

1
これは、JavaScriptで実際に安全な唯一の演算子です。上記の論理「OR」演算子は別のものです。
vasilakisfil 2017年

@Filipposは、論理ORと&&メソッドでの異なる動作の例を示すことができますか?違いは考えられません
レッドピー

また、最初に変数に割り当てずに匿名値をナビゲートすることもできます。
Matt Jenkins

1
大好きです!結果を返さない可能性があるarray.find()操作の後でオブジェクトのプロパティを取得する場合に非常に便利です
Shiraz

24

私はlodashが考える_.get()のように、ここでは助けることができる_.get(user, 'name')、など、より複雑なタスク_.get(o, 'a[0].b.c', 'default-value')


5
このメソッドの私の主な問題は、プロパティの名前が文字列であるため、100%の信頼でIDEのリファクタリング機能を使用できないという事実です
RPDeshaies

21

現在、ドラフト仕様があります:

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

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

今のところ、私はlodashget(object, path [,defaultValue])またはdlvを使用するのが好きですdelve(obj, keypath)

アップデート(2019年12月23日現在):

オプションのチェーンがステージ4に移動しました


JavaScriptでLodashメイクプログラミング口に合う
ヤモリ

2
オプションのチェーニングは最近ステージ4に移動したため、ES2020
Nick Parsonsで

1
@NickParsonsありがとう!答えを更新しました。
ジャックタック

18

2019アップデート

JavaScriptには、ElvisオペレーターとSafe Navigationオペレーターの両方に相当するものがあります。


安全なプロパティアクセス

任意連鎖演算子は?.)は、現在であるステージ4のECMAScript 提案今日はBabelで使用できます

// `undefined` if either `a` or `b` are `null`/`undefined`. `a.b.c` otherwise.
const myVariable = a?.b?.c;

論理AND演算子は&&)このシナリオを処理するために、「古い」、より-詳細な方法です。

const myVariable = a && a.b && a.c;

デフォルトの提供

nullish合体演算子は??)現在のステージ3のECMAScript 提案今日はBabelで使用できます。演算子の左側がnull値(null/ undefined)の場合、デフォルト値を設定できます。

const myVariable = a?.b?.c ?? 'Some other value';

// Evaluates to 'Some other value'
const myVariable2 = null ?? 'Some other value';

// Evaluates to ''
const myVariable3 = '' ?? 'Some other value';

論理OR演算子は||)の代替ソリューションであるわずかに異なる挙動を有します。演算子の左側がfalsyの場合、デフォルト値を設定できます。myVariable3以下の結果はmyVariable3上記とは異なることに注意してください。

const myVariable = a?.b?.c || 'Some other value';

// Evaluates to 'Some other value'
const myVariable2 = null || 'Some other value';

// Evaluates to 'Some other value'
const myVariable3 = '' || 'Some other value';

1
この回答にはもっと賛成票が必要です。Nullish Coalescing Operatorがステージ4に登場
Yerke

13

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

ただし、falseとnullを区別できるものは見つからず、0と空の文字列はfalse値であるため、正当に0またはとなる可能性があるvalue || default構文の使用は避けてください。value""


4
左のオペランドがnull以外の偽の値である場合、これにより予期しない動作が発生する可能性があることに注意してください。
Shog9

11

はいあります!🍾

オプションの連鎖はステージ4にあり、これによりuser?.address?.street式を使用できます。

リリースが待ちきれない場合は、インストール@babel/plugin-proposal-optional-chainingして使用できます。これは私のために働く私の設定です、またはちょうどNimmoの記事を読んでください

// package.json

{
  "name": "optional-chaining-test",
  "version": "1.0.0",
  "main": "index.js",
  "devDependencies": {
    "@babel/plugin-proposal-optional-chaining": "7.2.0",
    "@babel/core": "7.2.0",
    "@babel/preset-env": "^7.5.5"
  }
  ...
}
// .babelrc

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

console.log(user?.address?.street);  // it works

4
追加できるかどうかではなく、あるかどうかを尋ねました。私はこれが尋ねられたものではないことを考えると超便利ではないと思います。
DeanMWake

2
ECMAScript標準化プロセスのステージ3に達しました。es2020🚀- babeljs.io
docs

この答えは誤解を招くものだと思います。
Leonardo Raele、

1
この答えは正確ではありません!オプションのチェーニングはまだステージ3であり、ES2020はまだリリースされておらず、ファイナライズもされていません。少なくともあなたはそれが解放されるのを待たずにそれをどのように使用できるかについて述べました。
Maxie Berkmann

@gazdagergo問題ありません:)。
Maxie Berkmann

6

同等の簡単なエルビス演算子は次のとおりです。

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

2019年9月の更新

はい、JSはこれをサポートしています。オプションのチェーニングはv8で間もなく登場します続きを読む


まったく同じではありません。OPはnullの合体についてですが、それでも良い答えです。
Maxie Berkmann

4

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


3
厳密な意味ではtrueですが、他の回答でJavaScriptの論理OR演算子が一種のfalse -coalscing演算子として動作することができるので、多くの状況で同じ簡潔さを実現できます。
Shog9

1
これは、ヌル結合演算子ではありません。null結合は単一の値でのみ機能し、プロパティアクセス/関数呼び出しのチェーンでは機能しません。JavaScriptで論理OR演算子を使用してnull結合をすでに行うことができます。

いいえ、JavaScriptで論理ORを使用して偽合体を行うことができます。
andresp


2

私にはその解決策があります。自分のニーズに合わせて調整します。私のライブラリの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;
        }
    },

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


有望に見えますが、完全なソースを提出できますか?どこでも公開していますか?(例:GitHub)
エランメダン2012年

1
私が使用するコードから小さな抜粋を作成し、1週間程度でGitHubに投稿します。
balazstth

2

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

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のいずれかが未定義の場合、結果は未定義です。


1

私はこの記事(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または未定義に遭遇した式の結果は未定義になります。ワイルドになってObjectプロトタイプを変更できます!

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

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


1

これは長い間私にとって問題でした。Elvisオペレーターか何かを得たら、簡単に移行できるソリューションを考え出す必要がありました。

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

これを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

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

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

これをより使いやすくするパッケージを作成しました。

NPM jsdig Github jsdig

次のような単純なものを処理できます:

const world = {
  locations: {
    europe: 'Munich',
    usa: 'Indianapolis'
  }
};

world.dig('locations', 'usa');
// => 'Indianapolis'

world.dig('locations', 'asia', 'japan');
// => 'null'

またはもう少し複雑:

const germany = () => 'germany';
const world = [0, 1, { location: { europe: germany } }, 3];
world.dig(2, 'location', 'europe') === germany;
world.dig(2, 'location', 'europe')() === 'germany';

-6

個人的に使用

function e(e,expr){try{return eval(expr);}catch(e){return null;}};

そして、例えば安全な取得:

var a = e(obj,'e.x.y.z.searchedField');

2
まず、本当にevalを使うべきではありません。次に、これも機能しません。をe({a:{b:{c:{d:'test'}}}}, 'a.b.c.d')返しますnull
Pylinux

@Pylinuxは基本的に何が機能するかe = evalvar a = eval('obj.a.b.c.d')です。evalでも二番目のパラメータを取りません... developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...を
ドリアン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.