JavaScript'te `Object` sınıfı

JavaScript'te `Object` sınıfı

Bu makale JavaScript'teki Object sınıfını açıklar.

Bu makalede JavaScript'teki Object sınıfı, pratik örneklerle birlikte açıklanmaktadır.

YouTube Video

JavaScript'te Object sınıfı

Object, tüm JavaScript nesneleri için temel oluşturan dahili bir nesnedir. Object davranışları aracılığıyla, özellik yönetimi, kalıtım (prototype zinciri), numaralandırma, klonlama ve dondurma gibi dilin temel özelliklerinin çoğu sağlanır.`.

Object.create

Nesne oluşturmanın birden fazla yolu vardır ve amacınıza göre uygun olanı kullanmalısınız.

Nesne literal'i (en yaygın)

Aşağıdaki kod, nesne oluşturmanın en basit ve en okunabilir yolunu gösterir.

 1// Create an object using object literal
 2const user = {
 3  name: "Alice",
 4  age: 30,
 5  greet() {
 6    return `Hello, I'm ${this.name}`;
 7  }
 8};
 9
10console.log(user.greet()); // "Hello, I'm Alice"
  • Bu örnekte, özellikler ve metotlar literal kullanılarak tanımlanmıştır. Basittir ve genellikle üstün performans sunar.

new Object() yapıcı fonksiyonu

Object kurucusu nadiren kullanılır, ancak davranışını anlamak faydalı olabilir.

1// Create an object using the Object constructor
2const objFromCtor = new Object();
3objFromCtor.x = 10;
4objFromCtor.y = 20;
5
6console.log(objFromCtor); // { x: 10, y: 20 }
  • new Object() boş bir nesne döndürür, ancak literal {} daha kısa ve yaygındır.

Object.create ile prototip belirlemek

Object.create, belirli bir prototipe sahip bir nesne oluşturmak için kullanılır.

1// Create an object with a specified prototype
2const proto = { hello() { return "hi"; } };
3const obj = Object.create(proto);
4obj.name = "Bob";
5
6console.log(obj.hello()); // "hi"
7console.log(Object.getPrototypeOf(obj) === proto); // true
  • Object.create, prototipe dayalı nesne tasarımı için idealdir; böylece prototip zincirini hassas şekilde kontrol edebilirsiniz.

Özellik nitelikleri ve tanımlayıcıları

Özelliklerin 'value', 'writable', 'enumerable', ve 'configurable' gibi nitelikleri vardır; bunlar Object.defineProperty ile ayrıntılı olarak kontrol edilebilir.

defineProperty kullanımı için temel örnek

defineProperty kullanılarak enumerasyon yapılamayan ve sadece okunabilir özellikler tanımlamanın bir örneği aşağıdadır.

 1// Define a non-enumerable read-only property
 2const person = { name: "Carol" };
 3
 4Object.defineProperty(person, "id", {
 5  value: 12345,
 6  writable: false,
 7  enumerable: false,
 8  configurable: false
 9});
10
11console.log(person.id); // 12345
12console.log(Object.keys(person)); // ["name"] — "id" is non-enumerable
13person.id = 999; // silently fails or throws in strict mode
14console.log(person.id); // still 12345
  • defineProperty kullanılması, özellik davranışlarını (enumere etme, yeniden yazma ve silme gibi) hassas bir şekilde kontrol etmenizi sağlar.

Erişimci özellikler (getter / setter)

Erişimcilerle, özellik okuma ve yazmaya mantık ekleyebilirsiniz.

 1// Use getter and setter to manage internal state
 2const data = {
 3  _value: 1,
 4  get value() {
 5    return this._value;
 6  },
 7  set value(v) {
 8    if (typeof v === "number" && v > 0) {
 9      this._value = v;
10    } else {
11      throw new Error("value must be a positive number");
12    }
13  }
14};
15
16console.log(data.value); // 1
17data.value = 5;
18console.log(data.value); // 5
19// data.value = -1; // would throw
  • getter ve setter ile özellik erişimini dış bir API gibi ele alıp doğrulama veya yan etkiler ekleyebilirsiniz.

