6 مفهوم پیشرفته جاوااسکریپت که باید بدانید

آفلاین
user-avatar
عرفان حشمتی
21 مهر 1400, خواندن در 11 دقیقه

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

این مقاله لزوما همه جزئیات تکنیک‌هایی که توضیح می‌دهم را پوشش نمی‌دهد، اما یک مرور کلی به همراه نمونه کدهای جاوااسکریپت را در اختیارتان می‌گذارد.

1. استفاده از Closureها برای گسترش دامنه یک متغیر

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

به عبارتی Closure اجازه می‌دهد که دامنه متغیر از محدوده توابع فراتر رود. من به شخصه توصیف جرمی کیت در کتاب Bulletproof Ajax را دوست دارم که می‌گوید:

"Closureها را نوعی محدوده منطقه‌ای در نظر بگیرید: گسترده‌تر از لوکال و نه به اندازه گلوبال."

به منظور ایجاد Closure باید توابع را به صورت تو در تو بنویسید. تابع داخلی به همه متغیرهای محدوده تابع والد خود دسترسی دارد. این امر هنگام ایجاد متدها و ویژگی‌ها در بحث شی‌گرایی بسیار مفید است. در اینجا یک مثال ساده وجود دارد که نحوه استفاده از Closure را نشان می‌دهد:

function myObject() {
  this.property1 = "value1";
  this.property2 = "value2";
  var newValue = this.property1;
  this.performMethod = function() {
    myMethodValue = newValue;
    return myMethodValue;  	
  };
  }
  var myObjectInstance = new myObject();  
  alert(myObjectInstance.performMethod());

قسمت‌های کلیدی اسکریپت توابعی تو در تو و ناشناس هستند که هایلایت شده‌اند و در خط آخر توسط متد alert فراخوانی می‌شوند. از آنجا که این متد در واقع یک تابع تو در تو را صدا می‌زند، بنابراین قادر است مقدار متغیر newValue را بخواند، حتی اگر متغیر مد نظر در محدوده تابع یا متد نباشد.

توسعه‌دهندگان از Closureها همیشه استفاده می‌کنند (حتی به طور ناخودآگاه)، زیرا در چنین مواقعی یک تابع ناشناس داخل یک تابع دیگر قرار می‌گیرد و از متغیرهای محدوده تابع والد استفاده می‌کند. قدرت Closure هنگامی که آن متد (تابع داخلی) فراخوانی می‌گردد، آشکار می‌شود و مقادیری که معمولا در دسترس نیستند در محدوده "regional" قرار می‌گیرند و بنابراین می‌توانند به عنوان هر مقدار دیگری استفاده شوند.

برای کسب اطلاعات بیشتر و ارتباط بهتر با آنها می‌توانید به این مقاله مراجعه کنید.

2. استفاده از Object Literalها برای انتقال آرگومان‌های اختیاری

اینجا یک نکته کدنویسی مفید وجود دارد و باید هنگام کار با توابعی که می‌توانند تعداد زیادی آرگومان اختیاری را بپذیرند، در نظر داشته باشید. به جای انتقال تعداد زیادی از آرگومان‌ها به شیوه مرسوم که می‌تواند تابع را بی‌دلیل پیچیده کند، فقط می‌توانید یک آرگومان را عبور دهید و در نهایت مجموعه‌ای از آرگومان‌ها به وجود می‌آید که در یک Object Literal تعریف شده است.

بیایید قبل از هر چیز به چگونگی انجام این کار به شیوه معمول نگاه کنیم، در این صورت می‌توانیم تفاوت‌ها را ببینیم:

function showStatistics(name, team, position, average, homeruns, rbi) {
  document.write("<p><strong>Name:</strong> " + arguments[0] + "<br />");
  document.write("<strong>Team:</strong> " + arguments[1] + "<br />");

  if (typeof arguments[2] === "string") {
    document.write("<strong>Position:</strong> " + position + "<br />"); 
  }
  if (typeof arguments[3] === "number") {
    document.write("<strong>Batting Average:</strong> " + average + "<br />");
  }
  if (typeof arguments[4] === "number") {
    document.write("<strong>Home Runs:</strong> " + homeruns + "<br />");
  }
  if (typeof arguments[5] === "number") {
    document.write("<strong>Runs Batted In:</strong> " + rbi + "</p>"); 
  }
}
showStatistics("Mark Teixeira");
showStatistics("Mark Teixeira", "New York Yankees");
showStatistics("Mark Teixeira", "New York Yankees", "1st Base", .284, 32, 101);

تابع بالا می‌تواند حداکثر 6 آرگومان داشته باشد. 2 آرگومان اول اجباری است، پس در داخل تابع وجود آنها را بررسی نمی‌کنیم. 4 آرگومان آخر اجباری نیست، بنابراین فقط مقادیر آنها را در صورت وجود نمایش می‌دهیم.

