לדלג לתוכן

4.4 מודולים פתרון

פתרון - מודולים


תרגיל 1 - ייצוא וייבוא בסיסיים

// stringUtils.js
export function capitalize(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
}

export function reverse(str) {
    return str.split("").reverse().join("");
}

export function countWords(str) {
    return str.trim().split(/\s+/).length;
}
// mathUtils.js
export function clamp(value, min, max) {
    return Math.min(Math.max(value, min), max);
}

export function average(...numbers) {
    if (numbers.length === 0) return 0;
    const sum = numbers.reduce((acc, n) => acc + n, 0);
    return sum / numbers.length;
}

export function isPrime(n) {
    if (n < 2) return false;
    for (let i = 2; i <= Math.sqrt(n); i++) {
        if (n % i === 0) return false;
    }
    return true;
}
// main.js
import { capitalize, reverse, countWords } from "./stringUtils.js";
import { clamp, average, isPrime } from "./mathUtils.js";

console.log(capitalize("hello"));       // "Hello"
console.log(reverse("abc"));            // "cba"
console.log(countWords("hello world")); // 2
console.log(clamp(15, 0, 10));          // 10
console.log(average(1, 2, 3, 4));       // 2.5
console.log(isPrime(7));                // true

תרגיל 2 - ייצוא ברירת מחדל

// Logger.js
export const LOG_LEVELS = {
    INFO: "info",
    WARN: "warn",
    ERROR: "error"
};

export default class Logger {
    constructor(prefix) {
        this.prefix = prefix;
    }

    log(message) {
        console.log(`[${this.prefix}] ${message}`);
    }

    warn(message) {
        console.log(`[${this.prefix}] WARNING: ${message}`);
    }

    error(message) {
        console.log(`[${this.prefix}] ERROR: ${message}`);
    }
}
// main.js
import Logger, { LOG_LEVELS } from "./Logger.js";

const logger = new Logger("App");
logger.log("Started");        // "[App] Started"
logger.warn("Low memory");    // "[App] WARNING: Low memory"
logger.error("Crash!");       // "[App] ERROR: Crash!"
console.log(LOG_LEVELS.WARN); // "warn"

תרגיל 3 - barrel file

// validators/string.js
export function isNotEmpty(str) {
    return typeof str === "string" && str.trim().length > 0;
}

export function hasMinLength(str, min) {
    return typeof str === "string" && str.length >= min;
}
// validators/number.js
export function isPositive(n) {
    return typeof n === "number" && n > 0;
}

export function isInRange(n, min, max) {
    return typeof n === "number" && n >= min && n <= max;
}
// validators/email.js
export function isValidEmail(str) {
    if (typeof str !== "string") return false;
    const parts = str.split("@");
    if (parts.length !== 2) return false;
    return parts[0].length > 0 && parts[1].includes(".");
}
// validators/index.js (barrel file)
export { isNotEmpty, hasMinLength } from "./string.js";
export { isPositive, isInRange } from "./number.js";
export { isValidEmail } from "./email.js";
// main.js
import { isNotEmpty, isPositive, isValidEmail } from "./validators/index.js";

console.log(isNotEmpty("hello"));          // true
console.log(isNotEmpty(""));               // false
console.log(isPositive(-5));               // false
console.log(isPositive(10));               // true
console.log(isValidEmail("a@b.com"));      // true
console.log(isValidEmail("not-an-email")); // false

תרגיל 4 - שינוי שמות בייבוא

// main.js

// import with renamed aliases
import {
    format as zeroPad,
    parse as toInt,
    validate as isDefined
} from "./helpers.js";

console.log(zeroPad(5));       // "05"
console.log(toInt("42"));     // 42
console.log(isDefined(null));  // false
console.log(isDefined("hi")); // true

// import everything as an object
import * as helpers from "./helpers.js";

console.log(helpers.format(3));       // "03"
console.log(helpers.parse("100"));    // 100
console.log(helpers.validate(0));     // true

תרגיל 5 - ייבוא דינמי

async function loadModule(moduleName) {
    try {
        const module = await import(`./modules/${moduleName}.js`);
        return module;
    } catch (error) {
        console.error(`Failed to load module "${moduleName}":`, error.message);
        return null;
    }
}

// usage
const math = await loadModule("math");
if (math) {
    console.log(math.add(2, 3)); // 5
}

const nonExistent = await loadModule("does-not-exist");
// "Failed to load module "does-not-exist": ..."
console.log(nonExistent); // null
function loadOnClick(buttonId, moduleName) {
    const button = document.getElementById(buttonId);
    let loaded = false;

    button.addEventListener("click", async () => {
        if (loaded) {
            console.log(`Module "${moduleName}" already loaded`);
            return;
        }

        const module = await loadModule(moduleName);
        if (module) {
            loaded = true;
            console.log(`Module "${moduleName}" loaded successfully`);
            // module is now available for use
        }
    });
}

// usage:
loadOnClick("loadChartBtn", "chart");

שימו לב ל-closure: המשתנה loaded נשמר ב-closure של ה-event handler, ומונע טעינה כפולה.


תרגיל 6 - מערכת פלאגינים עם מודולים

// plugins/uppercase.js
export const name = "uppercase";
export const description = "Converts text to uppercase";

export function execute(input) {
    return input.toUpperCase();
}
// plugins/reverse.js
export const name = "reverse";
export const description = "Reverses the input string";

export function execute(input) {
    return input.split("").reverse().join("");
}
// plugins/wordcount.js
export const name = "wordcount";
export const description = "Returns the number of words in the input";

export function execute(input) {
    return input.trim().split(/\s+/).length;
}
// PluginManager.js
class PluginManager {
    #plugins = new Map();

    async loadPlugin(name) {
        try {
            const module = await import(`./plugins/${name}.js`);
            this.#plugins.set(module.name, {
                name: module.name,
                description: module.description,
                execute: module.execute
            });
            console.log(`Plugin "${module.name}" loaded`);
        } catch (error) {
            console.error(`Failed to load plugin "${name}":`, error.message);
        }
    }

    listPlugins() {
        return Array.from(this.#plugins.values()).map(p => ({
            name: p.name,
            description: p.description
        }));
    }

    run(pluginName, input) {
        const plugin = this.#plugins.get(pluginName);
        if (!plugin) {
            throw new Error(`Plugin "${pluginName}" not found`);
        }
        return plugin.execute(input);
    }
}

// usage
const manager = new PluginManager();
await manager.loadPlugin("uppercase");
await manager.loadPlugin("reverse");
await manager.loadPlugin("wordcount");

console.log(manager.listPlugins());
// [
//   { name: "uppercase", description: "Converts text to uppercase" },
//   { name: "reverse", description: "Reverses the input string" },
//   { name: "wordcount", description: "Returns the number of words in the input" }
// ]

console.log(manager.run("uppercase", "hello world")); // "HELLO WORLD"
console.log(manager.run("reverse", "hello"));          // "olleh"
console.log(manager.run("wordcount", "one two three")); // 3