我将通过例子解释:
猫王运营商(?:)
“猫王操作员”是一种缩短 Java的三元运算符。一 这个方便的例子是 返回'合理的默认值' 如果表达式解析为false或 空值。一个简单的例子可能看起来像 这样:
def gender = user.male ? "male" : "female" //traditional ternary operator usage
def displayName = user.name ?: "Anonymous" //more compact Elvis operator
安全导航操作员(?。)
使用安全导航操作符 避免NullPointerException。 通常在您有参考时 您可能需要验证的对象 在访问之前它不是null 对象的方法或属性。 为避免这种情况,安全导航 operator只会返回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
答案 0 :(得分:122)
您可以使用逻辑“OR”运算符代替Elvis运算符:
例如displayname = user.name || "Anonymous"
。
但Javascript目前没有其他功能。如果您想要其他语法,我建议您查看CoffeeScript。它有一些类似于你正在寻找的速记。
例如The Existential Operator
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'>
实际上仅用于开发而非生产。
答案 1 :(得分:87)
我认为以下内容相当于安全导航操作符,但有点长:
var streetName = user && user.address && user.address.street;
streetName
将是user.address.street
或undefined
的值。
如果您希望将其默认为其他内容,则可以与上述快捷方式结合使用或提供:
var streetName = (user && user.address && user.address.street) || "Unknown Street";
答案 2 :(得分:79)
Javascript的logical OR operator为short-circuiting,可以取代您的“Elvis”运营商:
var displayName = user.name || "Anonymous";
但是,据我所知,与您的?.
运营商无关。
答案 3 :(得分:54)
我偶尔会发现以下习惯用法:
a?.b.?c
可以改写为:
((a||{}).b||{}).c
这利用了以下事实:在对象上获取未知属性返回undefined,而不是像null
或undefined
那样抛出异常,因此我们用空对象替换null和undefined在导航之前。
答案 4 :(得分:22)
我认为lodash _.get()
可以提供帮助,例如_.get(user, 'name')
,以及更复杂的任务,例如_.get(o, 'a[0].b.c', 'default-value')
答案 5 :(得分:11)
对于前者,您可以使用||
。 Javascript“逻辑或”运算符,而不是简单地返回固定的true和false值,如果它为真,则遵循返回其左参数的规则,否则评估并返回其右参数。如果您只对真值感兴趣,那么它的效果会相同,但这也意味着foo || bar || baz
会返回包含真值的foo,bar或baz中最左边的一个。
但是,您找不到可以区分false和null的字符,而0和空字符串都是false值,因此请避免使用value || default
构造,其中value
可以合法地为0或{{ 1}}。
答案 6 :(得分:11)
还没有。也许很快目前有一个规范草案:
https://github.com/tc39/proposal-optional-chaining
https://tc39.github.io/proposal-optional-chaining/
但是现在,我想使用lodash get(object, path [,defaultValue])
或dlv delve(obj, keypath)
答案 7 :(得分:5)
这通常称为空合并运算符。 Javascript没有。
答案 8 :(得分:4)
UPDATE SEP 2019
是的,JS现在支持此功能。 v8 read more
即将推出可选链接答案 9 :(得分:3)
我有一个解决方案,根据你自己的需要定制它,摘自我的一个libs:
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;
}
},
就像一个魅力。享受更少的痛苦!
答案 10 :(得分:3)
您可以通过以下方式获得大致相同的效果:
var displayName = user.name || "Anonymous";
答案 11 :(得分: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中的任何一个未定义,则*结果未定义。
答案 12 :(得分:1)
跳转到很晚,目前在第2阶段有一个可选链的建议[1],其中提供了babel插件[2]。我目前所知道的浏览器中都没有它。
答案 13 :(得分:1)
这是我的“猫王”功能。将根对象和链作为字符串传递。它总是返回链的第一个“未定义”元素。同时适用于对象,数组,方法和基元。
elvis(myObject, 'categories.shirts[0].getPrice().currency');
工作示例:
const elvis = (obj, keychain) => {
const handleArray = (parent, key) => {
if (key.indexOf('[') > -1) {
const arrayName = key.split('[')[0];
const arrayIndex = +key.split('[')[1].slice(0, -1);
return parent[arrayName] && parent[arrayName][arrayIndex];
}
if (key.indexOf('(') > -1) {
const methodName = key.split('(')[0];
return parent[methodName] && parent[methodName]();
}
return parent[key];
}
const keys = keychain.split('.');
let base = obj;
for (let i = 0; i < keys.length; i += 1) {
base = handleArray(base, keys[i]);
if (typeof base === 'undefined') return base;
}
return base;
}
//--------
const myObject = {
categories: {
getFoo: () => 'foo',
shirts: [
{ color: 'red' },
{ color: 'blue' }
]
}
}
console.log(elvis(myObject, 'categories.shirts[0].color'));
console.log(elvis(myObject, 'categories.getFoo()'));
console.log(elvis(myObject, 'categories.getBar()'));
console.log(elvis(myObject, 'categories.shirts[0].length'));
console.log(elvis(myObject, 'categories.pans[2].color'));
答案 14 :(得分: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
好吧,我知道这会使代码有些不可读,但这是一个简单的线性解决方案,效果很好。我希望它可以帮助某人:)
答案 15 :(得分:0)
我创建了一个程序包,使它更易于使用。
您可以处理诸如和对象之类的简单事情:
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';
答案 16 :(得分:0)
JavaScript现在具有猫王操作员和安全导航操作员的等效功能。
安全财产访问
optional chaining operator(?.
)当前是stage 4 ECMAScript proposal。您可以use it today with Babel。
// `undefined` if either `a` or `b` are `null`/`undefined`. `a.b.c` otherwise.
const myVariable = a?.b?.c;
logical AND operator(&&
)是处理这种情况的“旧”,更详细的方法。
const myVariable = a && a.b && a.c;
提供默认值
nullish coalescing operator(??
)当前是stage 3 ECMAScript proposal。您可以use it today with Babel。如果运算符的左侧为零值(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';
logical OR operator(||
)是另一种解决方案,行为略有不同。如果运算符的左侧为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';
答案 17 :(得分:0)
我阅读了这篇文章(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或undefined的表达式,结果将是不确定的。您可以野生并修改对象原型!
Object.prototype.getSafe = function (expression) {
return safe.safeGet(this, expression);
};
example.getSafe((x) => x.foo.woo);
答案 18 :(得分:0)
对于使用某些mixin的安全导航操作员来说,这是一个有趣的解决方案。
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();
答案 19 :(得分:-5)
我个人使用
function e(e,expr){try{return eval(expr);}catch(e){return null;}};
,例如safe get:
var a = e(obj,'e.x.y.z.searchedField');