come utilizzare javascript Object.defineProperty


183

Ho cercato in giro come usare il Object.definePropertymetodo, ma non sono riuscito a trovare nulla di decente.

Qualcuno mi ha dato questo frammento di codice :

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

Ma non lo capisco. Principalmente, getè quello che non riesco a ottenere (gioco di parole intenzionale). Come funziona?


Risposte:


499

Dato che hai posto una domanda simile , facciamo un passo alla volta. È un po 'più lungo, ma potrebbe farti risparmiare molto più tempo di quanto abbia speso per scrivere questo:

La proprietà è una funzionalità OOP progettata per una netta separazione del codice client. Ad esempio, in alcuni e-shop potresti avere oggetti come questo:

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}

Quindi nel tuo codice cliente (l'e-shop), puoi aggiungere sconti ai tuoi prodotti:

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

In seguito, il proprietario dell'e-shop potrebbe rendersi conto che lo sconto non può essere superiore all'80%. Ora è necessario trovare OGNI occorrenza della modifica dello sconto nel codice client e aggiungere una riga

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

Quindi il proprietario dell'e-shop può cambiare ulteriormente la sua strategia, ad esempio "se il cliente è rivenditore, lo sconto massimo può essere del 90%" . Ed è necessario apportare nuovamente la modifica su più posizioni, inoltre è necessario ricordare di modificare queste righe ogni volta che la strategia viene modificata. Questo è un cattivo design. Ecco perché l' incapsulamento è il principio base di OOP. Se il costruttore fosse così:

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; } 
}

Quindi puoi semplicemente modificare i metodi getDiscount( accessor ) e setDiscount( mutator ). Il problema è che la maggior parte dei membri si comporta come variabili comuni, solo lo sconto ha bisogno di cure speciali qui. Ma una buona progettazione richiede l'incapsulamento di ogni membro di dati per mantenere il codice estensibile. Quindi è necessario aggiungere un sacco di codice che non fa nulla. Anche questo è un pessimo design, un antipasto a base di caldaia . A volte non è possibile semplicemente convertire i campi in metodi in un secondo momento (il codice eshop potrebbe ingrandirsi o alcuni codici di terze parti potrebbero dipendere dalla versione precedente), quindi la piastra della caldaia è meno male qui. Tuttavia, è malvagio. Ecco perché le proprietà sono state introdotte in molte lingue. È possibile mantenere il codice originale, semplicemente trasformare il membro dello sconto in una proprietà congete setblocchi:

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

Nota l'ultima ma una riga: la responsabilità per il valore di sconto corretto è stata spostata dal codice cliente (definizione e-shop) alla definizione del prodotto. Il prodotto è responsabile di mantenere coerenti i propri membri dei dati. Un buon design è (approssimativamente detto) se il codice funziona allo stesso modo dei nostri pensieri.

Tanto sulle proprietà. Ma JavaScript è diverso dai linguaggi puri orientati agli oggetti come C # e codifica le funzionalità in modo diverso:

In C # , la trasformazione dei campi in proprietà è una modifica sostanziale , pertanto i campi pubblici devono essere codificati come Proprietà implementate automaticamente se il codice potrebbe essere utilizzato in client compilati separatamente.

In Javascript , le proprietà standard (membro di dati con getter e setter descritti sopra) sono definite dal descrittore di accesso (nel collegamento presente nella domanda). Esclusivamente, è possibile utilizzare il descrittore di dati (quindi non è possibile utilizzare vale a dire valore e impostare sulla stessa proprietà):

  • descrittore accessor = get + set (vedere l'esempio sopra)
    • get deve essere una funzione; il suo valore di ritorno viene utilizzato nella lettura della proprietà; se non specificato, il valore predefinito non è definito , il che si comporta come una funzione che restituisce un valore non definito
    • set deve essere una funzione; il suo parametro è riempito con RHS nell'assegnare un valore alla proprietà; se non specificato, il valore predefinito non è definito , il che si comporta come una funzione vuota
  • descrittore dati = valore + scrivibile (vedere l'esempio seguente)
    • valore predefinito non definito ; se scrivibile , configurabile ed enumerabile (vedi sotto) sono vere, la proprietà si comporta come un normale campo dati
    • scrivibile - default false ; se non è vero , la proprietà è di sola lettura; il tentativo di scrivere viene ignorato senza errori *!

Entrambi i descrittori possono avere questi membri:

  • configurabile - default false ; se non è vero, la proprietà non può essere eliminata; il tentativo di eliminazione viene ignorato senza errori *!
  • enumerabile : impostazione predefinita false ; se vero, verrà ripetutofor(var i in theObject); se falso, non verrà ripetuto, ma sarà comunque accessibile come pubblico

* a meno che non sia in modalità rigorosa : in tal caso JS interrompe l'esecuzione con TypeError a meno che non venga rilevato nel blocco try-catch

Per leggere queste impostazioni, utilizzare Object.getOwnPropertyDescriptor().

Impara con l'esempio:

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

Se non si desidera consentire al codice client tali trucchi, è possibile limitare l'oggetto di tre livelli di confinamento:

  • Object.preventExtensions (yourObject) impedisce l'aggiunta di nuove proprietà a yourObject . UtilizzareObject.isExtensible(<yourObject>)per verificare se il metodo è stato utilizzato sull'oggetto. La prevenzione è superficiale (leggi sotto).
  • Object.seal (yourObject) come sopra e le proprietà non possono essere rimosse (impostate efficacementeconfigurable: falsesu tutte le proprietà). UtilizzareObject.isSealed(<yourObject>)per rilevare questa funzione sull'oggetto. Il sigillo è poco profondo (leggi sotto).
  • Object.freeze (yourObject) come sopra e le proprietà non possono essere modificate (imposta effettivamentewritable: falsetutte le proprietà con descrittore di dati). La proprietà scrivibile di Setter non è interessata (poiché non ne ha una). Il congelamento è superficiale : significa che se la proprietà è Object, le sue proprietà NON SONO bloccate (se lo si desidera, è necessario eseguire qualcosa di simile a "congelamento profondo", simile alla copia profonda - clonazione ). UtilizzareObject.isFrozen(<yourObject>)per rilevarlo.

Non devi preoccuparti di questo se scrivi solo poche righe divertenti. Ma se vuoi codificare un gioco (come hai detto nella domanda collegata), dovresti davvero preoccuparti di un buon design. Prova a google qualcosa su antipattern e odore di codice . Ti aiuterà a evitare situazioni come "Oh, devo riscrivere completamente il mio codice!" , può farti risparmiare mesi di disperazione se vuoi programmare molto. In bocca al lupo.


Questa parte è chiara. function Product(name,price) { this.name = name; this.price = price; var _discount; // private member Object.defineProperty(this,"discount",{ get: function() { return _discount; }, set: function(value) { _discount = value; if(_discount>80) _discount = 80; } }); } 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
abu abu,

27

getè una funzione che viene chiamata quando si tenta di leggere il valore player.health, come in:

console.log(player.health);

In realtà non è molto diverso da:

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

Viene impostato l'opposto di get, che verrà utilizzato quando si assegna al valore. Non essendoci setter, sembra che l'assegnazione alla salute del giocatore non sia intesa:

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

Un esempio molto semplice:

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


è proprio come una funzione che non è necessario utilizzare ()per chiamare ... Non capisco quale sia stata l'idea quando hanno inventato questa cosa. Le funzioni sono totalmente le stesse: jsbin.com/bugipi/edit?js,console,output
vsync

15

defineProperty è un metodo su Object che consente di configurare le proprietà per soddisfare alcuni criteri. Ecco un semplice esempio con un oggetto dipendente con due proprietà firstName e lastName e aggiungi le due proprietà sovrascrivendo il metodo toString sull'oggetto.

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

Otterrai Output come: Jameel Moideen

Ho intenzione di cambiare lo stesso codice usando defineProperty sull'oggetto

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());

Il primo parametro è il nome dell'oggetto e quindi il secondo parametro è il nome della proprietà che stiamo aggiungendo, nel nostro caso è toString e quindi l'ultimo parametro è un oggetto json che ha un valore che sarà una funzione e tre parametri scrivibili, enumerabili e configurabile. Proprio ora ho appena dichiarato tutto come vero.

Se esegui l'esempio otterrai Output come: Jameel Moideen

Capiamo perché abbiamo bisogno delle tre proprietà come scrivibili, enumerabili e configurabili.

scrivibile

Una delle parti molto fastidiose di javascript è, ad esempio se cambi la proprietà toString in qualcos'altro

inserisci qui la descrizione dell'immagine

se lo esegui di nuovo, tutto si rompe. Cambiamo scrivibile in falso. Se esegui di nuovo lo stesso, otterrai l'output corretto di "Jameel Moideen". Questa proprietà impedirà di sovrascriverla successivamente.

enumerabile

se si stampano tutte le chiavi all'interno dell'oggetto, è possibile visualizzare tutte le proprietà incluso toString.

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

inserisci qui la descrizione dell'immagine

se si imposta enumerable su false, è possibile nascondere la proprietà String a tutti gli altri. Se esegui di nuovo questo, otterrai firstName, lastName

configurabile

se in seguito qualcuno ha ridefinito l'oggetto in seguito, ad esempio enumerabile su true ed eseguirlo. Puoi vedere di nuovo la proprietà String.

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));

inserisci qui la descrizione dell'immagine

puoi limitare questo comportamento impostando configurabile su false.

Il riferimento originale di queste informazioni proviene dal mio blog personale


1
Ho capito che lo avevi sul tuo blog e l'hai appena incollato qui, ma almeno lo so per il futuro: gli screencaps non sono popolari su SO. Non è possibile copiare il codice per provarlo e il codice non verrà visualizzato dai motori di ricerca o dalla tecnologia assistiva.
Domino,

@JacqueGoupil Hai ragione. Aggiornerò aggiungendo il codice anziché lo screenshot
Code-EZ

3

Fondamentalmente, definePropertyè un metodo che accetta 3 parametri: un oggetto, una proprietà e un descrittore. Ciò che sta accadendo in questa particolare chiamata è che la "health"proprietà playerdell'oggetto viene assegnata a 10 più 15 volte il livello dell'oggetto del giocatore.


0

sì no più funzione che si estende per setup setter & getter questo è il mio esempio Object.defineProperty (obj, name, 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 () è una funzione globale ... Non è disponibile all'interno della funzione che dichiara l'oggetto altrimenti. Dovrai usarlo staticamente ...


0

Sommario:

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

Object.definePropertyviene utilizzato per creare una nuova proprietà sull'oggetto giocatore. Object.definePropertyè una funzione che è nativamente presente nell'ambiente di runtime JS e accetta i seguenti argomenti:

Object.defineProperty(obj, prop, descriptor)

  1. L' oggetto su cui vogliamo definire una nuova proprietà
  2. Il nome della nuova proprietà che vogliamo definire
  3. oggetto descrittore

L'oggetto descrittore è la parte interessante. Qui possiamo definire le seguenti cose:

  1. configurabile <boolean> : se true il descrittore di proprietà può essere modificato e la proprietà può essere eliminata dall'oggetto. Se configurabile è falsele proprietà del descrittore che vengono passate Object.definePropertynon possono essere modificate.
  2. Scrivibile <boolean> : se truela proprietà può essere sovrascritta utilizzando l'operatore di assegnazione.
  3. Enumerabile <boolean> : se true la proprietà può essere ripetuta in un for...inciclo. Inoltre quando si utilizza la Object.keysfunzione sarà presente il tasto. Se la proprietà è false, non verranno ripetuti utilizzando un for..inciclo e non verranno visualizzati durante l'utilizzoObject.keys .
  4. get <function> : una funzione che viene chiamata ogni volta che è richiesta la proprietà. Invece di dare il valore diretto questa funzione viene chiamata e il valore restituito viene dato come valore della proprietà
  5. set <function> : viene assegnata una funzione che viene chiamata ogni volta che viene assegnata la proprietà. Invece di impostare il valore diretto, questa funzione viene chiamata e il valore restituito viene utilizzato per impostare il valore della proprietà.

Esempio:

const player = {
  level: 10
};

Object.defineProperty(player, "health", {
  configurable: true,
  enumerable: false,
  get: function() {
    console.log('Inside the get function');
    return 10 + (player.level * 15);
  }
});

console.log(player.health);
// the get function is called and the return value is returned as a value

for (let prop in player) {
  console.log(prop);
  // only prop is logged here, health is not logged because is not an iterable property.
  // This is because we set the enumerable to false when defining the property
}


0

import { CSSProperties } from 'react'
import { BLACK, BLUE, GREY_DARK, WHITE } from '../colours'

export const COLOR_ACCENT = BLUE
export const COLOR_DEFAULT = BLACK
export const FAMILY = "'Segoe UI', sans-serif"
export const SIZE_LARGE = '26px'
export const SIZE_MEDIUM = '20px'
export const WEIGHT = 400

type Font = {
  color: string,
  size: string,
  accent: Font,
  default: Font,
  light: Font,
  neutral: Font,
  xsmall: Font,
  small: Font,
  medium: Font,
  large: Font,
  xlarge: Font,
  xxlarge: Font
} & (() => CSSProperties)

function font (this: Font): CSSProperties {
  const css = {
    color: this.color,
    fontFamily: FAMILY,
    fontSize: this.size,
    fontWeight: WEIGHT
  }
  delete this.color
  delete this.size
  return css
}

const dp = (type: 'color' | 'size', name: string, value: string) => {
  Object.defineProperty(font, name, { get () {
    this[type] = value
    return this
  }})
}

dp('color', 'accent', COLOR_ACCENT)
dp('color', 'default', COLOR_DEFAULT)
dp('color', 'light', COLOR_LIGHT)
dp('color', 'neutral', COLOR_NEUTRAL)
dp('size', 'xsmall', SIZE_XSMALL)
dp('size', 'small', SIZE_SMALL)
dp('size', 'medium', SIZE_MEDIUM)

export default font as Font


0

Definisce una nuova proprietà direttamente su un oggetto o modifica una proprietà esistente su un oggetto e restituisce l'oggetto.

Nota: questo metodo viene chiamato direttamente sul costruttore Object anziché su un'istanza di tipo Object.

   const object1 = {};
   Object.defineProperty(object1, 'property1', {
      value: 42,
      writable: false, //If its false can't modify value using equal symbol
      enumerable: false, // If its false can't able to get value in Object.keys and for in loop
      configurable: false //if its false, can't able to modify value using defineproperty while writable in false
   });

inserisci qui la descrizione dell'immagine

Spiegazione semplice su define Property.

Codice di esempio: https://jsfiddle.net/manoj_antony32/pu5n61fs/


0

Object.defineProperty(Array.prototype, "last", {
  get: function() {
    if (this[this.length -1] == undefined) { return [] }
    else { return this[this.length -1] }
  }
});

console.log([1,2,3,4].last) //returns 4

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.