Prototip ve kalıtım (prototype / __proto__ / Object.getPrototypeOf)

JavaScript'te kalıtım, sınıflara değil prototype zincirine dayanır. Nesneler, diğer nesneleri prototipleri olarak kullanabilirler.

Object.getPrototypeOf ve Object.setPrototypeOf

Aşağıdaki örnek, prototiplerin nasıl inceleneceğini ve ayarlanacağını göstermektedir.

1// Inspect and change prototype
2const base = { speak() { return "base"; } };
3const derived = Object.create(base);
4console.log(Object.getPrototypeOf(derived) === base); // true
5
6const other = { speak() { return "other"; } };
7Object.setPrototypeOf(derived, other);
8console.log(derived.speak()); // "other"
  • Object.getPrototypeOf, bir nesnenin prototipini getirir.
  • Object.setPrototypeOf, mevcut bir nesnenin prototipini değiştirir; ancak, performansı etkileyebileceğinden dikkatli kullanılmalıdır.

Önemli yerleşik metodlar

Object.prototype tarafından sağlanan örnek metodlardan ve Object’in sahip olduğu statik metodlardan en sık kullanılan ve önemli olanları açıkça açıklayacağız.

hasOwnProperty, isPrototypeOf, toString, valueOf

hasOwnProperty, isPrototypeOf, toString ve valueOf, nesnelerin temel davranışlarını tanımlar.

 1// Demonstrate prototype methods
 2const base = { greet() { return "hello"; } };
 3const child = Object.create(base);
 4const a = { x: 1 };
 5
 6console.log(a.hasOwnProperty("x")); // true
 7console.log(a.hasOwnProperty("toString")); // false — toString is inherited
 8
 9console.log(a.toString()); // "[object Object]" by default
10
11console.log(base.isPrototypeOf(child)); // true
12console.log(Object.prototype.isPrototypeOf(child)); // true
  • hasOwnProperty, bir özelliğin doğrudan o nesnede olup olmadığını kontrol etmek için temel bir metottur.
  • isPrototypeOf, hedef nesnenin kendisini bir prototip olarak içerip içermediğini kontrol eder.

Object.keys, Object.values, Object.entries

Object.keys, Object.values ve Object.entries, bir nesnenin kendi numaralandırılabilir özelliklerinin listelerini döndürür. Bunlar yineleme ve dönüştürme için kullanışlıdır.

 1// Keys, values and entries
 2const item = { id: 1, name: "Widget", price: 9.99 };
 3
 4// ["id", "name", "price"]
 5console.log(Object.keys(item));
 6
 7// [1, "Widget", 9.99]
 8console.log(Object.values(item));
 9
10// [["id",1], ["name","Widget"], ["price",9.99]]
11console.log(Object.entries(item));
  • Bu metotlar, nesneleri yinelemek ve dönüştürmek için sıkça kullanılır.

Object.assign

Object.assign, sığ kopyalama ve birleştirme için kullanılır. Prototiplerin ve erişimci özelliklerin kopyalanmadığına dikkat edin.

1// Shallow copy / merge using Object.assign
2const target = { a: 1 };
3const source = { b: 2 };
4const result = Object.assign(target, source);
5
6console.log(result); // { a: 1, b: 2 }
7console.log(target === result); // true (merged into target)
  • İç içe nesnelerde yalnızca referanslar kopyalanır, bu nedenle derin kopyalama için farklı bir uygulama kullanmalısınız.

Object.freeze, Object.seal, Object.preventExtensions

Object.freeze, Object.seal ve Object.preventExtensions nesnelerin değiştirilebilirliğini kontrol eder.

 1// Freeze vs seal vs preventExtensions
 2const obj = { a: 1 };
 3Object.freeze(obj);
 4obj.a = 2; // fails silently or throws in strict mode
 5delete obj.a; // fails
 6
 7const obj2 = { b: 2 };
 8Object.seal(obj2);
 9obj2.b = 3; // allowed
