درک ماژول‌ها و import و export آنها در جاوا اسکریپت

ترجمه و تالیف : عرفان حشمتی
تاریخ انتشار : 23 دی 99
خواندن در 6 دقیقه
دسته بندی ها : جاوا اسکریپت

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

همانطور که وب سایت‌ها با ظهور فریمورک‌هایی مانند Angular ، React و Vue تکامل یافته‌اند، شرکت‌ها به جای برنامه‌های دسکتاپ، برنامه‌های وب پیشرفته ایجاد می‌کنند. اکنون جاوا اسکریپت نقش اصلی را در مرورگر ایفا می‌کند. در نتیجه نیاز به استفاده از کد شخص ثالث برای کارهای مشترک، تجزیه کد در فایل‌های ماژولار و جلوگیری از شلوغ شدن فضای نام بسیار بیشتر است.

ECMAScript 2015 ماژولهایی را به زبان جاوا اسکریپت معرفی کرده است که اجازه استفاده از اعلانات import و export را می‌دهد. در این آموزش می‌فهمید که ماژول جاوا اسکریپت چیست و چگونه می‌توان برای ساماندهی کد از import و export استفاده کرد.

برنامه نویسی ماژولار

قبل از ظهور مفهوم ماژول‌ها در جاوا اسکریپت، هنگامی که یک توسعه دهنده می‌خواست کد خود را سامان دهی کند، چندین فایل ایجاد می‌کرد و به عنوان اسکریپت‌های جداگانه به آنها لینک می‌داد. برای توضیح این موضوع، یک فایل index.html و دو فایل جاوا اسکریپت function.js و script.js ایجاد کنید.

فایل index.html مجموع، تفاضل، ضرب و تقسیم دو عدد را نمایش می‌دهد و به دو فایل جاوا اسکریپت در تگ‌های  <script>لینک می‌شود. index.html را در یک ویرایشگر متن باز کرده و کد زیر را اضافه کنید:

 <!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>JavaScript Modules</title>
  </head>

  <body>
    <h1>Answers</h1>
    <h2><strong id="x"></strong> and <strong id="y"></strong></h2>

    <h3>Addition</h3>
    <p id="addition"></p>

    <h3>Subtraction</h3>
    <p id="subtraction"></p>

    <h3>Multiplication</h3>
    <p id="multiplication"></p>

    <h3>Division</h3>
    <p id="division"></p>

    <script class='lozad' data-src='functions.js'></script>
    <script class='lozad' data-src='script.js'></script>
  </body>
</html>

این کد HTML مقدار متغیرهای x و y را در یک تگ <h2> و مقدار عملیات روی آن متغیرها را در تگ <p> زیر نمایش می‌دهد. خصوصیت id عناصر برای دستکاری DOM تنظیم شده است که در فایل script.js انجام می‌شود. این فایل مقادیر x و y را نیز تنظیم خواهد کرد. برای کسب اطلاعات بیشتر در مورد HTML و نحوه ساخت وب سایت این آموزش را ببینید.

فایل functions.js شامل توابع ریاضی است که در اسکریپت دوم استفاده خواهد شد. فایل functions.js را باز کرده و موارد زیر را اضافه کنید:

function sum(x, y) {
  return x + y
}

function difference(x, y) {
  return x - y
}

function product(x, y) {
  return x * y
}

function quotient(x, y) {
  return x / y
}

سرانجام فایل script.js مقادیر x و y را تعیین می‌کند، توابع را روی آنها اعمال می‌کند و نتیجه را نمایش می‌دهد:

const x = 10
const y = 5

document.getElementById('x').textContent = x
document.getElementById('y').textContent = y

document.getElementById('addition').textContent = sum(x, y)
document.getElementById('subtraction').textContent = difference(x, y)
document.getElementById('multiplication').textContent = product(x, y)
document.getElementById('division').textContent = quotient(x, y)

پس از تنظیم این فایل‌ها و ذخیره آنها، می‌توانید index.html را در یک مرورگر باز کرده و وب سایت خود را با تمام نتایج مشاهده کنید:

