Neden yeniden tanışma? Çünkü JavaScriptdünyanın en yanlış anlaşılan programlama dili olma gibi bir üne sahip. Genellikle bir oyuncak olarak atfedilmesine karşın, aldatıcı sadeliğinin altında güçlü dil özellikleri barındırır. Bunlardan biri, inanılmaz sayıda yüksek profilli uygulama tarafından kullanılıyor olmasıdır. Bu da bize Javascript teknolojisine ait derin bir bilginin her web veya mobil geliştirici için önemli bir beceri olduğunu gösterir.

Dilin geçmişini anlatarak başlamak faydalı olacaktır. Javascript 1995 yılında Netscape'de bir mühendis olan Brendan Eich tarafından oluşturulmuştur ve Netscape 2 ile eş zamanlı olarak 1996 yılının başlarında kullanıma açılmıştır. Başta Livescript ismi ile çıkacakken -aralarında hemen hemen hiç benzerlik olmamasına rağmen- Sun Microsystem şirketine ait Java dilinin popülerliğini arttırmak adına talihsiz bir pazarlama seçimi ile Javascript ismi ile devam edilmiştir. Bu olay o zamandan beri karışıklıklığa neden olmaktadır.

Birkaç ay sonra Microsoft dilin çoğu platforma uyumlu bir versiyonu olan Jscript'i IE 3 ile çıkardı. Netscape, dili Avrupa standartlarında bir organizasyon olan Ecma International'a sundu ve bu da 1997'de ECMAScript standardının ilk versiyonu ile sonuçlandı. Standart 1999'da ECMAScript edition 3 olarak önemli bir güncellemeyi aldı ve o zamandan beri oldukça istikrarlı kaldı. Ancak dördüncü baskı, dil karmaşıklığı ile ilgili politik farklılıklar nedeniyle terk edildi. Dördüncü baskının pek çok bölümü, 2009 yılı Aralık ayında yayınlanan ECMAScript 5. versiyonunun  ve 2015 yılında yayınlanacak olan standartın 6. büyük versiyonunun temelini oluşturdu.

Daha aşina olduğumuz için bu yazı boyunca ECMAScript yerine Javascript ismi ile devam edeceğiz.

