在javascript中是否有一个null-coalescing (Elvis)操作符或安全导航操作符?

时间:2022-04-23 11:47:03

I'll explain by example:

我将解释的例子:

Elvis Operator (?: )

猫王操作符(?:)

The "Elvis operator" is a shortening of Java's ternary operator. One instance of where this is handy is for returning a 'sensible default' value if an expression resolves to false or null. A simple example might look like this:

“Elvis运算符”是Java三元运算符的缩写。这很方便的一个例子是,如果表达式解析为false或null,就返回一个“合理的默认值”。一个简单的例子可能是这样的:

def gender = user.male ? "male" : "female"  //traditional ternary operator usage

def displayName = user.name ?: "Anonymous"  //more compact Elvis operator

Safe Navigation Operator (?.)

安全的导航操作符(?)。

The Safe Navigation operator is used to avoid a NullPointerException. Typically when you have a reference to an object you might need to verify that it is not null before accessing methods or properties of the object. To avoid this, the safe navigation operator will simply return null instead of throwing an exception, like so:

安全导航操作符用于避免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

14 个解决方案

#1


92  

You can use the logical 'OR' operator in place of the Elvis operator:

您可以使用逻辑“或”操作符代替Elvis操作符:

For example displayname = user.name || "Anonymous" .

例如displayname = user.name ||“Anonymous”。

But Javascript currently doesn't have the other functionality. I'd recommend looking at CoffeeScript if you want an alternative syntax. It has some shorthand that is similar to what you are looking for.

但是Javascript目前没有其他功能。如果您需要其他语法,我建议您查看CoffeeScript。它有一些类似于你正在寻找的东西的简写。

For example The Existential Operator

例如存在算子

zip = lottery.drawWinner?().address?.zipcode

Function shortcuts

功能快捷键

()->  // equivalent to function(){}

Sexy function calling

性感的函数调用

func 'arg1','arg2' // equivalent to func('arg1','arg2')

There is also multiline comments and classes. Obviously you have to compile this to javascript or insert into the page as <script type='text/coffeescript>' but it adds a lot of functionality :) . Using <script type='text/coffeescript'> is really only intended for development and not production.

还有多行注释和类。显然,您必须将其编译为javascript或作为

#2


67  

Javascript's logical OR operator is short-circuiting and can replace your "Elvis" operator:

Javascript的逻辑或操作符正在短路,可以替换“Elvis”操作符:

var displayName = user.name || "Anonymous";

However, to my knowledge there's no equivalent to your ?. operator.

然而,据我所知,没有什么比得上你的?操作符。

#3


55  

I think the following is equivalent to the safe navigation operator, although a bit longer:

我认为以下内容相当于安全导航操作符,虽然有点长:

var streetName = user && user.address && user.address.street;

streetName will then be either the street name or null/undefined.

streetName将是街道名或空/未定义。

If you want it to default to something else you can combine with the above shortcut or to give:

如果你想让它默认为其他的东西,你可以结合上面的快捷方式或给予:

var streetName = (user && user.address && user.address.street) || "Unknown Street";

#4


33  

I've occasionally found the following idiom useful:

我偶尔发现下面这个习语很有用:

a?.b.?c

can be rewritten as:

可以写成:

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

This takes advantage of the fact that getting unknown attributes on an object returns undefined, rather than throwing an exception as it does on null or undefined, so we replace null and undefined with an empty object before navigating.

这利用了在对象上获取未知属性会返回未定义的属性这一事实,而不是像在null或undefined上那样抛出异常,因此我们在导航之前将null和undefined替换为一个空对象。

#5


17  

i think lodash _.get() can help here, as in _.get(user, 'name'), and more complex tasks like _.get(o, 'a[0].b.c', 'default-value')

我认为lodash _.get()可以在这里有所帮助,就像在_中一样。获取(user, 'name')和更复杂的任务,比如_。get(o,”[0]。b。c”、“默认值”)

#6


10  