10// delete obj2.b; // fails
11
12const obj3 = { c: 3 };
13Object.preventExtensions(obj3);
14obj3.d = 4; // fails
  • freeze en katısıdır; nesnenin özelliklerinde herhangi bir değişikliği engeller.
  • seal, özelliklerin eklenmesini veya silinmesini engeller, ancak mevcut özelliklerin değerlerinin değiştirilmesine izin verir.
  • preventExtensions, yalnızca yeni özelliklerin eklenmesini engeller; mevcut özellikler hâlâ değiştirilebilir veya silinebilir.

Nesne numaralandırılabilirliği, sırası ve for...in / for...of

for...in, numaralandırılabilir özellik adlarını döngüye sokar, fakat ayrıca prototype zincirindeki özellikleri de içerir; bu yüzden genellikle hasOwnProperty ile birlikte kullanılır. Object.keys() ile for...of'u birleştirmek daha güvenlidir ve niyetinizi daha net gösterir.

 1// Safe enumeration
 2const obj = Object.create({ inherited: true });
 3obj.own = 1;
 4
 5for (const key in obj) {
 6  if (obj.hasOwnProperty(key)) {
 7    console.log("own prop:", key);
 8  } else {
 9    console.log("inherited prop:", key);
10  }
11}
12
13for (const key of Object.keys(obj)) {
14  console.log("key via Object.keys:", key);
15}
  • Özellik numaralandırma kuralları ECMAScript spesifikasyonunda tanımlanmıştır ve bazı durumlarda sıralama garanti edilirken bazılarında edilmez. Genel bir kural olarak, sayılar olarak yorumlanan anahtarlar artan düzende sıralanır; diğer anahtarlar ise eklenme sırasını takip eder.

Klonlama ve derin kopyalama

Nesne kopyalamanın iki türü vardır: Object.assign veya yayma (spread) sözdizimi ile yapılan yüzeysel kopyalama ve özyineli (recursive) yöntemlerle yapılan derin kopyalama. Duruma göre bunları uygun şekilde kullanmak önemlidir.

Sığ kopyalama (yayılma sözdizimi / Object.assign)

1// Shallow copy with spread operator
2const original = { a: 1, nested: { x: 10 } };
3const shallow = { ...original };
4shallow.nested.x = 99;
5console.log(original.nested.x); // 99 — nested object is shared
  • Sığ kopyalama ile iç içe nesneler aynı referansı paylaşır; bu yüzden orijinal nesnedeki değişiklikler kopyayı da etkileyebilir.

Basit derin kopyalama (bazı sınırlamalarla)

JSON yöntemiyle hızlıca derin kopya oluşturmak mümkündür, ancak fonksiyonlar, Date, döngüsel referanslar ve undefined değerler gibi kayıplar yaşanır. Gerçek anlamda derin klonlama için özel bir kütüphane kullanmanız gerekir.

1// Deep clone using JSON methods — limited use-cases only
2const source = { a: 1, d: new Date(), nested: { x: 2 } };
3const cloned = JSON.parse(JSON.stringify(source));
4console.log(cloned); // ok for plain data, but Date becomes string, functions lost
  • JSON tabanlı metotlar, basit verileri hızla işlemek için kullanışlıdır; fakat yaygın durumlarda beklenmedik davranışlar gösterebilir.

Mixin'ler ve nesne kompozisyonu

Çoklu kalıtım yerine, davranışların mixin'lerle birleştirilmesi deseni sıklıkla kullanılır.

 1// Simple mixin function
 2const canEat = {
 3  eat() { return `${this.name} eats`; }
 4};
 5const canWalk = {
 6  walk() { return `${this.name} walks`; }
 7};
 8
 9function createPerson(name) {
10  const person = { name };
11  return Object.assign(person, canEat, canWalk);
12}
13
14const p = createPerson("Dana");
15console.log(p.eat()); // "Dana eats"
16console.log(p.walk()); // "Dana walks"
  • Mixin'ler esnektir; ancak özellik adı çakışmaları ve test edilebilirlik konusunda dikkatli olmalısınız.

Yaygın tuzaklar ve en iyi uygulamalar

İşte bazı yaygın tuzaklar ve en iyi uygulamalar.

  • Değiştirilebilirlik Nesneler varsayılan olarak değiştirilebilirdir. Durum yönetimi yapılan uygulamalarda, Object.freeze veya değiştirilemez (immutable) bir kütüphane kullanarak değiştirilemez veri yapıları kullanmayı düşünün.

  • Prototip kirliliği Object.assign veya döngülerle harici veriyi doğrudan bir objeye birleştirmek, __proto__ veya constructor gibi özel özelliklerle beklenmeyen yan etkilere ve güvenlik açıklarına yol açabilir. Girdileri doğrudan birleştirmeden önce kullanıcı verilerini filtreleyin.

  • for...in kullanırken dikkat edilmesi gerekenler for...in, prototip özelliklerini de döngüye alır; bu yüzden hasOwnProperty ile kontrol edin. Object.keys kullanımı daha açıktır.

  • Sığ kopya hatalı kullanımı İç içe nesnelerdeki değişikliklerin orijinal nesneyi etkilemesini önlemek için derin kopyalama gerekip gerekmediğini düşünün.

Pratik Örnek: Değiştirilemez Nesne Güncelleme Deseni

Durumu doğrudan değiştirmeden yeni bir nesne döndüren bu desenler, React ve benzeri kütüphanelerde yaygın olarak kullanılır.

 1// Immutable update example
 2const state = { todos: [{ id: 1, text: "Buy milk", done: false }] };
 3
 4// Toggle todo done immutably
 5function toggleTodo(state, todoId) {
 6  return {
 7    ...state,
 8    todos: state.todos.map(t => t.id === todoId ? { ...t, done: !t.done } : t)
 9  };
10}
11
12const newState = toggleTodo(state, 1);
13console.log(state.todos[0].done); // false
14console.log(newState.todos[0].done); // true
  • Bu kod, orijinal state nesnesini doğrudan değiştirmeden yeni bir durum nesnesi (state object) oluşturmanın bir örneğidir. toggleTodo fonksiyonu, todos dizisini kopyalar ve yalnızca hedef eleman değiştirilmiş olarak yeni bir nesne döndürür; böylece orijinal state aynı kalır.
  • Değiştirilemez güncellemeler, yan etkileri azaltır ve durum yönetimini kolaylaştırır.

Pratik Örnek: Güvenli Birleştirme (Prototip Kirliliğine Dikkat Edin)

Harici JSON birleştirilirken, prototip kirliliğini önlemek için __proto__'yu dikkate almayın.

 1// Safe merge ignoring __proto__ keys
 2function safeMerge(target, source) {
 3  for (const key of Object.keys(source)) {
 4    if (key === "__proto__" || key === "constructor") continue;
 5    target[key] = source[key];
 6  }
 7  return target;
 8}
 9
10const target = {};
11const source = JSON.parse('{"a":1,"__proto__":{"polluted":true}}');
12safeMerge(target, source);
13console.log(target.polluted); // undefined — safe
14console.log({}.polluted); // undefined — prototype not polluted
  • Bu tür bir koruma, kütüphane ve çerçevelerde de önemlidir.

Performans dikkatleri

Performans açısından şu noktalara dikkat edin:.

  • Sık sık prototip değiştirmek (Object.setPrototypeOf) veya dinamik olarak özellik ekleyip çıkarmak, motorun gerçekleştirdiği optimizasyonları engellediğinden kaçınılmalıdır.
  • Birçok küçük nesne oluştururken, nesneler aynı yapıya (aynı özellik setine) sahip olduğunda optimizasyon daha etkili olur.
  • Derin kopyalama maliyetlidir. Kullanımını en aza indirin veya fark tabanlı güncellemeleri tercih edin.

Özet

Object, JavaScript'in merkezindedir ve nesne oluşturma, özellik kontrolü, kalıtım, kopyalama ve değiştirilebilirlik yönetimi gibi çok çeşitli özellikler sağlar. Object.defineProperty, Object.assign ve Object.freeze gibi API'leri anlamak önemlidir ve prototip kirliliği ile yüzeysel kopyalama gibi tuzaklardan kaçınmak için dikkatli tasarım yapılmalıdır.

Yukarıdaki makaleyi, YouTube kanalımızda Visual Studio Code'u kullanarak takip edebilirsiniz. Lütfen YouTube kanalını da kontrol edin.

YouTube Video