2013-08-30 4 views
128

J'ai cherché comment utiliser la méthode Object.defineProperty, mais je n'ai rien trouvé de décent.comment utiliser javascript Object.defineProperty

Quelqu'un m'a donné this snippet of code:

Object.defineProperty(player, "health", { 
    get: function() { 
     return 10 + (player.level * 15); 
    } 
}) 

Mais je ne comprends pas. Principalement, le get est ce que je ne peux pas obtenir (jeu de mots). Comment ça marche?

+0

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty c'est un excellent tutoriel ici. – Martian2049

Répondre

386

Puisque vous avez demandé un similar question, prenons-le pas à pas. C'est un peu plus long, mais cela peut vous faire gagner beaucoup plus de temps que ce que j'ai écrit:

La propriété est une fonctionnalité de POO conçue pour la séparation nette du code client. Par exemple, dans certains e-shop vous pourriez avoir des objets comme celui-ci:

function Product(name,price) { 
    this.name = name; 
    this.price = price; 
    this.discount = 0; 
} 

var sneakers = new Product("Sneakers",20); // {name:"Sneakers",price:20,discount:0} 
var tshirt = new Product("T-shirt",10); // {name:"T-shirt",price:10,discount:0} 

Puis dans votre code client (e-shop), vous pouvez ajouter des remises à vos produits:

function badProduct(obj) { obj.discount+= 20; ... } 
function generalDiscount(obj) { obj.discount+= 10; ... } 
function distributorDiscount(obj) { obj.discount+= 15; ... } 

plus tard , le propriétaire du magasin en ligne pourrait réaliser que la réduction ne peut pas être supérieure à 80%. Maintenant, vous devez trouver toutes les occurrences de la modification d'actualisation dans le code client et ajouter une ligne

if(obj.discount>80) obj.discount = 80; 

Puis le propriétaire e-shop peut encore changer sa stratégie, comme « si le client est revendeur, la remise maximale peut être 90% ". Et vous devez faire le changement sur plusieurs endroits et vous devez vous rappeler de modifier ces lignes à chaque fois que la stratégie est modifiée. C'est un mauvais design. C'est pourquoi l'encapsulation est le principe de base de la POO.Si le constructeur était comme ça:

function Product(name,price) { 
    var _name=name, _price=price, _discount=0; 
    this.getName = function() { return _name; } 
    this.setName = function(value) { _name = value; } 
    this.getPrice = function() { return _price; } 
    this.setPrice = function(value) { _price = value; } 
    this.getDiscount = function() { return _discount; } 
    this.setDiscount = function(value) { _discount = value; } 
} 

Ensuite, vous pouvez simplement l'alter getDiscount (accesseur) et setDiscount méthodes (mutator). Le problème est que la plupart des membres se comportent comme des variables communes, juste la réduction nécessite des soins spéciaux ici. Mais une bonne conception nécessite l'encapsulation de chaque membre de données pour maintenir le code extensible. Vous devez donc ajouter beaucoup de code qui ne fait rien. C'est aussi une mauvaise conception, un passe-partout antipattern. Parfois, vous ne pouvez pas simplement refactoriser les champs à des méthodes plus tard (le code de l'eshop peut devenir grand ou un code tiers peut dépendre de l'ancienne version), de sorte que le standard est moins mauvais ici. Mais encore, c'est le mal. C'est pourquoi les propriétés ont été introduites dans plusieurs langues. Vous pouvez conserver le code original, transformer simplement l'élément d'escompte dans une propriété avec get et set blocs:

function Product(name,price) { 
    this.name = name; 
    this.price = price; 
//this.discount = 0; // <- remove this line and refactor with the code below 
    var _discount; // private member 
    Object.defineProperty(this,"discount",{ 
    get: function() { return _discount; }, 
    set: function(value) { _discount = value; if(_discount>80) _discount = 80; } 
    }); 
} 

// the client code 
var sneakers = new Product("Sneakers",20); 
sneakers.discount = 50; // 50, setter is called 
sneakers.discount+= 20; // 70, setter is called 
sneakers.discount+= 20; // 80, not 90! 
alert(sneakers.discount); // getter is called 

Notez la dernière ligne: la responsabilité de la valeur d'actualisation correcte a été transférée du code client (e définition de magasin) à la définition du produit. Le produit est responsable de la cohérence de ses membres de données. Un bon design est (à peu près dit) si le code fonctionne de la même manière que nos pensées.

Tellement de propriétés. Mais javascript est différent de pures langages orientés objet tels que C# et les codes les caractéristiques différemment:

En C#, transformant les champs en propriétés est un breaking change, si les champs publics doivent être codés comme Auto-Implemented Properties si votre code peut être utilisé dans client compilé séparément.

En Javascript, les propriétés standard (membre de données avec getter et setter décrit ci-dessus) sont définis par descripteur accesseur (dans le lien que vous avez dans votre question). En exclusivité, vous pouvez utiliser descripteur de données (vous ne pouvez donc pas utiliser i.e.valeur et mis sur la même propriété):

  • descripteur accesseur = get + set (voir l'exemple ci-dessus)
    • obtenir doit être une fonction; sa valeur de retour est utilisée pour lire la propriété; si non spécifié, la valeur par défaut est indéfini, qui se comporte comme une fonction qui renvoie undefined
    • mis doit être une fonction; son paramètre est rempli avec RHS en assignant une valeur à la propriété; si non spécifié, la valeur par défaut est undefined, qui se comporte comme une fonction vide
  • descripteur de données = valeur + inscriptible (voir l'exemple ci-dessous)
    • valeur défaut undefined; si inscriptible, configurable et dénombrable (voir ci-dessous) sont vraies, la propriété se comporte comme un champ de données ordinaires
    • inscriptible - par défaut faux; sinon true, la propriété est en lecture seule; tentative d'écriture est ignorée sans erreur *!

Les deux descripteurs peuvent avoir ces membres:

  • configurables - par défaut faux; si ce n'est pas vrai, la propriété ne peut pas être supprimée; tentative de suppression est ignorée sans erreur *!
  • dénombrable - par défaut faux; si vrai, il sera itéré dans for(var i in theObject); si elle est fausse, il ne sera pas réitérée, mais il est encore accessible du public

*, sauf dans strict mode - dans ce cas, JS arrête l'exécution avec TypeError moins qu'il ne soit pris dans try-catch block

Pour lire ces paramètres, utilisez Object.getOwnPropertyDescriptor().

savoir par exemple:

var o = {}; 
Object.defineProperty(o,"test",{ 
    value: "a", 
    configurable: true 
}); 
console.log(Object.getOwnPropertyDescriptor(o,"test")); // check the settings  

for(var i in o) console.log(o[i]); // nothing, o.test is not enumerable 
console.log(o.test); // "a" 
o.test = "b"; // o.test is still "a", (is not writable, no error) 
delete(o.test); // bye bye, o.test (was configurable) 
o.test = "b"; // o.test is "b" 
for(var i in o) console.log(o[i]); // "b", default fields are enumerable 

Si vous ne souhaitez pas autoriser le code client de ces tricheurs, vous pouvez restreindre l'objet de trois niveaux de confinement:

  • Object.preventExtensions(yourObject) Prévient nouvelles propriétés à ajouter à yourObject. Utilisez Object.isExtensible(<yourObject>) pour vérifier si la méthode a été utilisée sur l'objet. La prévention est peu profonde (lire ci-dessous).
  • Object.seal(yourObject) comme ci-dessus et les propriétés ne peuvent pas être supprimées (définit effectivement configurable: false à toutes les propriétés). Utilisez Object.isSealed(<yourObject>) pour détecter cette fonctionnalité sur l'objet. Le sceau est peu profond (lire ci-dessous).
  • Object.freeze(yourObject) comme ci-dessus et les propriétés ne peuvent pas être modifiées (en fait writable: false à toutes les propriétés avec un descripteur de données). La propriété d'écriture de Setter n'est pas affectée (puisqu'elle n'en a pas). Le gel est peu profond: cela signifie que si la propriété est Objet, ses propriétés ne sont pas gelées (si vous le souhaitez, vous devriez effectuer quelque chose comme "deep freeze", similaire à deep copy - cloning). Utilisez Object.isFrozen(<yourObject>) pour le détecter.

Vous n'avez pas besoin de déranger avec ceci si vous écrivez juste quelques lignes amusantes. Mais si vous voulez coder un jeu (comme vous l'avez mentionné dans la question liée), vous devriez vraiment vous préoccuper du bon design. Essayez de google quelque chose à propos de antipatterns et odeur de code. Cela vous aidera à éviter des situations comme "Oh, j'ai besoin de réécrire complètement mon code!", il peut vous faire économiser des mois de désespoir si vous voulez beaucoup coder. Bonne chance.

21

get est une fonction qui est appelée lorsque vous essayez de lire la valeur player.health, comme dans:

console.log(player.health); 

Il est effectivement pas très différent:

player.getHealth = function(){ 
    return 10 + this.level*15; 
} 
console.log(player.getHealth()); 

Le contraire de get est réglé , qui serait utilisé lorsque vous affectez à la valeur. Comme il n'y a pas de définition, il semble que l'attribution à la santé du joueur ne vise pas:

player.health = 5; // Doesn't do anything, since there is no set function defined 

Un exemple très simple:

var player = { 
 
    level: 5 
 
}; 
 

 
Object.defineProperty(player, "health", { 
 
    get: function() { 
 
    return 10 + (player.level * 15); 
 
    } 
 
}); 
 

 
console.log(player.health); // 85 
 
player.level++; 
 
console.log(player.health); // 100 
 

 
player.health = 5; // Does nothing 
 
console.log(player.health); // 100

+0

c'est juste comme une fonction que vous n'avez pas besoin d'utiliser réellement '()' pour appeler ... Je ne comprends pas quelle était l'idée quand ils ont inventé cette chose. Les fonctions sont totalement les mêmes: https://jsbin.com/bugipi/edit?js,console,output – vsync

+0

Comment puis-je choisir une réponse ??? –

2

Fondamentalement, defineProperty est une méthode qui prend en 3 paramètres - un objet, une propriété et un descripteur. Ce qui se passe dans cet appel particulier, c'est que la propriété "health" de l'objet player est affectée à 10 plus 15 fois le niveau de cet objet joueur.

0

oui non plus pour fonction extension setter de configuration & getter ceci est mon exemple Object.defineProperty (obj, nom, func)

var obj = {}; 
['data', 'name'].forEach(function(name) { 
    Object.defineProperty(obj, name, { 
     get : function() { 
      return 'setter & getter'; 
     } 
    }); 
}); 


console.log(obj.data); 
console.log(obj.name); 
0

Object.defineProperty() est un function..Its mondial non disponible à l'intérieur de la fonction qui déclare l'objet autrement.Vous devrez l'utiliser statiquement ...

7

defineProperty est une méthode sur Object qui vous permet de configurer les propriétés pour répondre à certains critères s. Voici un exemple simple avec un objet employé avec deux propriétés firstName & lastName et ajouter les deux propriétés en remplaçant la méthode toString sur l'objet.

var employee = { 
    firstName: "Jameel", 
    lastName: "Moideen" 
}; 
employee.toString=function() { 
    return this.firstName + " " + this.lastName; 
}; 
console.log(employee.toString()); 

Vous obtiendrez sortie comme: Jameel Moideen

Je vais changer le même code en utilisant DefineProperty sur l'objet

var employee = { 
    firstName: "Jameel", 
    lastName: "Moideen" 
}; 
Object.defineProperty(employee, 'toString', { 
    value: function() { 
     return this.firstName + " " + this.lastName; 
    }, 
    writable: true, 
    enumerable: true, 
    configurable: true 
}); 
console.log(employee.toString()); 

Le premier paramètre est le nom du objet puis second paramètre est le nom de la propriété que nous ajoutons, dans notre cas c'est toString et ensuite le dernier paramètre est json objet qui a une valeur va être une fonction et trois paramètres writable, énumérable et co nfigurable.Right maintenant je viens de déclarer tout comme vrai.

Si u exécuter l'exemple, vous obtiendrez comme sortie: Jameel Moideen

Comprenons pourquoi nous avons besoin des trois propriétés telles que inscriptible, dénombrable et configurable. inscriptible Une de la partie très ennuyeux du javascript, si vous modifiez la propriété toString à quelque chose d'autre par exemple enter image description here

si elle est exécutée cette fois, tout se brise Changeons inscriptible à false. Si vous recommencez la même opération, vous obtiendrez la bonne sortie en tant que 'Jameel Moideen'. Cette propriété empêchera l'écrasement de cette propriété plus tard. enumerable Si vous imprimez toutes les clés à l'intérieur de l'objet, vous pouvez voir toutes les propriétés, y compris toString.

console.log(Object.keys(employee)); 

enter image description here

si vous définissez dénombrable false, vous pouvez masquer toString propriété de tout le monde. Si exécuter ce nouveau vous obtiendrez firstName, lastName configurable

si quelqu'un plus tard redéfinie l'objet sur plus tard, par exemple dénombrable true et l'exécuter. Vous pouvez voir la propriété toString est revenue.

var employee = { 
    firstName: "Jameel", 
    lastName: "Moideen" 
}; 
Object.defineProperty(employee, 'toString', { 
    value: function() { 
     return this.firstName + " " + this.lastName; 
    }, 
    writable: false, 
    enumerable: false, 
    configurable: true 
}); 

//change enumerable to false 
Object.defineProperty(employee, 'toString', { 

    enumerable: true 
}); 
employee.toString="changed"; 
console.log(Object.keys(employee)); 

enter image description here

vous pouvez limiter ce comportement par un jeu configurable à false.

Orginal reference of this information is from my personal Blog

+1

Je comprends que vous avez eu ceci sur votre blog et juste collé ici, mais au moins le savoir pour l'avenir: screencaps ne sont pas populaires sur SO. Vous ne pouvez pas copier le code pour l'essayer et le code ne sera pas vu par les moteurs de recherche ou la technologie d'assistance. –

+0

@JacqueGoupil Vous avez raison.je mettrai à jour par ajout de code au lieu de capture d'écran – JEMI

Questions connexes