Çoğu programlama dilinin aksine, JavaScript dilinin hiçbir girdi veya çıktı kavramı yoktur. Bir ana bilgisayar ortamında bir betik dili olarak çalışacak şekilde tasarlanmıştır ve dış dünyayla iletişim kurmak için mekanizmalar sağlamak ana bilgisayar ortamına bağlıdır. En yaygın ana bilgisayar ortamı tarayıcıdır, ancak JavaScript yorumlayıcıları Adobe Acrobat, Photoshop, SVG görüntüleri, Yahoo!'nun Widget altyapısında ve node.js. gibi sunucu ortamlarında da bulunabilir. JavaScript'in kullanıldığı alanların listesi burada bitmez. Ayrıca, açık kaynaklı Apache CouchDB, gömülü bilgisayarlar veya GNOME (GNU / Linux işletim sistemleri için en popüler GUI'ler) gibi tam masaüstü ortamları gibi NoSQL veritabanlarını da içerir.

Genel Bakış

JavaScript türlere, operatörlere ve standart hazır nesnelere sahip nesneye yönelik, dinamik bir dildir. Sözdizim kuralları Java ve C dillerinden gelmektedir ve bu dillerden birçok yapıyı başarılı şekilde barındırır. Bu diller ile arasındaki önemli bir fark, JavaScript'in sınıflara (class) sahip olmamasıdır. JavaScript, sınıfların yaptığı işleri nesne prototiplerini (object prototype) kullanarak yapar. Bir diğer temel fark ise fonksiyonların birer nesne olmasıdır. Bu şekilde fonksiyonların da yürütülebilir kodu saklama ve diğer her nesne gibi aktarılma kapasitesi vardır.

Her dilin yapıtaşı sayılabilecek olan unsuru öğrenerek başlayalım: türler (types). JavaScript programları değerleri (value) manipüle eder ve bu değerlerin hepsi bir türe aittir. JavaScript'in türleri şunlardır:

... oh, ayrıca undefined ve null, ki ... biraz garipler. Ayrıca özel bir nesne kabul edebileceğimiz Array. Bir de bedava elde edebileceğiniz nesneler olan Date ve RegExp.  Teknik olarak fonksiyonlar da özel bir tür nesnedir. Bu nedenle asıl diyagramımız böyle görünmeli:

Ayrıca bazı gömme Error türlerinden de söz edebiliriz. Ancak ilk diyagramla hareket etmemiz bizim işimizi kolaylaştıracaktır.

Sayılar 

JavaScript'teki sayılar, spesifikasyonlara göre "çift duyarlıklı 64 bit format IEEE 754 değerleri" dir. Ancak bunun bazı ilginç sonuçları var. Örneğin JavaScript'te tamsayı gibi bir şey yoktur, bu yüzden eğer C veya Java'da kullanılan matematiğe alışkınsanız, aritmetiğinize biraz dikkat etmelisiniz.

Ayrıca aşağıdaki gibi olaylara dikkat edin:

0.1 + 0.2 == 0.30000000000000004

Pratikte tamsayı değerleri 32-bit int olarak sayılır (ve bazı tarayıcı uygulamalarında bu şekilde saklanır). Bu, bit-temelli eylemler gerçekleştirmek istediğinizde önemli bir ayrıntıdır.

Ekleme, çıkarma, modül (veya kalan) aritmetik ve benzeri dahil olmak üzere standart aritmetik operatörler desteklenir. Daha ileri matematiksel fonksiyonlar gerçekleştirmek istiyorsanız kullanmanız gereken gömme nesne Math'dir:

Math.sin(3.5);
var d = Math.PI * r * r;

parseInt() gömme fonksiyonunu kullanarak bir string'i tamsayıya çevirebilirsiniz. Bu fonksiyon isteğe bağlı ikinci bir argüman olarak sayı tabanını alır:

parseInt('123', 10); // 123
parseInt('010', 10); // 10

Eski tarayıcılarda "0" değeri ile başlayan stringler sekizli taban kabul ediliyordu. Eğer taban değerini yazmazsanız 2013 öncesi tarayıcılarda şaşırtıcı sonuçlar alabilirsiniz:

parseInt("010"); // 8
parseInt("0x10"); // 16

Yukarıda gördüğünüz hata, parseInt() fonksiyonu, yazılan string'i baştakı 0 nedeniyle sekizli taban cinsinden işlem yapması yüzünden gerçekleşti.

Bir sayıyı binary'den tamsayıya dönüştürmek isterseniz tek yapmanız gereken tabanı değiştirmektir:

parseInt("11", 2); // 3

Benzer şekilde float türü sayıları ayrıştırmak için parseFloat() gömülü fonksiyonunu kullanabilirsiniz. Bu fonksiyon, kuzeni sayabileceğimiz  parseInt() fonksiyonunun aksine 10 tabanlı sistemi kullanır.

Ayrıca +  operatörünü kullanarak değerleri sayıya çevirebilirsiniz:

+ '42'; //42 
+ '010'; //10
+ '0x10'; //16

String sayısal bir değer değil ise karşınıza NaN (Açılımı: "Not a Number"' = sayı değil) çıkar:

parseInt("hello", 10); // NaN

NaN toksik gibidir: eğer onu bir girdi olarak herhangi bir matematiksel operasyonda kullanırsanız sonuç yine NaN olacaktır:

NaN + 5; // NaN

NaN  isNaN() gömme fonksiyonu ile test edilebilir:

isNaN(NaN); // true

JavaScript ayrıca Infinity (sonsuz) ve-Infinity (- sonsuz) gibi özel değerlere sahiptir:

1 / 0; //  Infinity 
-1 / 0; // -Infinity

 Infinity, -Infinity ve NaN değerlerini gömme fonksiyon  isFinite() ile test edebilirsiniz: 

isFinite(1/0); // false
isFinite(-Infinite); //false
isFinite(NaN); // false
Not: parseInt() ve parseFloat() fonksiyonları, belirtilen sayı biçimi için geçerli olmayan bir karaktere ulaşıncaya kadar bir dizeyi ayrıştırır, ardından bu noktaya kadar ayrıştırılan sayıyı döndürür. Ancak "+" operatörü, içinde geçersiz bir karakter varsa dizeyi  NaN 'a dönüştürür. Bahsettiğimiz yöntemleri kullanarak"10.2abc" dizesini konsolda kendiniz ayrıştırmayı denerseniz farklarını daha iyi anlayabilirsiniz.

Stringler

JavaScript'teki stringler, karakter dizileridir. Daha doğrusu her karakter 16 bitlik bir sayı ile temsil edilen Unicode karakter dizisidir. Bu, uluslararasılaşma ile uğraşmak zorunda kalmış herkese iyi bir haber diyebiliriz.

Tek bir karakteri temsil etmek istiyorsanız, sadece 1 uzunluğunda bir string kullanmanız yeterlidir.

Bir string'in uzunluğunu bulmak istiyorsanız length özelliğini kullanabilirsiniz:

"merhaba".length; // 7

Böylelikle Javascript nesnelerine ilk adımımızı atmış olduk! Stringlerin de nesne olduğundan bahsetmiş miydik? Onların da stringleri manipüle etmek ve stringlerden bilgi almak için kullanılabilecek metodları vardır:

> 'merhaba'.charAt(0); // "m"
> 'merhaba, dünya'.replace('merhaba', 'elveda'); // "elveda, dünya"
> 'merhaba'.toUpperCase(); // MERHABA

Diğer Türler

JavaScript, bir değer sayılmayan (ve sadece null  anahtar kelimesi ile erişilebilen) null ve atanmamış değer olduğunu belirten undefined arasındaki farkı ayırt edebilir. Daha sonra değişkenlerden bahsedeceğiz, ancak şimdiden belirtmeliyiz ki JavaScript'te değer atamadan da değişken tanımlamak mümkündür. Bunu yaparsanız, değişkenin türü undefined olur, bu da bize undefined türünün aslında bir sabit olduğunu gösterir.

Javascript, true (doğru) ve false (yanlış) değerlerini alabilen bir boolean türüne sahiptir. Aşağıdaki kurallar dahilinde her değer boolean'e çevrilebilir:

  1.  false, 0, boş stringler (""), NaN, ve undefined değerlerinin tamamı false,
  2. Kalan tüm değerler true olur.

Bu dönüşümü, Boolean() fonksiyonunu kullanarak kolayca gerçekleştirebilirsiniz:

Boolean('');  // false
Boolean(234); // true

Ancak bu dönüşüm çoğu zaman gereksizdir çünkü Javascript bir boolean -bir if else ifadesi gibi (aşağıda görebilirsiniz)- beklediğinde bu dönüşümü sessizce gerçekleştirir. Bu nedenle "doğru değerler" ve "yanlış değerler"den bahsederken aslında  true ve false haline gelen değerlerden bahsediyoruz. Bu sizin true ve false değerleri yerine "truthy" ve "falsy" gibi sözcükler de kullanabileceğiniz anlamına gelir.

&& (ve), || (veya), ve ! (değil) gibi mantıksal boolean işlemleri desteklenmektedir; aşağıda daha fazla bilgi bulabilirsiniz.

Değişkenler

Javascript'de yeni değişkenler letconst, veya var anahtar kelimeleri ile tanımlanır.

let, blok-düzeyinde değişkenleri tanımlamaya yarar. Tanımlanan değişken sadece kapsadığı blok dahilinde geçerlidir.

let a;
let isim = "eren";

Aşağıda let ile tanımlanmış bir değişken örneği görebilirsiniz:

// letDegiskenim burada kullanılabilir *değil*

for (let letDegiskenim = 0; letDegiskenim < 5; letDegiskenim++) {
    // letDegiskenim sadece burada kullanılabilir
}

// letDegiskenim burada kullanılabilir *değil*

const ile ileride değiştirme amacınız olmayan değişkenler tanımlayabilirsiniz. Değişken sadece içinde tanımlandığı blokta kullanılabilir.

const Pi = 3.14;
Pi = 1; // const değişkenlerine yeni değerler atanamayacağı için hata verecektir.

var en çok kullanılan tanımlayıcı anahtar sözcüktür. let ve const'daki kısıtlamalar bu anahtar sözcükte geçerli değildir. Bunun nedeni Javascript'de geçmişten beri bir değişken tanımlamak için var kullanılmasıdır. Bu anahtar sözcükle tanımlanan değişkenler, tanımlandığı fonksiyon içerisinde kullanılabilir.

var a;
var isim = 'eren';

Aşağıda var ile tanımlanmış bir değişken örneği görebilirsiniz:

// varDegiskenim burada kullanılabilir 

for (let varDegiskenim = 0; varDegiskenim < 5; varDegiskenim++) {
    // varDegiskenim tüm fonksiyon için kullanılabilir
}

// varDegiskenim burada kullanılabilir

Eğer bir değişkeni değer atamadan tanımlarsanız, türü undefined olur.

Javascript ve Java gibi diller arasındaki önemli bir fark, Javascript'de blokların değil sadece fonksiyonların kapsama alanınından bahsedebilmemizdir. Yani bileşik bir ifade (örneğin bir if kontrol yapısı gibi) içerisinde var ile tanımlanan değişken, tüm fonksiyon içerisinde kullanılabilir. Ancak, ESMAScript 2015'den beri, let ve const tanımlayıcıları size blok-düzeyinde değişkenler oluşturmanıza yarar.

Operatörler

Javascript'de +, -, *, / ve % gibi sayısal operatörler bulunur. (%: "kalan" operatörüdür ve modülüs ile aynı değildir.). Değerler = ile atanır, ayrıca += ve -= gibi bileşke atama ifadeleri de kullanılabilir.

x += 5;
x = x + 5;

 ++ and -- operatörlerini sırasıyla arttırmak ve azaltmak için kullanabilirsiniz.  Bu operatörler, değerden önce veya sonra yazılabilir (++deger ve deger++ gibi).

+ operatorü stringleri birbirine bağlamak için de kullanılabilir:

> "merhaba" + " dünya"
hmerhaba dünya

Eğer bir sayıya (veya herhangi bir değere) string eklerseniz, her şey stringe dönüştürülür. Aşağıdaki örneğe bir göz atın:

> '3' + 4 + 5 // "345"
> 3 + 4 + '5' // "75"

Bir değere boş string ekleyerek onu stringe dönüştürmek pratik bir yoldur.

Javascript'de karşılaştırmalar <><= ve >= ile yapılır. Bu operatörleri hem string hem de sayı değerleri için kullanabilirsiniz. Eşitlik ise bundan biraz daha karmaşık bir konudur. Çift-eşitlik operatörü, aşağıda görebileceğiniz gibi farklı türleri karşılaştırdığınızda düşündüğünüzden farklı sonuçlar verebilir:

123 == '123' // true
1 == true;   // true

Farklı türlerle çalışırken bu tarz karışıklıklarlardan kaçınmak için üçlü-eşitlik operatörünü kullanın:

123 === '123'; // false
1 === true;   // false

Javascript'de ayrıca != ve !== operatörleri de vardır.

Eğer bit-dizeyinde işlemlerle ilgileniyorsanız buraya tıklayarak referans belgelerinden daha ayrıntılı bilgi alabilirsiniz.

Kontrol yapıları

Javascript, C ailesine benzer nitelikte kontrol yapılarına sahiptir. Koşullu ifadeler if ve else ile desteklenir; isterseniz onları aşağıda göreceğiniz gibi birlikte kullanabilirsiniz:

var isim = 'kediler';
if (isim  == 'köpekler') {
    isim += 'havlar';
} else if (isim == 'kediler') {
    isim += 'miyavlar';
} else {
    isim += '!';
}
isim == 'kediler miyavlar';

JavaScript'de iki tür döngü (loop) vardır: while ve do-while . İlki basit döngüler için idealken; ikincisi, döngünün gövdesinin en az bir kez yürütüldüğünden emin olmak istediğiniz döngüler içindir:

while (true) {
  // sonsuz döngü!
}

var input;
do {
  input = get_input();
} while (inputIsNotValid(input))

JavaScript'de for döngüsü aynı C ve Java'daki gibidir, döngünün kontrol bilgisini tek bir satıra yazmanıza olanak sağlar.

for (var i = 0; i < 5; i++) {
  // 5 kez yürütülecektir
}

JavaScript'de ayrıca döngüler için iki önemli ifade vardır: for...of

for (let value of aray) {
  // value değeri ile ilgili bir şey yap
}

ve for...in:

for (let property in object) {
  // object'e ait property değeri ile ilgili bir şey yap
}​​​

&& ve || kısa devre mantığı ile çalışır, ikinci işlenecek olan işlenen (operand), ilkine bağlıdır. Bu şekilde null nesnelerinin niteliklerine erişmeden önce kontrol etmek için kullanılabilir:

var name = o && o.getName();

Veya değerleri önbelleğe almak için (falsy değerleri geçersiz olduğunda):

var name = cachedName || (cachedName = getName());

JavaScript koşullu ifadeler için üçlü bir operatöre sahiptir:

var giriş_izni = (yaş > 18) ? 'evet' : 'hayır';

switch ifadesi bir sayıya veya string'e bağlı olarak birden fazla kolda kullanılabilir:

switch (eylem) {
  case 'çiz':
    Çizim();
    break;
  case 'ye':
    Yemek();
    break;
  default:
    birSeyYapma();
}

break ifadesini eklemezseniz yürütme esnasında kod bir sonraki seviyeye "atlayacaktır". Bu çoğu zaman isteyeceğiniz bir durum değildir  — ancak eğer amacınız fallthrough ( açıkça yeniden yönlendirilmedikçe, yürütme işleminin bir listede sonraki duruma geçtiği durum)  yapmak ise yorum eklemeniz debug sürecini kolaylaştıracaktır:

switch (a) {
  case 1: // fallthrough
  case 2:
    Yemek();
    break;
  default:
    birSeyYapma();
}

default maddesi tamamen isteğe bağlıdır.  İsterseniz hem switch kısmında hem de case kısmında ifade kullanabilirsiniz. 

switch (1 + 3) {
  case 2 + 2:
    yaşasın();
    break;
  default:
    aslaGerceklesmez();
}

Nesneler

Javascript nesneleri (objects), basit isim-değer çiftleri olarak düşünülebilir. Bu açıdan diğer dillerdeki karşılıkları aşağıdaki gibidir:

  • Python'da kütüphane
  • Perl ve Ruby'de Hash
  • C and C++'da Hash tabloları
  • Java'da HashMap
  • PHP'de İlişkili Diziler

Bu veri yapısının çok yaygın kullanıldığı gerçeği, çok yönlülüğünün bir kanıtıdır. JavaScript'teki her şey bir nesneden oluştuğu için, her JavaScript programı doğal olarak çok sayıda komut çizelgesi (hash table) aramaları içerir.

"İsim" kısmı bir Javascript string'i iken, değer olarak herhangi bir Javascript değeri yazabilirsiniz — diğer nesneler de dahil. Bu şekilde istediğiniz kadar komplike veri yapıları oluşturabilirsiniz.

Boş nesne oluşturmanın iki temel yolu vardır:

var nesne = new Object();

Ve:

var nesne = {};

Bunlar semantik olarak eşdeğerdir; ikinci nesne örneğine değişmez sözdizimi (literal syntax) denir ve daha kullanışlıdır. Bu sözdizimi aynı zamanda JSON formatının özüdür ve her zaman tercih edilmelidir.

Değişmez sözdizimi ile bir nesneyi tamamen tanımlayabilirsiniz:

var nesne = {
  isim: 'Havuç',
  kime: 'Max', 
  detaylar: {
    renk: 'turuncu',
    boyut: 12
  }
};

Özelliklere erişmek için aşağıda gördüğünüz iki yolu da kullanabilirsiniz:

nesne.detaylar.renk; // turuncu
nesne['detaylar']['boyut']; // 12

Aşağıdaki örnekte bir nesne prototipi (Kişi) ve bu prototipin bir örneğini (sen) oluşturacağız:

function Kişi(isim, yaş) {
  this.isim= isim;
  this.yaş = yaş;
}

// Bir nesne tanımlayalım
var sen = new Kişi('Sen', 24); 
// Adı "Sen" olan 24 yaşında bir kişi yaratmış olduk.

Bir kere oluşturulduktan sonra, nesnenin değerlerine erişebilmenin iki yolu vardır: 

// nokta notasyonu
nesne.isim = 'Ayşe';
var isim = nesne.isim;

Ve...

// parantez notasyonu 
nesne['isim'] = 'Ayşe';
var isim = nesne['isim'];
// Anahtar (key) tanımlamak için değişken kullanabilirsiniz.
var kullanıcı = prompt('Anahtar değeriniz nedir?')
obj[kullanıcı] = prompt('Anahtarın değeri nedir?')

Yukarıda gördüğünüz örnekler de semantik olarak eşdeğerdir. İkinci metodun avantajı, özelliğin ismi string olarak sunulduğu için, yükleme zamanı (run-time) içerisinde hesaplanmasıdır. Ancak bu yöntemin kullanılması bazı Javascript motoru ve sıkıştırma optimizasyonlarının (minifier optimizations) çalışmasına engel olabilir.  Ayrıca parantez notasyonu ile rezerve sözcükleri kullanarak özellik atama ve çağırma işlemleri yapabilirsiniz:

obj.for = 'Simon'; // Sözdizimi hatası verecektir çünkü 'for' rezerve sözcüklerden
obj['for'] = 'Simon'; // düzgün çalışır

ECMAScript 5'den itibaren, rezerve sözcükler nesne özelliği olarak kullanılabiliyor. Daha fazla bilgi için: ES5 Spesifikasyonu

Nesneler ve prototipleri hakkında daha fazla bilgi için Object.prototype adresini ziyaret edebilirsiniz. Nesne prototipleri  ve nesne prototip zincirleri için daha detaylı öğrenmek için: Kalıtım ve prototip zinciri.

ECMAScript 5'den itibaren, nesne anahtarları parantez notasyonuyla oluşturulmuş değişkenler ile tanımlanabilir. Yani var telKullanıcı = {}; telKullanıcı[telNum] = 12345yerine sadece {[telNum]: 12345} ifadesini kullanmak mümkündür.

Diziler

Javascript'de diziler (arrays) aslında özel birer nesneden ibarettir. Çoğu açıdan sıradan nesneler gibi çalışırlar (sayısal özelliklere doğal olarak yalnızca [] ile erişilebilir) ancak onları nesnelerden ayıran sihirli bir özellik vardır: 'length'. Bu değer her zaman en yüksek index değerinden bir fazladır.

Dizi oluşturmanın bir yolu aşağıdaki gibidir:

var a = new Array();
a[0] = 'köpek';
a[1] = 'kedi';
a[2] = 'tavuk';
a.length; // 3

Bir diğer yol ise değişmez dizi (literal array) oluşturmaktır:

var a = ['köpek', 'kedi', 'tavuk'];
a.length; // 3

Fark edebileceğiniz gibi array.length her zaman dizideki öğe sayısı anlamına gelmez. Aşağıdaki örneğe dikkat edin:

var a = ['köpek', 'kedi', 'tavuk'];
a[100] = 'tilki';
a.length; // 101

Unutmayın — dizinin uzunluğu en yüksek index'den bir fazladır.

Eğer varolmayan bir dizi index'ini sorgularsanız, undefined sonucunu alırsınız:

typeof a[90]; // undefined

Yukarıda gördüğünüz [] ve length unsurları ile istediğiniz her dizi üzerinden for döngüsü ile geçebilirsiniz:

for (var i = 0; i < a.length; i++) {
  //  a[i] kere ... yap
}

ES2015, diziler gibi yinelenen nesneler için daha kullanışlı bir for...of döngüsünü tanıttı:

for (const currentValue of a) {
  // currentValue kadar ... yap
}

for...in döngüsü kullanarak da bir dizi üzerinden geçebilirsiniz ancak bu işlem dizi elemanları üzerinden değil, dizi index'i üzerinden işlenir. Ayrıca Array.prototype 'a yeni özellikler eklendiği takdirde, onlar da döngüde yer alacaktır. Bu nedenle bu döngü türü diziler için önerilmez. 

Bir dizi üzerinden geçmenin bir diğer yolu ECMAScript 5 ile eklenen forEach() yöntemidir:

['köpek', 'kedi', 'tavuk'].forEach(function(currentValue, index, array) {
  // currentValue veya array[index] ile ... yap
});

Bir diziye yeni bir eleman eklemek istiyorsanız aşağıdaki basit metodu uygulamanız yeterli olacaktır:

a.push(item);

Diziler için birçok metod mevcuttur. Tam dokümantasyon için buraya tıklayabilirsiniz.

Metod ismi Açıklama
a.toString()  toString() içerisine virgülle ayırarak yazılan her öğeyi string şeklinde return eder.
a.toLocaleString()  toLocaleString() içerisine virgülle ayırarak yazılan her öğeyi string şeklinde return eder.
a.concat(item1[, item2[, ...[, itemN]]]) Seçilen öğeler eklenmiş bir biçimde yeni bir dizi return eder.
a.join(sep) Diziyi string'e dönüştürür. — öğeleri sep ile ayırarak
a.pop() Son öğeyi siler ve return eder.
a.push(item1, ..., itemN) Öğeleri dizinin sonuna ekler.
a.reverse() Diziyi ters çevirir.
a.shift() İlk değeri siler ve return eder
a.slice(start[, end]) Bir alt dizi oluşturur.
a.sort([cmpfn]) İsteğe bağlı bir karşılaştırma fonksiyonu alır.
a.splice(start, delcount[, item1[, ...[, itemN]]]) Bir bölümü silerek ve yerine daha fazla öğe ile ekleyerek bir diziyi modife etmenizi sağlar.
a.unshift(item1[, item2[, ...[, itemN]]]) Öğeleri dizinin başlangıcına ekler.

Fonksiyonlar

Nesnelerle gibi fonksiyonlar da Javascript'i anlamak için öğrenmeniz gereken ana bileşenlerden biridir. En temel fonksiyon aşağıda görebileceğiniz gibi oldukça kolaydır:

function topla(x, y) {
  var toplam = x + y;
  return toplam;
}

Javascript fonksiyonları birden fazla adlandırılmış parametre alabileceği gibi, parametresiz de oluşturulabilir. Fonksiyon gövdesi istediğiniz kadar ifade içerebilir ve o fonksiyonda yer alan yerel değişkenler de tanımlayabilir. return ifadesi ile birlikte istediğiniz değer getirilir ve fonksiyon sonlandırılır. Eğer hiçbir return ifadesi kullanılmamışsa (veya değer girilmemiş boş bir return kullanılmışsa) Javascript undefined sonucunu verir.

Adlandırılmış parametreler tam bir kılavuz niteliğindedir. Fonksiyonun beklediği parametreleri girmeden de çağırabilirsiniz, sonuç olarak karşınıza undefined çıkacaktır.

topla(); // NaN
// undefined bir değerle toplama işlemi gerçekleştiremezsiniz.

Ayrıca fonksiyonun beklediğinden fazla argüman da ekleyebilirsiniz:

topla(2, 3, 4); // 5
// İlk iki değerle işlem yapıp 4'ü ihmal edecektir

Biraz saçma gelebilir; ama fonksiyonlar, gövdelerinde ek bir değişken olarak bulunan argümanlara erişebilir. Argümanlar, fonksiyonlara verilen tüm değerleri kaydeden dizi-benzeri nesnelerdir. Hadi toplama fonksiyonumuzu istediğimiz kadar değer alabileceği şekilde yeniden yazalım:

function topla() {
  var toplam = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    toplam += arguments[i];
  }
  return toplam;
}

topla(2, 3, 4, 5); // 14

Yine de bu fonksiyonu kullanmak yerine 2 + 3 + 4 + 5  yazmak daha mantıklı duruyor. O yüzden bir de ortalama alan bir fonksiyon yazalım:

function ort() {
  var toplam = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    toplam += arguments[i];
  }
  return toplam / arguments.length;
}

