Yasin Ateş

Frontend Mülakatlarında En Sık Karşılaştığım Sorular ve Cevapları

Frontend Mülakatlarında En Sık Karşılaştığım Sorular ve Cevapları

Bu yazıda, frontend mülakat süreçlerinde en sık karşılaştığım soruları ve bunlara vereceğin en etkili cevapları ele alacağız. JavaScript’in çalışma mantığından React’in iç dünyasına, Git komutlarından performans optimizasyonuna kadar geniş bir yelpazeyi kapsıyoruz.

🧠 JavaScript’in Temelleri

Call Stack nedir, ne işe yarar?

JavaScript single thread’li bir dil olduğu için, aynı anda sadece bir işlem yapabilir. İşte tam bu noktada Call Stack devreye giriyor.

Call Stack, JavaScript motorunun “şu an ne yapıyorum?” sorusunu takip ettiği bir veri yapısıdır. Bir fonksiyon çağrıldığında stack’e eklenir (push), fonksiyon tamamlandığında ise stack’ten çıkarılır (pop). Bu yapı LIFO (Last In, First Out) prensibiyle çalışır.

function birinci() {
 console.log("Birinci fonksiyon");
 ikinci();
}

function ikinci() {
 console.log("İkinci fonksiyon");
}

birinci();

Yukarıdaki örnekte Call Stack sırası şöyle gelişir:

★ birinci() stack'e eklenir

★ ikinci() stack'e eklenir

★ ikinci() tamamlanır ve stack'ten çıkar

★ birinci() tamamlanır ve stack'ten çıkar

JavaScript’in çalışma mantığı nasıldır?

Bir değişken ya da fonksiyon oluşturulduğunda arka planda neler olur? Gelin bunu bir örnekle somutlaştıralım.

JavaScript kodu çalışmaya başladığında iki aşama vardır:

1. Compilation (Derleme) Aşaması: JavaScript motoru kodu önce tarar. var ile tanımlanan değişkenlerin ve fonksiyon bildirimlerinin "var olduğu bilgisi" hafızaya alınır (ama değerleri henüz atanmaz).

2. Execution (Yürütme) Aşaması: Kod satır satır çalıştırılır, değerler atanır, fonksiyonlar çağrılır.

Bu iki aşama birlikte Hoisting kavramını doğurur. Bir sonraki başlıkta buna değineceğiz.

"var" diye bir keyword neden var?

var, JavaScript'in ilk sürümlerinden beri gelen bir anahtar kelimedir. let ve const ES6 (2015) ile geldi. Peki varı neden hâlâ bilmemiz gerekiyor?

var ile tanımlanan değişkenler function scope veya global scope seviyesindedir. Bu, blok kapsamını (if, for gibi) yok sayması anlamına gelir.

if (true) {
 var eski = "var ile tanımlandım";
 let yeni = "let ile tanımlandım";
}

console.log(eski); // "var ile tanımlandım" — sızdı!
console.log(yeni); // ReferenceError — blok dışında erişilemez

Kodu incelerken dikkat etmemiz gereken noktalar:

★ var block scope değildir, bu beklenmedik hatalara yol açar

★ let ve const block scope'a sahiptir ve modern JavaScript'te tercih edilir

★ Artık kullanmasak da 🥲, legacy kodlarda varı görebiliriz, davranışını bilmemizde fayda var

Hoisting nedir?

JavaScript, kodunu çalıştırmadan önce değişken ve fonksiyon bildirimlerini dosyanın en üstüne “taşır” gibi davranır. Buna Hoisting denir.

console.log(isim); // undefined — hata değil!
var isim = "Rabia";

selamVer(); // "Merhaba!" — çalışır!
function selamVer() {
 console.log("Merhaba!");
}

★ var ile tanımlanan değişkenler "taşınır" ama değerleri taşınmaz; değer undefined olarak gelir

★ Fonksiyon bildirimleri (function foo() {}) tamamen taşınır, yani hem tanım hem gövde

★ let ve const de teknik olarak hoist edilir ama "Temporal Dead Zone" nedeniyle erişilemez

Pure Function nedir?

Bir fonksiyon şu iki kurala uyuyorsa pure function olarak adlandırılır:

★ Aynı girdiler için her zaman aynı çıktıyı üretir

★ Yan etkileri yoktur (dışarıdaki bir değişkeni değiştirmez, API çağrısı yapmaz, vs.)

// Pure function ✅
function topla(a, b) {
 return a + b;
}

// Pure değil ❌ — dış değişkeni değiştiriyor
let toplam = 0;
function ekle(sayi) {
 toplam += sayi; // yan etki
}

Pure function’lar test yazmayı kolaylaştırır, hata ayıklamayı basitleştirir ve React gibi kütüphanelerin render optimizasyonlarının temelini oluşturur.

Arrow function ve normal function’da this kullanımı farkı nedir?

Bu, mülakatçıların gerçekten sevdiği bir konu! 😄 20 teknik görüşmeden 18'inde aldım bu soruyu

Normal fonksiyonlarda this, fonksiyonun nasıl çağrıldığına göre belirlenir. Yani this dinamiktir.

Arrow fonksiyonlarda ise this, fonksiyonun tanımlandığı andaki kapsama (lexical scope) bağlıdır. this sabitlenir.

const nesne = {
 isim: "Rabia",
 normalFonksiyon: function () {
 console.log(this.isim); // "Rabia" — doğru
 },
 arrowFonksiyon: () => {
 console.log(this.isim); // undefined — this global scope'u gösterir
 },
};

nesne.normalFonksiyon();
nesne.arrowFonksiyon();

React’te class component’lerde bu farkın önemi:

class Buton extends React.Component {
 // Arrow function — this otomatik bind'lı
 handleClickArrow = () => {
 console.log(this.state); // Güvenli!
 };

 // Normal fonksiyon — constructor'da bind etmezsen this kaybolur
 handleClickNormal() {
 console.log(this.state); // undefined (bind edilmemişse)
 }

 render() {
 return (
 <>
 <button onClick={this.handleClickArrow}>Arrow</button>
 <button onClick={this.handleClickNormal.bind(this)}>Normal</button>
 </>
 );
 }
}

★ Arrow function ile tanımlanan class metotları, constructor'da bind gerektirmez

★ Normal fonksiyon ile tanımlananlarda bind(this) ya da constructor'da this.metot = this.metot.bind(this) şart

Closure Function nedir?

Closure, bir fonksiyonun kendi scope’u dışındaki değişkenlere erişebildiği ve bu değişkenleri “hatırladığı” durumdur. Teoriden pratiğe geçelim:

function sayac() {
 let adet = 0;

 return function () {
 adet++;
 console.log("Adet:", adet);
 };
}

const arttir = sayac();
arttir(); // Adet: 1
arttir(); // Adet: 2
arttir(); // Adet: 3

★ sayac() çağrıldığında bir iç fonksiyon döner

★ Bu iç fonksiyon, adet değişkenine olan referansını "kapatır" (close over)

★ Her çağrıda adet hâlâ bellekte yaşamaya devam eder çünkü closure buna referans tutar

Closure’lar, özel değişkenler oluşturmak, factory fonksiyonlar yazmak ve event listener’larda callback’ler tanımlamak için çok kullanılır.

⚡ Asenkron JavaScript

Event Loop — Memory Heap — Call Stack

Event Loop ve Memory Heap nedir?

Peki ama JavaScript tek thread’liyken asenkron işlemleri nasıl yapabiliyor? İşte tam bu noktada Event Loop devreye giriyor.

JavaScript çalışma ortamı şu bileşenlerden oluşur:

Call Stack: Çalışan fonksiyonların takip edildiği yer

Memory Heap: Değişkenlerin ve nesnelerin depolandığı hafıza alanı

Web APIs: setTimeout, fetch, DOM event'leri gibi tarayıcı tarafından sağlanan API'ler

Callback Queue (Macrotask Queue): Tamamlanan async işlemlerin callback’lerinin beklediği kuyruk

Microtask Queue: Promise callback’lerinin beklediği, öncelikli kuyruk

Event Loop’un görevi: Call Stack boşaldığında, önce Microtask Queue’yu, ardından Callback Queue’yu kontrol eder ve bekleyen callback’leri stack’e ekler.

console.log("1");

setTimeout(() => console.log("2"), 0);

Promise.resolve().then(() => console.log("3"));

console.log("4");

// Çıktı: 1, 4, 3, 2

★ 1 ve 4 senkron olarak çalışır

★ 3 (Promise) microtask olduğundan 2'den önce gelir

★ 2 (setTimeout) macrotask olduğundan en son çalışır

JavaScript Single Thread mi, Multi Thread mi?

JavaScript single thread’li bir dildir. Yani aynı anda yalnızca bir iş yapabilir. Ancak bunu “JavaScript asenkron işlem yapamaz” şeklinde yorumlamak yanlış olur.

Peki bu thread’leri açıp kapatabilir miyiz?

Web Workers sayesinde evet! 🥹

// main.js — ana thread
const worker = new Worker("worker.js");

worker.postMessage({ sayi: 1000000 });

worker.onmessage = function (e) {
 console.log("Sonuç:", e.data.sonuc);
};

// worker.js — ayrı thread
self.onmessage = function (e) {
 let sonuc = 0;
 for (let i = 0; i < e.data.sayi; i++) {
 sonuc += i;
 }
 self.postMessage({ sonuc });
};

★ Web Worker, ana thread’den bağımsız bir thread başlatır

★ DOM’a erişemez ama ağır hesaplamalar için mükemmeldir

★ postMessage ile ana thread ve worker arasında iletişim kurulur

Promise fonksiyonları ve durumları nelerdir?

Bir Promise, asenkron bir işlemin sonucunu temsil eden bir nesnedir. Üç durumu vardır:

Pending: İşlem devam ediyor, henüz bir sonuç yok

Fulfilled: İşlem başarıyla tamamlandı

Rejected: İşlem hata ile sonuçlandı

const veri = new Promise((resolve, reject) => {
 // Simüle edilmiş API çağrısı
 const basarili = true;

 if (basarili) {
 resolve({ kullanici: "Rabia" });
 } else {
 reject(new Error("Bir şeyler ters gitti!"));
 }
});

veri
 .then((data) => console.log("Başarılı:", data))
 .catch((err) => console.error("Hata:", err));

Yaygın Promise metotları:

★ Promise.all([p1, p2, p3]) bütün promise'ler tamamlanana kadar bekler; biri reddedilirse tümü reddedilir

★ Promise.allSettled([p1, p2]) reddedilenler de dahil olmak üzere tümünün sonuçlanmasını bekler

★ Promise.race([p1, p2]) ilk tamamlanan ya da reddedilen promise kazanır

★ Promise.any([p1, p2]) ilk başarıyla tamamlanan kazanır; tümü reddedilirse hata fırlatır

🔧 Build Araçları ve Modüler Yapı

Webpack’te Tree Shaking nedir?

“Tree Shaking”, kullanılmayan JavaScript kodunu final bundle’dan temizleme işlemine verilen isimdir. Ağacı silkip ölü yaprakları düşürmek gibi düşün. 🌳

// utils.js
export function kullanilan() {
 return "Ben varım!";
}

export function kullanilmayan() {
 return "Ben bundle'a girmeyeceğim";
}

// main.js
import { kullanilan } from "./utils";
kullanilan();

★ Webpack (ES module syntax ile birlikte), kullanilmayan fonksiyonunun hiçbir yerde import edilmediğini tespit eder

★ Production build’de bu fonksiyon bundle’a dahil edilmez

★ Çalışması için import/export (CommonJS require değil) kullanılması şarttır

Gulp/Grunt ile Webpack arasındaki farklar nelerdir?

Bu üçü de JavaScript build süreçlerinde kullanılan araçlardır ama temelden farklı felsefelere sahiptir.

Gulp — Grunt — Webpack farkları

Gulp ve Grunt (Task Runner):

★ Görev tabanlı çalışır: dosyaları kopyala, SCSS derle, resimleri optimize et gibi adım adım işlemler tanımlarsın

★ Modüller arasındaki bağımlılığı yönetmez

★ Pipeline mantığıyla çalışır: bir görevin çıktısı diğerinin girdisidir

Webpack (Module Bundler):

★ Uygulamanı bir bağımlılık grafiği olarak modelleyerek tüm modülleri tek (veya birden fazla) bundle’a toplar

★ JS, CSS, resim ve font gibi her dosyayı bir modül olarak ele alır

★ Code splitting, lazy loading, tree shaking gibi gelişmiş özellikler sunar

Sonuç olarak, modern projelerde Webpack (veya Vite gibi alternatifleri) tercih edilirken Gulp/Grunt daha çok eski projelerde veya basit otomasyon görevlerinde görürsün.

Webpack’in import edilmeyen CSS’leri dahil etme özelliği

Bu, birçok geliştiricinin bilmediği bir Webpack özelliği! Webpack normalde yalnızca import edilen dosyaları bundle’a dahil eder. Peki ya bir SCSS dosyasını doğrudan import etmek istemediğinde?

webpack.config.js içinde entry noktasına doğrudan CSS/SCSS dosyası ekleyebilirsin:

// webpack.config.js
module.exports = {
 entry: {
 main: "./src/index.js",
 globalStyles: "./src/styles/global.scss", // JS import olmadan dahil edilir
 },
 // ...
};

Ya da sass-loader ile birlikte @import kullanımı:

// main.scss
@import "variables";
@import "reset";
@import "typography";
/* Bu dosyaları JS'den import etmesen bile Webpack bundle'a ekler */

★ MiniCssExtractPlugin ile bu CSS'ler ayrı bir dosyaya çıkarılır

★ Global stil dosyaları (reset, typography) için bu yöntem temiz bir çözüm sunar

⚛️ React Ekosistemi

React Functional Component ve Class Component farkları

ES6 ve React Hooks gelene kadar state ve lifecycle yönetimi için class component şarttı. Artık durum değişti.

Class Component:

class Sayac extends React.Component {
 state = { adet: 0 };

 artir = () => {
 this.setState({ adet: this.state.adet + 1 });
 };

 render() {
 return <button onClick={this.artir}>{this.state.adet}</button>;
 }
}

Functional Component (modern):

function Sayac() {
 const [adet, setAdet] = useState(0);

 return <button onClick={() => setAdet(adet + 1)}>{adet}</button>;
}

★ Functional component’ler daha az kod, daha okunabilir yapı sunar

★ Hooks ile state ve side effect yönetimi yapılabilir

★ Class component’lerde this karmaşası yoktur

★ Performance açısından functional component’ler daha optimize edilebilir

useReducer ve useMemo ne işe yarar?

useReducer:

useState'in kardeşidir ama karmaşık state mantığı için daha uygundur. Redux'a benzer bir dispatch + reducer pattern uygular.

const initialState = { adet: 0 };

function reducer(state, action) {
 switch (action.type) {
 case "ARTIR":
 return { adet: state.adet + 1 };
 case "AZALT":
 return { adet: state.adet - 1 };
 default:
 return state;
 }
}

function Sayac() {
 const [state, dispatch] = useReducer(reducer, initialState);

 return (
 <>
 <p>{state.adet}</p>
 <button onClick={() => dispatch({ type: "ARTIR" })}>+</button>
 <button onClick={() => dispatch({ type: "AZALT" })}>-</button>
 </>
 );
}

★ Birden fazla alt değer içeren state için idealdir ★ State geçişleri öngörülebilir ve test edilebilir hale gelir

useMemo:

Pahalı hesaplamaların sonucunu memoize (önbelleğe) eder. Bağımlılıklar değişmediği sürece hesaplama yeniden yapılmaz.

function Liste({ ogeler, filtre }) {
 const filtrelenmis = useMemo(() => {
 return ogeler.filter((o) => o.includes(filtre)); // Pahalı işlem
 }, [ogeler, filtre]); // Yalnızca bunlar değişince yeniden hesapla

 return filtrelenmis.map((o) => <div key={o}>{o}</div>);
}

★ Her render’da yeniden hesaplanmasını istemediğin işlemler için kullan

★ Gereksiz kullanmak performansı iyileştirmez, aksine hafıza kullanımını artırır

React.memo, useMemo ve useCallback farkları nedir?

Bu üçü de performans optimizasyonu içindir ama farklı şeyler memoize ederler.

React.memo (Component memoization):

const PahaliBileseni = React.memo(function ({ deger }) {
 // Yalnızca "deger" prop'u değişince yeniden render edilir
 return <div>{deger}</div>;
});

useMemo (Değer memoization):

const hesaplananDeger = useMemo(() => {
 return pahalıHesaplama(girdi);
}, [girdi]);

useCallback (Fonksiyon memoization):

const handleClick = useCallback(() => {
 yapSomething(id);
}, [id]); // id değişmediği sürece aynı referansı döner

★ React.memo → bir component'in gereksiz render'ını önler

★ useMemo → bir değerin gereksiz yeniden hesaplanmasını önler

★ useCallback → bir fonksiyonun gereksiz yeniden oluşturulmasını önler (özellikle React.memo'lu bileşene prop olarak geçilecekse)

React ile SSR (Server Side Rendering) nasıl yapılır?

React varsayılan olarak client-side rendering yapar. SSR için iki ana yöntem var:

1. Next.js kullanmak

// pages/urunler.js
export async function getServerSideProps() {
 const res = await fetch("https://api.example.com/urunler");
 const urunler = await res.json();

 return { props: { urunler } };
}

export default function Urunler({ urunler }) {
 return urunler.map((u) => <div key={u.id}>{u.ad}</div>);
}

★ getServerSideProps her istek geldiğinde sunucuda çalışır

★ Sayfa HTML olarak hazır gönderilir; bu sayede SEO dostu bir yapı elde edilir!

2. ReactDOMServer ile manuel SSR (Express ile):

// server.js (Express)
import { renderToString } from "react-dom/server";
import App from "./App";

app.get("*", (req, res) => {
 const html = renderToString(<App />);
 res.send(`<html><body><div id="root">${html}</div></body></html>`);
});

★ Next.js, SSR altyapısını tamamen soyutladığı için pratikte en çok kullanılan çözümdür

★ Manuel yaklaşım tam kontrol ister ama hydration gibi konuları kendin yönetmen gerekir

Styled Components nedir, neden kullanmalıyız?

Styled Components, CSS-in-JS yaklaşımını benimseyerek React bileşenlerine doğrudan CSS yazmanı sağlayan bir kütüphanedir.

import styled from "styled-components";

const Buton = styled.button`
 background: ${(props) => (props.primary ? "#007bff" : "white")};
 color: ${(props) => (props.primary ? "white" : "#007bff")};
 padding: 8px 16px;
 border-radius: 4px;
 border: 2px solid #007bff;
`;

function App() {
 return (
 <>
 <Buton primary>Birincil</Buton>
 <Buton>İkincil</Buton>
 </>
 );
}

★ CSS class çakışması (conflict) yoktur; her bileşen benzersiz bir class üretir

★ Props ile dinamik stil yazmak çok temiz ve okunabilir

★ Kullanılmayan stiller otomatik temizlenir

★ Theming desteği: global tema değişkenleri ThemeProvider ile kolayca yönetilir

İtiraf etmek gerekirse ben Styled Component ve CSS in JS türevlerinden nefret ediyorum 😅. CSS Module ile SCSS kullanmayı tercih ediyorum genelde. Makaleyi yazarken styled component kodları çalışıyor mu diye test etmedim. Yanlış çalışıyorsa sorumlu değilim, dokümantasyonundan teyit etmeden benim verdiğim kodu kullanmayın 😁

React’te hataları görme: Error Boundary ne işe yarar?

Normal try-catch, React’in render metodundaki hataları yakalayamaz. Bunun için Error Boundary kullanılır.

class HataSiniri extends React.Component {
 state = { hataVarMi: false };

 static getDerivedStateFromError(error) {
 return { hataVarMi: true };
 }

 componentDidCatch(error, info) {
 console.error("Yakalanan hata:", error, info);
 }

 render() {
 if (this.state.hataVarMi) {
 return <h2>Bir şeyler ters gitti 😞</h2>;
 }
 return this.props.children;
 }
}

// Component ile kullanırken;
<HataSiniri>
 <BozukComponent />
</HataSiniri>;

★ Yalnızca class component olarak yazılabilir (şu an için hook alternatifi yok)

★ Render, lifecycle ve alt bileşen hatalarını yakalar

★ react-error-boundary kütüphanesi functional wrapper sunar

React Query ne işe yarar?

React Query (artık TanStack Query), React uygulamalarında server state yönetimini kolaylaştıran bir kütüphanedir.

import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";

function Kullanicilar() {
 const { data, isLoading, error } = useQuery({
 queryKey: ["kullanicilar"],
 queryFn: () => fetch("/api/kullanicilar").then((r) => r.json()),
 });

 if (isLoading) return <p>Yükleniyor...</p>;
 if (error) return <p>Hata oluştu!</p>;

 return data.map((k) => <div key={k.id}>{k.isim}</div>);
}

★ Otomatik caching: aynı veriyi defalarca fetch etmez

★ Background refetching: veri eskiyince arka planda günceller

★ Loading, error, success state’lerini kendin yönetmene gerek kalmaz

★ Optimistic updates, infinite scrolling, pagination desteği

🏗️ Mimari ve Tasarım Prensipleri

Dependency Injection nedir?

Dependency Injection, bir sınıf veya fonksiyonun ihtiyaç duyduğu bağımlılıkları (servisleri, nesneleri) kendi oluşturması yerine dışarıdan almasıdır.

// DI OLMADAN ❌ 
class SiparisServisi {
 constructor() {
 this.db = new VeritabaniServisi(); // Bağımlılığı kendisi oluşturuyor
 }
}

// DI İLE ✅ 
class SiparisServisi {
 constructor(db) {
 this.db = db; // Bağımlılık dışarıdan enjekte ediliyor
 }
}

const db = new VeritabaniServisi();
const siparisServisi = new SiparisServisi(db);

★ Test yazmak kolaylaşır: gerçek DB yerine mock geçilebilir

★ Sınıflar birbirinden daha az bağımlı (loose coupling) olur

★ Angular bu prensibi çok güçlü şekilde uygular

SOLID’in “S”si ne anlama gelir?

SOLID’in ilk harfi Single Responsibility Principle (Tek Sorumluluk Prensibi)’dir.

Bir sınıf veya fonksiyon, yalnızca tek bir işten sorumlu olmalı ve onu değiştirmek için yalnızca tek bir neden olmalıdır.

// ❌ SRP İHLALİ — çok fazla sorumluluk
class Kullanici {
 kaydet() { /* DB'ye yaz */ }
 emailGonder() { /* Email at */ }
 raporOlustur() { /* Rapor üret */ }
}

// ✅ SRP UYUMLU — her sınıf tek iş yapar
class KullaniciRepository { kaydet() { /* DB'ye yaz */ } }
class EmailServisi { gonder() { /* Email at */ } }
class RaporServisi { olustur() { /* Rapor üret */ } }

★ Değişiklikler izole olur; bir yerdeki değişiklik diğerini etkilemez

★ Kod daha okunabilir ve bakımı daha kolaydır

★ Test yazmak çok kolaylaşır

🌿 Git ve Versiyon Kontrolü

Git Merge vs Rebase

Bu ikisi de iki branch’i birleştirmenin yollarıdır ama tarih (history) farklı kalır.

Merge:

git checkout main
git merge feature/yeni-ozellik

★ Birleştirme geçmişini olduğu gibi korur

★ Bir “merge commit” oluşturur

★ Takım çalışmasında güvenli ve şeffaftır

Rebase:

git checkout feature/yeni-ozellik
git rebase main

★ Feature branch commit’lerini main’in üstüne “yeniden yazar”

★ Temiz, doğrusal bir tarih oluşturur

Paylaşılan branch’lerde kesinlikle kullanma; başkalarının commit geçmişi bozulur

Benim tercihim: kişisel feature branch’lerde rebase, takıma açık branch'lerde merge.

Git Cherry-pick nedir?

cherry-pick, başka bir branch'teki belirli bir commit'i mevcut branch'ine uygulamanı sağlar. Tüm branch'i merge etmek istemiyorsun ama tek bir düzeltmeye ihtiyacın var.

# Hangi commit'i almak istiyorsun?
git log feature/hata-duzeltme

# O commit'in hash'ini al ve cherry-pick et
git cherry-pick abc1234

# Birden fazla commit:
git cherry-pick abc1234 def5678

★ Production’da acil bir hotfix’i release branch’ine taşımak için mükemmel

★ Aynı commit ID’si kopyalanmaz; yeni bir commit oluşturulur

★ Conflict olabilir, aynı merge gibi çözülmesi gerekir

🚀 Performans ve Web API’leri

Google Lighthouse için geliştirme yaparken nelere dikkat etmeliyiz?

Lighthouse, Google’ın web performans, erişilebilirlik ve SEO denetim aracıdır. Mülakatçılar bu soruyla senin production kalitesine olan duyarlılığını ölçer.

Performance için:

★ Görsel optimizasyonu: WebP/AVIF formatları, width ve height atriblütleri, lazy loading (loading="lazy")

★ JavaScript bundle boyutunu küçült: code splitting, tree shaking, dynamic import

★ Kritik CSS’i inline al, render-blocking kaynakları ertele

★ Font yüklemesini optimize et: font-display: swap, preload

Accessibility için:

★ Tüm img etiketlerine anlamlı alt metni ekle

★ Renk kontrastı oranlarını WCAG standartlarına göre kontrol et

★ ARIA label’larını doğru kullan

SEO için:

★ Her sayfada benzersiz <title> ve <meta description> olsun

★ Hiyerarşik heading yapısı (h1 → h2 → h3) koru

// Next.js örneği
import Image from "next/image";

export default function Urun({ urun }) {
 return (
 <article>
 <h1>{urun.ad}</h1>
 <Image
 src={urun.resim}
 alt={urun.resimAlt}
 width={800}
 height={600}
 loading="lazy"
 />
 </article>
 );
}

Service Worker nedir?

Service Worker, tarayıcının arka planında çalışan, web sayfasından bağımsız bir JavaScript dosyasıdır. Ağ isteklerini intercepte edebilir, cache yönetimi yapabilir ve push notification gönderebilir.

// service-worker.js dosyasını kaydet
if ("serviceWorker" in navigator) {
 navigator.serviceWorker.register("/service-worker.js").then(() => {
 console.log("Service Worker kayıt başarılı!");
 });
}

// service-worker.js içinde
self.addEventListener("fetch", (event) => {
 event.respondWith(
 caches.match(event.request).then((cachedResponse) => {
 return cachedResponse || fetch(event.request);
 })
 );
});

★ Offline çalışma (PWA’nın temel taşı) için şarttır

★ Arka planda push notification gönderebilir

★ Ağ isteklerini proxy gibi yönlendirebilir, cache-first veya network-first stratejileri uygulayabilir

Array Metotları: some, every, map, filter, reduce ve aralarındaki farklar

Bu kadar teori yeter, artık ellerimizi kirletelim! 🛠️

const sayilar = [1, 2, 3, 4, 5];

// map — her elemanı dönüştürür, yeni dizi döner
const ikiKat = sayilar.map((s) => s * 2);
// [2, 4, 6, 8, 10]

// filter — koşulu sağlayanları döner
const ciftler = sayilar.filter((s) => s % 2 === 0);
// [2, 4]

// reduce — diziyi tek değere indirger
const toplam = sayilar.reduce((acc, s) => acc + s, 0);
// 15

// some — en az bir eleman koşulu sağlıyorsa true
const biriYuzdenBuyuk = sayilar.some((s) => s > 100);
// false

// every — tüm elemanlar koşulu sağlıyorsa true
const hepsiPozitif = sayilar.every((s) => s > 0);
// true

// find — koşulu sağlayan ilk elemanı döner
const ilkBuyuk = sayilar.find((s) => s > 3);
// 4

// findIndex — koşulu sağlayan ilk elemanın index'ini döner
const ilkBuyukIndex = sayilar.findIndex((s) => s > 3);
// 3

// flat — iç içe dizileri düzleştirir
const ic_ice = [1, [2, 3], [4, [5]]];
console.log(ic_ice.flat(2)); // [1, 2, 3, 4, 5]

Özet:

★ map ve filter her zaman yeni bir dizi döner; orjinali değiştirmez

★ some ve every boolean döner; find eleman veya undefined döner

★ reduce en esnek olanıdır; her türlü dönüşüm için kullanılabilir

★ forEach değer döndürmez; yalnızca yan etki için kullanılır (logging gibi)

Debounce ve Throttle nedir, hangi durumda hangisini kullanmalıyız?

İkisi de sık tetiklenen olayları (scroll, resize, keyup gibi) kontrol altına almak için kullanılan performans optimizasyon tekniklerdir. Ama çalışma mantıkları birbirinden farklıdır.

Debounce (“Duraksayınca çalış”):

Fonksiyonu, son tetiklemeden belirli bir süre geçtikten sonra çalıştırır. Kullanıcı bir şeyler yapmaya devam ettiği sürece fonksiyon bekler.

function debounce(fn, gecikme) {
 let zamanlayici;
 return function (...args) {
 clearTimeout(zamanlayici);
 zamanlayici = setTimeout(() => fn.apply(this, args), gecikme);
 };
}

const aramayiIsle = debounce((deger) => {
 console.log("API isteği gönderildi:", deger);
}, 500);

// Kullanıcı her tuşa bastığında tetiklenir ama
// 500ms duraksayınca API isteği gönderilir
inputElement.addEventListener("keyup", (e) => aramayiIsle(e.target.value));

★ Kullanıcı yazmayı bırakana kadar bekler, gereksiz API çağrısı yapmaz

★ Arama kutuları, form validasyonu ve pencere yeniden boyutlandırma için idealdir

Throttle (“Aralıklarla çalış”):

Fonksiyonu belirli zaman aralıklarında en fazla bir kez çalıştırır. Kullanıcı ne kadar hızlı tetiklerse tetiklesin, fonksiyon belirlenen süreyi bekler.

function throttle(fn, aralik) {
 let sonCalisma = 0;
 return function (...args) {
 const simdi = Date.now();
 if (simdi - sonCalisma >= aralik) {
 sonCalisma = simdi;
 fn.apply(this, args);
 }
 };
}

const scrolluIsle = throttle(() => {
 console.log("Scroll pozisyonu güncellendi:", window.scrollY);
}, 200);

// Kullanıcı ne kadar hızlı scroll ederse etsin
// fonksiyon en fazla 200ms'de bir çalışır
window.addEventListener("scroll", scrolluIsle);

★ Belirli aralıklarla düzenli güncelleme gerektiğinde kullanılır

★ Scroll takibi, sonsuz liste (infinite scroll) ve oyun döngüleri için idealdir

Hangisini ne zaman kullanmalıyım?

Debounce — Throttle

Debounce kullanmamız gereken durumlar:

★ Arama kutusu: kullanıcı yazmayı bitirince API çağrısı yap

★ Form alanı validasyonu: her tuş vuruşunda değil, duraksayınca kontrol et

★ Pencere resize sonrası yeniden hesaplama: boyutlandırma bitince tetikle

Throttle kullanmamız gereken durumlar:

★ Scroll pozisyonu takibi: her pixel’de değil, düzenli aralıklarla güncelle

★ Sonsuz liste yükleme (infinite scroll): çok sık tetiklenmesin

★ Buton tıklama: çift tıklamayı ve çoklu isteği önle

★ Mouse hareketi izleme: her harekette değil, aralıklarla işle

Kısa bir formülle özetlersek: “Bitince bir kez çalışsın” istiyorsak debounce, “Düzenli aralıklarla çalışsın” istiyorsak throttle.

📬 Geri Bildirim

Makaleyi yazarken, kaynakları belirleme ve araştırma için kendi notlarımı, yazım denetimi ve ek araştırma için Claude Opus 4.6 modelini kullandım. Resimleri üretmek için ise gemini-3.1 Flash Image Preview (Nano Banana 2) [web-search] ve Gemini 3 Pro Preview 2k (Nano Banana Pro) modelini kullandım.

Yazı ile ilgili tavsiye, öneri, eleştirileri dikkate alıyorum. İletişime geçmek isterseniz bana websitemdeki sosyal medya adreslerimden veya Linkedin üzerinden ulaşabilirsiniz.

Sevgiyle kalın, Yasin 🤗

📚 Makaleyi Yazarken Kullandığım Kaynaklar