JAVASCRIPT’IN TARIHÇESI

JS Module(IIFE →CJS→AMD →ES6)

Javascript’in Module Yapısı Nasıl Gelişti ?, Module Nedir ? IIFE, (Common.JS,NPM), (AMD, Require), Import, Export , gibi kavramları anlatmaya çalışacağım.

--

Giriş

Bu yazıyı daha önceden yazmış olduğum Javascript’in Tarihçesi yazısının bir devamı olarak yazıyorum. Bir çok kavramı tek bir yazıda ele almanın yaratacağı karmaşıklıktan kaçmak için bu yönteme başvurdum. Bu yazılardaki amacım önceden Javascript’in varolan hangi özelliklerinin yetmediğini ve bu geliştirmeyle neyi hedeflediklerini anlatacağım.

Referanslar bölümündeki Mejin Leechor videosu üzerinden konuyu anlatmayı çalışacağım. Videodaki konuyu anlatış şekli benim JS yazılarımdaki geçmişten, gününüze ve geleceğe bakması açısından benzerlik gösteriyor.

  • Javascript Module Yapısı Nasıl Gelişti ?
  • Pre-Modules Dönemi (inline-scripting)
  • DIY Modules Dönemi (IIFE)
  • NodeJS Dönemi (Common.JS — Sync)
  • Browser Module Dönemi (AMD, Require.JS — Async)
  • ES6 Module Dönemi

1. Javascript Module Yapısı Nasıl Gelişti ?

from JavaScript Modules Past & Present
  • Pre-Modules Dönemi: Javascript inline-script ve script etiketleri ile kullanıldığı dönem. Küçük animasyon ve renk değişikliklerinin yapılarak hazırlanma web sayfalarının bulunduğu dönem. Her türlü değişken ve fonksiyonun global scope tanımlandığı dönem.
  • DIY-Modules Dönemi: Javascript ilk kütüphanelerin Yahoo UI, JQuery, utility kütüphanelerinin ortaya çıktığı dönem. Bu dönemde ilk modül soyutlamalarının yapıldığını görebilirsiniz. Bunun için IIFE (Immediately Invoked Function Expression) yöntemi kullanılmıştır.
  • NodeJS Dönemi (Common.JS — Sync): JS Local ve Sunucu makinelerde çalışabilir hale gelmesi. NodeJS ile birlikte JS ivmesini ve ihtiyaçlarını arttırmıştır. NodeJS içerisinde direk module kavramını geliştirmesi Common.JS . gerekmiştir. Burdaki işlem sırasında local makinenin üzerindeki dosyalara erişim ihtiyacı olduğu için Sync loading yeterli olmaktadır. Modül cache içerisindeyse direk burdan yok ise direk local dosyadan okunarak erişilmektedir. (NodeJS import nasıl destekliyor ?)
  • Browser Module Dönemi (AMD ve Require.JS — Async): Sunucu tarafında olan bu gelişmeler doğal olarak tarayıcı tarafınıda tetikledi. Fakat tarayıcıda araya Network girdiği için Modullerin async olarak yüklenmesi bunun içinde bir standart oluşturulması gerekiyordu Async Module Definition ve Require.JS ile bu problemlere çözüm bulundu.
  • ES6 Modules: JS kendi çevresinde ekosisteminde gerçekleşen çözüm yöntemlerini zaman içerisinde kendi içerisine alıp standart hale getirmiştir. import, export anahtar kelimelerle bu işlemleri en basit,okunabilir ve anlaşılır haline getirmiştir.

2. Pre-Modules Dönemi?

Pre-modules (90 ortası, 2000 başlarına kadar) olan zamanlarda IE ve Netscape arasındaki yarışma devam ediyordu. Tarayıcılar üzerinde yapabildikleriniz çok kısıtlıydı. Bu süreçte JS kullanımı oldukça azdı tüm işlerinizi tek bir html(index) dosyası içerisinde script tag ile inline-scripting işlemlerinizi yapabilmekteydiniz.

<html>
<body>
JS Script
<script>
var sum=3+6;
</script>
<div id="msg"></div>
<div id="sum"></div>
<script>
document.getElementById("sum").innerHTML = sum;
</script>
</body>
<script>
document.getElementById("msg").innerHTML = "Merhaba Dunya";
</script>
</html>
Sonuç

Script tag diğer bir kullanımıda index.html ile diğer js bilgilerini birbirinden ayrıştırmaktır. Örneğin yukarıdaki küçük kod parçaları yerine tim js kodlarını tek bir dosyada toplayabilirim . Örneğin tüm kodları index.js dosyasının içerisinde toplayabilirim. Veya DOMOps.js , MathOps.js şeklinde farklı farklı dosyalara bölebilirim.

Bence zaten Modülerlik ilk bölümle HTML , JS ve CSS dosyaların ayrı ayrı dosyalara bölünmesi ile gerçekleşmiştir. İlerleyen süreçlerde JS kodları mantıklarına göre ayrı ayrı dosyalara bölünerek kodun okunabilirliği ve daha büyük ölçekte yazılabilmesi imkanı buldu.

<script src=URL>

Peki bu yapı büyük ölçekli yazılımlar geliştirmek için uygun mu ?

Bu sorunun cevabını direk JS oluşturan Brendan Eich, vermiş. Javascript bu şekilde geniş ölçekte kullanılacağını hiç kimsenin düşünmediğini. Böyle ufak code parçaları ile başlangıç seviyesindekileri hedeflediklerini. Gmail vb büyük kod geliştirebilmek için güçlü API’lere ve modül sistemine ihtiyaç olduğundan bahsetmiş.

Brendan Eich JS hakkındaki düşünceleri

Peki bu kadar basit bir dil nasıl olurda günümüzdeki halini alarak Belkide dünyanın en çok kullandığı dil halini alır ve farklı türdeki geliştiricilerin ihtiyaçlarını karşılar? Basit bir dil demek belkide yanlış çünkü ileride anlatacağım Fonksiyonel Programlama içeren Scheme ve Self dillerinden esinlenerek Javascript geliştirilmiştir.

Self programla dilinin özellikleri bold ile işaretlediklerimin prototype-base inheritance temellerini oluşturduğunu görebilirsiniz.

About Self

Self is a prototype-based dynamic object-oriented programming language, environment, and virtual machine centered around the principles of simplicity, uniformity, concreteness, and liveness. Self includes a programming language, a collection of objects defined in the Self language, and a programming environment built in Self for writing Self programs. The language and environment attempt to present objects to the programmer and user in as direct and physical a way as possible. The system uses the prototype-based style of object construction.

Self contains a user interface and programming environment designed for “serious” programming, enabling the programmer to create and modify objects entirely within the environment, and then save the object into files for distribution purposes. The metaphor used to present an object to the user is that of an outliner, allowing the user to view varying levels of detail. Also included in the environment is a graphical debugger, and tools for navigation through the system.

Scheme dilinin bold ile işaretlediğim kısımların ES6 ile birlikte gelen Fonksiyonel Programla özellikleri High Order Functions, vb temellerini oluşturduğunu görebilirsiniz.

Scheme is a general-purpose computer programming language. It is a high-level language, supporting operations on structured data such as strings, lists, and vectors, as well as operations on more traditional data such as numbers and characters. While Scheme is often identified with symbolic applications, its rich set of data types and flexible control structures make it a truly versatile language. Scheme has been employed to write text editors, optimizing compilers, operating systems, graphics packages, expert systems, numerical applications, financial analysis packages, virtual reality systems, and practically every other type of application imaginable. Scheme is a fairly simple language to learn, since it is based on a handful of syntactic forms and semantic concepts and since the interactive nature of most implementations encourages experimentation. Scheme is a challenging language to understand fully, however; developing the ability to use its full potential requires careful study and practice.

Problem neydi? Bu dönemde ne gibi sorunlar ve zorluklar yaşanıyordu ?

  • Kodun tekrar kullanabilirliği
  • Diğer kodlara bağımlılığın yönetilmesi
  • Bakım zorlukları

3. DIY Modules Dönemi (IIFE)

Modül Nedir? Ben modülleri atom ve moleküle benzetiyorum. Bu atom dediğimiz kavramları incelediğimizde, farklı farklı atomlar bulunur. Bu atomları kendi başına diğer bileşenlere ihtiyacı olmayan modüller olarak düşünebiliriz.

Periodic Table

Bu atomların içlerine baktığımızda proton ve nötron’dan oluşan bir çekirdek ve bu çekirdeğin çevresinde dolaşan elektronlardan oluşur.

Atom Structure

ve bunların birleşiminden moleküller oluşur. Bu moleküllerin birbirleri ile birleşimi ile eşyalar, sistemler veya organizmalar oluşur.

Molecule Structure

Kod modülleride buna benzerlik gösterir. kod blokları oluşturmak ve bunları birbirine bağlayak kütüphaneler , uygulamalar vb büyük sistemler kurma imkanı doğar. Peki Modül nedir ?

  • Kendi iç mantıksal işlemlerini yapan.
  • Arayüz aracılığı ile dışarıya veri ve fonksiyon sağlayan
  • Kendiside dışarıdan ihtiyacı olduğu kod modüllerini kullanabilen yapılardır.

DIY Modules . 2000'lerde Yahoo Internet dünyasında oldukça güçlüydü. Google’dan daha güçlüydü. Google 1998 daha yeni kurulmuştu ve Yahoo mühendisleri Global değişkenlerin kötü sonuçlara sebep olduğunu söylediler. Bunlar için manifesto oluşturdular.

  • Çünkü ortak değişkenlerin heryerden erişilebilir olması kodun içerisinde bir anda verdiğiniz değerin birileri tarafından değiştirilmesi anlamına geliyordu.
  • Başkasının görmemesi ve kullanmaması gereken fonksiyonları başkalarının kullanması anlamına geliyor.
  • Bazı zamanlarda 3rd Party kütüphanelerin kodlarının veya değişken isimlerinin çakışması anlamına geliyordu.

bu soyutlamaları gerçekleştirmek için IIFE(Immediately invoked function expression) kullandılar.

IIFE Nedir ? Neden Arrow Functions anlatırken tüm fonksiyon türlerinden bahsetmiştim. IIFE konusunda da ileride module kısmında daha detaylı anlatacağımı belirtmiştim.

Eğer bir fonksiyon hemen çağrılıp oluşturulan sonuç/sonuç objesi bir değişkende saklanıyorsa bu tip fonksiyonlara IIFE (Immediately invoked function expression) deniyor. Bu tip fonksiyonların amacı genelde bu scope içerisinde oluşan değişkenlerin sadece o kapsam(scope) erişilebilir olmasını sağlıyor. Genelde JQuery, Prototype gibi kütüphanelerde değişken isimlerinin çakışmaması ve scope içerisinde işlemlerin yapılabilmesi için IIFE yöntemi kullanılmıştır. Daha sonra anlatacağım module kavramları ile birlikte ileride bu konu üzerinde daha detaylı duracağım.

Burda ilk kütüphaneler ile birlikte binlerce Global değişken yerine Namespacing kavramı ortamı oluştu. Yahoo ait, JQuery ait birtane büyük Global Object olsun ve heryerde bu nesne üzerinden erişim gerçekleşsin ve bu nesnenin içerisindeki değişkenler dışarıdan direk erişilemesin. Yahoo veya JQuery objeleri tek başlarına tüm sayfanın state tutan ve yöneten kütüphaneler haline geldi.

Aşağıdaki kod bloğunda counter istediğim gibi dışarıdan erişiyorum.

var myModule={ counter:0} 
myModule.counter++;
console.log(myModule.counter); //1

IIFE ile yazdığımız kod bloğunda ise counter dışarıdan erişime kapattık. Global state oluşturmadan state return fonksiyonu içerisinden yönetmeye başladık. İlk dönem UI kütüphanelerinin yaptığı buydu. Global değişken ve fonksiyon kullanımını yönetmek.

var myModule=(()=>{ var counter=0; 
increment=()=>counter++;
print=()=>console.log(counter);
return {increment:increment,print:print}
})();
myModule.increment();
myModule.print(); //1

4. NodeJS Dönemi (Common.JS — Sync)

Spesification

2009 Node.JS ile birlikte Sunucu içinde içinde Javascript kullanabilme imkanı olduğu ve burada büyük bir potensiyel olduğu düşünüyorlardı. Sonuçta fullstack tek bir dille WebApp sadece JS ile geliştirebilme imkanı ortaya çıkıyordu.

Bu Enterprise ihtiyaçlar yani JS ile daha büyük projeler geliştirebilmek için JS modüllerinin tarayıcıdan farklı bir şekilde kod ile daha standart programatik bir modül loading ihtiyacını doğurdu.

DipNot: Burda tarayıcıların sadece JS çalıştırmasının, PHP, JSP, ASP, Ruby vb dillerin en sonunda Web tarafında uygulamalar için JS, HTML üretiyordu ama Backend giderek standart hale geldi. Controller — Service — Data katmanı ve burdaki mantıklar, REST → GraphQL doğru giderek kod geliştirmekten configurasyon yönetimine döndü. UI ise daha fazla mantıksal işlemin ve bileşenlerin olduğu karmaşık yapılara döndü. Bu durum hem fullstack kavramını hemde JS Web Stack yapılarını oluşturmaya başladı. Hem geliştirici maliyetlerini, hemde bakım maliyetlerini düşürmek patronların daha çok işine gelirdi sonuçta.

Bu aşamada Web Uygulaması geliştirme’de JS inanılmaz bir ivme kazandı. Aynısı Python için yapay zeka ve türevleri için geçerli. Sonuçta Python yapay zeka projesi yapan bir grup tüm teknoloji stack Python üzerine yapıp, Web uygulamasınıda Python geliştirebilir.

Kevin Dangoor “What Server Side JavaScript needs” blog yazısında gelecekte bir ekosistem oluşturmak için gereken gereksinimleri bir öneri yazısında topladı.

  • Cross-interpreter standard library, JS çalıştırıcıları ( Rhino, Spidermonkey, V8 and JSCore) ortak kullandığı standart kütüphanelerin oluşturulması.
  • Standard arayüzler
  • Modüllerin birbirlerini içermesi için standartlar
  • Kod paketleme , versiyonlama standartları
  • Package repository (NPM gibi)

NPM Modules 2 blog yazım. JS ekosistemin oluşması için en önemli merkezi oluşturur. Bu konuda daha detaylı bilgi almak isterseniz Modules 2 yazısını okuyarak Modül Kavramına devam edebilirsiniz.

Biz bu kısımda Modüllerin birbirlerini içermesi için standartların ne olduğuna bakarsak; CommonJS aşağıdaki gibi loading mekanizmasının kurulması ve sunucu tabanlı tasarım mantığını hedeflediğini görebiliriz.

  • import : require() metodu
  • export: module.exports objesi

Basit bir örnekle açıklamak gerekirse. require(aws-sdk) ile modül bağlantısı kurup dışarıyada module.exports = { purgeQueue} dışarıya hizmet sunan fonksiyonlar oluşturuyoruz. Tüm yapı bunun üzerine kurulu.

AWS-SQS fonksiyon çağırma

Burda require işlemi nasıl çalışıyor. Require aslında bir fonksiyon ve sync load işlemi gerçekleştiriyor. İlk başta da bahsettiğim gibi Node local dosya sistemindeki JS erişmesi gerektiği için Async ihtiyaçlar bu aşamada birincil öncelik değil.

  • Module._LOAD : Öncelikle cache bakıyor. Yoksa module oluşturma işlemine geçiyor dosyayı yüklüyor ve MODULE._COMPILE fonksiyonunu çağırıyor. IIFE gibi bir scope wrapper(kaplayıcı) fonksiyon oluşturuyor ve module.exports return ediyor. Aslında mekanizmalar aynı sadece spesifikasyonu belirleyip bu kısmı insanlardan soyutluyorlar ve geliştiricilerin bunları düşünmesine gerek kalmıyor.
CommonJS Require

Wrapper fonksiyon aşağıdaki gibi

Require Wrapper Fonksiyon

5. Browser Module Dönemi (AMD Require.JS — Async)

Tarayıcı tarafında CJS çalıştıran bir takım mekanizmalar olsada tarayıcı tarafındaki esas sorunu çözmüyordu. Sonuçta Network üzerinden kullanıcının bilgisayarına yüklenmesi gereken JS dosyalarının Async indirilmesi ve yüklenmesi gerekiyordu. Daha optimize çalışması gerekiyordu.

Bunun için AMD ( Asynchronous Module Definition) oluşturuldu. Ve Require.JS Module Loader oluşturuldu. Bunun amacı aşağıda belirtildiği gibi

RequireJS is a JavaScript file and module loader. It is optimized for in-browser use, but it can be used in other JavaScript environments, like Rhino and Node. Using a modular script loader like RequireJS will improve the speed and quality of your code.

IE 6+ ………. compatible ✔
Firefox 2+ ….. compatible ✔
Safari 3.2+ …. compatible ✔
Chrome 3+ …… compatible ✔
Opera 10+ …… compatible ✔

Get started then check out the API.

//Calling define with module ID, dependency array, and factory function
define('myModule', ['dep1', 'dep2'], function (dep1, dep2) {

//Define the module value by returning a value.
return function () {};
});

6. ES6 Module Dönemi

Son olarak bu kadar farklı farklı Module yükleme yöntemini ve spesifikasyonunu tek bir standart altında toplayabilirmiyiz. IIFE, CJS, AMD yöntemlerini direk JS Standartları içerisinde nasıl sunabiliriz düşündüler

  • JS içerisindeki built-in modül yapısı bu şekilde olmalı.
  • Tüm tarayıcılar tarafından desteklenmeli
  • Asenkron(Async) çalışmalı

ES Module Syntax incelediğimizde export sayesinde başka modüllerle paylaşmak istediğimiz değişken ve fonksiyonları(In JavaScript, functions are first-class objects) kolayca sağlayabildiğimizi . import sayesinde de diğer modüllerden istediğimiz değişkenleri veya sabitleri kullanabildiğimizi görebilirsiniz. Aşağıdaki şekilde kullanabilirsiniz.

ES6 Module Syntax
  • export
  • export default
  • import * as from
  • import {def1, def2} from
  • import def1 from

Peki bunu JS içerisinden değilde HTML içerisinden kullanmak istersek nasıl yapacağız. Bunun içinde script tag içerisinde type olarak module kullanabilirsiniz.

<script type="module" src="main.js"></script>

Referanslar

Okumaya Devam Et 😃

Bu yazının devamı veya yazı grubundaki diğer yazılara erişmek için bu linke tıklayabilirsiniz.

--

--