اشتراک‌ها
اشیاء انتزاعی یا Abstract Objects

شیء انتزاعی یا Abstract Object توصیفی از شی‌ء واقعی بدون در نظر گرفتن جزئیات آن شی است. به عنوان مثال ما برای شی خودرو خصوصیات رنگ، چرخ ...

اشیاء انتزاعی یا Abstract Objects
اشتراک‌ها
تست میزان امنیت پسوردها
نکته جالب تو این وب سایت
1.تغییر رنگ پشت زمینه (متناسب با سطح امنیت) 2.پیغام Infinity years برای پسورد‌های تخیلی ...
تست میزان امنیت پسوردها
نظرات مطالب
EF Code First #10
البته فضای نام  System.Data.Entity به رنگ خاکستری در اومده ، یعنی عملا هیچ استفاده ای از اون نمیشه.
آیا این فضای نام ارتباطی با ورژن EF و NET 4.5. نداره؟
نظرات مطالب
EF Code First #10
استاد این Alert ها (دایره های خاکستری رنگ) هم خیلی مهم هستند؟ آلرتش هم Unbound result set هست که تقریباً برای بیشتر Entity هام همینه.
مطالب
ASP.NET Web API - قسمت سوم
در قسمت اول به دلایل ایجاد Web API پرداخته شد و در قسمت دوم مثالی ساده از Web API را بررسی کردیم. در این قسمت، مثال قبل را تست کرده و نحوه‌ی تعامل jQuery با آن را بررسی می‌کنیم.

فراخوانی Web API از طریق مرورگر

با فشردن کلید F5، پروژه را اجرا کنید. شکل ذیل ظاهر می‌شود.


صفحه ای که ظاهر می‌شود، یک View است که توسط HomeController و متد Index آن برگشت داده شده است. برای فراخوانی متدهای موجود در کلاس Controller مثال قسمت قبل که مربوط به Web API است، باید به یکی از آدرس‌های اشاره شده در قسمت قبل برویم. به عنوان مثال، برای به دست آوردن لیست تمامی محصولات، به آدرس http://localhost:xxxx/api/products بروید. xxxx، شماره‌ی پورتی است که Web Server داخلی Visual Studio در هنگام اجرای پروژه به آن اختصاص می‌دهد. آن را نسبت به پروژه‌ی خود تغییر دهید.
نتیجه‌ی دریافتی بستگی به نوع مرورگری دارد که استفاده می‌کنید. Internet Explorer از شما در مورد باز کردن یا ذخیره‌ی فایلی با نام products پرسش می‌کند (شکل ذیل).


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

[{"Id":1,"Name":"Tomato soup","Category":"Groceries","Price":1.39},{"Id":2,"Name":
"Yo-yo","Category":"Toys","Price":3.75},{"Id":3,"Name":"Hammer","Category":
"Hardware","Price":16.99}]
اما مرورگر Firefox، محصولات را در قالب XML نشان می‌دهد (شکل ذیل).


دلیل تفاوت در نتیجه‌ی دریافتی این است که مرورگر Internet Explorer و Firefox، هر یک مقدار متفاوتی را در هدر Accept درخواست، ارسال می‌کنند. بنابراین، Web API نیز مقدار متفاوتی را در پاسخ برگشت می‌دهد.

حال به آدرس‌های ذیل بروید: 

http://localhost:xxxx/api/products/1
http://localhost:xxxx/api/products?category=hardware

اولین آدرس، باید محصولی با مشخصه‌ی 1 را برگشت دهد و دومین آدرس، لیستی از تمامی محصولاتی که در دسته‌ی hardware قرار دارند را برگشت می‌دهد (در مثال ما فقط یک آیتم این شرط را دارد).