For the former, you can use ||. The Javascript "logical or" operator, rather than simply returning canned true and false values, follows the rule of returning its left argument if it is true, and otherwise evaluating and returning its right argument. When you're only interested in the truth value it works out the same, but it also means that foo || bar || baz returns the leftmost one of foo, bar, or baz that contains a true value.

对于前者,可以使用||。Javascript“逻辑或”运算符,而不是简单地返回固定的真值和假值,遵循的规则是,如果它是真,就返回它的左参数,否则计算并返回它的右参数。当你只对真值感兴趣时,结果是一样的,但它也意味着foo || bar || baz返回了foo, bar,或baz中最左边的一个包含一个真值。

You won't find one that can distinguish false from null, though, and 0 and empty string are false values, so avoid using the value || default construct where value can legitimately be 0 or "".

但是,您不会找到一个可以区分false和null的值,并且0和空字符串是假值,因此避免使用值||默认构造,其中的值可以合法地为0或“”。

#7


6  

Not yet. Maybe soon. There is currently a draft spec:

还没有。也许很快。目前有一个规范草案:

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

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

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

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

For now, though, I like to use lodash get(object, path, [defaultValue]) or dlv delve(obj, keypath)

现在,我喜欢使用lodash get(object, path, [defaultValue])或dlv delve(obj, keypath)

#8


4  

This is more commonly known as a null-coalescing operator. Javascript does not have one.

这就是通常所说的null-coalescing运算符。Javascript没有。

#9


3  

I have a solution for that, tailor it to your own needs, an excerpt from one of my 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;
        }
    },

works like a charm. Enjoy the less pain!

就像一个魅力。享受更少的痛苦!

#10


3  

Here's a simple elvis operator equivalent:

这里有一个简单的猫王运算符等价:

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

#11


2  

You can achieve roughly the same effect by saying:

你可以说:

var displayName = user.name || "Anonymous";

#12


1  

You could roll your own:

你可以自己动手:

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;
};

And the use it like this:

它的用途是这样的:

var result = resolve(obj, 'a.b.c.d'); 

* result is undefined any one of a, b, c or d is undefined

*结果不定义a、b、c或d中的任何一个

#13


0  

This was an interesting solution for the safe navigation operator using some mixin..

对于使用某些mixin的安全导航操作符来说,这是一个有趣的解决方案。

http://jsfiddle.net/avernet/npcmv/

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();

#14


-4  

Personally i use

我个人使用

function e(e,expr){try{return eval(expr);}catch(e){return null;}};

and for example safe get:

例如,安全获取:

var a = e(obj,'e.x.y.z.searchedField');

#1


92  

You can use the logical 'OR' operator in place of the Elvis operator:

您可以使用逻辑“或”操作符代替Elvis操作符:

For example displayname = user.name || "Anonymous" .

例如displayname = user.name ||“Anonymous”。

But Javascript currently doesn't have the other functionality. I'd recommend looking at CoffeeScript if you want an alternative syntax. It has some shorthand that is similar to what you are looking for.

但是Javascript目前没有其他功能。如果您需要其他语法,我建议您查看CoffeeScript。它有一些类似于你正在寻找的东西的简写。

For example The Existential Operator

例如存在算子

zip = lottery.drawWinner?().address?.zipcode

Function shortcuts

功能快捷键

()->  // equivalent to function(){}

Sexy function calling

性感的函数调用

func 'arg1','arg2' // equivalent to func('arg1','arg2')

There is also multiline comments and classes. Obviously you have to compile this to javascript or insert into the page as <script type='text/coffeescript>' but it adds a lot of functionality :) . Using <script type='text/coffeescript'> is really only intended for development and not production.

还有多行注释和类。显然,您必须将其编译为javascript或作为

#2


67  

Javascript's logical OR operator is short-circuiting and can replace your "Elvis" operator:

Javascript的逻辑或操作符正在短路,可以替换“Elvis”操作符:

var displayName = user.name || "Anonymous";

However, to my knowledge there's no equivalent to your ?. operator.

然而,据我所知,没有什么比得上你的?操作符。

