جاوا اسکریپت یکی از محبوبترین زبانهای شناخته شده در جهان است؛ زبانی مناسب برای طراحی وب، برنامههای ترکیبی گوشیهای هوشمند و حتی سمت سرور که بهبودهای زیادی را در چند سال اخیر تجربه کرده است. علاوه بر این، به دلیل گستردگی جاوا اسکریپت، معمولا نقطه شروعی برای یادگیری برنامهنویسی تحت وب یا کنترل ربات نیز محسوب میشود. برنامهنویسی که بتواند در زبان جاوا اسکریپت به خوبی کدنویسی کرده و برنامههایی سازمانیافته و منظمی را تولید کند، مطمئنا جایگاه ویژهای را در صنعت برنامهنویسی خواهد داشت.
در این مقاله، به معرفی تعدادی از تکنیکهای خاص و نکات کلیدی در زبان جاوا اسکریپت میپردازیم. مهم نیست از کدام کتابخانه جاوا اسکریپت و برای چه هدفی استفاده میکنید، این تکنیکها میتوانند بهبود زیادی را در برنامهنویسی شما به ارمغان آورند. با تکفارسهمراه باشید.
پیشنیاز این آموزش تسلط به مفاهیم و عبارات زبان جاوا اسکریپت است. اما اگر به تازگی این زبان را شروع کردهاید، باز هم میتواند مرجع تمرین خوبی برای شما باشد. اگر تمام این ۴۴ تمرین زیر را یاد بگیرید، مطمئنا میتوانید از پس انجام پروژههای بزرگتری هم برآیید :)
توجه داشته باشید که کدهای موجود در این مقاله در نسخه ۳۰ مرورگر کروم مورد آزمایش قرار گرفتهاند. این نسخه از ورژن ۸ موتور جاوا اسکریپت قدرت گرفته است.
در زمان تعریف یک متغیر حتما نوع آن (var، let یا const) را مشخص کنید.
تخصیص یک متغیر ناشناس باعث تولید متغیر سراسری (Global variable) خواهد شد. تا جایی که امکان است از تعریف این دسته از متغیرها در کدنویسی پرهیز کنید.
همچنین سعی کنید به هیچعنوان از عبارت var برای تعریف متغیرهای خود استفاده نکنید. به چه دلیل؟ برای اطلاعات بیشتر اینجا کلیک کنید
به جای == از === استفاده کنید.
عملگر == یا (!=) تنها یک نوع تبدیل خودکار را در عبارت انجام خواهد داد و سپس به سراغ مقایسه دو عبارت میرود.
اما عملگر === (یا !==) هیچ تبدیلی را شامل نمیشود و از ابتدا، تنها مقدار و نوع دو عبارت را با یکدیگر مقایسه میکند، اگر نوع عبارات با یکدیگر برابر نباشد، عملگرد === بدون اتلاف وقت false را به خروجی میبرد. به همین دلیل نیز عملکرد سریعتری نسبت به == دارد.
[۱۰] === ۱۰ // is false [۱۰] == ۱۰ // is true '۱۰' == ۱۰ // is true '۱۰' === ۱۰ // is false [] == ۰ // is true [] === ۰ // is false '' == false // is true but true == "a" is false '' === false // is false
عبارات undefined، null، ۰، false، NaN، ‘ ‘ (رشته خالی)، همگی در عبارات boolean اشاره به مقدار false دارند.
پس لازم نیست زیاد خود را درگیر این مقادیر کنید و در صورت نیاز از بهترین مقدار در برنامهنویسی خود استفاده کنید.
در پایان عبارات از ; (سیمیکالن) استفاده کنید.
استفاده از سیمیکالن میتواند تکنیک بسیار خوبی در کدنویسی باشد. البته قرار دادن آن در پایان عبارات جاوا اسکریپت الزامی نیست و در بیشتر موارد توسط کمپایلر جاوا اسکریپ افزوده خواهد شد، اما در بعضی مواقع نیز مکان سیمیکالن به طور اشتباه شناسایی شده و باعث بروز خطا و یا باگ نرمافزاری میشود. پس بخاطر نظم هم که شده از سیمیکالن استفاده کنیم.
از تکنیک object constructor بیشتر استفاده کنید.
عمل object constructor همانطور که نام آن پیداست، باعث تولید یک آبجکت میشود. این روش با نظمی که ارائه میدهد میتواند کدنویسی را تا حد زیادی بهبود بخشد.
function Person(firstName, lastName){ this.firstName = firstName; this.lastName = lastName; } var Saad = new Person("Saad", "Mousliki");
در هنگام استفاده از typeof، instanceof و constructor دقت لازم را داشته باشید.
عبارت typeof عملگری برای برگرداندن نوع متغیر در قالب یک رشته است. توجه داشته باشید که استفاده از typeof در null به صورت یک شیء (Object) به خروجی خواهد رفت. علاوه بر این، انواع مختلف اشیاء در جاوا اسکریپ، نظیر آرایه، دیتا و … نیز به حالت شیء برگردانده میشوند.
عبارت constructor یکی از خصوصیات prototype است که میتوان دستور آن را به وسیله کدنویسی لغو کرد.
عبارت instanceof نیز یکی دیگر از عملگردهای جاوا اسکریپت است که آبجکت مورد نظر را با انواع استاندارد آبجکت مقایسه میکند و اگر نوع مورد نظر را یافت، خروجی را به صورت true نشان خواهد داد.
var arr = ["a", "b", "c"]; typeof arr; // return "object" arr instanceof Array // true arr.constructor(); //[]
ساخت یک تابع خودفراخوان
این نوع تابع در جاوا اسکریپ با نام Self-Invoked شناخته میشود. تابع خودفراخوان بعد از تولید، بدون نیاز به فراخوانی شروع به اجرا خواهد شد. شکل کلی این دسته از توابع به صورت زیر است:
(function(){ // some private code that will be executed automatically })(); (function(a,b){ var result = a+b; return result; })(۱۰,۲۰)
دریافت مقادیر تصادفی از میان یک آرایه
var items = [12, 548 , 'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' , 2145 , 119]; var randomItem = items[Math.floor(Math.random() * items.length)];
دریافت یک عدد تصادفی در محدودهای مشخص
این تکنیک میتواند زمانی مفید باشد که قصد تولید دادههای جعلی برای ارزیابی یک برنامه یا ساختار را دارید.
var x = Math.floor(Math.random() * (max - min + 1)) + min;
تولید آرایهای از اعداد صفر تا مقدار مشخص
var numbersArray = [] , max = 100; for( var i=1; numbersArray.push(i++) < max;); // numbers = [1,2,3 ... 100]
تولید کاراکترهایی با حروف الفبای تصادفی
function generateRandomAlphaNum(len) { var rdmString = ""; for( ; rdmString.length < len; rdmString += Math.random().toString(36).substr(2)); return rdmString.substr(0, len); }
برهمزدن آرایهای از اعداد به صورت تصادفی
var numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411]; numbers = numbers.sort(function(){ return Math.random() - 0.5}); /* the array numbers will be equal for example to [120, 5, 228, -215, 400, 458, -85411, 122205] */
حذف فاصله از رشته string
تابع ()trim یکی از مواردی است که در بروزرسانیهای اخیر موتور جاوا اسکریپ اضافه شده است و میتوان از آن برای حذف فاصله یا فضای سفید در یک آبجکت String استفاده نمود. اما در نسخههای قبلی جاوا اسکریپ چنین تابعی وجود ندارد، پس برای استفاده باید آن را به صورت دستی در آبجکت String بسازید:
String.prototype.trim = function(){return this.replace(/^s+|s+$/g, "");};
افزودن یک آرایه به آرایه دیگر
var array1 = [12 , "foo" , {name "Joe"} , -2458]; var array2 = ["Doe" , 555 , 100]; Array.prototype.push.apply(array1, array2); /* array1 will be equal to [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
تبدیل آبجکت arguments به یک آرایه
آبجکت arguments از جهاتی به آرایه شبیه است، به عنوان مثال از ویژگی length برخوردار میباشد و شمارش خواص آن نیز به مانند آرایهها از صفر شروع میشود اما از اعمالی مثل forEach یا map پشتیبانی نمیکند. به همین دلیل در بعضی شرایط نیاز داریم تا arguments را به آرایه تبدیل کنیم.
var argArray = Array.prototype.slice.call(arguments);
تأیید تابع در صورتی که متغیر تنها به صورت عدد باشد
در بعضی از مسائل نیاز است تا مقدار ورودی یک متغیر تنها به صورت عدد باشد. البته راههای بسیاری برای نوشتن این تابع وجود دارد اما تابع زیر بهترین عملکرد را در سریعترین زمان ممکن ارائه خواهد داد:
function isNumber(n){ return !isNaN(parseFloat(n)) && isFinite(n); }
تأیید تابع در صورتی که متغیر به صورت یک آرایه باشد
این روش نیز درست مانند مثال قبل عمل خواهد کرد با این تفاوت که اگر ورودی شامل متغیری از نوع آرایه است، مقدار true را برمیگرداند.
function isArray(obj){ return Object.prototype.toString.call(obj) === '[object Array]' ; }
دریافت کمترین یا بیشترین مقدار در یک آرایه
var numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411]; var maxInNumbers = Math.max.apply(Math, numbers); var minInNumbers = Math.min.apply(Math, numbers);
پاککردن اطلاعات یک آرایه
var myArray = [12 , 222 , 1000 ]; myArray.length = 0; // myArray will be equal to [].
استفاده از split به جای delete برای پاککردن یک آیتم از آرایه
برای پاککردن یک آیتم در آرایه از split به جای عمل delete استفاده کنید.
استفاده از delete باعث جایگزینی undefined در آیتم پاک شده خواهد شد، از این رو میتواند باعث بوجود آمدن باگ نرمافزاری در برنامه شود.
پس به جای کد زیر …
var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ]; items.length; // return 11 delete items[3]; // return true items.length; // return 11 /* items will be equal to [12, 548, "a", undefined × ۱, ۵۴۷۸, "foo", 8852, undefined × ۱, "Doe", 2154, 119] */
از این روش استفاده کنید:
var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ]; items.length; // return 11 items.splice(3,1) ; items.length; // return 10 /* items will be equal to [12, 548, "a", 5478, "foo", 8852, undefined × ۱, "Doe", 2154, 119] */
به خاطر داشته باشید که عمل delete تنها در حذف ویژگیهای یک آبجکت کاربرد دارد.
کوتاهکردن یک آرایه با استفاده از عمل length
این تکنیک نیز درست مانند مثال ۱۹ عمل میکند.
var myArray = [12 , 222 , 1000 , 124 , 98 , 10 ]; myArray.length = 4; // myArray will be equal to [12 , 222 , 1000 , 124].
علاوه بر این، اگر length را مقداری بیشتر از طول آرایه قرار دهید، آیتمهای جدیدی با مقادیر undefined ساخته خواهد شد.
(عکس۲۰)
استفاده منطقی از عبارات AND/OR
عبارات زیر عملکردی درست مانند عبارت ;()if (foo == 10) doSomething خواهند داشت:
var foo = 10; foo == 10 && doSomething(); // is the same thing as if (foo == 10) doSomething(); foo == 5 || doSomething(); // is the same thing as if (foo != 5) doSomething();
همچنین میتواند از عبارت OR برای تنظیم مقدار پیشفرض در یک متغیر استفاده کرد:
function doSomething(arg1){ arg1 = arg1 || 10; // arg1 will have 10 as a default value if it’s not already set }
استفاده از تابع ()map برای ایجاد حلقه میان آیتمهای یک آرایه
var squares = [1,2,3,4].map(function (val) { return val * val; }); // squares will be equal to [1, 4, 9, 16]
گردکردن یک عدد
var num =2.443242342; num = num.toFixed(4); // num will be equal to 2.4432
توجه داشته باشید که تابع toFixed مقدار string را به خروجی میبرد، نه یک عدد.
نکات مربوط به مسائل Float
در زبان جاوا اسکریپ مقدار ۰٫۳ === ۰٫۱ + ۰٫۲ برابر با false است.
این تناقض از نسخه ۶۴ بیت باینری در استاندارد IEEE 754 نشأت میگیرد که باعث شده تا تمام اعداد جاوا اسکریپ به صورت floating point ذخیره شوند.
برای حل این مشکل میتوانید از توابع ()toFixed و یا ()toPrecision استفاده کنید.
بررسی خواص یک آبجکت هنگام استفاده از حلقه for-in
قطعه کد زیر از تکرار حلقه درون خواص prototypeآبجکت جلوگیری میکند:
for (var name in object) { if (object.hasOwnProperty(name)) { // do something with name } }
استفاده از کاما در تعریف یک متغیر
var a = 0; var b = ( a++, 99 ); console.log(a); // a will be equal to 1 console.log(b); // b is equal to 99
ذخیره متغیرها برای اعمال محاسبات
در این قطعه کد، با استفاده از انتخابگر jQuery، عناصر DOM را ذخیره میکنیم:
var navright = document.querySelector('#right'); var navleft = document.querySelector('#left'); var navup = document.querySelector('#up'); var navdown = document.querySelector('#down');
تأیید متغیر قبل از ارسال به تابع isFinite
isFinite(0/0) ; // false isFinite("foo"); // false isFinite("10"); // true isFinite(10); // true isFinite(undefined); // false isFinite(); // false isFinite(null); // true !!!
از استفاده مقادیر منفی در index آرایهها پرهیز کنید
توجه داشته باشید که مقادیر ارسال شده به تابع splice منفی نباشند. در غیر این صورت باعث بروز باگ نرمافزاری میشود.
var numbersArray = [1,2,3,4,5]; var from = numbersArray.indexOf("foo") ; // from is equal to -1 numbersArray.splice(from,2); // will return [5]
تکنیک سریالسازی (در هنگام استفاده از فایل JSON)
به عبارات مقایسه شده زیر توجه کنید:
var person = {name :'Saad', age : 26, department : {ID : 15, name : "R&D"} }; var stringFromPerson = JSON.stringify(person); /* stringFromPerson is equal to "{"name":"Saad","age":26,"department":{"ID":15,"name":"R&D"}}" */ var personFromString = JSON.parse(stringFromPerson); /* personFromString is equal to person object */
از استفاده eval و عمل Function constructor پرهیز کنید.
استفاده از این دو عبارت در کدنویسی بسیار هزینهبر است زیرا موتور اسکریپ باید در هر بار، کد منبع را به کد اجرایی تبدیل کند.
var func1 = new Function(functionCode); var func2 = eval(functionCode);
از تابع with در کدنویسی استفاده نکنید.
متغیرها با استفاده از تابع ()with در دامنه سراسری (global scope) درج خواهند شد. از این رو، اگر متغیر دیگری نیز از نام مشابهی برخوردار باشد، ممکن است باعث بروز تناقض و ایجاد باگ نرمافزاری شود.
از حلقه for-in برای آرایهها استفاده نکنید.
به جای کد زیر …
var sum = 0; for (var i in arrayNumbers) { sum += arrayNumbers[i]; }
از این عبارت استفاده کنید:
var sum = 0; for (var i = 0, len = arrayNumbers.length; i < len; i++) { sum += arrayNumbers[i]; }
علاوه بر این، از آنجایی که تعاریف i و len اولین جمله در حلقه for هستند، پس تنها یک بار فراخوانی خواهند شد. به همین دلیل نیز قطعه کد بالا بسیار سریعتر از کد زیر عمل خواهد کرد:
for (var i = 0; i < arrayNumbers.length; i++)
همانطور که مشاهده میکنید، عبارت arrayNumbers.length در هربار تکرار حلقه، از نو محاسبه میشود.
توجه: البته این مشکل در نسخه جدید موتور جاوا اسکریپ برطرف شده است.
به جای رشتههای string ، توابع را به setTimeout و setInterval ارسال کنید.
در صورت ارسال رشته به این دو تابع، رشته به مانند eval (در مثال ۳۲) ارزیابی خواهد شد. از این رو عملکردی کندتر را ارائه خواهد داد.
پس به جای قطعه کد زیر …
setInterval('doSomethingPeriodically()', 1000); setTimeout('doSomethingAfterFiveSeconds()', 5000);
از این روش استفاده کنید :
setInterval(doSomethingPeriodically, 1000); setTimeout(doSomethingAfterFiveSeconds, 5000);
به جای استفاده پیدرپی از if/else از switch/case استفاده کنید.
زمانی که تعداد شروط if در برنامه میان ۲ تا ۱۰ عنوان باشد، بهتر است از switch/case استفاده کنید تا برنامه علاوه بر سرعت بالاتر، از نظم بیشتری برخوردار باشد.
استفاده از switch/case با محدوده عددی
با استفاده از روش زیر، میتوان از عبارت switch/case در محدوده عددی استفاده نمود.
function getCategory(age) { var category = ""; switch (true) { case isNaN(age): category = "not an age"; break; case (age >= 50): category = "Old"; break; case (age <= 20): category = "Baby"; break; default: category = "Young"; break; }; return category; } getCategory(5); // will return "Baby"
ساخت یک آبجکت و اختصاص آبجکت دیگر به عنوان ویژگی prototype در آن
function clone(object) { function OneShotConstructor(){}; OneShotConstructor.prototype= object; return new OneShotConstructor(); } clone(Array).prototype ; // []
حذف کدهای HTML از یک متن
function escapeHTML(text) { var replacements= {"<": "<", ">": ">","&": "&", """: """}; return text.replace(/[<>&"]/g, function(character) { return replacements[character]; }); }
از استفاده try-catch در یک حلقه پرهیز کنید.
این کار باعث ایجاد تداخل در عملکرد حلقه خواهد شد. پس به جای قطعه کد زیر …
var object = ['foo', 'bar'], i; for (i = 0, len = object.length; i <len; i++) { try { // do something that throws an exception } catch (e) { // handle exception } }
از این روش استفاده کنید:
var object = ['foo', 'bar'], i; try { for (i = 0, len = object.length; i <len; i++) { // do something that throws an exception } } catch (e) { // handle exception }
استفاده از تابع setTimeout درون XMLHttpRequests
میتوانید در صورت طولانی شدن زمان درخواست XHR از سرور (به عنوان مثال به دلیل مشکلات اینترنت)، از تابع setTimeout برای لغو درخواست استفاده کنید.
var xhr = new XMLHttpRequest (); xhr.onreadystatechange = function () { if (this.readyState == 4) { clearTimeout(timeout); // do something with response data } } var timeout = setTimeout( function () { xhr.abort(); // call error callback }, ۶۰*۱۰۰۰ /* timeout after a minute */ ); xhr.open('GET', url, true); xhr.send();
البته به خاطر داشته باشید که در حالت کلی باید از ارسال درخواستهای همگام (XHR (synchronous پرهیز کنید.
سروکار زدن با خطای timeout در WebSocket
به طور کلی، سرور در زمانی که ارتباط Websocket ایجاد شده باشد و فعالیتی در مدت ۳۰ ثانیه انجام نشود، خطای timeout ارسال خواهد کرد. همچنین فایروال نیز ممکن است بعد از مشاهده عدم فعالیت شبکه، این خطا را ارسال کند.
برای رفع این مشکل، میتوانید در مدت زمان معینی، پیامهای خالی به سرور ارسال کنید. از این رو، دو تابع زی را به برنامه خود بیافزایید. تابع اول ارتباط را زنده نگاه خواهد داشت و دیگری آن را لغو میکند. با استفاده از این رو ش میتوانید خطای timeout را کنترل کنید:
(مقدار timerID را نسبت به نیاز خود تغییر دهید.)
var timerID = 0; function keepAlive() { var timeout = 15000; if (webSocket.readyState == webSocket.OPEN) { webSocket.send(''); } timerId = setTimeout(keepAlive, timeout); } function cancelKeepAlive() { if (timerId) { cancelTimeout(timerId); } }
تابع ()keepAlive باید در قسمت پایانی عبارت ()onOpen افزوده شود. همچنین ()cancelKeepAlice نیز در آخر عبارت ()onClose کاربرد خواهد داشت.
عملگرهای اولیه بسیار سریعتر از عملگرهای تولیدشده در توابع هستند.
به عنوان مثال، به جای این قطعه کد …
var min = Math.min(a,b); A.push(v);
از این روش استفاده کنید:
var min = a < b ? a : b; A[A.length] = v;
از ابزارهای زیباساز و همچنین خلاصهساز کد غفلت نکنید.
قبل از اجرای پروژه حتما از ابزارهایی مثل JSLint برای مرتبسازی کد و همچنین JSMin برای خلاصهسازی استفاده کنید.
****
خب، این بخش از آموزش جاوا اسکریپ هم به پایان رسید. البته تکنیکها و روشهای زیادی در کدنویسی وجود دارد، پس طبیعتا تنها بخش کوچکی از آنها را توانستیم در این مقاله گردآوری کنیم.
اگر فکرمیکنید این آموزش برایتان مفید بوده، یا سوال و پیشنهادی در این مورد دارید، دیدگاه خودتان را در پایین همین مقاله برایمان ارسال کنید. از شنیدن نظرات شما خوشحال میشویم.