Prototip kalıtım
null
ve undefined
hariç her ilkel veri türünün bir prototipi vardır. Bu prototip, değerlerle çalışma yöntemleri sağlayan ilgili bir nesne sarmalayıcısıdır. Bir ilkel üzerinde bir yöntem veya özellik araması çağrıldığında JavaScript, ilkel nesneyi perde arkasında sarmalayarak yöntemi çağırır ya da bunun yerine sarmalayıcı nesnede özellik aramasını gerçekleştirir.
Örneğin, bir dize değişmez değerinin kendi yöntemi yoktur ancak ilgili String
nesne sarmalayıcısı sayesinde üzerinde .toUpperCase()
yöntemini çağırabilirsiniz:
"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL
Buna prototip kalıtım denir. Bu, bir değerin ilgili kurucusundan özellikleri ve yöntemleri devralınmasıdır.
Number.prototype
> Number { 0 }
> constructor: function Number()
> toExponential: function toExponential()
> toFixed: function toFixed()
> toLocaleString: function toLocaleString()
> toPrecision: function toPrecision()
> toString: function toString()
> valueOf: function valueOf()
> <prototype>: Object { … }
Bu yapıcıları kullanarak, değerlerine göre tanımlamak yerine ilkel öğeler oluşturabilirsiniz. Örneğin, String
kurucusunun kullanılması bir dize değişmezi değil, dize nesnesi oluşturur: Bu nesne, yalnızca dize değerimizi değil, kurucunun devralınan tüm özelliklerini ve yöntemlerini de içerir.
const myString = new String( "I'm a string." );
myString;
> String { "I'm a string." }
typeof myString;
> "object"
myString.valueOf();
> "I'm a string."
Sonuç olarak elde edilen nesneler, çoğu durumda onları tanımlamak için kullandığımız değerler gibi davranır. Örneğin, new Number
yapıcısını kullanarak bir sayı değeri tanımlamak Number
prototipinin tüm yöntemlerini ve özelliklerini içeren bir nesneyle sonuçlansa da bu nesnelerde matematiksel operatörleri tam sayılarda kullanacağınız gibi kullanabilirsiniz:
const numberOne = new Number(1);
const numberTwo = new Number(2);
numberOne;
> Number { 1 }
typeof numberOne;
> "object"
numberTwo;
> Number { 2 }
typeof numberTwo;
> "object"
numberOne + numberTwo;
> 3
JavaScript'in yerleşik prototip kalıtım özelliği, bu yapıcıların pratik bir fayda sağlamadığı anlamına geldiğinden bu yapıcıları çok nadiren kullanmanız gerekir. Oluşturucuları kullanarak ilkel oluşturmak da beklenmedik sonuçlara yol açabilir. Bunun nedeni, sonucun basit bir değişmez değer değil, bir nesne olmasıdır:
let stringLiteral = "String literal."
typeof stringLiteral;
> "string"
let stringObject = new String( "String object." );
stringObject
> "object"
Bu durum, katı karşılaştırma operatörlerinin kullanımını karmaşık hale getirebilir:
const myStringLiteral = "My string";
const myStringObject = new String( "My string" );
myStringLiteral === "My string";
> true
myStringObject === "My string";
> false
Otomatik noktalı virgül ekleme (ASI)
JavaScript yorumlayıcıları, bir komut dosyasını ayrıştırırken atlanan noktalı virgül örneklerini düzeltmeye çalışmak için otomatik noktalı virgül ekleme (ASI) adlı bir özellik kullanabilir. JavaScript ayrıştırıcı, izin verilmeyen bir jetonla karşılaşırsa aşağıdaki koşullardan biri veya daha fazlası geçerli olduğu sürece olası söz dizimi hatasını düzeltmek için söz konusu jetondan önce noktalı virgül eklemeye çalışır:
- Bu jeton, önceki jetondan satır sonu işaretiyle ayrılır.
- Bu jeton
}
. - Önceki jeton
)
'tür ve eklenen noktalı virgül,do
…while
ifadesinin bitiş noktalı virgülü olur.
Daha fazla bilgi için ASI kurallarına bakın.
Örneğin, aşağıdaki ifadelerden sonra noktalı virgüllerin atlanması ASI nedeniyle söz dizimi hatasına neden olmaz:
const myVariable = 2
myVariable + 3
> 5
Ancak ASI, aynı satırdaki birden fazla ifadeyi hesaba katamaz. Aynı satıra birden fazla ifade yazarsanız bunları noktalı virgüllerle ayırdığınızdan emin olun:
const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier
const myVariable = 2; myVariable + 3;
> 5
ASI, JavaScript'e yerleştirilmiş bir tür söz dizimi esnekliği değil, hata düzeltme girişimidir. Doğru kod oluşturmak için bu işleve güvenmek zorunda kalmamak amacıyla uygun yerlerde noktalı virgül kullandığınızdan emin olun.
Yüksek güvenlik modu
JavaScript'in nasıl yazıldığına dair standartlar, dilin ilk tasarımı sırasında düşünülen her şeyin çok ötesine geçti. JavaScript'in beklenen davranışında yapılan her yeni değişiklik, eski web sitelerinde hatalara neden olmamalıdır.
ES5, mevcut uygulamaları bozmadan JavaScript semantiğiyle ilgili bazı uzun süredir devam eden sorunları ele almak için "katı mod"u kullanıma sunar. Bu mod, bir komut dosyasının tamamı veya tek bir işlev için daha kısıtlayıcı bir dil kuralı grubunu etkinleştirmenin bir yoludur. Yüksek güvenlik modunu etkinleştirmek için bir komut dosyasının veya işlevin ilk satırında "use strict"
dize değişmezini ve ardından noktalı virgül kullanın:
"use strict";
function myFunction() {
"use strict";
}
Katı mod, belirli "güvenli olmayan" işlemleri veya desteği sonlandırılmış özellikleri engeller, yaygın "sessiz" hatalar yerine açık hatalar verir ve gelecekteki dil özellikleriyle çakışabilecek söz dizimi kullanımlarını yasaklar. Örneğin, değişken kapsamı ile ilgili erken tasarım kararları, geliştiricilerin var
anahtar kelimesini atlayarak bir değişkeni tanımlarken kapsayıcı bağlamdan bağımsız olarak yanlışlıkla genel kapsamı "kirletme" olasılığını artırıyordu:
(function() {
mySloppyGlobal = true;
}());
mySloppyGlobal;
> true
Modern JavaScript çalışma zamanları, bu davranışı düzeltirken bu davranışa dayalı web sitelerini yanlışlıkla veya kasıtlı olarak bozma riskini göze almalıdır. Bunun yerine modern JavaScript, geliştiricilerin yeni çalışmalar için katı modu etkinleştirmesine izin vererek ve katı modu varsayılan olarak yalnızca eski uygulamaları bozmayacak yeni dil özellikleri bağlamında etkinleştirerek bu sorunu önler:
(function() {
"use strict";
mySloppyGlobal = true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal
"use strict"
değerini dize değişmezi olarak yazmanız gerekir.
Şablon literali (use strict
) çalışmaz. Ayrıca, çalıştırılabilir koddan önce "use strict"
'yi amaçlanan bağlamında eklemeniz gerekir. Aksi takdirde, yorumcu bu ifadeyi yoksayar.
(function() {
"use strict";
let myVariable = "String.";
console.log( myVariable );
sloppyGlobal = true;
}());
> "String."
> Uncaught ReferenceError: assignment to undeclared variable sloppyGlobal
(function() {
let myVariable = "String.";
"use strict";
console.log( myVariable );
sloppyGlobal = true;
}());
> "String." // Because there was code prior to "use strict", this variable still pollutes the global scope
Referansla, değere göre
Bir nesnenin özellikleri, işlev parametreleri ve dizi, küme veya harita öğeleri gibi tüm değişkenler, ilkel bir değer veya referans değer içerebilir.
Bir ilkel değer bir değişkenden diğerine atandığında JavaScript motoru bu değerin bir kopyasını oluşturur ve değişkene atar.
Bir nesneyi (sınıf örnekleri, diziler ve işlevler) bir değişkene atadığınızda, değişken bu nesnenin yeni bir kopyasını oluşturmak yerine nesnenin bellekte depolanan konumuna referans içerir. Bu nedenle, bir değişken tarafından referans verilen bir öğe değiştirildiğinde yalnızca söz konusu değişkenin içerdiği değer değil, referans verilen öğe de değişir. Örneğin, yeni bir değişkeni nesne referansı içeren bir değişkenle başlatır ve ardından bu nesneye mülk eklemek için yeni değişkeni kullanırsanız mülk ve değeri orijinal nesneye eklenir:
const myObject = {};
const myObjectReference = myObject;
myObjectReference.myProperty = true;
myObject;
> Object { myProperty: true }
Bu, yalnızca nesneleri değiştirmek için değil, aynı zamanda sıkı karşılaştırmalar yapmak için de önemlidir. Çünkü nesneler arasındaki sıkı eşitlik, her iki değişkenin de true
olarak değerlendirilmesi için aynı nesneyi referans almasını gerektirir. Yapısal olarak aynı olsalar bile farklı nesnelere referans veremezler:
const myObject = {};
const myReferencedObject = myObject;
const myNewObject = {};
myObject === myNewObject;
> false
myObject === myReferencedObject;
> true
Bellek ayırma
JavaScript, otomatik bellek yönetimi kullanır. Bu, belleğin geliştirme sırasında açıkça ayrılması veya ayrılmasının kaldırılması gerekmediği anlamına gelir. JavaScript motorlarının bellek yönetimine yaklaşımlarının ayrıntıları bu modülün kapsamı dışında olsa da belleğin nasıl ayrıldığını anlamak, referans değerlerle çalışmak için yararlı bir bağlam sağlar.
Bellekte iki "alan" vardır: "stack" ve "heap". Yığın, bu verileri depolamak için gereken sabit miktarda alan komut dosyası yürütülmeden önce tahsis edilebildiğinden statik verileri (ilkel değerler ve nesnelere referanslar) depolar. Yığın, boyutları yürütme sırasında değişebileceği için dinamik olarak ayrılmış alana ihtiyaç duyan nesneleri depolar. Bellek, referansı olmayan nesneleri bellekten kaldıran "çöp toplama" adlı bir işlemle boşaltılır.
Ana iş parçacığı
JavaScript, temel olarak "eşzamanlı" bir yürütme modeline sahip tek iş parçacıklı bir dildir. Yani tek seferde yalnızca bir görev yürütebilir. Bu sıralı yürütme bağlamına ana iş parçacığı denir.
Ana iş parçacığı, HTML'yi ayrıştırma, sayfanın bölümlerini oluşturma ve yeniden oluşturma, CSS animasyonları çalıştırma ve basitten (metin vurgulama gibi) karmaşığa (form öğeleriyle etkileşim kurma gibi) kadar değişen kullanıcı etkileşimlerini işleme gibi diğer tarayıcı görevleri tarafından paylaşılır. Tarayıcı tedarikçileri, ana iş parçacığı tarafından gerçekleştirilen görevleri optimize etmenin yollarını bulmuş olsa da daha karmaşık komut dosyaları, ana iş parçacığının kaynaklarını çok fazla kullanabilir ve genel sayfa performansını etkileyebilir.
Bazı görevler, Web İşleyicileri adlı arka plan iş parçacıklarında bazı sınırlamalarla birlikte yürütülebilir:
- İşleyici iş parçacıkları yalnızca bağımsız JavaScript dosyalarında işlem yapabilir.
- Tarayıcı penceresine ve kullanıcı arayüzüne erişimi önemli ölçüde azaltılmış veya hiç yok.
- Ana mesaj dizisiyle iletişim kurma şekilleri sınırlıdır.
Bu sınırlamalar, bu iş parçacıklarını ana iş parçacığının işgal edebileceği odaklanmış, yoğun kaynak kullanan görevler için ideal kılar.
Çağrı yığını
"Yürütme bağlamlarını" (etkin olarak yürütülen kod) yönetmek için kullanılan veri yapısı, çağrı yığını (genellikle "yığın" olarak adlandırılır) adlı bir listetir. Bir komut dosyası ilk kez yürütüldüğünde JavaScript yorumlayıcısı bir "global yürütme bağlamı" oluşturur ve bu bağlamı çağrı yığınına gönderir. Bu global bağlamdaki ifadeler, yukarıdan aşağıya doğru tek tek yürütülür. Yorumlayıcı, global bağlamı yürütürken bir işlev çağrısıyla karşılaştığında, söz konusu çağrı için bir "işlev yürütme bağlamı"nı yığının en üstüne iter, global yürütme bağlamını duraklatır ve işlev yürütme bağlamını yürütür.
Bir işlev her çağrıldığında, söz konusu çağrının işlev yürütme bağlamı, geçerli yürütme bağlamının hemen üstüne, yığının en üstüne itilir. Çağrı yığını "son giren, ilk çıkan" şeklinde çalışır. Yani yığının en üstündeki en son işlev çağrısı yürütülür ve çözülene kadar devam eder. Bu işlev tamamlandığında yorumlayıcı, işlevi çağrı yığınından kaldırır ve bu işlev çağrısını içeren yürütme bağlamı, yığıntaki en yüksek öğe olur ve yürütmeyi devam ettirir.
Bu yürütme bağlamları, yürütülmeleri için gerekli tüm değerleri yakalar. Ayrıca, ebeveyn bağlamına göre işlevin kapsamında kullanılabilen değişkenleri ve işlevleri de belirler ve işlev bağlamında this
anahtar kelimesinin değerini belirler ve ayarlar.
Etkinlik döngüsü ve geri çağırma kuyruğu
Bu sıralı yürütme, geri çağırma işlevleri içeren asenkron görevlerin (ör. bir sunucudan veri getirme, kullanıcı etkileşimine yanıt verme veya setTimeout
ya da setInterval
ile ayarlanan zamanlayıcıları bekleme) ya bu görev tamamlanana kadar ana ileti dizisini engellediği veya geri çağırma işlevinin yürütme bağlamı yığına eklendiği anda mevcut yürütme bağlamını beklenmedik bir şekilde kesintiye uğrattığı anlamına gelir. JavaScript, bu sorunu gidermek için "etkinlik döngüsü" ve "geri çağırma kuyruğu"ndan (bazen "mesaj kuyruğu" olarak da adlandırılır) oluşan etkinlik odaklı bir "eşzamanlılık modeli" kullanarak asynkron görevleri yönetir.
Ana iş parçacığında bir asynkron görev yürütüldüğünde geri çağırma işlevinin yürütme bağlamı, çağrı yığınının üstüne değil, geri çağırma kuyruğuna yerleştirilir. Etkinlik döngüsü, bazen reaktör olarak adlandırılan ve çağrı yığınının ve geri çağırma kuyruğunun durumunu sürekli olarak sorgulayan bir kalıptır. Geri çağırma kuyruğunda görev varsa ve etkinlik döngüsü, çağrı yığınının boş olduğunu belirlerse geri çağırma kuyruğundaki görevler, yürütülmek üzere yığına tek tek itilir.