📌 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) // true2) 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); // 103) 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 objects5) 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:
| Özellik | ESM (import/export) | CommonJS (require/module.exports) |
|---|---|---|
| Yukleme | Asenkron (statik analiz) | Senkron |
| Tree-shaking | Destekler | Desteklemez |
| Top-level await | Destekler | Desteklemez |
| Browser | Dogrudan destekler | Bundler gerekir |
| Node.js | .mjs veya "type": "module" | Varsayilan |
| Dinamik import | import() | require() |
this (top-level) | undefined | module.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 hazirjavascript
// =============================================
// 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, 48) 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 yontemjavascript
// =============================================
// 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-Originheader'i donmeli - Preflight:
OPTIONSrequest 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; // breakpoint13) 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 durdurjavascript
// =============================================
// 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 donerjavascript
// =============================================
// 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 = {
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'",
};
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-evaljavascript
// =============================================
// 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 bagisikjavascript
// =============================================
// 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-reportjavascript
// =============================================
// 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:
| Özellik | Jest | Vitest |
|---|---|---|
| Hiz | Orta (Node.js tabanlı) | Hızlı (Vite/esbuild tabanlı) |
| Konfigürasyon | jest.config.js | vitest.config.ts (Vite config ile birlestirilebilir) |
| ESM destegi | Sınırlı (deneysel) | Tam destek |
| TypeScript | ts-jest veya @swc/jest gerekir | Kutudan cikar |
| Watch modu | --watch | --watch (daha hızlı, HMR kullanir) |
| UI | Yok | @vitest/ui (tarayici arayuzu) |
| Uyumluluk | Jest API | Jest-uyumlu API |
| Snapshot | Destekler | Destekler |
| Coverage | --coverage (babel/v8) | --coverage (v8/istanbul) |
| Topluluk | Cok büyük, olgun | Hizla buyuyen |
| Öneri | React (CRA), mevcut projeler | Vite 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 yeterli18) 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 dizimijavascript
// =============================================
// 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 guvenli19) Hızlı Referans
| Konu | Bilgi |
|---|---|
| Değişken | const > let, var kullanma |
| Esitlik | Her zaman === kullan |
| Template | `Hello ${name}` |
| Spread | [...arr], {...obj} |
| Destructure | const { a, b } = obj |
| Optional chain | obj?.prop?.sub |
| Nullish | value ?? default |
| Array transform | map, filter, reduce |
| Immutable array | toSorted, toReversed, with |
| Async | async/await + try/catch |
| Paralel | Promise.all, Promise.allSettled |
| DOM | querySelector + addEventListener |
| Fetch | await fetch(url) + res.json() |
| Clone | structuredClone(obj) |
| Group | Object.groupBy(arr, fn) |
| Module | import/export (ESM) |
| Worker | new Worker("file.js") |
| Observer | IntersectionObserver, ResizeObserver |
| Güvenlik | textContent > innerHTML |
| Test | describe, it, expect, vi.fn() |
Artik JavaScript'in modern ozelliklerini kapsamli sekilde biliyorsun!
Ilgili Rehberler
Frontend
- Frontend Genel Bakış
- TypeScript Rehberi
- React Rehberi
- Next.js Rehberi
- Vue.js Rehberi
- CSS & Tailwind
- Web Performance
- Three.js Rehberi