ES6 (ECMAScript 2015) Haziran 2015’de yayınlanan 6. ECMAScript sürümüdür.

ES6 ile yeni neler geldi ?

ES6 bu yazı dizisindeki diğer sürümlere göre en fazla değişiklik gelen sürüm olma özelliğini taşıyor. Bu nedenle bu yazı biraz uzun oalabilir. 🙂 ES6 ile birlikte Javascript’e gelen yenilikler şöyle;

  • LET ve CONST anahtar kelimeleri
  • FOR..OF döngüsü
  • Varsayılan (Default) metot parametreleri
  • Rest/Spread operatörleri
  • Destructuring assignment
  • Template Literals/Strings
  • Arrow Functions
  • Promises
  • Classes
  • Modules
  • Generators
  • Map ve Set
  • Symbols
  • Unicode
  • Proxy
  • Reflection
  • Tail Call Optimization
  • Binary and Octal

Let & Const

ES5’e kadar javscriptde sadece global kapsam (scope) ve fonksiyon kapsamı söz konusuydu. ES6 ile birlikte blok kapsamı hayatımıza girdi.

var b=10;

function myFunction() {
  let c=20;
  console.log('değer 1:', c); //20
  if ( b === 10 && c==20) {
     let d=30;
     console.log("değer 2:",d); // 30
  }
  console.log("değer 1", c); // 20
  console.log("değer 2", d); // undefined
}
console.log("değer 1", c); // undefined
console.log("değer 3", b); // 10
myFunction();

const let ile benzer şekilde çalışıyor, fakat const ile tanımlanan bir sabite hemen aynı satırda bir değer atanması gerekiyor ve bu sabitin değeri kodun devmında değiştirilemiyor.

For…Of

for…of döngüsü for…in ve forEach’e alternatif olarak kullanılıyor. For…in döngüsünde array elemanın değeri değil indeksi size sunuluyor ve değere bu indeks ile ulaşmak zorunda kalıyorsunuz. forEach’de array’in hem indeksi hem değeri sunulurken, for…of döngüsünde sadece değeri sunuluyor.

// for kullanımı
for (let i = 0; i < arr.length; ++i) {
  console.log(arr[i]);
}

// for ... in kullanımı
for (let i in arr) {
  console.log(arr[i]);
}

// forEach kullanımı
arr.forEach((v, i) => console.log(v));

// for...of kullanımı
for (const v of arr) {
  console.log(v);
}

Rest Operatörü

Rest operatörü (...) belirtilmemiş diğer anahtarları aynen kopyalamaya yarar. Örnekle inceleyelim.

Bir fonksiyona gönderilen parametre adı öncesinde rest operatörü ( …. ) eklerseniz, bu parametre geri kalan tüm parametreleri bir array olarak alacaktır

function format(pattern, ...params) {
    return {pattern, params};
}
format(1, 2, 3);
    // { pattern: 1, params: [ 2, 3 ] }

Metodlar/Fonksiyonlar için varsayılan parametreler

Kendi adıma javascriptde eksikliği en çok hissettiğim ve kod hamallığı yaptırdığı için sinir olduğum bir eksiklik giderilmiş oldu. ES6 öncesinde fonksiyon tanımlanırken varsayılan parametre verilmiyordu ve bunu sağlamak için fonksiyon içerisinde koşullu ifade kullanarak “undefined” değerini istediğimiz varsayılan değere çeviriyorduk. ES6 ile birlikte bu sorun giderilmiş oldu;

function myFunction(x, y=1)
{
 console.log("x:", x");
 console.log("y":, y);
}

myFunction(5);   // x:5  y:1 sonucunu konsola basar
myFunction(5,3); // x:5  y:3 sonucunu konsola basar

Spread Operatörü

Spread operatörü, rest operatörü ile aynı sytax’e sahip. Aslına bakarsanız … operatörünün işlevi kullanıldığı yere göre değişiyor diyebiliriz.

… operatörü ile bir değişkene birden fazla değer gönderirseniz rest operatörü, birden fazla değişken gereken bir yerde bir array gönderirseniz spread operatörü olarak kullanmış olursunuz. Spread operatörüne örnek aşağıdaki gibidir.

let arr1 = [1,2,3];
let arr2 = [4,5];
let newArr = [...arr1, ...arr2]
console.log(newArr) // [1,2,3,4,5]
//veya
console.log( max(...arr1) ); // 3

Destructuring

Destructuring bir arrayin değerlerini değişkenlere atamayı sağlar. Örneğin;

const arr = [1,2];
const [x,y] = arr;
console.log("x: ", x, "y:", y);

Template Literals/Strings

Strtingler için gömülü ifadeler yerleştirmemize izin vererek çıktıları kolaylaştırır. Örneğin;

let a=1;
let b=2;
let c=3;
console.log('a: ' + a + ' b:' + b + ' c:' + c); // a: 1 b: 2 c: 3
console.log('a: ${a} b: ${b} c: ${c}' );        // a: 1 b: 2 c: 3

Arrow Functions

Arrow functions ES6 ile birlikte gelen en önemli değişiklik/eklemelerden biri. Arrow function’lar tek satırlık kodlardır ve Python ve Javadaki lambda fonksiyonlarına benzerler. => operatörü hayatımıza girmeden önce uzun uzun fonksiyon tanımlamamız gerekiyordu.

const val = (x, y) => { return  x * y };
console.log(val(3,5)) //35

Promises

Bir diğer önemli eklemede promise desteği oldu. Promise’ler sayesinde asenkron fonksiyonları daha kullanışlı bir şekilde tanımlayabilir hale geldik. Promise’ler olmadan önce aynı işlemler için callback fonksiyonlar tanımlanıp asenkron fonksiyona parametre olarak gönderiliyordu.

function asyncFunc() {
    return new Promise(
        function (resolve, reject) {
            ···
            resolve(result);
            ···
            reject(error);
        });
}

Asenktron fonksiyonları yukarıdaki gibi tanımlayabiliyoruz. Bu fonksiyonlar asenktron yanıtı bir promise üzerinden gönderiyorlar. Bu fonksiyon içerisinde sonuç veya hata döndürebiliyor, çağırıldığı yerde de buna göre kodlar çalıştırabiliyoruz. Şöyle ki;

asyncFunc()
.then(result => { // başarılı durumda çalışacak kod })
.catch(error => { // hata durumunda çalışacak kod });

Class’lar

Nesne Tabanlı Programla dillerinin neredeyse tamamında olan class anahtar kelimesi ES6’ya kadar javascript’de mevcut değildi. Nesne niteliklerini fonksiyon olarak tanımlayarak, object.prototype kullanarak bu yapıyı simule etmeye çalışıyorduk. Oluyor muydu, evet, ama ES6 ile birlikte bu resmi ve doğru düzgün bir çözüme kavuşmuş oldu.

A class and a subclass:

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return `(${this.x}, ${this.y})`;
    }
}

class ColorPoint extends Point {
    constructor(x, y, color) {
        super(x, y);
        this.color = color;
    }
    toString() {
        return super.toString() + ' in ' + this.color;
    }
}

const cp = new ColorPoint(25, 8, 'green');
cp.toString(); // '(25, 8) in green'
cp instanceof ColorPoint // true
> cp instanceof Point // true

Modüller

Uzun süre önce Javascript’de Modüller başlıklı bir yazı yazmıştım. Buraya sıkıştırmak yerine, ayıntılı bilgi için o yazıyı okumanızı tavsiye edeceğim.

Generatorler

Generatörleri istediğiniz zaman duraksatıp tekrar başlatabileceğiniz kod parçaları olarak düşünebilirsiniz. Bir generator fonksiyon tanımlamak için function anahtar kelimesinin hemen sonrasına bir * eklemeniz yeterli oluyor. Örneğin;

function* genFunc() {
    // A
    console.log('Birinci');
    yield;
    console.log('İkinci');
}

Bu fonksiyon içindeki yield anahtar kelimesi fonksiyonun kendisini durdurmasını sağlar. Fonksiyonlar yield ile veri alabilir ya da gönderebilir. (Go’da GoRoutine, C#’daki Corutines’e oldukça benziyor)

Bir generator fonksiyonunu çağırdığınızda bir generator nesnesi geri döner ve bu nesne ile süreci kontrol edebilirsiniz. Fonksiyon ilk satırda duraklamış olarak başlar, next() metodu çalıştırıldığında yield anahtar kelimesine kadar devam ederek tekrar duraklar.

const genObj = genFunc();

genObj.next();
// Birinci
genObj.next();
// İkinci

Generator tipleri

4 farklı yöntemle generator tanımlayabiliriz.

  1. Generator fonksiyon olarak tanımlamak
function* genFunc() { ··· }
const genObj = genFunc();

2. Generator fonksiyon ifadesi olarak bir sabit/değişkene atamak

 const genFunc = function* () { ··· };
 const genObj = genFunc();

3. Bir nesnenin metodu olarak tanımlamak

 const obj = {
     * generatorMethod() {
         ···
     }
 };
 const genObj = obj.generatorMethod();

4. Class metodu olarak tanımlamak

 class MyClass {
     * generatorMethod() {
         ···
     }
 }
 const myInst = new MyClass();
 const genObj = myInst.generatorMethod();

Map

Diğer dillerde de olan Map tipi anahtar:değer ikilisi (key-value pair) şeklinde değerler saklamamıza izin veriyor. Aynı şeyi array’de yapıyor, o zaman neden map’lere ihtiyacımız var. Map’ler kullanılacak anahtar bakımından sizi daha özgür kılıyor, indeks anahtarı olarak string, int ve hatta object kullanabilirsiniz. Get, set ve has metodları ile değerleri getirme, değiştirme ve varlığını kontrol etmek kolaylaşır.

> const map = new Map(); // Boş mir map oluşturalım.
> const KEY = {};

> map.set(KEY, 123);
> map.get(KEY) // 123
> map.has(KEY) // true
> map.delete(KEY); //true
> map.has(KEY) // false

// Map'i oluşturma esnasında değer atamak için mapi anahtar-değer şeklinde bir array göndererek oluşturabilirsiniz. 

const map = new Map([
    [ 1, 'bir' ],
    [ 2, 'iki' ],
    [ 3, 'üç' ]
]);

Set

Set tipi sıralanabilir olsa da indekslerini elde etmek veya indeksle bir elemanına ulaşmak mümkün değildir. Bu nedenle indexOf/find fonksiyonlarını kullanmadan önce array’e dönüştürmek gerekir.

const mySet = new Set(['1', '2', '3']);
[...mySet].indexOf('2') // 1 döndürür

const mySet = new Set([{1: 'bir'}, {2: 'iki'}, {3: 'üç'}]);
[...mySet].find(object => object[2] === 'iki'); // {2: 'iki'} döndürür

Symbols

Selman Samet’in Medium’da paylaştığı Türkçe kaynağı okuyarak semboller hakkında bilgi edinebilirsiniz.

Unicode

ES6 ile javascriptin unicode desteği güçlendi.

Proxy

ES6 ile gelen proxy nesnesi sayesinde nesneler için proxyler tanımlayabilir, o nesneye yönelik işlemleri engelleyebilir veya değiştirebilirsiniz. ( Bakınız Proxy Tasarım Deseni ) Proxy nesnesi parametre olarak hedef nesne ve bir işleyici alır.

const target = {
  message1: "merhaba",
  message2: "millet"
};

const handler1 = {
  get: function(target, prop, receiver) {
    return "dünya";
  }
};

const handler2 = {
  get: function (target, prop, receiver) {
    if (prop === "message2") {
      return "dünya";
    }
    return Reflect.get(...arguments);
  },
};

const proxy1 = new Proxy(target, handler1);
const proxy2 = new Proxy(target, handler2);

console.log(proxy1.message1); // dünya
console.log(proxy1.message2); // dünya

console.log(proxy2.message1); // merhaba
console.log(proxy2.message2); // dünya

Reflect

Proxy’e benzer şekilde müdahale edilebilir işlemler için yöntemler sağlayan Reflect nesnesi ES6 ile birlikte kullanılabilir oldu. Proxy’e göre daha geniş imkanlar sunan Reflect API çalışma anında değiştirilebilen dinamik kodlar oluşturmanıza olanak sağlıyor. Ayrı bir yazıda inceleyeceğim Reflect API ile ilgili yazıyı tamamladığımda bağlantıyı buraya bırakacağım.

Tail Call Optimization (TCO)

ES6, çağrı yığınını (call stack) büyütmeden belirli fonksiyon çağırılarını yapmanızı sağlayan “tail call optimization” özelliğini getiriyor. Öte yandan TCO ECMaScript standartının bir parçası olsa da çoğu javascript motoru tarafından desteklenmiyor. İşin ilginci standartlara uyum konusunda diğerleri kadar istekli olmayan Safari TCO’yu destekliyor. 🙂

Yine de TCO’nun ne olduğunu ve çalışma mantığını öğrenmek isterrseniz Dr. Axel Rauschmayer’in şu blog postundan (İngilizce) faydalanabilirsiniz.
Binary Literals & Octal Literals

Octal ve Binary Literals kullanımları yaygın olmadığı için burada değinmeyeceğim, ihtiyacınız olursa yorumlara yazmanızı rica ederim.

Bu Yazıda Yapılan Değişiklikler
  • 11.05.2022: Yazı özeti düzenlendi. İç yazılara linkleme yapıldı.