javascript 中是不是有空合并 (Elvis) 运算符或安全导航运算符?
Posted
技术标签:
【中文标题】javascript 中是不是有空合并 (Elvis) 运算符或安全导航运算符?【英文标题】:Is there a null-coalescing (Elvis) operator or safe navigation operator in javascript?javascript 中是否有空合并 (Elvis) 运算符或安全导航运算符? 【发布时间】:2011-09-30 15:12:43 【问题描述】:我会举例说明:
猫王运算符 (?: )
“猫王接线员”是缩写 Java的三元运算符。一 这很方便的例子是 返回一个“合理的默认”值 如果表达式解析为 false 或 空值。一个简单的例子可能看起来像 这个:
def gender = user.male ? "male" : "female" //traditional ternary operator usage
def displayName = user.name ?: "Anonymous" //more compact Elvis operator
安全导航运算符 (?.)
使用了安全导航运算符 避免 NullPointerException。 通常当您参考 您可能需要验证的对象 在访问之前它不为空 对象的方法或属性。 为了避免这种情况,安全导航 运算符将简单地返回 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
【问题讨论】:
C# 中存在“Elvis 运算符”——但它被称为 null 合并运算符(不那么令人兴奋):-) 如果你想要另一种语法,你可以看看 cofeescript 这个问题有点混乱......它混合了 3 个不同的运算符? : (三元运算符,在问题中拼写出来,可能是错字),?? (null 合并,在 javascript 中确实存在)和 ?. (猫王)在 JavaScript 中不存在。答案并没有很好地阐明这种区别。 @JoelFan 您能否提供指向有关 javascript 中正确空合并 (??
) 的文档的链接?到目前为止,我发现的所有内容都表明 JS 只有“虚假”合并(使用 ||
)。
好吧,我不是说JS字面上有??但它有零合并......但即使在那里我也有点错。话虽如此,我已经看到很多使用 || 的 JS 代码。尽管存在错误的陷阱,但作为一个无效的合并
【参考方案1】:
??
可以在 js 中使用,相当于 kotlin 中的?:
【讨论】:
【参考方案2】:2020 年更新
JavaScript 现在具有 Elvis Operator 和 Safe Navigation Operator 的等价物。
安全的财产访问
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.b.c;
提供默认值
nullish coalescing operator (??
) 目前是 stage 4 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';
【讨论】:
这个答案需要更多的支持。 Nullish Coalescing Operator 现在处于第 4 阶段。a && a.b && a.c
应该是a && a.b && a.b.c
。我自己无法编辑它,因为它的变化不足以让 SO 接受,而且我不想做“改变无关紧要的事情以使其变成 6 个字符”的事情。
您可以使用 [] 语法添加执行此操作的方法 - 从 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 开始,所有这些都是可能的:obj.val?.prop obj.val?.[expr] obj.arr ?.[index] obj.func?.(args)【参考方案3】:
目前有一个草案规范:
https://github.com/tc39/proposal-optional-chaining
https://tc39.github.io/proposal-optional-chaining/
不过,我现在喜欢使用lodash get(object, path [,defaultValue])
或dlv delve(obj, keypath)
更新(截至 2019 年 12 月 23 日):
可选链接已移至第 4 阶段
【讨论】:
Lodash 让 javascript 编程更受欢迎 可选链最近移至stage 4,所以我们将在 ES2020 中看到它【参考方案4】:我创建了一个包,使它更易于使用。
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';
【讨论】:
【参考方案5】:是的,有! ?
Optional chaining 处于第 4 阶段,这使您可以使用 user?.address?.street
公式。
如果等不及发布,安装@babel/plugin-proposal-optional-chaining
即可使用。
以下是适合我的设置,或者直接阅读Nimmo's article。
// 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
【讨论】:
他问有没有,而不是你能不能加一个。考虑到它不是被要求的,我认为这不是超级有用。 它达到了 ECMAScript 标准化过程的第 3 阶段。 es2020 ? -- babeljs.io/docs/en/babel-plugin-proposal-optional-chaining 我认为这个答案具有误导性。 这个答案不太正确! Optional chaining 仍处于第 3 阶段,ES2020 尚未发布,甚至尚未最终确定。至少您提到了如何使用它而无需等待它发布。 @gazdagergo 没问题 :)。【参考方案6】:我偶尔发现以下习语很有用:
a?.b?.c
可以改写为:
((a||).b||).c
这利用了在对象上获取未知属性返回 undefined 的事实,而不是像在 null
或 undefined
上那样抛出异常,因此我们在导航之前将 null 和 undefined 替换为空对象。
【讨论】:
嗯,它很难阅读,但比那种冗长的&&
方法要好。 +1。
这实际上是javascript中唯一真正安全的运算符。上面提到的逻辑“或”运算符是另一回事。
@Filippos 你能举例说明逻辑 OR 与 && 方法的不同行为吗?我想不出有什么区别
它还允许在不首先将其分配给变量的情况下导航匿名值。
爱它!如果您想在可能不会返回任何结果的 array.find() 操作之后获取对象的属性,这真的很有用【参考方案7】:
2019 年 9 月更新
是的,JS 现在支持这个。 v8 即将推出可选链接 read more
【讨论】:
不太一样。 OP 是关于空合并的,但还是不错的答案。【参考方案8】:这对我来说是一个很长一段时间的问题。我必须想出一个解决方案,一旦我们得到 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
我知道这会使代码有点难以阅读,但它是一个简单的单行解决方案并且效果很好。我希望它可以帮助某人:)
【讨论】:
【参考方案9】:很晚才加入,目前在第 2 阶段有一个关于可选链的提案[1],并提供了一个 babel 插件[2]。它目前不在我所知道的任何浏览器中。
-
https://github.com/tc39/proposal-optional-chaining
https://www.npmjs.com/package/@babel/plugin-proposal-optional-chaining
【讨论】:
【参考方案10】:你可以自己滚动:
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 中的任何一个未定义,则结果未定义。
【讨论】:
【参考方案11】:我阅读了这篇文章 (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 的表达式,结果将是未定义的。你可以wild修改对象原型!
Object.prototype.getSafe = function (expression)
return safe.safeGet(this, expression);
;
example.getSafe((x) => x.foo.woo);
【讨论】:
【参考方案12】:我认为以下相当于安全导航运算符,虽然有点长:
var streetName = user && user.address && user.address.street;
streetName
将是 user.address.street
或 undefined
的值。
如果您希望它默认为其他内容,您可以与上述快捷方式结合使用或提供:
var streetName = (user && user.address && user.address.street) || "Unknown Street";
【讨论】:
加上一个空传播和空合并的好例子! 这行得通,只是你不知道你得到的是 null 还是 undefined 【参考方案13】:您可以使用逻辑“或”运算符代替 Elvis 运算符:
例如 displayname = user.name || "Anonymous"
。
但 Javascript 目前没有其他功能。如果您想要替代语法,我建议您查看CoffeeScript。它有一些与您正在寻找的类似的速记。
例如存在运算符
zip = lottery.drawWinner?().address?.zipcode
功能快捷键
()-> // equivalent to function()
性感的函数调用
func 'arg1','arg2' // equivalent to func('arg1','arg2')
还有多行 cmets 和类。显然,您必须将其编译为 javascript 或以 <script type='text/coffeescript>'
的形式插入页面,但它增加了很多功能:)。使用 <script type='text/coffeescript'>
实际上只用于开发而非生产。
【讨论】:
在大多数情况下,逻辑或并不是完全需要的东西,因为您可能希望它仅在左侧未定义时才选择右侧操作数,但在它已定义且不正确时不选择。 CoffeeScript 主页使用<script type="text/coffeescript">
。
虽然这回答了这个问题,但它几乎完全是关于咖啡脚本而不是 javascript,并且一半以上是关于描述与 OP 无关的咖啡脚本好处。我建议将其归结为与问题相关的内容,就像咖啡脚本的其他好处一样美妙。
我要去香蕉吗?当然,user2451227 的反对意见(目前有 4 票)是无效的,因为如果表达式/左操作数被定义且错误,则同样不会选择三元的中间操作数(即带有猫王运算符的右操作数)。在这两种情况下,你都必须去x === undefined
。
请考虑更新此内容以提及optional chaining operator, ?.
,。 Browser support 不是我将它用于一般代码的地步,但它正朝着那个方向发展。此外,现在还有nullish coalescing operator (??),具有类似的状态。【参考方案14】:
我个人使用
function e(e,expr)tryreturn eval(expr);catch(e)return null;;
例如安全获取:
var a = e(obj,'e.x.y.z.searchedField');
【讨论】:
首先是really shouldn't use eval。其次,这甚至不起作用:e(a:b:c:d:'test', 'a.b.c.d')
返回null
。
@Pylinux 基本上可以工作的是e = eval
,var a = eval('obj.a.b.c.d')
。 eval
甚至不需要第二个参数...developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…【参考方案15】:
对于使用一些 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();
【讨论】:
【参考方案16】:我认为 lodash _.get()
可以在这里提供帮助,例如 _.get(user, 'name')
,以及更复杂的任务,例如 _.get(o, 'a[0].b.c', 'default-value')
【讨论】:
我对这种方法的主要问题是,由于属性的名称是字符串,你不能再使用 100% 信任的 IDE 的重构功能【参考方案17】:这是一个简单的 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
【讨论】:
【参考方案18】:我有一个解决方案,可以根据您自己的需要进行定制,摘自我的一个库:
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) 我将从我使用它的代码中创建一个小摘录,并在一周左右将其发布到 GitHub 上。【参考方案19】:对于前者,您可以使用||
。 Javascript“逻辑或”运算符不是简单地返回固定的真假值,而是遵循如果为真则返回其左参数的规则,否则评估并返回其右参数。当您只对真值感兴趣时,结果相同,但这也意味着 foo || bar || baz
返回 foo、bar 或 baz 中包含真值的最左边的一个。 p>
但是,您不会找到可以区分 false 和 null 的方法,并且 0 和空字符串是 false 值,因此请避免使用 value || default
构造,其中 value
可以合法地为 0 或 ""
。
【讨论】:
请注意,当左操作数为非空错误值时,这可能会导致意外行为。【参考方案20】:你可以通过以下方式达到大致相同的效果:
var displayName = user.name || "Anonymous";
【讨论】:
【参考方案21】:Javascript 的 logical OR operator 是 short-circuiting 并且可以替换您的“Elvis”运算符:
var displayName = user.name || "Anonymous";
但是,据我所知,没有与您的 ?.
运算符等效的选项。
【讨论】:
+1,我忘了||
可以这样使用。请注意,这不仅会在表达式为 null
时合并,而且还会在未定义、false
、0
或空字符串时合并。
@Cameron,确实如此,但问题中提到了这一点,似乎是提问者的意图。 ""
或 0
可能出乎意料:)【参考方案22】:
这通常称为空值合并运算符。 Javascript 没有。
【讨论】:
true 在严格意义上,但正如其他答案所指出的那样,JavaScript 的逻辑 OR 运算符可以表现为一种 false -coalescing 运算符,允许您在很多情况。 这不是一个空合并运算符。 Null-coalescing 仅适用于单个值,而不适用于属性访问/函数调用链。您已经可以使用 JavaScript 中的逻辑 OR 运算符进行空值合并。 不,您可以使用 JavaScript 中的逻辑 OR 进行错误合并。以上是关于javascript 中是不是有空合并 (Elvis) 运算符或安全导航运算符?的主要内容,如果未能解决你的问题,请参考以下文章