javascript Object.defineProperty nasıl kullanılır


183

Object.definePropertyYöntemin nasıl kullanılacağını araştırdım , ama iyi bir şey bulamadım.

Birisi bana bu kod snippet'ini verdi :

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

Ama anlamıyorum. Temelde, getne elde edemez (pun amaçlı). O nasıl çalışır?


Yanıtlar:


499

Eğer bir istediğinden beri benzer bir soru , en adım adım alalım. Biraz daha uzun, ama bunu yazmak için harcadığımdan çok daha fazla zaman kazandırabilir:

Özellik , istemci kodunun temiz bir şekilde ayrılması için tasarlanmış bir OOP özelliğidir. Örneğin, bazı e-mağazalarda bunun gibi nesneler olabilir:

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}

Ardından müşteri kodunuzda (e-mağaza) ürünlerinize indirim ekleyebilirsiniz:

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

Daha sonra, e-mağaza sahibi, indirimin% 80'den fazla olamayacağını fark edebilir. Şimdi, müşteri kodunda indirim değişikliğinin HERHANGİ bir oluşumunu bulmanız ve bir satır eklemeniz gerekir

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

Daha sonra e-mağaza sahibi, "müşteri bayiyse, maksimum indirim% 90 olabilir" gibi stratejisini daha fazla değiştirebilir . Değişikliği tekrar birden çok yerde yapmanız gerekiyor, ayrıca strateji her değiştiğinde bu satırları değiştirmeyi unutmamalısınız. Bu kötü bir tasarım. Bu yüzden kapsülleme OOP'nin temel prensibidir. Yapıcı şöyle olsaydı:

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

Ardından getDiscount( accessor ) ve setDiscount( mutator ) yöntemlerini değiştirebilirsiniz. Sorun şu ki, üyelerin çoğu ortak değişkenler gibi davranıyor, sadece indirim burada özel bir bakıma ihtiyaç duyuyor. Ancak iyi bir tasarım, kodu genişletilebilir tutmak için her veri üyesinin kapsüllenmesini gerektirir. Yani hiçbir şey yapmaz kod çok eklemeniz gerekir. Bu da kötü bir tasarım, bir kazan plakası antipatternidir . Bazen alanları daha sonra yöntemlere göre yeniden düzenleyemezsiniz (eshop kodu büyüyebilir veya bazı üçüncü taraf kodları eski sürüme bağlı olabilir), bu nedenle ısıtıcı levha burada daha az kötüdür. Ama yine de, şeytani. Bu yüzden özellikler birçok dile tanıtıldı. Orijinal kodu saklayabilirsiniz, sadece indirim üyesini bir mülke dönüştürüngetve setbloklar:

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

Son satırdan başka bir satıra dikkat edin: Doğru iskonto değerinin sorumluluğu müşteri kodundan (e-mağaza tanımı) ürün tanımına taşındı. Ürün, veri üyelerini tutarlı tutmaktan sorumludur. Kod düşüncelerimizle aynı şekilde çalışıyorsa iyi bir tasarım (kabaca söylenir).

Özellikleri hakkında çok fazla. Ancak javascript, C # gibi saf Nesne yönelimli dillerden farklıdır ve özellikleri farklı kodlar:

C # 'da , alanların özelliklere dönüştürülmesi son derece önemli bir değişiklik olduğundan, kodunuz ayrı olarak derlenmiş istemcide kullanılabiliyorsa, ortak alanların Otomatik Uygulanan Özellikler olarak kodlanması gerekir .

Javascript'te , standart özellikler (yukarıda açıklanan alıcı ve ayarlayıcıya sahip veri üyesi) erişimci tanımlayıcısı tarafından (sorunuzda bulunan bağlantıda) tanımlanır. Özel olarak, veri tanımlayıcıyı kullanabilirsiniz (böylece ie değerini kullanamazsınız ve aynı mülkte ayarlayamazsınız ):

  • accessor descriptor = get + set (yukarıdaki örneğe bakın)
    • get bir işlev olmalıdır; dönüş değeri özelliği okumada kullanılır; belirtilmezse, varsayılan tanımsızdır ve tanımsız döndüren bir işlev gibi davranır
    • set bir işlev olmalıdır; parametresi, özelliğe bir değer atamada RHS ile doldurulur; belirtilmezse, varsayılan tanımsızdır ve boş bir işlev gibi davranır
  • veri tanımlayıcı = değer + yazılabilir (aşağıdaki örneğe bakın)
    • değer varsayılan tanımsız ; eğer yazılabilir , yapılandırılabilir ve enumerable (aşağıya bakınız) doğruysa, sıradan bir veri alanına gibi mülkiyet davranacağını
    • yazılabilir - varsayılan yanlış ; doğru değilse, özellik salt okunurdur; yazma girişimi hatasız göz ardı edilir *!

Her iki tanımlayıcı da şu üyelere sahip olabilir:

  • yapılandırılabilir - varsayılan yanlış ; doğru değilse, özellik silinemez; silme girişimi hatasız göz ardı edilir *!
  • numaralandırılabilir - varsayılan yanlış ; doğruysa, tekrarlanacaktırfor(var i in theObject); yanlışsa, yinelenmez, ancak yine de herkese açık olarak erişilebilir

* katı modda olmadığı sürece - bu durumda JS, try-catch bloğuna yakalanmadığı sürece TypeError ile çalıştırmayı durdurur

Bu ayarları okumak için tuşunu kullanın Object.getOwnPropertyDescriptor().

Örnekle öğrenin:

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

İstemci kodunu bu tür hilelere izin vermek istemiyorsanız, nesneyi üç sınırlama ile sınırlayabilirsiniz:

  • Object.preventExtensions (yourObject) ,Object öğenize yeni özelliklerin eklenmesini engeller . Object.isExtensible(<yourObject>)Yöntemin nesnede kullanılıp kullanılmadığını kontrol etmek içinkullanın. Önleme sığdır (aşağıyı okuyun).
  • Object.seal (yourObject) yukarıdakiyle aynıdır ve özellikler kaldırılamaz (configurable: falsetüm özelliklereetkin bir şekilde ayarlanır). Object.isSealed(<yourObject>)Nesnedeki bu özelliği algılamak içinkullanın. Mühür sığdır (aşağıyı okuyun).
  • Object.freeze (yourObject) yukarıdakiyle aynıdır ve özellikler değiştirilemez (writable: falseveri tanımlayıcı ile tüm özelliklereetkili bir şekilde ayarlanır). Setter'ın yazılabilir özelliği etkilenmez (bir tane olmadığından). Donma sığdır : bu özellik Nesne ise, özelliklerinin dondurulmadığı anlamına gelir (isterseniz, derin kopya klonlamaya benzer şekilde "derin dondurma" gibi bir şey gerçekleştirmelisiniz). AlgılamakObject.isFrozen(<yourObject>)içinkullanın.

Sadece birkaç satır eğlenceli yazarsanız, bununla uğraşmanıza gerek yok. Ancak bir oyunu kodlamak istiyorsanız (bağlantılı soruda belirtildiği gibi), iyi tasarımı önemsemeniz gerekir. Antipatterns ve kod kokusu hakkında bir şeyler google deneyin . Bu gibi durumlardan kaçınmak için yardımcı olacaktır "Oh, tamamen yine benim kodu yeniden yazmak gerekiyor!" , çok kodlamak isterseniz aylarca umutsuzluğunuzu kurtarabilir. İyi şanslar.


Bu kısım açıktır. 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

getaşağıdaki player.healthgibi değeri okumaya çalıştığınızda çağrılan bir işlevdir :

console.log(player.health);

Etkili olarak çok farklı değil:

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

Get değerinin tersi, değere atandığınızda kullanılacak şekilde ayarlanır. Belirleyici olmadığından, oyuncunun sağlığına atanmanın amaçlanmadığı anlaşılmaktadır:

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

Çok basit bir örnek:

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


bu aslında ()aramak için kullanmanız gerekmeyen bir işlev gibi ... Bu şeyi icat ettiklerinde fikrin ne olduğunu anlamıyorum. İşlevler tamamen aynıdır: jsbin.com/bugipi/edit?js,console,output
vsync

15

defineProperty , Object üzerindeki özellikleri, bazı ölçütleri karşılayacak şekilde yapılandırmanıza izin veren bir yöntemdir. FirstName & lastName adlı iki özelliğe sahip bir çalışan nesnesiyle basit bir örnek ve nesne üzerindeki toString yöntemini geçersiz kılarak iki özelliği ekleyin .

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

Çıktıyı şu şekilde alırsınız: Jameel Moideen

Aynı kodu nesnede defineProperty kullanarak değiştireceğim

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

İlk parametre nesnenin adıdır ve daha sonra ikinci parametre eklediğimiz özelliğin adıdır, bizim durumumuzda toString'dir ve daha sonra son parametre, bir işlevi olacak bir değere ve üç parametreye yazılabilir, numaralandırılabilir json nesnesidir Şimdi her şeyi doğru olarak ilan ettim.

Eğer u örneğini çalıştırırsanız çıktıyı şöyle alırsınız: Jameel Moideen

Yazılabilir, numaralandırılabilir ve yapılandırılabilir gibi üç özelliğe neden ihtiyacımız olduğunu anlayalım.

yazılabilir

JavaScript'in çok can sıkıcı kısımlarından biri, toString özelliğini örneğin başka bir şeye değiştirirseniz

resim açıklamasını buraya girin

bunu tekrar çalıştırırsan, her şey kırılır. Yazılı olarak yanlış olarak değiştirelim. Aynı şekilde tekrar çalıştırırsanız, 'Jameel Moideen' olarak doğru çıktıyı alırsınız. Bu özellik, daha sonra bu özelliğin üzerine yazılmasını engelleyecektir.

sayılabilir

nesnenin içindeki tüm anahtarları yazdırırsanız, toString de dahil olmak üzere tüm özellikleri görebilirsiniz.

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

resim açıklamasını buraya girin

numaralandırılabilir değerini false olarak ayarlarsanız, toString özelliğini herkesten gizleyebilirsiniz. Bunu tekrar çalıştırırsanız firstName, lastName alırsınız

yapılandırılabilir

Birisi daha sonra nesneyi daha sonra yeniden tanımladıysa, örneğin doğru olarak numaralandırılabilir ve çalıştırılabilir. ToString özelliğinin tekrar geldiğini görebilirsiniz.

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

resim açıklamasını buraya girin

yapılandırılabilir değerini false olarak ayarlayarak bu davranışı kısıtlayabilirsiniz.

Bu bilgilerin orijinal referansı kişisel Blogumdan


1
Bunu blogunuzda bulduğunuzu ve buraya yapıştırdığınızı anlıyorum, ancak en azından bunu gelecek için biliyorum: screencaps SO'da popüler değil. Denemek için kodu kopyalayamazsınız ve kod arama motorları veya yardımcı teknoloji tarafından görülmez.
Domino

@JacqueGoupil Haklısın. Ekran görüntüsü yerine kod ekleyerek güncelleyeceğim
Code-EZ

3

Temel olarak, definePropertybir nesne, bir özellik ve bir tanımlayıcı olmak üzere 3 parametre alan bir yöntemdir. Bu özel çağrıda olan şey, nesnenin "health"özelliğinin playero oyuncu nesnesi seviyesinin 10 artı 15 katına atanmasıdır.


0

evet artık kurulum ayarlayıcı ve alıcı için işlev genişletme bu benim örneğim 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 () global bir işlevdir .. Nesneyi aksi bildiren işlev içinde kullanılamaz. Statik olarak kullanmanız gerekir ...


0

Özet:

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

Object.definePropertyplayer nesnesinde yeni bir özellik oluşturmak için kullanılır. Object.definePropertyJS çalışma zamanı ortamında yerel olarak bulunan ve aşağıdaki bağımsız değişkenleri alan bir işlevdir:

Object.defineProperty(obj, prop, descriptor)

  1. Üzerinde yeni bir özellik tanımlamak istediğimiz nesne
  2. Yeni özelliğin adı biz tanımlamak istediğiniz
  3. tanımlayıcı nesne

Tanımlayıcı nesne ilginç kısımdır. Burada aşağıdakileri tanımlayabiliriz:

  1. configurable <boolean> : true Özellik tanımlayıcı değiştirilebilirse ve özellik nesneden silinebilirse. Yapılandırılabilirse, falseaktarılan tanımlayıcı özellikler Object.definePropertydeğiştirilemez.
  2. Yazılabilir <boolean> : trueAtama işleci kullanılarak özelliğin üzerine yazılabilirse.
  3. Numaralandırılabilir <boolean> : true Özellik bir for...indöngü içinde yinelenebilirse . Ayrıca Object.keysişlevi kullanırken anahtar mevcut olacaktır. Özellik ise, falsebir for..indöngü kullanılarak yinelenmez ve kullanırken gösterilmez Object.keys.
  4. get <function> : Özellik her çağrıldığında çağrılan bir işlev gereklidir. Doğrudan değer vermek yerine bu fonksiyon çağrılır ve döndürülen değer özelliğin değeri olarak verilir
  5. set <function> : Özellik atandığında çağrılan bir işlev atanır. Doğrudan değeri ayarlamak yerine bu fonksiyon çağrılır ve döndürülen değer özelliğin değerini ayarlamak için kullanılır.

Misal:

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

Doğrudan bir nesne üzerinde yeni bir özellik tanımlar veya bir nesne üzerinde var olan bir özelliği değiştirir ve nesneyi döndürür.

Not: Bu yöntemi, Object türünün bir örneği yerine doğrudan Object yapıcısında çağırırsınız.

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

resim açıklamasını buraya girin

Tanımlama özelliği hakkında basit açıklama.

Örnek kod: 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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.