Skip to content

📌 Ne Zaman Kullanılır?

  • ✅ Her yerde — web, server, mobil, masaüstü, IoT
  • ⚠️ Büyük projede tip güvenliği yok (TypeScript kullan)
  • ❌ Type-safe gereken projeler (TypeScript tercih et)

Önerilen Kullanım: Temel web geliştirme + DOM manipülasyon Alternatifler: TypeScript (tip güvenliği), Dart, Kotlin/JS

JavaScript Rehberi — Vanilla JS, DOM, ESNext & Patterns (EN + TR)

📘 Complete English + Turkish guide for pure JavaScript: fundamentals, DOM, async, networking, patterns, performance, testing, and modern ES2024+ features. (📘 Saf JavaScript için kapsamlı rehber — temel, DOM, async, ağ, kalıplar, performans, test ve modern ES2024+ özellikleri.)

1) Dil Temelleri

javascript
// --- Değişkenler (Variables) ---
const PI = 3.14159;          // değiştirilemez (immutable binding)
let count = 0;               // değiştirilebilir (mutable)
// var → kullanma, scope sorunları var (avoid var)

// --- Tipler (Types) ---
typeof 42            // "number"
typeof "hello"       // "string"
typeof true          // "boolean"
typeof undefined     // "undefined"
typeof null          // "object" (historical bug)
typeof {}            // "object"
typeof []            // "object" → Array.isArray([]) === true
typeof Symbol()      // "symbol"
typeof 42n           // "bigint"

// --- String ---
const name = "Fahri";
const greeting = `Merhaba, ${name}!`;        // template literal
const multi = `Satır 1
Satır 2`;

// String methodları
"hello".toUpperCase()        // "HELLO"
"hello world".split(" ")    // ["hello", "world"]
"hello".includes("ell")     // true
"hello".startsWith("he")    // true
"hello".padStart(10, ".")   // ".....hello"
"  space  ".trim()           // "space"
"abc".repeat(3)              // "abcabcabc"
"hello".at(-1)               // "o" (ES2022)
"abc".replaceAll("b", "x")  // "axc" (ES2021)

// --- Number ---
Number.isInteger(5)          // true
Number.isFinite(Infinity)    // false
Number.isNaN(NaN)            // true
parseInt("42px")             // 42
parseFloat("3.14")           // 3.14
(3.14159).toFixed(2)         // "3.14"

// --- Equality ---
5 === 5       // true (strict — her zaman bunu kullan)
5 == "5"      // true (loose — kaçın / avoid)
null == undefined  // true (tek istisna)
Object.is(NaN, NaN)  // true

2) Functions, Scope & Closures

javascript
// Function declaration (hoisted)
function add(a, b) {
  return a + b;
}

// Function expression (not hoisted)
const multiply = function(a, b) {
  return a * b;
};

// Arrow function (lexical this)
const increment = (x) => x + 1;
const greet = (name) => `Hello, ${name}!`;

// Default parameters
function createUser(name, role = "viewer") {
  return { name, role };
}

// Rest parameters
function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4);  // 10

// --- this & bind ---
const obj = {
  name: "Fahri",
  greet() { return `Hi, ${this.name}`; }
};
const fn = obj.greet;
fn();                    // undefined (lost context)
fn.call(obj);            // "Hi, Fahri"
const bound = fn.bind(obj);
bound();                 // "Hi, Fahri"

// Arrow function → this'i dışarıdan alır (lexical this)
const timer = {
  seconds: 0,
  start() {
    setInterval(() => { this.seconds++; }, 1000);  // works
  }
};

// --- Closures ---
function makeCounter() {
  let count = 0;
  return {
    next: () => ++count,
    reset: () => (count = 0),
    value: () => count,
  };
}
const counter = makeCounter();
counter.next();  // 1
counter.next();  // 2
counter.reset(); // 0

// --- IIFE (Immediately Invoked) ---
const result = (() => {
  const secret = "hidden";
  return secret.toUpperCase();
})();

// --- Currying ---
const multiply2 = (a) => (b) => a * b;
const double = multiply2(2);
double(5);  // 10

3) Objects, Prototypes & Classes

javascript
// --- Object literals ---
const user = {
  name: "Fahri",
  age: 25,
  greet() { return `Hi, ${this.name}`; },
  get info() { return `${this.name} (${this.age})`; },
};

// Computed property names
const key = "role";
const obj2 = { [key]: "admin" };  // { role: "admin" }

// Object methods
Object.keys(user);       // ["name", "age", "greet", "info"]
Object.values(user);     // ["Fahri", 25, ...]
Object.entries(user);    // [["name","Fahri"], ["age",25], ...]
Object.assign({}, user, { age: 26 });   // shallow merge
const clone = structuredClone(user);     // deep clone (ES2022)

// Optional chaining & nullish coalescing
const city = user?.address?.city ?? "Bilinmiyor";

// --- Classes ---
class Animal {
  #sound;  // private field (ES2022)

  constructor(name, sound) {
    this.name = name;
    this.#sound = sound;
  }

  speak() {
    return `${this.name} says ${this.#sound}`;
  }

  static create(name, sound) {
    return new Animal(name, sound);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name, "Woof");
  }

  fetch(item) {
    return `${this.name} fetches ${item}`;
  }
}

const dog = new Dog("Rex");
dog.speak();      // "Rex says Woof"
dog.fetch("ball"); // "Rex fetches ball"

4) Arrays & Iterables

javascript
const nums = [3, 1, 4, 1, 5, 9, 2, 6];

// --- Dönüştürme (Transform) ---
nums.map(n => n * 2);           // [6, 2, 8, 2, 10, 18, 4, 12]
nums.filter(n => n > 3);        // [4, 5, 9, 6]
nums.reduce((sum, n) => sum + n, 0);  // 31
nums.find(n => n > 4);          // 5
nums.findIndex(n => n > 4);     // 4
nums.every(n => n > 0);         // true
nums.some(n => n > 8);          // true
nums.includes(4);               // true
nums.flat();                    // flatten nested arrays
nums.flatMap(n => [n, n * 2]);  // flat + map

// --- Sıralama ---
[...nums].sort((a, b) => a - b);         // artan (ascending)
[...nums].sort((a, b) => b - a);         // azalan (descending)
// Locale-aware string sort
["ç", "a", "ö"].sort((a, b) => a.localeCompare(b, "tr"));

// --- Yeni ES2023+ methodlar ---
nums.findLast(n => n > 3);         // 6 (sondan ara)
nums.findLastIndex(n => n > 3);    // 7
nums.toSorted((a, b) => a - b);    // immutable sort
nums.toReversed();                  // immutable reverse
nums.toSpliced(2, 1, 99);          // immutable splice
nums.with(0, 100);                  // immutable replace at index

// --- Array.from & Array.of ---
Array.from("hello");               // ["h","e","l","l","o"]
Array.from({ length: 5 }, (_, i) => i);  // [0,1,2,3,4]
Array.of(1, 2, 3);                 // [1, 2, 3]

// --- Set & Map ---
const unique = [...new Set([1, 2, 2, 3])];  // [1, 2, 3]

const cache = new Map();
cache.set("key", "value");
cache.get("key");    // "value"
cache.has("key");    // true
cache.size;          // 1

// --- WeakMap & WeakRef ---
const wm = new WeakMap();  // GC-friendly, keys must be objects

5) Destructuring, Spread & Rest

javascript
// --- Array destructuring ---
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first=1, second=2, rest=[3,4,5]

const [a, , b] = [1, 2, 3];  // skip: a=1, b=3

// Swap
let x = 1, y = 2;
[x, y] = [y, x];

// --- Object destructuring ---
const { name, age, role = "viewer" } = user;

// Rename
const { name: userName, age: userAge } = user;

// Nested
const { address: { city } = {} } = user;

// --- Spread ---
const arr2 = [...nums, 10, 11];
const obj3 = { ...user, age: 30 };  // override age

// --- Function params ---
function render({ title, body, footer = "default" }) {
  console.log(title, body, footer);
}
render({ title: "Hi", body: "Content" });

6) Module System (ESM vs CommonJS)

javascript
// =============================================
// ESM (ECMAScript Modules) — Modern standart
// =============================================

// --- Named exports ---
// utils.js
export const PI = 3.14159;
export function area(r) { return PI * r * r; }

// --- Default export ---
// logger.js
export default class Logger {
  log(msg) { console.log(`[LOG] ${msg}`); }
}

// --- Import ---
import Logger from "./logger.js";
import { PI, area } from "./utils.js";
import * as utils from "./utils.js";

// --- Dynamic import (code splitting / lazy loading) ---
const module = await import("./heavy-module.js");
module.doSomething();

// Kosullu yukleme
if (condition) {
  const { helper } = await import("./optional.js");
  helper();
}

// --- Re-export ---
export { area } from "./utils.js";
export { default as Logger } from "./logger.js";

// --- import.meta ---
console.log(import.meta.url);      // dosyanin tam URL'i
console.log(import.meta.resolve("./lib.js"));  // cozulmus yol

// HTML'de ESM kullanimi
// <script type="module" src="app.js"></script>
// <script type="module">
//   import { greet } from "./utils.js";
//   greet();
// </script>
javascript
// =============================================
// CommonJS (CJS) — Node.js geleneksel format
// =============================================

// --- Export ---
// utils.cjs
const PI = 3.14159;
function area(r) { return PI * r * r; }
module.exports = { PI, area };

// Tek export
// logger.cjs
class Logger {
  log(msg) { console.log(`[LOG] ${msg}`); }
}
module.exports = Logger;

// --- Require ---
const { PI, area } = require("./utils.cjs");
const Logger = require("./logger.cjs");

// Kosullu yukleme (senkron)
if (condition) {
  const helper = require("./optional.cjs");
}

ESM vs CommonJS karsilastirmasi:

ÖzellikESM (import/export)CommonJS (require/module.exports)
YuklemeAsenkron (statik analiz)Senkron
Tree-shakingDesteklerDesteklemez
Top-level awaitDesteklerDesteklemez
BrowserDogrudan desteklerBundler gerekir
Node.js.mjs veya "type": "module"Varsayilan
Dinamik importimport()require()
this (top-level)undefinedmodule.exports

7) Async Derinlemesine: Callback → Promise → async/await

javascript
// =============================================
// Evrim: Callback → Promise → async/await
// =============================================

// --- 1. Callback (eski yontem) ---
// "Callback hell" / "Pyramid of doom" sorunu
function getUser(id, callback) {
  setTimeout(() => {
    callback(null, { id, name: "Fahri" });
  }, 100);
}

function getPosts(userId, callback) {
  setTimeout(() => {
    callback(null, [{ title: "Post 1" }]);
  }, 100);
}

// ic ice callback'ler — okunamaz hale gelir
getUser(1, (err, user) => {
  if (err) return console.error(err);
  getPosts(user.id, (err, posts) => {
    if (err) return console.error(err);
    console.log(user.name, posts);
  });
});

// --- 2. Promise (ES2015) ---
function getUserP(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve({ id, name: "Fahri" }), 100);
  });
}

function getPostsP(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve([{ title: "Post 1" }]), 100);
  });
}

// Zincirleme (chaining) — daha okunaklı
getUserP(1)
  .then(user => getPostsP(user.id).then(posts => ({ user, posts })))
  .then(({ user, posts }) => console.log(user.name, posts))
  .catch(err => console.error(err));

// --- 3. async/await (ES2017) — en temiz yontem ---
async function loadUserData(id) {
  try {
    const user = await getUserP(id);
    const posts = await getPostsP(user.id);
    console.log(user.name, posts);
    return { user, posts };
  } catch (err) {
    console.error("Veri yuklenemedi:", err.message);
    throw err;
  }
}
javascript
// =============================================
// Promise combinators (paralel islemler)
// =============================================

// --- Promise.all — hepsi basarili olmalı ---
// Biri bile reject olursa tum Promise reject olur
const [users, posts, comments] = await Promise.all([
  fetch("/api/users").then(r => r.json()),
  fetch("/api/posts").then(r => r.json()),
  fetch("/api/comments").then(r => r.json()),
]);

// --- Promise.allSettled — hepsi bitene kadar bekle ---
// Basarili veya basarisiz, hepsinin sonucunu dondurur
const results = await Promise.allSettled([
  fetch("/api/a"),
  fetch("/api/b"),
  fetch("/api/c"),
]);
results.forEach(r => {
  if (r.status === "fulfilled") console.log("Basarili:", r.value);
  else console.error("Basarisiz:", r.reason);
});

// Sadece basarili sonuclari filtrele
const successful = results
  .filter(r => r.status === "fulfilled")
  .map(r => r.value);

// --- Promise.race — ilk biten kazanir ---
// Timeout pattern
const fastest = await Promise.race([
  fetch("/api/data"),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error("Timeout")), 5000)
  ),
]);

// --- Promise.any — ilk basarili olan kazanir ---
// Tumu basarisiz olursa AggregateError firlatir
try {
  const first = await Promise.any([
    fetch("https://mirror1.example.com/data"),
    fetch("https://mirror2.example.com/data"),
    fetch("https://mirror3.example.com/data"),
  ]);
  console.log("Ilk basarili:", first);
} catch (err) {
  // AggregateError: All promises were rejected
  console.error(err.errors); // tum hatalarin dizisi
}
javascript
// =============================================
// Promise.withResolvers (ES2024)
// =============================================

// Eskiden boyle yapiliyordu:
let resolve, reject;
const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

// Simdi tek satirda:
const { promise: p, resolve: res, reject: rej } = Promise.withResolvers();

// Ornek: event-tabanlı islemi Promise'e cevirme
function waitForEvent(element, eventName) {
  const { promise, resolve } = Promise.withResolvers();
  element.addEventListener(eventName, resolve, { once: true });
  return promise;
}
const clickEvent = await waitForEvent(button, "click");
javascript
// =============================================
// Async Iterators & for-await-of
// =============================================

// Async generator fonksiyon
async function* fetchPages(baseUrl) {
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const res = await fetch(`${baseUrl}?page=${page}`);
    const data = await res.json();
    hasMore = data.hasMore;
    page++;
    yield data.items;
  }
}

// Kullanim
for await (const items of fetchPages("/api/products")) {
  items.forEach(item => console.log(item.name));
}

// ReadableStream ile async iteration
async function readStream(response) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    console.log(decoder.decode(value));
  }
}
javascript
// =============================================
// Top-level await (ES2022, sadece ESM)
// =============================================

// Modul seviyesinde dogrudan await kullanilabilir
const config = await fetch("/config.json").then(r => r.json());
const db = await connectToDatabase(config.dbUrl);

export { config, db };

// Baska bir modülde import ederken, o modul
// yukaridaki await'ler cozulene kadar bekler
import { config, db } from "./setup.js";
// config ve db burada hazir
javascript
// =============================================
// Async Error Handling Kaliplari
// =============================================

// --- try/catch wrapper (tekrar eden try/catch'den kurtulma) ---
async function tryCatch(promise) {
  try {
    const data = await promise;
    return [data, null];
  } catch (err) {
    return [null, err];
  }
}

// Kullanim
const [user, error] = await tryCatch(fetchUser(1));
if (error) {
  console.error("Kullanici yuklenemedi:", error.message);
}

// --- Retry pattern ---
async function withRetry(fn, maxRetries = 3, delay = 1000) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (err) {
      if (i === maxRetries - 1) throw err;
      console.warn(`Deneme ${i + 1} basarisiz, tekrar deneniyor...`);
      await new Promise(r => setTimeout(r, delay * (i + 1)));
    }
  }
}

const data = await withRetry(() => fetch("/api/flaky").then(r => r.json()));

// --- Concurrent limit (paralel siniri) ---
async function mapWithLimit(items, limit, fn) {
  const results = [];
  const executing = new Set();

  for (const item of items) {
    const p = fn(item).then(result => {
      executing.delete(p);
      return result;
    });
    executing.add(p);
    results.push(p);

    if (executing.size >= limit) {
      await Promise.race(executing);
    }
  }

  return Promise.all(results);
}

// Ayni anda en fazla 3 istek
const urls = ["/api/1", "/api/2", "/api/3", "/api/4", "/api/5"];
const responses = await mapWithLimit(urls, 3, url => fetch(url));
javascript
// =============================================
// Event Loop sirasi (ozet)
// =============================================

// 1. Sync code (call stack)
// 2. Microtasks (Promise.then, queueMicrotask, MutationObserver)
// 3. Macrotasks (setTimeout, setInterval, I/O, requestAnimationFrame)

console.log("1");
setTimeout(() => console.log("4"), 0);
queueMicrotask(() => console.log("2.5"));
Promise.resolve().then(() => console.log("3"));
console.log("2");
// Cikti: 1, 2, 2.5, 3, 4

8) DOM Derinlemesine

javascript
// =============================================
// Seciciler (Selectors)
// =============================================

const el = document.querySelector("#app");           // tek eleman
const items = document.querySelectorAll(".item");     // NodeList
const byId = document.getElementById("main");        // ID ile
const byClass = document.getElementsByClassName("card");  // HTMLCollection (canli)
const byTag = document.getElementsByTagName("div");       // HTMLCollection (canli)

// querySelector vs getElementById farki:
// querySelector CSS secici kullanir, daha esnek
// getElementById biraz daha hizli
// querySelectorAll statik NodeList dondurur (canli degil)
javascript
// =============================================
// createElement & DOM olusturma
// =============================================

// --- Temel eleman olusturma ---
const div = document.createElement("div");
div.className = "card";
div.id = "card-1";
div.textContent = "Merhaba Dunya";
document.body.appendChild(div);

// --- Daha karmasik eleman ---
const article = document.createElement("article");
article.innerHTML = `
  <h2>Baslik</h2>
  <p>Paragraf icerigi</p>
`;
document.querySelector("#content").appendChild(article);

// --- insertAdjacentHTML (innerHTML'den daha esnek) ---
el.insertAdjacentHTML("beforebegin", "<p>Once</p>");   // elemandan once
el.insertAdjacentHTML("afterbegin", "<p>Ic bas</p>");   // ic kismin basina
el.insertAdjacentHTML("beforeend", "<p>Ic son</p>");    // ic kismin sonuna
el.insertAdjacentHTML("afterend", "<p>Sonra</p>");      // elemandan sonra

// --- insertAdjacentElement ---
const span = document.createElement("span");
span.textContent = "Yeni";
el.insertAdjacentElement("afterbegin", span);

// --- Eleman silme ---
el.remove();                              // kendini sil
parent.removeChild(child);                // cocuk elemani sil

// --- Eleman degistirme ---
const newEl = document.createElement("div");
oldEl.replaceWith(newEl);                 // modern
parent.replaceChild(newEl, oldEl);        // eski yontem
javascript
// =============================================
// DocumentFragment (performansli toplu ekleme)
// =============================================

// Sorun: Her appendChild DOM'u yeniden cizer (reflow)
// Cozum: DocumentFragment kullanarak tek seferde ekle

const fragment = document.createDocumentFragment();
const data = ["Elma", "Armut", "Portakal", "Muz", "Cilek"];

data.forEach(item => {
  const li = document.createElement("li");
  li.textContent = item;
  li.className = "fruit-item";
  fragment.appendChild(li);  // DOM'a dokunmuyor
});

document.querySelector("#fruit-list").appendChild(fragment);
// Tek bir reflow — 5 ayri ekleme yerine

// Alternatif: template elementi
const template = document.querySelector("#card-template");
const clone = template.content.cloneNode(true);
clone.querySelector(".title").textContent = "Yeni Kart";
document.querySelector("#cards").appendChild(clone);
javascript
// =============================================
// classList API
// =============================================

const box = document.querySelector(".box");

box.classList.add("active");                    // sinif ekle
box.classList.add("highlight", "visible");      // birden fazla
box.classList.remove("hidden");                 // sinif kaldir
box.classList.toggle("open");                   // varsa kaldir, yoksa ekle
box.classList.toggle("dark", isDarkMode);       // kosullu toggle
box.classList.contains("active");               // var mi? true/false
box.classList.replace("old-class", "new-class"); // sinif degistir

// Tum siniflari listele
console.log([...box.classList]);  // ["box", "active", "highlight", "visible"]

// className ile toplu degistirme (dikkat: hepsini siler)
box.className = "box active";
javascript
// =============================================
// dataset API (data-* attribute'leri)
// =============================================

// HTML: <div id="user" data-user-id="42" data-role="admin" data-is-active="true">

const userEl = document.querySelector("#user");

// Okuma (data-user-id → dataset.userId olur, camelCase)
console.log(userEl.dataset.userId);    // "42"
console.log(userEl.dataset.role);      // "admin"
console.log(userEl.dataset.isActive);  // "true" (her zaman string)

// Yazma
userEl.dataset.score = "100";          // data-score="100" eklenir
userEl.dataset.userId = "99";          // data-user-id="99" olur

// Silme
delete userEl.dataset.role;            // data-role attribute'u silinir

// Tum data attribute'lerini gorme
console.log({ ...userEl.dataset });
// { userId: "99", isActive: "true", score: "100" }
javascript
// =============================================
// DOM Traversal (agacta gezinme)
// =============================================

const item = document.querySelector(".item");

// --- Ebeveynler ---
item.parentElement;                   // dogrudan ust eleman
item.closest(".container");           // en yakin eslesen ata
item.closest("[data-section]");       // attribute ile ara

// --- Cocuklar ---
item.children;                        // HTMLCollection (sadece elemanlar)
item.childNodes;                      // NodeList (text node'lar dahil)
item.firstElementChild;               // ilk cocuk eleman
item.lastElementChild;                // son cocuk eleman
item.childElementCount;               // cocuk eleman sayisi

// --- Kardesler ---
item.previousElementSibling;          // onceki kardes eleman
item.nextElementSibling;              // sonraki kardes eleman

// --- Tum cocuklari donme ---
for (const child of item.children) {
  console.log(child.tagName, child.textContent);
}

// querySelectorAll ile cocuklarda arama
const links = item.querySelectorAll("a[href]");
javascript
// =============================================
// Event Delegation (olay delegasyonu)
// =============================================

// Sorun: 1000 elemana 1000 listener eklemek bellek israfı
// Cozum: Ust elemana tek listener ekle, event.target ile kontrol et

// --- Temel event delegation ---
document.querySelector("#todo-list").addEventListener("click", (e) => {
  // Hangi butona tiklandi?
  const deleteBtn = e.target.closest("[data-action='delete']");
  if (deleteBtn) {
    const todoItem = deleteBtn.closest(".todo-item");
    const id = todoItem.dataset.id;
    deleteTodo(id);
    todoItem.remove();
    return;
  }

  const editBtn = e.target.closest("[data-action='edit']");
  if (editBtn) {
    const todoItem = editBtn.closest(".todo-item");
    startEdit(todoItem.dataset.id);
    return;
  }

  // Checkbox toggle
  const checkbox = e.target.closest("input[type='checkbox']");
  if (checkbox) {
    const todoItem = checkbox.closest(".todo-item");
    toggleComplete(todoItem.dataset.id, checkbox.checked);
  }
});

// --- Dinamik eklenen elemanlar icin ---
// Event delegation sayesinde sonradan eklenen elemanlar da calisir
function addTodo(text) {
  const li = document.createElement("li");
  li.className = "todo-item";
  li.dataset.id = Date.now();
  li.innerHTML = `
    <input type="checkbox">
    <span>${text}</span>
    <button data-action="edit">Duzenle</button>
    <button data-action="delete">Sil</button>
  `;
  document.querySelector("#todo-list").appendChild(li);
  // Yeni listener eklemeye gerek yok!
}
javascript
// =============================================
// Events (ileri duzey)
// =============================================

// --- Event ozellikleri ---
el.addEventListener("click", (e) => {
  e.preventDefault();           // varsayilan davranisi engelle
  e.stopPropagation();          // ustlere yayilmayi durdur
  e.stopImmediatePropagation(); // ayni elemandaki diger listener'lari da durdur
  e.target;                     // tiklanan orijinal eleman
  e.currentTarget;              // listener'in bagli oldugu eleman
  e.type;                       // "click"
  e.timeStamp;                  // olay zamani
});

// --- once: true (tek seferlik) ---
el.addEventListener("click", handler, { once: true });

// --- passive: true (scroll performansi) ---
window.addEventListener("scroll", onScroll, { passive: true });

// --- capture phase ---
parent.addEventListener("click", handler, { capture: true });
// veya
parent.addEventListener("click", handler, true);

// --- Custom Events ---
const event = new CustomEvent("userLogin", {
  detail: { userId: 42, username: "fahri" },
  bubbles: true,
  cancelable: true,
});
el.dispatchEvent(event);

// Dinleme
el.addEventListener("userLogin", (e) => {
  console.log("Kullanici girisi:", e.detail.username);
});

// --- Form handling ---
const form = document.querySelector("form");
form.addEventListener("submit", (e) => {
  e.preventDefault();
  const data = new FormData(form);
  const obj = Object.fromEntries(data);
  console.log(obj);
});

9) Fetch API Genisletilmis

javascript
// =============================================
// HTTP Metodlari (GET / POST / PUT / PATCH / DELETE)
// =============================================

// --- GET ---
async function getUsers() {
  const res = await fetch("/api/users");
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
  return await res.json();
}

// GET ile query parametreleri
const params = new URLSearchParams({
  page: 1,
  limit: 20,
  sort: "name",
  order: "asc",
});
const res = await fetch(`/api/users?${params}`);

// --- POST ---
async function createUser(userData) {
  const res = await fetch("/api/users", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${token}`,
    },
    body: JSON.stringify(userData),
  });

  if (!res.ok) {
    const error = await res.json();
    throw new Error(error.message || `HTTP ${res.status}`);
  }

  return await res.json();
}

const newUser = await createUser({ name: "Fahri", role: "admin" });

// --- PUT (tum kaydi guncelle) ---
async function updateUser(id, userData) {
  const res = await fetch(`/api/users/${id}`, {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(userData),
  });
  return await res.json();
}

// --- PATCH (kismi guncelleme) ---
async function patchUser(id, partialData) {
  const res = await fetch(`/api/users/${id}`, {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(partialData),
  });
  return await res.json();
}

await patchUser(42, { role: "editor" });  // sadece role alanini guncelle

// --- DELETE ---
async function deleteUser(id) {
  const res = await fetch(`/api/users/${id}`, {
    method: "DELETE",
    headers: { "Authorization": `Bearer ${token}` },
  });

  if (!res.ok) throw new Error(`Silme basarisiz: HTTP ${res.status}`);
  return res.status === 204 ? null : await res.json();
}
javascript
// =============================================
// Headers API
// =============================================

// Headers olusturma
const headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("Authorization", "Bearer token123");
headers.set("X-Custom-Header", "value");

// Headers okuma
headers.get("Content-Type");  // "application/json"
headers.has("Authorization"); // true

// Headers uzerinde donme
for (const [key, value] of headers) {
  console.log(`${key}: ${value}`);
}

// Headers ile fetch
const res = await fetch("/api/data", { headers });

// Response header'larini okuma
const contentType = res.headers.get("Content-Type");
const rateLimit = res.headers.get("X-RateLimit-Remaining");
javascript
// =============================================
// AbortController (istek iptali ve timeout)
// =============================================

// --- Basit timeout ---
async function fetchWithTimeout(url, timeoutMs = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

  try {
    const res = await fetch(url, { signal: controller.signal });
    clearTimeout(timeoutId);
    return await res.json();
  } catch (err) {
    clearTimeout(timeoutId);
    if (err.name === "AbortError") {
      throw new Error(`Istek zaman asimina ugradi (${timeoutMs}ms)`);
    }
    throw err;
  }
}

// --- AbortSignal.timeout (modern alternatif) ---
const res = await fetch("/api/data", {
  signal: AbortSignal.timeout(5000),
});

// --- Kullanici iptali (ornegin sayfa degistirme) ---
const controller = new AbortController();

// Birden fazla istegi ayni controller ile iptal et
async function loadDashboard(signal) {
  const [users, stats, logs] = await Promise.all([
    fetch("/api/users", { signal }).then(r => r.json()),
    fetch("/api/stats", { signal }).then(r => r.json()),
    fetch("/api/logs", { signal }).then(r => r.json()),
  ]);
  return { users, stats, logs };
}

// Iptal etme
controller.abort();
// Tum 3 istek de iptal olur

// --- AbortSignal.any (birden fazla sinyal birlesimi) ---
const userController = new AbortController();
const timeoutSignal = AbortSignal.timeout(10000);

const res2 = await fetch("/api/data", {
  signal: AbortSignal.any([userController.signal, timeoutSignal]),
});
javascript
// =============================================
// File Upload (FormData)
// =============================================

// --- Basit dosya yukleme ---
async function uploadFile(file) {
  const formData = new FormData();
  formData.append("file", file);
  formData.append("description", "Profil fotografi");

  const res = await fetch("/api/upload", {
    method: "POST",
    body: formData,
    // Content-Type header'i ekleme! Browser otomatik ayarlar (boundary dahil)
  });

  return await res.json();
}

// Input element ile kullanim
const fileInput = document.querySelector("input[type='file']");
fileInput.addEventListener("change", async (e) => {
  const file = e.target.files[0];
  if (!file) return;

  // Dosya boyutu kontrolu (5MB limit)
  if (file.size > 5 * 1024 * 1024) {
    alert("Dosya 5MB'dan buyuk olamaz");
    return;
  }

  // Dosya tipi kontrolu
  if (!file.type.startsWith("image/")) {
    alert("Sadece resim dosyalari yuklenebilir");
    return;
  }

  const result = await uploadFile(file);
  console.log("Yuklendi:", result.url);
});

// --- Coklu dosya yukleme ---
async function uploadMultiple(files) {
  const formData = new FormData();
  for (const file of files) {
    formData.append("files", file);
  }

  return await fetch("/api/upload/batch", {
    method: "POST",
    body: formData,
  }).then(r => r.json());
}

// --- Yukleme ilerlemesi (XMLHttpRequest ile) ---
function uploadWithProgress(file, onProgress) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    const formData = new FormData();
    formData.append("file", file);

    xhr.upload.addEventListener("progress", (e) => {
      if (e.lengthComputable) {
        onProgress(Math.round((e.loaded / e.total) * 100));
      }
    });

    xhr.addEventListener("load", () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(new Error(`HTTP ${xhr.status}`));
      }
    });

    xhr.addEventListener("error", () => reject(new Error("Ag hatasi")));
    xhr.open("POST", "/api/upload");
    xhr.send(formData);
  });
}

await uploadWithProgress(file, (percent) => {
  console.log(`Yukleniyor: %${percent}`);
  progressBar.style.width = `${percent}%`;
});
javascript
// =============================================
// Fetch Error Handling (kapsamli)
// =============================================

// Onemli: fetch() sadece ag hatalarinda reject olur.
// 404, 500 gibi HTTP hatalari reject OLMAZ! res.ok kontrolu sart.

async function apiFetch(url, options = {}) {
  const defaultHeaders = {
    "Content-Type": "application/json",
    "Accept": "application/json",
  };

  try {
    const res = await fetch(url, {
      ...options,
      headers: { ...defaultHeaders, ...options.headers },
    });

    // HTTP hata kontrolu
    if (!res.ok) {
      const errorBody = await res.json().catch(() => null);
      const error = new Error(
        errorBody?.message || `HTTP ${res.status}: ${res.statusText}`
      );
      error.status = res.status;
      error.response = errorBody;
      throw error;
    }

    // 204 No Content icin bos dondurun
    if (res.status === 204) return null;

    return await res.json();
  } catch (err) {
    if (err.name === "TypeError") {
      // Ag hatasi (offline, DNS, CORS)
      throw new Error("Sunucuya ulasilamadi. Internet baglantinizi kontrol edin.");
    }
    throw err;
  }
}

// Kullanim
try {
  const users = await apiFetch("/api/users");
} catch (err) {
  if (err.status === 401) {
    redirectToLogin();
  } else if (err.status === 404) {
    showNotFound();
  } else {
    showError(err.message);
  }
}

CORS notlari:

  • Server Access-Control-Allow-Origin header'i donmeli
  • Preflight: OPTIONS request for non-simple methods/headers
  • Credentials: fetch(url, { credentials: "include" })

10) Storage

javascript
// --- localStorage (kalici / persistent) ---
localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme");
localStorage.removeItem("theme");
localStorage.clear();

// JSON sakla
localStorage.setItem("user", JSON.stringify({ name: "Fahri" }));
const user2 = JSON.parse(localStorage.getItem("user"));

// --- sessionStorage (sekme kapaninca silinir) ---
sessionStorage.setItem("token", "abc123");

// --- IndexedDB (buyuk veriler, offline) ---
// idb kutuphanesi ile daha kolay:
// npm install idb
import { openDB } from "idb";
const db = await openDB("myDB", 1, {
  upgrade(db) {
    db.createObjectStore("items", { keyPath: "id" });
  },
});
await db.put("items", { id: 1, name: "Item 1" });
const item = await db.get("items", 1);

11) ES2022-2024+ Yeni Ozellikler

javascript
// --- ES2022 ---
// Top-level await
const data = await fetch("/data.json").then(r => r.json());

// Private class fields
class Counter {
  #count = 0;
  increment() { this.#count++; }
  get value() { return this.#count; }
}

// Object.hasOwn (Object.prototype.hasOwnProperty yerine)
Object.hasOwn({ a: 1 }, "a");  // true

// at() — negatif index
[1, 2, 3].at(-1);   // 3
"hello".at(-1);      // "o"

// Error cause
throw new Error("Failed", { cause: originalError });

// structuredClone (deep clone)
const deep = structuredClone({ nested: { a: 1 } });

// --- ES2023 ---
// Array: findLast, findLastIndex
[1, 2, 3, 4].findLast(n => n % 2 === 0);      // 4

// Immutable array methods
[3, 1, 2].toSorted();         // [1, 2, 3] (orijinal degismez)
[1, 2, 3].toReversed();       // [3, 2, 1]
[1, 2, 3].with(1, 99);        // [1, 99, 3]
[1, 2, 3, 4].toSpliced(1, 2); // [1, 4]

// --- ES2024 ---
// Object.groupBy
const people = [
  { name: "A", age: 25 },
  { name: "B", age: 30 },
  { name: "C", age: 25 },
];
Object.groupBy(people, p => p.age);
// { 25: [{name:"A",...}, {name:"C",...}], 30: [{name:"B",...}] }

// Promise.withResolvers
const { promise, resolve, reject } = Promise.withResolvers();

// Map.groupBy
Map.groupBy(people, p => p.age);

// RegExp v flag (Unicode sets)
/[\p{Emoji}--\p{ASCII}]/v;
javascript
// =============================================
// ES2024+ Detayli Ornekler
// =============================================

// --- Object.groupBy pratik ornekler ---
const products = [
  { name: "Laptop", category: "elektronik", price: 15000 },
  { name: "Telefon", category: "elektronik", price: 8000 },
  { name: "Kitap", category: "egitim", price: 50 },
  { name: "Kurs", category: "egitim", price: 500 },
  { name: "Masa", category: "mobilya", price: 2000 },
];

// Kategoriye gore gruplama
const byCategory = Object.groupBy(products, p => p.category);
// {
//   elektronik: [{ name: "Laptop", ... }, { name: "Telefon", ... }],
//   egitim: [{ name: "Kitap", ... }, { name: "Kurs", ... }],
//   mobilya: [{ name: "Masa", ... }]
// }

// Fiyat araligina gore gruplama
const byPriceRange = Object.groupBy(products, p => {
  if (p.price < 100) return "ucuz";
  if (p.price < 5000) return "orta";
  return "pahali";
});

// --- Immutable Array methodlari detayli ---
const scores = [85, 92, 78, 95, 88];

// toSorted — orijinal dizi degismez
const sorted = scores.toSorted((a, b) => b - a);  // [95, 92, 88, 85, 78]
console.log(scores);  // [85, 92, 78, 95, 88] — ayni

// toReversed — orijinal dizi degismez
const reversed = scores.toReversed();  // [88, 95, 78, 92, 85]

// toSpliced — orijinal dizi degismez
const withoutThird = scores.toSpliced(2, 1);      // [85, 92, 95, 88]
const withInsert = scores.toSpliced(2, 0, 100);   // [85, 92, 100, 78, 95, 88]

// with — belirli index'teki degeri degistir
const updated = scores.with(0, 99);  // [99, 92, 78, 95, 88]

// Zincirleme (chaining)
const result = scores
  .toSorted((a, b) => b - a)
  .toSpliced(3)          // ilk 3'u al
  .with(0, 100);          // birincinin puanini 100 yap
// [100, 92, 88]

12) Error Handling & Debugging

javascript
// --- try/catch/finally ---
try {
  const data = JSON.parse(input);
} catch (err) {
  if (err instanceof SyntaxError) {
    console.error("Gecersiz JSON:", err.message);
  } else {
    throw err;  // bilmedigin hatalari yeniden firlat
  }
} finally {
  cleanup();
}

// --- Custom Error ---
class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

throw new ValidationError("email", "Gecersiz email formati");

// --- Global error handler ---
window.addEventListener("error", (e) => {
  console.error("Uncaught:", e.message, e.filename, e.lineno);
});

window.addEventListener("unhandledrejection", (e) => {
  console.error("Unhandled promise:", e.reason);
});

// --- Debugging ---
console.log("basic log");
console.table([{ a: 1 }, { a: 2 }]);
console.group("Group");
  console.log("inside");
console.groupEnd();
console.time("op");
  heavyOperation();
console.timeEnd("op");
debugger;  // breakpoint

13) Performance & Memory

javascript
// --- Debounce (son cagiridan N ms sonra calistir) ---
function debounce(fn, ms) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), ms);
  };
}
input.addEventListener("input", debounce(handleSearch, 300));

// --- Throttle (N ms'de en fazla 1 kez) ---
function throttle(fn, ms) {
  let last = 0;
  return (...args) => {
    const now = Date.now();
    if (now - last >= ms) {
      last = now;
      fn(...args);
    }
  };
}
window.addEventListener("scroll", throttle(handleScroll, 100));

// --- requestAnimationFrame ---
function animate() {
  // update positions...
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

// --- Performance API ---
performance.mark("start");
doWork();
performance.mark("end");
performance.measure("doWork", "start", "end");
const [measure] = performance.getEntriesByName("doWork");
console.log(`${measure.duration}ms`);

14) Web APIs

javascript
// =============================================
// IntersectionObserver (gorunurluk izleme)
// =============================================

// Lazy loading, sonsuz scroll, animasyon tetikleme icin kullanilir

const observer = new IntersectionObserver((entries, obs) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      // Eleman gorunur alana girdi
      const img = entry.target;
      img.src = img.dataset.src;          // lazy load
      img.classList.add("loaded");
      obs.unobserve(entry.target);        // artik izleme
    }
  });
}, {
  root: null,              // viewport (varsayilan)
  rootMargin: "100px",     // 100px onceden tetikle
  threshold: 0.1,          // %10'u gorunur olunca
});

// Tum lazy resimleri izle
document.querySelectorAll("img[data-src]").forEach(img => {
  observer.observe(img);
});

// Sonsuz scroll ornegi
const sentinel = document.querySelector("#scroll-sentinel");
const scrollObserver = new IntersectionObserver(async (entries) => {
  if (entries[0].isIntersecting) {
    const newItems = await fetchNextPage();
    appendItems(newItems);
  }
}, { rootMargin: "200px" });
scrollObserver.observe(sentinel);
javascript
// =============================================
// ResizeObserver (boyut degisikligi izleme)
// =============================================

const resizeObserver = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const { width, height } = entry.contentRect;
    console.log(`Eleman boyutu: ${width}x${height}`);

    // Responsive davranis
    if (width < 400) {
      entry.target.classList.add("compact");
    } else {
      entry.target.classList.remove("compact");
    }
  }
});

const container = document.querySelector(".container");
resizeObserver.observe(container);

// Izlemeyi durdur
// resizeObserver.unobserve(container);
// resizeObserver.disconnect();  // hepsini durdur
javascript
// =============================================
// MutationObserver (DOM degisikligi izleme)
// =============================================

const mutationObserver = new MutationObserver((mutations) => {
  for (const mutation of mutations) {
    switch (mutation.type) {
      case "childList":
        mutation.addedNodes.forEach(node => {
          if (node.nodeType === Node.ELEMENT_NODE) {
            console.log("Yeni eleman eklendi:", node.tagName);
          }
        });
        mutation.removedNodes.forEach(node => {
          console.log("Eleman silindi:", node);
        });
        break;

      case "attributes":
        console.log(
          `${mutation.attributeName} degisti:`,
          mutation.target.getAttribute(mutation.attributeName)
        );
        break;

      case "characterData":
        console.log("Metin degisti:", mutation.target.textContent);
        break;
    }
  }
});

mutationObserver.observe(document.querySelector("#app"), {
  childList: true,        // cocuk eklenme/silinme
  attributes: true,       // attribute degisiklikleri
  characterData: true,    // metin degisiklikleri
  subtree: true,          // tum alt agaci izle
  attributeFilter: ["class", "data-state"],  // sadece belirli attribute'ler
  attributeOldValue: true, // eski degeri de kaydet
});

// Izlemeyi durdur
// mutationObserver.disconnect();
javascript
// =============================================
// Web Workers (arka plan islemleri)
// =============================================

// --- worker.js ---
self.onmessage = (e) => {
  const { type, data } = e.data;

  switch (type) {
    case "fibonacci":
      const result = fibonacci(data);
      self.postMessage({ type: "result", data: result });
      break;

    case "sort":
      const sorted = data.toSorted((a, b) => a - b);
      self.postMessage({ type: "result", data: sorted });
      break;
  }
};

function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

// --- main.js ---
const worker = new Worker("worker.js");

worker.postMessage({ type: "fibonacci", data: 40 });

worker.onmessage = (e) => {
  console.log("Sonuc:", e.data.data);
};

worker.onerror = (e) => {
  console.error("Worker hatasi:", e.message);
};

// Worker'i sonlandir
// worker.terminate();

// --- Inline Worker (dosya olmadan) ---
const workerCode = `
  self.onmessage = (e) => {
    const result = e.data.map(n => n * n);
    self.postMessage(result);
  };
`;
const blob = new Blob([workerCode], { type: "application/javascript" });
const inlineWorker = new Worker(URL.createObjectURL(blob));
inlineWorker.postMessage([1, 2, 3, 4, 5]);
inlineWorker.onmessage = (e) => console.log(e.data);  // [1, 4, 9, 16, 25]
javascript
// =============================================
// BroadcastChannel (sekmeler arasi iletisim)
// =============================================

// Ayni origin'deki tum sekmeler/pencereler arasi mesajlasma

// Sekme 1
const channel = new BroadcastChannel("app-channel");
channel.postMessage({ type: "THEME_CHANGE", theme: "dark" });

// Sekme 2 (veya baska bir sekme)
const channel2 = new BroadcastChannel("app-channel");
channel2.onmessage = (e) => {
  if (e.data.type === "THEME_CHANGE") {
    document.body.className = e.data.theme;
    console.log("Tema degistirildi:", e.data.theme);
  }
};

// Ornek: Oturum kapatma tum sekmelere yayilsin
function logout() {
  const bc = new BroadcastChannel("auth");
  bc.postMessage({ type: "LOGOUT" });
  bc.close();
  window.location.href = "/login";
}

// Tum sekmelerde dinle
const authChannel = new BroadcastChannel("auth");
authChannel.onmessage = (e) => {
  if (e.data.type === "LOGOUT") {
    window.location.href = "/login";
  }
};
javascript
// =============================================
// Web Storage (localStorage / sessionStorage) Ileri Duzey
// =============================================

// --- Storage event (sekmeler arasi senkronizasyon) ---
// Bu event SADECE DIGER sekmelerde tetiklenir, ayni sekmede degil
window.addEventListener("storage", (e) => {
  console.log("Degisen key:", e.key);
  console.log("Eski deger:", e.oldValue);
  console.log("Yeni deger:", e.newValue);
  console.log("URL:", e.url);

  if (e.key === "theme") {
    applyTheme(e.newValue);
  }
});

// --- Storage wrapper (tip guvenli, expire destekli) ---
const storage = {
  set(key, value, ttlMs = null) {
    const item = {
      value,
      timestamp: Date.now(),
      ttl: ttlMs,
    };
    localStorage.setItem(key, JSON.stringify(item));
  },

  get(key) {
    const raw = localStorage.getItem(key);
    if (!raw) return null;

    const item = JSON.parse(raw);
    if (item.ttl && Date.now() - item.timestamp > item.ttl) {
      localStorage.removeItem(key);
      return null;  // suresi dolmus
    }
    return item.value;
  },

  remove(key) {
    localStorage.removeItem(key);
  },
};

// 1 saatlik cache
storage.set("apiCache", { users: [...] }, 60 * 60 * 1000);
const cached = storage.get("apiCache");  // suresi dolmussa null doner
javascript
// =============================================
// Clipboard API (pano islemi)
// =============================================

// --- Metin kopyalama ---
async function copyText(text) {
  try {
    await navigator.clipboard.writeText(text);
    console.log("Kopyalandi!");
  } catch (err) {
    console.error("Kopyalama basarisiz:", err);
    // Fallback (eski tarayicilar)
    const textarea = document.createElement("textarea");
    textarea.value = text;
    document.body.appendChild(textarea);
    textarea.select();
    document.execCommand("copy");
    textarea.remove();
  }
}

// --- Metni okuma ---
async function pasteText() {
  try {
    const text = await navigator.clipboard.readText();
    console.log("Yapistirilan:", text);
    return text;
  } catch (err) {
    console.error("Okuma izni reddedildi:", err);
  }
}

// --- Buton ile kullanim ---
document.querySelector("#copy-btn").addEventListener("click", () => {
  const code = document.querySelector("#code-block").textContent;
  copyText(code);
});
javascript
// =============================================
// Notification API (bildirimler)
// =============================================

// --- Izin isteme ---
async function requestNotificationPermission() {
  if (!("Notification" in window)) {
    console.log("Bu tarayici bildirimleri desteklemiyor");
    return false;
  }

  if (Notification.permission === "granted") return true;
  if (Notification.permission === "denied") return false;

  const permission = await Notification.requestPermission();
  return permission === "granted";
}

// --- Bildirim gonderme ---
async function notify(title, options = {}) {
  const allowed = await requestNotificationPermission();
  if (!allowed) return;

  const notification = new Notification(title, {
    body: options.body || "",
    icon: options.icon || "/icon.png",
    badge: options.badge || "/badge.png",
    tag: options.tag || "default",      // ayni tag = guncelleme
    requireInteraction: false,
    silent: false,
  });

  notification.onclick = () => {
    window.focus();
    notification.close();
    if (options.onClick) options.onClick();
  };

  // Otomatik kapanma
  if (options.autoClose) {
    setTimeout(() => notification.close(), options.autoClose);
  }

  return notification;
}

// Kullanim
notify("Yeni Mesaj", {
  body: "Fahri size bir mesaj gonderdi",
  tag: "message-42",
  autoClose: 5000,
  onClick: () => navigateTo("/messages/42"),
});

15) Security (Güvenlik)

javascript
// =============================================
// innerHTML vs textContent
// =============================================

// innerHTML — HTML olarak yorumlar (XSS riski)
// textContent — duz metin olarak ekler (guvenli)

const userInput = '<img src=x onerror="alert(document.cookie)">';

// TEHLIKELI — kullanici script calistirabir
el.innerHTML = userInput;

// GUVENLI — HTML olarak yorumlanmaz
el.textContent = userInput;
// Gosterilen: <img src=x onerror="alert(document.cookie)">

// HTML escape fonksiyonu (innerHTML kullanmak zorundaysaniz)
function escapeHtml(str) {
  const map = {
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#039;",
  };
  return str.replace(/[&<>"']/g, (c) => map[c]);
}

// Guvenli HTML ekleme
el.innerHTML = `<p>${escapeHtml(userInput)}</p>`;

// DOMPurify kutuphanesi (daha guvenilir)
// npm install dompurify
// import DOMPurify from "dompurify";
// el.innerHTML = DOMPurify.sanitize(userInput);
javascript
// =============================================
// eval() Tehlikeleri
// =============================================

// eval() verilen string'i JavaScript olarak calistirir
// Asla kullanici girdisi ile kullanmayin!

// TEHLIKELI
const userCode = "alert('hacked')";
eval(userCode);  // kullanici istedigini calistirir

// TEHLIKELI — dolayli eval
const fn = new Function("return " + userCode);
fn();

// TEHLIKELI — setTimeout string parametresi
setTimeout("alert('hacked')", 1000);  // eval gibi calisir

// GUVENLI alternatifler
// JSON parse icin:
const data = JSON.parse(jsonString);  // eval(jsonString) DEGIL

// Dinamik property erisimi icin:
const value = obj[propertyName];      // eval("obj." + propertyName) DEGIL

// Hesaplama icin:
// Guvenli bir math parser kutuphanesi kullanin
// ornegin: mathjs, expr-eval
javascript
// =============================================
// Prototype Pollution
// =============================================

// Object.prototype'a zararli property ekleme saldirisi

// TEHLIKELI — dogrudan kullanici girdisini merge etme
function unsafeMerge(target, source) {
  for (const key in source) {
    if (typeof source[key] === "object") {
      target[key] = target[key] || {};
      unsafeMerge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
}

// Saldiri
const malicious = JSON.parse('{"__proto__": {"isAdmin": true}}');
unsafeMerge({}, malicious);
// Artik TUM nesnelerde: ({}).isAdmin === true

// GUVENLI — __proto__ ve constructor'i filtrele
function safeMerge(target, source) {
  for (const key of Object.keys(source)) {
    if (key === "__proto__" || key === "constructor" || key === "prototype") {
      continue;  // tehlikeli key'leri atla
    }
    if (typeof source[key] === "object" && source[key] !== null) {
      target[key] = target[key] || {};
      safeMerge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
  return target;
}

// GUVENLI — Object.create(null) ile prototype'siz obje
const safeObj = Object.create(null);
// safeObj.__proto__ === undefined (prototype yok)

// GUVENLI — Map kullanin
const safeMap = new Map();  // prototype pollution'a karsi bagisik
javascript
// =============================================
// Content Security Policy (CSP)
// =============================================

// CSP, tarayiciya hangi kaynaklarin yuklenebilecegini soyler
// XSS saldirilarina karsi en etkili onlemlerden biri

// Server header ornegi:
// Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'

// Meta tag ile (daha az onerilen):
// <meta http-equiv="Content-Security-Policy"
//       content="default-src 'self'; script-src 'self'">

// CSP direktifleri:
// default-src  — tum kaynaklar icin varsayilan
// script-src   — JavaScript kaynaklari
// style-src    — CSS kaynaklari
// img-src      — resim kaynaklari
// connect-src  — fetch/XHR/WebSocket baglantilari
// font-src     — font kaynaklari
// frame-src    — iframe kaynaklari
// media-src    — video/audio kaynaklari

// Ornek: Sadece kendi domain'inden yukle, inline script yasak
// Content-Security-Policy: default-src 'self'; script-src 'self'

// Ornek: Google Fonts ve kendi CDN'inize izin ver
// Content-Security-Policy: default-src 'self';
//   style-src 'self' https://fonts.googleapis.com;
//   font-src 'self' https://fonts.gstatic.com;
//   img-src 'self' https://cdn.example.com

// report-uri ile ihlalleri raporla
// Content-Security-Policy: ...; report-uri /csp-report
javascript
// =============================================
// XSS Korunma Kontrol Listesi
// =============================================

// 1. Kullanici girdisini ASLA innerHTML ile ekleme
//    textContent veya DOMPurify kullan

// 2. URL'leri dogrula
function isSafeUrl(url) {
  try {
    const parsed = new URL(url);
    return ["http:", "https:", "mailto:"].includes(parsed.protocol);
  } catch {
    return false;
  }
}
// javascript: protokolunu engellemek icin
// <a href="javascript:alert('xss')"> saldirisi

// 3. Cookie'leri HttpOnly yap (JS erisemez)
// Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict

// 4. CSP header'i kullan (yukaridaki bolum)

// 5. Subresource Integrity (harici script'ler icin)
// <script src="https://cdn.example.com/lib.js"
//         integrity="sha384-abc123..."
//         crossorigin="anonymous"></script>

// 6. Input sanitization — sunucu tarafinda da dogrula!
//    Client-side dogrulama UX icindir, guvenlik icin degil

// 7. Template literal icerisinde de dikkatli ol
const unsafeHtml = `<div>${escapeHtml(userInput)}</div>`;

// 8. JSON.stringify ile data attribute'lerine yazarken
el.dataset.config = JSON.stringify(config);
// Okurken
const config2 = JSON.parse(el.dataset.config);

16) Design Patterns

javascript
// --- Module Pattern ---
const Store = (() => {
  const state = {};
  return {
    get: (k) => state[k],
    set: (k, v) => { state[k] = v; },
    getAll: () => ({ ...state }),
  };
})();

// --- Observer / PubSub ---
class EventBus {
  #listeners = {};

  on(event, fn) {
    (this.#listeners[event] ??= []).push(fn);
  }

  off(event, fn) {
    this.#listeners[event] = (this.#listeners[event] || []).filter(f => f !== fn);
  }

  emit(event, data) {
    (this.#listeners[event] || []).forEach(fn => fn(data));
  }
}

const bus = new EventBus();
bus.on("save", (data) => console.log("Saved:", data));
bus.emit("save", { id: 1 });

// --- Factory ---
function createButton(type) {
  const base = { click() { console.log("clicked"); } };
  if (type === "primary") return { ...base, class: "btn-primary" };
  if (type === "danger") return { ...base, class: "btn-danger" };
  return { ...base, class: "btn-default" };
}

// --- Singleton ---
class Database {
  static #instance;
  static getInstance() {
    if (!Database.#instance) Database.#instance = new Database();
    return Database.#instance;
  }
}

// --- Proxy (reactivity) ---
const reactive = (obj, onChange) => new Proxy(obj, {
  set(target, key, value) {
    target[key] = value;
    onChange(key, value);
    return true;
  }
});

const state = reactive({ count: 0 }, (k, v) => console.log(`${k} = ${v}`));
state.count++;  // "count = 1"

17) Testing (Jest vs Vitest)

Jest vs Vitest karsilastirmasi:

ÖzellikJestVitest
HizOrta (Node.js tabanlı)Hızlı (Vite/esbuild tabanlı)
Konfigürasyonjest.config.jsvitest.config.ts (Vite config ile birlestirilebilir)
ESM destegiSınırlı (deneysel)Tam destek
TypeScriptts-jest veya @swc/jest gerekirKutudan cikar
Watch modu--watch--watch (daha hızlı, HMR kullanir)
UIYok@vitest/ui (tarayici arayuzu)
UyumlulukJest APIJest-uyumlu API
SnapshotDesteklerDestekler
Coverage--coverage (babel/v8)--coverage (v8/istanbul)
ToplulukCok büyük, olgunHizla buyuyen
ÖneriReact (CRA), mevcut projelerVite projeleri, yeni projeler
bash
# Vitest kurulumu (Vite projeleri icin)
npm install -D vitest

# Jest kurulumu (genel)
npm install -D jest

# Coverage icin
npm install -D @vitest/coverage-v8    # Vitest
npm install -D --save-dev jest         # Jest (dahili)
javascript
// =============================================
// Unit Test
// =============================================

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";

// --- Test edilen fonksiyonlar ---
function add(a, b) { return a + b; }
function multiply(a, b) { return a * b; }
function divide(a, b) {
  if (b === 0) throw new Error("Sifira bolme hatasi");
  return a / b;
}

// --- Testler ---
describe("Matematik fonksiyonlari", () => {
  describe("add", () => {
    it("iki pozitif sayiyi toplamalı", () => {
      expect(add(2, 3)).toBe(5);
    });

    it("negatif sayilarla calismali", () => {
      expect(add(-1, 1)).toBe(0);
      expect(add(-5, -3)).toBe(-8);
    });

    it("ondalik sayilarla calismali", () => {
      expect(add(0.1, 0.2)).toBeCloseTo(0.3);
    });
  });

  describe("divide", () => {
    it("bolme yapmali", () => {
      expect(divide(10, 2)).toBe(5);
    });

    it("sifira bolmede hata firlatmali", () => {
      expect(() => divide(10, 0)).toThrow("Sifira bolme hatasi");
    });
  });
});

// --- Yaygin matcher'lar ---
describe("Matcher ornekleri", () => {
  it("esitlik kontrolleri", () => {
    expect(42).toBe(42);                         // kesin esitlik (===)
    expect({ a: 1 }).toEqual({ a: 1 });          // derin esitlik
    expect({ a: 1, b: 2 }).toMatchObject({ a: 1 }); // kismi esitlik
  });

  it("truthiness kontrolleri", () => {
    expect(true).toBeTruthy();
    expect(0).toBeFalsy();
    expect(null).toBeNull();
    expect(undefined).toBeUndefined();
    expect("hello").toBeDefined();
  });

  it("sayi kontrolleri", () => {
    expect(10).toBeGreaterThan(5);
    expect(10).toBeLessThanOrEqual(10);
    expect(0.1 + 0.2).toBeCloseTo(0.3, 5);
  });

  it("string kontrolleri", () => {
    expect("hello world").toContain("world");
    expect("hello").toMatch(/^hel/);
  });

  it("dizi kontrolleri", () => {
    expect([1, 2, 3]).toContain(2);
    expect([1, 2, 3]).toHaveLength(3);
    expect([{ id: 1 }, { id: 2 }]).toContainEqual({ id: 1 });
  });
});
javascript
// =============================================
// DOM Test (@testing-library veya jsdom)
// =============================================

// vitest.config.ts icinde:
// export default { test: { environment: "jsdom" } }

import { describe, it, expect, beforeEach } from "vitest";

describe("Todo List DOM", () => {
  beforeEach(() => {
    document.body.innerHTML = `
      <div id="app">
        <input id="todo-input" type="text">
        <button id="add-btn">Ekle</button>
        <ul id="todo-list"></ul>
      </div>
    `;
  });

  function addTodo(text) {
    const list = document.querySelector("#todo-list");
    const li = document.createElement("li");
    li.textContent = text;
    li.className = "todo-item";
    list.appendChild(li);
  }

  it("todo eklenince listede gostermeli", () => {
    addTodo("Test gorevi");

    const items = document.querySelectorAll(".todo-item");
    expect(items).toHaveLength(1);
    expect(items[0].textContent).toBe("Test gorevi");
  });

  it("birden fazla todo eklenebilmeli", () => {
    addTodo("Gorev 1");
    addTodo("Gorev 2");
    addTodo("Gorev 3");

    const items = document.querySelectorAll(".todo-item");
    expect(items).toHaveLength(3);
  });

  it("input degeri okunabilmeli", () => {
    const input = document.querySelector("#todo-input");
    input.value = "Yeni gorev";
    expect(input.value).toBe("Yeni gorev");
  });
});
javascript
// =============================================
// Mock & Spy
// =============================================

import { describe, it, expect, vi, beforeEach } from "vitest";

// --- Mock fonksiyon ---
describe("Mock ornekleri", () => {
  it("fonksiyon cagrilmali", () => {
    const callback = vi.fn();
    callback("hello");

    expect(callback).toHaveBeenCalled();
    expect(callback).toHaveBeenCalledWith("hello");
    expect(callback).toHaveBeenCalledTimes(1);
  });

  it("donus degeri ayarlanmali", () => {
    const getUser = vi.fn()
      .mockReturnValueOnce({ name: "Fahri" })
      .mockReturnValueOnce({ name: "Ali" })
      .mockReturnValue({ name: "Varsayilan" });

    expect(getUser().name).toBe("Fahri");     // 1. cagri
    expect(getUser().name).toBe("Ali");        // 2. cagri
    expect(getUser().name).toBe("Varsayilan"); // 3+ cagri
  });
});

// --- Modul mock ---
// api.js dosyasini mock'la
vi.mock("./api", () => ({
  fetchUsers: vi.fn().mockResolvedValue([
    { id: 1, name: "Fahri" },
    { id: 2, name: "Ali" },
  ]),
}));

import { fetchUsers } from "./api";

describe("API mock", () => {
  it("mock edilmis veriyi dondurmeli", async () => {
    const users = await fetchUsers();
    expect(users).toHaveLength(2);
    expect(users[0].name).toBe("Fahri");
  });
});

// --- Spy (mevcut fonksiyonu izle) ---
describe("Spy ornekleri", () => {
  it("console.log cagrisi izlenmeli", () => {
    const spy = vi.spyOn(console, "log");
    console.log("test mesaji");

    expect(spy).toHaveBeenCalledWith("test mesaji");
    spy.mockRestore();  // orijinal fonksiyonu geri yukle
  });

  it("Date.now mock'lanmali", () => {
    const now = new Date("2025-01-01").getTime();
    vi.spyOn(Date, "now").mockReturnValue(now);

    expect(Date.now()).toBe(now);
    vi.restoreAllMocks();
  });
});

// --- Timer mock ---
describe("Timer mock", () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it("debounce calismali", () => {
    const fn = vi.fn();
    const debounced = debounce(fn, 300);

    debounced();
    debounced();
    debounced();

    expect(fn).not.toHaveBeenCalled();

    vi.advanceTimersByTime(300);

    expect(fn).toHaveBeenCalledTimes(1);
  });
});
javascript
// =============================================
// Async Test
// =============================================

import { describe, it, expect, vi } from "vitest";

describe("Async islemler", () => {
  // --- async/await ---
  it("veri cekmeli", async () => {
    const data = await fetchData("/api/test");
    expect(data).toBeDefined();
    expect(data.id).toBe(1);
  });

  // --- reject olma durumu ---
  it("hatali istekte reject olmali", async () => {
    await expect(fetchData("/api/404")).rejects.toThrow("HTTP 404");
  });

  // --- fetch mock ---
  it("fetch mock ile test", async () => {
    global.fetch = vi.fn().mockResolvedValue({
      ok: true,
      json: () => Promise.resolve({ id: 1, name: "Test" }),
    });

    const res = await fetch("/api/users/1");
    const data = await res.json();

    expect(data.name).toBe("Test");
    expect(fetch).toHaveBeenCalledWith("/api/users/1");
  });

  // --- Timeout testi ---
  it("yavas API timeout vermeli", async () => {
    vi.useFakeTimers();

    const promise = fetchWithTimeout("/api/slow", 3000);

    vi.advanceTimersByTime(3000);

    await expect(promise).rejects.toThrow("Timeout");

    vi.useRealTimers();
  });
});
javascript
// =============================================
// Test Coverage
// =============================================

// vitest.config.ts
// export default {
//   test: {
//     coverage: {
//       provider: "v8",           // veya "istanbul"
//       reporter: ["text", "html", "lcov"],
//       exclude: [
//         "node_modules/",
//         "test/",
//         "**/*.config.*",
//       ],
//       thresholds: {
//         branches: 80,
//         functions: 80,
//         lines: 80,
//         statements: 80,
//       },
//     },
//   },
// };

// Calistirma
// npx vitest --coverage

// Coverage turleri:
// - Line coverage: satirlarin yuzde kaci calistirıldı
// - Branch coverage: if/else kollarinin yuzde kaci test edildi
// - Function coverage: fonksiyonlarin yuzde kaci cagirildi
// - Statement coverage: ifadelerin yuzde kaci calistirıldı

// Tavsiyeler:
// - %80+ coverage hedefleyin (ama %100 saplantisi gereksiz)
// - Branch coverage ozellikle onemli (edge case'ler)
// - Kritik is mantigi %100 olmali
// - Utility fonksiyonlari %100 olmali
// - UI bilesenleri icin %60-80 yeterli

18) Tips & Best Practices

javascript
// =============================================
// const > let, var kullanma
// =============================================

// const: degistirilemez baglama (referans degismez, icerik degisebilir)
const API_URL = "https://api.example.com";
const users = [];
users.push("Fahri");  // gecerli — dizi icerigi degisir, referans degismez

// let: sadece gercekten degisecek degiskenler icin
let count = 0;
count++;

// var: KULLANMA — function scope, hoisting sorunlari
// var x = 1; // kaçınılması gereken eski soz dizimi
javascript
// =============================================
// Strict equality (===) her zaman kullan
// =============================================

// == (loose) beklenmedik tur donusumleri yapar
0 == ""         // true (!)
0 == false      // true (!)
"" == false     // true (!)
null == undefined // true
"0" == false    // true (!)

// === (strict) tip kontrolu yapar
0 === ""        // false
0 === false     // false
"" === false    // false
null === undefined // false

// Tek istisna: null/undefined kontrolu
// == ile ikisini birden yakalayabilirsin
if (value == null) {
  // value === null VEYA value === undefined
}
javascript
// =============================================
// Optional Chaining (?.)
// =============================================

// Eskiden (uzun ve tekrarlı)
const city = user && user.address && user.address.city;

// Simdi (temiz ve guvenli)
const city2 = user?.address?.city;

// Fonksiyon cagrisi
const result = obj.method?.();

// Dizi erisimi
const first = arr?.[0];

// Derin zincir
const zip = response?.data?.user?.address?.zipCode;

// Varsayilan degerle birlestir
const theme = settings?.display?.theme ?? "light";
javascript
// =============================================
// Nullish Coalescing (??) vs OR (||)
// =============================================

// || (OR) — falsy degerleri atlar: 0, "", false, null, undefined, NaN
const port = config.port || 3000;    // config.port = 0 ise 3000 olur (!)
const name = config.name || "Anonim"; // config.name = "" ise "Anonim" olur (!)

// ?? (Nullish) — sadece null ve undefined'i atlar
const port2 = config.port ?? 3000;    // config.port = 0 ise 0 kalir
const name2 = config.name ?? "Anonim"; // config.name = "" ise "" kalir

// Nullish assignment (??=)
let options = {};
options.timeout ??= 5000;    // sadece null/undefined ise ata
options.retries ??= 3;
javascript
// =============================================
// Early Return (erken donus)
// =============================================

// KOTU — ic ice if'ler (pyramid of doom)
function processOrder(order) {
  if (order) {
    if (order.items.length > 0) {
      if (order.payment) {
        if (order.payment.verified) {
          // asil is mantigi
          return submitOrder(order);
        } else {
          return { error: "Odeme dogrulanmadi" };
        }
      } else {
        return { error: "Odeme bilgisi yok" };
      }
    } else {
      return { error: "Sepet bos" };
    }
  } else {
    return { error: "Siparis bulunamadi" };
  }
}

// IYI — early return ile duz akis
function processOrder(order) {
  if (!order) return { error: "Siparis bulunamadi" };
  if (order.items.length === 0) return { error: "Sepet bos" };
  if (!order.payment) return { error: "Odeme bilgisi yok" };
  if (!order.payment.verified) return { error: "Odeme dogrulanmadi" };

  return submitOrder(order);
}
javascript
// =============================================
// Destructuring (parcalama)
// =============================================

// Fonksiyon parametrelerinde
function createUser({ name, email, role = "viewer", avatar = null }) {
  return { name, email, role, avatar, createdAt: Date.now() };
}
createUser({ name: "Fahri", email: "f@test.com" });

// API response'unu parcala
const { data: users, meta: { total, page } } = await apiResponse;

// Dongu icinde
const products = [
  { name: "Laptop", price: 15000 },
  { name: "Telefon", price: 8000 },
];
for (const { name, price } of products) {
  console.log(`${name}: ${price} TL`);
}

// Import'ta rename
import { useState as useStateHook } from "react";

// Swap (gecici degisken olmadan)
let a = 1, b = 2;
[a, b] = [b, a];
javascript
// =============================================
// Spread & Rest (yayma & toplama)
// =============================================

// --- Spread (yayma) ---
// Dizi birlestirme
const all = [...arr1, ...arr2, ...arr3];

// Obje birlestirme (sag taraf kazanir)
const config = { ...defaults, ...userConfig, ...overrides };

// Dizi kopyalama (shallow)
const copy = [...original];

// Obje kopyalama (shallow)
const clone = { ...original };

// Fonksiyon argumanlari
const numbers = [1, 5, 3, 9, 2];
Math.max(...numbers);  // 9

// --- Rest (toplama) ---
// Fonksiyon parametreleri
function log(level, ...messages) {
  console[level](...messages);
}
log("warn", "Dikkat:", "eski API", "kullaniliyor");

// Destructuring ile
const { id, ...rest } = user;
// id: 1, rest: { name: "Fahri", age: 25 }

const [first, ...others] = [1, 2, 3, 4, 5];
// first: 1, others: [2, 3, 4, 5]
javascript
// =============================================
// Template Literals (sablonlu metinler)
// =============================================

// Basit interpolasyon
const msg = `Merhaba ${name}, ${age} yasindasin.`;

// Cok satirli
const html = `
  <div class="card">
    <h2>${title}</h2>
    <p>${description}</p>
  </div>
`;

// Ifade kullanimi
const status = `Durum: ${isActive ? "Aktif" : "Pasif"}`;
const total = `Toplam: ${items.reduce((s, i) => s + i.price, 0)} TL`;

// Tagged template literals
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => {
    const value = values[i] ? `<mark>${values[i]}</mark>` : "";
    return result + str + value;
  }, "");
}

const highlighted = highlight`Merhaba ${name}, ${role} olarak giris yaptiniz.`;
// "Merhaba <mark>Fahri</mark>, <mark>admin</mark> olarak giris yaptiniz."

// SQL injection'a karsi (tagged template)
function sql(strings, ...values) {
  return {
    text: strings.join("$"),
    values: values,
  };
}
const query = sql`SELECT * FROM users WHERE id = ${userId} AND role = ${role}`;
// Parametreli sorgu — injection guvenli

19) Hızlı Referans

KonuBilgi
Değişkenconst > let, var kullanma
EsitlikHer zaman === kullan
Template`Hello ${name}`
Spread[...arr], {...obj}
Destructureconst { a, b } = obj
Optional chainobj?.prop?.sub
Nullishvalue ?? default
Array transformmap, filter, reduce
Immutable arraytoSorted, toReversed, with
Asyncasync/await + try/catch
ParalelPromise.all, Promise.allSettled
DOMquerySelector + addEventListener
Fetchawait fetch(url) + res.json()
ClonestructuredClone(obj)
GroupObject.groupBy(arr, fn)
Moduleimport/export (ESM)
Workernew Worker("file.js")
ObserverIntersectionObserver, ResizeObserver
GüvenliktextContent > innerHTML
Testdescribe, it, expect, vi.fn()

Artik JavaScript'in modern ozelliklerini kapsamli sekilde biliyorsun!


Ilgili Rehberler

Frontend

Diger Kategoriler

Developer Guides & Technical References