ES6 / 2015でのヌルセーフプロパティアクセス(および条件付き割り当て)


149

そこにあるnullES6(ES2015 / JavaScript.next /ハーモニー)等に-safeプロパティアクセス(nullの伝播/存在)オペレータ?.のCoffeeScript例えばは?それともES7で計画されていますか?

var aThing = getSomething()
...
aThing = possiblyNull?.thing

これはおおよそ次のようになります。

if (possiblyNull != null) aThing = possiblyNull.thing

理想的には解決策は、(たとえ割り当てるべきではありませんundefined)にaThingあればpossiblyNull、ISnull


3
@naomikヌルチェックのこの種のは、非常に役立ちますあなたのためには例えば、深くネストされたプロパティをチェックしているif文if( obj?.nested?.property?.value )の代わりにif( obj && obj.nested && obj.nested.property && obj.nested.property.value )
ショーン・ウォルシュ

@SeanWalshオブジェクトがそれほど深くネストされている場合、または関数がオブジェクトの深くまで掘り込んでいる場合は、アプリにも他のいくつかの問題がある可能性があります。
ありがとう

1
と比較var appConfig = loadConfig(config, process.env); connect(appConfig.database);してくださいconnect(config)。オブジェクトconnect全体を渡す代わりに、はるかに単純なオブジェクトを渡すconfigことができます。、のようなものを試みる代わりにconf.username、を使用できます。参照:デメテルの法則conf.passwordconfig[process.env]?.database?.usernameconfig[process.env]?.database?.password
ありがとう

また、デフォルトの設定やプロパティのサニタイズなどを行う場合(これはloadConfig上記の例で行うことができます)、プロパティの存在について推測し、アプリの無数の領域でnullチェックをスキップできます。
ありがとう

4
@naomik言語がオブジェクトのネストをサポートしている限り、アプリ自体のアーキテクチャについてあなたや私が何を考えているかに関係なく、言語は依然として有用な機能です。余談ですが、このような複雑なオブジェクトグラフは、複雑なデータモデルをモデル化しているORMで非常に一般的です。
Sean Walsh

回答:


98

更新(2020-01-31):人々はまだこれを見つけているようですが、これが現在の話です:

更新(2017-08-01):公式プラグインを使用したい場合は、新しい変換を使用してBabel 7のアルファビルドを試すことができます。あなたのマイレージは異なる場合があります

https://www.npmjs.com/package/babel-plugin-transform-optional-chaining

オリジナル

これを達成する機能は現在ステージ1にあります:オプションのチェーン。

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

今日それを使用したい場合は、それを実現するBabelプラグインがあります。

https://github.com/davidyaha/ecmascript-optionals-proposal


条件付き割り当てが含まれていないことは正しく理解できますか?とにかくstreet = user.address?.street設定streetしますか?
ᆼᆺᆼ2017年

1
実は残念ながらそうだと思います。割り当てられるStreet と思いますundefinedしかし、少なくともundefinedのプロパティにアクセスしようとしてもスローされません。
basicdays 2017年

2
また、左側の演算子を使用できるようにも見えます。それが未定義のものを評価した場合、右側は評価されません。babelプラグインは少し異なる場合がありますが、まだ自分でテストしていません。
basicdays 2017年

1
の左側の条件付き割り当てについては=、現在の公式仕様ではサポートされていないようです。github.com/tc39/proposal-optional-chaining#not-supported
basicdays

1
2020年5月の時点で、現在のブラウザーとTypescriptがこれを実装しているようです!
Josh Diehl

65

それは?ほど良くありません。演算子ですが、同様の結果を得るには次のようにします。

user && user.address && user.address.postcode

ためnullundefinedの両方であるfalsy値(この文献を参照)、後のプロパティ&&先例がヌルまたは未定義ではない場合、オペレータにのみアクセスされます。

または、次のような関数を作成することもできます。

function _try(func, fallbackValue) {
    try {
        var value = func();
        return (value === null || value === undefined) ? fallbackValue : value;
    } catch (e) {
        return fallbackValue;
    }
}

使用法:

_try(() => user.address.postcode) // return postcode or undefined 

または、フォールバック値:

_try(() => user.address.postcode, "none") // return postcode or a custom string

おそらく私は質問を明確にする必要があります。私が
求めてい

その当然の結果です。安全に逆を見つけるには、使用することができます !(user && user.address && user.address.postcode) :)
rob2d

foo && foo.bar && foo.bar.quux ...大規模なコードベースではこれは醜く、多くの複雑さが増すため、避けた方がよいでしょう。
Skylar Saveland 2017年

1
これは私がインターネットで見つけた最もクリーンなソリューションです。私はこれをtypescriptで使用します:_get<T>(func: () => T, fallbackValue?: T) :T
tomwassing

3
user.address.postcodeが定義されていない場合、ではなく_try(() => user.address.postcode, "")が返さundefined""ます。したがって、コード_try(() => user.address.postcode, "").lengthは例外を発生させます。
Alexander Chen


17

安全な物件アクセスのためのバニラ代替

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

最も簡潔な条件付き割り当てはおそらくこれでしょう

try { b = a.b.c.d.e } catch(e) {}

では、このメカニズムを使用して、質問で課題をどのように記述しますか?possiblyNullが定義されていない場合に割り当てない条件が必要nullですか?
'2016年

もちろん、割り当てを実行するかどうかを確認する必要があります。'='演算子を使用すると、データ、未定義、またはnullであっても、必ず何かが割り当てられます。したがって、上記は単なる安全なプロパティアクセスです。条件付き代入演算子はどの言語でも存在しますか?
yagger 2016年

もちろん、たとえば、CoffeeScriptの場合、それも含まれます||=
ᆼᆺᆼ2016年

+1私の最初の考えでさえでした(((a.b || {}).c || {}).d || {}).e。しかし、より正確には、((((a || {}).b || {}).c || {}).d || {}).e
IsmailSは

6

いいえ、ES6にはnull伝播演算子はありません。既知のパターンのいずれかを使用する必要があります

ただし、分解を使用できる場合があります。

({thing: aThing} = possiblyNull);

ES7にそのような演算子を追加するための多くの議論(例えばthis)がありますが、実際に離陸したものはありません。


それは有望に見えたが、少なくともバベルがそれで何をするかは、ちょうどaThing = possiblyNull.thing
それと

1
@PeterVarga:おっと、そうです、構造化は、プロパティが存在しない場合は機能しますが、オブジェクトが存在しない場合は機能しませんnullこのパターンとよく似ていますが、構文がわかりにくいデフォルト値を指定する必要があります。
Bergi

4

ここのリストを見る、現在、ECmascriptに安全なトラバーサルを追加する提案はありません。したがって、これを行うための優れた方法がないだけでなく、近い将来に追加される予定もありません。


1
// Typescript
static nullsafe<T, R>(instance: T, func: (T) => R): R {
    return func(instance)
}

// Javascript
function nullsafe(instance, func) {
    return func(instance);
};

// use like this
const instance = getSomething();
let thing = nullsafe(instance, t => t.thing0.thing1.thingx);

1

オプションの連鎖?.と無効な結合??

?.インラインを直接使用して、存在を安全にテストできるようになりました。最新のブラウザはすべてそれをサポートしています。

?? 未定義またはnullの場合、デフォルト値を設定するために使用できます。

aThing = possiblyNull ?? aThing
aThing = a?.b?.c ?? possiblyNullFallback ?? aThing

プロパティが存在する場合、?.次のチェックに進むか、有効な値を返します。障害が発生すると、すぐに短絡して戻りundefinedます。

const example = {a: ["first", {b:3}, false]}

example?.a  // ["first", {b:3}, false]
example?.b  // undefined

example?.a?.[0]     // "first"
example?.a?.[1]?.a  // undefined
example?.a?.[1]?.b  // 3

domElement?.parentElement?.children?.[3]?.nextElementSibling

null?.()                // undefined
validFunction?.()       // result
(() => {return 1})?.()  // 1

デフォルトの定義値を確保するには、を使用できます??。最初の真の値が必要な場合は、を使用できます||

example?.c ?? "c"  // "c"
example?.c || "c"  // "c"

example?.a?.[2] ?? 2  // false
example?.a?.[2] || 2  // 2

ケースをチェックしない場合は、左側のプロパティが存在する必要があります。そうでない場合は、例外がスローされます。

example?.First         // undefined
example?.First.Second  // Uncaught TypeError: Cannot read property 'Second' of undefined

?.ブラウザサポート -78%、2020年7月

??ブラウザサポート -78%

Mozillaドキュメント

-

論理的な無効な割り当て、2020 +ソリューション

新事業者は、現在、ブラウザに追加されている??=||=&&=。彼らはあなたが探していることを完全には行いませんが、コードの目的によっては同じ結果になる可能性があります。

注:これらはまだ公開ブラウザのバージョンでは一般的ではありませんが、Babelは適切にトランスパイルするはずです。在庫状況に応じて更新されます。

??=左側が未定義またはnullかどうかをチェックし、すでに定義されている場合は短絡します。そうでない場合は、左側に右側の値が割り当てられます。||=および&&=は似ていますが、||and &&演算子に基づいています。

基本的な例

let a          // undefined
let b = null
let c = false

a ??= true  // true
b ??= true  // true
c ??= true  // false

オブジェクト/配列の例

let x = ["foo"]
let y = { foo: "fizz" }

x[0] ??= "bar"  // "foo"
x[1] ??= "bar"  // "bar"

y.foo ??= "buzz"  // "fizz"
y.bar ??= "buzz"  // "buzz"

x  // Array [ "foo", "bar" ]
y  // Object { foo: "fizz", bar: "buzz" }

ブラウザサポート 2020年7月-.03%

Mozillaドキュメント


1
質問は条件付き割り当てについてもです
ᆼᆺᆼ

?.および??でのいくつかの例と、状況に応じて機能する可能性のある詳細な今後のソリューションを追加しました。現在の最善の解決策は、たぶんそれを行うことでしょうaThing = possiblyNull?.thing ?? aThing
Gibolt

0

安全なディープゲットメソッドは、underscore.jsに自然に適合するように見えますが、問題は文字列プログラミングを回避することです。文字列プログラミングを回避するために@Felipeの回答を変更します(または少なくともエッジケースを呼び出し元に戻します)。

function safeGet(obj, props) {
   return (props.length==1) ? obj[keys[0]] :safeGet(obj[props[0]], props.slice(1))
}

例:

var test = { 
  a: { 
    b: 'b property value',
    c: { }
  } 
}
safeGet(test, ['a', 'b']) 
safeGet(test, "a.b".split('.'))  

Rickを言い換えると、これは追加のステップを含む文字列プログラミングのように聞こえます。
tocqueville '20

1
lodashがディープゲット/セットを実装しました_.get(obj, array_or_dotstring)
プロトタイプ

1
しかし、たとえJavascriptのドットと文字列アクセサ表記は、基本的には文字列のプログラミングで公平にobj.a.b.cobj['a']['b']['c']
プロトタイプ

1
どこkeysから来たの?
Levi Roberts、

-2

これはJavaScriptの質問であることは承知していますが、Rubyは要求されたすべての方法でこれを処理していると思います。そのため、これは関連する参照ポイントだと思います。

.&try、および&&彼らの強みと潜在的な落とし穴があります。ここにそれらのオプションの素晴らしい要約http : //mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

TLDR; Rubyists結論はつまりdig目に簡単に、という強い保証の両方でかがnull割り当てられます。

TypeScriptの簡単な実装を次に示します。

export function dig(target: any, ...keys: Array<string>): any {
  let digged = target
  for (const key of keys) {
    if (typeof digged === 'undefined') {
      return undefined // can also return null or a default value
    }
    if (typeof key === 'function') {
      digged = key(digged)
    } else {
      digged = digged[key]
    }
  }
  return digged
}

これは、入れ子の深さに使用でき、関数を処理します。

a = dig(b, 'c', 'd', 'e');
foo = () => ({});
bar = dig(a, foo, 'b', 'c')

このtryアプローチは、以前の回答で示したように、JSで読むのも同様に優れています。また、この実装の欠点の1つであるループも必要ありません。


私はこの答えを楽しい/完全な/代替の解決策としてここに載せましたb / cルビーが構文糖でこれを行う方法が好きです。コミュニティのためにこれを公開するだけです。もう一度投票してください。この投稿は喜んで削除します。乾杯!
theUtherSide

ECMAScript2019がRubyのような「オプションのチェーン」について素晴らしい提案をしているのを見るのは本当にクールです:github.com/tc39/proposal-optional-chaining 今日はBabelプラグイン経由で利用でき、オブジェクトのメソッドの呼び出しにも機能します:babeljs.io / docs / en / babel-plugin-proposal-optional-chaining
theUtherSide

-5

この質問は2018年に少しリフレッシュする必要があると思いました。これは、ライブラリを使用せずにうまく行うObject.defineProperty()ことができ、次のように使用できます。

myVariable.safeGet('propA.propB.propC');

私は、この安全を考慮して(とJS-倫理的な)理由のwriteableenumerableできるようになりました定義definePropertyの方法はObject、とMDNに文書化

以下の関数定義:

Object.defineProperty(Object.prototype, 'safeGet', { 
    enumerable: false,
    writable: false,
    value: function(p) {
        return p.split('.').reduce((acc, k) => {
            if (acc && k in acc) return acc[k];
            return undefined;
        }, this);
    }
});

これを実証するために、コンソール出力付きのjsBinをまとめました。jsBinバージョンでは、空の値に対するカスタム例外も追加したことに注意してください。これはオプションなので、上記の最小限の定義から除外しました。

改善は歓迎されています


2
これにより、事実上、コードを文字列に書き込みます。それをするのは本当に悪い考えです。それはあなたのコードをリファクタリングすることを不可能にし、IDEフレンドリーではありません。
tocqueville '20
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.