2024 JavaScript Mülakat Soruları ve Cevapları

Büşra Aktaş
Bursa Bilişim Topluluğu
10 min readMar 17, 2024

Teknolojilerin kullanıldıkları alanlar ve proje hedefleri günden güne gelişirken mülakatlarda karşılaştığımız sorularda değişkenlik gösterir hale geldi. Özellikle JavaScript’in büyük bir ekosisteme sahip olmasınında ışığında sektör veya projeye bağlı olarak niş sorular ortaya çıkabiliyor. Karşılaşabileceğimiz farklı soru ve cevapları takip etmenin güncel kalabilmek için pozitif etkisi olduğunu düşünüyorum. Bugün birlikte kült ve güncel mülakat soru cevaplarına göz atarken aynı zamanda JavaScript bilgimizi de tazeleyeceğiz. Hazırsanız başlayalım!

1. “ == ” ve “ === ” arasındaki fark nedir?

== Eşitlik Operatörü (Loose Equality Operator): İki değeri karşılaştırırken, öncelikle tür dönüşümü yaparak iki değerin eşit olup olmadığını kontrol eder. Yani, eğer karşılaştırılan iki değerin veri tipleri farklıysa, JavaScript motoru otomatik tür dönüşümü (type coercion) yapar ve sonra değerleri karşılaştırır. Bu durum, beklenmeyen sonuçlara yol açabilir.

0 == false   
// true, çünkü 0 ve false her ikisi de "falsey" değerlerdir,
type coercion ile dönüştürüldükten sonra eşit kabul edilir.

---------------------------------------------------------------------------

'2' == 2
// true, çünkü string değerdeki '2' sayıya dönüştürülür ve değerler eşit olur.

=== Katı Eşitlik Operatörü (Strict Equality Operator): Hem değerleri hem de değerlerin türlerini karşılaştırır. Karşılaştırılan iki değerin hem değer olarak hem de tür olarak eşit olması gerektiği anlamına gelir. Tür dönüşümü yapılmaz. Dolayısıyla eğer iki değerin türleri farklıysa operatör doğrudan false değerini döndürür. Bu operatör daha güvenilir karşılaştırmalar yapılmasını sağlar.

0 === false  
// false, çünkü değerlerin türleri farklıdır.
// biri number diğeri ise boolean türündedir.

---------------------------------------------------------------------------

'2' === 2
// false, çünkü biri string diğeri number türündedir.

2. Truthy ve Falsy değerler nelerdir? Örnek verebilir misiniz?

JavaScript, dinamik tiplemeye sahip bir dildir ve ifadelerin değerlerini koşullu bağlamlarda true veya false olarak otomatik olarak çevirir. Bu durum geliştiricilere esneklik sağlarken aynı zamanda kodun anlaşılabilirliğini ve güvenilirliğini artırmak için dikkatli kullanılmalıdır. JavaScript'teki her değer, koşullu bir bağlamda "truthy" veya "falsy" olarak değerlendirilir. Bu kavramlar program akış kontrolü, koşullu ifadeler ve mantıksal operatörlerle çalışırken büyük bir öneme sahiptir.

JavaScript’te belirli değerler vardır ki bunlar koşullu bir bağlamda değerlendirildiklerinde false olarak kabul edilir. Bu "falsy" değerler şunlardır;

  • false: Boolean türünün false değeri.
  • 0, -0: Sıfır ve negatif sıfır.
  • "", '', ````: Boş string değerleri.
  • null: Bir nesnenin kasıtlı olarak boş bırakıldığını belirten özel bir değer.
  • undefined: Bir değişkenin değer atanmamış olduğunu gösterir.
  • NaN: "Not a Number" anlamına gelir ve geçersiz bir sayısal işlemin sonucunu temsil eder.

Bu değerler dışında kalan her şey koşullu bağlamda true olarak değerlendirilir ve bu nedenle "truthy" kabul edilir.

Falsy Bir Değerin Kullanımı:

let a = 32;
let b = 64 - a;

if (a-b) {
// Bu blok çalıştırılmaz çünkü 0 falsy bir değerdir.
} else {
// Bu blok çalışır.
}

Truthy Bir Değerin Kullanımı:

Diğer taraftan, boş olmayan bir string ("hello") truthy bir değerdir ve koşullu bir ifade içinde true olarak değerlendirilir.

if ("hello") {
// Bu blok çalışır çünkü "hello" truthy bir değerdir.
}

3. Scope kavramı nedir ? Global scope, local scope ve lexical scope kavramlarından bahsedebilir misiniz ?

JavaScript’te scope (kapsam), bir değişkenin veya fonksiyonun kodun hangi bölümlerinden erişilebilir olduğunu belirler. Scope kavramı değişkenlerin, fonksiyonların ve objelerin hem görünürlüğü hem de yaşam süresi üzerinde kritik bir etkiye sahiptir. JavaScript’te üç temel scope türü vardır: global scope, local (fonksiyon) scope ve lexical (blok) scope.

Global Scope

Bir değişken global scope’ta tanımlandığında bu değişkene programın her yerinden erişilebilir. Global scope, kodun en üst seviyesinde yani herhangi bir fonksiyon veya blok içinde olmayan alandır. Global değişkenler, uygulama yaşam döngüsü boyunca erişilebilir ve değiştirilebilir durumdadır. Bu da onları potansiyel olarak tehlikeli hale getirir çünkü herhangi bir fonksiyon tarafından kazara değiştirilebilirler. Özellikle uzun kod satırları arasında benzer değişken isimleri kullanılıyorsa..

var globalVar = "Bu bir global değişkendir.";

function exampleFunction() {
console.log(globalVar); // Bu bir global değişkendir.
}
exampleFunction();

Local Scope (Fonksiyon Scope)

Bir değişken bir fonksiyonun içerisinde tanımlandığında bu değişkene sadece o fonksiyon içinden erişilebilir. Bu durumda değişkenin local scope’a sahip olduğu anlamına gelir. Her fonksiyon kendi scope’una sahiptir ve bu scope içinde tanımlanan değişkenler dışarıdan erişilemez. Fonksiyon dışında aynı isimle başka bir değişken tanımlanabilir ve bu fonksiyon içindeki değişkeni etkilemez ve değiştirmez.

function exampleFunction() {
var localVar = "Bu bir local değişkendir.";
console.log(localVar); // Bu bir local değişkendir.
}
exampleFunction();
console.log(localVar); // ReferenceError: localVar is not defined

Lexical Scope (Blok Scope)

ES6 ile birlikte let ve const anahtar kelimeleri tanıtıldı ve bu da beraberinde blok-level scope kavramını getirdi. Blok scope, bir değişkenin tanımlandığı {} bloğu (örneğin, bir for döngüsü, if bloğu vs.) içinde sınırlı olduğu durumdur. Bu tür durumlarda değişkenin sadece o blok içinde erişilebilir olduğu ve blok dışında erişilemez olduğu anlamına gelir.

function exampleFunction() {
if (true) {
let blockVar = "Bu bir block-level değişkendir.";
console.log(blockVar); // Bu bir block-level değişkendir.
}
console.log(blockVar); // ReferenceError: blockVar is not defined
}
exampleFunction();

Lexical scoping, bir fonksiyonun tanımlandığı yerin ve o fonksiyonun hangi değişkenlere erişebileceğini belirlediği bir kavramdır. Bir iç içe fonksiyon, kapsayıcı fonksiyonunun değişkenlerine erişebilir, ancak tersi doğru değildir. Fonksiyonlar arası ilişkilerin ve kapsamların, kod yazıldığı anda (lexically) belirlendiği anlamına gelir.

function outerFunction() {
var outerVar = "Dış fonksiyon değişkeni";

function innerFunction() {
var innerVar = "İç fonksiyon değişkeni"
console.log(outerVar); // Dış fonksiyon değişkeni
}

innerFunction();
}
outerFunction();

innerFunction outerFunction’ın sahip olduğu outerVar değişkenine erişebiliyorken, outerFunction innerFunction içerisinde tanımlanmış lexical scope’a sahip innerVar değişkenine erişemez.

4. IIFE (Immediately Invoked Function Expression) Nedir?

JavaScript ekosisteminde özellikle kapsam izolasyonu isim çakışmalarını önleme ve veri gizliliği sağlama gibi konulara elegan bir çözüm sunar. Bu konsept, fonksiyon tanımının hemen ardından parantez içerisinde çağrılmasıyla karakterize edilir ve bu sayede fonksiyonun tanımlandığı anda çalıştırılmasını sağlar. IIFE’lerin kullanımı, yüksek düzeyde modülerlik ve kodun sürdürülebilirliği için kritik öneme sahiptir.

Kapsam İzolasyonu:

Bir web uygulamasında, birden fazla JavaScript dosyası ve kütüphane kullanıldığında global kapsamda isim çakışmaları yaşanabilir. IIFE kullanarak her modülü kendi kapsamında izole edebiliriz:

// Modül 1
(function() {
var name = 'Modül 1';
console.log(name); // Modül 1
})();

// Modül 2
(function() {
var name = 'Modül 2';
console.log(name); // Modül 2
})();

Bu yaklaşım, name değişkeninin her iki modülde de kullanılmasına rağmen birbirleriyle çakışmadan çalışmasını sağlar.

Gizlilik ve Encapsulation:

IIFE, modüller arası sıkı bir sınır çizerek yalnızca gerekli veri ve fonksiyonların dışarıya açılmasını sağlar. Örneğin, bir modülün içindeki bazı yardımcı fonksiyonları gizlemek isteyebiliriz:

var myModule = (function() {
var privateVar = 'Gizli Veri';
var publicMethod = function() {
console.log(privateVar + ' artık erişilebilir.');
};

return {
exposeMethod: publicMethod
};
})();

myModule.exposeMethod(); // "Gizli Veri artık erişilebilir."

Yukarıda privateVar ve publicMethod dış kapsama karşı gizli tutulurken, sadece exposeMethod üzerinden kontrollü bir şekilde erişilebilir hale getirilmiştir.

IIFE kullanımı JavaScript’te kod organizasyonu, modülarite ve kapsam yönetimi konularında önemli bir araçtır. Modern JavaScript’te modül sistemleri ve ES6+ ile gelen yeni özellikler, IIFE’nin bazı kullanımlarını azaltsa da bu konsept özellikle legacy kodlarda ve belirli tasarım kalıplarında hala geçerliliğini korumaktadır.

5. JavaScript’de this nedir?

JavaScript’te this anahtar kelimesi bir fonksiyonun veya metotun çağrıldığı bağlamı (context) ifade eder. Yani this fonksiyonun çalışma anındaki sahibi olan nesneye bir referanstır. this'in değeri, fonksiyonun nasıl ve nerede çağrıldığına bağlı olarak değişiklik gösterir.

Global kontekste this:

Tarayıcıda, global kontekste this, global nesneyi ifade eder. Node.js'de ise, bu global nesne farklı bir nesnedir (global), fakat mantık benzerdir.

console.log(this === window); // Tarayıcıda true döner

Fonksiyonlarda this:

Bir fonksiyon global kontekste çağrıldığında this yine global nesneyi ifade eder fakat strict mode ('use strict') kullanıldığında global fonksiyonların içindeki this undefined değerini alır.

function showThis() {
console.log(this);
}
showThis(); // Global nesneyi veya strict mode'da undefined'ı gösterir

Metotlarda this:

Bir nesnenin metodu olarak çağrıldığında this o nesneyi ifade eder.

const myObject = {
myMethod() {
console.log(this);
}
};

myObject.myMethod(); // `this`, `myObject`'ı ifade eder

Constructor Fonksiyon this:

Bir constructor fonksiyonu new anahtar kelimesi ile kullanıldığında, this yeni oluşturulan nesneyi ifade eder.

function Car(make, model) {
this.make = make;
this.model = model;
}

const myCar = new Car('Toyota', 'Corolla');
console.log(myCar.make); // Toyota

Event Listenerthis:

Bir DOM olay işleyicisinde this, olayın bağlandığı elementi ifade eder.

<button id="myButton">Click me</button>
<script>
document.getElementById('myButton').addEventListener('click', function() {
console.log(this); // `this`, buton elementini ifade eder
});
</script>

Arrow Fonksiyonthis:

Arrow fonksiyonları (=>), kendi this değerlerini bağlamazlar. Bunun yerine arrow fonksiyonunun oluşturulduğu kapsamdaki this değerini kullanırlar.

const myObject = {
myMethod() {
setTimeout(() => {
console.log(this); // `this`, `myObject`'ı ifade eder
}, 1000);
}
};

myObject.myMethod();

🌟 this’i kontrol altına almak :

JavaScript, this'in dinamik yapısını yönetmek için .bind(), .call(), ve .apply() gibi metotlar sunar. Bu metotlar fonksiyonun this değerini manuel olarak ayarlamak için kullanılabilir.

function showThis() {
console.log(this);
}

const myObject = {};

const boundShowThis = showThis.bind(myObject);
boundShowThis(); // `this`, `myObject`'ı ifade eder

6. JavaScript’te ‘use strict’ nedir ve nasıl etkinleştirilebilir?

ECMAScript 5 ile tanıtılan bir betik veya fonksiyonun “strict mode” (katı mod) içinde çalıştırılmasını sağlayan bir direktiftir. Strict mode, JavaScript yorumlayıcısına “sessiz” hataları gerçek hatalar olarak atmasını, daha kesin hata kontrolü yapmasını, daha güvenli ve optimize edilmiş bir çalışma ortamı sağlamasını söyler. Use strict modu daha iyi performans, hata yönetimi ve güvenlik avantajları sunar.

Global Kapsamda Etkinleştirme: Bir betiğin en üstüne "use strict" ekleyerek tüm script’in strict mode altında çalışmasını sağlayabilirsiniz. Modun bu yöntemi script’in tamamı için geçerli olur.

Fonksiyon Kapsamında Etkinleştirme: "use strict" ifadesini sadece belirli bir fonksiyonun içine yerleştirerek yalnızca o fonksiyonun strict mode altında çalışmasını sağlayabilirsiniz. Bu yöntemde global kapsamı etkilemez.

7. Void(0) nedir ve neden kullanılır?

JavaScript’te void(0) ifadesi, genellikle bir bağlantının (<a href="#">) varsayılan davranışını engellemek için kullanılır. void operatörü, belirtilen ifadeyi değerlendirir ve undefined döndürür. Özellikle href attribute kullanıldığında, sayfanın yeniden yüklenmesini veya istenmeyen şekilde başka bir URL'e yönlendirilmesini önlemek için yararlıdır. void(0) genellikle bir JavaScript fonksiyonunu tetiklemek için kullanılan bağlantılarda kullanılır;

<a href="javascript:void(0)" onclick="myFunction()">Click Me!</a>

Bu teknik, kullanıcı bağlantıya tıkladığında myFunction() fonksiyonunun çağrılmasını sağlar ancak bağlantının varsayılan davranışı olan sayfanın yeniden yüklenmesi veya başka bir sayfaya yönlendirmesini engellenir.

8. ViewState ve SessionState nedir özetleyebilir misin?

ViewState:

  • Kapsamı: Yalnızca mevcut sayfaya özeldir.
  • Saklama Yeri: Kullanıcı tarayıcısında, sayfanın kendisinde saklanır. Veriler, bir form elemanı içinde base64 kodlanmış bir string olarak gönderilir.
  • Kullanım Senaryoları: Sayfa postback’leri arasında kontrol değerlerinin saklanması gibi sayfa-spesifik durum yönetimi için idealdir.
  • Performans ve Güvenlik: Büyük veri miktarları sayfa yüklenme süresini etkileyebilir. Şifrelenmemiş veriler güvenlik riski oluşturabilir.

SessionState:

  • Kapsamı: Kullanıcının web uygulamasındaki tüm oturumu boyunca geçerlidir.
  • Saklama Yeri: Sunucu tarafında saklanır. Kullanıcıya özgü veriler, sunucu üzerinde bir oturumda tutulur.
  • Kullanım Senaryoları: Kullanıcı oturumu boyunca erişilmesi gereken kullanıcı tercihleri ve oturum bilgileri gibi verilerin saklanması için idealdir.
  • Performans ve Güvenlik: Sunucu kaynaklarını kullanır ancak kullanıcı tarafından erişilemez ve bu daha güvenlidir. Büyük ölçekteki uygulamalar için ölçeklendirme stratejileri gerektirebilir.

9. JavaScript’te zamanlayıcı yöntemleri diyince aklına neler geliyor ve nasıl çalışıyorlar?

JavaScript’te zamanlayıcılar, belirli bir gecikme süresinden sonra bir fonksiyonun çalıştırılmasını sağlayan iki temel fonksiyon aracılığıyla kullanılır; setTimeout ve setInterval.

Bu methodlar JavaScript’te asenkron programlamanın temellerini anlamak için harika tekniklerdir.

setTimeout fonksiyonu, belirtilen bir gecikme süresi (milisaniye cinsinden) sonrasında bir kez çalışacak bir fonksiyonu zamanlar. Bu method gecikmeli görevlerin yürütülmesi, animasyonlar veya kullanıcı etkileşimine yanıt olarak bir süre bekledikten sonra işlemlerin gerçekleştirilmesi gibi durumlar için elverişlidir.

setTimeout(() => {
console.log('Bu mesaj 2 saniye sonra gösterilecek.');
}, 2000);

setInterval fonksiyonu, belirli zaman aralıklarıyla sürekli olarak çalışacak bir fonksiyon tanımlamanıza olanak tanır.

setInterval(() => {
console.log('Her 3 saniyede bir bu mesaj gösterilecek.');
}, 3000);

Zamanlayıcıların İptali

Her iki fonksiyon da bir zamanlayıcı tanımladığında bu zamanlayıcıya özgü bir tanımlayıcı (ID) döndürür. Tanımlayıcı, clearTimeout veya clearInterval fonksiyonları ile kullanılarak zamanlayıcının iptal edilmesini sağlar.

let timerId = setTimeout(() => {
console.log('Bu mesaj gösterilmeyecek.');
}, 1000);

// Zamanlayıcıyı iptal et
clearTimeout(timerId);

JavaScript Zamanlayıcılarının Özellikleri ve Davranışları

  • JavaScript tek iş parçacıklıdır ve zamanlayıcılar asenkron bir şekilde çalışır. Bu bilgi belirtilen sürenin zamanlayıcının tam olarak o anda çalışacağını garanti etmediği anlamına gelir.. Aslında sadece en az o kadar süre bekledikten sonra çalışacağını belirtir.
  • Zamanlayıcıların belirttiği süre, işlenmekte olan diğer işlemlerin tamamlanmasını bekler. Yoğun bir işlem sırasında bir zamanlayıcı beklenenden daha geç çalışabilir.
  • Zamanlayıcılar, — özellikle setInterval kullanılırken — işlem yoğunluğuna dikkat edilmesi gereken senaryolarda dikkatli kullanılmalıdır.

🌟 Zamanlayıcılar kullanıcı etkileşimini geliştiren animasyonlar, veri yenileme işlemleri ve kullanıcı girişlerini zamanlamak gibi JavaScript uygulamalarında birçok kritik asenkron durumda önemli rol oynar.

10. JavaScript’te asenkron programlamanın temellerinden bahsedebilir misin?

JavaScript’te asenkron programlama, zaman alıcı işlemlerin uygulamanın akışını bloke etmeden gerçekleştirilmesini sağlar. Bu bağlamda Promise, async ve await kavramları modern JavaScript asenkron programlamanın temel yapı taşlarıdır. Her biri farklı senaryolar için avantajlar ve bazı dezavantajlar sunar.

Promise

Bir Promise, bir işlemin tamamlanmasını temsil eder ve bu işlem bir değerle sonuçlanabilir veya bir hata ile reddedilebilir. Promise nesnesi üç durumdan birinde olabilir: pending (beklemede), fulfilled (yerine getirildi) veya rejected (reddedildi).

Avantajları:

  • Asenkron işlemleri zincirleme (chaining) sayesinde daha okunabilir hale getirir.
  • Hata yakalama (catch bloğu) ile hataları daha etkili yönetme imkanı sunar.

Dezavantajları:

  • Zaman zaman callback yapısına benzer “callback hell” benzeri karmaşık zincirlemelere yol açabilir.
  • Birden fazla asenkron işlemi paralel olarak kontrol etmek Promise.all gibi ekstra yöntemler gerektirebilir ve bazen kodu karmaşıklaştırabilir.

Async/await

async ve await, ES2017'de tanıtılan ve Promise'leri daha okunabilir ve senkron bir şekilde kullanmayı sağlayan anahtar kelimelerdir. Bir fonksiyon async olarak işaretlendiğinde, bu fonksiyon otomatik olarak bir Promise döndürür. await operatörü ise bir Promise'nin sonucunu beklemek için kullanılır ve yalnızca async fonksiyonlar içinde kullanılabilir.

Avantajları:

  • Asenkron kodu, senkronmuş gibi daha okunabilir ve anlaşılır bir şekilde yazma imkanı sunar.
  • Hata yakalama için try/catch bloklarını kullanarak daha sezgisel hata yönetimi sağlar.

Dezavantajları:

  • await eğer dikkatli kullanılmazsa asenkron işlemlerin yanlışlıkla seri olarak çalıştırılmasına yol açabilir ve bu da performans kaybına neden olabilir.
  • Eski JavaScript motorlarında veya platformlarda desteklenmeyebilir ve bu nedenle transpilation (Babel gibi araçlarla ES5'e çevirme) gerekebilir.

11. Callback Hell nedir ve nasıl önleyebiliriz bahsedebilir misin?

Callback hell, JavaScript’teki asenkron işlemleri yönetmek için callback fonksiyonlarının aşırı kullanımıyla oluşan okunması ve bakımı zor kod yapısına verilen isimdir. Bahsi geçen yapı, genellikle “Piramit of Doom” olarak da adlandırılır. Çünkü iç içe geçmiş callback fonksiyonları bir piramit şeklini andırır. Derinlemesine iç içe geçmiş callback’ler, kodun anlaşılabilirliğini ve sürdürülebilirliğini olumsuz etkiler.

— Callback Hell’i Önleme Yöntemleri

1. Modularization (Modülerleştirme)

Fonksiyonları daha küçük, tekrar kullanılabilir parçalara ayırarak kodun okunabilirliğini ve yönetilebilirliğini arttırabiliriz. Her fonksiyon belirli bir görevi yerine getirmeli ve birbirinden bağımsız olmalıdır. Bu teknik kodun daha anlaşılır ve test edilebilir olmasını sağlar.

2. Promise Kullanımı

Promise’ler, asenkron işlemleri daha düzenli bir şekilde yönetmek için kullanılır. Bir işlemin tamamlanmasını temsil ederler ve başarılı (resolve) veya başarısız (reject) sonuçları ele almak için metodlar sunarlar. Callback'ler yerine promise'ler kullanarak callback hell'den kaçınabiliriz.

function asyncOperation() {
return new Promise((resolve, reject) => {
// Asenkron işlem
if (/* işlem başarılı */) {
resolve(result);
} else {
reject(error);
}
});
}

asyncOperation()
.then(result => {
// İşlem başarılı
})
.catch(error => {
// İşlemde hata
});

3. Async/Await Kullanımı

ES7 ile tanıtılan async/await sözdizimi, promise tabanlı asenkron işlemleri daha sezgisel bir şekilde yazmanızı sağlar. Bir fonksiyonu async olarak işaretlemek o fonksiyonun bir Promise döndüreceği anlamına gelir. await operatörü ise bir Promise'in çözülmesini bekler ve bu süreçte fonksiyonun yürütülmesini askıya alır. Bu yaklaşım, asenkron kodun senkronmuş gibi yazılmasını sağlayarak okunurluğunu artırır.

async function asyncTask() {
try {
const result = await asyncOperation();
// İşlem başarılı
} catch (error) {
// İşlemde hata
}
}

Yorumlarınız veya önerileriniz varsa benimle iletişime geçebilir ve blog serilerimi daha da iyileştirmeme destek olabilirsiniz. Sıradaki popüler konumuzun yazısıyla görüşmek üzere!

Buraya tıklayarak bana ulaşabilirsiniz🙋‍♀️️

Yazılarıma kahve ısmarlayarak destek olabilirsiniz☕️

--

--