2024 JavaScript Mülakat Soruları ve Cevapları
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!