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


نمایش پیام‌ها و اخطارهای یک برنامه‌ی Angular توسط ng2-toasty

در مطلب «ایجاد Drop Down List‌های آبشاری در Angular» در قسمت دریافت اطلاعات drop down دوم از سرور، اگر کاربر مجددا گروه را بر روی حالت «لطفا گروهی را انتخاب کنید ...» قرار دهد، مقدار categoryId به undefined تغییر می‌کند:
  fetchProducts(categoryId?: number) {
    console.log(categoryId);

    this.products = [];

    if (categoryId === undefined || categoryId.toString() === "undefined") {
      return;
    }
در اینجا می‌خواهیم توسط کامپوننت Angular2 Toasty، پیام متناسبی را نمایش دهیم:



پیشنیازهای کار با کامپوننت Angular2 Toasty توسط یک برنامه‌ی Angular CLI

برای کار با کامپوننت Angular2 Toasty، ابتدا از طریق خط فرمان به پوشه‌ی ریشه‌ی برنامه وارد شده و سپس دستور ذیل را صادر می‌کنیم:
> npm install ng2-toasty --save
اینکار سبب خواهد شد تا این کامپوننت در پوشه‌ی node_modules\ng2-toasty نصب شده و همچنین فایل package.json نیز جهت درج مدخل آن به روز رسانی شود:


یک نکته: اگر در حین اجرای این دستور به خطای ذیل برخوردید:
 npm ERR! Error: EPERM: operation not permitted, rename
چون VSCode پوشه‌ی node_modules را تحت نظر قرار می‌دهد، ممکن است یک سری اعمال npm مجوز اجرا را پیدا نکنند. بنابراین ابتدا VSCode را بسته و مجددا دستور npm را اجرا کنید.

پس از آن نیاز است یکی از شیوه‌نامه‌هایی را که در تصویر فوق ملاحظه می‌کنید، در فایل angular-cli.json. مشخص کنیم:
"styles": [
    "../node_modules/bootstrap/dist/css/bootstrap.min.css",
    "../node_modules/ng2-toasty/bundles/style-bootstrap.css",
    "styles.css"
],
که برای نمونه در اینجا، شیوه‌نامه‌ی بوت استرپ آن انتخاب شده‌است.

سپس باید به فایل src\app\app.module.ts مراجعه کرد و ماژول این کامپوننت را معرفی نمود:
import { ToastyModule } from "ng2-toasty";

@NgModule({
  imports: [
    BrowserModule,
    ToastyModule.forRoot(),

همچنین در همین قسمت، به فایل قالب src\app\app.component.html مراجعه کرده و selector tag این کامپوننت را در ابتدای آن تعریف می‌کنیم:
 <ng2-toasty [position]="'top-right'"></ng2-toasty>
در اینجا با استفاده از property binding و تعیین مقدار رشته‌ای top-right، محل نمایش اعلانات برنامه را مشخص می‌کنیم. مقدارهای ممکن آن شامل bottom-right، bottom-left، top-right، top-left، top-center، bottom-center، center-center هستند. برای مثال اگر می‌خواهید آن‌را در میانه‌ی صفحه نمایش دهید، مقدار center-center را انتخاب کنید. همچنین باید دقت داشت که این مقدار باید درون '' قرار گیرد تا مشخص شود که رشته‌ای به خاصیت position انتساب داده شده‌است و این مقدار یک خاصیت عمومی تعریف شده‌ی در کامپوننت متناظر با قالب، نیست.


نمایش یک پیام خطا توسط ToastyService

اکنون که کار برپایی کامپوننت Angular2 Toasty به پایان رسید، کار کردن با آن به سادگی تزریق سرویس آن به سازنده‌ی یک کامپوننت و فراخوانی متدهای info، success ، wait ، error و warning آن است:
import { ToastyService, ToastOptions } from "ng2-toasty";

export class ProductGroupComponent implements OnInit {

  constructor(
    private productItemsService: ProductItemsService,
    private toastyService: ToastyService) { }

  fetchProducts(categoryId?: number) {
    console.log(categoryId);

    this.products = [];

    if (categoryId === undefined || categoryId.toString() === "undefined") {
      this.toastyService.error(<ToastOptions>{
        title: "Error!",
        msg: "Please select a category.",
        theme: "bootstrap",
        showClose: true,
        timeout: 5000
      });
      return;
    }
- در اینجا در ابتدا ماژول‌های مورد نیاز import شده‌اند.
- سپس ToastyService به سازنده‌ی کلاس کامپوننت مدنظر تزریق شده‌است تا بتوان از امکانات آن استفاده کرد.
- در ادامه، فراخوانی متد this.toastyService.error سبب نمایش اخطار قرمز رنگی می‌شود که تصویر آن‌را در ابتدای مطلب جاری مشاهده کردید.
- علت ذکر <ToastOptions> در اینجا این است که وجود آن سبب خواهد شد تا intellisense در VSCode فعال شود و پس از آن بتوان تمام گزینه‌های این متد و تنظیمات را بدون مراجعه‌ی به مستندات آن از طریق intellisense یافت و درج کرد:



مدیریت سراسری خطاهای مدیریت نشده، در یک برنامه‌ی Angular

در برنامه‌های Angular از این دست کدها بسیار مشاهده می‌شوند:
    this.productItemsService.getCategories().subscribe(
      data => {
        this.categories = data;
      },
      err => console.log("get error: ", err)
    );
تا اینجا قسمت err یا بروز خطا را با console.log مدیریت کرده‌ایم. در این حالت کاربر ممکن است 10 بار بر روی دکمه‌ای کلیک کند یا صفحه‌ای را بارگذاری کند و دست آخر متوجه نشود که مشکل کار چیست. به همین جهت می‌توان خطاها را نیز توسط ToastyService نمایش داد تا کاربران دقیقا متوجه بروز مشکل رخ داده شوند. اما ... به این ترتیب تکرار کد زیادی را خواهیم داشت و باید به ازای تمام این موارد، یکبار this.toastyService.error را فراخوانی کنیم. برای مدیریت بهتر یک چنین سناریویی در Angular، کلاس و سرویس توکاری به نام ErrorHandler وجود دارد. در هر قسمتی از برنامه‌ی Angular که استثنایی مدیریت نشده رخ دهد، ابتدا از این کلاس رد شده و سپس به برنامه انتشار پیدا می‌کند. بنابراین می‌توان یک ErrorHandler سفارشی را با ارث بری از آن تهیه کرد و سپس بجای سرویس توکار اصلی، به برنامه معرفی و از آن استفاده نمود. به این ترتیب می‌توان یک Global Error Interceptor را طراحی نمود.
به همین منظور کلاس جدیدی را به صورت ذیل در پوشه‌ی src\app اضافه می‌کنیم:
> ng g cl app.error-handler
با این خروجی
 installing class
  create src\app\app.error-handler.ts
سپس این کلاس را به نحو ذیل تکمیل خواهیم کرد:
import { ErrorHandler } from "@angular/core";

export class AppErrorHandler implements ErrorHandler {

  handleError(error: any): void {
    console.log("Error:", error);
  }
}
کلاس جدید AppErrorHandler از کلاس پایه ErrorHandler ارث بری می‌کند. بنابراین import آن‌را در ابتدای کار مشاهده می‌کنید. سپس باید متد handleError آن‌را با امضایی که مشاهده می‌کنید، پیاده سازی کنیم. فعلا با استفاده از console.log این خطا را در کنسول developer tools نمایش می‌دهیم.

اکنون نیاز است این ErrorHandler سفارشی را بجای نمونه‌ی اصلی به برنامه معرفی کنیم. برای این منظور به فایل src\app\app.module.ts مراجعه کرده و تغییرات ذیل را اعمال می‌کنیم:
import { NgModule, ErrorHandler } from "@angular/core";
import { AppErrorHandler } from "./app.error-handler";

@NgModule({
  providers: [
    { provide: ErrorHandler, useClass: AppErrorHandler }
  ]
ابتدا ErrorHandler به لیست imports اضافه شده‌است و همچنین محل تامین AppErrorHandler نیز مشخص گردیده‌است. سپس در قسمت providers ماژول جاری، از تعریف خاصی که ملاحظه می‌کنید، استفاده خواهد شد. به این ترتیب به Angular اعلام می‌کنیم، هرگاه نیازی به وهله‌ای از کلاس توکار ErrorHandler بود، وهله‌ای از کلاس سفارشی AppErrorHandler را مورد استفاده قرار بده.

اکنون برای آزمایش آن، در کدهای سمت سرور مطلب «ایجاد Drop Down List‌های آبشاری در Angular»، یک استثنای عمدی را قرار می‌دهیم:
[HttpGet("[action]/{categoryId:int}")]
public async Task<IActionResult> GetProducts(int categoryId)
{
   throw new Exception();
به این ترتیب هر زمانیکه گروهی انتخاب شد، دریافت محصولات آن گروه با خطا مواجه می‌شود.
برای اینکه AppErrorHandler، مورد استفاده قرار گیرد، قسمت err دریافت لیست محصولات را نیز حذف می‌کنیم (تا تبدیل به یک استثنای مدیریت نشده شود):
    this.productItemsService.getProducts(categoryId).subscribe(
      data => {
        this.products = data;
        this.isLoadingProducts = false;
      }// ,
      // err => {
      //   console.log("get error: ", err);
      //   this.isLoadingProducts = false;
      // }
    );
اکنون اگر برنامه را اجرا کنیم، چنین پیامی، در کنسول developer tools ظاهر می‌شود و مشخص است از فایل AppErrorHandler صادر شده‌است:



افزودن ToastyService به AppErrorHandler

در ادامه می‌خواهیم بجای console.log از ToastyService برای نمایش خطاهای مدیریت نشده‌ی برنامه در کلاس AppErrorHandler استفاده کنیم:
import { ToastyService, ToastOptions } from "ng2-toasty";
import { ErrorHandler } from "@angular/core";

export class AppErrorHandler implements ErrorHandler {

  constructor(private toastyService: ToastyService) {
  }

  handleError(error: any): void {
    // console.log("Error:", error);
    this.toastyService.error(<ToastOptions>{
      title: "Error!",
      msg: "Fatal error!",
      theme: "bootstrap",
      showClose: true,
      timeout: 5000
    });
  }
}
به همین منظور سرویس آن‌را به سازنده‌ی کلاس AppErrorHandler تزریق کرده و سپس از آن به نحو متداولی در متد handleError استفاده می‌کنیم. به این ترتیب بجای ده‌ها و یا صدها قسمت مدیریت err=>this.toastyService.error در برنامه، تنها یک مورد مدیریت مرکزی را خواهیم داشت.

مشکل اول! اکنون اگر برنامه را اجرا کنیم، در کنسول developer tools چنین خطایی ظاهر می‌شود:
 Uncaught Error: Can't resolve all parameters for AppErrorHandler: (?).
به این معنا که Angular قادر نیست وهله‌ای از AppErrorHandler را ایجاد کند؛ چون نمی‌داند که چگونه باید پارامتر سازنده‌ی ToastyService را وهله سازی و تزریق نماید. علت اینجا است که کار آغاز کلاس ویژه‌ی ErrorHandler سراسری، پیش از کار بارگذاری ماژول مرتبط با ToastyService انجام می‌شود. به همین جهت، این مورد جزو معدود مواردی است که باید به صورت دستی تزریق شود:
import { ErrorHandler, Inject } from "@angular/core";

export class AppErrorHandler implements ErrorHandler {

  constructor(
    @Inject(ToastyService) private toastyService: ToastyService
  ) {
  }
در اینجا توسط Inject decorator، کار تزریق دستی ToastyService انجام خواهد شد. اکنون اگر برنامه را مجدد اجرا کنیم، خطای قبلی برطرف شده‌؛ یعنی کلاس AppErrorHandler با موفقیت وهله سازی شده‌است.

مشکل دوم! اینبار برنامه را اجرا کنید. سپس گروهی را انتخاب نمائید. مشاهده می‌کنید که خطایی نمایش داده نشد؛ هرچند در کنسول developer tools می‌توان اثری از آن را مشاهده کرد. مجددا گروه دیگری را انتخاب کنید، در این بار دوم است که خطای ارائه شده‌ی توسط this.toastyService.error ظاهر می‌شود. توضیح آن نیاز به بررسی مفهومی به نام Zones در Angular دارد.


مفهوم Zones در Angular

زمانیکه متد this.toastyService.error در یک کامپوننت برنامه مورد استفاده قرار گرفت، به خوبی کار می‌کرد و در همان بار اول فراخوانی، پیام را نمایش می‌داد. اما با انتقال آن به کلاسAppErrorHandler ، این قابلیت از کار افتاد. علت اینجا است که زمینه‌ی اجرایی این قطعه کد، اکنون خارج از Zone یا ناحیه‌ی Angular است و به همین دلیل متوجه تغییرات آن نمی‌شود. Zone زمینه‌ی اجرایی اعمال async است و اگر به فایل package.json یک برنامه‌ی Angular دقت کنید، بسته‌ی zone.js، یکی از وابستگی‌های همراه آن است.
تغییرات حالت برنامه، توسط یکی از اعمال ذیل رخ می‌دهند:
الف) بروز رخ‌دادهایی مانند کلیک، ورود اطلاعات و یا ارسال فرم
ب) اعمال Ajax ایی
ج) استفاده از Timers مانند استفاده از setTimeout و  setInterval

هر سه مورد یاد شده از نوع async بوده و زمانیکه رخ می‌دهند، حالت برنامه را تغییر خواهند داد. Angular نیز تنها به این موارد علاقمند بوده و به آ‌ن‌ها در جهت به روز رسانی رابط کاربری برنامه واکنش نشان می‌دهد.
برای مثال this.toastyService.error دارای خاصیتی است به نام timeout: 5000 که در آن، مورد «ج» فوق رخ می‌دهد؛ یعنی یک Timer پس از 5 ثانیه سبب بسته شدن آن خواهد شد. به همین جهت است که اگر پیش از پایان این 5 ثانیه مجددا درخواست واکشی لیست محصولات یک گروه را بدهیم، خطای مربوطه مشاهده می‌شود. چون Angular زمینه‌ی اجرایی لازم را فراهم کرده (یا همان Zone در اینجا) و مجبور به واکنش به عملیات async از نوع Timer است.

برای دسترسی به امکانات کتابخانه‌ی zone.js، می‌توان از طریق تزریق سرویس آن به نام NgZone به سازنده‌ی کلاس شروع کرد:
import { ToastyService, ToastOptions } from "ng2-toasty";
import { ErrorHandler, Inject, NgZone } from "@angular/core";
import { LocationStrategy, PathLocationStrategy } from "@angular/common";

export class AppErrorHandler implements ErrorHandler {

  constructor(
    @Inject(NgZone) private ngZone: NgZone,
    @Inject(ToastyService) private toastyService: ToastyService,
    @Inject(LocationStrategy) private locationProvider: LocationStrategy
  ) {
  }

  handleError(error: any): void {
    // console.log("Error:", error);

    const url = this.locationProvider instanceof PathLocationStrategy ? this.locationProvider.path() : "";
    const message = error.message ? error.message : error.toString();
    this.ngZone.run(() => {
      this.toastyService.error(<ToastOptions>{
        title: "Error!",
        msg: `URL:${url} \n ERROR:${message}`,
        theme: "bootstrap",
        showClose: true,
        timeout: 5000
      });
    });

    // IMPORTANT: Rethrow the error otherwise it gets swallowed
    // throw error;
  }
}
در اینجا فراخوانی this.ngZone.run سبب می‌شود تا درخواست نمایش خطای رخ‌داده وارد Angular Zone شده و بلافاصله سبب نمایش آن گردد:
 


چند نکته
1- اگر می‌خواهید علاوه بر رخ‌دادگردانی سراسری خطاها، این خطاها را به محل اصلی آن‌ها نیز انتشار دهید، نیاز است سطر throw error را در انتهای متد handleError نیز ذکر کنید. در غیر اینصورت، کار در همینجا به پایان خواهد رسید و این خطاها دیگر منتشر نمی‌شوند.
2- روش دریافت URL جاری صفحه را نیز در اینجا مشاهده می‌کنید. این اطلاعات می‌توانند جهت ارسال به سرور برای ثبت و بررسی‌های بعدی مفید باشند.
3- مقدار new Error().stack معادل stack trace جاری است و تقریبا در تمام مرورگرهای جدید پشتیبانی می‌شود.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: angular-template-driven-forms-lab-07.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کرده‌اید. سپس به ریشه‌ی پروژه وارد شده و دو پنجره‌ی کنسول مجزا را باز کنید. در اولی دستورات
>npm install
>ng build --watch
و در دومی دستورات ذیل را اجرا کنید:
>dotnet restore
>dotnet watch run
اکنون می‌توانید برنامه را در آدرس http://localhost:5000 مشاهده و اجرا کنید.
مطالب
برنامه نویسی پیشرفته JavaScript - قسمت 1 - توابع

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

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

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

در تهیه‌ی این مجموعه مقالات از منابع زیر استفاده شده است:

1.  Pro JavaScript Techniques  (John Resig ، خالق JQuery  – فصول 2 و 3)

2.  Professional JavaScript for Web Developers (Third Edition)  (Nicholas C. Zakas  – فصول 3، 4، 5، 6 و 7)

3.  Object-Oriented JavaScript  (Stoyan Stefanov  – فصول 3، 4، 5، 6 و 8)

4.  و تجربه‌ی ناچیز اینجانب


توابع  (Functions)

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

function <functionName> ([arg0, arg1, …, argN]) {
<statements>
}

به عنوان مثال:

function sayHello(name, message) {
   alert("Hello " + name + ", " + message);
}

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

sayHello("Meysam", "welcome to site");

خروجی این تابع  “Hello Meysam, welcome to site”  می باشد که به صورت یک پنجره‌ی پیغام، نمایش می‌یابد. تابع فوق هیچ مقداری را به عنوان خروجی به برنامه‌ی اصلی یا محل فراخوانی خود بر نمی‌گرداند. اگر بخواهیم توسط تابع مقداری را برگردانیم می‌توانیم از دستور return  برای این منظور استفاده نماییم. به مثال زیر توجه کنید:

function sum(num1, num2) {
   return num1 + num2;
}

تابع  sum  دو عدد را به عنوان آرگومان ورودی دریافت نموده و حاصل جمع آن‌ها را توسط دستور  return  به عنوان خروجی بر می‌گرداند. تابع فوق را می‌توان به صورت زیر فراخوانی نمود:

alert(sum(10, 20));

خروجی :

30

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

function sum(num1, num2) {
   return num1 + num2;
   alert("Hello");
}

در مثال فوق دستور  alert به هیچ عنوان اجرا نخواهد شد؛ زیرا تابع با رسیدن به دستور return خاتمه می‌یابد. یک تابع می‌تواند شامل بیش از یک دستور return باشد.

function diff(num1,num2) {
   if (num1 > num2)
     return num1 - num2;
   else
     return num2 - num1;
}

تابع فوق اختلاف دو عدد را بدست می‌آورد و اگر عدد اول بزرگتر باشد، عدد دوم را از عدد اول تفریق می‌کند؛ در غیر اینصورت عدد اول را از عدد دوم تفریق می‌کند و به عنوان خروجی بر می‌گرداند.  نکته‌ی دیگری که لازم است بدانید این است که دستور  return  می تواند هیچ مقداری را بر نگرداند و به تنهایی بکار گرفته شود. در این صورت دقیقا بعد از دستور  return  سمی کالن  (;)  قرار می‌دهیم.

function checkNumber(num) {
   if (isNaN(num)) {
     alert("Not a number");
     return;
   }
   alert(num+" is a number");
}

تابع فوق یک ورودی را دریافت می‌نماید و در صورتی که آرگومان ورودی عدد نباشد پیغام  “Not a number”  را نمایش می‌دهد و از تابع خارج می‌شود. در صورتی که آرگومان ورودی یک عدد باشد، پیغام دوم را نمایش می‌دهد.

توجه:

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

function sum(num1, num2) {
   if (isNaN(num1) || isNaN(num2))
     return; // بهتر است حداقل مقدار 0 برگردانده شود
   return num1 + num2;
}


کار با آرگومان ها

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

با توجه به قابلیتی که در مورد آرگومانها ذکر شد و به دلیل عدم مدیریت نوع و تعداد آرگومان‌های ارسالی، مطمئنا جهت جلوگیری از بروز خطا در توابع، باید تعداد و نوع آرگومان‌های ارسالی بررسی و مدیریت شوند. همچنین در هر تابع، آرایه‌ای به نام arguments  به صورت توکار تعبیه شده است که مدیریت آرگومان‌ها را تسهیل می‌بخشد. به مثال زیر توجه کنید:

function sayHello() {
   alert("Hello " + arguments[0] + ", " + arguments[1]);
}

sayHello("Meysam", "welcome to site");

خروجی :

"Hello Meysam, welcome to site"

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

function sum() {
   var s = 0;
   for (var i = 0; i < arguments.length; i++)
     s += arguments[i];
   return s;
}

alert(sum());
alert(sum(10, 20, 30, 40, 50));
alert(sum(10));

خروجی :

0

150

10

در تابع فوق هیچ آرگومان ورودی وجود ندارد ولی این تابع را با 0، 5 و 1 آرگومان ورودی فراخوانی نمودیم. این تابع مجموع چند عدد را محاسبه و بر می‌گرداند و می‌تواند به تعداد نامحدودی عدد دریافت نماید. البته بهتر است نوع آرگومانهای ارسالی نیز بررسی شوند تا خطایی در محاسبات رخ ندهد. همچنین بجای حلقه for  از حلقه for…in  استفاده خواهم کرد.

function sum() {
   var s = 0;
   for (var i in arguments) {
     if (isNaN(arguments[i]))
       continue;
    s += arguments[i];
   }
   return s;
}

alert(sum());
alert(sum(10, 20, "a", 40, 50));
alert(sum(10));

خروجی :

0

120

10

اگر دقت کرده باشید در فراخوانی دوم، رشته  “a”  به تابع ارسال شده است و چون آرگومانهای نامعتبر را مدیریت نموده‌ایم، خطایی در خروجی رخ نمی‌دهد. به مثال زیر نیز توجه نمایید:

function sum(a,b,c) {
   return a + b + c;
}

alert(sum(10, 20, 30));
alert(sum(10, 20));
alert(sum());

خروجی :

60

NaN

NaN

تابع فوق دارای 3 آرگومان ورودی می‌باشد؛ ولی ما این تابع را با 2 و 0 آرگومان ورودی فراخوانی نمودیم که خروجی نامناسبی را تولید نموده است. برای رفع این مشکل و معتبر سازی آرگومان‌های ارسالی می‌توانیم به صورت زیر عمل نماییم:

function sum(a, b, c) {
   if (isNaN(a)) a = 0;
   if (isNaN(b)) b = 0;
   if (isNaN(c)) c = 0;
   return a + b + c;
}

alert(sum(10, 20, 30));
alert(sum(10, 20));
alert(sum());

خروجی :

60

30

0

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

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

توجه:

به آرگومانهایی که در تابع دارای نام می‌باشند و یا به عبارتی، آرگومانهایی که نام آنها در تابع ذکر می‌شود، Named Arguments  یا آرگومانهای نامی (اسمی و یا نامدار) می‌گویند. مثل آرگومان های a ، b  وc  در تابع sum


عدم پشتیبانی از سربارگذاری یا  Overloading

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

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

function calc(num1,num2) {
   return num1 + num2;
}

function calc(num1,num2) {
   return num1 - num2;
}

alert(calc(200,100));

خروجی :

100

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

نظرات مطالب
سفارشی سازی ASP.NET Core Identity - قسمت پنجم - سیاست‌های دسترسی پویا
دلیل آن عدم استفاده از تنظیمات پیش‌فرض این پروژه مانند زمان بررسی security stamp تغییر کرده‌است. حالت پیش‌فرض آن 30 دقیقه است اما در این پروژه البته به صفر تنظیم شده تا Logout آنی پیاده سازی شود. 30 دقیقه‌ی پیش‌فرض یعنی اگر کاربری را غیرفعال کردید، این شخص تا 30 دقیقه‌ی بعد می‌تواند به خرابکاری خودش ادامه دهد و هنوز لاگین باشد؛ بدون مشکلی!
مطالب
یک نکته درباره Angular routeProvider
بعد از مطالعه پست‌های ^ و ^    نکته ای به ذهنم رسید که بیان آن از بنده و مطالعه آن توسط شما خالی از لطف نیست. اگر مثال‌های پیاده سازی شده در پست‌های ^ و ^ را با AngularJs نسخه 1.2 اجرا نمایید به طور حتم با خطا روبرو می‌شوید و نتیجه مورد نظر حاصل نمی‌شود. در این پست نیز توسط یکی از دوستان اشاره ای به این مطلب شد.
دلیل خطا این است که از نسخه 1.2 به بعد در Angular سیستم مسیر یابی به این شکل امکان پذیر نیست و بخش مسیریابی به یک فایل دیگر به نام angular-route.js منتقل شده است. در نتیجه اگر به سبک نسخه‌های قبلی Angular از سیستم مسیریابی استفاده نمایید با خطا مواجه خواهید شد و خطای مورد نظر هم مربوط به عدم توانایی در تزریق وابستگی routeProvider$ به ماژول مورد نظر است. حال راه حل چیست؟
کافیست در هنگام تعریف ماژول، ngRoute را به عنوان وابستگی ماژول تعیین نمایید. و از طرفی فایل اسکریپتی angular-route.js را بعد از angular.js فراخوانی کنید.
بررسی مثال:
کد‌های زیر مربوط به مثال‌های پست قبلی می‌باشد که شرح کامل آن در این پست است:
var myFirstRoute = angular.module('myFirstRoute', []);
   
myFirstRoute.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/pageOne', {
        templateUrl: 'templates/page_one.html',
        controller: 'ShowPage1Controller'
      }).
      when('/pageTwo', {
        templateUrl: 'templates/page_two.html',
        controller: 'ShowPage2Controller'
      }).
      otherwise({
        redirectTo: '/pageOne'
      });
}]);
 
 
myFirstRoute.controller('ShowPage1Controller', function($scope) {
    $scope.message = 'Content of page-one.html';
});
  
  
myFirstRoute.controller('ShowPage2Controller', function($scope) {
    $scope.message = 'Content of page-two.html';
});
برای هماهنگ کردن مثال بالا با نسخه 1.2 باید به روش زیر عمل نمود:
var myFirstRoute = angular.module('myFirstRoute',['ngRoute']);
   
myFirstRoute.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/pageOne', {
        templateUrl: 'templates/page_one.html',
        controller: 'ShowPage1Controller'
      }).
      when('/pageTwo', {
        templateUrl: 'templates/page_two.html',
        controller: 'ShowPage2Controller'
      }).
      otherwise({
        redirectTo: '/pageOne'
      });
}]);
تنها تغییر، مشخص کردن ngRoute به عنوان وابستگی ماژول myFirstRoute است(خط اول). نکته دیگر لود فایل angular-route.js قبل از فراخوانی فایل بالا و بعد از فراخوانی فایل angular.js است:
<body ng-app="app">
   
    <div>
        <div>
        <div>
            <ul>
                <li><a href="#pageOne"> Show page one </a></li>
                <li><a href="#pageTwo"> Show page two </a></li>
            </ul>
        </div>
        <div>
            <div ng-view></div>
        </div>
        </div>
    </div>
   
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
     <script src="angular-route.js"></script>
    <script src="app.js"></script>
   
  </body>

اشتراک‌ها
کتابخانه angucomplete-alt
Autocomplete Directive for AngularJS. A fork of Daryl Rowland's angucomplete (https://github.com/darylrowland/angucomplete) with some extra features.  Demo
کتابخانه angucomplete-alt
اشتراک‌ها
وبینار بهینه سازی گیت توسط کلاب

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

همچنین میتوانید زمان وبینار را که در دو ساعت 6:30 به وقت تهران Eastern و 9:30 شب به وقت تهران Pacific برگزار خواهد شد را انتخاب کنید.

وبینار بهینه سازی گیت توسط کلاب
نظرات مطالب
پیاده سازی UnitOfWork به وسیله MEF

من الان یک ویژگی StringLength به طول 30 رو روی خاصیت Title کلاس Category مقاله جاری اضافه کردم. با همین کدهای فوق، این فیلد با طول 30 الان در دیتابیس قابل مشاهده است. آغاز دیتابیس اصلا کاری به MEF نداره.

این هم فایل مثالی که در آن ویژگی طول رشته اعمال شده برای آزمایش:

MefSample.cs

نظرسنجی‌ها
ساعت کاری محل کار شما چگونه است؟
ساعت کاری اداری 8 تا 17 و پنج شنبه ها تا 13
همه روزه 7:30 تا 14:30
پر کردن ساعت کاری مشخص ولی با ساعت کاری دلخواه
شنبه تا چهارشنبه از 8 تا 18