#3


55  

I think the following is equivalent to the safe navigation operator, although a bit longer:

我认为以下内容相当于安全导航操作符,虽然有点长:

var streetName = user && user.address && user.address.street;

streetName will then be either the street name or null/undefined.

streetName将是街道名或空/未定义。

If you want it to default to something else you can combine with the above shortcut or to give:

如果你想让它默认为其他的东西,你可以结合上面的快捷方式或给予:

var streetName = (user && user.address && user.address.street) || "Unknown Street";

#4


33  

I've occasionally found the following idiom useful:

我偶尔发现下面这个习语很有用:

a?.b.?c

can be rewritten as:

可以写成:

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

This takes advantage of the fact that getting unknown attributes on an object returns undefined, rather than throwing an exception as it does on null or undefined, so we replace null and undefined with an empty object before navigating.

这利用了在对象上获取未知属性会返回未定义的属性这一事实,而不是像在null或undefined上那样抛出异常,因此我们在导航之前将null和undefined替换为一个空对象。

#5


17  

i think lodash _.get() can help here, as in _.get(user, 'name'), and more complex tasks like _.get(o, 'a[0].b.c', 'default-value')

我认为lodash _.get()可以在这里有所帮助,就像在_中一样。获取(user, 'name')和更复杂的任务,比如_。get(o,”[0]。b。c”、“默认值”)

#6


10  

For the former, you can use ||. The Javascript "logical or" operator, rather than simply returning canned true and false values, follows the rule of returning its left argument if it is true, and otherwise evaluating and returning its right argument. When you're only interested in the truth value it works out the same, but it also means that foo || bar || baz returns the leftmost one of foo, bar, or baz that contains a true value.

对于前者,可以使用||。Javascript“逻辑或”运算符,而不是简单地返回固定的真值和假值,遵循的规则是,如果它是真,就返回它的左参数,否则计算并返回它的右参数。当你只对真值感兴趣时,结果是一样的,但它也意味着foo || bar || baz返回了foo, bar,或baz中最左边的一个包含一个真值。

You won't find one that can distinguish false from null, though, and 0 and empty string are false values, so avoid using the value || default construct where value can legitimately be 0 or "".

但是,您不会找到一个可以区分false和null的值,并且0和空字符串是假值,因此避免使用值||默认构造,其中的值可以合法地为0或“”。

#7


6  

Not yet. Maybe soon. There is currently a draft spec:

还没有。也许很快。目前有一个规范草案:

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

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

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

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

For now, though, I like to use lodash get(object, path, [defaultValue]) or dlv delve(obj, keypath)

现在,我喜欢使用lodash get(object, path, [defaultValue])或dlv delve(obj, keypath)

#8


4  

This is more commonly known as a null-coalescing operator. Javascript does not have one.

这就是通常所说的null-coalescing运算符。Javascript没有。

#9


3  

I have a solution for that, tailor it to your own needs, an excerpt from one of my 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;
        }
    },

works like a charm. Enjoy the less pain!

就像一个魅力。享受更少的痛苦!

#10


3  

Here's a simple elvis operator equivalent:

这里有一个简单的猫王运算符等价:

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

#11


2  

You can achieve roughly the same effect by saying:

你可以说:

var displayName = user.name || "Anonymous";

#12


1  

You could roll your own:

你可以自己动手:

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;
};

And the use it like this:

它的用途是这样的:

var result = resolve(obj, 'a.b.c.d'); 

* result is undefined any one of a, b, c or d is undefined

*结果不定义a、b、c或d中的任何一个

#13


0  

This was an interesting solution for the safe navigation operator using some mixin..

对于使用某些mixin的安全导航操作符来说,这是一个有趣的解决方案。

http://jsfiddle.net/avernet/npcmv/

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();

#14


-4  

Personally i use

我个人使用

function e(e,expr){try{return eval(expr);}catch(e){return null;}};

and for example safe get:

例如,安全获取:

var a = e(obj,'e.x.y.z.searchedField');