Acceso a propiedades null-safe (y asignación condicional) en ES6/2015


¿Existe un operador null-safe property access (null propagation / existence) en ES6 (ES2015/JavaScript.siguiente/Armonía) como ?. en CoffeeScript, por ejemplo? ¿O está previsto para ES7?

var aThing = getSomething()
...
aThing = possiblyNull?.thing

Esto será aproximadamente como:

if (possiblyNull != null) aThing = possiblyNull.thing

Idealmente la solución no debería asignar (even undefined) a aThing si possiblyNull es null

Author: Peter V, 2015-08-21

9 answers

Una característica que logra que se encuentra actualmente en la etapa 1: Encadenamiento opcional.

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

Si quieres usarlo hoy, hay un plugin de Babel que lo logra.

Https://github.com/davidyaha/ecmascript-optionals-proposal

Update (2017-08-01): Si quieres usar un plugin oficial, puedes probar la versión alpha de Babel 7 con la nueva transformación. Su kilometraje puede vary

Https://www.npmjs.com/package/babel-plugin-transform-optional-chaining

 61
Author: basicdays,
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-01-12 19:54:39

No es tan agradable como el ?. operador, pero para lograr un resultado similar se podría hacer:

user && user.address && user.address.postcode

Dado que null y undefined son ambos valores falsy ( ver esta referencia), la propiedad después del operador && solo se accede si el precedente no es nulo o indefinido.

Alternativamente, puedes escribir una función como esta:

function _try(func, fallbackValue) {
    try {
        var value = func();
        return (value === null || value === undefined) ? fallbackValue : value;
    } catch (e) {
        return fallbackValue;
    }
}

Uso:

_try(() => user.address.postcode) // return postcode or undefined 

O, con un valor de reserva:

_try(() => user.address.postcode, "none") // return postcode or a custom string
 37
Author: tocqueville,
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-10-02 11:30:41

No. Puedes usar lodash#get o algo así para esto en JavaScript.

 28
Author: Girafa,
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-08-21 11:26:14

Alternativa Vanilla para el acceso seguro a la propiedad

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

La asignación condicional más concisa probablemente sería esta

try { b = a.b.c.d.e } catch(e) {}
 8
Author: yagger,
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-11-15 10:45:50

No, no hay ningún operador de propagación nulo en ES6. Tendrás que ir con uno de los patrones conocidos.

Usted puede ser capaz de utilizar desestructuración, aunque:

({thing: aThing} = possiblyNull);

Hay muchas discusiones (por ejemplo, este) para agregar tal operador en ES7, pero ninguno realmente despegó.

 6
Author: Bergi,
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-05-23 10:31:02

Siguiendo la lista aquí, actualmente no hay ninguna propuesta para agregar recorrido seguro a Ecmascript. Así que no solo no hay una buena manera de hacer esto, sino que no se va a agregar en el futuro previsible.

 3
Author: Antimony,
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 20:56:03

Sé que esta es una pregunta de JavaScript, pero creo que Ruby maneja esto de todas las maneras solicitadas, así que creo que es un punto de referencia relevante.

.&, try, y & & tienen sus fortalezas y potenciales trampas. Un gran resumen de esas opciones aquí: http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby /

TLDR; La conclusión de los rubyistas es que dig es más fácil para los ojos y una garantía más fuerte de que un valor o null será asignar.

Aquí hay una explicación simple en TypeScript:

export function dig(target: any, ...keys: Array<string>): any {
  let digged = target
  for (const key of keys) {
    if (typeof digged === 'undefined') {
      return undefined // can also return null or a default value
    }
    if (typeof key === 'function') {
      digged = key(digged)
    } else {
      digged = digged[key]
    }
  }
  return digged
}

Esto se puede usar para cualquier profundidad de anidamiento y maneja funciones.

a = dig(b, 'c', 'd', 'e');
foo = () => ({});
bar = dig(a, foo, 'b', 'c')

El enfoque try es igualmente agradable de leer en JS, como se muestra en las respuestas anteriores. Tampoco requiere bucles, que es un inconveniente de esta implementación.

 0
Author: theUtherSide,
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-04-04 05:30:20

Pensé que esta pregunta necesitaba un poco de actualización para 2018. Esto se puede hacer muy bien sin ninguna biblioteca usando Object.defineProperty() y se puede usar de la siguiente manera:

myVariable.safeGet('propA.propB.propC');

Considero esto seguro (y js-ético) debido a las definiciones writeable y enumerable ahora disponibles para el método defineProperty de Object, como documentado en MDN

Definición de función a continuación:

Object.defineProperty(Object.prototype, 'safeGet', { 
    enumerable: false,
    writable: false,
    value: function(p) {
        return p.split('.').reduce((acc, k) => {
            if (acc && k in acc) return acc[k];
            return undefined;
        }, this);
    }
});

He reunido un jsBin con salida de consola para demostrar esto. Tenga en cuenta que en la versión jsBin También he añadido una excepción personalizada para los valores vacíos. Esto es opcional, así que lo he dejado fuera de la definición mínima anterior.

Las mejoras son bienvenidas

 0
Author: Felipe,
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-04-06 17:29:28

Un método de deep get seguro parece un ajuste natural para underscore.js pero allí el problema es evitar la programación de cadenas. Modificando la respuesta de @Felipe para evitar la programación de cadenas (o al menos devuelve los casos extremos al llamante):

function safeGet(obj, props) {
   return (props.length==1) ? obj[keys[0]] :safeGet(obj[props[0]], props.slice(1))
}

Ejemplo:

var test = { 
  a: { 
    b: 'b property value',
    c: { }
  } 
}
safeGet(test, ['a', 'b']) 
safeGet(test, "a.b".split('.'))  
 0
Author: prototype,
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-05-08 16:20:37