همچنین تابع را 3 بار (3 خط آخر) با تعداد آرگومان مختلف در هر مرتبه فراخوانی می‌کنیم. می‌بینید که اگر تعداد آرگومان‌های ارسال شده بیشتر باشد، کد ممکن است کمی نامرتب به نظر برسد و نگهداری یا خواندن آن دشوارتر شود.

حالا بیایید همان کد را با استفاده از Object Literal برای انتقال آرگومان‌ها بررسی کنیم:

function showStatistics(args) {
  document.write("<p><strong>Name:</strong> " + args.name + "<br />");
  document.write("<strong>Team:</strong> " + args.team + "<br />");
  if (typeof args.position === "string") {
    document.write("<strong>Position:</strong> " + args.position + "<br />"); 
  }
  if (typeof args.average === "number") {
    document.write("<strong>Average:</strong> " + args.average + "<br />");
  }
  if (typeof args.homeruns === "number") {
    document.write("<strong>Home Runs:</strong> " + args.homeruns + "<br />");
  }
  if (typeof args.rbi === "number") {
    document.write("<strong>Runs Batted In:</strong> " + args.rbi + "</p>");
  }
}

showStatistics({
  name: "Mark Teixeira"
});

showStatistics({
  name: "Mark Teixeira",
  team: "New York Yankees"
});

showStatistics({
  name: "Mark Teixeira",
  team: "New York Yankees",
  position: "1st Base",
  average: .284,
  homeruns: 32,
  rbi: 101
});

از نظر فنی روش دوم برای انتقال آرگومان‌ها ممکن است نیاز به کمی کد بیشتر داشته باشد، اما با وجود مجموعه‌ای بزرگ از آرگومان‌ها چند مزیت وجود دارد.

اول خود تابع ساده می‌شود، زیرا تنها یک آرگومان را می‌پذیرد که مجموعه‌ای از تمام مقادیر منتقل شده از Object Literal (نام، تیم، موقعیت و ...) است. به علاوه مقادیر واقعی آرگومان به راحتی قابل خواندن و قابل درک هستند و به سادگی بروزرسانی یا اصلاح می‌شوند، زیرا ارتباط مستقیمی بین مقادیر و آرگومان‌ها در این حالت وجود دارد.

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

3. دسترسی به همه عناصر موجود در DOM

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

در هر صورت دسترسی به عناصر ناشناس DOM از طریق جاوااسکریپت غیرممکن نیست. با استفاده از آنچه که "Contextual Targeting" نامیده می‌شود، می‌توانید تقریبا به هر عنصری در DOM دسترسی داشته باشید و آنها را تغییر دهید. تا زمانی که یک نقشه از الگوی کلی دارید که حاوی عنصری است که می‌خواهید هدف قرار دهید، می‌توانید به آن دسترسی پیدا کنید و آن را به همان روشی که با عنصر دارای نام کلاس یا شناسه مورد استفاده قرار می‌گیرد، دستکاری کنید.

بیایید چند کد HTML ایجاد کنیم که به عنوان صفحه نمونه ما عمل می‌کند:

<div id="header">
  <h1>Site Title</h1>
</div>
<div id="sidebar">
  <ul>
    <li><a href="#">Testing</a></li>
    <li><a href="#">Testing</a></li>
    <li><a href="#">Testing</a></li>
    <li><a href="#">Testing</a></li>
    <li><a href="#">Testing</a></li>
    <li><a href="#">Testing</a></li>
  </ul>
</div>
<div id="content">
  <h2>Page Title</h2>
  <p><a href="#">Lorum Ipsum link here</a>. Pellentesque habitant morbi
     tristique senectus et netus et malesuada fames ac turpis egestas.
     Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, 
     ante. Donec eu libero sit amet quam egestas semper.
     Aenean ultricies mi vitae est. Mauris placerat eleifend leo.
     Pellentesque habitant morbi tristique senectus et netus et malesuada
     fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae,
     ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam
     egestas semper. Aenean ultricies mi vitae est. Mauris
     placerat eleifend leo.</p>
  <p><span style="color: red;">Pellentesque habitant morbi</span>
    tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum
    tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec
    eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
    Mauris placerat eleifend leo. Pellentesque habitant morbi tristique senectus
    et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam,
    feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit
    amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat
    eleifend leo.</p>    
</div>
<div id="footer">
   <p>Copyright | <a href="#">contact</a> | <a href="#">policy</a> | 
      <a href="#">privacy</a></p>
</div>
 

با استفاده از کد HTML بالا اگر بخواهیم همه تگ‌های انکر روی صفحه را هدف قرار دهیم، می‌توانیم آنها را جمع آوری کرده و به این شکل دستکاری کنیم:

var myLinkCollection = document.getElementsByTagName("a");
  
for (i=0;i<myLinkCollection.length;i++) {
  // do something with the anchor tags here
}

اگر هم بخواهیم فقط تگ‌های انکر را در فوتر هدف قرار دهیم، آنها را بر اساس کانتکست یا عناصر اطراف آن مانند زیر مورد هدف قرار می‌دهیم:

var myFooterElement = document.getElementById("footer");
var myLinksInFooter = myFooterElement.getElementsByTagName("a");
for (i=0;i<myLinksInFooter.length;i++) {
  // do something with footer anchor tags here
}

خط اول اشاره‌ای به عنصر فوتر دارد. خط دوم همه تگ‌های <a> داخل فوتر را جمع آوری می‌کند. سپس روی آنها یک حلقه می‌زنیم و کاری را که می‌خواهیم با آنها انجام می‌دهیم. بنابراین همه آنها در دسترس قرار می‌گیرند، حتی اگر از طریق نام کلاس گروه بندی نشده باشند.

همانطور که در زیر نشان داده شده است، می‌توانید همین کار را با استفاده از ویژگی‌های گره نیز انجام دهید.

var myLinkCollection = document.getElementsByTagName("a");


for (i=0;i<myLinkCollection.length;i++) {
  if (myLinkCollection[i].parentNode.parentNode.id === "footer") {
    // do something with footer anchor tags here
  }
}

از کدی مشابه می‌توان برای هدف قرار دادن تگ واحد انکر در بخش "content" استفاده کرد.

همچنین می‌توانیم جستجوی تگ انکر خود را محدود به تگ‌هایی کنیم که دارای ویژگی href هستند، تا از یافتن لینک‌های درون صفحه جلوگیری شود. ما این کار را با استفاده از متد getAttribute انجام می‌دهیم:

var myLinkCollection = document.getElementsByTagName("a");

for (i=0;i<myLinkCollection.length;i++) {
  if (myLinkCollection[i].getAttribute("href")) {
    // do something with the anchor tags here
  }
}

در نهایت متوجه خواهید شد که یک تگ <span> با استایل درون خطی وجود دارد. استایل درون خطی می‌تواند از طریق یک سیستم مدیریت محتوا ایجاد شده باشد، بنابراین ممکن است شما توانایی ویرایش مستقیم آن را نداشته باشید. همچنین می‌توانید تمام عناصر <span> را با استایل‌های درون خطی مانند زیر هدف گذاری کنید:

var myLinkCollection = document.getElementsByTagName("span");

for (i=0;i<myLinkCollection.length;i++) {
  if (myLinkCollection[i].getAttribute("style")) {
    // do something with all anchors that have inline styles
  }
}

قابلیت‌های Contextual Targeting بی پایان است و اگر از یک کتابخانه جاوااسکریپت استفاده کنید که دستکاری DOM را ساده کند، امکانات بیشتری نیز در اختیار خواهید داشت.

4. استفاده از فضای نام برای جلوگیری از مغایرت کد

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

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

با ایجاد یک فضای نام منحصر به فرد می‌توانید از همه تداخل‌ها جلوگیری کنید. بیایید از تابع showStatistics استفاده کنیم تا نشان دهیم چگونه می‌توان کد را در فضای نام خود قرار داد:

if (typeof MY == "undefined") {
  MY = new Object();
  MY.CUSTOM = new Object();
}


MY.CUSTOM.namespace = function() {
  function showStatistics(args) {
    document.write("<p><strong>Name:</strong> " + args.name + "<br />");
    document.write("<strong>Team:</strong> " + args.team + "<br />");
    if (typeof args.position === "string") {
      document.write("<strong>Position:</strong> " + args.position + "<br />");
    }
    if (typeof args.average === "number") {
      document.write("<strong>Average:</strong> " + args.average + "<br />");
    }
    if (typeof args.homeruns === "number") {
      document.write("<strong>Home Runs:</strong> " + args.homeruns + "<br />");
    }
    if (typeof args.rbi === "number") {
      document.write("<strong>Runs Batted In:</strong> " + args.rbi + "</p>");
    }
  }


  showStatistics({
    name: "Mark Teixeira",
    team: "New York Yankees",
    position: "1st Base",
    average: .284,
    homeruns: 32,
    rbi: 101
  });
}
MY.CUSTOM.namespace();

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

تابع showStatistics دقیقا مشابه مثال قبلی است که از Object Literal برای عبور مقادیر استفاده می‌کند. اما در این مورد، کل تابع از جمله Object Literal در داخل my.custom.namespace محصور شده است. خط آخر کل تابع را با استفاده از علامت نقطه فراخوانی می‌کند و تابع دقیقا همانند حالت عادی اجرا می‌شود، با این تفاوت که از تداخل با تابع دیگری به نام "showStatistics" محافظت می‌شود.

5. توسعه برنامه‌های هیبریدی

اگر از ترکیب یک کتابخانه جاوااسکریپت و کد خام استفاده کنید، می‌توانید برنامه‌های قدرتمندی ایجاد نمایید. بسیاری از کتابخانه‌های جاوااسکریپت برای پیاده سازی انیمیشن‌های زیبا و سایر افکت‌های سفارشی - گاهی از طریق پلاگین‌ها - استفاده می‌شوند که اغلب نیازی به افزودن آنها به غیر از برخی مقادیر سفارشی ندارند.

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

اختراع مجدد چرخ فایده‌ای ندارد! بنابراین می‌توانید کتابخانه جاوااسکریپت مورد علاقه خود را پیاده سازی کرده و از فراخوانی‌های ساده Ajax، متدهای DOM و موارد دیگر استفاده کنید. در این صورت می‌توانید از مزایای کتابخانه برخوردار شوید، در حالی که همچنان مشغول نوشتن اسکریپت‌های سفارشی مخصوص پروژه خود هستید.

6. رندرینگ HTML

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

var pageContainer = document.getElementById("container");
var pageTitle = "Content Title";
var authorBio = "Mr. Lorum Ipsum";
var pageContent = "Lorum ipsum line text here. Lorum ipsum line text here. 
                   Lorum ipsum line text here. Lorum ipsum line text here.
                   Lorum ipsum line text here. Lorum ipsum line text here. 
                   Lorum ipsum line text here. Lorum ipsum line text here.
                   Lorum ipsum line text here.";
var footerContent = "Copyright 2009";
var HTMLCode = '\n<h1>' + pageTitle + '</h1>\n
               <div id="content">\n
               <p>' + pageContent + '</p>\n
               <div id="author_bio">\n
               <p>' + authorBio +'</p>\n
               </div>\n
               </div>\n
               <div id="footer">
               <p>' + footerContent + '</p>\n
               </div>\n';

pageContainer.innerHTML = HTMLCode;

خطی که باید در بالا به آن توجه شود خطی است که مقدار متغیر HTMLCode را مشخص می‌کند. این فقط در کد منبع یافت می‌شود، زیرا از کاراکتر "new line" استفاده می‌کند. بنابراین HTML کاملا خوبی به نظر می‌رسد. اما اگر این خط کد طولانی‌تر باشد، خواندن و نگهداری آن در فایل js. بسیار مشکل خواهد بود.

در اینجا همان کد بالا وجود دارد، اما از یک روش سازمان یافته‌تر برای نمایش HTML استفاده می‌شود:

var pageContainer = document.getElementById("container");
var pageTitle = "Content Title";
var authorBio = "Mr. Lorum Ipsum";
var pageContent = "Lorum ipsum line text here. Lorum ipsum line text here. 
                   Lorum ipsum line text here. Lorum ipsum line text here.
                   Lorum ipsum line text here. Lorum ipsum line text here. 
                   Lorum ipsum line text here. Lorum ipsum line text here.
                   Lorum ipsum line text here.";
var HTMLCode = 	'\n' +
                '<h1>' + pageTitle + '</h1>\n'
                '<div id="content">\n' +
                  '<p>' + pageContent + '</p>\n' +
                  '<div id="author_bio">\n' +
                    '<p>' + authorBio + '</p>\n' +
                  '</div>\n'
		        '</div>\n' +
                '<div id="footer">' +
                  '<p>' + footerContent + '</p>\n' +
                '</div>\n';
		
pageContainer.innerHTML = HTMLCode;

می‌بینید کد بسیار خواناتر شده و مطابق با رندر HTML در یک صفحه واقعی است. حتی شامل تورفتگی مناسب نیز هست و همچنان از کاراکتر new line برای قالب بندی صحیح HTML خروجی استفاده می‌کند.

جمع‌بندی

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

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

منبع

چه امتیازی به این مقاله می دید؟
خیلی بد
بد
متوسط
خوب
عالی

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

برای ارسال دیدگاه لازم است، ابتدا وارد سایت شوید.

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

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

آفلاین
user-avatar
عرفان حشمتی @heshmati74
مهندس معماری سیستم های کامپیوتری، طراح و توسعه دهنده وب سایت
دنبال کردن

گفتگو‌ برنامه نویسان

بخشی برای حل مشکلات برنامه‌نویسی و مباحث پیرامون آن وارد شو