--- title: JavaScript ile Tekrar Tanışmak (JS Eğitimi) slug: Web/JavaScript/A_re-introduction_to_JavaScript tags: - BetikKodlama - Giriş - JavaScript - Orta düzey - Rehber - öğren translation_of: Web/JavaScript/A_re-introduction_to_JavaScript ---
Neden yeniden tanışma? Çünkü {{Glossary("JavaScript")}}, dü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 {{Glossary("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.
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 {{jsxref("undefined")}} ve {{jsxref("null")}}, ki ... biraz garipler. Ayrıca özel bir nesne kabul edebileceğimiz {{jsxref("Array")}}. Bir de bedava elde edebileceğiniz nesneler olan {{jsxref("Date")}} ve {{jsxref("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 {{jsxref("Error")}} türlerinden de söz edebiliriz. Ancak ilk diyagramla hareket etmemiz bizim işimizi kolaylaştıracaktır.
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 {{jsxref("Math")}}'dir:
Math.sin(3.5); var d = Math.PI * r * r;
{{jsxref("Global_Objects/parseInt", "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, {{jsxref("Global_Objects/parseInt", "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 {{jsxref("Global_Objects/parseFloat", "parseFloat()")}} gömülü fonksiyonunu kullanabilirsiniz. Bu fonksiyon, kuzeni sayabileceğimiz {{jsxref("Global_Objects/parseInt", "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 {{jsxref("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
{{jsxref("Global_Objects/isNaN", "isNaN()")}} gömme fonksiyonu ile test edilebilir:
isNaN(NaN); // true
JavaScript ayrıca {{jsxref("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 {{jsxref("Global_Objects/isFinite", "isFinite()")}} ile test edebilirsiniz:
isFinite(1/0); // false isFinite(-Infinite); //false isFinite(NaN); // false
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.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
JavaScript, bir değer sayılmayan (ve sadece null
anahtar kelimesi ile erişilebilen) {{jsxref ("null")}} ve atanmamış değer olduğunu belirten {{jsxref ("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:
false
, 0
, boş stringler (""
), NaN
, ve undefined
değerlerinin tamamı false
,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.
Javascript'de yeni değişkenler let
, const
, 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.
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" merhaba 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.
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 array) {
// 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();
}
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:
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] = 12345
yerine sadece {[telNum]: 12345}
ifadesini kullanmak mümkündür.
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. |
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 {{jsxref("Function.apply", "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.
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.prototype
, Kiş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]
Kişi.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
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.
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.
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"});