Existe-t-il un moyen d'obtenir quelque chose comme le suivant pour travailler en JavaScript?
var foo = {
a: 5,
b: 6,
c: this.a + this.b // Doesn't work
};
Dans le formulaire actuel, ce code génère évidemment une erreur de référence car this
ne fait pas référence à foo
. Mais is y a-t-il un moyen d'avoir des valeurs dans les propriétés d'un littéral d'objet dépendant d'autres propriétés déclarées précédemment?
Eh bien, la seule chose que je peux vous dire, ce sont des accesseurs:
var foo = {
a: 5,
b: 6,
get c () {
return this.a + this.b;
}
};
foo.c; // 11
Il s'agit d'une extension syntaxique introduite par la spécification ECMAScript 5th Edition. Cette syntaxe est prise en charge par la plupart des navigateurs modernes (notamment IE9).
Vous pourriez faire quelque chose comme:
var foo = {
a: 5,
b: 6,
init: function() {
this.c = this.a + this.b;
return this;
}
}.init();
Ce serait une sorte d'initialisation ponctuelle de l'objet.
Notez que vous assignez en réalité la valeur de retour de init()
à foo
; vous devez donc return this
.
Il manque la réponse évidente et simple, alors pour être complet:
Mais is y a-t-il un moyen d'avoir des valeurs dans les propriétés d'un littéral d'objet dépendant d'autres propriétés déclarées précédemment?
Non. Toutes les solutions présentées ici le reportent après la création de l'objet (de différentes manières), puis attribuent la troisième propriété. La la plus simple} méthode consiste simplement à faire ceci:
var foo = {
a: 5,
b: 6
};
foo.c = foo.a + foo.b;
Tous les autres ne sont que des moyens plus indirects de faire la même chose. (Felix's est particulièrement intelligent, mais nécessite la création et la destruction d'une fonction temporaire, ce qui ajoute de la complexité; et laisse une propriété supplémentaire sur l'objet ou [si vous delete
cette propriété] affecte la performance des accès de propriété ultérieurs sur cet objet. )
Si vous avez besoin que tout soit dans une même expression, vous pouvez le faire sans la propriété temporaire:
var foo = function(o) {
o.c = o.a + o.b;
return o;
}({a: 5, b: 6});
Ou bien sûr, si vous devez le faire plus d'une fois:
function buildFoo(a, b) {
var o = {a: a, b: b};
o.c = o.a + o.b;
return o;
}
alors où vous devez l'utiliser:
var foo = buildFoo(5, 6);
Instanciez simplement une fonction anonyme:
var foo = new function () {
this.a = 5;
this.b = 6;
this.c = this.a + this.b;
};
Une fermeture devrait régler ce problème;
var foo = function() {
var a = 5;
var b = 6;
var c = a + b;
return {
a: a,
b: b,
c: c
}
}();
Toutes les variables déclarées dans foo
sont privées à foo
, comme on peut s'y attendre avec n'importe quelle déclaration de fonction. Et comme elles sont toutes dans la portée, elles ont toutes accès les unes aux autres sans avoir besoin de se référer à this
, comme on peut s'y attendre avec une fonction. La différence est que cette fonction renvoie un objet qui expose les variables privées et assigne cet objet à foo
. En fin de compte, vous renvoyez uniquement l'interface que vous souhaitez exposer en tant qu'objet avec l'instruction return {}
.
La fonction est ensuite exécutée à la fin avec le ()
, ce qui entraîne l'évaluation de l'objet foo entier, de toutes les variables dans l'instancié et de l'objet de retour ajouté en tant que propriétés de foo()
.
Désormais, dans ES6, vous pouvez créer des propriétés en cache paresseuses. Lors de la première utilisation, la propriété est évaluée une fois pour devenir une propriété statique normale. Résultat: la deuxième fois que la surcharge de la fonction mathématique est ignorée.
La magie est dans le getter.
const foo = {
a: 5,
b: 6,
get c() {
delete this.c;
return this.c = this.a + this.b
}
};
Dans la flèche getter this
prend le entourant la portée lexicale .
foo // {a: 5, b: 6}
foo.c // 11
foo // {a: 5, b: 6 , c: 11}
Tu pourrais le faire comme ça
var a, b
var foo = {
a: a = 5,
b: b = 6,
c: a + b
}
Cette méthode s’est révélée utile pour moi lorsque je devais faire référence à l’objet sur lequel une fonction était initialement déclarée. Ce qui suit est un exemple minimal de la façon dont je l'ai utilisé:
function createMyObject() {
var count = 0, self
return {
a: self = {
log: function() {
console.log(count++)
return self
}
}
}
}
En définissant self en tant qu'objet contenant la fonction d'impression, vous lui permettez de faire référence à cet objet. Cela signifie que vous n'aurez pas à lier la fonction d'impression à un objet si vous devez le transmettre ailleurs.
Si vous préférez utiliser this
comme illustré ci-dessous
function createMyObject() {
var count = 0
return {
a: {
log: function() {
console.log(count++)
return this
}
}
}
}
Ensuite, le code suivant enregistre 0, 1, 2 puis donne une erreur
var o = createMyObject()
var log = o.a.log
o.a.log().log() // this refers to the o.a object so the chaining works
log().log() // this refers to the window object so the chaining fails!
En utilisant la méthode automatique, vous garantissez que print retournera toujours le même objet, quel que soit le contexte dans lequel la fonction est exécutée. Le code ci-dessus fonctionnera parfaitement et consignera les valeurs 0, 1, 2 et 3 lors de l'utilisation de la version autonome de createMyObject()
.
Il y a plusieurs façons d'accomplir cela. Voici ce que je voudrais utiliser:
function Obj() {
this.a = 5;
this.b = this.a + 1;
// return this; // commented out because this happens automatically
}
var o = new Obj();
o.b; // === 6
Pour terminer, dans ES6, nous avons des classes (prises en charge au moment de l'écriture, uniquement par les navigateurs les plus récents, mais disponibles dans Babel, TypeScript et d'autres transpilers).
class Foo {
constructor(){
this.a = 5;
this.b = 6;
this.c = this.a + this.b;
}
}
const foo = new Foo();
pour des raisons de pensée, placez les propriétés d'un objet hors d'un scénario:
var foo = {
a: function(){return 5}(),
b: function(){return 6}(),
c: function(){return this.a + this.b}
}
console.log(foo.c())
il y a de meilleures réponses ci-dessus aussi. Voici comment j'ai modifié le code d'exemple que vous avez interrogé.
METTRE À JOUR:
var foo = {
get a(){return 5},
get b(){return 6},
get c(){return this.a + this.b}
}
// console.log(foo.c);
Vous pouvez le faire en utilisant le modèle de module. Juste comme:
var foo = function() {
var that = {};
that.a = 7;
that.b = 6;
that.c = function() {
return that.a + that.b;
}
return that;
};
var fooObject = foo();
fooObject.c(); //13
Avec ce modèle, vous pouvez instancier plusieurs objets foo en fonction de vos besoins.
Créer une nouvelle fonction sur le littéral de votre objet et invoquer un constructeur semble constituer un changement radical par rapport au problème initial, et est inutile.
Vous ne pouvez pas référencer une propriété frère lors de l'initialisation du littéral d'objet.
var x = { a: 1, b: 2, c: a + b } // not defined
var y = { a: 1, b: 2, c: y.a + y.b } // not defined
La solution la plus simple pour les propriétés calculées est la suivante (pas de tas, pas de fonctions, pas de constructeur):
var x = { a: 1, b: 2 };
x.c = x.a + x.b; // apply computed property
La clé de tout cela est PORTÉE.
Vous devez encapsuler le "parent" (objet parent) de la propriété que vous souhaitez définir comme étant son propre objet instancié. Vous pouvez ensuite faire référence aux propriétés du même parent à l'aide de la clé Word this
Il est très, très important de se rappeler que si vous faites référence à this
sans le faire d'abord, alors this
fera référence à l'étendue externe ... qui sera l'objet window
.
var x = 9 //this is really window.x
var bar = {
x: 1,
y: 2,
foo: new function(){
this.a = 5, //assign value
this.b = 6,
this.c = this.a + this.b; // 11
},
z: this.x // 9 (not 1 as you might expect, b/c *this* refers `window` object)
};
Les autres réponses postées ici sont meilleures mais voici une alternative:
init()
ou de code en dehors du littéral d'objetFonctions anonymes auto-exécutables et stockage de fenêtres
var foo = {
bar:(function(){
window.temp = "qwert";
return window.temp;
})(),
baz: window.temp
};
La commande est garantie (bar
avant baz
).
Bien sûr, cela pollue window
, mais je ne peux pas imaginer que quelqu'un écrive un script nécessitant que window.temp
soit persistant. Peut-être tempMyApp
si vous êtes paranoïaque.
C'est aussi moche mais parfois utile. Par exemple, lorsque vous utilisez une API avec des conditions d’initialisation rigides et que vous n’avez pas l’impression de la refactoriser, la portée est correcte.
Et c'est sec, bien sûr.
si votre objet est écrit sous forme de fonction qui retourne un objet ET que vous utilisez les méthodes d'attribut d'objet ES6, alors il est possible:
const module = (state) => ({
a: 1,
oneThing() {
state.b = state.b + this.a
},
anotherThing() {
this.oneThing();
state.c = state.b + this.a
},
});
const store = {b: 10};
const root = module(store);
root.oneThing();
console.log(store);
root.anotherThing();
console.log(store);
console.log(root, Object.keys(root), root.prototype);
Voici une façon soignée ES6:
var foo = (o => ({
...o,
c: o.a + o.b
}))({
a: 5,
b: 6
});
console.log(foo);
Je l'utilise pour faire quelque chose comme ça:
const constants = Object.freeze(
(_ => ({
..._,
flag_data: {
[_.a_flag]: 'foo',
[_.b_flag]: 'bar',
[_.c_flag]: 'oof'
}
}))({
a_flag: 5,
b_flag: 6,
c_flag: 7,
})
);
console.log(constants.flag_data[constants.b_flag]);
Que diriez-vous de cette solution cela fonctionnera avec des objets imbriqués avec tableau aussi bien
Object.prototype.assignOwnProVal
= function (to,from){
function compose(obj,string){
var parts = string.split('.');
var newObj = obj[parts[0]];
if(parts[1]){
parts.splice(0,1);
var newString = parts.join('.');
return compose(newObj,newString);
}
return newObj;
}
this[to] = compose(this,from);
}
var obj = { name : 'Gaurav', temp :
{id : [10,20], city:
{street:'Brunswick'}} }
obj.assignOwnProVal('street','temp.city.street');
obj.assignOwnProVal('myid','temp.id.1');
J'utilise le code suivant comme alternative, et cela fonctionne. Et la variable peut aussi être un tableau. (@ Fausto R.)
var foo = {
a: 5,
b: 6,
c: function() {
return this.a + this.b;
},
d: [10,20,30],
e: function(x) {
this.d.Push(x);
return this.d;
}
};
foo.c(); // 11
foo.e(40); // foo.d = [10,20,30,40]
Une approche différente, pour simplifier un peu les choses et ne pas définir de nouvelles fonctions dans l'objet, consiste à conserver l'objet d'origine avec ses propriétés, créer un deuxième objet avec les propriétés dynamiques qui utilisent les propriétés du premier puis fusionnez en utilisant spread .
Alors:
Objet
var foo = {
a: 5,
b: 6,
};
Object 2
var foo2 = {
c: foo.a + foo.b
};
Fusionner
Object.assign(foo, foo2);
ou
foo = {...foo, ...foo2};
Résultat de foo: _
{
"a":5,
"b":6,
"c":11
}
Tous dans le même js, pas de nouvelles fonctions dans l'objet, seulement en utilisant assign ou spread.
Une autre approche consisterait à déclarer l’objet d’abord avant de lui affecter des propriétés:
const foo = {};
foo.a = 5;
foo.b = 6;
foo.c = foo.a + foo.b; // Does work
foo.getSum = () => foo.a + foo.b + foo.c; // foo.getSum() === 22
Avec cela, vous pouvez utiliser le nom de la variable d'objet pour accéder aux valeurs déjà attribuées.
Idéal pour le fichier config.js
.
Jeter une option puisque je n'ai pas vu ce scénario exact couvert. Si vous ne voulez pas souhaitez que c
soit mis à jour lorsque a
ou b
est mis à jour, un ES6 IIFE fonctionne bien.
var foo = ((a,b) => ({
a,
b,
c: a + b
}))(a,b);
Pour mes besoins, j'ai un objet qui se rapporte à un tableau qui finira par être utilisé dans une boucle, je ne souhaite donc calculer qu'une seule configuration commune, c'est donc ce que j'ai:
let processingState = ((indexOfSelectedTier) => ({
selectedTier,
indexOfSelectedTier,
hasUpperTierSelection: tiers.slice(0,indexOfSelectedTier)
.some(t => pendingSelectedFiltersState[t.name]),
}))(tiers.indexOf(selectedTier));
Étant donné que je dois définir une propriété pour indexOfSelectedTier
et que je dois utiliser cette valeur lors de la définition de la propriété hasUpperTierSelection
, je calcule cette valeur d'abord et la passe en tant que paramètre à l'IIFE.