خلاصهای کوتاه در مورد WinRT
برخلاف تصور عموم برنامههای HTML/CSS/JavaScript ویندوز 8 منحصرا برای WinRT تهیه میشوند و ... و ... قابل انتقال به سایر سکوهای کاری «نیستند». اینها از API مخصوص WinRT استفاده میکنند تا معنا پیدا کنند. بنابراین این مورد اصلا web programming متداول نیست. به آن باید به چشم یک standalone Windows 8 app نگاه کرد.
OData چهار قسمت اصلی دارد:
- OData data model که یک راه عمومی برای مدیریت و توصیف دادهها را فراهم مینماید
- OData protocol که به کلاینت اجازه ایجاد درخواست و پاسخ از سرویس دهنده OData را میدهد.
- OData client libraries که امکان ساخت سادهتر نرم افزارها برای دسترسی به دادهها با قرارداد OData را میدهد.
- OData service سرویس دهنده و امکان دسترسی به دادهها را فراهم میسازد.
- ساده و انعطاف پذیر
- سورس باز بودن
- امکان استفاده در سیستمهای با دادههای رابطه ای و غیر رابطه ای
- امکان استفاده از دادهها با منابع ای که آدرس پذیر هستند یعنی دسترسی از طریق Url
- امکان دسترسی هر نوع گیرنده ای به داده ها
- امکان نمایش خروجی با فرمت Json یا Xml
- ...
تیم ASP.NET Identity پروژه نمونه ای را فراهم کرده است که میتواند بعنوان نقطه شروعی برای اپلیکیشنهای MVC استفاده شود. پیکربندیهای لازم در این پروژه انجام شدهاند و برای استفاده از فریم ورک جدید آماده است.
شروع به کار : پروژه نمونه را توسط NuGet ایجاد کنید
برای شروع یک پروژه ASP.NET خالی ایجاد کنید (در دیالوگ قالبها گزینه Empty را انتخاب کنید). سپس کنسول Package Manager را باز کرده و دستور زیر را اجرا کنید.
PM> Install-Package Microsoft.AspNet.Identity.Samples -Pre
پس از اینکه NuGet کارش را به اتمام رساند باید پروژه ای با ساختار متداول پروژههای ASP.NET MVC داشته باشید. به تصویر زیر دقت کنید.
همانطور که میبینید ساختار پروژه بسیار مشابه پروژههای معمول MVC است، اما آیتمهای جدیدی نیز وجود دارند. فعلا تمرکز اصلی ما روی فایل IdentityConfig.cs است که در پوشه App_Start قرار دارد.
اگر فایل مذکور را باز کنید و کمی اسکرول کنید تعاریف دو کلاس سرویس را مشاهده میکنید: EmailService و SmsService.
public class EmailService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Plug in your email service here to send an email. return Task.FromResult(0); } } public class SmsService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Plug in your sms service here to send a text message. return Task.FromResult(0); } }
اگر دقت کنید هر دو کلاس قرارداد IIdentityMessageService را پیاده سازی میکنند. میتوانید از این قرارداد برای پیاده سازی سرویسهای اطلاع رسانی ایمیلی، پیامکی و غیره استفاده کنید. در ادامه خواهیم دید چگونه این دو سرویس را بسط دهیم.
یک حساب کاربری مدیریتی پیش فرض ایجاد کنید
پیش از آنکه بیشتر جلو رویم نیاز به یک حساب کاربری در نقش مدیریتی داریم تا با اجرای اولیه اپلیکیشن در دسترس باشد. کلاسی بنام ApplicationDbInitializer در همین فایل وجود دارد که هنگام اجرای اولیه و یا تشخیص تغییرات در مدل دیتابیس، اطلاعاتی را Seed میکند.
public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext> { protected override void Seed(ApplicationDbContext context) { InitializeIdentityForEF(context); base.Seed(context); } //Create User=Admin@Admin.com with password=Admin@123456 in the Admin role public static void InitializeIdentityForEF(ApplicationDbContext db) { var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>(); const string name = "admin@admin.com"; const string password = "Admin@123456"; const string roleName = "Admin"; //Create Role Admin if it does not exist var role = roleManager.FindByName(roleName); if (role == null) { role = new IdentityRole(roleName); var roleresult = roleManager.Create(role); } var user = userManager.FindByName(name); if (user == null) { user = new ApplicationUser { UserName = name, Email = name }; var result = userManager.Create(user, password); result = userManager.SetLockoutEnabled(user.Id, false); } // Add user admin to Role Admin if not already added var rolesForUser = userManager.GetRoles(user.Id); if (!rolesForUser.Contains(role.Name)) { var result = userManager.AddToRole(user.Id, role.Name); } } }
تایید حسابهای کاربری : چگونه کار میکند
بدون شک با تایید حسابهای کاربری توسط ایمیل آشنا هستید. حساب کاربری ای ایجاد میکنید و ایمیلی به آدرس شما ارسال میشود که حاوی لینک فعالسازی است. با کلیک کردن این لینک حساب کاربری شما تایید شده و میتوانید به سایت وارد شوید.
اگر به کنترلر AccountController در این پروژه نمونه مراجعه کنید متد Register را مانند لیست زیر مییابید.
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Register(RegisterViewModel model) { if (ModelState.IsValid) { var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; var result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); var callbackUrl = Url.Action( "ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); await UserManager.SendEmailAsync( user.Id, "Confirm your account", "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>"); ViewBag.Link = callbackUrl; return View("DisplayEmail"); } AddErrors(result); } // If we got this far, something failed, redisplay form return View(model); }
public static ApplicationUserManager Create( IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) { var manager = new ApplicationUserManager( new UserStore<ApplicationUser>( context.Get<ApplicationDbContext>())); // Configure validation logic for usernames manager.UserValidator = new UserValidator<ApplicationUser>(manager) { AllowOnlyAlphanumericUserNames = false, RequireUniqueEmail = true }; // Configure validation logic for passwords manager.PasswordValidator = new PasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = true, RequireDigit = true, RequireLowercase = true, RequireUppercase = true, }; // Configure user lockout defaults manager.UserLockoutEnabledByDefault = true; manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5); manager.MaxFailedAccessAttemptsBeforeLockout = 5; // Register two factor authentication providers. This application // uses Phone and Emails as a step of receiving a code for verifying the user // You can write your own provider and plug in here. manager.RegisterTwoFactorProvider( "PhoneCode", new PhoneNumberTokenProvider<ApplicationUser> { MessageFormat = "Your security code is: {0}" }); manager.RegisterTwoFactorProvider( "EmailCode", new EmailTokenProvider<ApplicationUser> { Subject = "SecurityCode", BodyFormat = "Your security code is {0}" }); manager.EmailService = new EmailService(); manager.SmsService = new SmsService(); var dataProtectionProvider = options.DataProtectionProvider; if (dataProtectionProvider != null) { manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>( dataProtectionProvider.Create("ASP.NET Identity")); } return manager; }
در قطعه کد بالا کلاسهای EmailService و SmsService روی وهله ApplicationUserManager تنظیم میشوند.
manager.EmailService = new EmailService(); manager.SmsService = new SmsService();
درست در بالای این کدها میبینید که چگونه تامین کنندگان احراز هویت دو مرحله ای (مبتنی بر ایمیل و پیامک) رجیستر میشوند.
// Register two factor authentication providers. This application // uses Phone and Emails as a step of receiving a code for verifying the user // You can write your own provider and plug in here. manager.RegisterTwoFactorProvider( "PhoneCode", new PhoneNumberTokenProvider<ApplicationUser> { MessageFormat = "Your security code is: {0}" }); manager.RegisterTwoFactorProvider( "EmailCode", new EmailTokenProvider<ApplicationUser> { Subject = "SecurityCode", BodyFormat = "Your security code is {0}" });
تایید حسابهای کاربری توسط ایمیل و احراز هویت دو مرحله ای توسط ایمیل و/یا پیامک نیاز به پیاده سازی هایی معتبر از قراردارد IIdentityMessageService دارند.
پیاده سازی سرویس ایمیل توسط ایمیل خودتان
پیاده سازی سرویس ایمیل نسبتا کار ساده ای است. برای ارسال ایمیلها میتوانید از اکانت ایمیل خود و یا سرویس هایی مانند SendGrid استفاده کنید. بعنوان مثال اگر بخواهیم سرویس ایمیل را طوری پیکربندی کنیم که از یک حساب کاربری Outlook استفاده کند، مانند زیر عمل خواهیم کرد.
public class EmailService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Credentials: var credentialUserName = "yourAccount@outlook.com"; var sentFrom = "yourAccount@outlook.com"; var pwd = "yourApssword"; // Configure the client: System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient("smtp-mail.outlook.com"); client.Port = 587; client.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network; client.UseDefaultCredentials = false; // Creatte the credentials: System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(credentialUserName, pwd); client.EnableSsl = true; client.Credentials = credentials; // Create the message: var mail = new System.Net.Mail.MailMessage(sentFrom, message.Destination); mail.Subject = message.Subject; mail.Body = message.Body; // Send: return client.SendMailAsync(mail); } }
پیاده سازی سرویس ایمیل با استفاده از SendGrid
سرویسهای ایمیل متعددی وجود دارند اما یکی از گزینههای محبوب در جامعه دات نت SendGrid است. این سرویس API قدرتمندی برای زبانهای برنامه نویسی مختلف فراهم کرده است. همچنین یک Web API مبتنی بر HTTP نیز در دسترس است. قابلیت دیگر اینکه این سرویس مستقیما با Windows Azure یکپارچه میشود.
می توانید در سایت SendGrid یک حساب کاربری رایگان بعنوان توسعه دهنده بسازید. پس از آن پیکربندی سرویس ایمیل با مرحله قبل تفاوت چندانی نخواهد داشت. پس از ایجاد حساب کاربری توسط تیم پشتیبانی SendGrid با شما تماس گرفته خواهد شد تا از صحت اطلاعات شما اطمینان حاصل شود. برای اینکار چند گزینه در اختیار دارید که بهترین آنها ایجاد یک اکانت ایمیل در دامنه وب سایتتان است. مثلا اگر هنگام ثبت نام آدرس وب سایت خود را www.yourwebsite.com وارد کرده باشید، باید ایمیلی مانند info@yourwebsite.com ایجاد کنید و توسط ایمیل فعالسازی آن را تایید کند تا تیم پشتیبانی مطمئن شود صاحب امتیاز این دامنه خودتان هستید.
تنها چیزی که در قطعه کد بالا باید تغییر کند اطلاعات حساب کاربری و تنظیمات SMTP است. توجه داشته باشید که نام کاربری و آدرس فرستنده در اینجا متفاوت هستند. در واقع میتوانید از هر آدرسی بعنوان آدرس فرستنده استفاده کنید.
public class EmailService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Credentials: var sendGridUserName = "yourSendGridUserName"; var sentFrom = "whateverEmailAdressYouWant"; var sendGridPassword = "YourSendGridPassword"; // Configure the client: var client = new System.Net.Mail.SmtpClient("smtp.sendgrid.net", Convert.ToInt32(587)); client.Port = 587; client.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network; client.UseDefaultCredentials = false; // Creatte the credentials: System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(credentialUserName, pwd); client.EnableSsl = true; client.Credentials = credentials; // Create the message: var mail = new System.Net.Mail.MailMessage(sentFrom, message.Destination); mail.Subject = message.Subject; mail.Body = message.Body; // Send: return client.SendMailAsync(mail); } }
آزمایش تایید حسابهای کاربری توسط سرویس ایمیل
ابتدا اپلیکیشن را اجرا کنید و سعی کنید یک حساب کاربری جدید ثبت کنید. دقت کنید که از آدرس ایمیلی زنده که به آن دسترسی دارید استفاده کنید. اگر همه چیز بدرستی کار کند باید به صفحه ای مانند تصویر زیر هدایت شوید.
همانطور که مشاهده میکنید پاراگرافی در این صفحه وجود دارد که شامل لینک فعالسازی است. این لینک صرفا جهت تسهیل کار توسعه دهندگان درج میشود و هنگام توزیع اپلیکیشن باید آن را حذف کنید. در ادامه به این قسمت باز میگردیم. در این مرحله ایمیلی حاوی لینک فعالسازی باید برای شما ارسال شده باشد.
پیاده سازی سرویس SMS
برای استفاده از احراز هویت دو مرحله ای پیامکی نیاز به یک فراهم کننده SMS دارید، مانند Twilio . مانند SendGrid این سرویس نیز در جامعه دات نت بسیار محبوب است و یک C# API قدرتمند ارائه میکند. میتوانید حساب کاربری رایگانی بسازید و شروع به کار کنید.
پس از ایجاد حساب کاربری یک شماره SMS، یک شناسه SID و یک شناسه Auth Token به شما داده میشود. شماره پیامکی خود را میتوانید پس از ورود به سایت و پیمایش به صفحه Numbers مشاهده کنید.
شناسههای SID و Auth Token نیز در صفحه Dashboard قابل مشاهده هستند.
اگر دقت کنید کنار شناسه Auth Token یک آیکون قفل وجود دارد که با کلیک کردن روی آن شناسه مورد نظر نمایان میشود.
حال میتوانید از سرویس Twilio در اپلیکیشن خود استفاده کنید. ابتدا بسته NuGet مورد نیاز را نصب کنید.
PM> Install-Package Twilio
public class SmsService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { string AccountSid = "YourTwilioAccountSID"; string AuthToken = "YourTwilioAuthToken"; string twilioPhoneNumber = "YourTwilioPhoneNumber"; var twilio = new TwilioRestClient(AccountSid, AuthToken); twilio.SendSmsMessage(twilioPhoneNumber, message.Destination, message.Body); // Twilio does not return an async Task, so we need this: return Task.FromResult(0); } }
حال که سرویسهای ایمیل و پیامک را در اختیار داریم میتوانیم احراز هویت دو مرحله ای را تست کنیم.
آزمایش احراز هویت دو مرحله ای
پروژه نمونه جاری طوری پیکربندی شده است که احراز هویت دو مرحله ای اختیاری است و در صورت لزوم میتواند برای هر کاربر بصورت جداگانه فعال شود. ابتدا توسط حساب کاربری مدیر، یا حساب کاربری ای که در قسمت تست تایید حساب کاربری ایجاد کرده اید وارد سایت شوید. سپس در سمت راست بالای صفحه روی نام کاربری خود کلیک کنید. باید صفحه ای مانند تصویر زیر را مشاهده کنید.
در این قسمت باید احراز هویت دو مرحله ای را فعال کنید و شماره تلفن خود را ثبت نمایید. پس از آن یک پیام SMS برای شما ارسال خواهد شد که توسط آن میتوانید پروسه را تایید کنید. اگر همه چیز بدرستی کار کند این مراحل چند ثانیه بیشتر نباید زمان بگیرد، اما اگر مثلا بیش از 30 ثانیه زمان برد احتمالا اشکالی در کار است.
حال که احراز هویت دو مرحله ای فعال شده از سایت خارج شوید و مجددا سعی کنید به سایت وارد شوید. در این مرحله یک انتخاب به شما داده میشود. میتوانید کد احراز هویت دو مرحله ای خود را توسط ایمیل یا پیامک دریافت کنید.
پس از اینکه گزینه خود را انتخاب کردید، کد احراز هویت دو مرحله ای برای شما ارسال میشود که توسط آن میتوانید پروسه ورود به سایت را تکمیل کنید.
حذف میانبرهای آزمایشی
همانطور که گفته شد پروژه نمونه شامل میانبرهایی برای تسهیل کار توسعه دهندگان است. در واقع اصلا نیازی به پیاده سازی سرویسهای ایمیل و پیامک ندارید و میتوانید با استفاده از این میانبرها حسابهای کاربری را تایید کنید و کدهای احراز هویت دو مرحله ای را نیز مشاهده کنید. اما قطعا این میانبرها پیش از توزیع اپلیکیشن باید حذف شوند.
بدین منظور باید نماها و کدهای مربوطه را ویرایش کنیم تا اینگونه اطلاعات به کلاینت ارسال نشوند. اگر کنترلر AccountController را باز کنید و به متد ()Register بروید با کد زیر مواجه خواهید شد.
if (result.Succeeded) { var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>"); // This should not be deployed in production: ViewBag.Link = callbackUrl; return View("DisplayEmail"); } AddErrors(result);
نمایی که این متد باز میگرداند یعنی DisplayEmail.cshtml نیز باید ویرایش شود.
@{ ViewBag.Title = "DEMO purpose Email Link"; } <h2>@ViewBag.Title.</h2> <p class="text-info"> Please check your email and confirm your email address. </p> <p class="text-danger"> For DEMO only: You can click this link to confirm the email: <a href="@ViewBag.Link">link</a> Please change this code to register an email service in IdentityConfig to send an email. </p>
متد دیگری که در این کنترلر باید ویرایش شود ()VerifyCode است که کد احراز هویت دو مرحله ای را به صفحه مربوطه پاس میدهد.
[AllowAnonymous] public async Task<ActionResult> VerifyCode(string provider, string returnUrl) { // Require that the user has already logged in via username/password or external login if (!await SignInHelper.HasBeenVerified()) { return View("Error"); } var user = await UserManager.FindByIdAsync(await SignInHelper.GetVerifiedUserIdAsync()); if (user != null) { ViewBag.Status = "For DEMO purposes the current " + provider + " code is: " + await UserManager.GenerateTwoFactorTokenAsync(user.Id, provider); } return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl }); }
همانطور که میبینید متغیری بنام Status به ViewBag اضافه میشود که باید حذف شود.
نمای این متد یعنی VerifyCode.cshtml نیز باید ویرایش شود.
@model IdentitySample.Models.VerifyCodeViewModel @{ ViewBag.Title = "Enter Verification Code"; } <h2>@ViewBag.Title.</h2> @using (Html.BeginForm("VerifyCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken() @Html.ValidationSummary("", new { @class = "text-danger" }) @Html.Hidden("provider", @Model.Provider) <h4>@ViewBag.Status</h4> <hr /> <div class="form-group"> @Html.LabelFor(m => m.Code, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.Code, new { @class = "form-control" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <div class="checkbox"> @Html.CheckBoxFor(m => m.RememberBrowser) @Html.LabelFor(m => m.RememberBrowser) </div> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" class="btn btn-default" value="Submit" /> </div> </div> }
در این فایل کافی است ViewBag.Status را حذف کنید.
از تنظیمات ایمیل و SMS محافظت کنید
در مثال جاری اطلاعاتی مانند نام کاربری و کلمه عبور، شناسههای SID و Auth Token همگی در کد برنامه نوشته شده اند. بهتر است چنین مقادیری را بیرون از کد اپلیکیشن نگاه دارید، مخصوصا هنگامی که پروژه را به سرویس کنترل ارسال میکند (مثلا مخازن عمومی مثل GitHub). بدین منظور میتوانید یکی از پستهای اخیر را مطالعه کنید.
صورت مساله
میخواهیم اطلاعات نمایش داده شده در گرید را به نحوی فرمت کنیم که
الف) اگر id ردیف مساوی 5 بود، رنگ و پس زمینهی آن تغییر کند.
ب) نام محصول، به جزئیات آن لینک شود و این اطلاعات توسط یک jQuery UI Dialog نمایش داده شود.
ج) عدد قیمت با سه رقم جدا کننده همراه باشد.
تکمیل مدل برنامه
مدل قسمت اول صرفا یک محصول بود. مدل قسمت جاری، اطلاعات تولید/تامین کننده آنرا توسط کلاس Supplier نیز به همراه دارد:
namespace jqGrid02.Models { public class Product { public int Id { set; get; } public string Name { set; get; } public decimal Price { set; get; } public Supplier Supplier { set; get; } } public class Supplier { public int Id { set; get; } public string CompanyName { set; get; } public string Address { set; get; } public string PostalCode { set; get; } public string City { set; get; } public string Country { set; get; } public string Phone { set; get; } public string HomePage { set; get; } } }
کدهای سمت سرور
کدهای سمت سرور مانند متد GetProducts به همراه صفحه بندی و مرتب سازی پویای آن دقیقا مانند قسمت قبل است.
در اینجا فقط یک اکشن متد جدید جهت بازگشت اطلاعات تولید کنندهای مشخص با فرمت JSON، اضافه شدهاست:
public ActionResult GetGetSupplierData(int id) { var list = ProductDataSource.LatestProducts; var product = list.FirstOrDefault(x => x.Id == id); if (product == null) return Json(null, JsonRequestBehavior.AllowGet); return Json(new { product.Supplier.CompanyName, product.Supplier.Address, product.Supplier.PostalCode, product.Supplier.City, product.Supplier.Country, product.Supplier.Phone, product.Supplier.HomePage }, JsonRequestBehavior.AllowGet); }
کدهای سمت کلاینت
صفحه دیالوگی که قرار است اطلاعات تولید کننده را نمایش دهد، یک چنین ساختاری دارد:
<div dir="rtl" id="supplierDialog"> <span id="CompanyName"></span><br /><br /> <span id="Address"></span><br /> <span id="PostalCode"></span>, <span id="City"></span><br /> <span id="Country"></span><br /><br /> <span id="Phone"></span><br /> <span id="HomePage"></span> </div>
<script type="text/javascript"> function showSupplierDialog(linkElement, supplierId) { //request json data $.getJSON('@Url.Action("GetGetSupplierData","Home")', { id: supplierId }, function (data) { //set values in dialog for (var property in data) { if (data.hasOwnProperty(property)) { $('#' + property).text(data[property]); } } //get link position var linkPosition = $(linkElement).offset(); $('#supplierDialog').dialog('option', 'position', [linkPosition.left, linkPosition.top]); //open dialog $('#supplierDialog').dialog('open'); }); } $(document).ready(function () { $('#supplierDialog').dialog({ autoOpen: false, bgiframe: true, resizable: false, title: 'تولید کننده' }); $('#list').jqGrid({ // .... مانند قبل colNames: ['شماره', 'نام محصول', 'قیمت'], //columns model colModel: [ { name: 'Id', index: 'Id', align: 'right', width: 20, formatter: function (cellvalue, options, rowObject) { var cellValueInt = parseInt(cellvalue); if (cellValueInt == 5) { return "<span style='background: brown; color: yellow'>" + cellvalue + "</span>"; } return cellvalue; } }, { name: 'Name', index: 'Name', align: 'right', width: 300, formatter: function (cellvalue, options, rowObject) { return "<a href='#' onclick='showSupplierDialog(this, " + rowObject[0] + ");'>" + cellvalue + "</a>"; } }, { name: 'Price', index: 'Price', align: 'center', width: 50, formatter: 'currency', formatoptions: { decimalSeparator: '.', thousandsSeparator: ',', decimalPlaces: 2, prefix: '$' } } ], // .... مانند قبل }); }); </script>
در حالت ستون Id، از یک formatter سفارشی استفاده شدهاست. در اینجا این فرمت کننده به صورت یک callback عمل کرده و پیش از رندر نهایی اطلاعات، مقدار سلول جاری را توسط cellvalue در اختیار ما قرار میدهد. در این بین هر نوع فرمتی را که نیاز است میتوان اعمال کرد و سپس یک رشته را بازگشت میدهیم. این رشته در سلول جاری درج خواهد شد.
- اگر مانند ستون Name، نیاز به مقادیر سایر سلولها نیز وجود داشت، میتوان از آرایهی rowObject استفاده کرد. برای مثال در این حالت، یک لینک که کلیک بر روی آن سبب فراخوانی تابع showSupplierDialog میشود، در سلولهای ستون Name درج خواهند شد. اولین rowObject که در اینجا مورد استفاده است، به ستون اول یا همان Id محصول اشاره میکند.
- در ستون Price از یک سری formatter از پیش تعریف شده استفاده شدهاست. نمونهای از آن را در قسمت اول در ستون نمایش وضعیت موجود بودن محصول با تنظیم formatter: checkbox مشاهده کردهاید. در اینجا از یک formatter توکار دیگر به نام currency برای کار با مقادیر پولی استفاده شدهاست به همراه تنظیمات خاص آن.
- متد showSupplierDialog طوری تنظیم شدهاست که پس از دریافت Id یک محصول، آنرا به سرور ارسال کرده و مشخصات تولید کنندهی آنرا با فرمت JSON دریافت میکند. سپس در حلقهای که مشاهده میکنید، خواص شیء جاوا اسکریپتی دریافتی استخراج و به spanهای supplierDialog انتساب داده میشوند. جهت سهولت کار، Id این spanها دقیقا مساوی Id خواص شیء دریافتی از سرور، درنظر گرفته شدهاند.
- در مورد راست به چپ نمایش داده شدن عنوان دیالوگ، تغییرات CSS ایی لازم است که در قسمت اول بیان شدند.
برای مطالعه بیشتر
لیست کامل فرمت کنندههای توکار
فرمت کنندههای سفارشی
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید
jqGrid02.zip
ایجاد یک موتور جستجوی سفارشی جهت search bar فایرفاکس
- Adblock Plus 2.1.2 http://adblockplus.org/en/ - Add to Search Bar 2.0 http://firefox.maltekraus.de/extensions/add-to-search-bar - Calculator 1.1.27 http://code.google.com/p/firefoxcalculator/ - CodeBurner for Firebug 1.6 http://tools.sitepoint.com/codeburner - ColorZilla 2.8.1 http://www.colorzilla.com/ - Cookies Manager+ 1.5.1 https://addons.mozilla.org/en-US/firefox/addon/cookies-manager-plus/ - CSS Usage 0.2.9 http://spaghetticoder.org/cssusage/ - DOM Inspector 2.0.12 http://www.mozilla.org/projects/inspector/ - Extension List Dumper 1.15.2 http://www.sogame.cat/ - File Title 1.4 http://www.jasnapaka.com/mozilla/filetitle/ - Firebug 1.10.2 http://www.getfirebug.com/ - FireDiff 1.2.0 http://www.incaseofstairs.com/firediff - Firefinder for Firebug 1.04 http://robertnyman.com/firefinder/ - FireFTP 2.0.7 http://fireftp.mozdev.org - FireQuery 1.2 http://firequery.binaryage.com - Forecastfox 2.2.1 http://www.getforecastfox.com/ - Greasemonkey 1.0 http://www.greasespot.net/ - HighlightAll 1.6 http://jgoudey.free.fr/highlightall/ - HttpFox 0.8.11 http://code.google.com/p/httpfox/ - IranianCalendar 3.1.20081222 http://www.alavi.us/jcal/ - Linky 3.0.0 http://gemal.dk/mozilla/linky.html - MeasureIt 0.4.10 http://frayd.us/ - Open With 5.2.1 http://www.darktrojan.net/software/addons/openwith/ - PDF Download 3.0.0.2 http://www.pdfdownload.org - Pixel Perfect 1.7.1 http://www.pixelperfectplugin.com/ - Pray Times! 1.1.7 http://praytimes.org/ - Proxy Selector 0.80.7 http://addons.mozilla.org - Right-Click-Link 1.1.5 http://rickardandersson.com/ - SearchPreview 5.9 http://searchpreview.de/ - Server Spy 0.2.1 http://www.jacquet80.eu/mozilla/exts/ServerSpy/ - Session Manager 0.7.9 http://sessionmanager.mozdev.org/ - Show MyIP 0.8 http://www.tsql.de/download/firefox-addon-ip-adresse-ip-address - Show Picture 2.7 https://addons.mozilla.org/en-US/firefox/addon/show-picture/?src=api - Showcase 0.9.5.8 http://showcase.uworks.net/ - Snap Links Plus 2.2.1 http://snaplinks.mozdev.org/ - Sothink Web Video Downloader for Firefox 6.8 http://www.web-video-downloader.com - Speed Dial 0.9.6.8 http://speeddial.uworks.net/ - SQLite Manager 0.7.7 http://sqlite-manager.googlecode.com/ - Stylish 1.2.6 http://userstyles.org/ - Subtitle Matcher 0.3.4 http://ds6.ovh.org/hashsubtitles/subtitle_matcher_firefox_extensions.php - Tilt 1.0.1 http://blog.mozilla.com/tilt/ - UnMHT 5.7.5 http://www.unmht.org/unmht/en_index.html - UnPlug 2.050 http://unplug.dbatley.com/ - User Agent Switcher 0.7.3 http://chrispederick.com/work/user-agent-switcher/ - View Source Chart 3.05 http://zigrat.com - Viewstate Size 1.0.4 http://www.kirkov.dk/projects/firefox/viewstatesize - Web Developer 1.2.1 http://chrispederick.com/work/web-developer/firefox/ - YSlow 3.1.4 http://developer.yahoo.com/yslow
پایان پروژه ASP.NET Ajax Control Toolkit !
هدف مایکروسافت از اینکار مدیریت هر دو پروژه MVC و Web forms است آن هم با هزینه کم، کیفیت بالا و سازگار با تمام مرورگرها.
ضمنا این رو در نظر باشید که یکی از توانمندیهای jQuery، Ajax است (از کار با DOM گرفته تا دستکاری CSS نمایش داده شده، تا Animation ، مدیریت سادهتر رخدادها، اعمال قالب به سایت و غیره) و این مورد مزیت مهمی است نسبت به تمام کتابخانههایی که فقط برای یک کار و آن هم سهولت تولید برنامههای مبتنی بر Ajax ایجاد شدهاند و از چند مشکل مهم رنج میبرند:
- تک کارهاند. فقط Ajax .
- مشکل سازگاری با مرورگرهای مختلف را دارند.
- به صورت فعال توسعه داده نمیشوند؛ رفع باگ نمیشوند و غیره. برای مثال فواصل به روز رسانی همان Anthem.Net را بررسی کنید.
- توسعه پذیر نیستند. برای مثال آیا میتوان برای Anthem.Net افزونه نوشت؟
- حجم بالایی دارند.
- سرعت پایینی دارند.
jQuery در مورد تمام موارد عنوان شده حرف برای گفتن دارد از حجم کم تا سرعت بالاتر نسبت به اکثر کتابخانههای جاوا اسکریپتی دیگر تا توسعهی منظم، سازگاری عالی با مرورگرها، توسعه پذیری و صدها و هزاران افزونهی مهیا برای آن و غیره.
و RAD هزینه بر است. یعنی چی؟ یعنی حجم بالای کدهای اسکریپتی که باید به برنامهی شما مثلا توسط ASP.NET Ajax تزریق شود که مبادا شما بخواهید دست خودتان را به نوشتن چند سطر کد جاوا اسکریپتی آلوده کنید. همچنین حجم تبادل اطلاعات ASP.NET Ajax را هم که مبتنی است بر RAD را هم با حجم اطلاعات مبادله شده توسط jQuery مقایسه کنید (با پلاگین فایرباگ مربوط به فایرفاکس). این حجم واقعا زیاد است و قابل مقایسه نیست. (تمام اینها هزینههای RAD است)
ضمنا وجود 100 ها افزونه و پلاگین نوشته شده برای jQuery کار شما را بسیار بسیار ساده میکنند، مانند پاسخ قبلی من در این مطلب.
فقط باید کمی وقت بگذارید و چیزی را بیاموزید که واقعا ارزش دارد. گیرم فردا نخواستید با ASP.NET کار کنید. تمام این اطلاعات در PHP هم به درد شما میخورد، چون قسمت سمت کلاینت آنچنان تفاوتی نمیکند و jQuery یک کتابخانهی سمت کلاینت است.
- Observable state: در MobX نیز همانند Redux، کل شیء state به صورت یک شیء جاوا اسکریپتی ارائه میشود؛ با این تفاوت که در اینجا این شیء، یک Observable است که نمونهای از مفهوم آنرا در مثال قسمت قبل بررسی کردیم.
- Actions: متدهایی هستند که state را تغییر میدهند.
- Computed properties: در مورد مفهوم خواص محاسباتی در قسمت قبل بحث کردیم. این خواص، مقدار خود را بر اساس تغییرات سایر خواص Observable، به روز میکنند.
- Reactions: سبب بروز اثرات جانبی (side effects) میشوند؛ مانند تعامل با دنیای خارج. نمونهای از آن، متد autorun است که تغییرات Observableها را ردیابی میکند.
برای مثال خاصیت محاسباتی fullName، تغییرات سایر خواص Observable را احساس کرده و مقدار خودش را به روز میکند. سپس یک Reaction به آن، میتواند به روز رسانی DOM، جهت نمایش این تغییرات باشد و یا نمونهی دیگری که میتواند بسیاری از این مفاهیم را نمایش دهد، کلاس زیر است:
import { action, observable, computed } from 'mobx'; class PizzaCalculator { @observable numberOfPeople = 0; @observable slicesPerPerson = 2; @observable slicesPerPie = 8; @computed get slicesNeeded() { console.log('Getting slices needed'); return this.numberOfPeople * this.slicesPerPerson; } @computed get piesNeeded() { console.log('Getting pies needed'); return Math.ceil(this.slicesNeeded / this.slicesPerPie); } @action addGuest() { this.numberOfPeople!++; } }
- برای مثال زمانیکه تعریف observable numberOfPeople@ را داریم، به این معنا است که میخواهیم تغییرات تعداد افراد را تحت نظر قرار دهیم و اگر تغییری در مقدار آن صورت گرفت، آنگاه مقدار خواص محاسباتی که با computed@ مزین شدهاند، به صورت خودکار به روز رسانی شوند.
- action@ به این معنا است که متدی در اینجا، سبب بروز تغییری در state کلاس جاری میشود. MobX به همراه یک strict mode است که اگر فعال باشد، ذکر تزئین کنندهی action@ بر روی یک چنین متدهایی ضروری است، در غیراینصورت، الزامی به درج آن نیست.
در این قطعه کد تعدای console.log را هم ملاحظه میکنید. علت آن نمایش مفهوم کش کردن اطلاعات در MobX است. فرض کنید برای بار اول، مقدار یکی از خواصی را که به صورت observable تعریف شدهاند، تغییر میدهیم. در این حالت تمام خواص محاسباتی وابستهی به آنها، به صورت خودکار مجددا محاسبه شده و console.logها را نیز مشاهده خواهیم کرد. اگر برای بار دوم همین فراخوانی صورت گیرد و تغییری در مقادیر خواص observable صورت نگیرد، MobX از نگارش کش شدهی این خواص محاسباتی استفاده میکند و بیجهت سبب رندر مجدد UI نخواهد شد که در نهایت کارآیی بالایی را سبب خواهد شد. برای پیاده سازی یک چنین قابلیتی با Redux باید از مفهومی مانند React.memo و Memoization و کتابخانههای کمکی مانند Reselect استفاده کرد؛ اما در اینجا به صورت توکار و خودکار اعمال میشود.
ساختارهای دادهای که توسط MobX پشتیبانی میشوند
MobX از اکثر ساختارهای دادهای متداول در جاوا اسکریپت پشتیبانی میکند؛ برای مثال:
- اشیاء مانند ({})observable
- آرایهها مانند ([])observable
- Maps مانند observable(new Map())
چند نکته:
- همانطور که در قسمت قبل نیز ذکر شد، decorators در اصل یکسری تابع هستند و برای مثال میتوان observable را به صورت observable@ و یا به صورت یک تابع معمولی مورد استفاده قرار داد.
- اگر شیءای را به صورت ({})observable معرفی کنیم، با افزودن خواصی به آن پس از این فراخوانی، این خواص دیگر مورد ردیابی قرار نخواهند گرفت. علت آنرا هم در شبهکد زیر میتوان مشاهده کرد:
const extendObservable = (target, source) => { source.keys().forEach(key => { const wrappedInObservable = observable(source[key]); Object.defineProperty(target, key, { set: value.set. get: value.get }); }); };
برای رفع این مشکل میتوان از Map استفاده کرد. یعنی در اینجا اگر قرار است تعداد خواص اشیاء را به صورت پویا تغییر دهید، آنها را به صورت Map تعریف کنید؛ چون متد set آن توسط observableها ردیابی میشود.
استفاده از MobX با React توسط کتابخانهی mobx-react
تا اینجا MobX را به صورت متکی به خود مورد بررسی قرار دادیم. اکنون قصد داریم آنرا به یک برنامهی React متصل کنیم. برای اینکار کتابخانههای زیادی وجود دارند که در این قسمت کلیات روش کار با کتابخانهی mobx-react را در بین آنها بررسی میکنیم.
نصب کتابخانهی mobx-react
ابتدا نیاز است تا این کتابخانه را نصب کنیم:
> npm install --save mobx mobx-react
تحت نظر قرار دادن کامپوننتها
در ادامه پس از نصب کتابخانهی mobx-react، نیاز است کامپوننتها را تحت نظر MobX قرار دهیم که اینکار را میتوان توسط تزئین کنندهی observer آن انجام داد. همانطور که عنوان شد، تزئین کنندهها را میتوان به صورت معمولی observer@ به یک کلاس و یا به صورت فراخوانی تابع، برای مثال به یک کامپوننت تابعی اعمال کرد. برای نمونه کامپوننتهای کلاسی را به نحو زیر میتوان با observer@ مزین کرد:
import { observer } from "mobx-react"; @observer class Counter extends Component {
و یا کامپوننتهای تابعی را میتوان توسط متد observer به صورت زیر محصور کرد:
const Counter = observer(({ count }) => { return ( // ... ); });
class ContainerComponent extends Component () { componentDidMount() { this.stopListening = autorun(() => this.render()); } componentWillUnmount() { this.stopListening(); } render() { … } }
تعریف مخزن و اتصال آن به کامپوننتها
کار شیء Provider که بالاترین کامپوننت را در سلسله مراتب کامپوننتها محصور میکند، ارائهی store، به تمام کامپوننتهای فرزند است. در Redux فقط یک store را داریم که به شیء Provider آن ارسال میکنیم. اما در حین کار با MobX چنین محدودیتی وجود ندارد و میتوان چندین store را تعریف کرد و در اختیار برنامه قرار داد که شبهکد نحوهی تعریف آن به صورت زیر است:
import { Provider } from 'mobx-react'; import ItemStore from './store/ItemStore'; import Application from './components/Application'; const itemStore = new ItemStore(); ReactDOM.render( <Provider itemStore={itemStore}> <Application /> </Provider>, document.getElementById('root'), );
@inject('itemStore') class NewItem extends Component { // ...
const UnpackedItems = inject('itemStore')( observer(({ itemStore }) => ( // ... )), );
یک مثال: پیاده سازی مثال شمارشگر قسمت سوم این سری با mobx-react
در ادامه قصد داریم برنامهی شمارشگر ارائه شده در قسمت سوم بررسی redux را با mobx پیاده سازی کنیم. به همین جهت یک پروژهی جدید React را ایجاد میکنیم:
> create-react-app state-management-with-mobx-part2 > cd state-management-with-mobx-part2 > npm start
> npm install --save mobx mobx-react bootstrap
import "bootstrap/dist/css/bootstrap.css";
پس از آن فایل src\index.js را به صورت زیر تغییر میدهیم:
import "./index.css"; import "bootstrap/dist/css/bootstrap.css"; import { autorun, decorate, observable } from "mobx"; import React from "react"; import ReactDOM from "react-dom"; import Counter from "./components/Counter"; import * as serviceWorker from "./serviceWorker"; class Count { value = 0; increment = () => { this.value++; }; decrement = () => { this.value--; }; } decorate(Count, { value: observable }); const count = (window.count = new Count()); autorun(() => console.log("The count changed!", count.value)); ReactDOM.render( <main className="container"> <Counter count={count} /> </main>, document.getElementById("root") ); serviceWorker.unregister();
- در قسمت قبل، روش تحت نظر قرار دادن یک شیء متداول جاوا اسکریپتی را توسط متد observable مشاهده کردیم. در اینجا نگارش کلاسی آن مثال را بر اساس کلاس Count مشاهده میکنید. اگر نخواهیم از decorator ای مانند observable@ بر روی خاصیت value این کلاس استفاده کنیم، روش تابعی آنرا با فراخوانی متد decorate و ذکر نوع کلاس و سپس خاصیتی که باید به صورت observable تحت نظر قرار گیرد، در اینجا مشاهده میکنید. این هم یک روش کار با mobx است.
- پس از ایجاد کلاس Count که در اینجا نقش store را نیز بازی میکند، یک وهلهی جدید را از آن ساخته و به متغیر count در این ماژول و همچنین window.count انتساب میدهیم. انتساب window.count سبب میشود تا بتوان در کنسول توسعه دهندگان مرورگر، با نوشتن count و سپس enter، به محتویات این متغیر دسترسی یافت و یا حتی آنرا تغییر داد؛ مانند تصویر زیر که بلافاصله این تغییر، در UI برنامه نیز منعکس میشود:
- در اینجا تعریف شیء Provider را که پیشتر در مورد آن بحث کردیم، مشاهده نمیکنید؛ چون با تک کامپوننت Counter تعریف شدهی در این مثال، نیازی به آن نیست. میتوان این شیء store را به صورت مستقیم به props کامپوننت Counter ارسال کرد.
اکنون تعریف کامپوننت شمارشگر واقع در فایل src\components\Counter.jsx به صورت زیر خواهد بود که این کامپوننت، count را به صورت props دریافت میکند:
import { observer } from "mobx-react"; import React from "react"; const Counter = observer(({ count }) => { return ( <section className="card mt-5"> <div className="card-body text-center"> <span className="badge m-2 badge-primary">{count.value}</span> </div> <div className="card-footer"> <div className="d-flex justify-content-center align-items-center"> <button className="btn btn-secondary btn-sm" onClick={count.increment} > + </button> <button className="btn btn-secondary btn-sm m-2" onClick={count.decrement} > - </button> </div> </div> </section> ); }); export default Counter;
تا زمانیکه کامپوننت، با تابع observer محصور شدهاست، به props رسیده گوش فرا داده و خواص و اشیاء observable آنرا تشخیص میدهد و سبب رندر مجدد کامپوننت، با تغییری در آنها خواهد شد.
کدهای کامل این قسمت را میتوانید از اینجا دریافت کنید: state-management-with-mobx-part2.zip
mklink /j C:\android-sdk "C:\Program Files (x86)\Android\android-sdk"
<AotAssemblies>true</AotAssemblies> <EnableLLVM>true</EnableLLVM>
پارامترها در ES 6
Destructuring Arrays
بدون استفاده از Destructuring assignment برای دسترسی به اعضای یک آرایه و انتساب آنها به متغیرهای مختلف، روش متداول زیر مرسوم است:
var first = someArray[0]; var second = someArray[1]; var third = someArray[2];
var [first, second, third] = someArray;
یک مثال:
let [one, two, three] = ['globin', 'ghoul', 'ghost', 'white walker']; console.log(`one is ${one}, two is ${two}, three is ${three}`) // => one is globin, two is ghoul, three is ghost
همچنین در این مثال اگر علاقمند بودیم صرفا به اعضای اول و چهارم این آرایه دسترسی پیدا کنیم، میتوان نوشت:
let [firstMonster, , , fourthMonster] = ['globin', 'ghoul', 'ghost', 'white walker']; console.log(`the first monster is ${firstMonster}, the fourth is ${fourthMonster}`) // => one is globin, two is ghoul, three is ghost
امکان دسترسی به اعضای تو در تو نیز با Destructuring assignment پیش بینی شدهاست:
let nested = [1, [2, 3], 4]; let [a, [b], d] = nested; console.log(a); // 1 console.log(b); // 2 console.log(d); // 4
میتوان از Destructuring assignment جهت جابجایی مقادیر متغیرها بدون انتساب به یک متغیر موقتی نیز استفاده کرد:
let point = [1, 2]; let [xVal, yVal] = point; [xVal, yVal] = [yVal, xVal]; console.log(xVal); // 2 console.log(yVal); // 1
Destructuring Objects
امکانات Destructuring assignment، به کار با آرایهها محدود نمیشود و از آن میتوان برای کار با اشیاء نیز استفاده کرد. فرض کنید شیء pouch به صورت زیر تعریف شدهاست:
let pouch = {coins: 10};
let coins = pouch.coins;
let {coins} = pouch;
let pouch = {coins: 10}; let {coins: newVar1 } = pouch; console.log(newVar1); //10
در اینجا نیز امکان کار با اشیای تو در تو، پیش بینی شدهاست:
let point = { x: 1, y: 2, z: { one: 3, two: 4 } }; let { x: a, y: b, z: { one: c, two: d } } = point; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3 console.log(d); // 4
در انتساب فوق، خاصیت x شیء point به متغیر جدید a، خاصیت y شیء point به متغیر جدید b و خاصیت one شیء منتسب به خاصیت z، به متغیر c و خاصیت two شیء منتسب به خاصیت z، به متغیر d انتساب یافتهاند.
ترکیب Destructuring Objects و Destructuring Arrays
در مثال زیر، نمونهای ترکیبی از Destructuring اشیاء و آرایهها را با هم مشاهده میکنید:
let mixed = { one: 1, two: 2, values: [3, 4, 5] }; let { one: a, two: b, values: [c, , e] } = mixed; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3 console.log(e); // 5
Destructuring Function Arguments
از Destructuring در حین تعریف پارامترهای متدها نیز میتوان استفاده کرد.
function removeBreakpoint({ url, line, column }) { // ... }
و یا برای مثال در زبان #C امکان تعریف named arguments (آرگومانهای نامدار) و همچنین تعریف مقادیر پیش فرضی برای آنها وجود دارد. در اینجا نیز میتوان با استفاده از Destructuring به تعریفی مشابه آن برای ارائهی آرگومانهایی با مقادیر پیش فرض رسید:
function random ({ min=1, max=300 }) { return Math.floor(Math.random() * (max - min)) + min } console.log(random({})) // <- 174 console.log(random({max: 24})) // <- 18
و یا اینبار jQuery Ajax را میتوان با پارامترهای پیش فرض آن به صورت ذیل خلاصه نویسی کرد:
jQuery.ajax = function (url, { async = true, beforeSend = noop, cache = true, complete = noop, crossDomain = false, global = true, // ... more config }) { // ... do stuff };
function returnMultipleValues() { return [1, 2]; } var [foo, bar] = returnMultipleValues();
شبیه به همین مورد در حین کار با اشیاء نیز میسر است:
function returnMultipleValues() { return { foo: 1, bar: 2 }; } var { foo, bar } = returnMultipleValues();
تعریف مقادیر پیش فرض در حین Destructuring
در انتساب ذیل، چون شیء سمت راست، دارای خاصیت foo نیست، مقدار این پارامتر جدید undefined خواهد بود. برای رفع این مشکل میتوان به آن مقدار پیش فرضی را نیز نسبت داد:
var {foo=3} = { bar: 2 } console.log(foo) // <- 3
اگر مقدار پیش فرض، ذکر شود و خاصیت متناظر با آن دارای مقدار باشد، از همان مقدار اصلی ذکر شده استفاده میشود:
var {foo=3} = { foo: 2 } console.log(foo) // <- 2
var {foo=3} = { foo: undefined } console.log(foo) // <- 3
var [b=10] = [undefined] console.log(b) // <- 10 var [c=10] = [] console.log(c) // <- 10
ES6 — default + rest + spread
علاوه بر destructuring، سه قابلیت و بهبود دیگر نیز در زمینهی کار با متغیرها و پارامترها به ES 6 اضافه شدهاند:
1) امکان تعریف مقادیر پیش فرض پارامترها
function inc(number, increment) { increment = increment || 1; return number + increment; } console.log(inc(2, 2)); // 4 console.log(inc(2)); // 3
function inc(number, increment = 1) { return number + increment; } console.log(inc(2, 2)); // 4 console.log(inc(2)); // 3
function sum(a, b = 2, c) { return a + b + c; } console.log(sum(1, 5, 10)); // 16 -> b === 5 console.log(sum(1, undefined, 10)); // 13 -> b as default
function getDefaultIncrement() { return 1; } function inc(number, increment = getDefaultIncrement()) { return number + increment; } console.log(inc(2, 2)); // 4 console.log(inc(2)); // 3
2) Spread
متد جمع زیر را درنظر بگیرید:
function sum(a, b, c) { return a + b + c; }
var args = [1, 2, 3]; console.log(sum(…args)); // 6
var args = [1, 2]; console.log(sum(…args, 3)); // 6
مثالهایی از ساده سازی اعمال متداول در ES 5 (جاوا اسکریپت فعلی) با کمک ES 6:
الف) ترکیب spread و Destructuring
a = list[0], rest = list.slice(1)
[a, ...rest] = list
ب) ساده سازی کار با concat
بجای
[1, 2].concat(more)
[1, 2, ...more]
ج) افزودن یک رنج به یک آرایه
بجای
list.push.apply(list, [3, 4])
list.push(...[3, 4])
3) Rest
جاوا اسکریپت دارای شیءایی است به نام arguments که توسط آن میتوان به لیست پارامترهای یک متد دسترسی یافت. برای نمونه مثال ذیل را درنظر بگیرید:
function sum() { var numbers = Array.prototype.slice.call(arguments), result = 0; numbers.forEach(function (number) { result += number; }); return result; }
console.log(sum(1)); // 1 console.log(sum(1, 2, 3, 4, 5)); // 15
function sum(…numbers) { var result = 0; numbers.forEach(function (number) { result += number; }); return result; } console.log(sum(1)); // 1 console.log(sum(1, 2, 3, 4, 5)); // 15
در اینجا باید دقت داشت که پس از ذکر rest، دیگر نمیتوان پارامتری را تعریف کرد:
function sum(…numbers, last) { // causes a syntax error