在JavaScript中是否存在null-coalescing(Elvis)运算符或安全导航运算符?
我会通过例子来解释:
猫王操作员(?:)
“Elvis操作符”是Java三元操作符的缩写。 如果expression式parsing为false或null,则返回“合理的默认值”。 一个简单的例子可能是这样的:
def gender = user.male ? "male" : "female" //traditional ternary operator usage def displayName = user.name ?: "Anonymous" //more compact Elvis operator
安全导航操作员(?)
安全导航运算符用于避免NullPointerException。 通常当你有一个对象的引用时,你可能需要在访问对象的方法或属性之前validation它是否为空。 为了避免这种情况,安全导航操作符将简单地返回null而不是抛出exception,如下所示:
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
您可以使用逻辑“OR”运算符代替Elvis运算符:
例如displayname = user.name || "Anonymous"
displayname = user.name || "Anonymous"
但是,Javascript目前还没有其他的function。 我build议看看CoffeeScript,如果你想要一个替代的语法。 它有一些速记,类似于你正在寻找的。
例如存在的操作员
zip = lottery.drawWinner?().address?.zipcode
function快捷键
()-> // equivalent to function(){}
性感的function调用
func 'arg1','arg2' // equivalent to func('arg1','arg2')
还有多行注释和类。 很明显,你必须把这个编译成javascript或者以<script type='text/coffeescript>'
插入页面,但是它增加了很多function:)。 使用<script type='text/coffeescript'>
实际上仅用于开发而不是生产。
Javascript的逻辑OR运算符是短路的 ,可以代替你的“Elvis”运算符:
var displayName = user.name || "Anonymous";
但是,据我所知,这和你没有什么相同之处?.
运营商。
我认为以下相当于安全的导航操作员,虽然有点长:
var streetName = user && user.address && user.address.street;
streetName
将会是街道名称或null / undefined。
如果你想默认其他东西,你可以结合上面的快捷方式或给:
var streetName = (user && user.address && user.address.street) || "Unknown Street";
我偶尔会发现以下成语有用:
a?.b.?c
可以改写为:
((a||{}).b||{}).c
这利用了这样一个事实,即在对象上获取未知的属性返回未定义的,而不是抛出一个exception,因为它在null
或undefined
,因此我们在导航之前用一个空对象replacenull和undefined。
我认为lodash _.get()
可以在_.get(user, 'name')
和更复杂的任务(如_.get(o, 'a[0].b.c', 'default-value')
对于前者,你可以使用||
。 Javascript“逻辑”或“运算符”,而不是简单地返回jar头的真值和假值,遵循返回它的左参数的规则,如果它是真的,否则评估和返回它的正确的参数。 当你只对真值感兴趣时,结果是一样的,但也意味着foo || bar || baz
foo || bar || baz
foo || bar || baz
返回包含真实值的foo,bar或baz中最左边的一个 。
你不会find一个可以区分false的值,但是,0和空string都是false值,所以避免使用value || default
value || default
构造的value
可以合法地为0或""
。
这通常被称为空合并运算符。 Javascript没有一个。
我有一个解决scheme,根据自己的需要量身定制,从我的一个库中摘录:
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. Eg // 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; } },
奇迹般有效。 享受更less的痛苦!
这里有一个简单的elvis运算符等价物:
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
你可以通过说:
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) { return undefined; } } return returnObject; };
而使用它就像这样:
var result = resolve(obj, 'abcd');
*结果未定义a,b,c或d中的任何一个未定义
这是一个有趣的解决scheme,使用一些混合安全导航操作..
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();
我个人使用
function e(e,expr){try{return eval(expr);}catch(e){return null;}};
例如安全地得到:
var a = e(obj,'exyzsearchedField');