ort(2, 3, 4, 5); // 3.5

Oldukça kullanışlı, ancak gereksiz ifadeler olması gerekenden fazla kullanılmış gibi. Yazdığımız kodu kısaltmak için argüman dizimizin yerine Rest parameter syntax yolunu kullanabiliriz. Bu yöntem hem istediğimiz sayıda argümanı fonksiyonumuzda kullanıp hem de kod uzunluğunu minimum seviyede tutmanıza yarar. Rest parametre operatörü şu şekilde listeleme yapar: ...değişken, ve bu değişkenle beraber, fonksiyonla beraber çağrılan tüm argümanların bir listesi. Ayrıca for döngüsü yerine for..of döngüsünü kullanmamız daha mantıklı olacaktır.

function ort(...args) {
  var toplam = 0;
  for (let value of args) {
    toplam += value;
  }
  return toplam / args.length;
}

ort(2, 3, 4, 5); // 3.5

Yukarıdaki kodda args değişkeni, fonksiyona verilen tüm değerleri tutar.

Unutmamanız gereken önemli bir ayrıntı: rest parametre operatörü nerede tanımlandıysa, tanımladığı satırdan sonraki argümanları belleğe alacaktır. Bir diğer deyişle, fonksiyona verilen ilk değer firstValue değişkeninde, kalan argümanlar ise args'da saklanacaktır. Bu yararlı bir dil özelliği olsa da bizi yeni bir sorunla başbaşa bırakıyor. avg() fonksiyonu virgülle ayrılan argüman listesini alabilir — ancak eğer dizinin ortalamasını bulmak istersek ne yapmamız gerekir? Fonksiyonu aşağıdaki gibi yazmak iyi bir çözüm olabilir:

function avgArray(arr) {
  var toplam = 0;
  for (var i = 0, j = arr.length; i < j; i++) {
    toplam += arr[i];
  }
  return toplam / arr.length;
}

avgArray([2, 3, 4, 5]); // 3.5

Yine de önceden oluşturduğumuz fonksiyonu kullanabilmek daha iyi olurdu. Neyseki Javascript, size isteğe bağlı bir dizi argümana sahip bir fonksiyon çağırmanıza olanak sağlıyor. Bunun için tek yapmanız gereken apply() metodunu kullanmak.

avg.apply(null, [2, 3, 4, 5]); // 3.5

 Bir diğer apply() yöntemi ise argüman olarak dizi kullanmaktır. Bu bilgi dahilinde, fonksiyonların da birer nesne olduğunu tekrardan hatırlamış olalım.

Aynı sonucu spread operatörünü çağırarak da elde edebilirsiniz.

Örneğin: avg(...numbers)

JavaScript ile anonim fonksiyonlar oluşturabilirsiniz.

var avg = function() {
  var toplam = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    toplam += arguments[i];
  }
  return toplam / arguments.length;
};

Yukarıda gördüğünüz yöntem semantik olarak function avg() ifadesine eşdeğerdir. Oldukça güçlü olan bu yöntem ile normalde ifade yazabileceğiniz her yere fonksiyon tanımı koyabilirsiniz. Bu şekilde zekice hilelere başvurabilirsiniz. Aşağıda gördüğünüz yerel değişken "saklama" yöntemi bunlardan sadece biri:

