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ünget
ve set
bloklar:
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ır
for(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: false
tü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: false
veri 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.