Je me demande quand je devrais utiliser
Object.defineProperty
créer de nouvelles propriétés pour un objet. Je suis conscient que je suis capable de régler des choses comme
enumerable: false
mais quand avez-vous vraiment besoin de cela? Si vous venez de définir une propriété comme
myObject.myprop = 5;
ses descripteurs sont tous définis sur true, non? En fait, je suis plus curieux quand vous utilisez cet appel plutôt verbeux à .defineProperty () et pour quelles raisons.
Object.defineProperty
est principalement utilisé pour définir des propriétés avec des descripteurs de propriétés spécifiques (par exemple, en lecture seule (constantes), enumerability (pour ne pas afficher une propriété dans une boucle for (.. in ..)
, getters, setters).
"use strict";
var myObj = {}; // Create object
// Set property (+descriptor)
Object.defineProperty(myObj, 'myprop', {
value: 5,
writable: false
});
console.log(myObj.myprop);// 5
myObj.myprop = 1; // In strict mode: TypeError: myObj.myprop is read-only
Cette méthode étend le prototype Object
avec une propriété. Seul le getter est défini et l'énumération est définie sur false
.
Object.defineProperty(Object.prototype, '__CLASS__', {
get: function() {
return Object.prototype.toString.call(this);
},
enumerable: false // = Default
});
Object.keys({}); // []
console.log([].__CLASS__); // "[object Array]"
Des fonctionnalités telles que 'énumérable' sont rarement utilisées dans mon expérience . Le principal cas d'utilisation est constitué par les propriétés calculées:
var myObj = {};
myObj.width = 20;
myObj.height = 20;
Object.defineProperty(myObj, 'area', {
get: function() {
return this.width*this.height;
}
});
console.log(myObj.area);
Une très bonne raison d'utiliser Object.defineProperty est qu'elle vous permet de parcourir une fonction d'un objet en tant que propriété calculée, qui exécute la fonction au lieu de renvoyer le corps de la fonction.
Par exemple:
var myObj = {};
myObj.width = 20;
myObj.height = 20;
Object.defineProperty(myObj, 'area', {
get: function() {
return this.width*this.height;
},
enumerable: true
});
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key + " -> " + myObj[key]);
}
}
//width -> 20, height -> 20, area -> 400
Ajout de la fonction en tant que propriété à un objet littéral:
var myObj = {};
myObj.width = 20;
myObj.height = 20;
myObj.area = function() {
return this.width*this.height;
};
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key + " -> " + myObj[key]);
}
}
// width -> 20, height -> 20, area -> function() { return this.width*this.height;}
Assurez-vous de définir la propriété enumerable sur true afin de pouvoir la parcourir en boucle.
Par exemple, c'est ce que Vue.js garde trace des modifications apportées à l'objet data
:
Lorsque vous transmettez un objet JavaScript brut à une instance Vue en tant que son option
data
, Vue parcourra toutes ses propriétés et les convertira en _getter/setters
_ à l'aide deObject.defineProperty
. Il s’agit d’une fonctionnalité ES5 uniquement et non shimmable. C’est pourquoi Vue ne prend pas en charge IE8 et les versions antérieures.Les getter/setters sont invisibles pour l'utilisateur, mais ils permettent sous-jacent à Vue d'effectuer un suivi de dépendance et une notification de changement lorsque des propriétés sont accédées ou modifiées.
[...]
N'oubliez pas que même une version ultra-plate et de base de Vue.js utiliserait autre chose que _Object.defineProperty
_, mais la fonctionnalité principale en découle:
Ici vous pouvez voir un article dans lequel l'auteur implémente une version PoC minimale de quelque chose comme Vue.js: https://medium.com/js-dojo/understand-vue-reactivity-implementation-step-by- étape-599c3d51cd6c
Et voici une conférence (en espagnol) dans laquelle l’orateur construit quelque chose de similaire tout en expliquant la réactivité dans Vue.js: https://www.youtube.com/watch?v=axXwWU-L7RM
Object.defineProperty
vous empêche d'attribuer accidentellement des valeurs à une clé de sa chaîne de prototypes . Avec cette méthode, vous affectez uniquement à ce niveau d'objet particulier (pas à une clé de la chaîne de prototypes).
Par exemple: Il y a un objet comme {key1: value1, key2: value2}
et vous ne connaissez pas exactement sa chaîne de prototypes ou, par erreur, vous le manquez et il y a une propriété 'couleur' quelque part dans la chaîne de prototypes alors-
en utilisant un point (.) assignation -
cette opération attribue une valeur à la clé 'couleur' dans la chaîne de prototypes (si une clé existe quelque part) et vous retrouverez l'objet sans changement tel que . obj.color = 'blue'; // obj reste identique à {key1: value1, key2: value2}
en utilisant la méthode Object.defineProperty -
Object.defineProperty(obj, 'color', {
value: 'blue'
});
// maintenant obj ressemble à {key1: value1, key2: value2, color: 'blue'}
. il ajoute une propriété au même niveau.Ensuite, vous pouvez effectuer une itération en toute sécurité avec la méthode Object.hasOwnProperty()
.
Une bonne utilisation consiste à effectuer une interception ou à appliquer un motif classique Observateur/Observable de manière élégante:
En Javascript, les objets sont des collections de paires clé-valeur. Object.defineProperty()
est une fonction qui peut définir une nouvelle propriété sur un objet et définir les attributs suivants d'une propriété:
<any>
: La valeur associée à la clé<boolean>
: si writeable est défini sur true
La propriété peut être mise à jour en lui attribuant une nouvelle valeur. Si défini sur false
, vous ne pouvez pas modifier la valeur.<boolean>
: si enumerable est défini sur true
La propriété est accessible via une boucle for..in
. De plus, sont les seules clés de propriété énumérables retournées avec Object.keys()
<boolean>
: Si configurable est défini sur false
, vous ne pouvez pas modifier les attributs de propriété (valeur/accessible en écriture/configurable/configurable). De plus, vous ne pouvez pas modifier la valeur, vous ne pouvez pas le supprimer à l'aide de l'opérateur delete
.let obj = {};
Object.defineProperty(obj, 'prop1', {
value: 1,
writable: false,
enumerable: false,
configurable: false
}); // create a new property (key=prop1, value=1)
Object.defineProperty(obj, 'prop2', {
value: 2,
writable: true,
enumerable: true,
configurable: true
}); // create a new property (key=prop2, value=2)
console.log(obj.prop1, obj.prop2); // both props exists
for(const props in obj) {
console.log(props);
// only logs prop2 because writable is true in prop2 and false in prop1
}
obj.prop1 = 100;
obj.prop2 = 100;
console.log(obj.prop1, obj.prop2);
// only prop2 is changed because prop2 is writable, prop1 is not
delete obj.prop1;
delete obj.prop2;
console.log(obj.prop1, obj.prop2);
// only prop2 is deleted because prop2 is configurable and prop1 is not
Un cas d'utilisation intéressant que j'ai vu pour defineProperty
est que les bibliothèques fournissent à l'utilisateur une propriété d'erreur qui, si vous n'y accédez pas dans un certain intervalle, vous le feriez vous-même. Par exemple:
let logErrorTimeoutId = setTimeout(() => {
if (error) {
console.error('Unhandled (in <your library>)', error.stack || error);
}
}, 10);
Object.defineProperty(data, 'error', {
configurable: true,
enumerable: true,
get: () => {
clearTimeout(logErrorTimeoutId);
return error;
},
});
Source pour ce code: https://github.com/apollographql/react-apollo/blob/ddd3d8faabf135dca691d20ce8ab0bc24ccc414e/src/graphql.tsx#L510