var a = 1;
var b = 2;

(function() {
  var b = 3;
  a += b;
})();

a; // 4
b; // 2

JavaScript, fonksiyonları özyinelemeli (recursively) çağırmanıza olanak sağlar. Bu, özellikle tarayıcı DOM'larında rastlayacağınız ağaç yapıları (tree structures) ile çalışırken oldukça faydalıdır.

function countChars(elm) {
  if (elm.nodeType == 3) { // TEXT_NODE
    return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; child = elm.childNodes[i]; i++) {
    count += countChars(child);
  }
  return count;
}

Bu anonim fonksiyonlarda olası bir sorunu işaret eder. Eğer bu fonksiyonların bir adı yoksa, onları nasıl özyinelemeli çağırabiliriz? Javascript bunun için fonksiyon ifadelerini adlandırmanıza olanak sağlar. Aşağıda görebileceğiniz gibi IAIFEs (Immediately Invoked Function Expressions) özelliğini kullanabilirsiniz:

var charsInBody = (function counter(elm) {
  if (elm.nodeType == 3) { // TEXT_NODE
    return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; child = elm.childNodes[i]; i++) {
    count += counter(child);
  }
  return count;
})(document.body);

Yukarıdaki fonksiyona verilen isme sadece fonksiyonun kendi kapsamında erişilebilir. Bu şekilde Javascript motorunun daha fazla optimizasyon yapmasına olanak sağlamış olursunuz, böylelikle kodunuz daha okunur hale gelir. Ayrıca, hata ayıklama yaparken zamandan tasarruf edebilirsiniz.