برای وب سایت‌های دارای چند اسکریپت کوچک، این یک روش موثر برای تقسیم کد است. هرچند چند مشکل برای این روش وجود دارد:

  • آلودگی فضای نام گلوبال: همه متغیرهایی که در اسکریپت‌های خود ایجاد کرده‌اید (مجموع، تفاضل، ضرب و تقسیم) اکنون در شی window وجود دارند. اگر سعی کنید از یک متغیر دیگر به نام sum در فایل دیگری استفاده کنید، دانستن اینکه از کدام مقدار در هر قسمت از اسکریپت‌ها استفاده شده، کمی دشوار خواهد بود. زیرا همه آنها از یک متغیر windows.sum یکسان استفاده می‌کنند. تنها راه private بودن یک متغیر قرار دادن آن در محدوده تابع است. حتی ممکن است بین یک id در DOM به نام x و var x تداخل وجود داشته باشد.
  • مدیریت وابستگی: برای اطمینان از در دسترس بودن متغیرهای صحیح، اسكریپت‌ها باید از بالا به پایین بارگیری شوند. ذخیره اسکریپت‌ها به عنوان فایل‌های مختلف، بی‌نظمی ایجاد می‌کند. اما در اصل همان داشتن یک <script> درون خطی در صفحه مرورگر است.

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

پس از آن، چند راه‌حل با ماژول ارائه شد که شامل موارد زیر بود:

 CommonJS، یک رویکرد همزمان در Node.js، تعریف ماژول ناهمزمان (AMD) که یک رویکرد ناهمزمان بود و تعریف ماژول گلوبال (UMD) رویکردی که از هر دو سبک قبلی پشتیبانی می‌کرد.

با ظهور این راه‌حل‌ها به اشتراک گذاشتن و استفاده مجدد از کد در قالب پکیج‌ها، ماژول‌های قابل توزیع مانند موارد موجود در npm، برای توسعه دهندگان آسان‌تر بود. با این حال، از آنجا که راه حل‌های بسیاری وجود داشت و هیچ کدام از آنها بومی جاوا اسکریپت نبودند، برای استفاده از ماژول‌ها در مرورگرها ابزارهایی مانندBabel ، Webpack یا Browserify باید اجرا می‌شدند.

با توجه به مشکلات زیادی که در رویکرد فایل چندگانه و همچنین پیچیدگی راه‌حل‌های ارائه شده وجود داشت، توسعه دهندگان علاقه مند بودند که رویکرد برنامه نویسی ماژولار را به زبان جاوا اسکریپت وارد کنند. به همین دلیل ECMAScript 2015 از ماژول‌های جاوا اسکریپت پشتیبانی می‌کند.

ماژول مجموعه‌ای از کد است که به عنوان یک رابط برای ارائه قابلیت استفاده برای سایر ماژول‌ها و همچنین امکان اعتماد به اجرای سایر ماژول‌ها عمل می‌کند. ماژول هم کد را export می‌کند و هم برای استفاده از کد دیگر آن را import می‌کند. ماژول‌ها مفید هستند زیرا به توسعه دهندگان امکان استفاده مجدد از کد را می‌دهند، همچنین یک رابط ثابت ارائه می‌دهند که بسیاری از توسعه دهندگان می‌توانند از آن استفاده کنند و فضای نام گلوبال دیگر آلوده نمی‌شود.

ماژول‌ها (که بعضا به آنها ماژول‌های ECMAScript یا ES Modules نیز گفته می‌شود) اکنون بطور طبیعی در جاوا اسکریپت در دسترس اند و در ادامه این آموزش نحوه استفاده و پیاده سازی آنها را در کد خود کشف خواهید کرد.

ماژول‌های جاوا اسکریپت

ماژول‌ها در جاوا اسکریپت از کلمات کلیدی import و export استفاده می‌کنند:

  • import: برای خواندن کد صادر شده از ماژول دیگر استفاده می‌شود.
  • export: برای تهیه کد به سایر ماژول‌ها استفاده می‌شود.

برای نشان دادن چگونگی استفاده از این، فایل functions.js خود را به روز کنید تا یک ماژول شود و توابع را export کنید. شما export را در مقابل هر تابع اضافه خواهید کرد که آنها را در دسترس ماژول‌های دیگر قرار می‌دهد.

کد هایلایت شده زیر را به فایل خود اضافه کنید:

export function sum(x, y) {
  return x + y
}

export function difference(x, y) {
  return x - y
}

export function product(x, y) {
  return x * y
}

export function quotient(x, y) {
  return x / y
}

اکنون در script.js برای بازیابی کد از ماژول funksion.js در بالای فایل از import استفاده خواهید کرد.

توجه: import همیشه باید قبل از هر کد دیگری در بالای فایل باشد و همچنین لازم است مسیر نسبی نیز وجود داشته باشد.

کد هایلایت شده زیر را به script.js اضافه کنید:

import { sum, difference, product, quotient } from './functions.js'

const x = 10
const y = 5

document.getElementById('x').textContent = x
document.getElementById('y').textContent = y

document.getElementById('addition').textContent = sum(x, y)
document.getElementById('subtraction').textContent = difference(x, y)
document.getElementById('multiplication').textContent = product(x, y)
document.getElementById('division').textContent = quotient(x, y)

توجه داشته باشید که توابع فردی با نام گذاری آنها در پرانتزها import می‌شوند.

برای اطمینان از بارگیری این کد به عنوان یک ماژول و نه یک اسکریپت منظم، عبارت type=”module” را به تگ‌های اسکریپت در index.html اضافه کنید. هر کدی که از import یا export استفاده می‌کند باید خصوصیت زیر را داشته باشد:

 
...
<script type="module" class='lozad' data-src='functions.js'></script>
<script type="module" class='lozad' data-src='script.js'></script>

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

Output
Access to script at 'file:///Users/your_file_path/script.js' from origin 'null' has been blocked by CORS policy: Cross-origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

به دلیل سیاست CORS، ماژول‌ها باید در محیط سرور استفاده شوند که می‌توانید آن را به صورت محلی با سرور http یا در اینترنت با سرویس دهنده هاست تنظیم کنید.

ماژول‌ها از چند جهت با اسکریپت‌های معمولی متفاوت هستند:

  • چیزی به دامنه گلوبال (window) اضافه نمی‌کنند.
  • همیشه در وضعیت strict هستند.
  • بارگیری دو ماژول در دو فایل مشابه هیچ تأثیری نخواهد داشت، زیرا آن‌ها فقط یک بار اجرا می‌شوند.
  • به یک محیط سرور نیاز دارند.

ماژول‌ها اغلب در کنار پکیج‌هایی مانند Webpack برای افزایش پشتیبانی مرورگر و قابلیت‌های اضافی استفاده می‌شوند، اما برای استفاده مستقیم در مرورگرها نیز در دسترس هستند.

در مرحله بعدی، برخی از روش‌های استفاده از import و export را خواهید آموخت.

export های با نام

همانطور که قبلا نشان داده شد، استفاده از سینتکس export به شما امکان می‌دهد مقادیری را که به نام آنها export شده است به صورت جداگانه وارد کنید. به عنوان مثال این نسخه ساده از function.js را در نظر بگیرید:

export function sum() {}
export function difference() {}

به شما این امکان را می‌دهد که با استفاده از آکلاد، sum و difference را با نام وارد کنید:

import { sum, difference } from './functions.js'

همچنین می‌توان از نام مستعار برای تغییر نام تابع استفاده کرد. می‌توانید این کار را انجام دهید تا از تداخل نام در ماژول جلوگیری کنید. در این مثال، sum به add تبدیل می‌شود و difference به subtract تغییر می‌یابد:

import {
  sum as add,
  difference as subtract
} from './functions.js'

add(1, 2) // 3

فراخوانی ()add در اینجا نتیجه تابع ()sum را نشان می‌دهد.

با استفاده از علامت * می‌توانید محتویات کل ماژول را به یک شی import کنید. در این حالت، sum و difference در شی mathFunctions به متد تبدیل می‌شوند.

import * as mathFunctions from './functions.js'

mathFunctions.sum(1, 2) // 3
mathFunctions.difference(10, 3) // 7

مقادیر اولیه، عبارات و اعلان تابع، توابع ناهمزمان و کلاس‌ها همگی می‌توانند export شوند، به شرط آنکه دارای یک شناسه باشند:

// Primitive values
export const number = 100
export const string = 'string'
export const undef = undefined
export const empty = null
export const obj = { name: 'Homer' }
export const array = ['Bart', 'Lisa', 'Maggie']

// Function expression
export const sum = (x, y) => x + y

// Function definition
export function difference(x, y) {
  return x - y
}

// Asynchronous function
export async function getBooks() {}

// Class
export class Book {
  constructor(name, author) {
    this.name = name
    this.author = author
  }
}

// Instantiated class
export const book = new Book('Lord of the Rings', 'J. R. R. Tolkien')

همه این export‌ ها را می‌توان با موفقیت import کرد. نوع دیگر export که در بخش بعدی کشف خواهید کرد به عنوان export پیش فرض شناخته می‌شود.

export های پیش فرض

در مثال‌های قبلی، شما export های متعددی را با نام صادر کرده و آنها را به صورت جداگانه یا به عنوان یک شی در هر export به عنوان یک متد بر روی شی import کرده‌اید. ماژول‌ها همچنین می‌توانند شامل export پیش فرض، با استفاده از کلمه کلیدی default باشند. export پیش فرض درون آکلاد قرار نمی‌گیرد، بلکه مستقیما به یک شناسه مشخص وارد می‌شود.

به عنوان مثال محتوای زیر را برای فایل functions.js در نظر بگیرید:

export default function sum(x, y) {
  return x + y
}

در فایل script.js می‌توانید تابع پیش فرض را به عنوان sum با موارد زیر import کنید:

import sum from './functions.js'

sum(1, 2) // 3

این می‌تواند خطرناک باشد، زیرا هیچ محدودیتی برای آنکه بتوانید export های پیش فرض را در هنگام import نام گذاری کنید، وجود ندارد. در این مثال، تابع پیش فرض به عنوان difference وارد می‌شود، اگرچه در واقع تابع sum است:

import difference from './functions.js'

difference(1, 2) // 3

به همین دلیل اغلب ترجیح داده می‌شود از export با نام استفاده شود. برخلاف آن، export پیش فرض نیازی به شناسه ندارد و یک مقدار اولیه به خودی خود یا یک تابع بی‌نام می‌تواند به عنوان export پیش فرض استفاده شود. در زیر مثالی از یک شی به عنوان export پیش فرض استفاده شده است:

export default {
  name: 'Lord of the Rings',
  author: 'J. R. R. Tolkien',
}

می‌توانید این را تحت عنوان book با موارد زیر import کنید:

import book from './functions.js'

به طور مشابه، مثال زیر export یک تابع بی نام را به عنوان export پیش فرض نشان می‌دهد:

export default () => 'This function is anonymous'

این را می‌توان با script.js زیر import کرد:

import anonymousFunction from './functions.js'

export با نام و export پیش فرض را می‌توان در کنار یکدیگر استفاده کرد، مانند این ماژول که دو مقدار نام گذاری شده و یک مقدار پیش فرض export می‌کند:

export const length = 10
export const width = 5

export default function perimeter(x, y) {
  return 2 * (x + y)
}

می‌توانید این متغیرها و تابع پیش فرض را با موارد زیر import کنید:

import calculatePerimeter, { length, width } from './functions.js'

calculatePerimeter(length, width) // 30

اکنون مقدار پیش فرض و مقادیر نام گذاری شده هر دو در دسترس اسکریپت هستند.

جمع‌بندی

برنامه نویسی ماژولار به شما امکان می‌دهد کدها را به اجزای جداگانه تقسیم کنید. این می‌تواند به شما در استفاده مجدد و سازگار بودن کد کمک کند و در عین حال از فضای نام گلوبال محافظت می‌کند. یک رابط ماژول می‌تواند در جاوا اسکریپت با کلمات کلیدی import و export پیاده سازی شود.

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

منبع

گردآوری و تالیف عرفان حشمتی
آفلاین
user-avatar

مهندس معماری سیستم های کامپیوتری، برنامه نویس و طراح وب سایت، علاقه مند به دنیای آی تی و تکنولوژی.

دیدگاه‌ها و پرسش‌ها

برای ارسال نظر لازم است ابتدا وارد سایت شوید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید