Saltar al contenido

cómo usar javascript Object.defineProperty

Solución:

Ya que hiciste una pregunta similar, vayamos paso a paso. Es un poco más largo, pero puede ahorrarle mucho más tiempo del que he dedicado a escribir esto:

Propiedad es una función de programación orientada a objetos diseñada para una separación limpia del código del cliente. Por ejemplo, en alguna tienda electrónica puede tener objetos como este:

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}

Luego, en su código de cliente (la tienda electrónica), puede agregar descuentos a sus productos:

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

Más tarde, el propietario de la tienda electrónica podría darse cuenta de que el descuento no puede ser superior, digamos, al 80%. Ahora necesita encontrar CADA ocurrencia de la modificación del descuento en el código del cliente y agregar una línea

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

Entonces, el propietario de la tienda electrónica puede cambiar aún más su estrategia, como “si el cliente es revendedor, el descuento máximo puede ser del 90%”. Y debe realizar el cambio en varios lugares nuevamente, además debe recordar modificar estas líneas cada vez que se cambie la estrategia. Este es un mal diseño. Es por eso encapsulamiento es el principio básico de la programación orientada a objetos. Si el constructor era así:

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

Entonces puedes simplemente alterar el getDiscount (accesor) y setDiscount (mutador) métodos. El problema es que la mayoría de los miembros se comportan como variables comunes, solo el descuento necesita un cuidado especial aquí. Pero un buen diseño requiere la encapsulación de cada miembro de datos para mantener el código extensible. Entonces necesitas agregar mucho código que no hace nada. Este también es un mal diseño, un antipatrón repetitivo. A veces, no puede simplemente refactorizar los campos a métodos más adelante (el código de la eshop puede crecer mucho o algún código de terceros puede depender de la versión anterior), por lo que el texto estándar es menos malo aquí. Pero aún así, es malvado. Es por eso que las propiedades se introdujeron en muchos idiomas. Puede conservar el código original, simplemente transformar el miembro de descuento en una propiedad con get y set bloques:

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

Tenga en cuenta la última línea, pero una: la responsabilidad del valor de descuento correcto se trasladó del código de cliente (definición de tienda electrónica) a la definición de producto. El producto es responsable de mantener la coherencia de sus miembros de datos. Un buen diseño es (dicho más o menos) si el código funciona de la misma manera que nuestros pensamientos.

Mucho sobre propiedades. Pero javascript es diferente de los lenguajes orientados a objetos puros como C # y codifica las características de manera diferente:

C ª#, transformar campos en propiedades es un cambio importante, por lo que los campos públicos deben codificarse como propiedades implementadas automáticamente si su código puede usarse en el cliente compilado por separado.

En Javascript, las propiedades estándar (miembro de datos con getter y setter descritos anteriormente) están definidas por descriptor de acceso (en el enlace que tienes en tu pregunta). Exclusivamente, puedes usar descriptor de datos (entonces no puedes usar ie valor y colocar en la misma propiedad):

  • descriptor de acceso = get + set (vea el ejemplo anterior)

    • obtener debe ser una función; su valor de retorno se usa para leer la propiedad; si no se especifica, el valor predeterminado es indefinido, que se comporta como una función que devuelve undefined
    • colocar debe ser una función; su parámetro se llena con RHS al asignar un valor a la propiedad; si no se especifica, el valor predeterminado es indefinido, que se comporta como una función vacía
  • descriptor de datos = valor + escribible (vea el ejemplo a continuación)

    • valor defecto indefinido; si escribible, configurable y enumerable (ver más abajo) son verdaderas, la propiedad se comporta como un campo de datos ordinario
    • escribible – defecto falso; que no cierto, la propiedad es de solo lectura; el intento de escritura se ignora sin error *!

Ambos descriptores pueden tener estos miembros:

  • configurable – defecto falso; si no es cierto, la propiedad no se puede eliminar; el intento de eliminar se ignora sin error *!
  • enumerable – defecto falso; si es cierto, se repetirá en for(var i in theObject); si es falso, no se repetirá, pero aún es accesible como público

* a menos que esté en modo estricto; en ese caso, JS detiene la ejecución con TypeError a menos que esté atrapado en el bloque try-catch

Para leer estas configuraciones, use Object.getOwnPropertyDescriptor().

Aprenda con el ejemplo:

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 no desea permitirle al código del cliente tales trampas, puede restringir el objeto en tres niveles de confinamiento:

  • Object.preventExtensions (suObjeto) evita que se agreguen nuevas propiedades yourObject. Usar Object.isExtensible(<yourObject>) para comprobar si el método se utilizó en el objeto. La prevención es poco profundo (Lee abajo).
  • Object.seal (suObjeto) igual que el anterior y las propiedades no se pueden eliminar (efectivamente establece configurable: false a todas las propiedades). Usar Object.isSealed(<yourObject>) para detectar esta característica en el objeto. El sello es poco profundo (Lee abajo).
  • Object.freeze (yourObject) igual que arriba y las propiedades no se pueden cambiar (efectivamente establece writable: false a todas las propiedades con descriptor de datos). La propiedad de escritura de Setter no se ve afectada (ya que no la tiene). La helada es poco profundo: significa que si la propiedad es Object, sus propiedades NO ESTÁN congeladas (si lo desea, debe realizar algo como “deep freeze”, similar a la copia profunda – clonación). Usar Object.isFrozen(<yourObject>) para detectarlo.

No necesitas molestarte con esto si escribes solo unas pocas líneas divertidas. Pero si quieres codificar un juego (como mencionaste en la pregunta vinculada), debes preocuparte por un buen diseño. Intenta buscar en Google algo sobre antipatrones y olor a código. Te ayudará a evitar situaciones como “¡Oh, necesito volver a escribir mi código completamente!”, puede ahorrarle meses de desesperación si desea codificar mucho. Buena suerte.

get es una función que se llama cuando intenta leer el valor player.health, como en:

console.log(player.health);

Efectivamente, no es muy diferente a:

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

Se establece lo opuesto a get, que se usaría cuando asigna el valor. Dado que no hay un colocador, parece que no se pretende asignar a la salud del jugador:

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

Un ejemplo muy 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

defineProperty es un método en Object que le permite configurar las propiedades para cumplir con algunos criterios. Aquí hay un ejemplo simple con un objeto de empleado con dos propiedades firstName y lastName y agregue las dos propiedades anulando el Encadenar método en el objeto.

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

Obtendrá salida como: Jameel Moideen

Voy a cambiar el mismo código usando defineProperty en el objeto

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

El primer parámetro es el nombre del objeto y luego el segundo parámetro es el nombre de la propiedad que estamos agregando, en nuestro caso es toString y luego el último parámetro es el objeto json que tiene un valor que será una función y tres parámetros escribibles, enumerables y configurable En este momento acabo de declarar todo como verdadero.

Si ejecuta el ejemplo, obtendrá la salida como: Jameel Moideen

Entendamos por qué necesitamos las tres propiedades como escribible, enumerable y configurable.

escribible

Una de las partes más molestas de javascript es, si cambia la propiedad toString a otra cosa, por ejemplo

ingrese la descripción de la imagen aquí

si ejecuta esto de nuevo, todo se rompe. Cambiemos la capacidad de escritura a falsa. Si vuelve a ejecutar lo mismo, obtendrá la salida correcta como ‘Jameel Moideen’. Esta propiedad evitará que se sobrescriba esta propiedad más adelante.

enumerable

si imprime todas las claves dentro del objeto, puede ver todas las propiedades, incluido toString.

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

ingrese la descripción de la imagen aquí

si establece enumerable en falso, puede ocultar la propiedad toString de todos los demás. Si ejecuta esto de nuevo, obtendrá firstName, lastName

configurable

si alguien más tarde redefinió el objeto en later, por ejemplo, enumerable a true y lo ejecuta. Puede ver que la propiedad toString volvió a aparecer.

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

ingrese la descripción de la imagen aquí

puede restringir este comportamiento estableciendo configurable en falso.

La referencia original de esta información es de mi Blog personal.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *