ماژول پترن یا module pattern تو جاوااسکریپت/نود.جی‌اس

هر جایی که نوشتم جاوااسکریپت میتونی نود.جی‌اس بزاری
هر جایی که نوشتم جاوااسکریپت میتونی نود.جی‌اس بزاری

یه design pattern خوب که به یه دلیل خوب تو جاوااسکریپت استفاده میشه. این پترن encapsulation رو بهت میده. ماژول ها تو جاوااسکریپت عموما به صورت singleton استفاده میشن. module pattern یه روش خوب برای ایجاد سرویس ها و یه راهکار عالی برای توسعه نرم افزار بر اساس رویکرد TDD هست.

نکته: این موضوع بزرگتر از یه پسته ولی در عین حال موضوع پیچیده ای نیست.

اینم یه اسکرین شات از گوگل که مدعای ما رو ثابت میکنه
اینم یه اسکرین شات از گوگل که مدعای ما رو ثابت میکنه

ماژول چیه و چرا باید استفاده بشه؟

یه نگاهی که به اطرافت بندازی میبینی که هر چیزی که وجود داره از اجزایی تشکیل شده که در کنار هم یه سیستم رو تشکیل میدن. محدوده‌ی عمل هر جزء کاملا مشخصه، هدفشون مشخصه، چجوری با اجزاء دیگه باید همکاری بکنه هم مشخصه. خب پس موضوع ماژولار بودن تمام سیستم ها کاملا مشخص شد.

  • ترکیب پذیری (Composability)
  • استفاده مجدد (Reusability): دیگه با `Ctrl + c` و `Ctrl + v` خداحافظی میکنی.
  • اهرمی کار کردن (Leverage) به این معنی که اجزا میتونن هر جایی تولید بشن و تنها این قضیه مهم هست که در نهایت همه اجزا کنار هم قرار بگیرن تا سیستم ساخته بشه
  • ایزوله کردن (Isolation) به این معنی که هر جزء میتونه توسط هر کسی تولید بشه، بدون اینکه به خاطر این تقسیم کار به مشکل بخورن و نتونن کارشون رو تموم بکنن.
  • سازمان دهی (Organization) وقتی سیستم رو ماژولار درست کنی نتیجه اون اینه که کار رو سازمان دهی کردی؛ یعنی مشخص کردی هر تیم چجوری با تیم دیگه تعامل سازنده داشته باشه، محصول نهایی هر تیم باید چی باشه و ...

همه این مزایا به سه موضوع بر میگردن:

  1. قابلیت نگهداری (Maintainability): اینجا جایی هست که میگه یه ماژول باید وابستگی خیلی کمی (loosely coupled باشه، مراجعه به dependency injection تو نود.جی‌اس) داشته باشه و بتونی به صورت جداگانه هر ماژول رو آپدیت بکنی. اگه این بحث رو نداشته باشی میتونی اینجوری بهش نگاه بکنی که وقتی میخوای چرخ ماشین رو عوض بکنی مجبور بشی محور یا موتوریت رو هم عوض بکنی.
  2. بحث namespace pollution: فرض کن یه کلاس یا تابعی تو یه ماژول تعریف میکنی و با همون نام تو یه ماژول دیگه هم میای یه متغییر تعریف می‌کنی. اینجا هست که ماژول ها به کمک میان و از تداخل نام ها جلوگیری میکنن.
  3. مفهوم Closure نحوه‌ی تعریف scope هر آبجکت رو مشخص میکنه. این موضوع توی ES6 رفته پشت صحنه؛ یعنی under the hood پیاده سازی شده و شما فقط باید قوانینش رو بدونید (مراجعه شود به این و این و حتما این فیلم آموزشی).



اجزای ماژول

فکر کنم شما هم مثل من به فانکشن ها، کلاس ها و کامپوننت های ری‌اکت خودتون دارید فکر می‌کنید. خب پس یه ماژول شامل ۳ بخش هست: import/require، کد هایی که زدی و exports/module.export

  • بخش وابستگی های (Dependencies) ماژول یا import ها

وقتی که به ماژول third party یا ماژولی که خودت نوشتی داخل یه ماژول دیگه نیاز داری میتونی اونو import بکنی. به اون ماژول dependency میگن.

  • بخش کد ها

بعد از import کردن dependency ها می‌تونی کد های ماژول رو بزنی

  • بخش exports

تو این بخش یه اینترفیس رو باید export کرد. حالا هر جایی ماژول رو import کردی میتونی از اون چیزی که export کردی استفاده بکنی.



الگوی Immediately Invoked Function Expression یا IIFE

تو این روش ما یه anonymous closure تعریف می‌کنیم. تو این مدل یه تابع بی نام که در بر گیرنده کد هایمون هست تعریف می‌کنیم. روش زیر یه روش خیلی خوب توی ES5 برای اجرا کردن ماژول هامون تو مد strict بود (strict mode کد هامون رو از برخی بخش های خطرناک جاوااسکریپت محافظت میکنه).

(function() {
  'use strict';
  // Your code here
  // All function and variables are scoped to this function
})();

اگه بخوای کد های بالا رو یکمی تغییر بدی تا بتونی به عنوان یه ماژول ازش استفاده بکنی و همزمان بتونی private و public رو هم تو جاوااسکریپت داشته باشی باید این حرکت رو بزنی:

var myModule = (function() {
  'use strict';
  var _privateProperty = 'Hello World';
  function _privateMethod() { console.log(_privateProperty) }

  return {
    publicMethod: function() { _privateMethod() }
  };
})();
myModule.publicMethod(); // outputs 'Hello World'

توجه بکن که جاوااسکریپت به _ اهمیتی نمیده. منظورم اینه که اونو ما برای این میزاریم که به خودمون یاداوری بکنیم که این پراپرتی ها یا متد ها private هستن.


اینجا برات با ES5 کد زدم تا ببینی که وقتی میگیم نود.جی‌اس به صورت پیشفرض singleton هست و module pattern یعنی چی:

همون طور که تو عکس مشخصه من وقتی یه متغیر رو در سطح ماژول تعریف می‌کنم دیگه مهم نیست که چند بار require بشه. اولین باری که require بشه میره تو مموری و هر جای دیگه ای هم که require بشه به همون reference داده میشه.

پس وقتی از یه چیزی new میکنی تو یه ماژول میتونی مطمئن باشی که هر چند بار اون ماژول رو require کنی مقدار new شده، که تو متغیر ذخیره کردی همون مقداری هست که با اولین require گرفته.

اینم با ES6

فقط اینجا اومدیم new شده کلاس رو return کردیم. حالا هر جایی که ماژولی که در بر گیرنده کلاس A هست رو require بکنی همشون به یه instance از کلاس A اشاره دارند.