Je vais expliquer par exemple:
Elvis Opérateur (?:)
"L'opérateur Elvis" est un raccourcissement de l'opérateur ternaire de Java. Un exemple de où cela est pratique est pour renvoyer une valeur 'par défaut raisonnable' si une expression est fausse ou nul. Un exemple simple pourrait ressembler à ce:
def gender = user.male ? "male" : "female" //traditional ternary operator usage
def displayName = user.name ?: "Anonymous" //more compact Elvis operator
Opérateur de navigation sécurisée (?.)
L'opérateur Safe Navigation est utilisé pour éviter une exception NullPointerException . Généralement, lorsque vous faites référence à un objet que vous pourriez avoir besoin de vérifier qu'il n'est pas nul avant d'accéder à méthodes ou propriétés de l'objet . Pour éviter cela, la navigation en toute sécurité l'opérateur retournera simplement null au lieu de lancer une exception, comme alors:
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
Vous pouvez utiliser l'opérateur 'OU' logique à la place de l'opérateur Elvis:
Par exemple displayname = user.name || "Anonymous"
.
Mais Javascript n’a actuellement aucune autre fonctionnalité. Je vous recommande de regarder CoffeeScript si vous voulez une syntaxe alternative. Il possède des raccourcis similaires à ceux que vous recherchez.
Par exemple, l'opérateur existentiel
Zip = lottery.drawWinner?().address?.zipcode
Raccourcis de fonction
()-> // equivalent to function(){}
Appel de fonction sexy
func 'arg1','arg2' // equivalent to func('arg1','arg2')
Il y a aussi des commentaires et des classes multilignes. Évidemment, vous devez le compiler en javascript ou l'insérer dans la page en tant que <script type='text/coffeescript>'
mais cela ajoute beaucoup de fonctionnalités :) L'utilisation de <script type='text/coffeescript'>
n'est en réalité que destinée au développement et non à la production.
L'opérateur logique OR de Javascript est court-circuitant et peut remplacer votre opérateur "Elvis":
var displayName = user.name || "Anonymous";
Cependant, à ma connaissance, il n'y a pas d'équivalent à votre opérateur ?.
.
Je pense que ce qui suit est équivalent à l'opérateur de navigation sécurisée, bien qu'un peu plus long:
var streetName = user && user.address && user.address.street;
streetName
sera alors soit la valeur de user.address.street
, soit undefined
.
Si vous voulez utiliser autre chose par défaut, vous pouvez combiner le raccourci ci-dessus ou donner:
var streetName = (user && user.address && user.address.street) || "Unknown Street";
J'ai parfois trouvé l'idiome suivant utile:
a?.b.?c
peut être réécrit comme:
((a||{}).b||{}).c
Cela tire parti du fait que l'obtention d'attributs inconnus sur un objet renvoie non défini, plutôt que de lever une exception comme sur null
ou undefined
. Nous remplaçons donc null et non défini par un objet vide avant la navigation.
je pense que lodash _.get()
peut aider ici, comme dans _.get(user, 'name')
, et à des tâches plus complexes comme _.get(o, 'a[0].b.c', 'default-value')
Pour les premiers, vous pouvez utiliser ||
. L'opérateur Javascript "logique ou", plutôt que de simplement renvoyer des valeurs vraies et fausses, conserve la règle de retourner son argument de gauche s'il est vrai, sinon d'évaluer et de retourner son argument de droite. Lorsque vous êtes uniquement intéressé par la valeur de vérité, cela fonctionne de la même manière, mais cela signifie également que foo || bar || baz
renvoie le plus à gauche de foo, bar ou baz qui contient une valeur vraie .
Vous n'en trouverez pas cependant qui puisse distinguer false de null, null, et 0 et chaîne vide sont des valeurs fausses, évitez donc d'utiliser la construction value || default
où value
peut légitimement être 0 ou ""
.
Pas encore. Peut-être bientôt. Il existe actuellement un brouillon de spécification:
https://github.com/tc39/proposal-optional-chaining
https://tc39.github.io/proposal-optional-chaining/
Pour l'instant, cependant, j'aime bien utiliser lodash get(object, path, [defaultValue])
ou dlv delve(obj, keypath)
Voici un équivalent simple d'opérateur 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
Ceci est plus communément appelé opérateur à coalescence nulle. Javascript n'en a pas.
J'ai une solution pour cela, adaptez-la à vos besoins, un extrait de l'une de mes bibliothèques:
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;
}
},
fonctionne comme un charme. Profitez de moins de douleur!
Vous pouvez rouler le vôtre:
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;
};
Et utilisez-le comme ceci:
var result = resolve(obj, 'a.b.c.d');
* résultat est indéfini si l'un des a, b, c ou d est indéfini.
Vous pouvez obtenir à peu près le même effet en disant:
var displayName = user.name || "Anonymous";
Voici ma fonction "elvis". Passez l'objet racine et la chaîne sous forme de chaîne. Il retourne toujours le premier élément 'non défini' de la chaîne. Fonctionne avec les objets, les tableaux, les méthodes et les primitives.
elvis(myObject, 'categories.shirts[0].getPrice().currency');
Exemple de travail:
const elvis = (obj, keychain) => {
const handleArray = (parent, key) => {
if (key.indexOf('[') > -1) {
const arrayName = key.split('[')[0];
const arrayIndex = +key.split('[')[1].slice(0, -1);
return parent[arrayName] && parent[arrayName][arrayIndex];
}
if (key.indexOf('(') > -1) {
const methodName = key.split('(')[0];
return parent[methodName] && parent[methodName]();
}
return parent[key];
}
const keys = keychain.split('.');
let base = obj;
for (let i = 0; i < keys.length; i += 1) {
base = handleArray(base, keys[i]);
if (typeof base === 'undefined') return base;
}
return base;
}
//--------
const myObject = {
categories: {
getFoo: () => 'foo',
shirts: [
{ color: 'red' },
{ color: 'blue' }
]
}
}
console.log(elvis(myObject, 'categories.shirts[0].color'));
console.log(elvis(myObject, 'categories.getFoo()'));
console.log(elvis(myObject, 'categories.getBar()'));
console.log(elvis(myObject, 'categories.shirts[0].length'));
console.log(elvis(myObject, 'categories.pans[2].color'));
Très tardivement, il existe une proposition [1] d’enchaînement optionnel actuellement à la phase 2, avec un plugin babel [2] disponible. Ce n'est pas actuellement dans aucun navigateur que je sache.
C’était une solution intéressante pour l’opérateur de navigation sûre qui utilisait du 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();
J'ai lu cet article ( https://www.beyondjava.net/elvis-operator-aka-safe-navigation-javascript-TypeScript ) et modifié la solution à l'aide de proxies.
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;
}
Vous l'appelez comme ça:
safe.safeGet(example, (x) => x.foo.woo)
Le résultat sera indéfini pour une expression rencontrant null ou indéfini le long de son chemin. Vous pouvez aller wild et modifier le prototype d'objet!
Object.prototype.getSafe = function (expression) {
return safe.safeGet(this, expression);
};
example.getSafe((x) => x.foo.woo);
Cela a été un problème pour moi pendant une longue période. Je devais trouver une solution qui puisse être facilement migrée une fois que nous obtenons un opérateur Elvis ou quelque chose du genre.
C'est ce que j'utilise fonctionne pour les tableaux et les objets
mettre cela dans le fichier tools.js ou quelque chose
// 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]
};
exemple d'utilisation:
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
eh bien, je sais que cela rend le code un peu illisible, mais c’est une solution simple à un seul support qui fonctionne très bien. J'espère que ça aide quelqu'un :)