ככל שכמות הפריימוורקים של ג'אווהסקריפט הלכה ועלתה. כך עלה הצורך במודולים – יחידות עצמאיות של קוד שאפשר לייבא לתוך הקוד שלך. יש המון שימושים לכך בג'אווהסקריפט וגם אתם משתמשים בה – מ-MooTools ו-jQuery ועד ל-common.js ול-require.js.
עד כה, הדרך הפשוטה ביותר לממש מודולים היתה באמצעות פונקציה אנונימית שמריצה את עצמה – מה שנקרא בשפות גויי הים: Immediately-Invoked Function Expression או בקיצור – IIFE. סוג של דצ"ך עד"ש באח"ב רק בג'אווהסקריפטית 🙂
אבל אני סוטה מהנושא. מה זו פונקציה אנונימית שמריצה את עצמה? משהו מהסוג הזה:
//Module Starts
(function(window){
var sum = function(x, y){
return x + y;
}
var sub = function(x, y){
return x - y;
}
var math = {
findSum: function(a, b){
return sum(a,b);
},
findSub: function(a, b){
return sub(a, b);
}
}
window.math = math;
})(window);
//Module Ends
console.log(math.findSum(1, 2)); //3
console.log(math.findSub(1, 2)); //-1
אם זה נראה לכם מוכר, אתם לא תופתעו לגלות שגם jQuery משתמשת בסוג הזה של מודולים. אם זה לא נראה לכם מוכר, תסתכלו על הקוד. מה שיכול להראות לכם משונה זה הדבר הזה:
(function(window){
})(window);
למה צריך את זה בכלל? זה נועד לבודד את המודול מהסקופ הכללי ולא לתת לפונקציות ולמשתנים שלו להתערבב עם הכלל.
יש ספריות/סביבות של ג'אווהסקריפט שלקחו את זה יותר רחוק ומאפשרות להכניס מודולים כקובץ עצמאי כשהקריאה אליהם נעשית מתוך הג'אווהסקריפט עצמו. למשל, משהו בסגנון הזה:
//math.js fille
var sum = function(x, y){
return x + y;
}
var sub = function(x, y){
return x - y;
}
var math = {
findSum: function(a, b){
return sum(a,b);
},
findSub: function(a, b){
return sub(a, b);
}
}
//All the variable we want to expose outside needs to be property of "exports" object.
exports.math = math;
//some other js file
//no file extension required
var math = require("./math").math;
console.log(math.findSum(1, 2)); //3
console.log(math.findSub(1, 2)); //-1
הקוד שלעיל מתאר שני קבצים. החלק הראשון הוא קובץ בשם math.js והקובץ השני הוא קובץ כלשהו שקורא באמצעות פונקצית require לקובץ math.js ומפעיל אותו. אם אתם מכירים node.js, הסינטקס הזה אמור להיות לכם מאוד מוכר. הדיזיין הזה נקרא Asynchronous Module Definition או בראשי תבות AMD והספריה שמאפשרת אותו נקראת commonJS והיא זמינה בעיקר באפליקציות node.js. היא הרבה יותר נוחה ואינטואיטיבית מ-IIFE, אבל למרבה הצער היה אפשר לממש אותה בעיקר בסביבת השרת. נכון שיש כל מיני פתרונות לאפשר AMD בדפדפנים, אבל הם לא היו מספיק טובים. עד שהגיעה ECMAScript 6 שמאפשרת לנו להשתמש במודולים באופן אינהרנטי ופשוט בדיוק כמו AMD.
הגדרת המודול
הדוגמה שלי היא פשוטה ביותר, יש לי מודול שנקרא math ויש לו תכונה אחת שנקראת pi ומתודה אחת שנקראת sum. איך אצור את המודול? אצור תיקיה בשם lib וקובץ בשם math. כך הקובץ יראה:
export class Math {
constructor() {
this.sum = function(x, y){
return x + y;
}
this.pi = = 3.141593;
}
findSum(a, b) {
return this.sum(a, b);
}
getPi() {
return this.sub(a, b);
}
}
למי שעוקב אחר המדריך, הסינטקס של class יראה מאוד טבעי. למי שלא ולא מבין מה אני רוצה ממנו, זה הזמן לעבור על class. אפשר לראות שה-class חושף החוצה שתי מתודות חמודות. עוד משהו מאוד קריטי הוא שיש export. ה- export הוא מה שאני מייצא. עכשיו למימוש. בכל קובץ שהוא בפרויקט, אני רק צריך לבצע ייבוא של הקובץ הזה. להזכירכם – שמרתי אותו ב lib/math.js. אחרי הייבוא, אני אבצע instance ל-class לתוך משתנה וזה הכל!
import {Math} from 'lib/math';
var math = new Math();
console.log(math.findSum(1, 2)); //3
console.log(math.getPi()); //3.141593
מה קורה פה? לא צריך להבהל. הדבר הכי חשוב הוא ה-import שבו אני צריך להכניס שני דברים – את שם ה-class שאני עושה לו import (במקרה הזה Math) ואת שם הקובץ ומיקומו. אחרי שעשיתי את זה, ה-class ששמו בישראל הוא Math, זמין עבורי ואני יכול לעשות לו instance (כלומר לקרוא לו) ולהשתמש במתודות שלו בלי בעיה.
רוצים עוד דוגמה? למה לא? לא חייבים להשתמש ב-class אפילו. שימו לב לקובץ name שיצרתי:
//File: directory/name.js
export var environment = 'develop';
export var userName = 'Moshe';
export function sum(x,y) { return x + y;};
//Other file
import * as names from "directory/names";
console.log(names.environment); //develop
console.log(names.userName); //Moshe
console.log(names.sum(2,3)); //5
איזה יופי – כל דבר שאני עושה לו export, אני יכול לייבא אותו. שימו לב שאני יכול להשתמש ב
import {environment, userName, sum} as names from "directory/names";
או בכוכבית כדי לקבל את הכל. את ה-import אני עושה לתוך משתנה ועכשיו כל מה שעשיתי לו export חשוף בלי בעיה ובלי חשש להתנגשות סקופ וזיהום הסקופ שלי. ואל תגידו שזה לא פשוט ונחמד.
6 תגובות
לא מכיר JS, אבל תבדוק את ()getPi
רן, מחכה למאמרים שלך כל יום ראשון!
לא הבנתי את הטריק, זה מוכר גם בדפדפן או רק בצד שרת?
ES6 לא מוכר בשום דפדפן, בשביל זה יש Babel..
בכל אופן, מדובר פשוט בסינטקס ג'אווה סקריפטי טהור.
זה כבר לא מדויק לשמחתנו. רוב הפיצ'רים של ES6 עובדים בדפדפנים. אפילו על הדפדפן האהוב.
גם בדפדפן וגם בצד השרת. במאמר הראשון בסדרה הסברתי בדיוק איך בודקים איפה זה עובד.
ספציפית הפיצ'ר הזה (נכון לעכשיו) לא עובד באופן טבעי באף מערכת שאני מכיר – אלא אם כן משתמשים ב-Babel וגם על כך מוסבר במאמר הראשון.
אהבתי אחי
עשית לי סדר