نکته: در صورتی که در هنگام فراخوانی هر یک از متدهای Web API با خطای ذیل مواجه شدید، دستور [("AcceptVerbs("GET", "POST] را به ابتدای متدها اضافه کنید.

The requested resource does not support http method 'GET'
 

فراخوانی Web API با استفاده از کتابخانه‌ی jQuery

در قسمت قبل، متدهای Web API را مستقیماً از طریق وارد کردن آدرس آنها در نوار آدرس مرورگر فراخوانی کردیم. اما در اکثر اوقات، این متدها با روش‌های برنامه نویسی توسط یک Client فراخوانی می‌شوند. اجازه بدهید Clientیی ایجاد کنیم که با استفاده از jQuery، متدهای ما را فراخوانی می‌کند.
در Solution Explorer، از پوشه‌ی Views و سپس Home، فایل Index.cshtml را باز کنید.

تمامی محتویات این View را حذف و کدهای ذیل را در آن قرار دهید.  

<!DOCTYPE html>
<html>
<head>
    <title>ASP.NET Web API</title>
    <script src="../../Scripts/jquery-1.7.2.min.js" 
        type="text/javascript"></script>
</head>
<body>
    <div>
        <h1>All Products</h1>
        <ul id='products' />
    </div>
    <div>
        <label for="prodId">ID:</label>
        <input type="text" id="prodId" size="5"/>
        <input type="button" value="Search" onclick="find();" />
        <p id="product" />
    </div>
</body>
</html>


بازیابی لیستی از محصولات

برای بازیابی لیستی از محصولات، فقط کافی است تا یک درخواست از نوع GET به آدرس "/api/products" بفرستید. این کار با jQuery به صورت ذیل انجام می‌شود. 

<script type="text/javascript">
    $(document).ready(function () {
        // Send an AJAX request
        $.getJSON("api/products/",
        function (data) {
            // On success, 'data' contains a list of products.
            $.each(data, function (key, val) {

                // Format the text to display.
                var str = val.Name + ': $' + val.Price;

                // Add a list item for the product.
                $('<li/>', { html: str })    
                .appendTo($('#products'));   
            });
        });
    });
</script>

متد getJSON، یک درخواست AJAX از نوع GET را ارسال می‌کند و پاسخ دریافتی آن نیز با فرمت JSON خواهد بود. دومین پارامتر متد getJSON، یک callback است که پس از دریافت موفقیت آمیز پاسخ اجرا می‌شود.


بازیابی یک محصول با استفاده از مشخصه‌ی آن

برای بازیابی یک محصول با استفاده از مشخصه‌ی آن، یک درخواست از نوع GET به آدرس "api/products/id/" ارسال کنید. id، مشخصه‌ی محصول است. کد ذیل را در ادامه‌ی کد قبل و پیش از تگ <script/> قرار دهید.

function find() {
    var id = $('#prodId').val();
    $.getJSON("api/products/" + id,
        function (data) {
            var str = data.Name + ': $' + data.Price;
            $('#product').html(str);
        })
    .fail(
        function (jqXHR, textStatus, err) {
            $('#product').html('Error: ' + err); 
        });
}


باز هم از متد getJSON استفاده کردیم، اما این بار مقدار id برای آدرس از یک Text Box خوانده و آدرس ایجاد می‌شود. پاسخ دریافتی، یک محصول در قالب JSON است.


اجرای پروژه

پروژه را با فشردن کلید F5 اجرا کنید. پس از نمایش فرم، تمامی محصولات بر روی صفحه نمایش داده می‌شوند. عدد 1 را وارد و بر روی دکمه‌ی Search کلیک کنید، محصولی که مشخصه‌ی آن 1 است نمایش داده می‌شود (شکل ذیل).

اگر مشخصه ای را وارد کنید که وجود ندارد، خطای 404 با مضمون "Error: Not Found" بر روی صفحه نمایش داده می‌شود و در صورتی که به جای عدد، عبارتی غیر عددی وارد کنید، خطای 400 با مضمون: "Error: Bad Request" نمایش داده می‌شود. در Web API، تمامی پاسخ‌ها باید در قالب کدهای وضعیت HTTP باشند (شکل ذیل). این یکی از اصول اساسی کار با وب سرویس‌ها است. وفادار ماندن به مفاهیم پایه‌ی وب، دید بهتری در مورد اتفاقاتی که می‌افتد به شما می‌دهد. 

در قسمت بعد با مفهوم مسیریابی در ASP.NET Web API آشنا می‌شوید.

مطالب
عبارت using و نحوه استفاده صحیح از آن
مثال ساده زیر را که در مورد تعریف یک کلاس Disposable و سپس استفاده از آن توسط عبارت using است را به همراه سه استثنایی که در این متدها تعریف شده است، در نظر بگیرید:
using System;

namespace TestUsing
{
    public class MyResource : IDisposable
    {
        public void DoWork()
        {
            throw new ArgumentException("A");
        }

        public void Dispose()
        {
            throw new ArgumentException("B");
        }
    }

    public static class TestClass
    {
        public static void Test()
        {
            using (MyResource r = new MyResource())
            {
                throw new ArgumentException("C");
                r.DoWork();
            }
        }
    }
}
به نظر شما قطعه کد زیر چه عبارتی را نمایش می‌دهد؟ C یا B یا A؟
try
{
     TestClass.Test();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

پاسخ: برخلاف تصور (که احتمالا C است؛ چون قبل از فراخوانی متد DoWork سبب بروز استثناء شده است)، فقط B را در خروجی مشاهده خواهیم کرد!
و این دقیقا مشکلی است که در حین کار با کتابخانه iTextSharp برای اولین بار با آن مواجه شدم. روش استفاده متداول از iTextSharp به نحو زیر است:
using (var pdfDoc = new Document(PageSize.A4))  
{  
   //todo: ...
}
در این بین هر استثنایی رخ دهد، در لاگ‌های خطای سیستم شما تنها خطاهای مرتبط با خود iTextSharp را مشاهده خواهید کرد و نه مشکل اصلی را که در کدهای ما وجود داشته است. البته این یک مشکل عمومی است و اگر «using statement and suppressed exceptions» را در گوگل جستجو کنید به نتایج مشابه زیادی خواهید رسید.
و خلاصه نتایج هم این است:
اگر به ثبت جزئیات خطاهای سیستم اهمیت می‌دهید (یکی از مهم‌ترین مزیت‌های دات نت نسبت به بسیاری از فریم ورک‌های مشابه که حداکثر خطای 0xABC12EF را نمایش می‌دهند)، از using استفاده نکنید! using در پشت صحنه به try/finally ترجمه می‌شود و بهتر است این مورد را دستی نوشت تا اینکه کامپایلر اینکار را به صورت خودکار انجام دهد.
در اینجا باز هم به یک سری کد تکراری try/finally خواهیم رسید و همانطور که در مباحث کاربردهای Action و Func در این سایت ذکر شد، می‌توان آن‌را تبدیل به کدهایی با قابلیت استفاده مجدد کرد. یک نمونه از پیاده سازی آن‌را در این سایت «C# Using Blocks can Swallow Exceptions » می‌توانید مشاهده کنید که خلاصه آن کلاس زیر است:
using System;

namespace Guard
{
    public static class SafeUsing
    {
        public static void SafeUsingBlock<TDisposable>(this TDisposable disposable, Action<TDisposable> action)
            where TDisposable : IDisposable
        {
            disposable.SafeUsingBlock(action, d => d);
        }

        internal static void SafeUsingBlock<TDisposable, T>(this TDisposable disposable, Action<T> action, Func<TDisposable, T> unwrapper)
            where TDisposable : IDisposable
        {
            try
            {
                action(unwrapper(disposable));
            }
            catch (Exception actionException)
            {
                try
                {
                    disposable.Dispose();
                }
                catch (Exception disposeException)
                {
                    throw new AggregateException(actionException, disposeException);
                }

                throw;
            }

            disposable.Dispose();
        }
    }
}
برای استفاده از کلاس فوق مثلا در حالت بکارگیری iTextSharp خواهیم داشت:
new Document(PageSize.A4).SafeUsingBlock(pdfDoc =>
{
  //todo: ...
});
علاوه بر اینکه SafeUsingBlock یک سری از اعمال تکراری را کپسوله می‌کند، از AggregateException نیز استفاده کرده است (معرفی شده در دات نت 4). به این صورت چندین استثنای رخ داده نیز در سطحی بالاتر قابل دریافت و بررسی خواهند بود و استثنایی در این بین از دست نخواهد رفت.
مطالب
روشی برای DeSerialize کردن QueryString به یک کلاس
چند روز پیش در حال استفاده از افزونه‌ی jQuery Bootgrid بودم که داده‌های خود را در قالب زیر به صورت کوئری استرینگ ارسال می‌کند.
current=1&rowCount=10&sort[sender]=asc&searchPhrase=&id=b0df282a-0d67-40e5-8558-c9e93b7befed

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

دوست دارم چیزی شبیه به DeSerialize کردن فرمت json توسط کتابخانه Json.net داشته باشم؛ پس در اولین قدم یک attribute با مشخصات زیر می‌سازیم:
    [AttributeUsage(AttributeTargets.Property,Inherited = true)]
    public class RequestBodyField:Attribute
    {
        public string Field;
        public RequestBodyField(string field)
        {
            this.Field = field;
        }
    }
سپس در کلاس اصلی، ما این خصوصیت‌ها را در بالای propertyها تعریف کرده و با کلید‌های موجود در کوئری استرینگ برابر می‌کنیم:
    public class EmployeesRequestBody
    {
        [RequestBodyField("current")]
        public  int CurrentPage { get; set; }

        [RequestBodyField("rowcount")]
        public int RowCount { get; set; }

        [RequestBodyField("searchPhrase")]
        public string SearchPhrase { get; set; }

        [RequestBodyField("sort")]
        public NameValueCollection SortDictionary { get; set; }
    }
سپس کلاس زیر را می‌نویسیم که وظیفه دارد کلاس‌های از جنس بالا را با query string‌ها رسیده در درخواست مطابقت دهد:
 public T GetFromQueryString<T>() where T : new()
        {
            var obj = new T();

            var queryString = HttpContext.Current.Request.QueryString;
            var queries = HttpUtility.ParseQueryString(queryString.ToString());

            var properties = typeof(T).GetProperties();
            foreach (var property in properties)
            {
                foreach (Attribute attribute in property.GetCustomAttributes(true))
                {
                    var requestBodyField = attribute as RequestBodyField; 
                    if (requestBodyField == null) continue;

                    //get value of query string
                    var valueAsString = queries[requestBodyField.Field];

                    var converter = TypeDescriptor.GetConverter(property.PropertyType);
                    var value = converter.ConvertFrom(valueAsString);

                    if (value == null)
                        continue;

                    property.SetValue(obj, value, null);
                }
            }
            return obj;
        }
این متد یک تعریف کلاس را دریافت می‌کند. سپس رشته‌ی کوئری استرینگ موجود در بدنه درخواست را دریافت کرده و با استفاده از کد زیر اشیا را به صورت nameValueCollection دریافت می‌کنیم.
HttpContext.Current.Request.QueryString
نکته: همچنین متد httputility.parseQueryString یک رشته کوئری استرینگ دریافت می‌کند و کوئری استرینگ را به زوج نام و مقدار nameValueCollection تبدیل میکند.

 سپس در مرحله‌ی بعدی با استفاده از Reflection پراپرتی‌هایی را که دارای attribute تعریف شده هستند، پیدا می‌کنیم.
مقدار داده شده به attribute را در nameValueCollection بررسی می‌کنیم و در صورت موجود بودن، مقدار آن را می‌گیریم. از آنجا که این مقدار از نوع رشته است و ممکن است مقدار داخل آن عددی یا هر نوع دیگری باشد، باید آن را به نوع صحیح تبدیل کنیم که خطوط زیر کار تبدیل را انجام می‌دهند:
   var converter = TypeDescriptor.GetConverter(property.PropertyType);
   var value = converter.ConvertFrom(valueAsString);

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

نکته‌ای که در اینجا نیاز به تلاش بیشتر دارد، کلید sort در کوئری استرینگ است. با نگاهی دقیق‌تر متوجه می‌شوید که خود کلید دو مقدار دارد که یکی از مقادیرش با کلید ترکیب شده است. این حالت روش ارسال آرایه‌ها با نام کلیدی متفاوت در کوئری استرینگ است. این حالت ارسال باعث می‌شود که گرید بتواند حالت multi sort را نیز پیاده سازی کند.
پس برای دریافت این نوع مقادیر کمی کد به آن اضافه می‌کنیم. برای دریافت مقادیر آرایه‌ای کد زیر را به سیستم اضافه می‌کنیم:
if (valueAsString == null)
                    {
                        var keys = from key in queries.AllKeys where key.StartsWith(requestBodyField.Field) select key;

                        var collection = new NameValueCollection();

                        foreach (var key in keys)
                        {
                            var openBraketIndex = key.IndexOf("[", StringComparison.Ordinal);
                            var closeBraketIndex = key.IndexOf("]", StringComparison.Ordinal);

                            if (openBraketIndex < 0 || closeBraketIndex < 0)
                                throw new Exception("query string is corrupted.");

                            openBraketIndex++;
                            //get key in [...]
                            var fieldName = key.Substring(openBraketIndex, closeBraketIndex - openBraketIndex);
                            collection.Add(fieldName, queries[key] );
                        }
                        property.SetValue(obj, collection, null);
                        continue;
                    }
در صورتیکه شما کلید sort را درخواست کنید و از آنجا که کلید اصلی با نام [sort[sender است، مقدار null بازگشت می‌دهد. پس ما می‌توانیم به این مقدار شک کنیم که شاید این کلید حاوی مقدار مورد نظر ماست؛ پس این حالت را بررسی میکنیم.  برای بررسی، با استفاده از linq بررسی می‌کنیم که اگر کلید‌های namValueCollection با این کلید (در اینجا sort) آغاز می‌شوند، پس به احتمال زیاد همان حالت مورد نظر ما رخ داه است. پس اندیس‌های [ و ] را می‌گیریم و اگر اندیس هر دو بزرگتر از صفر بود مقدار ما بین آن را به عنوان کلید بیرون می‌کشیم و در یک namValueCollection جدید قرار می‌دهیم و در نهایت به پراپرتی پاس می‌دهیم. کد نهایی این متد به شکل زیر است:
        public T GetFromQueryString<T>() where T : new()
        {
            var obj = new T();
            var properties = typeof(T).GetProperties();

            var queryString = HttpContext.Current.Request.QueryString;
            var queries = HttpUtility.ParseQueryString(queryString.ToString());

            foreach (var property in properties)
            {
                foreach (Attribute attribute in property.GetCustomAttributes(true))
                {
                    var requestBodyField = attribute as RequestBodyField; 
                    if (requestBodyField == null) continue;

                    //get value of query string
                    var valueAsString = queries[requestBodyField.Field];

                    if (valueAsString == null)
                    {
                        var keys = from key in queries.AllKeys where key.StartsWith(requestBodyField.Field) select key;

                        var collection = new NameValueCollection();

                        foreach (var key in keys)
                        {
                            var openBraketIndex = key.IndexOf("[", StringComparison.Ordinal);
                            var closeBraketIndex = key.IndexOf("]", StringComparison.Ordinal);

                            if (openBraketIndex < 0 || closeBraketIndex < 0)
                                throw new Exception("query string is corrupted.");

                            openBraketIndex++;
                            //get key in [...]
                            var fieldName = key.Substring(openBraketIndex, closeBraketIndex - openBraketIndex);
                            collection.Add(fieldName, queries[key]);
                        }
                        property.SetValue(obj, collection, null);
                        continue;
                    }

                    var converter = TypeDescriptor.GetConverter(property.PropertyType);
                    var value = converter.ConvertFrom(valueAsString);

                    if (value == null)
                        continue;

                    property.SetValue(obj, value, null);
                }
            }
            return obj;
        }

حال  به صورت زیر این متد را صدا می‌زنیم:
public virtual ActionResult GetEmployees()
{
     var request = new Requests().GetFromQueryString<EmployeesRequestBody>();
}
 
نظرات نظرسنجی‌ها
بعنوان مدیر تیم نرم افزاری با تعداد متوسط (6-7 نفر) با متدولوژی اسکرام، کدام روش را بیشتر می پسندید و استفاده می کنید؟
در مورد اسکرام تا جایی که بنده اطلاعات دارم و مطالعه کردم، اسکرام یک فریم ورک هست در واقع نه متدولوژی. یکی از اساتیدی که در ایران تدریس میکنند آقای صفری هستند که کتابی هم در این زمینه نوشتند که میتونید مطالعه بفرمایید. ایشان هرجایی که از اسکرام حرف زدند لفظ فریم ورک را هم در آن آورده اند. بنابراین بر اساس تفاوت فریم ورک و متدولوژی میتوانیم اینگونه برداشت کنیم که تمامی روش‌ها میتوانند بر اساس نیاز سیستم شما درست باشند، اما باید توجه داشته باشید که در اسکرام ما چیزی به عنوان role به معنای مستحکم و خشک خودش نداریم. در اسکرام حتی تحلیل هم جزئی از یک وظیفه (task) در نظر گرفته میشود. به همین دلیل وقتی که در افقی بلندتر به پروژه نگاه میکنیم، بک لاگ‌ها را مشاهده میکنیم که در یک بکلاگ تمامی فرایند‌های تحلیل و حتی تست را هم باید در وظایف در نظر بگیریم. بر اساس سخن آقای نواصری، این روش بیشتر به RUP و مدل‌های نرم افزاری سنتی شبیه است که البته بنده نقض نمیکنم. چون غول نرم افزاری دنیا به نام IBM پشت این قضیه است.
و اما در مورد نظرسنجی باید عرض کنم، به نظر بنده در گزینه 1 تقسیم کار به صورت وظیفه ای و سپردن یک قسمت از برنامه چیزی است که در اسکرام تعریف شده، اما لزومی ندارد که تسک شما (مثلا یک تسک تراکنش بانکی) که شامل view و design و تحلیل و backend میشود همگی در یک تسک دیده شود. بنابراین حس میکنم شما در شکاندن تسک‌ها باید تسک‌ها را به قسمت‌های کوچکتری تقسیم کنید. به این صورت ادامه گزینه 1 هم درست میشود، چون دیگر تمامی لایه‌ها را یک نفر انجام نمیدهد. و یک تسک تراکنش بانکی به 3 یا 4 تسک تقسیم میشود.
و در آخر اینکه اسکرام بنای تمامی این تحلیل‌ها را روی یک عبارت استوار کرده و آن هم "تعامل بین اعضا" است.
مطالب
React 16x - قسمت 2 - بررسی پیشنیازهای جاوا اسکریپتی - بخش 1
برای کار با React، نیاز است با ES6 آشنایی داشته باشید که در این سایت، یک سری کامل بررسی مقدمات آن‌را پیشتر مرور کرده‌ایم. علاوه بر توصیه‌ی مطالعه‌ی این سری (اینکار الزامی است)، در این قسمت خلاصه‌ی بسیار سریع و کاربردی آن‌را که بیشتر در برنامه‌های مبتنی بر React مورد استفاده قرار می‌گیرند، با هم مرور خواهیم کرد. در قسمت‌های بعدی، اهمیت ذکر این خلاصه بیشتر مشخص می‌شود.

برای بررسی ویژگی‌های جاوا اسکریپت مدرن، یک پروژه‌ی جدید React را ایجاد می‌کنیم.
> create-react-app sample-02
> cd sample-02
> npm start
سپس تمام کدهای داخل index.js را نیز حذف می‌کنیم. اکنون تمام کدهای خالص جاوا اسکریپتی خود را داخل این فایل خواهیم نوشت.
به علاوه چون در این قسمت خروجی UI نخواهیم داشت، تمام خروجی را در کنسول developer tools مرورگر خود می‌توانید مشاهده کنید (فشردن دکمه‌ی F12).


var، let و const

در اکثر زبان‌های برنامه نویسی، متغیرها در محدوده‌ی دید قطعه کدی که تعریف شده‌اند (scope)، قابل دسترسی هستند. برای نمونه محتوای فایل index.js پروژه را به صورت زیر تغییر داده و با فرض اجرای دستور npm start، خروجی آن‌را می‌توان در کنسول مرورگر مشاهده کرد.
function sayHello() {
  for (var i = 0; i < 5; i++) {
    console.log(i);
  }
  console.log(i);
}

sayHello();
در این مثال متغیر i، مخصوص قطعه کد حلقه، تعریف شده‌است. بنابراین به ظاهر نباید خارج از این حلقه نیز قابل دسترسی باشد. اما خروجی آن به صورت زیر است:


در آخرین پیمایش حلقه، i مساوی 5 شده و از حلقه خارج می‌شود. اما چون در اینجا برای تعریف متغیر از واژه‌ی کلیدی var استفاده شده‌است، محدوده‌ی دید آن به کل تابعی که در آن تعریف شده‌است، بسط پیدا می‌کند. به همین جهت در این خروجی، عدد 5 را نیز مشاهده می‌کند که حاصل دسترسی به i، خارج از حلقه‌است.
برای یک دست سازی این رفتار با سایر زبان‌های برنامه نویسی، در ES6، واژه‌ی کلیدی جدیدی به نام let تعریف شده‌است که میدان دید متغیر را به قطعه کدی که در آن تعریف شده‌است، محدود می‌کند. اکنون اگر در حلقه‌ی فوق بجای var از let استفاده شود، یک چنین خطایی در مرورگر ظاهر خواهد شد که عنوان می‌کند، i استفاده شده‌ی در خارج از حلقه، تعریف نشده‌است.
./src/index.js
  Line 14:15:  'i' is not defined  no-undef

Search for the keywords to learn more about each error.

علاوه بر let، واژه‌ی کلیدی جدید const نیز به ES6 اضافه شده‌است که از آن برای تعریف ثوابت استفاده می‌شود. constها نیز همانند let، میدان دید محدود شده‌ای به قطعه کد تعریف شده‌ی در آن دارند؛ اما قابلیت انتساب مجدد را ندارند:
 const x = 1;
x = 2; // Attempting to override 'x' which is a constant.
اگر یک چنین قطعه کدی را اجرا کنیم، خطای x is const را در مرورگر می‌توان مشاهده کرد.

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


اشیاء در جاوا اسکریپت

اشیاء در جاوا اسکریپت به صورت مجموعه‌ای از key/value‌ها تعریف می‌شوند:
const person = {
  name: "User 1",
  walk: function() {}, // method
  talk() {} // concise method
};
در اینجا امکان تعریف یک تابع نیز وجود دارد که چون درون یک شیء قرار می‌گیرد، اینبار «متد» نامیده می‌شود. همچنین در ES6 می‌توان این متدها را به صورت معمولی، مانند متد talk نیز تعریف کرد که به آن‌ها concise method می‌گویند. بنابراین نحوه‌ی تعریف فوق را به نحو زیر نیز می‌توان خلاصه کرد:
const person = {
  name: "User 1",
  walk() {},
  talk() {}
};
پس از تعریف این شیء، روش دسترسی به اجزای آن به صورت زیر است:
person.talk();
person.name = "User 3";
person["name"] = "User 2";
به دو مورد اول، روش dot notation می‌گویند که از همان ابتدا دقیقا مشخص است کدامیک از خواص و متدهای شیء تعریف شده، مورد استفاده قرار می‌گیرند.
مورد آخر همان روش استفاده از key/valueها است که اساس اشیاء جاوا اسکریپتی را تشکیل می‌دهد. البته از این روش فقط زمانی استفاده کنید که قرار است یکسری کار پویا صورت گیرند (مقدار key به صورت متغیر دریافت شود) و از ابتدا مشخص نیست که کدام خاصیت یا متد قرار است تعریف و استفاده شود:
const targetMember = "name";
person[targetMember] = "User 2";


واژه‌ی کلیدی this در جاوا اسکریپت

از واژه‌ی کلیدی this، در قسمت‌های بعدی زیاد استفاده خواهیم کرد. به همین جهت نیاز است تفاوت‌های اساسی آن‌را با سایر زبان‌های برنامه نویسی بررسی کنیم.
همان شیء person را که پیشتر تعریف کردیم درنظر بگیرید. در متد walk آن، مقدار this را لاگ می‌کنیم:
const person = {
  name: "User 1",
  walk() {
    console.log(this);
  },
  talk() {}
};

person.walk();
خروجی این قطعه، به صورت زیر است:


شیء this در جاوا اسکریپت، همانند سایر زبان‌های برنامه نویسی مانند سی‌شارپ و یا جاوا رفتار نمی‌کند. در سایر زبان‌های نامبرده شده، this همواره ارجاعی را به وهله‌ای از شیء جاری، باز می‌گرداند؛ دقیقا همانند تصویری که در بالا مشاهده می‌کنید. در اینجا نیز this جاوا اسکریپتی لاگ شده، ارجاعی را به وهله‌ی جاری شیء person، بازگشت داده‌است. اما مشکل اینجا است که this در جاوا اسکریپت، همیشه به این صورت رفتار نمی‌کند!
برای نمونه در ادامه یک ثابت را به نام walk تعریف کرده و آن‌را به person.walk مقدار دهی می‌کنیم:
const walk = person.walk;
console.log(walk);
دقت داشته باشید که در اینجا از () استفاده نشده‌است (متد walk اجرا نشده‌است). یعنی صرفا «ارجاعی» از متد walk شیء person را به ثابت walk نسبت داده‌ایم. بنابراین اکنون ثابت walk نیز یک function است که حاصل console.log آن به صورت زیر است:


سؤال: اکنون اگر این function را با فراخوانی ()walk اجرا کنیم، چه خروجی را می‌توان مشاهده کرد؟


اینبار this لاگ شده، به شیء person اشاره نمی‌کند و شیء استاندارد window مرورگر را بازگشت داده‌است!
اگر یک function به صورت متدی از یک شیء فراخوانی شود، مقدار this همواره اشاره‌گری به وهله‌ای از آن شیء خواهد بود. اما اگر این تابع به صورت متکی به خود و به صورت یک function و نه متد یک شیء، فراخوانی شود، اینبار this، شیء سراسری جاوا اسکریپت یا همان شیء window را بازگشت می‌دهد.

یک نکته: اگر strict mode جاوا اسکریپت را در پروژه‌ی جاری فعال کنیم، بجای شیء window، مقدار undefined را در خروجی فوق شاهد خواهیم بود.


اتصال مجدد this به شیء اصلی در جاوا اسکریپت

تا اینجا دریافتیم که اگر یک function را به صورت متکی به خود و نه جزئی از یک شیء فراخوانی کنیم، شیء this در این حالت به شیء window سراسری مرورگر اشاره می‌کند و اگر strict mode فعال باشد، فقط undefined را بازگشت می‌هد. اکنون می‌خواهیم بررسی کنیم که چگونه می‌توان این مشکل را برطرف کرد؛ یعنی صرف‌نظر از نحوه‌ی فراخوانی متدها یا تابع‌ها، this همواره ارجاعی را به شیء person بازگشت دهد.
در جاوا اسکریپت، تابع‌ها نیز شیء هستند. برای مثال person.walk نوشته شده نیز یک شیء است. برای اثبات ساده‌ی آن فقط یک دات را پس از person.walk قرار دهید:


همانطور که مشاهده می‌کنید، شیء person.walk مانند تمام اشیاء دیگر جاوا اسکریپت، به همراه متد bind نیز هست. کار آن، انقیاد یک تابع، به یک شیء است. یعنی هرچیزی را که به عنوان آرگومان آن، به آن ارسال کنیم، به عنوان مقدار شیء this درنظر می‌گیرد:
const walk2 = person.walk.bind(person);
console.log(walk2);
walk2();
در اینجا متد bind، یک وهله‌ی جدید از person.walk را بازگشت می‌دهد که در آن شیء person را به عنوان شیء this، تنظیم کرده‌است. به همین جهت اینبار فراخوانی walk2 که به شیء person متصل شده‌است، به this صحیحی بجای window سراسری اشاره می‌کند. از این روش در برنامه‌های مبتنی بر React زیاد استفاده می‌شود.


Arrow functions

تابع زیر را درنظر بگیرید که به یک ثابت انتساب داده شده‌است:
const square = function(number) {
  return number * number;
};
در ES6، روش ساده‌تر و تمیزتری برای این نوع تعاریف، ذیل ویژگی جدید Arrow functions اضافه شده‌است. برای تبدیل قطعه کد فوق به یک arrow function، ابتدا واژه‌ی کلیدی function را حذف می‌کنیم. سپس بین پارامتر تابع و {}، یک علامت <= (که به آن fat arrow هم می‌گویند!) قرار می‌دهیم:
const square2 = (number) => {
  return number * number;
};
اگر مانند اینجا تنها یک تک پارامتر وجود داشته باشد، می‌توان پرانتزهای ذکر شده را نیز حذف کرد:
const square2 = number => {
  return number * number;
};
و اگر این متد پارامتری نداشت، از () استفاده می‌شود.
در ادامه اگر بدنه‌ی این تابع، فقط حاوی یک return بود، می‌توان آن‌را به صورت زیر نیز خلاصه کرد (در اینجا {} به همراه واژه‌ی کلیدی return حذف می‌شوند):
const square3 = number => number * number;
console.log(square3(5));
این یک سطر ساده شده، دقیقا معادل اولین const square ای است که نوشتیم. نحوه‌ی فراخوانی آن نیز مانند قبل است.

اکنون مثال مفید دیگری را در مورد Arrow functions بررسی می‌کنیم که بیشتر شبیه به عبارات LINQ در #C است:
const jobs = [
  { id: 1, isActive: true },
  { id: 2, isActive: true },
  { id: 3, isActive: true },
  { id: 4, isActive: true },
  { id: 5, isActive: false }
];
در اینجا آرایه‌ای از اشیاء job را مشاهده می‌کنید که مورد آخر آن، فعال نیست. اکنون می‌خواهیم لیست کارهای فعال را گزارشگیری کنیم:
var activeJobs = jobs.filter(function(job) {
  return job.isActive;
});
متد filter در جاوا اسکریپت، بر روی تک تک عناصر آرایه‌ی jobs حرکت می‌کند. سپس هر job را به پارامتر متد ارسالی آن که predicate نام دارد، جهت دریافت یک خروجی true و یا false، ارائه می‌دهد. اگر خروجی این متد true باشد، آن job انتخاب خواهد شد و در لیست نهایی گزارش، ظاهر می‌شود.
در ادامه می‌توان این تابع را توسط arrow functions به صورت ساده‌تر زیر نیز نوشت:
var activeJobs2 = jobs.filter(job => job.isActive);
ابتدا واژه‌ی کلیدی function را حذف می‌کنیم. سپس چون یک تک پارامتر را دریافت می‌کند، نیازی به ذکر پرانتزهای آن نیز نیست. در ادامه چون یک تک return را داریم، { return }  را با یک <= جایگزین خواهیم کرد. در اینجا نیازی به ذکر سمی‌کالن انتهای return هم نیست. نوشتن یک چنین کدی تمیزتر و خواندن آن، ساده‌تر است.


ارتباط بین arrow functions و شیء this

نکته‌ی مهمی را که باید در مورد arrow functions دانست این است که شیء this را rebind نمی‌کنند (rebind: مقدار دهی مجدد؛ ریست کردن).
در مثال زیر، ابتدا شیء user با متد talk که در آن شیء this، لاگ شده، ایجاد شده و سپس این متد فراخوانی گردیده‌است:
const user = {
  name: "User 1",
  talk() {
    console.log(this);
  }
};
user.talk();
همانطور که انتظار می‌رود، this ای که در اینجا لاگ می‌شود، دقیقا ارجاعی را به وهله‌ی جاری شیء user دارد.
اکنون اگر متد لاگ کردن را داخل یک تایمر قرار دهیم چه اتفاقی رخ می‌دهد؟
const user = {
  name: "User 1",
  talk() {
    setTimeout(function() {
      console.log(this);
    }, 1000);
  }
};
user.talk();
متد setTimeout، متدی را که به عنوان آرگومان اول آن دریافت کرده، پس از 1 ثانیه اجرا می‌کند.
در این حالت خروجی console.log، مجددا همان شیء سراسری window مرورگر است و دیگر به وهله‌ی جاری شیء user اشاره نمی‌کند. علت اینجا است که پارامتر اول متد setTimeout که یک callback function نام دارد، جزئی از هیچ شیءای نیست. بنابراین دیگر مانند فراخوانی متد ()user.talk در مثال قبلی کار نمی‌کند؛ چون متکی به خود است. هر زمان که یک متد متکی به خود غیر وابسته‌ی به یک شیء را اجرا کنیم، به صورت پیش‌فرض this آن، به شیء window مرورگر اشاره می‌کند.

سؤال: چگونه می‌توان درون یک callback function متکی به خود، به this همان شیء user جاری دسترسی یافت؟
یک روش حل این مساله، ذخیره this شیء user در یک متغیر و سپس ارسال آن به متد متکی به خود setTimeout است:
const user2 = {
  name: "User 2",
  talk() {
    var self = this;
    setTimeout(function() {
      console.log(self);
    }, 1000);
  }
};
user2.talk();
این روشی است که سال‌ها است وجود دارد؛ با ارائه‌ی arrow functions، دیگر نیازی به اینکار نیست و می‌توان از روش زیر استفاده کرد:
const user3 = {
  name: "User 3",
  talk() {
    setTimeout(() => console.log(this), 1000);
  }
};
user3.talk();
در اینجا callback function را تبدیل به یک arrow function کرده‌ایم و چون arrow functions شیء this را rebind نمی‌کنند، یعنی شیء this را به ارث می‌برند. بنابراین console.log مثال فوق، دقیقا به this شیء user اشاره می‌کند و دوباره آن‌را مقدار دهی مجدد نمی‌کند و از همان نمونه‌ی موجود قطعه کد تعریف شده، استفاده خواهد کرد.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: sample-02.zip

در قسمت بعد نیز بررسی پیشنیازهای جاوا اسکریپتی شروع به کار با React را ادامه خواهیم داد.