İçeriğe geç
academia.sh

Ders 02 / 14

ES Modülleri

Adlandırılmış ve varsayılan dışa aktarım, içe aktarımın canlı bağ davranışı, bağlamanın değerlendirmeden önce gelmesi ve statik yapının ağaç sarsmaya verdiği imkân.

İçindekiler

Önceki ders tek dosyalık ölçüm betiğini bırakırken asıl soruyu ortaya koymuştu: dosya bölünürken hangi adın dışarıdan görülebilir kalacağına kim karar verir? Dilin standart modül sözdiziminde bu kararı modülün kendisi verir ve kararını kaynağın içinde yazılı olarak bildirir.

Bu ders o sözdizimini ve arkasındaki mekanizmayı ele alır. Sözdizim kısa; mekanizma, kurs boyunca kullanılacak birkaç davranışın kaynağıdır.

Dışa Aktarım Yüzeyi

Ölçüm betiği üç sorumluluğa bölünür. Metni parçalara ayırma işi bir modüle, sayısal ölçüm ikinci bir modüle, ikisini birleştiren giriş noktası üçüncüsüne gider.

// dosya: src/bolumleme.mjs
const SOZCUK_AYIRACI = /[^\p{L}\p{N}]+/u;
const CUMLE_AYIRACI = /[.?]+/;

export function sozcuklereBol(metin) {
  return metin.split(SOZCUK_AYIRACI).filter((s) => s.length > 0);
}

export function cumlelereBol(metin) {
  return metin.split(CUMLE_AYIRACI).map((c) => c.trim()).filter((c) => c.length > 0);
}
// dosya: src/istatistik.mjs
export function ortalamaUzunluk(sozcukler) {
  if (sozcukler.length === 0) return 0;
  const toplam = sozcukler.reduce((t, s) => t + s.length, 0);
  return toplam / sozcukler.length;
}

export function enUzunSozcuk(sozcukler) {
  return sozcukler.reduce((en, s) => (s.length > en.length ? s : en), '');
}
// dosya: src/index.mjs
import { cumlelereBol, sozcuklereBol } from './bolumleme.mjs';
import { enUzunSozcuk, ortalamaUzunluk } from './istatistik.mjs';

export function olc(metin) {
  const sozcukler = sozcuklereBol(metin);
  return {
    sozcukSayisi: sozcukler.length,
    cumleSayisi: cumlelereBol(metin).length,
    ortalamaUzunluk: ortalamaUzunluk(sozcukler),
    enUzun: enUzunSozcuk(sozcukler),
  };
}

export { cumlelereBol, sozcuklereBol };
// dosya: dene.mjs
import { olc } from './src/index.mjs';

console.log(olc('Modul kendi kapsamini tasir. Betik genel kapsamda calisir.'));
$ node dene.mjs
{
  sozcukSayisi: 8,
  cumleSayisi: 2,
  ortalamaUzunluk: 6.125,
  enUzun: 'kapsamini'
}

İki ayrıntı önemlidir. Birincisi, SOZCUK_AYIRACI sabiti artık yalnızca kendi modülünde görünür; export almadığı için dışarıdan erişilemez. Görünürlük, dosya sınırının değil, bildirimin özelliğidir.

İkincisi, index.mjs dosyasının son satırı iki adı yeniden dışa aktarır. Giriş noktası, alt modüllerin bir bölümünü kendi yüzeyine taşıyarak paketin dışarıya sunduğu arayüzü tek yerde toplar. ortalamaUzunluk ve enUzunSozcuk bu listeye alınmadı: iç hesaplama ayrıntısı olarak kaldılar. Dışa aktarım yüzeyi bir tasarım kararıdır ve kütüphanenin ileride ne kadar değişebileceğini belirler.

Adlandırılmış ve Varsayılan Dışa Aktarım

İki biçim vardır. Adlandırılmış dışa aktarım (named export) bir adı yüzeye koyar; tüketici o adı yazarak alır. Varsayılan dışa aktarım (default export) adsız tek bir değer sunar; tüketici ona istediği adı verir.

export function olc(metin) { /* ... */ }        // adlandırılmış
export default function olc(metin) { /* ... */ }  // varsayılan

Ayrım yalnızca yazım kolaylığı değildir. Adlandırılmış dışa aktarımda ad, modülün sözleşmesinin parçasıdır: yanlış yazılan bir ad bağlama aşamasında yakalanır. Varsayılan dışa aktarımda ad tüketicinin seçimidir; iki dosyanın aynı değeri farklı adlarla çağırması mümkündür ve otomatik araçlar bu adı izleyemez. Kütüphane arayüzlerinde adlandırılmış biçim, adın kararlı kalması gereken durumlar için daha güçlü bir sözleşme kurar.

İçe Aktarılan Ad Bir Bağdır

İçe aktarma, değeri kopyalamaz; dışa aktaran modüldeki bildirime bir canlı bağ (live binding) kurar. Dışa aktaran modül değeri değiştirdiğinde, içe aktaran taraf yeni değeri görür.

// dosya: kayit.mjs
console.log('kayit.mjs gövdesi çalıştı');
export let cagriSayisi = 0;
export function say() { cagriSayisi += 1; }
// dosya: ana.mjs
console.log('ana.mjs gövdesinin ilk satırı');
import { cagriSayisi, say } from './kayit.mjs';

console.log('içe aktarılan değer:', cagriSayisi);
say();
say();
console.log('iki çağrıdan sonra:', cagriSayisi);
$ node ana.mjs
kayit.mjs gövdesi çalıştı
ana.mjs gövdesinin ilk satırı
içe aktarılan değer: 0
iki çağrıdan sonra: 2

cagriSayisi sayısal bir değerdir, yani değiştirilemez bir ilkel; buna rağmen ikinci okuma 2 verdi. Okunan şey değer değil, bağdır.

Bağ tek yönlüdür. İçe aktaran taraf bağa yazamaz:

// dosya: atama.mjs — kayit.mjs yukarıdaki dosyadır
import { cagriSayisi } from './kayit.mjs';

cagriSayisi = 5;
$ node atama.mjs 2>&1 | grep -E 'Error' | head -1
TypeError: Assignment to constant variable.

Bu kısıt, bir modülün durumunu yalnızca kendi kodunun değiştirebileceğini garanti eder. Dışarıdan değişiklik isteniyorsa modül bunun için bir fonksiyon dışa aktarmalıdır.

Bağlama Değerlendirmeden Önce Gelir

Çıktıdaki satır sırası dikkat çekicidir: kayit.mjs gövdesi, ana.mjs dosyasının kendi konsol yazımından önce çalıştı. Oysa içe aktarma deyimi o yazımdan sonra geliyordu.

Bunun nedeni, modül yüklemenin üç ayrı aşamada yürümesidir:

  1. Ayrıştırma ve çözümleme. Kaynak okunur, içe aktarma belirteçleri (specifier) dosyalara çevrilir; bulunan her dosya için işlem yinelenir. Sonuç bir modül çizgesidir.
  2. Bağlama. Her içe aktarılan ad, dışa aktaran modüldeki bildirimle eşleştirilir. Karşılığı bulunamayan ad burada hata verir.
  3. Değerlendirme. Modül gövdeleri, bağımlılıktan bağımlıya doğru bir kez çalıştırılır.

Bilgisayarlar Nasıl Çalışır kursundaki bağlama ve yükleme dersinde bağlayıcının yaptığı iş buydu: simgeleri çözmek ve eksik olanı bildirmek. Modül sisteminin ikinci aşaması aynı işi kaynak düzeyinde yapar. Sonucu da benzerdir — eksik ad, kod çalışmadan yakalanır:

// dosya: hatali.mjs — kayit.mjs yalnızca cagriSayisi ve say adlarını dışa aktarır
import { cagriSayisi, sayac } from './kayit.mjs';

console.log('bu satır hiç çalışmaz', cagriSayisi, sayac);
$ node hatali.mjs 2>&1 | grep 'SyntaxError'
SyntaxError: The requested module './kayit.mjs' does not provide an export named 'sayac'

Hata türü dikkat çekicidir: sözdizim hatası. Modül grafiğinin tutarlılığı, program başlamadan denetlenen bir özelliktir. Aynı şey eksik dosya için de geçerlidir:

// dosya: eksik-bagimlilik.mjs
console.log('bu satır çalışmaz');
import './olmayan-modul.mjs';
$ node eksik-bagimlilik.mjs 2>&1 | grep '^Error' | sed "s|$(pwd -P)/||g"
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'olmayan-modul.mjs' imported from eksik-bagimlilik.mjs

Konsola hiçbir şey yazılmadı. sed çağrısı yalnızca iletideki mutlak dizin yolunu kısaltmak içindir; yol, çalıştırdığınız dizine göre değişir.

Ad Alanı Nesnesi

Bir modülün tüm dışa aktarımları tek bir nesnede toplanarak da alınabilir:

// dosya: adalani.mjs — src/index.mjs yukarıda tanımlanan giriş noktasıdır
import * as olcer from './src/index.mjs';

console.log(Object.keys(olcer));
console.log(Object.isFrozen(olcer));
console.log(olcer[Symbol.toStringTag]);
$ node adalani.mjs
[ 'cumlelereBol', 'olc', 'sozcuklereBol' ]
false
Module

Anahtarların sırası, kaynaktaki yazım sırası değil, sözlük sırasıdır: ad alanı nesnesinin içeriği kaynak metnin düzeninden bağımsız, belirlenimci bir şekilde üretilir. Nesne dondurulmuş değildir ama genişletilemez; üzerine yeni ad eklenemez, var olan adlar silinemez.

Statik Yapının Sonucu

İçe ve dışa aktarma deyimleri yalnızca en üst düzeyde bulunabilir ve belirteçleri sabit dizgi olmak zorundadır. Bu kısıt, ilk bakışta esnekliği azaltan bir katılık gibi görünür; karşılığında kod çalıştırılmadan çözümlenebilir hâle gelir.

Somut kazanç, kullanılmayan dışa aktarımların çıktıdan düşürülebilmesidir; buna ağaç sarsma (tree shaking) denir. Bir araç, index.mjs dosyasının yalnızca olc adını kullandığını kaynağa bakarak görebilir ve cumlelereBol işlevini çıktıya hiç koymayabilir. Aynı çıkarımı, adların çalışma anında hesaplandığı bir sistemde yapmak genel olarak olanaksızdır.

İkinci kazanç, araçların modül çizgesini programı çalıştırmadan kurabilmesidir. Kurs boyunca göreceğiniz paketleyici, çözümleyici ve düzenleyici desteklerinin tümü bu özelliğe dayanır.

Özet

  • Dışa aktarım bildirim düzeyinde belirlenir; export almayan ad, dosya dışından erişilemez ve giriş noktası yeniden dışa aktarımla arayüzü tek yerde toplar.
  • Adlandırılmış dışa aktarımda ad sözleşmenin parçasıdır ve yanlış yazımı bağlama aşamasında yakalanır; varsayılan dışa aktarımda adı tüketici seçer.
  • İçe aktarılan ad değerin kopyası değil, dışa aktaran modüldeki bildirime kurulmuş canlı bağdır ve içe aktaran taraftan yazılamaz.
  • Yükleme üç aşamalıdır: çözümleme, bağlama, değerlendirme. Eksik ad ve eksik dosya hataları ilk iki aşamada, hiçbir gövde çalışmadan çıkar.
  • Sözdizimin statik olması ağaç sarsmayı ve çizgenin kod çalıştırılmadan kurulmasını mümkün kılar.

Sonraki Adım

Bu modeli tek seçenek sanmak yanıltıcı olur. Dilin daha eski ve hâlâ yaygın olarak karşılaşılan ikinci bir modül sistemi vardır; orada içe aktarma bir deyim değil, bir fonksiyon çağrısıdır ve tüm davranışlar buradan farklılaşır. Sonraki ders o sistemi, bu sistemle birlikte nasıl yaşadığını ve dairesel bağımlılıkta ikisinin nasıl ayrıştığını inceler.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat