سرور مجازی

۲ مطلب با کلمه‌ی کلیدی «جاوا» ثبت شده است

  • ۰
  • ۰

 نحوه کار با فایل های فشرده در Node.js

ورود به سایت

 

معرفی

کار با فایل ها یکی از کارهای رایج در بین توسعه دهندگان است. با افزایش حجم فایل های شما، فضای قابل توجهی روی هارد دیسک شما اشغال می شود. دیر یا زود ممکن است لازم باشد فایل ها را به سرورهای دیگر منتقل کنید یا چندین فایل را از دستگاه محلی خود به سیستم عامل های مختلف آپلود کنید. برخی از این پلتفرم ها محدودیت اندازه فایل دارند و فایل های بزرگ را نمی پذیرند. برای حل این مشکل، می توانید فایل ها را در یک فایل ZIP گروه بندی کنید. فایل ZIP یک فرمت آرشیو است که فایل ها را با الگوریتم فشرده سازی بدون اتلاف فشرده و فشرده می کند. الگوریتم می تواند داده ها را بدون از دست دادن داده ها بازسازی کند. در Node.js، می توانید از ماژول adm-zip برای ایجاد و خواندن آرشیوهای ZIP استفاده کنید.

در این آموزش از ماژول adm-zip برای فشرده سازی، خواندن و از حالت فشرده خارج کردن فایل ها استفاده خواهید کرد. ابتدا چندین فایل را با استفاده از adm-zip در یک آرشیو ZIP ترکیب می‌کنید. سپس محتویات بایگانی ZIP را فهرست می کنید. پس از آن، یک فایل را به یک بایگانی ZIP موجود اضافه می‌کنید و در نهایت، یک بایگانی ZIP را در یک فهرست استخراج می‌کنید.

پیش نیازها

برای دنبال کردن این آموزش، شما نیاز دارید:

  • نصب Node.js روی محیط محلی یا سرور شما
  • آشنایی با نحوه نوشتن یک برنامه Node.js
  • درک اولیه از برنامه نویسی ناهمزمان در جاوا اسکریپت.
  • آشنایی با نحوه کار با فایل ها در Node.js.

مرحله 1 – راه اندازی پروژه

در این مرحله، دایرکتوری پروژه خود را ایجاد کرده و adm-zip را به عنوان یک وابستگی نصب می‌کنید. این دایرکتوری جایی است که شما فایل های برنامه خود را نگه می دارید. همچنین دایرکتوری دیگری حاوی فایل های متنی و تصویر ایجاد خواهید کرد. شما این فهرست را در بخش بعدی بایگانی خواهید کرد.

با دستور زیر یک دایرکتوری به نام zip_app ایجاد کنید:

  • mkdir zip_app

با دستور cd به پوشه جدید ایجاد شده بروید:

cd zip_app

در داخل دایرکتوری، یک فایل package.json برای مدیریت وابستگی های پروژه ایجاد کنید:

npm init -y

گزینه -y یک فایل پیش فرض package.json ایجاد می کند.

بعد، adm-zip را با دستور npm install نصب کنید:

npm install adm-zip

پس از اجرای دستور، npm adm-zip را نصب کرده و فایل package.json را به روز می کند.

سپس یک دایرکتوری به نام test ایجاد کنید و وارد آن شوید:

mkdir test && cd test

در این دایرکتوری شما سه فایل متنی ایجاد کرده و یک تصویر را دانلود خواهید کرد. این سه فایل با محتوای ساختگی پر می شوند تا اندازه فایل آنها بزرگتر شود. این به نشان دادن فشرده سازی ZIP هنگام بایگانی این فهرست کمک می کند.

file1.txt را ایجاد کنید و با استفاده از دستور زیر آن را با محتوای ساختگی پر کنید:

yes “dummy content” | head -n 100000 > file1.txt

دستور yes محتوای ساختگی رشته را به طور مکرر ثبت می کند. با استفاده از دستور pipe |، خروجی را از دستور yes ارسال می کنید تا به عنوان ورودی دستور head استفاده شود. دستور head بخشی از ورودی داده شده را در خروجی استاندارد چاپ می کند. گزینه -n تعداد خطوطی که باید در خروجی استاندارد نوشته شود را مشخص می کند. در نهایت، خروجی سر را با استفاده از > به فایل جدید file1.txt هدایت می‌کنید.

یک فایل دوم با رشته “محتوای ساختگی” تکرار شده 300000 خط ایجاد کنید:

yes “dummy content” | head -n 300000 > file2.txt

فایل دیگری با رشته محتوای ساختگی که 600000 خط تکرار شده است ایجاد کنید:

yes “dummy content” | head -n 600000 > file3.txt

در نهایت، یک تصویر را با استفاده از curl در دایرکتوری دانلود کنید:

curl -O https://assets.vpsgol.net/how-to-process-images-in-node-js-with-sharp/underwater.png

با دستور زیر به دایرکتوری اصلی پروژه برگردید:

cd ..

.. شما را به دایرکتوری والد که zip_app است منتقل می کند.

اکنون دایرکتوری پروژه را ایجاد کرده اید، adm-zip را نصب کرده اید و یک دایرکتوری با فایل ها برای بایگانی ایجاد کرده اید. در مرحله بعدی، یک دایرکتوری را با استفاده از ماژول adm-zip بایگانی خواهید کرد.

مرحله 2 – ایجاد یک آرشیو ZIP

در این مرحله از adm-zip برای فشرده سازی و بایگانی دایرکتوری که در قسمت قبل ایجاد کرده اید استفاده می کنید.

برای بایگانی دایرکتوری، ماژول adm-zip را وارد کرده و از روش addLocalFolder() ماژول برای اضافه کردن دایرکتوری به شی ZIP ماژول adm-zip استفاده می کنید. پس از آن، از متد writeZip() ماژول برای ذخیره آرشیو در سیستم محلی خود استفاده خواهید کرد.

یک فایل جدید createArchive.js در ویرایشگر متن دلخواه خود ایجاد و باز کنید. این آموزش از nano، یک ویرایشگر متن خط فرمان استفاده می کند:

nano createArchive.js

سپس، در ماژول adm-zip در فایل createArchive.js خود نیاز دارید:

zip_app/createArchive.js

const AdmZip = require("adm-zip");

ماژول adm-zip کلاسی را ارائه می دهد که حاوی متدهایی برای ایجاد آرشیو ZIP است.

از آنجایی که در طول فرآیند بایگانی با فایل‌های بزرگ مواجه می‌شوید، ممکن است تا زمانی که بایگانی ZIP ذخیره نشود، رشته اصلی را مسدود کنید. برای نوشتن کد غیر مسدود کننده، یک تابع ناهمزمان برای ایجاد و ذخیره یک آرشیو ZIP تعریف می کنید.

در فایل createArchive.js خود، کد هایلایت شده زیر را اضافه کنید:

zip_app/createArchive.js

const AdmZip = require("adm-zip");

async function createZipArchive() {
  const zip = new AdmZip();
  const outputFile = "test.zip";
  zip.addLocalFolder("./test");
  zip.writeZip(outputFile);
  console.log(`Created ${outputFile} successfully`);
}

createZipArchive();

 

createZipArchive یک تابع ناهمزمان است که یک آرشیو ZIP از یک دایرکتوری مشخص ایجاد می کند. چیزی که آن را ناهمزمان می کند، کلمه کلیدی async است که قبل از برچسب تابع تعریف کرده اید. در داخل تابع، نمونه‌ای از ماژول adm-zip ایجاد می‌کنید که روش‌هایی را ارائه می‌کند که می‌توانید برای خواندن و ایجاد آرشیو استفاده کنید. هنگامی که یک نمونه ایجاد می کنید، adm-zip یک ZIP درون حافظه ایجاد می کند که در آن می توانید فایل ها یا دایرکتوری ها را اضافه کنید.

در مرحله بعد، نام بایگانی را تعریف کرده و آن را در متغیر outputDir ذخیره می کنید. برای افزودن دایرکتوری آزمایشی به آرشیو درون حافظه، متد addLocalFolder() را از adm-zip با مسیر دایرکتوری به عنوان آرگومان فراخوانی می کنید.

پس از اضافه شدن دایرکتوری، شما متد writeZip() را از adm-zip با متغیری حاوی نام آرشیو ZIP فراخوانی می کنید. متد writeZip() آرشیو را در دیسک محلی شما ذخیره می کند.

پس از انجام این کار، console.log() را فراخوانی کنید تا ثبت کنید که فایل ZIP با موفقیت ایجاد شده است.

در نهایت، تابع createZipArchive() را فراخوانی می کنید.

قبل از اجرای فایل، کد را در یک بلوک try…catch بپیچید تا خطاهای زمان اجرا را مدیریت کنید:

zip_app/createArchive.js

const AdmZip = require("adm-zip");

async function createZipArchive() {
  try {
    const zip = new AdmZip();
    const outputFile = "test.zip";
    zip.addLocalFolder("./test");
    zip.writeZip(outputFile);
    console.log(`Created ${outputFile} successfully`);
  } catch (e) {
    console.log(`Something went wrong. ${e}`);
  }
}

createZipArchive();

در داخل بلوک try، کد سعی می کند یک بایگانی ZIP ایجاد کند. در صورت موفقیت آمیز بودن، تابع ()createZipArchive با پرش از بلوک catch خارج می شود. اگر ایجاد یک بایگانی ZIP باعث خطا شود، اجرا به بلوک catch پرش می‌کند و خطا را در کنسول ثبت می‌کند.

با CTRL+X فایل را در نانو ذخیره کرده و از آن خارج شوید. برای ذخیره تغییرات، y را وارد کنید و با فشار دادن ENTER در ویندوز یا کلید RETURN در مک، فایل را تأیید کنید.

فایل createArchive.js را با استفاده از دستور node اجرا کنید:

node createArchive.js

خروجی زیر را دریافت خواهید کرد:

 

Output

Created test.zip successfully

محتویات دایرکتوری را فهرست کنید تا ببینید آیا آرشیو ZIP ایجاد شده است یا خیر:

ls

خروجی زیر را دریافت خواهید کرد که آرشیو را در میان مطالب نشان می دهد:

 

Output

createArchive.js  node_modules  package-lock.json
package.json  test  test.zip

با تأیید اینکه بایگانی ZIP ایجاد شده است، بایگانی ZIP و اندازه فایل دایرکتوری آزمایشی را با هم مقایسه می کنید تا ببینید فشرده سازی کار می کند یا خیر.

اندازه دایرکتوری تست را با استفاده از دستور du بررسی کنید:

du -h test

پرچم -h به du دستور می دهد که اندازه فهرست را در قالبی قابل خواندن توسط انسان نشان دهد.

پس از اجرای دستور، خروجی زیر را دریافت خواهید کرد:

 

Output

15M test

سپس، اندازه فایل بایگانی test.zip را بررسی کنید:

du -h test.zip

دستور du خروجی زیر را ثبت می کند:

 

Output

760K    test.zip

همانطور که می بینید، ایجاد فایل ZIP اندازه دایرکتوری را از 15 مگابایت (مگابایت) به 760 کیلوبایت (KB) کاهش داده است، که یک تفاوت بزرگ است. فایل ZIP قابل حمل تر و اندازه کوچکتر است.

اکنون که یک بایگانی ZIP ایجاد کردید، آماده فهرست کردن محتویات در یک فایل ZIP هستید.

مرحله 3 – فهرست کردن فایل ها در آرشیو ZIP

در این مرحله، با استفاده از adm-zip، تمام فایل‌های موجود در آرشیو ZIP را می‌خوانید و فهرست می‌کنید. برای انجام این کار، ماژول adm-zip را با مسیر آرشیو ZIP خود نمونه سازی می کنید. سپس متد ()getEntries ماژول را فراخوانی می کنید که آرایه ای از اشیاء را برمی گرداند. هر شی اطلاعات مهمی در مورد یک آیتم در آرشیو ZIP دارد. برای فهرست کردن فایل‌ها، روی آرایه تکرار می‌کنید و از شی به نام فایل دسترسی پیدا می‌کنید و آن را در کنسول وارد می‌کنید.

readArchive.js را در ویرایشگر متن دلخواه خود ایجاد و باز کنید:

nano readArchive.js

در readArchive.js خود، کد زیر را برای خواندن و فهرست کردن محتویات یک بایگانی ZIP اضافه کنید:

zip_app/readArchive.js

const AdmZip = require("adm-zip");

async function readZipArchive(filepath) {
  try {
    const zip = new AdmZip(filepath);

    for (const zipEntry of zip.getEntries()) {
      console.log(zipEntry.toString());
    }
  } catch (e) {
    console.log(`Something went wrong. ${e}`);
  }
}

readZipArchive("./test.zip");

ابتدا به ماژول adm-zip نیاز دارید.

در مرحله بعد، تابع readZipArchive() را تعریف می کنید که یک تابع ناهمزمان است. در داخل تابع، یک نمونه از adm-zip با مسیر فایل ZIP که می‌خواهید بخوانید ایجاد می‌کنید. مسیر فایل توسط پارامتر filepath ارائه می شود. adm-zip فایل را می خواند و آن را تجزیه می کند.

پس از خواندن بایگانی، یک عبارت for…of تعریف می کنید که روی اشیاء در آرایه ای تکرار می شود که متد getEntries() از adm-zip هنگام فراخوانی آن را برمی گرداند. در هر تکرار، شی به متغیر zipEntry اختصاص داده می شود. در داخل حلقه، شما شی را به رشته ای تبدیل می کنید که شی را با استفاده از متد Node.js toString() نشان می دهد، سپس با استفاده از متد console.log() آن را در کنسول وارد می کنید.

در نهایت، تابع readZipArchive() را با مسیر فایل آرشیو ZIP به عنوان آرگومان فراخوانی می کنید.

فایل خود را ذخیره کرده و از آن خارج شوید، سپس فایل را با دستور زیر اجرا کنید:

node readArchive.js

خروجی مشابه موارد زیر را دریافت خواهید کرد (ویرایش شده برای اختصار):

 

Output

{
    "entryName": "file1.txt",
    "name": "file1.txt",
    "comment": "",
    "isDirectory": false,
    "header": {
        ...
    },
    "compressedData": "<27547 bytes buffer>",
    "data": "<null>"
}
...

کنسول چهار شی را ثبت می کند. سایر اشیاء ویرایش شده اند تا آموزش مختصر بماند.

هر فایل در بایگانی با یک شی شبیه به آنچه در خروجی قبلی است نشان داده می شود. برای دریافت نام فایل برای هر فایل، باید به ویژگی name دسترسی داشته باشید.

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

zip_app/readArchive.js

const AdmZip = require("adm-zip");

async function readZipArchive(filepath) {
  try {
    const zip = new AdmZip(filepath);

    for (const zipEntry of zip.getEntries()) {
      console.log(zipEntry.name);
    }
  } catch (e) {
    console.log(`Something went wrong. ${e}`);
  }
}

readZipArchive("./test.zip");

ویرایشگر متن خود را ذخیره کرده و از آن خارج شوید. حالا با دستور node دوباره فایل را اجرا کنید:

node readArchive.js

با اجرای فایل خروجی زیر حاصل می شود:

 

Output

file1.txt
file2.txt
file3.txt
underwater.png

اکنون خروجی نام فایل هر فایل را در آرشیو ZIP ثبت می کند.

اکنون می توانید هر فایل را در یک آرشیو ZIP بخوانید و فهرست کنید. در بخش بعدی، فایلی را به آرشیو ZIP موجود اضافه می‌کنید.

مرحله 4 – افزودن یک فایل به یک بایگانی موجود

در این مرحله، یک فایل ایجاد می‌کنید و بدون استخراج آن، آن را به بایگانی ZIP که قبلا ایجاد کرده‌اید، اضافه می‌کنید. ابتدا، با ایجاد یک نمونه adm-zip، آرشیو ZIP را می خوانید. دوم، شما از متد ()addFile ماژول برای اضافه کردن فایل در ZIP استفاده می کنید. در نهایت، بایگانی ZIP را در سیستم محلی ذخیره خواهید کرد.

یک فایل file4.txt با محتوای ساختگی که 600000 خط تکرار شده است ایجاد کنید:

yes “dummy content” | head -n 600000 > file4.txt

updateArchive.js را در ویرایشگر متن خود ایجاد و باز کنید:

nano updateArchive.js

در ماژول adm-zip و ماژول fs که به شما امکان می دهد با فایل های موجود در فایل updateArchive.js خود کار کنید، نیاز دارید:

const AdmZip = require("adm-zip");
const fs = require("fs").promises;

شما نیاز به نسخه مبتنی بر وعده از نسخه ماژول fs دارید که به شما امکان می دهد کدهای ناهمزمان بنویسید. هنگامی که شما یک متد fs را فراخوانی می کنید، یک وعده برمی گرداند.

سپس در فایل updateArchive.js خود، کد برجسته شده زیر را برای افزودن یک فایل جدید به بایگانی ZIP اضافه کنید:

zip_app/updateArchive.js

const AdmZip = require("adm-zip");
const fs = require("fs").promises;

async function updateZipArchive(filepath) {
  try {
    const zip = new AdmZip(filepath);

    content = await fs.readFile("./file4.txt");
    zip.addFile("file4.txt", content);
    zip.writeZip(filepath);
    console.log(`Updated ${filepath} successfully`);
  } catch (e) {
    console.log(`Something went wrong. ${e}`);
  }
}

updateZipArchive("./test.zip");

updateZipArchive یک تابع ناهمزمان است که یک فایل را در سیستم فایل می خواند و آن را به یک ZIP موجود اضافه می کند. در تابع، شما یک نمونه از adm-zip را با مسیر فایل آرشیو ZIP در مسیر فایل به عنوان پارامتر ایجاد می کنید. در مرحله بعد، متد readFile() ماژول fs را برای خواندن فایل در سیستم فایل فراخوانی می کنید. متد readFile() یک وعده را برمی‌گرداند که با کلمه کلیدی await حل می‌کنید (wait فقط در توابع ناهمزمان معتبر است). پس از حل شدن، متد یک شی بافر را برمی گرداند که حاوی محتویات فایل است.

در مرحله بعد، متد addFile() را از adm-zip فراخوانی می کنید. این روش دو آرگومان می گیرد. آرگومان اول نام فایلی است که می خواهید به آرشیو اضافه کنید و آرگومان دوم شی بافر حاوی محتویات فایلی است که متد readFile() می خواند.

پس از آن، متد writeZip ماژول adm-zip را برای ذخیره و نوشتن تغییرات جدید در آرشیو ZIP فراخوانی می‌کنید. پس از انجام این کار، متد console.log() را برای ثبت یک پیام موفقیت آمیز فراخوانی می کنید.

در نهایت، تابع updateZipArchive() را با مسیر فایل آرشیو Zip به عنوان آرگومان فراخوانی می کنید.

فایل خود را ذخیره کرده و از آن خارج شوید. فایل updateArchive.js را با دستور زیر اجرا کنید:

node updateArchive.js

خروجی را به این صورت خواهید دید:

 

Output

Updated ./test.zip successfully

اکنون، تأیید کنید که آرشیو ZIP حاوی فایل جدید است. فایل readArchive.js را اجرا کنید تا محتویات آرشیو ZIP را با دستور زیر فهرست کنید:

node readArchive.js

خروجی را به این صورت خواهید دید:

 

Output

Updated ./test.zip successfully

اکنون، تأیید کنید که آرشیو ZIP حاوی فایل جدید است. فایل readArchive.js را اجرا کنید تا محتویات آرشیو ZIP را با دستور زیر فهرست کنید:

node readArchive.js

خروجی زیر را دریافت خواهید کرد:

file1.txt
file2.txt
file3.txt
file4.txt
underwater.png

این تایید می کند که فایل به ZIP اضافه شده است.

اکنون که می توانید یک فایل را به یک بایگانی موجود اضافه کنید، بایگانی را در بخش بعدی استخراج خواهید کرد.

مرحله 5 – استخراج یک بایگانی فشرده

در این مرحله، تمام محتویات یک بایگانی ZIP را می خوانید و در یک دایرکتوری استخراج می کنید. برای استخراج بایگانی ZIP، adm-zip را با مسیر فایل بایگانی نمونه‌سازی می‌کنید. پس از آن، متد ()extractAllTo ماژول را با نام دایرکتوری که می‌خواهید محتوای ZIP استخراج‌شده شما در آن قرار گیرد، فراخوانی می‌کنید.

ExtractArchive.js را در ویرایشگر متن خود ایجاد و باز کنید:

nano extractArchive.js

در ماژول adm-zip و ماژول مسیر در فایل extractArchive.js خود نیاز دارید:

zip_app/extractArchive.js

const AdmZip = require("adm-zip");
const path = require("path");

ماژول مسیر روش های مفیدی را برای برخورد با مسیرهای فایل ارائه می دهد.

هنوز در فایل extractArchive.js خود، کد هایلایت شده زیر را برای استخراج آرشیو اضافه کنید:

zip_app/extractArchive.js

const AdmZip = require("adm-zip");
const path = require("path");

async function extractArchive(filepath) {
  try {
    const zip = new AdmZip(filepath);
    const outputDir = `${path.parse(filepath).name}_extracted`;
    zip.extractAllTo(outputDir);

    console.log(`Extracted to "${outputDir}" successfully`);
  } catch (e) {
    console.log(`Something went wrong. ${e}`);
  }
}

extractArchive("./test.zip");

ExtractArchive() یک تابع ناهمزمان است که پارامتری حاوی مسیر فایل بایگانی ZIP را می گیرد. در داخل تابع، adm-zip را با مسیر فایل بایگانی ZIP ارائه شده توسط پارامتر filepath، نمونه سازی می کنید.

بعد، شما یک الگو را به معنای واقعی کلمه تعریف می کنید. در داخل مکان‌نمای تحت اللفظی الگو (${})، متد parse() را از ماژول path با مسیر فایل فراخوانی می‌کنید. متد parse() یک شی را برمی گرداند. برای دریافت نام فایل ZIP بدون پسوند فایل، ویژگی name را به شیئی که متد parse() برمی گرداند اضافه می کنید. هنگامی که نام بایگانی برگردانده شد، الگو به معنای واقعی کلمه مقدار را با رشته _extracted درون یابی می کند. سپس مقدار در متغیر outputDir ذخیره می شود. این نام دایرکتوری استخراج شده خواهد بود.

در مرحله بعد، متد ExtractAllTo ماژول adm-zip را با نام دایرکتوری ذخیره شده در outputDir برای استخراج محتویات در دایرکتوری فراخوانی می کنید. پس از آن، برای ثبت یک پیام موفقیت آمیز، console.log() را فراخوانی می کنید.

در نهایت، تابع extractArchive() را با مسیر آرشیو ZIP فراخوانی می کنید.

فایل خود را ذخیره کنید و از ویرایشگر خارج شوید، سپس فایل extractArchive.js را با دستور زیر اجرا کنید:

node extractArchive.js

خروجی زیر را دریافت می کنید:

 

Output

Extracted to "test_extracted" successfully

تأیید کنید که دایرکتوری حاوی محتویات ZIP ایجاد شده است:

ls

خروجی زیر را دریافت خواهید کرد:

 

Output

createArchive.js   file4.txt   package-lock.json
readArchive.js  test.zip        updateArchive.js
extractArchive.js  node_modules  package.json
test           test_extracted

اکنون به دایرکتوری حاوی محتویات استخراج شده بروید:

cd test_extracted

فهرست مطالب در دایرکتوری:

ls

خروجی زیر را دریافت خواهید کرد:

 

Output

file1.txt  file2.txt  file3.txt  file4.txt  underwater.png

اکنون می توانید ببینید که دایرکتوری دارای تمام فایل هایی است که در دایرکتوری اصلی بودند.

شما اکنون محتوای آرشیو ZIP را در یک فهرست استخراج کرده اید.

نتیجه

در این آموزش، شما یک بایگانی ZIP ایجاد کردید، محتویات آن را فهرست کردید، یک فایل جدید به آرشیو اضافه کردید و تمام محتوای آن را با استفاده از ماژول adm-zip در یک فهرست استخراج کردید. این به عنوان پایه خوبی برای کار با آرشیوهای ZIP در Node.js خواهد بود.

 

https://vpsgol.net/product/vps-germany/

 

https://vpsgol.net/product/vps-usa/

 

https://vpsgol.net/product/vps-france/

 

https://vpsgol.net/product/vps-canada/

 

https://vpsgol.net/product/vps-poland/

 

https://vpsgol.net/product/vps-netherlands/

 

https://vpsgol.net/product/vps-england/

 

برچسب‌ها:Node.jsپلتفرم جاواجاواخرید سرور مجازیسرورسرور مجازیفشرده سازی در Node.jsنصب Node.js

  • behnam gol mohamadi
  • ۰
  • ۰

نحوه استفاده از دکوراتورها در TypeScript

ورود به سایت

معرفی

TypeScript افزونه ای از زبان جاوا اسکریپت است که از زمان اجرای جاوا اسکریپت با یک نوع تایپ کننده زمان کامپایل استفاده می کند. این ترکیب به توسعه دهندگان اجازه می دهد تا از اکوسیستم جاوا اسکریپت و ویژگی های زبان کامل استفاده کنند ، در حالی که علاوه بر این ، نوع تایپ استاتیک ، شمارش ها ، کلاس ها و رابط ها را در بالای آن اضافه می کنند. یکی از این ویژگی های اضافی پشتیبانی از دکوراتور است.

دکوراتورها راهی برای decorator اعضای یک کلاس یا خود کلاس با قابلیت های اضافی هستند. هنگامی که از یک decorator کننده برای یک کلاس یا عضو کلاس استفاده می کنید ، در واقع یک تابع را فراخوانی می کنید که جزئیات decorator شده را دریافت می کند ، و اجرای decorator قادر خواهد بود کد را به صورت پویا تغییر داده و قابلیت های اضافی را اضافه کند. کاهش کد بویلر آنها راهی برای متا برنامه نویسی در TypeScript هستند ، که یک تکنیک برنامه نویسی است که برنامه نویس را قادر می سازد تا کدی ایجاد کند که از کدهای دیگر خود برنامه به عنوان داده استفاده کند.

این آموزش به شما نشان می دهد که چگونه decorator خود را در TypeScript برای کلاس ها و اعضای کلاس ایجاد کنید ، و همچنین نحوه استفاده از آنها. شما را از طریق نمونه کد های مختلف راهنمایی می کند ، که می توانید آنها را در محیط TypeScript خود یا TypeScript Playground دنبال کنید ، یک محیط آنلاین که به شما امکان می دهد TypeScript را مستقیماً در مرورگر بنویسید.

پیش نیازها

برای پیگیری این آموزش ، شما نیاز دارید:

  • محیطی که در آن می توانید برنامه های TypeScript را اجرا کنید تا همراه با مثال ها دنبال کنید. برای تنظیم این دستگاه در دستگاه محلی خود ، به موارد زیر نیاز دارید.
    • هر دو Node و npm (یا نخ) به منظور اجرای یک محیط توسعه که بسته های مربوط به TypeScript را مدیریت می کند ، نصب شده است. این آموزش با Node.js نسخه 14.3.0 و npm نسخه 6.14.5 آزمایش شده است. برای نصب روی macOS یا اوبونتو 18.04 ، مراحل نحوه نصب Node.js و ایجاد محیط توسعه محلی در macOS یا بخش Installing using a PPA از نحوه نصب Node.js در اوبونتو 18.04 را دنبال کنید. اگر از زیر سیستم Windows برای Linux (WSL) استفاده می کنید ، این نیز کار می کند.
    • علاوه بر این ، به TypeScript Compiler (tsc) نصب شده بر روی دستگاه خود نیاز دارید. برای انجام این کار ، به وب سایت رسمی TypeScript مراجعه کنید.
  • اگر نمی خواهید محیط TypeScript را در دستگاه محلی خود ایجاد کنید ، می توانید از زمین بازی رسمی TypeScript برای پیگیری استفاده کنید.
  • شما به دانش کافی در مورد جاوا اسکریپت ، به ویژه نحو ES6+ ، مانند تخریب ، عملگرهای استراحت و واردات/صادرات نیاز دارید. اگر به اطلاعات بیشتری در مورد این موضوعات نیاز دارید ، خواندن نحوه کدگذاری در سری JavaScript توصیه می شود
  • این آموزش به جنبه های ویرایشگرهای متنی که از TypeScript پشتیبانی می کنند اشاره می کند و خطاهای خطی را نشان می دهد. این برای استفاده از TypeScript ضروری نیست ، اما از ویژگیهای TypeScript بیشتر استفاده می کند. برای به دست آوردن مزایای این موارد ، می توانید از ویرایشگر متنی مانند Visual Studio Code استفاده کنید که از TypeScript پشتیبانی کامل دارد. همچنین می توانید این مزایا را در TypeScript Playground امتحان کنید

همه نمونه های نشان داده شده در این آموزش با استفاده از TypeScript نسخه 4.2.2 ایجاد شده است.

فعال کردن پشتیبانی Decorators در TypeScript

در حال حاضر ، decorator هنوز یک ویژگی آزمایشی در TypeScript هستند و بنابراین ، ابتدا باید آن را فعال کنید. در این بخش ، بسته به نحوه کار با TypeScript ، نحوه فعال کردن decorator در TypeScript را خواهید دید.

CLI کامپایلر TypeScript

برای فعال کردن پشتیبانی دکوراتورها در هنگام استفاده از TypeScript Compiler CLI (tsc) تنها مرحله اضافی مورد نیاز این است که یک پرچم اضافی –experimentalDecorators:

tsc –experimentalDecorators

tsconfig.json

هنگام کار در پروژه ای که دارای فایل tsconfig.json است ، برای فعال کردن decorator تجربی ، باید ویژگی experimentalDecorators را به شیء compilerOptions اضافه کنید:

{
“compilerOptions”: {
“experimentalDecorators”: true
}
}

در زمین بازی TypeScript ، دکوراتورها به طور پیش فرض فعال هستند.

با استفاده از نحو دکوراتور

در این بخش ، شما در کلاسهای TypeScript از decorator استفاده خواهید کرد.

در TypeScript ، می توانید با استفاده از نحو خاصexpression ، decorator کننده ایجاد کنید ، جایی که بیان یک تابع است که به طور خودکار در زمان اجرا با جزئیات مربوط به هدفdecorator فراخوانی می شود.

هدف یک دکوراتور بستگی به جایی دارد که آنها را اضافه می کنید. در حال حاضر ، decorator را می توان به اجزای زیر کلاس اضافه کرد:

  • خود اعلام کلاس
  • خواص
  • دستیاران
  • مواد و روش ها
  • مولفه های

برای مثال ، فرض کنید شما یک decorator به نام مهر و موم شده دارید که Object.seal را در یک کلاس فراخوانی می کند. برای استفاده از دکوراتور خود می توانید موارد زیر را بنویسید:

@sealed
class Person {}

در کد برجسته شده توجه کنید که decorator را درست قبل از هدف decorator مهر و موم شده خود اضافه کرده اید ، در این مورد ، اعلان کلاس Person.

همین امر برای انواع دیگر decorators معتبر است:

@classDecorator
class Person {
  @propertyDecorator
  public name: string;

  @accessorDecorator
  get fullName() {
    // ...
  }

  @methodDecorator
  printName(@parameterDecorator prefix: string) {
    // ...
  }
}

 

برای افزودن چند decorator ، آنها را یکی پس از دیگری به هم اضافه کنید:

@decoratorA
@decoratorB
class Person {}

ایجاد دکوراتورهای کلاس در TypeScript

در این بخش مراحل ایجاد decorator کلاس در TypeScript را طی خواهید کرد.

برای decorator به نامdecoratorA ، به TypeScript می گویید که باید تابع decoratorA را فراخوانی کند. یک تابع با جزئیات نحوه استفاده از decorator در کد خود فراخوانی می شود. به عنوان مثال ، اگر decorator را برای اعلان کلاس اعمال کرده اید ، تابع جزئیات مربوط به کلاس را دریافت می کند. این عملکرد باید در حدی باشد که دکوراتور شما بتواند کار کند.

برای ایجاد دکوراتور خود ، باید تابعی با همان نام decorator خود ایجاد کنید. به این معنی که برای ایجاد decorator کلاس مهر و موم شده که در قسمت قبل مشاهده کردید ، باید یک تابع مهر و موم شده ایجاد کنید که مجموعه خاصی از پارامترها را دریافت می کند. بیایید دقیقاً همین کار را انجام دهیم:

@sealed
class Person {}

function sealed(target: Function) {
  Object.seal(target);
  Object.seal(target.prototype);
}

پارامتر (های) منتقل شده به دکوراتور بستگی به محل استفاده از دکوراتور دارد. پارامتر اول معمولاً هدف نامیده می شود.

دکوراتور مهر و موم شده فقط در اعلان های کلاس استفاده می شود ، بنابراین عملکرد شما یک پارامتر واحد ، هدف ، که از نوع Function است دریافت می کند. این سازنده کلاسی خواهد بود که decorator کننده روی آن اعمال شده است.

در تابع مهر و موم شده ، سپس Object.seal را روی هدف ، که سازنده کلاس است ، و همچنین در نمونه اولیه آنها فراخوانی می کنید. هنگامی که این کار را انجام می دهید ، هیچ ویژگی جدیدی نمی تواند به سازنده کلاس یا ویژگی آنها اضافه شود ، و خصوصیات موجود به عنوان غیر قابل تنظیم علامت گذاری می شوند.

مهم است که به یاد داشته باشید که در حال حاضر نمی توان نوع TypeScript هدف را هنگام استفاده از decorator گسترش داد. این بدان معناست که ، به عنوان مثال ، شما نمی توانید یک فیلد جدید با استفاده از یک تزئین کننده به کلاس اضافه کنید و آن را از نظر نوع ایمن کنید.

اگر درdecorator کلاس مهر و موم شده مقداری را برگردانید ، این مقدار به تابع سازنده جدید کلاس تبدیل می شود. این می تواند مفید باشد اگر می خواهید سازنده کلاس را به طور کامل بازنویسی کنید.

شما اولین decorator کننده خود را ایجاد کرده اید و از آن در کلاس استفاده کرده اید. در قسمت بعدی نحوه ایجاد کارخانه های decorator را خواهید آموخت.

ایجاد Decorator Factories

گاهی اوقات هنگام استفاده از دکوراتور باید گزینه های اضافی را به دکوراتور منتقل کنید و برای این کار باید از کارخانه های decorator استفاده کنید. در این بخش نحوه ایجاد آن کارخانه ها و استفاده از آنها را خواهید آموخت.

کارخانه های decorator توابعی هستند که عملکرد دیگری را برمی گردانند. آنها این نام را دریافت می کنند زیرا خود اجرای decorator نیستند. در عوض ، آنها عملکرد دیگری را که مسئول اجرای decorator است ، باز می گردانند و به عنوان یک تابع بسته بندی عمل می کنند. آنها در سفارشی سازی دکوراتورها مفید هستند ، زیرا اجازه می دهند کد مشتری هنگام استفاده از گزینه ها به decorator منتقل کند.

بیایید تصور کنیم که شما یک دکوراتور کلاس به نام decoratorA دارید و می خواهید گزینه ای را اضافه کنید که می توانید هنگام فراخوانی با دکوراتور مانند پرچم بولی تنظیم کنید. شما می توانید با نوشتن یک کارخانه تزئیناتی مشابه کارخانه زیر به این مهم برسید:

const decoratorA = (someBooleanFlag: boolean) => {
  return (target: Function) => {
  }
}

در اینجا ، تابع decoratorA با پیاده سازی تزئین کننده ، عملکرد دیگری را برمی گرداند. توجه کنید که چگونه کارخانه تزئینات پرچم بولی را به عنوان تنها پارامتر خود دریافت می کند:

const decoratorA = (someBooleanFlag: boolean) => {
  return (target: Function) => {
  }
}

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

const decoratorA = (someBooleanFlag: boolean) => {
  return (target: Function) => {
  }
}

@decoratorA(true)
class Person {}

در اینجا ، وقتی از decoratorA decorator استفاده می کنید ، قرار است کارخانه تزئینات با پارامتر someBooleanFlag روی true تنظیم شود. سپس اجرای تزئین کننده خود اجرا می شود. این به شما امکان می دهد رفتار دکوراتور خود را بر اساس نحوه استفاده از آن تغییر دهید ، و سفارشی سازی و استفاده مجدد از دکوراتورها را از طریق برنامه خود آسان کنید.

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

const decoratorA = (someBooleanFlag: boolean) => {
  return (target: Function) => {
  }
}

@decoratorA
class Person {}

کامپایلر TypeScript دو خطا به شما می دهد که ممکن است بسته به نوع تزئین کننده متفاوت باشد. برای تزئین کنندگان کلاس خطاها 1238 و 1240 است:

 

Output

Unable to resolve signature of class decorator when called as an expression.
  Type '(target: Function) => void' is not assignable to type 'typeof Person'.
    Type '(target: Function) => void' provides no match for the signature 'new (): Person'. (1238)
Argument of type 'typeof Person' is not assignable to parameter of type 'boolean'. (2345)

شما فقط یک کارخانه تزئینات ایجاد کرده اید که قادر به دریافت پارامترها و تغییر رفتار آنها بر اساس این پارامترها است. در مرحله بعد با نحوه ایجاد تزئین کننده املاک آشنا می شوید.

ایجاد Property Decorators

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

هر دکوراتور املاک پارامترهای زیر را دریافت می کند:

  • برای خواص استاتیک ، تابع سازنده کلاس. برای سایر ویژگی ها ، نمونه اولیه کلاس.
  • نام عضو.

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

در اینجا یک عملکرد تزئین کننده وجود دارد که نام اعضا را روی کنسول چاپ می کند:

const printMemberName = (target: any, memberName: string) => {
console.log(memberName);
};

class Person {
@printMemberName
name: string = “Jon”;
}

وقتی کد TypeScript بالا را اجرا می کنید ، موارد زیر را در کنسول چاپ می کنید:

 

Output

name

می توانید از طراحان املاک برای نادیده گرفتن ملک مورد تزئین استفاده کنید. این را می توان با استفاده از Object.defineProperty همراه با یک setter و getter جدید برای ویژگی انجام داد. بیایید ببینیم چگونه می توانید یک تزئین کننده با نام فهرست مجاز ایجاد کنید ، که فقط اجازه می دهد یک ویژگی بر روی مقادیر موجود در یک لیست مجاز استاتیک تنظیم شود:

const allowlist = ["Jon", "Jane"];

const allowlistOnly = (target: any, memberName: string) => {
  let currentValue: any = target[memberName];

  Object.defineProperty(target, memberName, {
    set: (newValue: any) => {
      if (!allowlist.includes(newValue)) {
        return;
      }
      currentValue = newValue;
    },
    get: () => currentValue
  });
};

ابتدا ، شما یک لیست مجاز استاتیک در بالای کد ایجاد می کنید:

const allowlist = [“Jon”, “Jane”];

سپس در حال پیاده سازی تزئین کننده اموال هستید:

const allowlistOnly = (target: any, memberName: string) => {
  let currentValue: any = target[memberName];

  Object.defineProperty(target, memberName, {
    set: (newValue: any) => {
      if (!allowlist.includes(newValue)) {
        return;
      }
      currentValue = newValue;
    },
    get: () => currentValue
  });
};

توجه داشته باشید که چگونه از هر کدام به عنوان نوع هدف استفاده می کنید:

const allowlistOnly = (target: any, memberName: string) => {

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

در خط اول پیاده سازی تزئین کننده خود ، مقدار فعلی دارایی را که تزئین می کنید در متغیر currentValue ذخیره می کنید:

let currentValue: any = target[memberName];

برای خواص استاتیک ، در صورت وجود ، مقدار پیش فرض آنها تنظیم می شود. برای خواص غیر استاتیک ، این همیشه تعریف نشده است. این به این دلیل است که در زمان اجرا ، در کد جاوا اسکریپت کامپایل شده ، تزئین کننده قبل از اینکه ویژگی نمونه روی مقدار پیش فرض تنظیم شود ، اجرا می شود.

سپس با استفاده از Object.defineProperty ویژگی را لغو می کنید:

Object.defineProperty(target, memberName, {
  set: (newValue: any) => {
    if (!allowlist.includes(newValue)) {
      return;
    }
    currentValue = newValue;
  },
  get: () => currentValue
});

فراخوانی Object.defineProperty دارای getter و setter است. دریافت کننده مقدار ذخیره شده در متغیر currentValue را برمی گرداند. اگر تنظیم کننده مقدار currentVariable را روی newValue تنظیم کند ، اگر در لیست مجاز باشد.

بیایید از دکوراتوری که شما نوشتید استفاده کنیم. کلاس Person زیر را ایجاد کنید:

class Person {
  @allowlistOnly
  name: string = "Jon";
}

اکنون یک نمونه جدید از کلاس خود ایجاد می کنید ، و تنظیمات آزمایشی و بدست آوردن ویژگی نمونه نام:

const allowlist = ["Jon", "Jane"];

const allowlistOnly = (target: any, memberName: string) => {
  let currentValue: any = target[memberName];

  Object.defineProperty(target, memberName, {
    set: (newValue: any) => {
      if (!allowlist.includes(newValue)) {
        return;
      }
      currentValue = newValue;
    },
    get: () => currentValue
  });
};

class Person {
  @allowlistOnly
  name: string = "Jon";
}

const person = new Person();
console.log(person.name);

person.name = "Peter";
console.log(person.name);

person.name = "Jane";
console.log(person.name);

 

هنگام اجرای کد باید خروجی زیر را مشاهده کنید:

 

Output

Jon
Jon
Jane

مقدار هرگز روی Peter تنظیم نمی شود ، زیرا Peter در لیست مجاز نیست.

اگر می خواهید کد خود را کمی بیشتر استفاده کنید و اجازه دهید هنگام استفاده از دکوراتور ، لیست مجاز تنظیم شود ، چه می کنید؟ این یک مورد استفاده عالی برای decorator factories. است. بیایید دقیقاً همین کار را انجام دهیم ، با تبدیل دکوراسیون مجاز خود فقط دکوراتور به decorator factories.:

const allowlistOnly = (allowlist: string[]) => {
  return (target: any, memberName: string) => {
    let currentValue: any = target[memberName];

    Object.defineProperty(target, memberName, {
      set: (newValue: any) => {
        if (!allowlist.includes(newValue)) {
          return;
        }
        currentValue = newValue;
      },
      get: () => currentValue
    });
  };
}

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

اکنون برای استفاده از دکوراتور خود ، باید از فهرست مجاز مانند کد برجسته زیر عبور کنید:

class Person {
  @allowlistOnly(["Claire", "Oliver"])
  name: string = "Claire";
}

سعی کنید کدی شبیه کد قبلی که نوشتید اجرا کنید ، اما با تغییرات جدید:

const allowlistOnly = (allowlist: string[]) => {
  return (target: any, memberName: string) => {
    let currentValue: any = target[memberName];

    Object.defineProperty(target, memberName, {
      set: (newValue: any) => {
        if (!allowlist.includes(newValue)) {
          return;
        }
        currentValue = newValue;
      },
      get: () => currentValue
    });
  };
}

class Person {
  @allowlistOnly(["Claire", "Oliver"])
  name: string = "Claire";
}

const person = new Person();
console.log(person.name);
person.name = "Peter";
console.log(person.name);
person.name = "Oliver";
console.log(person.name);

کد باید خروجی زیر را به شما بدهد:

 

Output

Claire
Claire
Oliver

نشان می دهد که آنطور که انتظار می رود کار می کند ، person.name هرگز روی Peter تنظیم نمی شود ، زیرا Peter در فهرست مجاز داده شده نیست.

اکنون که اولین تزئین کننده دارایی خود را با استفاده از عملکرد معمول تزئین کننده و کارخانه تزئینات ایجاد کرده اید ، وقت آن است که به نحوه ایجاد تزئینات برای دستیاران کلاس نگاهی بیندازید.

ایجاد Accessor Decorators

در این قسمت نگاهی به چگونگی تزئین اعضای کلاس می اندازید.

درست مانند دکوراتورهای املاک ، دکوراتورهای مورد استفاده در یک accessor پارامترهای زیر را دریافت می کنند:

برای خواص استاتیک ، تابع سازنده کلاس ، برای همه خواص دیگر ، نمونه اولیه کلاس.
نام عضو.
اما متفاوت از دکوراتور ویژگی ، پارامتر سوم را نیز با ویژگی توصیف کننده ویژگی عضو دسترسی دریافت می کند.

با توجه به این واقعیت که ویژگی های توصیف کننده شامل هر دو تنظیم کننده و گیرنده برای یک عضو خاص است ، تزئین کننده های جانبی فقط می توانند برای تنظیم کننده یا گیرنده یک عضو واحد اعمال شوند ، نه برای هر دو.

اگر یک مقدار از تزئین کننده accessor خود را برگردانید ، این مقدار به عنوان توصیف کننده ویژگی جدید accessor برای هر دو گروه getter و setter تبدیل می شود.

در اینجا نمونه ای از یک تزئین کننده است که می تواند برای تغییر پرچم قابل شمارش دسترسی گیرنده/تنظیم کننده استفاده شود:

const enumerable = (value: boolean) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    propertyDescriptor.enumerable = value;
  }
}

در مثال به نحوه استفاده از کارخانه تزئینات توجه کنید. این به شما این امکان را می دهد که هنگام تماس با دکوراتور پرچم شمارش شده را مشخص کنید. در اینجا نحوه استفاده از دکوراتور خود آورده شده است:

class Person {
  firstName: string = "Jon"
  lastName: string = "Doe"

  @enumerable(true)
  get fullName () {
    return `${this.firstName} ${this.lastName}`;
  }
}

دکوراتورهای اکسسوری شبیه به دکوراتورهای املاک هستند. تنها تفاوت این است که آنها پارامتر سوم را با توصیف کننده ویژگی دریافت می کنند. اکنون که اولین تزئین کننده اکسسوری خود را ایجاد کرده اید ، بخش بعدی نحوه ایجاد تزئین کننده متد را به شما نشان می دهد.

ایجاد Method Decorators

در این قسمت نگاهی به نحوه استفاده از تزئین کننده های متد می اندازید.

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

بیایید از همان دکوراتور قابل شمارش که قبلاً ایجاد کرده اید دوباره استفاده کنیم ، اما این بار در متد getFullName کلاس Person زیر:

const enumerable = (value: boolean) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    propertyDescriptor.enumerable = value;
  }
}

class Person {
  firstName: string = "Jon"
  lastName: string = "Doe"

  @enumerable(true)
  getFullName () {
    return `${this.firstName} ${this.lastName}`;
  }
}

اگر مقداری را از تزئین کننده متد خود برگردانده اید ، این مقدار به توصیف کننده جدید ویژگی متد تبدیل می شود.

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

const deprecated = (deprecationReason: string) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    return {
      get() {
        const wrapperFn = (...args: any[]) => {
          console.warn(`Method ${memberName} is deprecated with reason: ${deprecationReason}`);
          propertyDescriptor.value.apply(this, args)
        }

        Object.defineProperty(this, memberName, {
            value: wrapperFn,
            configurable: true,
            writable: true
        });
        return wrapperFn;
      }
    }
  }
}

 

در اینجا ، شما در حال ایجاد یک تزئین کننده با استفاده از یک کارخانه تزئینات هستید. این کارخانه تزئینی یک آرگومان واحد از نوع string دریافت می کند ، که دلیل عدم استفاده از آن است ، همانطور که در قسمت برجسته زیر نشان داده شده است:

const deprecated = (deprecationReason: string) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    // ...
  }
}

deprecationReason بعداً هنگام ورود پیام منسوخ شدن به کنسول استفاده می شود. در اجرای تزئین منسوخ شده خود ، یک مقدار را برمی گردانید. هنگامی که مقداری را از یک تزئین کننده متد برمی گردانید ، این مقدار روی توصیف کننده ویژگی این عضو بازنویسی می کند.

شما از این مزیت استفاده می کنید تا متد را به روش کلاس تزئین شده خود اضافه کنید. به این ترتیب می توانید پیاده سازی خود روش را تغییر دهید.

اما چرا فقط از Object.defineProperty به جای بازگشت یک تزئین کننده جدید برای روش استفاده نکنید؟ این امر ضروری است زیرا باید به مقدار این دسترسی داشته باشید ، که برای روشهای کلاس غیر استاتیک ، به نمونه کلاس متصل است. اگر مستقیماً از Object.defineProperty استفاده می کنید ، هیچ راهی برای بازیابی مقدار آن برای شما وجود نخواهد داشت ، و اگر متدی که به هر نحوی از این روش استفاده می کرد ، تزئین کننده هنگام اجرای روش پیچیده شده از داخل اجرای تزئین کننده ، کد شما را می شکند.

در مورد شما ، خود گیرنده این مقدار را برای نمونه های غیراستاتیک به نمونه کلاس و برای متدهای استاتیک به سازنده کلاس محدود می کند.

در داخل getter شما سپس یک تابع wrapper به صورت محلی ایجاد می کنید ، به نام wrapperFn ، این تابع با استفاده از console یک پیام را به کنسول وارد می کند. هشدار دهید ، با رد deprecationReason دریافت شده از کارخانه decorator ، سپس با استفاده از propertyDescriptor.value روش اصلی را فراخوانی می کنید. apply (this، args) ، به این ترتیب متد اصلی نامیده می شود که این مقدار به درستی به نمونه کلاس متصل شده است در صورتی که یک روش غیر استاتیک بود.

سپس از definProperty برای بازنویسی مقدار متد خود در کلاس استفاده می کنید. این مانند یک مکانیسم یادآوری عمل می کند ، زیرا تماس های متعدد با یک روش دیگر با گیرنده شما تماس نمی گیرد ، بلکه مستقیماً با wrapperFn تماس می گیرد. اکنون شما با استفاده از Object.defineProperty ، عضو کلاس را طوری تنظیم می کنید که wrapperFn شما به عنوان مقدار آن باشد.

بیایید از دکوراتور منسوخ شده شما استفاده کنیم:

const deprecated = (deprecationReason: string) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    return {
      get() {
        const wrapperFn = (...args: any[]) => {
          console.warn(`Method ${memberName} is deprecated with reason: ${deprecationReason}`);
          propertyDescriptor.value.apply(this, args)
        }

        Object.defineProperty(this, memberName, {
            value: wrapperFn,
            configurable: true,
            writable: true
        });
        return wrapperFn;
      }
    }
  }
}

class TestClass {
  static staticMember = true;

  instanceMember: string = "hello"

  @deprecated("Use another static method")
  static deprecatedMethodStatic() {
    console.log('inside deprecated static method - staticMember =', this.staticMember);
  }

  @deprecated("Use another instance method")
  deprecatedMethod () {
    console.log('inside deprecated instance method - instanceMember =', this.instanceMember);
  }
}

TestClass.deprecatedMethodStatic();

const instance = new TestClass();
instance.deprecatedMethod();

در اینجا ، شما یک TestClass با دو ویژگی ایجاد کرده اید: یکی استاتیک و دیگری غیر استاتیک. شما همچنین دو روش ایجاد کرده اید: یکی استاتیک و دیگری غیر استاتیک.

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

 

Output

(warning) Method deprecatedMethodStatic is deprecated with reason: Use another static method
inside deprecated static method - staticMember = true
(warning)) Method deprecatedMethod is deprecated with reason: Use another instance method
inside deprecated instance method - instanceMember = hello

این نشان می دهد که هر دو روش به درستی با عملکرد wrapper شما بسته شده است ، که پیامی را با دلیل منسوخ شدن به کنسول وارد می کند.

اکنون شما اولین تزئین کننده متد خود را با استفاده از TypeScript ایجاد کرده اید. بخش بعدی نحوه ایجاد آخرین نوع تزئین کننده پشتیبانی شده توسط TypeScript ، یک تزئین کننده پارامتر را به شما نشان می دهد.

ایجاد پارامترهای Decorators

تزئینات پارامتری را می توان در پارامترهای روش کلاس استفاده کرد. در این بخش نحوه ایجاد آن را خواهید آموخت.

عملکرد تزئین کننده مورد استفاده با پارامترها پارامترهای زیر را دریافت می کند:

برای خواص استاتیک ، تابع سازنده کلاس. برای سایر خصوصیات ، نمونه اولیه کلاس.
نام عضو.
فهرست پارامترها در لیست پارامترهای متد.
تغییر هر چیزی که مربوط به خود پارامتر باشد امکان پذیر نیست ، بنابراین چنین تزئیناتی فقط برای مشاهده استفاده از خود پارامترها مفید هستند (مگر اینکه از موارد پیشرفته تری مانند بازتاب داده های بازتاب استفاده کنید).

در اینجا یک نمونه از تزئین کننده است که شاخص پارامتر تزئین شده را به همراه نام روش چاپ می کند:

function print(target: Object, propertyKey: string, parameterIndex: number) {
  console.log(`Decorating param ${parameterIndex} from ${propertyKey}`);
}

سپس می توانید پارامتر decorators خود را مانند این استفاده کنید:

class TestClass {
  testMethod(param0: any, @print param1: any) {}
}

اجرای کد بالا باید موارد زیر را در کنسول نمایش دهد:

 

Output

Decorating param 1 from testMethod

شما اکنون یک  پارامتر decorator ایجاد کرده و اجرا کرده اید و نتیجه ای را که شاخص پارامتر decorator را برمی گرداند چاپ می کنید.

نتیجه
در این آموزش ، شما تمام decorators پشتیبانی شده توسط TypeScript را پیاده سازی کرده اید ، از آنها در کلاس ها استفاده کرده اید و تفاوت های هر یک را یاد گرفته اید. اکنون می توانید برای کاهش کد صفحه بویلر در پایگاه کد خود ، نوشتن دکوراتورهای خود را شروع کنید ، یا با اطمینان بیشتر از دکوراتورهای کتابخانه هایی مانند Mobx استفاده کنید.

decoratorMethod DecoratorsProperty DecoratorsTypeScriptپارامترهای Decoratorsجاواجاوا اسکریپتسرورسرور مجازیکامپایلر TypeScript

  • behnam gol mohamadi