例で説明します。
エルビス演算子(?:)
「エルビス演算子」は、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
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'>
の使用は、実際には開発用であり、実稼働用ではありません。
次は安全なナビゲーション演算子と同等ですが、もう少し長くなります。
var streetName = user && user.address && user.address.street;
streetName
は、user.address.street
またはundefined
の値になります。
デフォルトを他の何かにしたい場合は、上記のショートカットと組み合わせるか、以下を指定できます。
var streetName = (user && user.address && user.address.street) || "Unknown Street";
私は時折、次のイディオムが役立つことを発見しました。
a?.b.?c
次のように書き換えることができます。
((a||{}).b||{}).c
これは、null
またはundefined
の場合のように例外をスローするのではなく、オブジェクトの不明な属性を取得するとundefinedが返されるという事実を利用します。
lodash _.get()
は、_.get(user, 'name')
のようにここで役立ち、_.get(o, 'a[0].b.c', 'default-value')
のようなより複雑なタスクだと思います
未だに。多分すぐに。現在、ドラフト仕様があります。
https://github.com/tc39/proposal-optional-chaining
https://tc39.github.io/proposal-optional-chaining/
ただし、現時点では、 lodash get(object, path [,defaultValue])
または dlv delve(obj, keypath)
を使用します
前者の場合、||
を使用できます。 Javascriptの「論理OR」演算子は、単純に既定のtrue値とfalse値を返すのではなく、trueの場合は左の引数を返し、それ以外の場合は右の引数を評価して返すという規則に従います。真理値のみに関心がある場合は同じになりますが、foo || bar || baz
は真の値を含むfoo、bar、またはbazの左端の1つを返すことも意味します。
ただし、falseとnullを区別できるものは見つかりません。0と空の文字列はfalse値なので、value
が正当に0またはvalue || default
になる可能性のある""
コンストラクトの使用は避けてください。 。
エルビス演算子に相当する簡単なものを次に示します。
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
これは、一般にヌル合体演算子として知られています。 Javascriptにはありません。
私はそのための解決策を持っています、あなた自身のニーズに合わせて調整します、私のライブラリの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;
}
},
魅力のように機能します。少ない痛みをお楽しみください!
次のように言って、ほぼ同じ効果を達成できます。
var displayName = user.name || "Anonymous";
あなたはあなた自身を転がすことができます:
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でオプションのチェーンの提案[1]があり、Babelプラグイン[2]が利用可能です。現在、私が知っているどのブラウザにもありません。
はい、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
これは、ミックスインを使用する安全なナビゲーションオペレーターにとって興味深いソリューションでした。
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();
この記事を読んで( 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);
これは私にとって長い間問題でした。エルビスのオペレーターか何かを得たら、簡単に移行できるソリューションを考え出さなければなりませんでした。
これは私が使用するものです。配列とオブジェクトの両方で機能します
これを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
コードが少し判読できなくなることはわかっていますが、シンプルなワンライナーソリューションであり、うまく機能します。私はそれが誰かを助けることを願っています:)