Ek

Prototip devri

null ve undefined dışındaki her temel veri türünün bir prototipi vardır. Prototip, değerlerle çalışma yöntemleri sağlayan ve karşılık gelen bir nesne sarmalayıcıdır. Bir basit öğede yöntem veya özellik araması çağrıldığında, JavaScript arka planda temel öğeyi sarmalar ve yöntemi çağırır veya bunun yerine sarmalayıcı nesnesinde özellik aramasını gerçekleştirir.

Örneğin, bir dize sabitinin kendi yöntemi yoktur ancak karşılık gelen String nesne sarmalayıcı sayesinde .toUpperCase() yöntemini çağırabilirsiniz:

"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL

Buna prototip kalıtımı adı verilir. Özellikler ve yöntemler, bir değerin karşılık gelen oluşturucusundan devralını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 { … }

Sadece değerlerine göre tanımlamak yerine bu kurucuları kullanarak temel öğeler oluşturabilirsiniz. Örneğin, String oluşturucusunu kullandığınızda dize değişmez değeri değil, dize nesnesi oluşturulur. Bu nesne, yalnızca dize değerimizi değil, aynı zamanda oluşturucunun 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."

Çoğunlukla, sonuçta ortaya çıkan nesneler, onları tanımlamak için kullandığımız değerlerle davranır. Örneğin, new Number oluşturucusunu kullanarak bir sayı değeri tanımlamak Number prototipinin tüm yöntemlerini ve özelliklerini içeren bir nesne elde edilmesini sağlasa bile, bu nesnelerde matematiksel operatörleri sayı değişmez değerlerinde yaptığı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

Bu kurucuları çok nadiren kullanmanız gerekir, çünkü JavaScript'in yerleşik prototip kalıtımı, bunların pratik bir fayda sağlamadığı anlamına gelir. Oluşturucuları kullanarak primitif oluşturmak da beklenmedik sonuçlara yol açabilir çünkü sonuç basit bir değişmez değer değil, nesnedir:

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ılması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üzeltmek için otomatik noktalı virgül ekleme (ASI) adlı özelliği kullanabilir. JavaScript ayrıştırıcı, izin verilmeyen bir jetonla karşılaşırsa aşağıdaki koşullardan biri veya birkaçı doğru olduğu sürece olası söz dizimi hatasını düzeltmek için bu jetondan önce bir noktalı virgül eklemeye çalışır:

  • Bu jeton önceki jetondan bir satır sonuyla ayrılır.
  • Jeton: }.
  • Önceki jeton ) ve eklenen noktalı virgül, do...while ifadesinin son 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 yazacaksanız bunları noktalı virgülle ayırdığınızdan emin olun:

const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier

const myVariable = 2; myVariable + 3;
> 5

ASI, hata düzeltme girişimidir, JavaScript'te yerleşik olan söz dizimsel bir esneklik değildir. Doğru kodu üretmek için bu özelliğe güvenmemeniz için uygun durumlarda noktalı virgül kullandığınızdan emin olun.

Yüksek güvenlik modu

JavaScript'in nasıl yazıldığını yöneten standartlar, zamanla dilin ilk tasarımında dikkate alınan unsurların çok daha ötesine geçti. JavaScript'in beklenen davranışında yapılan her yeni değişiklik, eski web sitelerinde hatalara neden olmamalıdır.

ES5, bir komut dosyasının tamamı veya tek bir işlev için daha kısıtlayıcı bir dil kuralları grubunu etkinleştirmenin bir yolu olan "katı modu" kullanıma sunarak, mevcut uygulamaları bozmadan JavaScript semantiğiyle ilgili uzun süredir var olan bazı sorunları ele alır. Yüksek düzey modunu etkinleştirmek için komut dosyasının veya işlevin ilk satırında "use strict" değişmez değerini ve ardından noktalı virgül kullanın:

"use strict";
function myFunction() {
  "use strict";
}

