Buradaki birçok kişi nesne oluşturmak için en iyi yol olmadığını söylese de, 2019'dan itibaren JavaScript'te neden nesne oluşturmak için bu kadar çok yol olduğu konusunda bir mantık var ve bunun JavaScript'in farklı iterasyonlar üzerindeki ilerlemesi ile ilgisi var. 1997 yılına kadar uzanan EcmaScript sürümleri.
ECMAScript 5'ten önce, nesne oluşturmanın sadece iki yolu vardı: yapıcı işlevi veya değişmez gösterim (yeni Object () için daha iyi bir alternatif). Yapıcı işlev gösterimi ile, birden çok örneğe (yeni anahtar sözcükle) örneklenebilecek bir nesne oluştururken, değişmez gösterim bir singleton gibi tek bir nesne sunar.
// constructor function
function Person() {};
// literal notation
var Person = {};
Kullandığınız yönteme bakılmaksızın, JavaScript nesneleri yalnızca anahtar / değer çiftlerinin özellikleridir:
// Method 1: dot notation
obj.firstName = 'Bob';
// Method 2: bracket notation. With bracket notation, you can use invalid characters for a javascript identifier.
obj['lastName'] = 'Smith';
// Method 3: Object.defineProperty
Object.defineProperty(obj, 'firstName', {
value: 'Bob',
writable: true,
configurable: true,
enumerable: false
})
// Method 4: Object.defineProperties
Object.defineProperties(obj, {
firstName: {
value: 'Bob',
writable: true
},
lastName: {
value: 'Smith',
writable: false
}
});
JavaScript'in ilk sürümlerinde, sınıf tabanlı kalıtmayı taklit etmenin tek gerçek yolu yapıcı işlevlerini kullanmaktı. yapıcı işlevi, 'yeni' anahtar kelimeyle çağrılan özel bir işlevdir. Kural olarak, fonksiyon tanımlayıcısı büyük harfle yazılır, ancak zorunlu değildir. Yapıcı içinde, yapıcı işlevinin dolaylı olarak oluşturduğu nesneye özellikler eklemek için 'this' anahtar sözcüğüne başvururuz. Yapıcı işlevi, açıkça döndürülen anahtar sözcüğü ve başka bir şey döndürmediğiniz sürece, doldurulmuş özelliklere sahip yeni nesneyi dolaylı olarak arama işlevine dolaylı olarak döndürür.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.sayName = function(){
return "My name is " + this.firstName + " " + this.lastName;
}
}
var bob = new Person("Bob", "Smith");
bob instanceOf Person // true
SayName yöntemiyle ilgili bir sorun var. Genellikle, Nesneye Dayalı Sınıf tabanlı programlama dillerinde, nesneleri nesne oluşturmak için fabrika olarak kullanırsınız. Her nesnenin kendi örnek değişkenleri olacaktır, ancak sınıf planında tanımlanan yöntemlere bir işaretçi olacaktır. Ne yazık ki, JavaScript'in yapıcı işlevini kullanırken, her çağrıldığında, yeni oluşturulan nesne üzerinde yeni bir sayName özelliği tanımlayacaktır. Böylece her nesnenin kendine özgü sayName özelliği olacaktır. Bu, daha fazla bellek kaynağı tüketecektir.
Artırılmış bellek kaynaklarına ek olarak, yapıcı işlevinin içindeki yöntemleri tanımlamak, kalıtım olasılığını ortadan kaldırır. Yine, yöntem yeni oluşturulan nesnede bir özellik olarak tanımlanacak ve başka bir nesne olmayacak, bu nedenle miras böyle çalışamaz. Dolayısıyla JavaScript, prototip zincirini kalıtım biçimi olarak sunarak JavaScript'i prototip bir dil haline getirir.
Bir ebeveyniniz varsa ve bir ebeveyniniz bir çocuğun birçok özelliğini paylaşıyorsa, çocuk bu özellikleri miras almalıdır. ES5'ten önce şu şekilde gerçekleştirildi:
function Parent(eyeColor, hairColor) {
this.eyeColor = eyeColor;
this.hairColor = hairColor;
}
Parent.prototype.getEyeColor = function() {
console.log('has ' + this.eyeColor);
}
Parent.prototype.getHairColor = function() {
console.log('has ' + this.hairColor);
}
function Child(firstName, lastName) {
Parent.call(this, arguments[2], arguments[3]);
this.firstName = firstName;
this.lastName = lastName;
}
Child.prototype = Parent.prototype;
var child = new Child('Bob', 'Smith', 'blue', 'blonde');
child.getEyeColor(); // has blue eyes
child.getHairColor(); // has blonde hair
Yukarıdaki prototip zincirini kullanma şeklimizin bir tuhaflığı var. Prototip canlı bir bağlantı olduğundan, prototip zincirindeki bir nesnenin özelliğini değiştirerek, başka bir nesnenin aynı özelliğini de değiştirmiş olursunuz. Açıkçası, bir çocuğun kalıtsal yöntemini değiştirmek ebeveynin yöntemini değiştirmemelidir. Object.create, bu sorunu bir çoklu dolgu kullanarak çözdü. Böylece, Object.create ile, bir çocuğun prototip zincirindeki özelliğini, ebeveynin prototip zincirindeki aynı özelliğini etkilemeden güvenle değiştirebilirsiniz.
ECMAScript 5, nesne oluşturma için yapıcı işlevinde yukarıda belirtilen hatayı çözmek için Object.create öğesini tanıttı. Object.create () yöntemi, varolan bir nesneyi yeni oluşturulan nesnenin prototipi olarak kullanarak yeni bir nesne OLUŞTURUR. Yeni bir nesne oluşturulduğundan, artık prototip zincirindeki child özelliğinin değiştirilmesinin, üst öğenin zincirdeki o özelliğe başvurusunu değiştirmesi sorununuz yoktur.
var bobSmith = {
firstName: "Bob",
lastName: "Smith",
sayName: function(){
return "My name is " + this.firstName + " " + this.lastName;
}
}
var janeSmith = Object.create(bobSmith, {
firstName : { value: "Jane" }
})
console.log(bobSmith.sayName()); // My name is Bob Smith
console.log(janeSmith.sayName()); // My name is Jane Smith
janeSmith.__proto__ == bobSmith; // true
janeSmith instanceof bobSmith; // Uncaught TypeError: Right-hand side of 'instanceof' is not callable. Error occurs because bobSmith is not a constructor function.
ES6'dan önce, işlev yapıcılarını ve Object.create'i kullanmak için yaygın bir yaratıcı model vardı:
const View = function(element){
this.element = element;
}
View.prototype = {
getElement: function(){
this.element
}
}
const SubView = function(element){
View.call(this, element);
}
SubView.prototype = Object.create(View.prototype);
Şimdi yapıcı işlevleriyle birleştirilmiş Object.create, JavaScript'te nesne oluşturma ve kalıtım için yaygın olarak kullanılmaktadır. Bununla birlikte, ES6, JavaScript'in mevcut prototip tabanlı kalıtım üzerinde sözdizimsel şeker olan sınıflar kavramını tanıttı. Sınıf sözdizimi, JavaScript'e yeni bir nesne tabanlı miras modeli getirmez. Böylece, JavaScript prototip bir dil olarak kalır.
ES6 sınıfları kalıtımı çok daha kolay hale getirir. Artık üst sınıfın prototip işlevlerini el ile kopyalamamız ve alt sınıfın yapıcısını sıfırlamamız gerekmiyor.
// create parent class
class Person {
constructor (name) {
this.name = name;
}
}
// create child class and extend our parent class
class Boy extends Person {
constructor (name, color) {
// invoke our parent constructor function passing in any required parameters
super(name);
this.favoriteColor = color;
}
}
const boy = new Boy('bob', 'blue')
boy.favoriteColor; // blue
Sonuçta, JavaScript'te bu 5 farklı Nesne Oluşturma stratejisi EcmaScript standardının evrimine denk geldi.