Fark ettiyseniz Javascript'de -diğer her şey gibi- fonksiyonlar da birer nesnedir. Böylelikle daha önceki bölümlerde öğrendiğiniz nesnelerdeki gibi, özellik ekleme ve değiştirmeniz mümkündür.

Özel nesneler

JavaScript'te nesne yönelimli programlama hakkında daha ayrıntılı bilgiler için Nesne Yönelimli Javascript'e Giriş sayfasına göz atabilirsiniz.

Nesne yönelimli programlamada; nesneler, yine aynı veriler üzerinde işlem yapan veri ve metod koleksiyonlarıdır. Javascript prototip tabanlı bir dil olduğu için, C++ ve Java'nın aksine sınıf (class) kavramına sahip değildir. Javascript'de sınıf yerine fonksiyonlar kullanılır. Ad ve soyad alanları olan bir Kişi fonksiyonu düşünelim. Tam adı görüntülemenin iki yolu olabilir: "ad, soyad" ve "soyad, ad". Fonksiyonları ve nesneleri kullanarak veriyi aşağıda göreceğiniz gibi ekrana yansıtabiliriz:

function Kişi(ad, soyad) {
  return {
    ad: ad,
    soyad: soyad
  };
}
function tamİsim(a) {
  return a.ad + ' ' + a.soyad;
}
function tamİsimTers(a) {
  return a.soyad + ', ' + a.ad;
}

s = Kişi('Simon', 'Willison');
tamİsim(s); // "Simon Willison"
tamİsimTers(s); // "Willison, Simon"

Yukarıda gördüğünüz kod çalışacaktır ama oldukça çirkin gözüküyor. Çünkü yapmak istediğiniz işlemler arttıkça global ad alanı (namespace) onlarca fonksiyon ile dolabilir. Bu nedenle yapmamız gereken şey nesnemize fonksiyon eklemek. Fonksiyonlar da birer nesne olduğu için işimiz oldukça kolay:

function Kişi(ad, soyad) {
  return {
    ad: ad,
    soyad: soyad,
    tamİsim: function() {
      return this.ad + ' ' + this.soyad;
    },
    tamİsimTers: function() {
      return this.soyad + ', ' + this.ad;
    }
  };
}

s = Kişi('Simon', 'Willison');
s.tamİsim(); // "Simon Willison"
s.tamİsimTers(); // "Willison, Simon"

Fark ettiyseniz burada yeni bir anahtar kelime kullandık: this. Fonksiyonun içinde kullanıldığında this, geçerli nesneyi ifade eder. Ne anlama geleceği fonksiyonu çağırma yöntemine göre belirlenir. Örneğin; bir nesne üzerinde, fonksiyonu nokta notasyonu veya parantez notasyonu ile çağırırsanız, o nesne this olur. Eğer nokta notasyonu çağrı işleminde kullanılmadıysa this global nesneyi ifade eder.

this anahtar sözcüğü birçok hataya sebep olabilir. Aşağıdaki örneğe bir göz atın:

s = Kişi('Simon', 'Willison');
var tamİsim = s.tamİsim;
fullName(); // undefined undefined 

tamİsim() fonksiyonunu  s.tamİsim()'i kullanmadan tek başına çağırdığımızda, this global nesneye bağlıdır. ad ve soyad gibi global değişkenler olmadığı için ekrana her biri için undefined sonucu yansıyacaktır.

Şimdi Kişi fonksiyonumuzu geliştirmek için this anahtar kelimesini kullanalım:

function Kişi(ad, soyad) {
  this.ad = ad;
  this.soyad = soyad;
  this.tamİsim = function() {
    return this.ad + ' ' + this.soyad;
  };
  this.tamİsimTers = function() {
    return this.soyad + ', ' + this.ad;
  };
}
var s = new Kişi('Simon', 'Willison');

Yeni bir anahtar kelimeden bahsetmenin vakti geldi: new. new, this ile doğrudan bağlantılıdır. Tamamen yeni bir boş nesne yaratır ve belirtilen fonksiyonu yeni nesnenin this değeriyle güncelleyerek çağırır. Fark ettiyseniz this, sadece this nesnesinde değişiklik yapar. Bir değeri return eden new anahtar kelimesidir. new tarafından çağırılacak fonksiyonlara kurucu (constructor) fonksiyon denir. Bu fonksiyonları new ile çağırmayı unutmamak için büyük harfle isimlendirmek yaygın bir yöntemdir.

Geliştirdiğimiz fonksiyon hala tamİsim()'i tek başına çağırdığımızda istediğimiz sonucu vermeyecek.

Kişi nesnelerimiz artık eskisinden daha iyi, ancak hala kötü tarafları mevcut. Her kişi nesnesi oluşturduğumuzda, iki yepyeni fonksiyon yaratmış oluyoruz — peki bu kod paylaşılsaydı daha iyi olmaz mıydı?

function tamİsim() {
  return this.ad + ' ' + this.soyad;
}
function tamİsimTers() {
  return this.soyad + ', ' + this.ad;
}
function Kişi(ad, soyad) {
  this.ad = ad;
  this.soyad = soyad;
  this.tamİsim= tamİsim;
  this.tamİsimTers = tamİsimTers;
}

Çok daha iyi. Artık metod fonksyionlarını bir defa oluşturuyoruz ve referansları kurucu fonksiyonun içinde atıyoruz. Bundan daha iyisini de yapabilir miyiz? Cevap evet:

function Kişi(ad, soyad) {
  this.ad = ad;
  this.soyad = soyad;
}
Kişi.prototype.tamİsim = function() {
  return this.ad+ ' ' + this.soyad;
};
Kişi.prototype.tamİsimTers = function() {
  return this.soyad + ', ' + this.ad;
};

Kişi.prototypeKişi'nin tüm özelliklerinin paylaşıldığı bir nesnedir. Bir diğer adı "prototip zinciri" olan bu nesne, bir arama zincirinin parçasını oluşturur. Kişi'ye ait ayarlanmamış bir özelliğe erişmek istediğinizde, Javascript bu özelliğin var olup olmadığını Kişi.prototype öğesinde arayacaktır. Böylelikle Kişi.prototype'a atanan her şey, this nesnesi kullanılarak kurucu fonksiyonda da geçerli hale gelecektir.

Bu, oldukça güçlü bir özellik. Javascript, size istediğiniz zaman, herhangi bir şeyin prototipini modifiye etmenize olanak tanır. Bu şekilde önceden oluşturduğunuz nesnelere yeni metodlar ekleyebilirsiniz:

s = new Kişi('Simon', 'Willison');
s.isimCaps(); // 1. satırda TypeError: s.isimCaps bir fonksiyon değil

Kişi.prototype.isimCaps = function() {
  return this.first.toUpperCase();
};
s.isimCaps(); // "SIMON"

İlginç bir şekilde gömülü Javascript nesnelerinin prototiplerine de bir şeyler ekleyebilirsiniz. Hadi String  nesnesine sözcükleri ters çevirmeye yarayan bir metod ekleyelim:

var s = 'Simon';
s.ters(); // 1. satırda TypeError: s.ters bir fonksiyon değil

String.prototype.ters = function() {
  var r = '';
  for (var i = this.length - 1; i >= 0; i--) {
    r += this[i];
  }
  return r;
};

s.ters(); // nomiS

Yeni metodumuz aşağıda görebileceğiniz durumlarda bile çalışıyor:

'Ters çevir'.ters(); // riveç sreT

Daha önce de bahsettiğimiz gibi; prototip, zincirin bir kısmını oluşturur. Bu zincirin kökü Object.prototype, içerdiği metodlardan biri ise toString()'dir. — hatırlarsanız bu metodu bir nesneyi string'e çevirmek için çağırıyorduk. Bu yöntem Kişi nesnelerimizin hata ayıklama sürecinde yardımcı olacaktır.

var s = new Kişi('Simon', 'Willison');
s.toString(); // [object Object]

Person.prototype.toString = function() {
  return '<Kişi: ' + this.isim() + '>';
}

s.toString(); // "<Kişi: Simon Willison>"

Hatırlarsanız avg.apply() metodu yeni bir boş argüman oluşturuyordu. Şimdi o konuya geri dönebiliriz.  apply() kullanacağımız ilk argüman, 'this' ile ele alınacak bir nesne olmalıdır. Aşağıda new anahtar kelimesinin kullanıldığı küçük bir uygulama görebilirsiniz:

function Deneme(constructor, ...args) {
  var o = {}; // Nesne yaratır
  constructor.apply(o, args);
  return o;
}

Bu, prototip zincirini oluşturmadığı için new ifadesinin tam anlamıyla karşılığı sayılmaz (bu durumu anlatması biraz zor) ve çok sık kullanacağınız bir şey değil, ancak yine de bilmek faydalı olacaktır. Bu örnekte ...args, "rest arguments" olarak da geçer. — adından da anlayacağınız üzere geriye kalan tüm argümanları temsil eder.

Bu nedenle bu satır,

var bill = Deneme(Kişi, 'William', 'Orange');

hemen hemen bu satır ile aynıdır:

var bill = new Kişi('William', 'Orange');

apply() metodunun kardeşi sayabileceğimiz call, apply()'dan farklı olarak genişletilmiş bir argüman listesi alır.

function soyadCaps() {
  return this.last.toUpperCase();
}
var s = new Kişi('Simon', 'Willison');
soyadCaps.call(s);
// Veya aşağıdaki yöntemi kullanabilirsiniz:
s.soyadCaps = soyadCaps;
s.soyadCaps(); // WILLISON

İç fonksiyonlar

JavaScript fonksiyonların içinde başka fonksiyonlar tanımlayabilirsiniz. Bu durumu  Kişi() fonksiyonumuzun önceki versiyonlarında görmüştük. İç içe oluşturulan fonksiyonlara dair önemli bir detay, içteki fonksiyonun içinde bulunduğu "parent" fonksiyon kapsamında kullanılan değişkenlere erişebilmesidir:

function parentFonk() {
  var a = 1;

  function içFonk() {
    var b = 4; // parentFonk bunu kullanamaz.
    return a + b; 
  }
  return içFonk(); // 5
}

Bu, daha sürdürülebilir bir kod yazmanızda büyük yarar sağlar. Eğer bir fonksiyonunuz, başka kısımlarda işinize yaramayacak bir veya daha fazla fonksiyona bağlıysa; bu fonksiyonları başka bir yerde çağıracağınız fonksiyonun içine yazabilirsiniz. Bu şekilde global düzeyde kullanılan değişken sayısını azaltacağınız için, daha güvenli bir koda sahip olursunuz.

Yazdığınız kod karmaşık bir hal aldıkça birden çok fonksiyon arasında değer paylaşmak yerine global değişkenler kullanmak daha cazip gelebilir — ki bu sebeple sürdürülmesi zor bir kod yaratmış olursunuz. İç içe fonksiyonlar dış fonksiyon ile değişken paylaşabildiği için global isim alanınızı (global namespace) kirletmeden, sadece ilgili fonksiyonları bir araya getirerek bu mekanizmayı kullanabilirsiniz.  —  bu şekilde kullanacağınız değişkenlere "yerel global" bile diyebiliriz. Ne kadar kullanışlı da olsa bu mekanizmayı dikkatli kullanmanız gerekir.

Kaplamlar

Sıra geldi Javascript'in en güçlü -bir yandan da en kafa karıştırıcı-  soyutlamalarından birine. Peki kaplamlar (closures) tam olarak ne yapar?

function ekle(a) {
  return function(b) {
    return a + b;
  };
}
var x = ekle(5);
var y = ekle(20);
x(6); // ?
y(7); // ?

ekle() fonksiyonun işlevini anlamışsınızdır; yeni bir "toplama" fonksiyonu oluşturur ve tek bir argümanla çağrıldığında, oluşturulduğu argümana o değeri ekler.

Burada gördüğünüz olayın gerçekleşme şekli, daha önce bahsettiğimiz iç fonksiyonların çalışma prensibiyle hemen hemen aynı: içeride tanımlanmış fonksiyon, dış fonksiyonun değişkenlerine erişim hakkı vardır. Buradaki tek fark ise şu; dış fonksiyon return edildiğinde yerel değişkenleri artık yok olacak gibi düşünürüz, ancak hala bellektedirler — aksi takdirde fonksiyonlarımız çalışmazdı. Dahası, ekle() fonksiyonunun yerel değişkenlerinin iki farklı "kopyası" bulunuyor. — bir 5 olan a ve bir de 20 olan a. Bu yüzden yukarıdaki kodun sonucu böyle olacaktır:

x(6); //  11
y(7); //  27

Şimdi arkaplanda ne olup bittiğini yazalım. Javascript ne zaman bir fonksiyonu yürütse, fonksiyon içerisindeki yerel değişkenleri tutma amaçlı bir 'kapsama alanı' oluşturur. Bu alan, fonksiyon parametresi olarak gönderilen tüm değişkenler üzerinden tanımlanır. Bahsettiğimiz durum tüm global değişken ve fonksiyonları içeren global nesneye benzese de, birkaç önemli farklılık taşır: ilk olarak, fonksiyon her yürütüldüğünde yepyeni bir kapsam nesnesi oluşturulur, ve ikinci olarak, kapsam nesneleri global nesnelerin aksine Javascript kodu üzerinden doğrudan erişilebilir değildir. Örneğin, geçerli kapsam nesnesinin özellikleri üzerinde yineleme yapmak için bir mekanizma yoktur.

Özetlemek gerekirse; ekle() çağırıldığında, fonksiyona geçen argüman, yani tek bir özelliğe sahip (a) bir kapsam nesnesi oluşturulur. Ardından ekle() fonksiyonu yeni bir fonksiyon return eder. Normal koşullarda Javascript'in çöp toplayıcısının ekle() için oluşturulan bu yeni fonksiyonu temizlemesi gerekirken, return edilen fonksiyon yine de kapsam fonksiyonu için referans konumunda durur. Bu nedenle ekle()'nin oluşturduğu fonksiyon nesnesine herhangi bir referans kalmayana dek bu kapsamda hiç bir öğe silinmeyecektir.

Kapsam nesneleri, JavaScript'in nesne sisteminde kullanılan prototip zincirine benzer şekilde, kapsam zinciri adı verilen bir zinciri oluşturur.

Kaplam, bir fonksiyon ile, o fonksiyon beraberinde oluşturulan kapsam nesnesinin bir kombinasyonudur. Kaplamlar durum (state) kaydetmenize olanak sağlar — böylelikle nesnelerin yerine kullanılabilirler. Kaplamlar hakkında bazı muhteşem bilgiler için buraya tıklayabilirsiniz.

Orijinal Belge Bilgileri

  • Yazar: Simon Willison
  • Son güncellenme tarihi: March 7, 2006
  • Copyright: © 2006 Simon Willison, contributed under the Creative Commons: Attribute-Sharealike 2.0 license.
  • Daha fazla bilgi: Bu ders ile ilgili daha fazla bilgi (ve orijinal sunuma ait slaytlar) için, Etech weblog post adresine girebilirsiniz.
wiki.languages({
  "fr": "fr/Une_r\u00e9introduction_\u00e0_JavaScript",
  "it": "it/Una_re-introduzione_a_Javascript",
  "ja": "ja/A_re-introduction_to_JavaScript",
  "ko": "ko/A_re-introduction_to_JavaScript",
  "pl": "pl/JavaScript/Na_pocz\u0105tek",
  "ru": "ru/\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0435_\u0432\u0432\u0435\u0434\u0435\u043d\u0438\u0435_\u0432_JavaScript",
  "zh-cn": "cn/A_re-introduction_to_JavaScript",
  "zh-tw": "zh_tw/\u91cd\u65b0\u4ecb\u7d39_JavaScript",
  "es": "es/JavaScript/Una_nueva_introducción_a_JavaScript"});

Document Tags and Contributors

Contributors to this page: Erenoz, ozankaraali, modularica, teoli, askn, berkerpeksag
Last updated by: Erenoz,