Yüksek düzey modu, belirli "güvenli olmayan" işlemleri veya desteği sonlandırılmış özellikleri engeller, yaygın "sessiz" olanların yerine açık hatalar verir ve gelecekteki dil özellikleriyle çakışabilecek söz diziminin kullanımını yasaklar. Örneğin, değişken kapsamla ilgili ilk tasarım kararları, geliştiricilerin kapsayıcı bağlamdan bağımsız olarak var anahtar kelimesini çıkararak bir değişkeni tanımlarken yanlışlıkla küresel kapsamı "kirletme" ihtimalini artırıyordu:

(function() {
  mySloppyGlobal = true;
}());

mySloppyGlobal;
> true

Modern JavaScript çalışma zamanları, bu davranışa dayanan herhangi bir web sitesini yanlışlıkla veya kasıtlı olarak bozma riski olmadan bu davranışı düzeltemez. Bunun yerine modern JavaScript, geliştiricilerin yeni işler için yüksek düzey modunu etkinleştirmesine izin vererek ve yüksek düzey modunu yalnızca eski uygulamaları bozmayacakları yeni dil özellikleri bağlamında varsayılan olarak etkinleştirerek bunu önler:

(function() {
    "use strict";
    mySloppyGlobal = true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal

"use strict" değerini dize değişmez değeri olarak yazmanız gerekir. Şablon değişmez değeri (use strict) çalışmaz. Ayrıca amaçlanan bağlamında yürütülebilir kodlardan önce "use strict" öğesini eklemeniz gerekir. Aksi halde, çevirmen bunu 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

Referansa göre, değere göre

Nesnenin özellikleri, işlev parametreleri ve bir dizi, küme veya harita'daki öğeler dahil olmak üzere tüm değişkenler, temel değer veya başvuru değeri içerebilir.

Bir değişkenden diğerine temel bir değer atandığında, JavaScript motoru bu değerin bir kopyasını oluşturur ve bunu değişkene atar.

Bir değişkene nesne (sınıf örnekleri, diziler ve işlevler) atadığınızda, değişken söz konusu nesnenin yeni bir kopyasını oluşturmak yerine, nesnenin bellekte depolanmış konumuna referans içerir. Bu nedenle, bir değişken tarafından referans verilen bir nesneyi değiştirmek, yalnızca söz konusu değişkenin içerdiği değeri değil, başvuruda bulunulan nesneyi değiştirir. Örneğin, nesne referansı içeren bir değişkenle yeni bir değişken başlatırsanız ve ardından bu nesneye özellik eklemek için yeni değişkeni kullanırsanız özellik ve değeri orijinal nesneye eklenir:

const myObject = {};
const myObjectReference = myObject;

myObjectReference.myProperty = true;

myObject;
> Object { myProperty: true }

Bu, yalnızca nesnelerin değiştirilmesi değil, aynı zamanda katı karşılaştırmalar yapmak için de önemlidir. Çünkü nesneler arasında katı eşitlik sağlamak için her iki değişkenin de true için aynı nesneye başvurması gerekir. Bu nesneler yapısal olarak aynı olsa 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önetimini kullanır. Diğer bir deyişle, geliştirme sürecinde belleğin açıkça ayrılması veya tahsis edilmesi gerekmez. 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ın anlaşılması, referans değerleriyle çalışmak için faydalı bir bağlam sağlar.

Bellekte iki "alan" vardır: "yığın" ve "yığın". Yığın, statik verileri (temel değerler ve nesne referansları) depolar. Bunun nedeni, bu verileri depolamak için gereken sabit alan, komut dosyası yürütülmeden önce atanabilmesidir. Yığın, nesneleri depolar. Bunlar, yürütme sırasında boyutları değişebileceği için dinamik olarak ayrılmış alana ihtiyaç duyar. "Atık toplama" adı verilen bir işlem, bellekte yer açar. Bu işlem, referansı olmayan nesneleri bellekten kaldırır.

Ana iş parçacığı

JavaScript, "eşzamanlı" yürütme modeline sahip, temelde 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ığı adı verilir.

Ana iş parçacığı HTML ayrıştırma, sayfanın bazı bölümlerini oluşturma ve yeniden oluşturma, CSS animasyonlarını çalıştırma ve basitten (metin vurgulama gibi) karmaşa (form öğeleriyle etkileşim gibi) kadar çeşitli 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ı buldu ancak daha karmaşık komut dosyaları ana iş parçacığının kaynaklarının çok büyük bir kısmını kullanmaya ve genel sayfa performansını etkilemeye devam edebilir.

Bazı görevler, bazı kısıtlamalarla birlikte Web Çalışanları adlı arka plan iş parçacıklarında yürütülebilir:

  • Çalışan 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şim önemli ölçüde azaldı veya hiç erişim yok.
  • Bunlar, ana iş parçacığıyla iletişim kurma konusunda sınırlıdır.

Bu sınırlamalar nedeniyle, ana iş parçacığını işgal edebilecek, yoğun kaynak kullanan görevler için idealdir.

Çağrı yığını

"Yürütme bağlamlarını" (aktif olarak yürütülen kod) yönetmek için kullanılan veri yapısı, çağrı yığını (genellikle yalnızca "yığın") olarak adlandırılan bir listedir. Bir komut dosyası ilk kez yürütüldüğünde, JavaScript yorumlayıcısı bir "genel yürütme bağlamı" oluşturur ve bu genel bağlam içinde yukarıdan aşağıya doğru teker teker yürütülen ifadelerle çağrı yığınına aktarır. Çevirmen, genel bağlamı yürütürken bir işlev çağrısıyla karşılaştığında, bu çağrı için yığının üst kısmına bir "işlev yürütme bağlamı" aktarır, genel 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, bu çağrıya ilişkin işlev yürütme bağlamı, mevcut yürütme bağlamının hemen üzerinde, yığının üst kısmına aktarılır. Çağrı yığını, "ilk giren, ilk çıkar" esasına göre çalışır. Diğer bir deyişle, yığında en yüksek olan en yeni işlev çağrısı yürütülür ve çözümlenene 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ı, tekrar yığındaki en yüksek öğe olur ve yürütmeye devam eder.

Bu yürütme bağlamları, yürütme işlemleri için gerekli olan tüm değerleri yakalar. Bunlar ayrıca üst bağlamına göre işlevin kapsamındaki değişkenleri ve işlevleri belirler ve this anahtar kelimesinin değerini işlevin bağlamında belirleyip ayarlar.

Etkinlik döngüsü ve geri çağırma sırası

Bu sıralı yürütme, sunucudan veri getirme, kullanıcı etkileşimine yanıt verme veya setTimeout ya da setInterval ile ayarlanmış zamanlayıcıları bekleme gibi geri çağırma işlevleri içeren eşzamansız görevlerin, bu görev tamamlanana kadar ana iş parçacığını engelleyebileceği veya geri çağırma işlevinin yürütme bağlamı yığına eklendiği anda geçerli yürütme bağlamını beklenmedik bir şekilde kesintiye uğratacağı anlamına gelir. JavaScript, bu sorunu ele almak için eşzamansız görevleri, "olay döngüsü" ve "geri arama sırası"ndan (bazen "ileti sırası" olarak adlandırılır) oluşan, etkinlik odaklı bir "eşzamanlılık modeli" kullanarak yönetir.

Ana iş parçacığında eşzamansız bir görev yürütüldüğünde, geri çağırma işlevinin yürütme bağlamı çağrı yığınının üzerine değil, geri çağırma sırasına yerleştirilir. Etkinlik döngüsü, bazen reaktör olarak adlandırılan ve çağrı yığınının ve geri çağırma sırasının durumunu sürekli olarak sorgulayan bir kalıptır. Geri çağırma sırasında görevler varsa ve etkinlik döngüsü çağrı yığınının boş olduğunu belirlerse geri çağırma sırasında yer alan görevler, yürütülmeleri için teker teker yığına aktarılır.