Javascript ES5 ve ES6 Nesne Yönemli Programlama Temel farklılıkları
Javascript ES5 ve ES6 Nesne Yönemli Programlama Temel farklılıkları

Giriş
Merhabalar bu yazımda sizlere Javascript’te ES5 ve ES6 standartlarında Nesne Yönelimli Programlama (OOP) nasıl uygulanır örnekler ile elimden geldiğince anlatmaya çalışacağım.
Bildiğiniz gibi OOP gerçek hayattaki somut nesne-özellik kavramını soyutlayan ve sanal ortamda kullanmamıza olanak sağlayan bir “Programlama Paradigması”. Javascript’in ES6’dan önce OOP desteği olmasına nazaran kalıplaşmış olan genel(Class, construct) yapıda değildi. Konu içerisinde iki standartta da nasıl kullanıldığını göreceğiz.
Neden Nesne Yapıcı Metodu Kullanmamız Lazım ?
Öncelik olarak Javascript’te neredeyse herşeyin bir nesne olduğunu unutmamanız gerekmekte, bu konuda bunlara detaylı bir şekilde değinmeyeceğim .
Javascript’te direkt olarak “literal” dediğimiz belirteçler ile belirtme yöntemi sayesinde direkt olarak bir javascript nesnesi oluşturabiliyoruz,
const kullanici = {
ad: 'Yilmaz',
soyad: 'Kadan',
telefon: '999 999 999',
okul: 'Düzce Üniversitesi'
}
Peki , direkt bir nesne oluşturabiliyorsak neden yapıcı metoda ihtiyaç duyarız ? bunun cevabını verelim:
Biliyorsunuz ki OOP veri tekrarını önler ve çıkış nedeni de kod karmaşıklığını minimuma indirmektir, OOP olmadan ve OOP kullanarak aynı örneği işleyelim:
Göstereceğim örnekte 3 adet aynı anahtar alanlara sahip Javascript nesnesi oluşturalım:
const araba1 = {
marka: "Audi",
model: "A8",
yakitturu: "Benzin"
}
const araba2 = {
marka: "BMW",
model: "520i",
yakitturu: "Benzin"
}
const araba3 = {
marka: "Mercedes",
model: "S400d",
yakitturu: "Benzin"
}
Gördüğünüz herhangi bir sınıfa bağlı kalmadan direkt olarak 3 farklı nesne oluşturabildik , peki bu nesnelerde farkettiğiniz bir olay var mı ? tabii ki özellik alanlarının ortak olduğunu gördünüz ve bunun temiz kod(Clean Code)’a aykırı olduğunu gördünüz peki bunu nasıl daha düzenli , yönetilebilir ve kullanışlı hale getirebiliriz tabii ki yapıcı(Constructor) metod sayesinde. Aşağıdaki örneği inceleyelim.
// ES5 standartında nesne yapıcı metodu
function Araba(marka, model , yakitturu){
this.marka = marka;
this.model = model;
this.yakitturu = yakitturu;
}
// Araba yapıcı metodundan instance(Örnek-nesne) oluşturma
const araba1 = new Araba("Audi","A8","Benzin");
const araba2 = new Araba("BMW","520i","Benzin");
const araba3 = new Araba("Mercedes","S400d","Benzin");
Gördüğünüz gibi bir yapıcı metod sayesinde özellikleri bir kez tanımladık ve yapıcı metodun parametreleri sayesinde direkt olarak atama işlemini yaptık, bu sayede kod tekrarını önlemiş ve daha kullanılabilir bu kod yapısı oluşturmuş olduk.
Bu nesnelere direkt erişim sağlarken “Literal” yöntem ile nasıl erişiyorsak aynı şekilde erişim sağlayabiliriz herhangi bir değişiklik yok. Örnek kullanımı aşağıdaki kod yapısında inceleyebilirsiniz.
// Oluşturulan nesnenin marka özelliğine erişir.
console.log(araba1.marka);
// Oluşturulan nesnenin model özelliğine erişir.
console.log(araba1.model);
Yapıcı metod gösteriminde kullanılan bazı anahtar kelimelerden bahsedeyim:
this anahtar sözcüğü : OOP hakkında daha önce bilgi sahibi iseniz bu anahtar sözcük size yabancı gelmeyecektir. Bu anahtar sözcük oluşturulan nesnenin bir referansını oluşturur ve metod içerisinden bu anahtar sözcük ile nesnenin özelliklerine erişim sağlayabilmekteyiz.
new anahtar sözcüğü : Bu anahtar sözcük ise yapıcı metotdan yeni bir nesne oluşturmak veya başlatmak için oluşturulmuş bir anahtar sözcüktür.
ES5 Ve ES6 Nesne Yapıcıları(Object Constructor)
- ES5
Prototip ve prototip Inheritance: ES5 standartında nasıl nesne yapıcısı kullanıldığını öğrenmiş olduk . Şimdi de ES5’de nasıl prototip kullanıldığını görelim.
Prototip nedir ?
Javascript’te tüm nesneler özelliklerini ve metotlarını Object sınıfının prototype isimli prototipinden almaktadır . Direkt olarak bir nesneye uygulandığı gibi kurucu bir metota da uygulanmaktadır . Benim burada işleyeceğim konu direkt olarak metoda nasıl uygulandığı konusu.const Araba = function(){ // Özellikler this.marka = "Audi"; this.yakitTuru = "Benzin"; this.hiz = "120"; // Metod this.hizliMi = function(){ if(this.hiz >= 120){ console.log("Bu araba hızlı bir araba"); }else{ console.log("Bu araba yavaş bir araba"); } } }
Yukarıdaki kodda gördüğünüz gibi yapıcı metot içerisinde özellik veya olarak yeni metodlar oluşturabiliyoruz ,
const Araba = function(){
// Özellikler
this.marka = "Audi";
this.yakitTuru = "Benzin";
this.hiz = "120";
}
// Prototip kullanımı
Araba.prototype.hizliMi = function (){
if(this.hiz >= 120){
console.log("Bu araba hızlıdır .");
}else{
console.log("Bu araba yavaşdır");
}
}
var araba1 = new Araba;
araba1.hizliMi();
Gördüğünüz gibi hangi yapıcı metoda yeni bir metot eklemek istiyorsak “.prototype” özelliğini kullanarak bir metot ekleyebiliyoruz.
Aynı zamanda prototype kullanarak yeni bir özellik de ekleyebiliriz.
Araba.prototype.renk = "Kırmızı";
Bu kod sayesinde Araba yapıcı metoduna yeni bir özellik daha eklemiş olduk.
Kısacası “Prototype” bize istediğimiz bir yapıcı metoda yeni özellikler ve metotlar eklememize olanak sağlar. Yapıcı metottan bağımısız bir şekilde yazılabiir olması kod okunabilirliği ve düzeni açısından bize kolaylık sağlar.
Peki neden direkt kurucu metot içerisinde fonksiyon tanımlayabiliyorken prototype kullanıyoruz ?
Kurucu metot içerisinde tanımladığımız tüm özellikler ve fonksiyonlar , o kurucu metottan türetilen nesneler için oluşturulur ve bellekte yer tutar ancak prototype olarak tanımladığımızda bu fonksiyonlar sadece referans olarak bellekte yer tutar ve sadece nesne üzerinden çağrıldığında kullanılır . Bu sayede bellek kullanımı minumuma iner.
ES5 Miras alma
ES5 standartında miras almak için fonksiyon nesnemizin _proto_ nesnesinde bulunan Call() fonksiyonunu kullanıyoruz. Örnekleyecek olursak.
const Araba = function(marka,yakitTuru,hiz){
// Özellikler
this.marka = marka;
this.yakitTuru = yakitTuru;
this.hiz = hiz;
this.markaYazdir = function (){
console.log(this.marka);
}
}
Yukarıdaki Araba metodunun markayazdir() fonksiyonunu ve diğer özellikleri başka bir yapıcı metotdan miras almak istiyorsak kullanım şöyle olacaktır.
const SporAraba = function(marka,yakitTuru,hiz){
Araba.call(this,marka,yakitTuru,hiz);
}
Yukarıda görüldüğü gibi SporAraba adında bir yapıcı metot daha oluşturduk ardından içerisinde miras almak istediğimiz bir başka yapıcı metot olan Araba sınıfının call metodunu çağırarak şu anki objemiz olan SporAraba metodunu “this” anahtar sözcüğü ile ilk parametrede belirtiyoruz. Ardından ana yapıcı metotta bulunan gerekli parametreleri sırası ile yolluyoruz.
Bu sayede artık SporAraba metodu Araba metodundan özellikleri ve metotları miras almış oldu.
var mercedes = new SporAraba("Mercedes","Dizel","10");
// Miras alınan metotun içerisinde bulunan metodları miras alma
mercedes.markaYazdir();
// Çıktı : Bu araba yavaştır.
Gördüğünüz gibi Araba kurucu metoduna ait bir fonksiyonu kullanabildik , ancak üst metoda yani Araba metoduna prototype olarak eklenen bir metodu aşağıdaki şekilde çağıramayız.
Araba.prototype.hizliMi = function (){
if(this.hiz >= 120){
console.log("Bu araba hızlıdır .");
}else{
console.log("Bu araba yavaşdır");
}
}
mercedes.hizliMi();
// Hata fırlatacaktır.
Bir prototip metodu veya özelliği miras olarak almak istiyorsak , miras alan metodumuza prototip eklememiz lazım ve bu prototip de Javascript içerisinde bulunan “Object” sınıfının create metodu ile yapılmaktadır .
Bu metod ile SporAraba kurucu metodumuza miras aldığımız Araba sınıfının prototiplerini yeni bir obje olarak atıyoruz.
Araba sınıfının prototiplerini listelemek istersek:
console.log(Araba.prototype);
Yazarak halihazırda içerisinde bulunan prototipleri görüntüleyebiliriz.
Biz de bu prototipleri miras alan metodumuza Object.create ile ekleyeceğiz.
İlgili kullanım:
// Miras alınacak sınıfın prototiplerini de miras alma
SporAraba.prototype = Object.create(Araba.prototype);
Bu kod bloğundan sonra artık miras aldığımız Araba sınıfının tüm prototiplerine erişim sağlayabileceğiz yani miras almış olacağız .
Bu işlemin ardından alttaki kod bloğunu çalıştıralım:
// Miras alınan metotun içerisinde bulunan prototip metodları miras alma
mercedes.hizliMi();
// Çıktı = Bu araba yavaştır.
ES6'DA OOP
ES6 standartı ile Javascript’e diğer gelişmiş programlama dillerinde olduğu gibi OOP desteği geldi . Java veya benzeri bir, OOP Paradigmasını kullanan programlama dilinde olduğu gibi Javascript’te de OOP kullanabiliyoruz .
ES6 ile sınıf oluşturma ve yapıcı metot kullanımı:
class Araba {
constructor(marka, yakitTuru, hiz) {
this.marka = marka;
this.yakitTuru = yakitTuru;
this.hiz = hiz;
}
}
Yukarıda gördüğünüz gibi diğer programlama dillerinde olduğu gibi class anahtar sözcüğü ile oluşturmak istediğimiz sınıfın adını yazıyoruz.
Ardından yapıcı metod(Constructor) oluşturuken constructor anahtar kelimesini kullanıyoruz ve içerisine kurucu metod ile beraber almak istediğimiz parametreleri giriyoruz.
ES6 ile metod oluşturma:
Javascript ES6 ile beraber OOP’de sınıf içerisinde bir metod oluşturacağımız zaman metodun başında "function” kullanmamanız gerektiğini unutmayın
class Araba {
constructor(marka, yakitTuru, hiz){
this.marka = marka;
this.yakitTuru = yakitTuru;
this.hiz = hiz;
}
// Metot kullanımı
markaYazdir(){
console.log(`Marka : ${this.marka}`)
}
}
var araba = new Araba("Audi","Uçak yakıtı","200");
araba.markaYazdir();
// Çıktı : Marka : Audi
ES6’da miras alma(Inheritance):
Extends anahtar kelimesi ile bir başka sınıftan miras alabilmekteyiz , burada Erişim Belirteçleri (Access Modifiers) ‘den bahsetmeyeceğim.
Örnek miras alma yapısı :
class SporAraba extends Araba{
}
SporAraba sınıfı extends anahtar kelimesi sayesinde Araba sınıfından miras aldı , bu miras bildiğiniz gibi public veya protected olan tüm sınıflara ve özelliklere erişim yapılabilir anlamına geliyor .
Örnek olarak üst sınıftan bir fonksiyona erişmek istiyorsak aşağıdaki gibi bir kullanım işimizi görecektir.
Araba sınıfında markaYazdir() adında bir metot olduğunu ve bu metoda SporAraba sınıfından türeyen bir nesneden nasıl erişebildiğimizi inceleyelim.
var sporAraba = new SporAraba("Tofaş","kolonya","400");
sporAraba.markaYazdir();
// Çıktı : Marka : Tofaş
ES6’da üst sınıfın kurucu metodunu kullanma (Super Metodu)
ES6’da miras almak istediğimiz kurucu metoda call fonksiyonu ile erişebiliyorduk yalnız burada direkt super fonksiyonu sayesinde erişim sağlayabiliyoruz.
Super Fonksiyonu: Miras alınan sınıftan bir metodu çağırmak veya direkt olarak üst sınıfın yapıcı metodunu çağırmak için kullanılır.
Örneğin:
class SporAraba extends Araba {
constructor(marka, yakitTuru, hiz) {
// Bu yapıcı metot ana sınıfın yapıcı metodunu ezer, veri tekrarı olmaması için super() metodu sayesinde gelen verileri tekrar üst sınıfın kurucu metodunda yolluyoruz.
super(marka, yakitTuru, hiz);
// Kurucu metoda istediğimiz farklı işlemler yaptırabiliririz Örn:
// Yeni bir özellik oluşturabiliriz ve özellik SporAraba sınıfına ait olur.
this.sinirliUretim = true;
}
}
var sporAraba = new SporAraba("Tofaş", "kolonya", "400");
console.log(sporAraba.sinirliUretim);
// Çıktı : true
Farklı bir metodun alt sınıfta çağrılması :
class SporAraba extends Araba {
arabaBilgiYazdir(){
// Ebeveyn sınıfın marka yazdıran metodunu çağırıyoruz.
super.markaYazdir();
// Diğer bilgileri de burada yazdırıyoruz.
console.log("Hız:"+ this.hiz + " " + this.yakitTuru);
}
}
var sporAraba = new SporAraba("Tofaş", "kolonya", "400");
sporAraba.arabaBilgiYazdir();
// Çıktı : Marka : Tofaş
Hız:400 kolonya
SON SÖZ
Evet , bir yazının daha sonuna geldik . Bu yazımda sizlere Javascript ES5 ve ES6’da OOP yaklaşımının nasıl kullanıldığı konusunda ve aralarında nasıl farklılıklar olduğunu kavramanızı amaçladım, ES6 OOP konusuna detaylı olarak değinmedim , konumuz farklar olduğu için gerekli konulara değindim.
Umarım aradığınızı bulmuşsunuzdur… Başka bir yazıda görüşmek üzere .