¿Hay un operador de coalescencia nula (Elvis) u operador de navegación segura en javascript?


Voy a explicar por ejemplo:

Operador de Elvis (?: )

El "operador de Elvis" es un acortamiento del operador ternario de Java. Una ejemplo de donde esto es útil es para devolver un valor 'sensible por defecto' si una expresión se resuelve a false o nulo. Un ejemplo simple podría verse como esto:

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

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

Operador de Navegación segura (?.)

Se utiliza el operador de Navegación segura para evitar una excepción NullPointerException. Por lo general, cuando tener una referencia a un objeto que puede necesitar verificar que no es null antes de acceder métodos o propiedades del objeto. Para evitar esto, la navegación segura el operador simplemente devolverá null en lugar de lanzar una excepción, como entonces:

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
Author: Elias Zamaria, 2011-07-07

14 answers

Puede usar el operador lógico ' OR ' en lugar del operador de Elvis:

Por ejemplo displayname = user.name || "Anonymous" .

Pero Javascript actualmente no tiene la otra funcionalidad. Yo recomendaría mirar CoffeeScript si quieres una sintaxis alternativa. Tiene algo de taquigrafía que es similar a lo que estás buscando.

Por ejemplo El Operador Existencial

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

Atajos de función

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

Función sexy llamando

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

También Hay comentarios y clases multilínea. Obviamente tienes que compilar esto en javascript o insertarlo en la página como <script type='text/coffeescript>' pero añade mucha funcionalidad :) . Usar <script type='text/coffeescript'> es realmente solo para el desarrollo y no para la producción.

 98
Author: William,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-03 16:44:55

El operador lógico o de Javascript es cortocircuito y puede reemplazar su operador "Elvis":

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

Sin embargo, que yo sepa no hay equivalente a su operador ?..

 73
Author: Frédéric Hamidi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-07 16:36:17

Creo que lo siguiente es equivalente al operador de navegación segura, aunque un poco más largo:

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

streetName entonces será el nombre de la calle o null / undefined.

Si desea que por defecto a otra cosa que puede combinar con el acceso directo anterior o para dar:

var streetName = (user && user.address && user.address.street) || "Unknown Street";
 64
Author: samjudson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-04 11:31:23

Ocasionalmente he encontrado útil el siguiente modismo:

a?.b.?c

Se puede reescribir como:

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

Esto aprovecha el hecho de que obtener atributos desconocidos en un objeto devuelve indefinido, en lugar de lanzar una excepción como lo hace en null o undefined, por lo que reemplazamos null e undefined con un objeto vacío antes de navegar.

 41
Author: James_pic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-17 15:20:59

Creo que lodash _.get() puede ayudar aquí, como en _.get(user, 'name'), y las tareas más complejas como _.get(o, 'a[0].b.c', 'default-value')

 19
Author: tony_k,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-16 17:35:10

Para el primero, puede usar ||. El operador "lógico or" de Javascript, en lugar de simplemente devolver los valores verdaderos y falsos conservados, sigue la regla de devolver su argumento izquierdo si es verdadero, y de lo contrario evaluar y devolver su argumento derecho. Cuando solo estás interesado en el valor de verdad, funciona de la misma manera, pero también significa que foo || bar || baz devuelve el más a la izquierda de foo, bar o baz que contiene un valor verdadero.

No encontrarás uno que pueda distinguir false de null, sin embargo, y 0 y la cadena vacía son valores falsos, así que evite usar la construcción value || default donde value puede ser legítimamente 0 o "".

 10
Author: hobbs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-07 16:36:42

, todavía No. Tal vez pronto. Actualmente hay un borrador de especificación:

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

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

Por ahora, sin embargo, me gusta usar lodash get(object, path, [defaultValue]) o dlv delve(obj, keypath)

 8
Author: Jack Tuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-13 13:31:42

Esto se conoce más comúnmente como un operador de coalescencia nula. Javascript no tiene uno.

 4
Author: doctorless,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-07 16:34:15

Tengo una solución para eso, adaptarlo a sus propias necesidades, un extracto de una de mis 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;
        }
    },

Funciona como un encanto. ¡Disfruta menos dolor!

 3
Author: balazstth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-16 09:50:58

Aquí hay un simple operador de elvis equivalente:

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
 3
Author: cdmckay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-14 21:10:31

Puedes lograr aproximadamente el mismo efecto diciendo:

var displayName = user.name || "Anonymous";
 2
Author: Justin Ethier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-07 16:36:38

Usted podría rodar su propio:

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

Y úsalo así:

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

* el resultado es indefinido cualquiera de a, b, c o d es indefinido

 1
Author: Pylinux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-04 13:44:31

Esta fue una solución interesante para el operador de navegación segura utilizando algunos 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();
 0
Author: Dean Hiller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-29 14:55:59

Personalmente uso

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

Y por ejemplo safe get:

var a = e(obj,'e.x.y.z.searchedField');
 -4
Author: kudłaty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-12 07:56:44