From 747714ffd5d0e8bfc79bea5dc7e55334e80cdb67 Mon Sep 17 00:00:00 2001 From: Philippe Rigovanov Date: Wed, 6 Feb 2019 18:17:06 +0300 Subject: [PATCH 01/15] Update ru-RU.md (#118) just removed "`" symbols as it is in English original --- translations/ru-RU.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/translations/ru-RU.md b/translations/ru-RU.md index 1d70c57..ad46b34 100644 --- a/translations/ru-RU.md +++ b/translations/ru-RU.md @@ -132,21 +132,21 @@ Временная мертвая зона -`const` +const Блок Нет Да Да -`let` +let Блок Да Да Да -`var` +var Функция Да Да From 35003adf462bd7830d2e819fe000a19b65fe8dea Mon Sep 17 00:00:00 2001 From: Jason Cooke Date: Sat, 1 Jun 2019 03:39:30 +1200 Subject: [PATCH 02/15] docs: fix typo (#120) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 288b7c6..fef2907 100644 --- a/README.md +++ b/README.md @@ -1239,7 +1239,7 @@ JavaScript is a [prototype-based](https://en.wikipedia.org/wiki/Prototype-based_ The word *class* is indeed error prone if you are familiar with classes in other languages. If you do, avoid assuming how JavaScript classes work on this basis and consider it an entirely different notion. -Since this document is not an attempt to teach you the language from the ground up, I will believe you know what prototypes are and how they behave. If you do not, see the external resouces listed below the sample code. +Since this document is not an attempt to teach you the language from the ground up, I will believe you know what prototypes are and how they behave. If you do not, see the external resources listed below the sample code. #### Samples From 6316f30c652577c26995c55defa8636b448be279 Mon Sep 17 00:00:00 2001 From: Rod Elias Date: Thu, 13 Jun 2019 02:20:38 -0300 Subject: [PATCH 03/15] fix typo on Wes Bos name (#121) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fef2907..49ab380 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ When you struggle to understand a notion, I suggest you look for answers on the - [MDN (Mozilla Developer Network)](https://developer.mozilla.org/en-US/search?q=) - [You don't know JS (book)](https://github.com/getify/You-Dont-Know-JS) - [ES6 Features with examples](http://es6-features.org) -- [WesBos blog (ES6)](http://wesbos.com/category/es6/) +- [Wes Bos blog (ES6)](http://wesbos.com/category/es6/) - [Javascript Basics for Beginners](https://www.udacity.com/course/javascript-basics--ud804) - a free Udacity course - [Reddit (JavaScript)](https://www.reddit.com/r/javascript/) - [Google](https://www.google.com/) to find specific blog and resources From 2e2b0cebb2326c374e5d9b2e5792d8fabbaf3355 Mon Sep 17 00:00:00 2001 From: Maxime Gabut Date: Wed, 10 Jul 2019 19:25:38 +0200 Subject: [PATCH 04/15] Small typo and indentation fixes (#122) * Small typo fix in README.md * Uniformize indentation in code samples --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 49ab380..8ba8cc2 100644 --- a/README.md +++ b/README.md @@ -1239,7 +1239,7 @@ JavaScript is a [prototype-based](https://en.wikipedia.org/wiki/Prototype-based_ The word *class* is indeed error prone if you are familiar with classes in other languages. If you do, avoid assuming how JavaScript classes work on this basis and consider it an entirely different notion. -Since this document is not an attempt to teach you the language from the ground up, I will believe you know what prototypes are and how they behave. If you do not, see the external resources listed below the sample code. +Since this document is not an attempt to teach you the language from the ground up, I will assume you know what prototypes are and how they behave. If you do not, see the external resources listed below the sample code. #### Samples @@ -1713,7 +1713,7 @@ The `static` keyword is used in classes to declare static methods. Static method #### Sample code ```js -class Repo{ +class Repo { static getName() { return "Repo name is modern-js-cheatsheet" } @@ -1735,12 +1735,12 @@ Static methods can be called within another static method by using the `this` ke To call a static method from another static method, the `this` keyword can be used like so; ```js -class Repo{ +class Repo { static getName() { return "Repo name is modern-js-cheatsheet" } - static modifyName(){ + static modifyName() { return this.getName() + '-added-this' } } @@ -1756,12 +1756,12 @@ Non-static methods can call static methods in 2 ways; To get access to a static method from a non-static method we use the class name and call the static method like a property. e.g `ClassName.StaticMethodName` ```js -class Repo{ +class Repo { static getName() { return "Repo name is modern-js-cheatsheet" } - useName(){ + useName() { return Repo.getName() + ' and it contains some really important stuff' } } @@ -1776,12 +1776,12 @@ console.log(r.useName()) // Repo name is modern-js-cheatsheet and it contains so Static methods can be called as properties on the constructor object. ```js -class Repo{ +class Repo { static getName() { return "Repo name is modern-js-cheatsheet" } - useName(){ + useName() { // Calls the static method as a property of the constructor return this.constructor.getName() + ' and it contains some really important stuff' } From 131987e0325b538b7697cf460aa584fe7d101cc7 Mon Sep 17 00:00:00 2001 From: Manuel Beaudru Date: Thu, 5 Sep 2019 08:49:07 +0200 Subject: [PATCH 05/15] Adds twitter link at the top of the cheatsheet --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ba8cc2..e56615c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ # Modern JavaScript Cheatsheet ![Modern JavaScript cheatsheet](https://i.imgur.com/aexPxMb.png) -Image Credits: [Ahmad Awais ⚡️](https://github.com/ahmadawais) +Image Credits: [Ahmad Awais ⚡️](https://github.com/ahmadawais) + +If you liked this content, you can ping me or follow me on Twitter :+1: + +[![Tweet for help](https://img.shields.io/twitter/follow/mbeaudru?label=Tweet%20%40mbeaudru&style=social)](https://twitter.com/mbeaudru/) ## Introduction From 2da46d7f5f7dcc2bdf6ec5402a7ac1cf2f61ed40 Mon Sep 17 00:00:00 2001 From: Manuel Beaudru Date: Thu, 5 Sep 2019 08:50:44 +0200 Subject: [PATCH 06/15] Fixes a typo and comment on Twitter referal --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e56615c..025e3ba 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ![Modern JavaScript cheatsheet](https://i.imgur.com/aexPxMb.png) Image Credits: [Ahmad Awais ⚡️](https://github.com/ahmadawais) -If you liked this content, you can ping me or follow me on Twitter :+1: +> If you like this content, you can ping me or follow me on Twitter :+1: [![Tweet for help](https://img.shields.io/twitter/follow/mbeaudru?label=Tweet%20%40mbeaudru&style=social)](https://twitter.com/mbeaudru/) From 789167cf0da8eff2f824e69d75ebd8b44d66103a Mon Sep 17 00:00:00 2001 From: Mayank Goyal <42898424+heromayank2@users.noreply.github.com> Date: Fri, 11 Oct 2019 23:30:07 +0530 Subject: [PATCH 07/15] Add Array.prototype.find (#123) --- README.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 025e3ba..9403f4d 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ When you struggle to understand a notion, I suggest you look for answers on the * [Array.prototype.map()](#arrayprototypemap) * [Array.prototype.filter()](#arrayprototypefilter) * [Array.prototype.reduce()](#arrayprototypereduce) + * [Array.prototype.find()](#arrayprototypefind) - [External Resource](#external-resource-2) + [Spread operator "..."](#spread-operator-) - [Sample code](#sample-code-3) @@ -607,19 +608,20 @@ console.log(y) // "b" - [Destructuring Objects - WesBos](http://wesbos.com/destructuring-objects/) - [ExploringJS - Destructuring](http://exploringjs.com/es6/ch_destructuring.html) -### Array methods - map / filter / reduce +### Array methods - map / filter / reduce / find -*Map*, *filter* and *reduce* are array methods that are coming from a programming paradigm named [*functional programming*](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0). +*Map*, *filter*, *reduce* and *find* are array methods that are coming from a programming paradigm named [*functional programming*](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0). To sum it up: - **Array.prototype.map()** takes an array, does something on its elements and returns an array with the transformed elements. - **Array.prototype.filter()** takes an array, decides element by element if it should keep it or not and returns an array with the kept elements only - **Array.prototype.reduce()** takes an array and aggregates the elements into a single value (which is returned) +- **Array.prototype.find()** takes an array, and returns the first element that satisfies the provided condition. I recommend to use them as much as possible in following the principles of functional programming because they are composable, concise and elegant. -With those three methods, you can avoid the use of *for* and *forEach* loops in most situations. When you are tempted to do a *for* loop, try to do it with *map*, *filter* and *reduce* composed. You might struggle to do it at first because it requires you to learn a new way of thinking, but once you've got it things get easier. +With those four methods, you can avoid the use of *for* and *forEach* loops in most situations. When you are tempted to do a *for* loop, try to do it with *map*, *filter*, *reduce* and *find* composed. You might struggle to do it at first because it requires you to learn a new way of thinking, but once you've got it things get easier. #### Sample code @@ -628,6 +630,7 @@ const numbers = [0, 1, 2, 3, 4, 5, 6]; const doubledNumbers = numbers.map(n => n * 2); // [0, 2, 4, 6, 8, 10, 12] const evenNumbers = numbers.filter(n => n % 2 === 0); // [0, 2, 4, 6] const sum = numbers.reduce((prev, next) => prev + next, 0); // 21 +const greaterThanFour = numbers.find((n) => n>4); // 5 ``` Compute total grade sum for students with grades 10 or above by composing map, filter and reduce: @@ -784,6 +787,21 @@ Function returns *acc* + *n* --> 15 + 6 --> 21 As it is the last iteration step, **.reduce** returns 21. +##### Array.prototype.find() + +```js +const greaterThanZero = numbers.find(function(n) { + return n > 0; // return number just greater than 0 is present +}); +console.log(greaterThanZero); // 1 +``` + +**Note** : You will frequently encounter this method used in combination with [arrow functions](#-arrow-function) + +We are using .find on the *numbers* array, .find is iterating on each element of the array and passes it to our function, until the condition is met. The goal of the function is to return the element that satisfies the current testing function. The .find method executes the callback function once for each index of the array until the callback returns a truthy value. + +**Note** : It immediately returns the value of that element (that satisfies the condition) if found. Otherwise, returns undefined. + #### External Resource - [Understanding map / filter / reduce in JS](https://hackernoon.com/understanding-map-filter-and-reduce-in-javascript-5df1c7eee464) From d07e4cd591885f87c0935b73c7f2b65d182ebcce Mon Sep 17 00:00:00 2001 From: haider-ilahi <57682541+haider-ilahi@users.noreply.github.com> Date: Sun, 17 Nov 2019 10:18:47 -0800 Subject: [PATCH 08/15] Updated variable declaration to remove ambiguity (#124) The current wording suggested that variables should be declared with const and then again with let when it is time to reassign them. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9403f4d..0b2af54 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ In JavaScript, there are three keywords available to declare a variable, and eac Variables declared with ```const``` keyword can't be reassigned, while ```let``` and ```var``` can. -I recommend always declaring your variables with ```const``` by default, and with ```let``` if you need to *mutate* it or reassign it later. +I recommend always declaring your variables with ```const``` by default, but with ```let``` if it is a variable that you need to *mutate* or reassign later. From 94d6169f8faaa0693e6e78a5b566104a82f0de59 Mon Sep 17 00:00:00 2001 From: Kamil Adam Date: Thu, 5 Mar 2020 11:56:28 +0100 Subject: [PATCH 09/15] Remove spaces that destroy the header --- translations/pl_PL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/pl_PL.md b/translations/pl_PL.md index 2c70237..5021c8f 100644 --- a/translations/pl_PL.md +++ b/translations/pl_PL.md @@ -1,4 +1,4 @@ -# Współczesny JavaScript - ściągawka +# Współczesny JavaScript - ściągawka ![Modern JavaScript cheatsheet](https://i.imgur.com/aexPxMb.png) Image Credits: [Ahmad Awais ⚡️](https://github.com/ahmadawais) From 27a38560df2cd8e2a4b183646f54dc3839696f71 Mon Sep 17 00:00:00 2001 From: wysiati <64269852+sachin-inguva@users.noreply.github.com> Date: Thu, 7 May 2020 00:59:07 +0530 Subject: [PATCH 10/15] Add a few "Complementary Resources" --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0b2af54..601630e 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ When you struggle to understand a notion, I suggest you look for answers on the - [MDN (Mozilla Developer Network)](https://developer.mozilla.org/en-US/search?q=) - [You don't know JS (book)](https://github.com/getify/You-Dont-Know-JS) +- [Eloquent JavaScript (book)](https://eloquentjavascript.net) +- [Douglas Crockford's blog](https://www.crockford.com/javascript/) - [ES6 Features with examples](http://es6-features.org) - [Wes Bos blog (ES6)](http://wesbos.com/category/es6/) - [Javascript Basics for Beginners](https://www.udacity.com/course/javascript-basics--ud804) - a free Udacity course From b4df488884fbd3db0611f9175fef940f35cfe6b7 Mon Sep 17 00:00:00 2001 From: Mostafa Ahangarha Date: Sun, 5 Dec 2021 14:22:09 +0300 Subject: [PATCH 11/15] Persian translation (#130) Co-authored-by: Mostafa Ahangarha --- translations/fa-IR.md | 1837 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1837 insertions(+) create mode 100644 translations/fa-IR.md diff --git a/translations/fa-IR.md b/translations/fa-IR.md new file mode 100644 index 0000000..130fcd5 --- /dev/null +++ b/translations/fa-IR.md @@ -0,0 +1,1837 @@ +# برگه تقلب جاوااسکریپت مدرن + +## مقدمه + +### انگیزه + +این سند، یک برگه تقلب برای مسائلی در جاوااسکپت است که به دفعات در کار با پروژه‌های مدرن و جدیدترین نمونه‌های کد با آن‌ها مواجه خواهید شد. + +این راهنما قصد ندارد جاوااسکریپت را از بیخ آموزش بدهد بلکه می‌خواهد کمکی باشد برای دانش مقدماتی توسعه‌دهندگانی که ممکن است به واسطه مفاهیم جاوااسکریپت به کار برده شده در کدهای مدرن (مثلا در یادگیری ری‌اکت) با آن‌ها دست و پنجه نرم کنند. + +در کنار این، تلاش می‌کنم گاهی نکاتی شخصی را ارائه دهم که ممکن است بحث برانگیز باشند اما هر کجا به آن‌ها اشاره کنم، بیان خواهم کرد که آن موارد، پیشنهادهای شخصی‌ام هستند. + +> **نکته:** بیشتر مفاهمیمی که اینجا معرفی می‌شوند از به‌روزرسانی زبان جاوااسکریپت (ES2015 یا آن‌طور که معمولا گفته می‌شود ES6) می‌آیند. می‌توانید ویژگی‌های جدیدی که در این به‌روزرسانی اضافه شده‌اند را [این‌ جا](http://es6-features.org/) پیدا کنید که بسیار خوب ارائه شده‌اند. + +## منابع مکمل + +پیشنهاد می‌کنم وقتی در درک یک مفهوم دچار مشکل می‌شوید، در منابع زیر دنبال پاسخ بگردید: + +- [MDN (Mozilla Developer Network)](https://developer.mozilla.org/en-US/search?q=) +- [You don't know JS (book)](https://github.com/getify/You-Dont-Know-JS) +- [Eloquent JavaScript (book)](https://eloquentjavascript.net) +- [Douglas Crockford's blog](https://www.crockford.com/javascript/) +- [ES6 Features with examples](http://es6-features.org) +- [Wes Bos blog (ES6)](http://wesbos.com/category/es6/) +- [Javascript Basics for Beginners](https://www.udacity.com/course/javascript-basics--ud804) - a free Udacity course +- [Reddit (JavaScript)](https://www.reddit.com/r/javascript/) +- [Google](https://www.google.com/) to find specific blog and resources +- [StackOverflow](https://stackoverflow.com/questions/tagged/javascript) + +## فهرست مطالب + +- [برگه تقلب جاوااسکریپت مدرن](#برگه-تقلب-جاوااسکریپت-مدرن) + - [مقدمه](#مقدمه) + - [انگیزه](#انگیزه) + - [منابع مکمل](#منابع-مکمل) + - [فهرست مطالب](#فهرست-مطالب) + - [مفاهیم](#مفاهیم) + - [اعلان متغیر: var، const، let](#اعلان-متغیر-var-const-let) + - [توضیح کوتاه](#توضیح-کوتاه) + - [نمونه کد](#نمونه-کد) + - [توضیح مبسوط](#توضیح-مبسوط) + - [منابع خارجی](#منابع-خارجی) + - [تابع پیکانی](#تابع-پیکانی) + - [نمونه کد](#نمونه-کد-1) + - [توضیح مبسوط](#توضیح-مبسوط-1) + - [اختصار](#اختصار) + - [ارجاع this](#ارجاع-this) + - [منابع مفید](#منابع-مفید) + - [مقدار پیش‌فرض پارامتر تابع](#مقدار-پیشفرض-پارامتر-تابع) + - [منابع خارجی](#منابع-خارجی-1) + - [تجزیه اشیاء و آرایه‌ها](#تجزیه-اشیاء-و-آرایهها) + - [توضیح با مثال](#توضیح-با-مثال) + - [منابع مفید](#منابع-مفید-1) + - [متدهای آرایه - map / filter / reduce / find](#متدهای-آرایه-map-filter-reduce-find) + - [نمونه کد](#نمونه-کد-2) + - [توضیح](#توضیح) + - [متد ‪Array.prototype.map()‬](#متد-arrayprototypemap) + - [متد ‪Array.prototype.filter()‬](#متد-arrayprototypefilter) + - [متد ‪Array.prototype.reduce()‬](#متد-arrayprototypereduce) + - [متد ‪Array.prototype.find()‬](#متد-arrayprototypefind) + - [منبع خارجی](#منبع-خارجی) + - [عملگر گسترش «...»](#عملگر-گسترش-) + - [نمونه کد](#نمونه-کد-3) + - [توضیح](#توضیح-1) + - [در شمارش‌پذیرها (مانند آرایه)](#در-شمارشپذیرها-مانند-آرایه) + - [پارامتر باقی (rest) تابع](#پارامتر-باقی-rest-تابع) + - [گسترش خاصیت‌های شیء](#گسترش-خاصیتهای-شیء) + - [منابع خارجی](#منابع-خارجی-2) + - [میان‌بر خاصیت شیء](#میانبر-خاصیت-شیء) + - [توضیح](#توضیح-2) + - [منابع خارجی](#منابع-خارجی-3) + - [وعده‌ها (Promises)](#وعدهها-promises) + - [نمونه کد](#نمونه-کد-4) + - [توضیح](#توضیح-3) + - [ساخت یک وعده](#ساخت-یک-وعده) + - [به‌کارگیری گرداننده وعده](#بهکارگیری-گرداننده-وعده) + - [منابع خارجی](#منابع-خارجی-4) + - [قالب لفظی (Template literals)](#قالب-لفظی-template-literals) + - [نمونه کد](#نمونه-کد-5) + - [منابع خارجی](#منابع-خارجی-5) + - [قالب لفظی برچسب‌دار](#قالب-لفظی-برچسبدار) + - [منابع خارجی](#منابع-خارجی-6) + - [درون‌ریزی / برون‌ریزی](#درونریزی-برونریزی) + - [توضیح به همراه نمونه کد](#توضیح-به-همراه-نمونه-کد) + - [برون‌ریزی بانام](#برونریزی-بانام) + - [درون‌ریزی / برون‌ریزی پیش‌فرض](#درونریزی-برونریزی-پیشفرض) + - [منابع خارجی](#منابع-خارجی-7) + - [مفهوم this در جاوااسکریپت](#مفهوم-this-در-جاوااسکریپت) + - [منابع خارجی](#منابع-خارجی-8) + - [کلاس](#کلاس) + - [نمونه‌ها](#نمونهها) + - [منابع خارجی](#منابع-خارجی-9) + - [کلیدواژه‌های `Extends` و `super`](#کلیدواژههای-extends-و-super) + - [نمونه کد](#نمونه-کد-6) + - [منابع خارجی](#منابع-خارجی-10) + - [استفاده از Async Await](#استفاده-از-async-await) + - [نمونه کد](#نمونه-کد-7) + - [توضیح با نمونه کد](#توضیح-با-نمونه-کد) + - [مدیریت خطا](#مدیریت-خطا) + - [منابع خارجی](#منابع-خارجی-11) + - [درستی / غلطی](#درستی-غلطی) + - [منابع خارجی](#منابع-خارجی-12) + - [توابع Anamorphisms و Catamorphisms](#توابع-anamorphisms-و-catamorphisms) + - [تابع Anamorphisms](#تابع-anamorphisms) + - [نمونه کد](#نمونه-کد-8) + - [تابع Catamorphisms](#تابع-catamorphisms) + - [نمونه کد](#نمونه-کد-9) + - [منابع خارجی](#منابع-خارجی-13) + - [تولیدکننده‌ها](#تولیدکنندهها) + - [نمونه کد](#نمونه-کد-10) + - [منابع خارجی](#منابع-خارجی-14) + - [متدهای ایستا](#متدهای-ایستا) + - [توضیح کوتاه](#توضیح-کوتاه-1) + - [نمونه کد](#نمونه-کد-11) + - [توضیح مبسوط](#توضیح-مبسوط-2) + - [فراخوانی متد ایستا توسط متد ایستای دیگر](#فراخوانی-متد-ایستا-توسط-متد-ایستای-دیگر) + - [فراخوانی متدهای ایستا توسط متدهای غیرایستا](#فراخوانی-متدهای-ایستا-توسط-متدهای-غیرایستا) + - [منابع خارجی](#منابع-خارجی-15) + - [واژه‌نامه](#واژهنامه) + - [قلمرو](#قلمرو) + - [تغییر متغیر](#تغییر-متغیر) + +## مفاهیم + +### اعلان متغیر: var، const، let + +در جاوااسکریپت سه کلیدواژه برای اعلان یک متغیر وجود دارند که هر یک دارای ویژگی‌های خاص خود هستند. این کلیدواژه‌ها عبارت‌اند از `var` و `let` و `const`. + +#### توضیح کوتاه + +متغیرهایی که با `const` اعلان می‌شوند را نمی‌توان دوباره مقداردهی کرد در حالی که اگر همان متغیر با `var` یا `let` تعریف می‌شد، امکان این کار فراهم می‌بود. + +پیشنهاد می‌کنم همیشه متغیرهای خود را به طور پیش‌فرض با `const` تعریف کنید مگر این که بخواهید آن را بعدا تغییر داده یا مقداردهی کنید که در این حالت پیشنهادم استفاده از `let` است. + +| |قلمرو|قابل مقداردهی|قابل تغییر|محدوده مرگ زمانی| +| --- | --- | --- | --- | --- | +|const|بلوک|خیر|بله|بله| +|let|بلوک|بله|بله|بله| +|var|تابع|بله|بله|خیر| + +#### نمونه کد +```javascript +const person = "Nick"; +person = "John" // خطایی اعلام می‌شود که مقدار متغییر نمی‌تواند تغییر کند +``` + +```javascript +let person = "Nick"; +person = "John"; +console.log(person) // "John", بازمقداردهی مجاز است +``` + +#### توضیح مبسوط +[قلمرو](#قلمرو) یک متغیر تقریبا به این معناست که «این متغیر در کجای کد قابل دسترس است». + +##### استفاده از var +متغیرهایی که با `var` اعلان می‌شوند *در قلمروی تابع* دردسترس‌اند به این معنا که وقتی متغیری در یک تابع تعریف شود، هر چیزی در آن تابع می‌توان به آن متغیر دسترسی داشه باشد. در کنار این، یک متغیر *در قلمروی تابع* که داخل یک تابع تعریف شده است نمی‌تواند خارج از آن تابع در دسترس باشد. + +پیشنهاد می‌کنم تصور کنید که یک متغیر *در قلمروی X* مانند آن است که آن متغیر، یکی خصوصیت (property) X باشد. + + +```javascript +function myFunction() { + var myVar = "Nick"; + console.log(myVar); // "Nick" - متغیر داخل تابع در دسترس است +} +console.log(myVar); // ReferenceError - متغیر خارج از تابع در دسترس نیست +``` +و این مثال: + +```javascript +function myFunction() { + var myVar = "Nick"; + if (true) { + var myVar = "John"; + console.log(myVar); // "John" + // چون متغیر در قلروی تابع است، مقدارش با مقدار جدید جایگزین می‌شود. + } + console.log(myVar); // "John" - ببینید که دستورات داخل شرط چه طور روی مقدار این متغیر اثر می‌گذارد +} +console.log(myVar); // ReferenceError - متغیر خارج از تابع در دسترس نیست +``` + +افزون بر این، متغییرهایی که توسط `var` اعلان می‌شوند به بالای قلمرو در زمان اجرا منتقل می‌گردند.این چیزی است که آن را [var hoisting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting) می‌خوانیم. + +این بخش از کد: + +```js +console.log(myVar) // undefined -- no error raised +var myVar = 2; +``` + +در زمان اجرا به این شکل فهمیده می‌شود: + +```js +var myVar; +console.log(myVar) // undefined -- no error raised +myVar = 2; +``` + +##### استفاده از let +شباهت‌هایی زیادی میان `let` و `var` وجود دارد اما متغیرهایی که با `let` تعریف می‌شوند: + +- *در قلمروی بلوک* هستند +- پیش از مقداردهی قابل دسترسی **نیستند** +- نمی‌توانند در همان قلمرو بازاعلان شوند + +بگذارید اثر تعریف متغیر در قلمروی بلوک را در همان مثال‌های پیشین ببینیم: + +```javascript +function myFunction() { + let myVar = "Nick"; + if (true) { + let myVar = "John"; + console.log(myVar); // "John" + // چون myVar یک متغیر در قلمروی بلوک است، در واقع یک متغیر جدید اعلان کرده‌ایم. + // این متغیر خارج از این بلوک قابل دسترس نیست و در حقیقت، کاملا مستقل + // از آن متغیر myVar است که اول تعریف شده بود. + } + console.log(myVar); // "Nick", ببینید که چه طور دستورات داخل شرط، تاثیری روی متغیر بیرون شرط ندارد +} +console.log(myVar); // ReferenceError - متغیر خارج از تابع قابل دسترس نیست +``` + +حال ببینیم که یعنی چه که متغیرهای `let` (و `const`) پیش از مقداردهی دسترسی نیستند: + +```js +console.log(myVar) // ReferenceError ! +let myVar = 2; +``` + +بر خلاف متغیرهای `var`، اگر تلاش کنید یک متغیر `let` یا `const` را پیش از مقداردهی بخوانید یا رویش بنویسید، خطا دریافت خواهید کرد. این پدیده معمولا با نام *محدوده مرگ زمانی* یا [*Temporal dead zone*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_Dead_Zone_and_errors_with_let) یا به اختصار *TDZ* خوانده می‌شود. + +> **نکته:** از منظر فنی، اعلان‌های متغیر با `let` و `const` نیز بالاتر اجرا می‌شوند (hoist) اما مقداردهی‌شان نه. از همین رو که این گونه ساخته شده‌اند که تا پیش از مقداردهی، قابل استفاده نباشند این حس ایجاد می‌شود که گویی مورد hoist قرار نگرده‌اند در حالی که قرار گرفته‌اند. اگر می‌خواهید بیشتر بدانید، به [این توضیح بسیار مبسوط](http://jsrocks.org/2015/01/temporal-dead-zone-tdz-demystified) مراجعه کنید. + +به علاوه، نمی‌توانید متغیرهای `let` را مجددا اعلان کنید: + +```js +let myVar = 2; +let myVar = 3; // SyntaxError +``` + +##### استفاده از const + +متغیرهای اعلان شده با `const` مشابه متغیرهای `let` رفتار می‌کنند با این تفاوت که قابل مقداردهی مجدد نیستند. به طور خلاصه، متغیرهای `const`: + +- *در قلمروی بلوک* هستند +- پیش از مقداردهی قابل دسترسی نیستند +- نمی‌توانند در همان قلمرو بازاعلان شوند +- نمی‌توانند مقداردهی مجدد شوند + +```js +const myVar = "Nick"; +myVar = "John" // خطایی می‌بینید که مقداردهی مجدد ممکن نیست +``` + +```js +const myVar = "Nick"; +const myVar = "John" // خطایی می‌بینید که اعلان مجدد ممکن نیست +``` + +اما یک نکته ظریف در این جا وجود دارد: متغیرهای `const` [تغییرناپذیر](#تغییر-متغیر) یا immutable نیستند! به معنای دقیق‌تر، آرایه‌ها و شیءهایی که با `const` اعلان شده‌اند **می‌توانند** تغییر کنند. + +برای شی‌ءها: + +```js +const person = { + name: 'Nick' +}; +person.name = 'John' // کار می‌کند چون این متغیر کاملا مقداردهی نشده، می‌تواند تغییر کند +console.log(person.name) // "John" +person = "Sandra" // خطا دریافت خواهید کرد چون قابل مقداردهی مجدد نیست +``` + +برای آرایه‌ها: + +```js +const person = []; +person.push('John'); // کار می‌کند چون این متغیر کاملا مقداردهی نشده، می‌تواند تغییر کند +console.log(person[0]) // "John" +person = ["Nick"] // خطا دریافت خواهید کرد چون قابل مقداردهی مجدد نیست +``` + +#### منابع خارجی + + +- [How let and const are scoped in JavaScript - WesBos](http://wesbos.com/javascript-scoping/) +- [Temporal Dead Zone (TDZ) Demystified](http://jsrocks.org/2015/01/temporal-dead-zone-tdz-demystified) + +### تابع پیکانی + +در به‌روزرسانی ES6 جاوااسکریپت، *توابع پیکانی* یا Arrow Function معرفی شدند که روشی دیگری برای اعلان و استفاده از توابع هستند. برخی از فواید این نوع توابع عبارت‌اند از: + +- مختصرتر هستند +- مقدار *this* از محیط برداشته می‌شود +- بازگشت تلویحی + +#### نمونه کد + +- اختصار و بازگشت تلویحی + +```js +function double(x) { return x * 2; } // روش سنتی +console.log(double(2)) // 4 +``` + +```js +const double = x => x * 2; // همان تابع به صورت تابع پیکانی با مقدار بازگشتی تلویحی +console.log(double(2)) // 4 +``` + +- ارجاع *this* + +در تابع پیکانی، *this* معادل مقدار *this* در بافتار اجرای محاط بر تابع است. اساسا با توابع پیکانی شما نیازی به ترفند `that = this` پیش از فراخوانی تابع دیگری داخل یک تابع ندارید. + +```js +function myFunc() { + this.myVar = 0; + setTimeout(() => { + this.myVar++; + console.log(this.myVar) // 1 + }, 0); +} +``` + +#### توضیح مبسوط + +##### اختصار + +توابع پیکانی در قیاس با توابع سندی به روش‌های زیادی مختصرتر هستند. بگذارید همه حالت‌های ممکن را مرور کنیم: + +- بازگشت تصریحی در مقابل تلویحی + +**بازگشت تصریحی** تابعی است که در آن کلیدواژه *return* در بدنه تابع استفاده شده باشد. + +```js + function double(x) { + return x * 2; // صریحا از کلیدواژه بازگشت استفاده شده است + } +``` + +در روش سنتی نوشتن تابع، بازگشت همیشه صریح بوده است. اما با توابع پیکانی، می‌توانید *بازگشت تلویحی* داشه باشید که یعنی برای بازگرداندن یک مقدار، نیازی به استفاده از کلیدواژه *return* ندارید. + +```js + const double = (x) => { + return x * 2; // بازگشت صریح + } +``` + +از آن جایی که این تابع فقط چیزی را بر می‌گرداند (فاقد هر گونه دستورالعملی پیش از کلیدواژه *return* است) می‌توانیم از بازگشت تلویحی استفاده کنیم. + +```js + const double = (x) => x * 2; +``` + +برای این منظور کافی است که کلیدواژه **return** و **گیومه‌ها را برداریم**. به همین دلیل است که این روش، بازگشت *تلویجی* خوانده می‌شود. کلیدواژه *return* وجود ندارد اما این تابع در واقع مقدار `x * 2` را برمی‌گرداند. + +> **توجه:** اگر تابع شما مقداری (با *اثر جانبی*) بر نمی‌گرداند، در حقیقت بازگشت تلویحی یا تصریحی انجام نمی‌دهد. + +در کنار این، اگر شما بخواهید تلویحا یک *شیء* را برگردانید **لازم است که آن را در پرانتز قرار دهید** چون در غیر این صورت، با آکولادهای بلوک دچار تداخل می‌شود. + +```js +const getPerson = () => ({ name: "Nick", age: 24 }) +console.log(getPerson()) // { name: "Nick", age: 24 } -- شیء به صورت تلویحی با کمک تابع پیکانی بازگشت داده شده است +``` + +- تنها یک آرگومان + +اگر تابع شما تنها یک پارامتر دریافت می‌کند می‌توانید پرانتز را از اطراف آن بردارید. اگر کد **double** بالایی را در نظر بگیریم: + +```js + const double = (x) => x * 2; // این تابع پیکانی تنها یک پارامتر دریافت می‌کند +``` + +پرانتزهای اطراف پارامتر می‌توانند برداشته شوند. + +```js + const double = x => x * 2; // این تابع پیکانی تنها یک پارامتر دریافت می‌کند +``` + +- بدون آرگومان + +وقتی ورودی‌ای به تابع پیکانی وجود ندارد باید یک جفت پرانتز وارد کنید مگر نه خطای نحوی دریافت خواهید کرد. + +```js + () => { + // پرانتز درج شده و همه چیز درست کار می‌کند + const x = 2; + return x; + } +``` + +```js + => { + // پرانتز درج نشده و کد اجرا نخواهد شد + const x = 2; + return x; + } +``` + +##### ارجاع *this* + +برای درک این ویژگی تازه معرفی شده همراه توابع پیکانی لازم است بدانید که **this** چگونه در جاوااسکریپت رفتار می‌کند. + +در یک تابع پیکانی، *this* معادل *this* در بافتار اجرایی محاط بر تابع است. این یعنی که تابع پیکانی به جای ایجاد یک *this* جدید، آن را از محیطی که در آن احاطه شده می‌قاپد. + +بدون تابع پیکانی اگر شما بخواهید از طریق *this* به یک متغیر در تابعی داخل یک تابع دسترسی پیدا کنید لازم است که از ترفند *that = this* یا *self = this* استفاده کنید. + +به عنوان مثال، استفاده کردن از تابع setTimeout داخل تابع myFunc: + +```js +function myFunc() { + this.myVar = 0; + var that = this; // that = this trick + setTimeout( + function() { // یک‫ this جدید در قلمروی این تابع ساخته می‌شود + that.myVar++; + console.log(that.myVar) // 1 + + console.log(this.myVar) // undefined -- اعلان تابع را ببینید + }, + 0 + ); +} +``` + +اما با تابع پیکانی، *this* از محیط پیرامونی‌اش گرفته می‌شود: + +```js +function myFunc() { + this.myVar = 0; + setTimeout( + () => { + // اینجا this از محیط پیرامونی‌اش یعنی تابع myFunc گرفته می‌شود + this.myVar++; + console.log(this.myVar) // 1 + }, + 0 + ); +} +``` + +#### منابع مفید + +- [Arrow functions introduction - WesBos](http://wesbos.com/arrow-functions/) +- [JavaScript arrow function - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) +- [Arrow function and lexical *this*](https://hackernoon.com/javascript-es6-arrow-functions-and-lexical-this-f2a3e2a5e8c4) + + +### مقدار پیش‌فرض پارامتر تابع + +از به‌روزرسانی ES2015 جاوااسکریپت می‌توانید با نحو زیر، مقدار پیش‌فرض برای پارامترهای تابع تنظیم کنید: + +```js +function myFunc(x = 10) { + return x; +} +console.log(myFunc()) // 10 +console.log(myFunc(5)) // 5 + +console.log(myFunc(undefined)) // 10 +console.log(myFunc(null)) // null +``` + +پارامتر پیش‌فرض تنها در دو و فقط دو موقعیت اعمال می‌شود: + +- پارامتری ارائه نشده باشد +- پارامتر *undefined* ارائه شده باشد + +به بیان دیگر، اگر مقدار *null* را به پارامتر پیش‌فرض بدهید، این مقدار **اعمال نخواهد شد**. + +> **نکته:** تخصیص مقدار پیش‌فرض می‌تواند با پارامترهای destructured هم استفاده شود (مفهوم بعدی را برای مثال ببینید) + +#### منابع خارجی + +- [Default parameter value - ES6 Features](http://es6-features.org/#DefaultParameterValues) +- [Default parameters - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters) + +### تجزیه اشیاء و آرایه‌ها + +*تجزیه* (destructuring) روشی آسان برای ساخت متغیرهای جدید از راه استخراج مقادیر از داده‌های ذخیره شده در اشیاء یا آرایه‌هاست. + +به عنوان مثال، *تجزیه کردن* می‌تواند به منظور تجزیه پارامترهای تابع و یا *this.props* در پروژه‌های ری‌اکت مورد استفاده قرار گیرد. + +#### توضیح با مثال + +- شیء + +بگذارید شیء زیر را برای تمام مثال‌ها در نظر بگیریم: + +```js +const person = { + firstName: "Nick", + lastName: "Anderson", + age: 35, + sex: "M" +} +``` + +بدون تجزیه کردن: + +```js +const first = person.firstName; +const age = person.age; +const city = person.city || "Paris"; +``` + +با تجزیه کردن، تماما در یک خط: + +```js +const { firstName: first, age, city = "Paris" } = person; // همین و بس + +console.log(age) // 35 -- ‫متغیر جدید age ساخته شده و معادل است با person.age +console.log(first) // "Nick" -- ‫متغیر جدید first ساخته شده و معادل است با person.firstName +console.log(firstName) // ReferenceError -- ‫در واقع person.firstName وجود دارد اما نام متغیر جدیدی که ساخته شده،first است +console.log(city) // "Paris" -- ‫متغیر جدیدی به نام city ساخته شده است اما چون person.city تغیرنشده است، مقدار موجود در city برابر است با مقدار پیش‌فرض ارائه شده یعنی Paris +``` + +**نکته:** آکولادهای استفاده شده در `const { age } = person` بعد از *const* نه برای اعلان شیء یا بلوک بلکه به عنوان نحو تجزیه نوشته شده است. + +- پارامترهای تابع + +*تجزیه کردن* معمولا برای تجزیه پارامترهای شیء در توابع استفاده می‌شود. + +بدون تجزیه کردن + +```js +function joinFirstLastName(person) { + const firstName = person.firstName; + const lastName = person.lastName; + return firstName + '-' + lastName; +} + +joinFirstLastName(person); // "Nick-Anderson" +``` + +با تجزیه کردن پارامتر *person* شیء، ما به یک تابع جمع و جورتر دست پیدا می‌کنیم: + +```js +function joinFirstLastName({ firstName, lastName }) { // ‫ما متغیرهای firstName و lastName را با تجزیه پارامترهای شیء person به دست می‌آوریم + return firstName + '-' + lastName; +} + +joinFirstLastName(person); // "Nick-Anderson" +``` + +استفاده از تجزیه به خصوص در زمان استفاده از توابع پیکانی، شیرین‌تر هم می‌شود: + +```js +const joinFirstLastName = ({ firstName, lastName }) => firstName + '-' + lastName; + +joinFirstLastName(person); // "Nick-Anderson" +``` + + +- آرایه + +آرایه زیر را در نظر بگیریم: + +```js +const myArray = ["a", "b", "c"]; +``` + +بدون تجزیه کردن + +```js +const x = myArray[0]; +const y = myArray[1]; +``` + +با تجزیه کردن + +```js +const [x, y] = myArray; // همین و پس + +console.log(x) // "a" +console.log(y) // "b" +``` + +#### منابع مفید + +- [ES6 Features - Destructuring Assignment](http://es6-features.org/#ArrayMatching) +- [Destructuring Objects - WesBos](http://wesbos.com/destructuring-objects/) +- [ExploringJS - Destructuring](http://exploringjs.com/es6/ch_destructuring.html) + +### متدهای آرایه - map / filter / reduce / find + +خواستگاه متدهای *Map*، *filter*، *reduce* و *find* برای آرایه، یک پارادایم برنامه‌نویسی به نام برنامه‌نویسی تابعی یا [*functional programming*](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0) است. + +به طور خلاصه: + +- متد ‪**Array.prototype.map()** ‬ یک آرایه دریافت می‌کند، کاری روی تک تک اعضای آن انجام می‌دهد و آرایه‌ای حاوی اعضای تغییریافته باز می‌گرداند. +- متد ‪**Array.prototype.filter()** ‬ یک آرایه دریافت می‌کند، عضو به عضو پیش می‌رود و تصمیم می‌گیرد که آیا آن عضو در آرایه‌ای که بازگشت داده خواهد شد باقی بماند یا خیر. نهایتا هم یک آرایه از اعضای تایید شده را باز می‌گرداند. +- متد ‪**Array.prototype.reduce()** ‬ یک آرایه دریافت ی‌کند اعضای آن را روی هم ریخته و به صورت یک مقدار واحد باز می‌گرداند. +- متد ‪**Array.prototype.find()** ‬ یک آرایه دریافت می‌کند و اولین عضوی را که شرط اعلام شده را داشته باشد باز می‌گرداند. + +پیشنهاد می‌کنم که تا می‌توانید از اصول برنامه‌نویسی تابعی بهره ببرید زیرا قابل ترکیب، موجز و زیبا هستند. + +با این چهار متدی که اشاره شد، می‌توانید در موارد بسیاری از به کار بردن حلقه *for* و *forEach* اجتناب کنید. به جای حلقه *for* سعی کنید از ترکیب این چهار متد استفاده کنید. شاید در نگاه اول قدری چالش برانگیز باشد. طبیعی است چون لازم است روش جدیدی برای اندیشیدن را فرابگیرید. اما وقتی این مهارت را فرابگیرید، کارها آسان‌تر می‌شوند. + +#### نمونه کد + +```js +const numbers = [0, 1, 2, 3, 4, 5, 6]; +const doubledNumbers = numbers.map(n => n * 2); // [0, 2, 4, 6, 8, 10, 12] +const evenNumbers = numbers.filter(n => n % 2 === 0); // [0, 2, 4, 6] +const sum = numbers.reduce((prev, next) => prev + next, 0); // 21 +const greaterThanFour = numbers.find((n) => n>4); // 5 +``` +محاسبه مجموع نمرات دانش‌آموزانی که نمره ۱۰ یا بالاتر گرفته‌اند با کمک ترکیب متدهای map و filter و reduce: + +```js +const students = [ + { name: "Nick", grade: 10 }, + { name: "John", grade: 15 }, + { name: "Julia", grade: 19 }, + { name: "Nathalie", grade: 9 }, +]; + +const aboveTenSum = students + .map(student => student.grade) // ‫ما آرایه student را روی آرایه‌ای از نمره‌های‌شان نگاشت (map) کردیم + .filter(grade => grade >= 10) // ‫ما آرایه grades را به منظور نگه‌داشتن نمره‌های ۱۰ و بالاتر مورد پالایش (filter) قرار دادیم + .reduce((prev, next) => prev + next, 0); // همه نمره‌های ۱۰ و بالاتر را یک به یک با هم جمع کردیم + +console.log(aboveTenSum) // 44 -- 10 (Nick) + 15 (John) + 19 (Julia), Nathalie below 10 is ignored +``` + +#### توضیح + +بیایید آرایه اعداد زیر را به عنوان مثلا در نظر بگیریم: + +```js +const numbers = [0, 1, 2, 3, 4, 5, 6]; +``` + +##### متد ‪Array.prototype.map() + +```js +const doubledNumbers = numbers.map(function(n) { + return n * 2; +}); +console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12] +``` + +چه شد؟ ما از متد ‪.map‬ روی آرایه *numbers* استفاده می‌کنیم. این متد می‌آید و روی یکایک اعضای آرایه حرکت می‌کند و مقدار آن‌ها را به تابع ما می‌دهد. هدف این تابع، تولید و بازگرداندن مقداری جدید بر اساس مقداری است که به آن داده شده و در نتیجه متد نگاشت (map)‌ می‌تواند مقدار جدید را جایگزین کند. + +بگذارید فقط برای این مثال، این تابع را باز کرده و واضح‌ترش کنیم: + +```js +const doubleN = function(n) { return n * 2; }; +const doubledNumbers = numbers.map(doubleN); +console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12] +``` + +**نکته:** شما به دفعات خواهید دید که این متد در ترکیب با توابع پیکانی استفاده می‌شود. + +```js +const doubledNumbers = numbers.map(n => n * 2); +console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12] +``` + +در این جا، ```numbers.map(doubleN)``` آرایهٔ ```[doubleN(0), doubleN(1), doubleN(2), doubleN(3), doubleN(4), doubleN(5), doubleN(6)]``` تولید می‌کند که معادل است با ```[0, 2, 4, 6, 8, 10, 12]```. + +> **نکته:** اگر به جای بازگرداندن آرایه جدید فقط به دنبال ایجاد یک حلقهٔ دارای اثر جانبی هستید احتمالا باید به سراغ همان for یا forEach بروید نه map. + +##### متد ‪Array.prototype.filter() + +```js +const evenNumbers = numbers.filter(function(n) { + return n % 2 === 0; // ‫اگر n زوج باشد مقدار true و در غیر این صورت false +}); +console.log(evenNumbers); // [0, 2, 4, 6] +``` + +**نکته:** شما به دفعات خواهید دید که این متد در ترکیب با توابع پیکانی استفاده می‌شود. + +```js +const evenNumbers = numbers.filter(n => n % 2 === 0); +console.log(evenNumbers); // [0, 2, 4, 6] +``` + +ما از متد ‪.filter‬ روی آرایه *numbers* استفاده می‌کنیم. این متد روی یکایک اعضای آرایه حرکت کرده و آن‌ها را به تابع ما می‌دهد. هدف تابع این است که یک مقدار بولی بازگرداند که مبنای تصمیم ما برای نگه داشتن آن عضو است. نهایتا filter آرایه‌ای از مقادیری را که نگه‌داشته است باز می‌گرداند. + +##### متد ‪Array.prototype.reduce() + +هدف متد کاهش (reduce) حرکت روی یکایک اعضای یک آرایه و کاستن آن‌ها به یک مقدار واحد است. این که چه طور این اعضا روی هم ریخته شوند به شما بستگی دارد. + +```js +const sum = numbers.reduce( + function(acc, n) { + return acc + n; + }, +‪ 0 // این، مقدار متغیر انباشت‌گر در نخستین گام محاسبه است +); + +console.log(sum) // 21 +``` + +**نکته:** شما به دفعات خواهید دید که این متد در ترکیب با توابع پیکانی استفاده می‌شود. + +```js +const sum = numbers.reduce((acc, n) => acc + n, 0); +console.log(sum) // 21 +``` + +درست هماهنند متدهای map و filter، متد reduce روی یک آرایه اعمال شده و یک تابع را به عنوان اولین پارامتر دریافت می‌کند. + +این بار اما تفاوت‌هایی وجود دارد: + +- متد ‪.reduce‬ دو پارامتر دریافت می‌کند + +اولی پارامتر یک تابع است که در هر گام از حرکت روی اعضای آرایه فراخوانده می‌شود. + +پارامتر دوم، مقدار متغیر انباشت‌گر (در این جا *acc*) در نخستین گام محاسبه است (برای فهمیدن، مورد بعدی را بخوانید). + +- پارامترهای تابع + +تابعی که به عنوان اولین پارامتر به ‪.reduce‬ می‌دهید دو پارامتر می‌خواهد. اولی (در این جا *acc*) متغیر انباشت‌گر و دومین پارامتر (*n*) عضو جاری آرایه است. + +متغیر انباشت‌گر برابر است با مقدار بازگشت داده شده از تابع در گام **قبلی** محاسبه. در اولین گام از محاسبه، *acc* برابر است با مقداری که در دومین پارامتر ‪.reduce‬ وارد شده است. + +###### در گام نخست + +مقدار acc برابر صفر است زیرا در دومین پارامتر reduce مقدار صفر وارد شده است. + +مقدار n هم صفر است چون اولین عضو آرایه *number* چنین مقداری دارد. + +تابع، مقدار ‪*acc* + *n* --> 0 + 0 --> 0‬ را برمی‌گرداند. + +###### در گام دوم + +مقدار acc برابر صفر است چون تابع ما در مرحله قبل این مقدار را بازگردانده بود. + +این بار n برابر یک است که دومین عضو آرایه *number* است. + +این بار تابع مقدار ‪*acc* + *n* --> 0 + 1 --> 1‬ را بازمی‌گرداند. + +###### در گام سوم + +مقدار acc برابر یک است زیرا تابع ما در مرحله قبل این مقدار را بازگردانده بود. + +این بار n برابر با ۲ است که سومین عضو آرایه *number* است. + +تابع، مقدار ‪*acc* + *n* --> 1 + 2 --> 3‬ را باز می‌گرداند. + + +###### در چهارمین گام + +مقدار acc برابر ۳ است زیرا تابع ما در مرحله قبل این مقدار را بازگردانده بود. + +این بار n برابر با ۳ است که چهارمین عضو آرایه *number* است. + +تابع، مقدار ‪*acc* + *n* --> 3 + 3 --> 6‬ را باز می‌گرداند. + + +###### نهایتا در آخرین گام + +مقدار acc برابر ۱۵ است زیرا تابع ما در مرحله قبل این مقدار را بازگردانده بود. + +مقدار n برابر با ۶ است که آخرین عضو آرایه *number* است. + +تابع، مقدار ‪*acc* + *n* --> 15 + 6 --> 21‬ را باز می‌گرداند. + +از آن جایی که این، آخرین گام محاسبه است، متد **‪.reduce‬** مقدار ۲۱ را باز می‌گرداند. + +##### متد ‪Array.prototype.find() + +```js +const greaterThanZero = numbers.find(function(n) { + return n > 0; // return number just greater than 0 is present +}); +console.log(greaterThanZero); // 1 +``` + +**نکته:** شما به دفعات خواهید دید که این متد در ترکیب با توابع پیکانی استفاده می‌شود. + +اینجا از متد یافتن یا find روی آرایه *numner* استفاده می‌کنیم. این متد روی یکایک اعضای آرایه حرکت کرده و تا زمانی که شرط تعیین شده برقرار شود آن‌ها را به تابع ما می‌دهد. هدف تابع، بازگرداندن عنصری است که روی آن، شرط موجود در تابع آزمون جاری محقق شود. متد find یک تابع callback را به ازای هر اندیس از آرایه و تا زمانی که آن تابع یک مقدار درست یا true را باز گرداند اجرا می‌کند. + +**نکته:** این متد به محض پیدا کردن عضوی از آرایه که شرط مشخص شده را محقق می‌کند، مقدارش را باز می‌گرداند. در غیر این صورت مقدار undefined را باز خواهد گرداند. + +#### منبع خارجی + +- [Understanding map / filter / reduce in JS](https://hackernoon.com/understanding-map-filter-and-reduce-in-javascript-5df1c7eee464) + +### عملگر گسترش «...» + +عملگر گسترش `...` (spread) با ES2015 معرفی شد و برای گستردن عناصر یک متغیر قابل شمارش یا iterable (مانند یک آرایه) به مکان‌هایی است که چندین عنصر قابل جای‌گذاری هستند. + +#### نمونه کد + +```js +const arr1 = ["a", "b", "c"]; +const arr2 = [...arr1, "d", "e", "f"]; // ["a", "b", "c", "d", "e", "f"] +``` + +```js +function myFunc(x, y, ...params) { + console.log(x); + console.log(y); + console.log(params) +} + +myFunc("a", "b", "c", "d", "e", "f") +// "a" +// "b" +// ["c", "d", "e", "f"] +``` + + +```js +const { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; +console.log(x); // 1 +console.log(y); // 2 +console.log(z); // { a: 3, b: 4 } + +const n = { x, y, ...z }; +console.log(n); // { x: 1, y: 2, a: 3, b: 4 } +``` + +#### توضیح + +##### در شمارش‌پذیرها (مانند آرایه) + +اگر دو آرایه زیر را داشته باشیم: + +```js +const arr1 = ["a", "b", "c"]; +const arr2 = [arr1, "d", "e", "f"]; // [["a", "b", "c"], "d", "e", "f"] +``` + +در آرایه *arr2*، اولین عضو ما یک آرایه است چرا که *arr1* همان‌گونه که هست در *arr2* تزریق شده است. اما چیزی که می‌خواهیم این است که *arr2*، آرایه‌ای از حروف باشد. برای این منظور می‌توانید عناصر *arr1* را در *arr2* *گسترش* دهیم. + +با عملگر گسترش + +```js +const arr1 = ["a", "b", "c"]; +const arr2 = [...arr1, "d", "e", "f"]; // ["a", "b", "c", "d", "e", "f"] +``` + +##### پارامتر باقی (rest) تابع + +در پارامترهای تابع می‌توانیم از عملگر باقی (rest) به منظور تزریق پارامترها به آرایه‌ای که رویش بتوانیم حلقه اجرا کنیم بهره می‌بریم. همواره یک شیء **arguments** همراه با هر تابعی وجود دارد که برابر است با آرایه‌ای از تمام پارامترهایی که به آن تابع حواله شده‌اند. + +```js +function myFunc() { + for (var i = 0; i < arguments.length; i++) { + console.log(arguments[i]); + } +} + +myFunc("Nick", "Anderson", 10, 12, 6); +// "Nick" +// "Anderson" +// 10 +// 12 +// 6 +``` + +اما حالتی را در نظر بگیرید که بخواهیم این تابع یک دانشجوی جدید همراه با معدل نمره‌هایش بسازد. آیا راحت‌تر نیست که که فقط دو پارامتر اول را استخراج کنیم و در دو متغیر مجزا قرار داده و سپس تمام نمره‌ها را آرایه‌ای بریزیم که بشود روی آن شمارش انجام داد؟ + +این دقیقا همان کاری است که عملگر باقی به ما اجازهٔ انجامش را می‌دهد! + +```js +function createStudent(firstName, lastName, ...grades) { + // firstName = "Nick" + // lastName = "Anderson" +‪ // [10, 12, 6] + // به کمک «...» تمام دیگر پارامترهای را دریافت و در آرایه «grades» ذخیره می‌کنیم + + const avgGrade = grades.reduce((acc, curr) => acc + curr, 0) / grades.length; //‫ معدل نمره‌های موجود در آرایه grades را محاسبه می‌کند + + return { + firstName: firstName, + lastName: lastName, + grades: grades, + avgGrade: avgGrade + } +} + +const student = createStudent("Nick", "Anderson", 10, 12, 6); +console.log(student); +// { +// firstName: "Nick", +// lastName: "Anderson", +// grades: [10, 12, 6], +// avgGrade: 9,33 +// } +``` + +> **نکته:** تابع createStudent تابع بدی است زیرا بررسی نمی‌کنیم که اساسا grades.length وجود دارد یا خیر و یا متفاوت از صفر است یا نه. اما چون خوانایی آن به این شکل بهتر بود، آن را این گونه نوشتم. + +##### گسترش خاصیت‌های شیء + +برای این مورد پیشنهاد می‌کنم توضیحات قبلی را درباره عملگر باقی بر روی پارامترهای تابع و عناصر شمارش‌پذیر (iterables) بخوانید. + +```js +const myObj = { x: 1, y: 2, a: 3, b: 4 }; +const { x, y, ...z } = myObj; // تجزیه شیء +console.log(x); // 1 +console.log(y); // 2 +console.log(z); // { a: 3, b: 4 } + +// متغیر z حاوی باقی مقادیر حاصل از تجزیهٔ شیء است: شیء myObj منهای مقادیر تجزیه شدهٔ x و y + +const n = { x, y, ...z }; +console.log(n); // { x: 1, y: 2, a: 3, b: 4 } + +// اینجا z حاوی خاصیت‌های شیء است که در n گسترانده می‌شود. +``` + +#### منابع خارجی + +- [TC39 - Object rest/spread](https://github.com/tc39/proposal-object-rest-spread) +- [Spread operator introduction - WesBos](https://github.com/wesbos/es6-articles/blob/master/28%20-%20Spread%20Operator%20Introduction.md) +- [JavaScript & the spread operator](https://codeburst.io/javascript-the-spread-operator-a867a71668ca) +- [6 Great uses of the spread operator](https://davidwalsh.name/spread-operator) + +### میان‌بر خاصیت شیء + +در زمان نسبت دادن یک متغیری به یک خاصیت شیء، اگر هر دو دارای نام یک‌سان باشند می‌توانیم این کار را انجام دهیم: + +```js +const x = 10; +const myObj = { x }; +console.log(myObj.x) // 10 +``` + +#### توضیح + +معمولا (پیش از ES2015) زمانی که یک *object literal* جدید تعریف می‌کنیم و بخواهیم متغیرها را به عنوان مقادیر خاصیت‌های شیء استفاده کنیم، باید چنین کدی بنویسیم: + +```js +const x = 10; +const y = 20; + +const myObj = { + x: x, // ‫نسبت دادن مقدار متغیر x به myObj.x + y: y // ‫نسبت دادن مقدار متغیر y به myObj.y +}; + +console.log(myObj.x) // 10 +console.log(myObj.y) // 20 +``` +همان‌طور که می‌بینید، این کار خیلی تکراری است زیرا نام خاصیت‌های myObj دقیقا همان نام‌های متغیرهایی است که می‌خواهیم نسبت دهیم. + +با معرفی ES2015، اگر نام متغیر همان نامی خاصیت باشد می‌توانیم از این روش میان‌بر استفاده کنیم: + +```js +const x = 10; +const y = 20; + +const myObj = { + x, + y +}; + +console.log(myObj.x) // 10 +console.log(myObj.y) // 20 +``` + +#### منابع خارجی + +- [Property shorthand - ES6 Features](http://es6-features.org/#PropertyShorthand) + +### وعده‌ها (Promises) + +وعده یا promise یک شیء است که می‌تواند به صورت هم‌گام از یک تابع ناهم‌گام بازگردانده شود. ([ارجاع](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261#3cd0)). + +وعده‌ها می‌توانند برای پرهیز از دوزخ callback یا [callback hell](http://callbackhell.com/) استفاده شوند که در پروژه‌های مدرن جاوااسکریپت به کرات بروز می‌کنند. + + +#### نمونه کد + +```js +const fetchingPosts = new Promise((res, rej) => { + $.get("/posts") + .done(posts => res(posts)) + .fail(err => rej(err)); +}); + +fetchingPosts + .then(posts => console.log(posts)) + .catch(err => console.log(err)); +``` + +#### توضیح + +وقتی که یک *درخواست ای‌جکس* می‌فرستید، پاسخ به‌هنگام یا هم‌گام نیست زیرا زمانی طول می‌کشد تا پاسخ این درخواست دریافت شود. حتی ممکن است به به دلیل دردسترس نبودن خدمت مورد نظر، این پاسخ هرگز به دست ما نرسد. + +برای مدیریت چنین شرایطی، ES2015 *وعده‌ها* یا Promises را به ما هدیه داده است. وعده‌ها می‌توانند یکی از سه حالت وضعیت را داشته باشند: + +- معلق (Pending) +- محقق شده (Fulfilled) +- رد شده (Rejected) + +در نظر بگیرید که می‌خواهیم از وعده‌ها برای مدیریت یک درخواست ای‌جکس برای واکشی منبع X استفاده کنیم. + +##### ساخت یک وعده + +ابتدا یک وعده را می‌سازیم. از متد get جی‌کوئری برای فرستادن درخواست ای‌جکش به X استفاده خواهیم کرد. + +```js +const xFetcherPromise = new Promise( // ‫با کلیدواژه new یک وعده می‌سازیم و آن را در یک متغیر ذخیره می‌کنیم + function(resolve, reject) { //‫ سازنده وعده یک پارامتر تابع دریافت می‌کند که خودش دو پارامتر resolve و reject دارد + $.get("X") // درخواست ای‌جکس را می‌فرستیم + .done(function(X) { //‫ وقتی درخواست فرستاده شد... + resolve(X); //‫ ...وعده با مقدار X به عنوان پارامتر رفع می‌شود + }) + .fail(function(error) { //‫ اگر درخواست با شکست مواجه شود... + reject(error); //‫ ...وعده با خطایی به عنوان پارامتر رد می‌شود + }); + } +) +``` + +همان طور که در مثال بالا دیده می‌شود، شیء Promise یک تابع *اجراکننده* (executor) دریافت می‌کند که خودش دو پارامتر **resolve** و **reject** می‌گیرد. این پارامترها توابعی هستند که وقتی صدا زده شوند، متناسب با شرایطی که حادث می‌شود وضعیت وعده را به *محقق شده* یا *رد شده* تغییر می‌دهند. + +وعده بعد از این جا ساخته می‌شود وارد وضعیت معلق می‌شود و تابع *اجرا کننده* بلادرنگ اجرا می‌شود. به محض صدا زده شدن *resolve* یا *reject* در تابع اجرا کننده، وعده اقدام به فراخوانی گرداننده (handler) مرتبط می‌کند. + +##### به‌کارگیری گرداننده وعده + +برای دریافت نتیجه (یا خطای) وعده بایستی به این شکل گرداننده‌ای به آن الصاق کنیم: + +```js +xFetcherPromise + .then(function(X) { + console.log(X); + }) + .catch(function(err) { + console.log(err) + }) +``` + +اگر وعده با موفقیت انجام شود، *resolve* اجرا شده و تابعی به صورت پارامتر ‪`.then`‬ وارد شده است اجرا خواهد شد. + +اگر به شکست بخورد، *reject* اجرا شده و تابعی که به صورت پارامتر به ‪`.catch`‬ وارد شده است اجرا خواهد شد. + +> **نکته:** اگر وعده پیش از الصاق گرداننده، محقق یا رد شده باشد، گرداننده فراخوانده خواهد شد. مسابقه‌ای بین کامل شدن عملیات ناهم‌گام و الصاق شدن گرداننده‌های آن نیست. [ارجاع](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Description) + +#### منابع خارجی + +- [JavaScript Promises for dummies - Jecelyn Yeen](https://scotch.io/tutorials/javascript-promises-for-dummies) +- [JavaScript Promise API - David Walsh](https://davidwalsh.name/promises) +- [Using promises - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) +- [What is a promise - Eric Elliott](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261) +- [JavaScript Promises: an Introduction - Jake Archibald](https://developers.google.com/web/fundamentals/getting-started/primers/promises) +- [Promise documentation - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) + +### قالب لفظی (Template literals) + +قالب لفظی یک «درج عبارت» یا [*expression interpolation*](https://en.wikipedia.org/wiki/String_interpolation) برای رشته‌های تک یا چندخطی است. + +به عبارت دیگر، یک نحو جدید رشته است که در آن می‌توانید به راحتی هر گونه عبارت (مثلا متغیرها) جاوااسکریپتی را استفاده کنید. + +#### نمونه کد + +```js +const name = "Nick"; +`Hello ${name}, the following expression is equal to four : ${2+2}`; + +// Hello Nick, the following expression is equal to four: 4 +``` + +#### منابع خارجی + +- [String interpolation - ES6 Features](http://es6-features.org/#StringInterpolation) +- [ES6 Template Strings - Addy Osmani](https://developers.google.com/web/updates/2015/01/ES6-Template-Strings) + +### قالب لفظی برچسب‌دار + +برچسب‌های قالب *توابعی هستند که می‌توانند به صورت پیش‌وند به [قالب‌های لفظی](#قالب-لفظی-template-literals) متصل شوند*. وقتی یک تابع به این روش صدا زده می‌شود اولین پارامتر، یک آرایه از *رشته‌ها* است که بین متغیرهای درج قالب ظاهرا می‌شوند و پارامتر بعدی مقادری است که باید درج شوند. از یک عملگر گسترش `...` برای دستیابی به همه آن‌ها بهره ببرید. [(مرجع)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals). + +> **نکته:** کتابخانه معروفی به نام [styled-components](https://www.styled-components.com/) به شدت به این ویژگی متکی است. + +این مثالی است از این که این قابلیت چگونه کار می‌کند: + +```js +function highlight(strings, ...values) { + const interpolation = strings.reduce((prev, current) => { + return prev + current + (values.length ? "" + values.shift() + "" : ""); + }, ""); + + return interpolation; +} + +const condiment = "jam"; +const meal = "toast"; + +highlight`I like ${condiment} on ${meal}.`; +// "I like jam on toast." +``` + +مثالی جالب‌تر: + +```js +function comma(strings, ...values) { + return strings.reduce((prev, next) => { + let value = values.shift() || []; + value = value.join(", "); + return prev + next + value; + }, ""); +} + +const snacks = ['apples', 'bananas', 'cherries']; +comma`I like ${snacks} to snack on.`; +// "I like apples, bananas, cherries to snack on." +``` + +#### منابع خارجی +- [Wes Bos on Tagged Template Literals](http://wesbos.com/tagged-template-literals/) +- [Library of common template tags](https://github.com/declandewet/common-tags) + +### درون‌ریزی / برون‌ریزی + +پیمانه‌ها (modules) در ES6 این امکان را فراهم می‌آورند که بتوان به متغیرها و توابعی که توسط یک پیمانه صریحا برون‌ریزی شده‌اند از پیمانه‌ای دیگر که آن پیمانه را درون‌ریزی می‌کنند دسترسی داشت. + +قویا پیشنهاد می‌کنم به منابع موجود روی MDN درباره درون‌ریزی/برون‌ریزی یا import/export (به منابع خارجی این بخش رجوع کنید) نگاهی داشته باشید که هم سرراست است و هم کامل. + +#### توضیح به همراه نمونه کد + +##### برون‌ریزی بانام + +برون‌ریزی بانام برای برون ریزی مقادیر از یک پیمانه مورد استفاده قرار می‌گیرد. + +> **نکته:** فقط می‌توانید [first-class citizens](https://en.wikipedia.org/wiki/First-class_citizen) که دارای نام هستند را به صورت بانام برون‌ریزی کنید. + +```js +// mathConstants.js +export const pi = 3.14; +export const exp = 2.7; +export const alpha = 0.35; + +// ------------- + +// myFile.js +import { pi, exp } from './mathConstants.js'; // درون‌ریزی نام‌دار -- با نحوی مشابه تجزیه کردن +console.log(pi) // 3.14 +console.log(exp) // 2.7 + +// ------------- + +// mySecondFile.js +import * as constants from './mathConstants.js'; +// تزریق همه مقادیر برون‌ریزی شده به متغیر constants +console.log(constants.pi) // 3.14 +console.log(constants.exp) // 2.7 +``` + +در حالی که درون‌ریزی‌های نام‌دار شبیه به *تجزیه کردن* به نظر می‌رسند، اما دارای نحوی متفاوت بوده و مشابه آن نیستند. آن‌ها نه از مقادیر پیش‌فرض پشتیبانی مي‌کنند و نه از تجزیه کردن *عمیق*. + +در کنار این، شما می‌توانید از مترادف‌ها (alias) استفاده کنید اما قواعد نحوی با آن چه که در تجزیه کردن وجود دارد متفاوت است. + +```js +import { foo as bar } from 'myFile.js'; +// در این جا foo درون‌ریزی شده و در متغیر جدیدی به نام bar تزریق شده است. +``` + +##### درون‌ریزی / برون‌ریزی پیش‌فرض + +نظر به برون‌ریزی پیش‌فرض، هر پیمانه تنها می‌تواند یک برون‌ریزی پیش‌فرض داشته باشد. این برون‌ریزی پیش‌فرض می‌تواند یک تابع، یک کلاس، یک شیء و یا هر چیز دیگری باشد. این مقدار مقدار برون‌ریزی اصلی (main) شناخته می‌شود چرا که راحت‌ترین روش برای درون‌ریزی است. [مرجع: MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#Description) + +```js +// coolNumber.js +const ultimateNumber = 42; +export default ultimateNumber; + +// ------------ + +// myFile.js +import number from './coolNumber.js'; +// برون‌ریزی پیش‌فرض، مستقل از این که چه نامی داشته باشد به طور خودکار متغیر number درون‌ریزی شده است. +console.log(number) // 42 +``` + +برون‌ریزی تابع + +```js +// sum.js +export default function sum(x, y) { + return x + y; +} +// ------------- + +// myFile.js +import sum from './sum.js'; +const result = sum(1, 2); +console.log(result) // 3 +``` + +#### منابع خارجی + +- [ES6 Modules in bulletpoints](https://ponyfoo.com/articles/es6#modules) +- [Export - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) +- [Import - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) +- [Understanding ES6 Modules](https://www.sitepoint.com/understanding-es6-modules/) +- [Destructuring special case - import statements](https://ponyfoo.com/articles/es6-destructuring-in-depth#special-case-import-statements) +- [Misunderstanding ES6 Modules - Kent C. Dodds](https://medium.com/@kentcdodds/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution-ad2d5ab93ce0) +- [Modules in JavaScript](http://exploringjs.com/es6/ch_modules.html#sec_modules-in-javascript) + +### مفهوم this در جاوااسکریپت + +رفتار عملگر *this* جاوااسکریپت متفاوت است از دیگر زبان‌ها و در بسیاری موارد به این بستگی دارد که چه طور یک تابع فراخوانی شده باشد. ([مرجع: MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this)) + +این مفهوم ظرافت‌های بسیاری دارد و تا حدودی دشوار است. شدیدا پیشنهاد می‌کنم که شیرجه عمیقی بزنید به منابع خارجی‌ای که پایین‌تر معرفی شده‌اند. با این حال آن چه را که شخصا از چیستی *this* در ذهنم دارم را اینجا ارائه می‌کنم. این نکته را از [این مقاله به قلم یودا کتز](http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/) فراگرفته‌ام. + +```js +function myFunc() { + ... +} +// پس از هر گزاره، مقدار *this* در تابع myFunc را خواهید دید + +myFunc.call("myString", "hello") // "myString" +// مقدار اولین پارامتر ‪.call‬ در *this* تزریق می‌شود + +// در حال غیرسخت‌گیرانه (non-strict mode) +myFunc("hello") // window +// در واقع ‪myFunc()‬ نحو راه‌دست‌تر برای myFunc.call(window,"hello") است + +// در حالت سخت‌گیرانه (strict mode) +myFunc("hello") // undefined +اینجا ‪myFunc()‬ نحو راه‌دست‌تر برای myFunc.call(undefined, "hello") است +``` + +```js +var person = { + myFunc: function() { ... } +} + +person.myFunc.call(person, "test") // ‫شیء person +// اولین پارامتر call در *this* تزریق می‌شود +person.myFunc("test") // ‫شیء person +// اینجا ‪person.myFunc()‬ نحو راه‌دست‌تر برای person.myFunc.call(person, "test") است +var myBoundFunc = person.myFunc.bind("hello") // ‫تابعی جدید می‌سازد که ما «hello» را در *this* آن تزریق می‌کنیم. +person.myFunc("test") // ‫شیء person +// متد bind تاثیر روی متد اصلی ندارد +myBoundFunc("test") // "hello" +//تابع myBoundFunc در واقع همان person.myFunc است که در آن «hello» به *this* بند (bind) زده شده است. +``` + +#### منابع خارجی + +- [Understanding JavaScript Function Invocation and "this" - Yehuda Katz](http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/) +- [JavaScript this - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) + +### کلاس + +جاوااسکریپت یک زبان برنامه‌نویسی بر پایه پیش‌نمونه یا [prototype-based](https://en.wikipedia.org/wiki/Prototype-based_programming) است (در حالی که جاوا یک زبان کلاس محور یا [class-based](https://en.wikipedia.org/wiki/Class-based_programming) است). ES6 کلاس‌های جاوااسکریپت را معرفی کرده است که بیشتر به منظور ارائه نحوی راه‌دست‌تر برای ارث‌بری مبتنی بر پیش‌نمونه است و نه یک مدل ارث‌بری کلاس‌محور ([مرجع](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)). + +اگر با کلاس‌ها در سایر زبان‌ها آشنایی داشته باشید، واژه *class* اینجا در واقع مستعد بروز خطاست. اگر آشنایی دارید، از تصور این که کلاس‌های جاوااسکریپت بر همان اساس کار می‌کنند پرهیز کنید و در نظر بگیرید که مفهومش در اینجا به کلی چیز دیگری است. + +از آن جایی که این سند تلاش ندارد تا زبان را از بیخ آموزش بدهد، فرض خواهم کرد که شما با پیش‌نمونه‌ها (prototypes) و این که چه طور کار می‌کنند آشنایی دارید. اگر آشنایی ندارید به منابع خارجی همین بخش مراجعه کنید. + +#### نمونه‌ها + +نحو پیش‌نمونه‌ها پیش از ES6: + +```js +var Person = function(name, age) { + this.name = name; + this.age = age; +} +Person.prototype.stringSentence = function() { + return "Hello, my name is " + this.name + " and I'm " + this.age; +} +``` + +نحو کلاس پس از ES6: + +```js +class Person { + constructor(name, age) { + this.name = name; + this.age = age; + } + + stringSentence() { + return `Hello, my name is ${this.name} and I am ${this.age}`; + } +} + +const myPerson = new Person("Manu", 23); +console.log(myPerson.age) // 23 +console.log(myPerson.stringSentence()) // "Hello, my name is Manu and I'm 23 +``` + +#### منابع خارجی + +برای فهم پیش‌نمونه: + +- [Understanding Prototypes in JS - Yehuda Katz](http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/) +- [A plain English guide to JS prototypes - Sebastian Porto](http://sporto.github.io/blog/2013/02/22/a-plain-english-guide-to-javascript-prototypes/) +- [Inheritance and the prototype chain - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) + +برای فهم کلاس‌ها: + +- [ES6 Classes in Depth - Nicolas Bevacqua](https://ponyfoo.com/articles/es6-classes-in-depth) +- [ES6 Features - Classes](http://es6-features.org/#ClassDefinition) +- [JavaScript Classes - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) + +### کلیدواژه‌های `Extends` و `super` + +کلیدواژه `extends` در اعلان کلاس یا عبارات کلی برای ساخت یک کلاس که فرزند کلاس دیگری است استفاده می‌شود ([مرجع: MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends)). زیرکلاس تمام خاصیت‌های فراکلاس را به ارث می‌برد و افزون بر این می‌تواند خاصیت‌های جدید اضافه کند یا خاصیت‌های ارث برده را تغییر دهد. + +کلیدواژه `super` برای فراخوانی توابع در والد شیء شامل سازنده (constructor) استفاده می‌شود. + +- کلیدواژه `super` بایستی پیش از کلیدواژه `this` در constructor استفاده شود +- اجرای ‪`super()`‬، سازنده کلاس والد را فرامی‌خواند. برای فرستادن چند آرگومان از سازندهٔ کلاس به سازندهٔ کلاس والد از `super(arguments)` استفاده کنید. +- اگر کلاس والد متدی (حتی ایستا یا static) به نام `X` دارد می‌توانید از ‪`super.X()`‬ برای فراخوانی‌اش در کلاس فرزند استفاده کنید. + +#### نمونه کد + +```js +class Polygon { + constructor(height, width) { + this.name = 'Polygon'; + this.height = height; + this.width = width; + } + + getHelloPhrase() { + return `Hi, I am a ${this.name}`; + } +} + +class Square extends Polygon { + constructor(length) { + // اینجا، سازندهٔ کلاس والد به همراه طول ارائه شده برای + // عرض و ارتفاع چندضلعی فراخوانده می‌شود + super(length, length); + // نکته: در کلاس‌های مشتق شده، ‪super()‬ بایستی پیش از this فراخوانی + // شود. در غیر این صورت خطای reference دریافت خواهید کرد. + this.name = 'Square'; + this.length = length; + } + + getCustomHelloPhrase() { + const polygonPhrase = super.getHelloPhrase(); + // دسترسی به متد والد با نحو ‪super.X() + return `${polygonPhrase} with a length of ${this.length}`; + } + + get area() { + return this.height * this.width; + } +} + +const mySquare = new Square(10); +console.log(mySquare.area) // 100 +console.log(mySquare.getHelloPhrase()) // 'Hi, I am a Square' +// در واقع Square از Polygon ارث بده و به متدهای آن دسترسی دارد +console.log(mySquare.getCustomHelloPhrase()) // 'Hi, I am a Square with a length of 10' +``` + +**نکته:** اگر در کلاس Square پیش از فراخوانی `super()` از `this` استفاده کنید، خطای ReferenceError دریافت خواهید کرد. + +```js +class Square extends Polygon { + constructor(length) { + this.height; // ReferenceError, super needs to be called first! + + // اینجا، سازندهٔ کلاس والد همراه با طول‌های ارائه شده + // برای طول و عرض چندضلعی فراخوانی می‌شود. + super(length, length); + + // توجه: در کلاس‌های مشتق شده، ‪super()‬ باید پیش از this + // فراخوانی شود. در غیر این صورت خطا دریافت خواهید کرد. + this.name = 'Square'; + } +} +``` + +#### منابع خارجی + +- [Extends - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends) +- [Super operator - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super) +- [Inheritance - MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance) + +### استفاده از Async Await + +افزون بر [وعده‌ها](#وعدهها-promises) ممکن است با یک نحو جدید دیگر نیز در مواجهه با کدهای ناهم‌گام مواجه شوید به نام *async / await*. + +هدف توابع async/await، ساده‌سازی رفتار استفاده از وعده‌ها به صورت هم‌گام و انجام برخی رفتارها روی گروهی از وعده‌هاست. درست همان طور که وعده‌ها مشابه callbackهای ساختاردار هستند، async/await مشابه ترکیب سازنده‌ها (generators) و وعده‌هاست. ([مرجمع: MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)) + +> **توجه:** لازم است پیش از تلاش برای فهم async / await بدانید که وعده‌ها چیستند و چگونه کار می‌کنند چرا که شالوده کار همان است. + +> **نکته ۲:** [*await* بایستی با یک تابع *async* استفاده شود](https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9#f3f0) که یعنی نمی‌توانید از await در سطح بالای کدتان استفاده کنید چرا که داخل یک تابع ناهمگام یا async نیست. + +#### نمونه کد + +```js +async function getGithubUser(username) { +// کلیدواژه asunc اجازه استفاده از await را در تابع می‌دهد که یعنی تابع، یک وعده را باز خواهد گرداند. + const response = await fetch(`https://api.github.com/users/${username}`); + // اجرای کد در اینجا متوقف می‌شود تا زمانی که وعده بازگردانده شده از fetch رفع شده و یک ‪responce.json()‬ بازگرداند +} + +getGithubUser('mbeaudru') + .then(user => console.log(user)) // logging user response + // نمی‌توان از نحو await استفاده کرد چون این کد داخل یک تابع async نیست + .catch(err => console.log(err)); // اگر خطایی در تابع ناهم‌گام ما بروز کند اینجا آن را دریافت خواهیم کرد +``` + +#### توضیح با نمونه کد + +در واقع *Async / Await* برروی وعده‌ها ساخته شده اما اجازه می‌دهد به سبکی دستوری‌تر (imperative) کدنویسی کنیم. + +عملگر *async* یک تابع را به عنوان تابع ناهم‌گام علامت‌گذاری می‌کند و همواره یک *وعده* یا Promise باز می‌گرداند. می‌توانید از عملگر *await* در یک تابع *async* به منظور ایجاد مکث در روند اجرا در آن خط تا زمانی که وعده بازگردانده شود استفاده شود. این وعده می‌تواند محقق یا رد شده باشد. + +```js +async function myFunc() { + // می‌توانیم از عملگر await استفاده کنیم زیرا در یک تابع async هستیم + return "hello world"; +} + +myFunc().then(msg => console.log(msg)) // "hello world" +// به واسطه عملگر async، مقدار بازگشتی از myFunc به یک وعده تغییر یافته است +``` + +وقتی به دستور *return* در یک تابع ناهم‌گام یا async می‌رسیم، وعده با مقداری که بازگشت داده می‌شود محقق شده است. اگر خطایی داخل تابع async بروز کند، وضعیت وعده به *رد شده* تغییر می‌کند. اگر مقداری از تابع async بازگردانده نشود، با اتمام اجرای تابع async همچنان وعده‌ای باز گردانده می‌شود که فاقد مقدار است. + +عملگر *await* برای آن استفاده می‌شود که منتظر محقق شدن *وعده* بمانیو و تنها می‌تواند در بدنه یک تابع *async* استفاده شود. به محض رسیدن اجرای برنامه به این خط، چرخه اجرا مکث می‌کند تا زمانی که وعده محقق شود. + +> **توجه:** *fatch* تابعی است که وعده‌ای باز می‌گرداند و اجازه می‌دهد که یک درخواست ای‌جکس بفرستیم. + +بگذارید ابتدا ببینیم چه طور می‌توانیم یک کاربر گیت‌هاب را با fetch دریافت کنیم: + +```js +function getGithubUser(username) { + return fetch(`https://api.github.com/users/${username}`).then(response => response.json()); +} + +getGithubUser('mbeaudru') + .then(user => console.log(user)) + .catch(err => console.log(err)); +``` + +و این، معادلش با استفاده از *async / await* است: + +```js +async function getGithubUser(username) { // promise + await keyword usage allowed + const response = await fetch(`https://api.github.com/users/${username}`); // اجرا در اینجا متوقف می‌شود تا وعده‫ fetch محقق شود + Execution stops here until fetch promise is fulfilled + return response.json(); +} + +getGithubUser('mbeaudru') + .then(user => console.log(user)) + .catch(err => console.log(err)); +``` + +نحو *async / await* به خصوص زمانی که می‌خواهید وعده‌های مستقل از هم را به هم زنجیر کنید بسیار راه‌دست است. + +به عنوان مثلا اگر نیاز داشته باشید توکنی را بگیرد که با آن بتوانید یک مطلب وبلاگ را واکشی کنید از پایگاه داده و سپس اطلاعات نویسنده را به دست آورید: + +> **توجه:** عبارت‌های *await* نیازمند آن است که داخل پرانتز قرار بگیرد تا بتوان متدها و خاصیت‌های مقدار حاصل شده‌اش در همان خط دست یافت. + +```js +async function fetchPostById(postId) { + const token = (await fetch('http://github.com/token_url')).json().token; + const post = (await fetch(`/posts/${postId}?token=${token}`)).json(); + const author = (await fetch(`/users/${post.authorId}`)).json(); + + post.author = author; + return post; +} + +fetchPostById('gzIrzeo64') + .then(post => console.log(post)) + .catch(err => console.log(err)); +``` + +##### مدیریت خطا + +جز در حالتی که عبارت‌های *await* را داخل بلوک‌های *try / catch* قرار داده باشیم، خطاهایی که بروز می‌کنند (فارغ از این که در بدنه تابع *async* حادث شده باشد یا زمانی که حین *await* معلق شده باشد) وعده‌ای که توسط تابع async بازگردانده می‌شود رد خواهد شد. استفاده از دستور `throw` در تابع async درست همانند برگرداندن یک وعده رده‌شده است ([(مرجع: PonyFoo)](https://ponyfoo.com/articles/understanding-javascript-async-await#error-handling)). + +> **توجه:** وعده‌ها مثل هم رفتار می‌کنند. + +این جا روشی است که به کمکش می‌توانید خطاهای مرتبط با وعده‌ها را مدیریت کنید: + +```js +function getUser() { // ‫این وعده رد خواهد شد! + return new Promise((res, rej) => rej("User not found !")); +} + +function getAvatarByUsername(userId) { + return getUser(userId).then(user => user.avatar); +} + +function getUserAvatar(username) { + return getAvatarByUsername(username).then(avatar => ({ username, avatar })); +} + +getUserAvatar('mbeaudru') + .then(res => console.log(res)) + .catch(err => console.log(err)); // "User not found !" +``` + +معادل آن با *async / await*: + +```js +async function getUser() { // وعده بازگردانده‌شده رد خواهد شد + throw "User not found !"; +} + +async function getAvatarByUsername(userId) => { + const user = await getUser(userId); + return user.avatar; +} + +async function getUserAvatar(username) { + var avatar = await getAvatarByUsername(username); + return { username, avatar }; +} + +getUserAvatar('mbeaudru') + .then(res => console.log(res)) + .catch(err => console.log(err)); // "User not found !" +``` + +#### منابع خارجی + +- [Async/Await - JavaScript.Info](https://javascript.info/async-await) +- [ES7 Async/Await](http://rossboucher.com/await/#/) +- [6 Reasons Why JavaScript’s Async/Await Blows Promises Away](https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9) +- [JavaScript awaits](https://dev.to/kayis/javascript-awaits) +- [Using Async Await in Express with Node 8](https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016) +- [Async Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) +- [Await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) +- [Using async / await in express with node 8](https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016) + +### درستی / غلطی + +در جاوااسکریپت، مقدار صحیح و غلط (true و false) مقداری است که در زمان ارزیابی در زمینه بولی، به مقدار بولی تبدیل می‌شود. به عنوان مثالی از زمینه بولی می‌توان به ارزیابی شرط `if` اشاره کرد. + +هر مقداری به مقدار صحیح یا ‍`true` تبدیل می‌شود مگر آن که برابر باشد با یکی از موارد زیر: + +- ```false``` +- ```0``` +- ```""``` (empty string) +- ```null``` +- ```undefined``` +- ```NaN``` + +این‌ها مثال‌هایی هستند از *مزینه بولی*: + +- ارزیابی شرط `if` + +```js +if (myVar) {} +``` + +مقدار `myVar` می‌تواند هر یک از شهروندان درجه یک یا [first-class citizen](https://en.wikipedia.org/wiki/First-class_citizen) (متغیر، تابع، بولی) باشد اما به مقدار بولی تبدیل می‌شود زیرا در یک زمینه بولی درحال ارزیابی شدن است. + +- بعد از عملگر منطقی **NOT** یعنی `!` + +اگر گزاره‌ای که این عملگر روی اعمال می‌شود قابل تبدیل شدن به true باشد، این عملگر باعث می‌شود که مقدار false برگردانده شود در غیر این صورت مقدار بازگشتی، true خواهد بود. + +```js +!0 // true -- 0 is falsy so it returns true +!!0 // false -- 0 is falsy so !0 returns true so !(!0) returns false +!!"" // false -- empty string is falsy so NOT (NOT false) equals false +``` + +- با سازنده شیٔ بولی + +```js +new Boolean(0) // false +new Boolean(1) // true +``` + +- در یک ارزیابی سه بخشی + +```js +myVar ? "truthy" : "falsy" +``` + +اینجا myVar در حال ارزیابی شدن در یک زمینه بولی است. + +زمانی که در حال مقایسه دو مقدار هستید مراقب باشید. مقادیر شیء (که باید تبدیل به true شوند) به مقدار بولی تبدیل **نمی‌شود* بلکه قهرا به واسطه [ToPrimitives specification](http://javascript.info/object-toprimitive) به مقدار اولیهٔ یک تبدیل می‌شوند. در اصل زمانی که یک شیء با یک مقدار بولی مانند ‬‪`[] == true`‬ مقایسه می‌شود، این عملیات رح مي‌دهد: ‪`[].toString() == true` + +```js +let a = [] == true // a is false since [].toString() give "" back. +let b = [1] == true // b is true since [1].toString() give "1" back. +let c = [2] == true // c is false since [2].toString() give "2" back. +``` + +#### منابع خارجی + +- [Truthy (MDN)](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) +- [Falsy (MDN)](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) +- [Truthy and Falsy values in JS - Josh Clanton](http://adripofjavascript.com/blog/drips/truthy-and-falsy-values-in-javascript.html) + +### توابع Anamorphisms و Catamorphisms + +#### تابع Anamorphisms + +‫Anamorphisms توابع هستند که از برخشی اشیاء به ساختارهای پیچیده‌تری شامل نوع شیء نگاشت می‌شوند. فرایند گسترش یا *unfolding* یک ساختار ساده به یک ساختار پیچیده‌تر هستند. گسترش دادن یک عدد صحیح به فهرستی (list) از اعداد صحیح را در نظر بگیرید. عدد صحیح، شیء اولیه ماست و فهرست اعداد صحیح، آن ساختار پیچیده‌تر. + + +##### نمونه کد + +```js +function downToOne(n) { + const list = []; + + for (let i = n; i > 0; --i) { + list.push(i); + } + + return list; +} + +downToOne(5) + //=> [ 5, 4, 3, 2, 1 ] +``` + +#### تابع Catamorphisms + +‫Catamorphisms نقطه مقابل Anamorphisms است و یک شیء با ساختاری پیچیده‌تر را به ساختاری ساده‌تر می‌کاهند. مثای بعدی را در نظر بگیرید که در آن، تابع `product` فهرستی از اعداد صحیح را گرفته و تنها یک عدد صحیح باز می‌گرداند. + +##### نمونه کد + +```js +function product(list) { + let product = 1; + + for (const n of list) { + product = product * n; + } + + return product; +} + +product(downToOne(5)) // 120 +``` + +#### منابع خارجی + +* [Anamorphisms in JavaScript](http://raganwald.com/2016/11/30/anamorphisms-in-javascript.html) +* [Anamorphism](https://en.wikipedia.org/wiki/Anamorphism) +* [Catamorphism](https://en.wikipedia.org/wiki/Catamorphism) + +### تولیدکننده‌ها + +راه دیگر نوشتن تابع `downToOne` استفاده از تولیدکننده‌ها یا Generatorهاست. برای برپاسازی یک شیء `Generator` می‌بایست از اعلان ‪`function *`‬ استفاده کنیم. تولیدکننده‌ها توابعی هستند که می‌شود از آن‌ها خارج و سپس وارد زمینه‌شان (متغیر گره‌خورده به‌شان) شد که در طول ورودها حفظ می‌شود. + +به عنوان مثال می‌توانیم تابع `downToOne` را این گونه بازنویسی کنیم: + +```js +function * downToOne(n) { + for (let i = n; i > 0; --i) { + yield i; + } +} + +[...downToOne(5)] // [ 5, 4, 3, 2, 1 ] +``` + +تابع تولیدکننده، یک شیء شمارش‌گر از بازمی‌گرداند. وقتی تابع ‪`Next()`‬ شمارش‌گر صدا زده می‌شود، تا رسیدن به عبارت `yield` (که مقدار مورد نظر برای بازگردانده شدن از آن شمارش به خصوص را تعیین می‌کند) و یا با ‪`yield*`‬ که به تابع تولید کننده دیگری اشاره می‌کند اجرا می‌شود. وقتی که عبارت `return` در تولیدکننده صدا زده می‌شود، تولیدکننده برچسب انجام شده دریافت می‌کند و مقدار تولید شده در آن شمارش بازگردانده می‌شود. فراخوانی‌های بعدی ‪`next()`‬ منجر به بازگرداندن مقادیر جدیدی نمی‌شود. + +#### نمونه کد + +```js +// Yield Example +function * idMaker() { + var index = 0; + while (index < 2) { + yield index; + index = index + 1; + } +} + +var gen = idMaker(); + +gen.next().value; // 0 +gen.next().value; // 1 +gen.next().value; // undefined +``` + +عبارت `yield` یک تولیدکننده را قادر می‌سازد تا در خلال شمارش، تابع تولیدکننده دیگری را فراخوانی کند. + +```js +// Yield * Example +function * genB(i) { + yield i + 1; + yield i + 2; + yield i + 3; +} + +function * genA(i) { + yield i; + yield* genB(i); + yield i + 10; +} + +var gen = genA(10); + +gen.next().value; // 10 +gen.next().value; // 11 +gen.next().value; // 12 +gen.next().value; // 13 +gen.next().value; // 20 +``` + +```js +// Generator Return Example +function* yieldAndReturn() { + yield "Y"; + return "R"; + yield "unreachable"; +} + +var gen = yieldAndReturn() +gen.next(); // { value: "Y", done: false } +gen.next(); // { value: "R", done: true } +gen.next(); // { value: undefined, done: true } +``` + +#### منابع خارجی + +* [Mozilla MDN Web Docs, Iterators and Generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators#Generators) + +### متدهای ایستا + +#### توضیح کوتاه + +در کلاس‌ها، کلیدواژه `static` برای اعلان متدهای ایستا استفاده می‌شود. متدهای ایستا، توابعی در کلاس هستند که به شیء کلاس تعلق داشته و در دسترس هیچ یک از نمونه‌های آن کلاس نیستند. + +##### نمونه کد + +```js +class Repo { + static getName() { + return "Repo name is modern-js-cheatsheet" + } +} + +console.log(Repo.getName()) // Repo name is modern-js-cheatsheet + +// توجه کنید که ما نیازی به ایجاد نمونه‌ای از کلاس Repo نداریم. + +let r = new Repo(); +console.log(r.getName()) // Uncaught TypeError: r.getName is not a function +``` + +#### توضیح مبسوط + +متدهای ایستا می‌توانند توسط دیگر متدهای ایستا و با کمک کلیدواژه `this` فراخوانی شوند. این کار توسط سایر متدهای غیرایستا قابل انجام نیست. متدهای غیرایستا نمی‌توانند با کمک کلیدواژه `this` مستقیما به متدهای ایستا دسترسی پیدا کنند. + +##### فراخوانی متد ایستا توسط متد ایستای دیگر + +به منظور فراخوانی یک متد ایستا دیگر توسط یک متد ایستای دیگر کافی است از کلیدواژه `this` به این شکل استفاده کنیم: + +```js +class Repo { + static getName() { + return "Repo name is modern-js-cheatsheet" + } + + static modifyName() { + return this.getName() + '-added-this' + } +} + +console.log(Repo.modifyName()) // Repo name is modern-js-cheatsheet-added-this +``` + +##### فراخوانی متدهای ایستا توسط متدهای غیرایستا + +این کار به دو روش قابل انجام است: + +1. ###### استفاده از نام کلاس + +برای دسترسی به متد ایستا از داخل یک متد غیر ایستا، از نام کلاس استفاده کرده و آن متد را مانند یک خاصیت مثل `ClassName.StaticMethodName` فرامی‌خوانیم: + +```js +class Repo { + static getName() { + return "Repo name is modern-js-cheatsheet" + } + + useName() { + return Repo.getName() + ' and it contains some really important stuff' + } +} + +// برای استفاده از متدهای غیرایستا لازم است نمونه‌ای از کلاس ساخته شود +let r = new Repo() +console.log(r.useName()) // Repo name is modern-js-cheatsheet and it contains some really important stuff +``` + +2. ###### استفاده از سازنده + +متدهای ایستا می‌توانند به عنوان خاصیت در شیء سازنده (constructor) صدا زده شوند. + +```js +class Repo { + static getName() { + return "Repo name is modern-js-cheatsheet" + } + + useName() { + // متد ایستا را به عنوان یک خاصیت سازنده فرا می‌خوانیم + return this.constructor.getName() + ' and it contains some really important stuff' +} + } + +// برای استفاده از متدهای غیرایستا لازم است نمونه‌ای از کلاس ساخته شود +let r = new Repo() +console.log(r.useName()) // Repo name is modern-js-cheatsheet and it contains some really important stuff +``` + +#### منابع خارجی +- [static keyword- MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static) +- [Static Methods- Javascript.info](https://javascript.info/class#static-methods) +- [Static Members in ES6- OdeToCode](http://odetocode.com/blogs/scott/archive/2015/02/02/static-members-in-es6.aspx) + +## واژه‌نامه + +### قلمرو + +زمینه‌ای که در ان مقادیر و عبارات «نمایان» هستند یا می‌توانند مورد ارجاع داده شوند. اگر یک متغیر یا عبارت دیگر در «قلمروی جاری» نیست برای استفاده قابل دسترس نخواهد بود. + +منبع: [MDN](https://developer.mozilla.org/en-US/docs/Glossary/Scope) + +### تغییر متغیر + +یک متغیر را تغییریافته می‌خوانیم اگر مقدار اولیه آن در ادامه کار تغییر کند. + +```js +var myArray = []; +myArray.push("firstEl") // آرایه ما تغییر یافته است +``` + +یک متغیر یا غیرقابل تغییر یا *immutable* می‌نامیم اگر نتوان آن را تغییر داد. + +[مقاله تغییرپذیری در MDN](https://developer.mozilla.org/en-US/docs/Glossary/Mutable) را برای جزئیات بیشتر ببینید. From 19899e6fd2889057c3fa263688f814cbd5edd86f Mon Sep 17 00:00:00 2001 From: Kasper Andreassen <79222410+kasperadk@users.noreply.github.com> Date: Mon, 25 Apr 2022 10:47:55 +0200 Subject: [PATCH 12/15] Fixed broken link (#132) Updated link to Array Methods in TOC --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 601630e..d5164c7 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ When you struggle to understand a notion, I suggest you look for answers on the + [Destructuring objects and arrays](#destructuring-objects-and-arrays) - [Explanation with sample code](#explanation-with-sample-code) - [Useful resources](#useful-resources-1) - + [Array methods - map / filter / reduce](#array-methods---map--filter--reduce) + + [Array methods - map / filter / reduce](#array-methods---map--filter--reduce--find) - [Sample code](#sample-code-2) - [Explanation](#explanation) * [Array.prototype.map()](#arrayprototypemap) From 6b63e2da09dd568456b4fa48993ea14a159f7db5 Mon Sep 17 00:00:00 2001 From: Muhammad Sahim Bhaur <64275858+MuhammadSahimBhaur@users.noreply.github.com> Date: Wed, 24 May 2023 13:32:41 +0500 Subject: [PATCH 13/15] Fixed wes blog 404 link (#137) * Fixed Wes Bos Blog Link * Fixed wes blog link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5164c7..c8da33d 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ When you struggle to understand a notion, I suggest you look for answers on the - [Eloquent JavaScript (book)](https://eloquentjavascript.net) - [Douglas Crockford's blog](https://www.crockford.com/javascript/) - [ES6 Features with examples](http://es6-features.org) -- [Wes Bos blog (ES6)](http://wesbos.com/category/es6/) +- [Wes Bos blog (ES6)](https://wesbos.com/javascript) - [Javascript Basics for Beginners](https://www.udacity.com/course/javascript-basics--ud804) - a free Udacity course - [Reddit (JavaScript)](https://www.reddit.com/r/javascript/) - [Google](https://www.google.com/) to find specific blog and resources From 19bc4c7e1a4ab058d06d6a01fa94078ab82bf155 Mon Sep 17 00:00:00 2001 From: Erik Behrends Date: Sat, 4 May 2024 09:36:08 +0200 Subject: [PATCH 14/15] German translation (#143) --- translations/de-DE.md | 1846 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1846 insertions(+) create mode 100644 translations/de-DE.md diff --git a/translations/de-DE.md b/translations/de-DE.md new file mode 100644 index 0000000..49dd1a5 --- /dev/null +++ b/translations/de-DE.md @@ -0,0 +1,1846 @@ +# Modern JavaScript Cheatsheet + +![Modern JavaScript cheatsheet](https://i.imgur.com/aexPxMb.png) +Bildnachweis: [Ahmad Awais ⚡️](https://github.com/ahmadawais) + +> Wenn dir dieser Inhalt gefällt, kannst du mich anpingen oder mir auf Twitter folgen :+1: + +[![Tweet for help](https://img.shields.io/twitter/follow/mbeaudru?label=Tweet%20%40mbeaudru&style=social)](https://twitter.com/mbeaudru/) + +## Einführung + +### Motivation + +Dieses Dokument ist ein Spickzettel für JavaScript, das du häufig in modernen Projekten und den meisten aktuellen Beispielcodes antreffen wirst. + +Dieser Leitfaden ist nicht dazu gedacht, dir JavaScript von Grund auf beizubringen, sondern ist eine Hilfe für Entwickler mit grundlegendem Wissen, die Schwierigkeiten haben, sich mit modernen Codebasen vertraut zu machen (oder sagen wir, React zu lernen), aufgrund der in JavaScript verwendeten Konzepte. + +Außerdem werde ich manchmal persönliche Tipps geben, die diskutierbar sein könnten, aber ich werde darauf hinweisen, dass es eine persönliche Empfehlung ist, wenn ich das tue. + +> **Hinweis:** Die meisten der hier eingeführten Konzepte stammen aus einem JavaScript Sprachupdate (ES2015, oft als ES6 bezeichnet). Neue Funktionen, die durch dieses Update hinzugefügt wurden, findest du [hier](http://es6-features.org); sehr gut gemacht. + +### Ergänzende Ressourcen + +Wenn du Schwierigkeiten hast, eine Vorstellung zu verstehen, schlage ich vor, dass du nach Antworten in den folgenden Ressourcen suchst: + +- [MDN (Mozilla Developer Network)](https://developer.mozilla.org/de/search?q=) +- [You don't know JS (Buch)](https://github.com/getify/You-Dont-Know-JS) +- [Eloquent JavaScript (Buch)](https://eloquentjavascript.net) +- [Douglas Crockford's Blog](https://www.crockford.com/javascript/) +- [ES6 Features mit Beispielen](http://es6-features.org) +- [Wes Bos Blog (ES6)](https://wesbos.com/javascript) +- [Javascript Grundlagen für Anfänger](https://www.udacity.com/course/javascript-basics--ud804) - ein kostenloser Udacity-Kurs +- [Reddit (JavaScript)](https://www.reddit.com/r/javascript/) +- [Google](https://www.google.com/) um spezifische Blogs und Ressourcen zu finden +- [StackOverflow](https://stackoverflow.com/questions/tagged/javascript) + +## Inhaltsverzeichnis + +- [Modern JavaScript Cheatsheet](#modern-javascript-cheatsheet) + * [Einführung](#einführung) + + [Motivation](#motivation) + + [Ergänzende Ressourcen](#ergänzende-ressourcen) + * [Inhaltsverzeichnis](#inhaltsverzeichnis) + * [Begriffe](#begriffe) + + [Variablendeklaration: var, const, let](#variablendeklaration-var-const-let) + - [Kurze Erklärung](#kurze-erklärung) + - [Beispielcode](#beispielcode) + - [Ausführliche Erklärung](#ausführliche-erklärung) + - [Externe Ressource](#externe-ressource) + + [Pfeilfunktion](#-pfeilfunktion) + - [Beispielcode](#beispielcode-1) + - [Ausführliche Erklärung](#ausführliche-erklärung-1) + * [Prägnanz](#prägnanz) + * [*this* Referenz](#this-referenz) + - [Nützliche Ressourcen](#nützliche-ressourcen) + + [Standardparameterwert für Funktionen](#standardparameterwert-für-funktionen) + - [Externe Ressource](#externe-ressource-1) + + [Destrukturierung von Objekten und Arrays](#destrukturierung-von-objekten-und-arrays) + - [Erklärung mit Beispielcode](#erklärung-mit-beispielcode) + - [Nützliche Ressourcen](#nützliche-ressourcen-1) + + [Array-Methoden - map / filter / reduce](#array-methoden---map--filter--reduce--find) + - [Beispielcode](#beispielcode-2) + - [Erklärung](#erklärung) + * [Array.prototype.map()](#arrayprototypemap) + * [Array.prototype.filter()](#arrayprototypefilter) + * [Array.prototype.reduce()](#arrayprototypereduce) + * [Array.prototype.find()](#arrayprototypefind) + - [Externe Ressource](#externe-ressource-2) + + [Spread-Operator "..."](#spread-operator-) + - [Beispielcode](#beispielcode-3) + - [Erklärung](#erklärung-1) + * [In Iterables (wie Arrays)](#in-iterables-wie-arrays) + * [Funktionsrestparameter](#funktionsrestparameter) + * [Objekteigenschaften ausbreiten](#objekteigenschaften-ausbreiten) + - [Externe Ressourcen](#externe-ressourcen) + + [Objekteigenschaften Kurzschreibweise](#objekteigenschaften-kurzschreibweise) + - [Erklärung](#erklärung-2) + - [Externe Ressourcen](#externe-ressourcen-1) + + [Promises](#promises) + - [Beispielcode](#beispielcode-4) + - [Erklärung](#erklärung-3) + * [Das Promise erstellen](#das-promise-erstellen) + * [Verwendung von Promise-Handlern](#verwendung-von-promise-handlern) + - [Externe Ressourcen](#externe-ressourcen-2) + + [Template Literale](#template-literale) + - [Beispielcode](#beispielcode-5) + - [Externe Ressourcen](#externe-ressourcen-3) + + [Tagged Template Literale](#tagged-template-literale) + - [Externe Ressourcen](#externe-ressourcen-4) + + [Imports / Exports](#imports--exports) + - [Erklärung mit Beispielcode](#erklärung-mit-beispielcode-1) + * [Benannte Exports](#benannte-exports) + * [Standardimport / export](#standardimport--export) + - [Externe Ressourcen](#externe-ressourcen-5) + + [JavaScript *this*](#-javascript-this) + - [Externe Ressourcen](#externe-ressourcen-6) + + [Klasse](#klasse) + - [Beispiele](#beispiele) + - [Externe Ressourcen](#externe-ressourcen-7) + + [Extends und super](#extends-und-super) + - [Beispielcode](#beispielcode-6) + - [Externe Ressourcen](#externe-ressourcen-8) + + [Async Await](#async-await) + - [Beispielcode](#beispielcode-7) + - [Erklärung mit Beispielcode](#erklärung-mit-beispielcode-2) + - [Fehlerbehandlung](#fehlerbehandlung) + - [Externe Ressourcen](#externe-ressourcen-9) + + [Truthy / Falsy](#truthy--falsy) + - [Externe Ressourcen](#externe-ressourcen-10) + + [Anamorphismen / Katamorphismen](#anamorphismen-und-katamorphismen) + - [Anamorphismen](#anamorphismen) + - [Katamorphismen](#katamorphismen) + - [Externe Ressourcen](#externe-ressourcen-11) + + [Generatoren](#generatoren) + - [Externe Ressourcen](#externe-ressourcen-12) + + [Statische Methoden](#statische-methoden) + - [Kurze Erklärung](#kurze-erklärung-1) + - [Beispielcode](#beispielcode-8) + - [Ausführliche Erklärung](#ausführliche-erklärung-2) + * [Andere statische Methoden aus einer statischen Methode aufrufen](#andere-statische-methoden-aus-einer-statischen-methode-aufrufen) + * [Statische Methoden aus nicht-statischen Methoden aufrufen](#statische-methoden-aus-nicht-statischen-methoden-aufrufen) + - [Externe Ressourcen](#externe-ressourcen-13) + * [Glossar](#glossar) + + [Scope](#-scope) + + [Variablenmutation](#-variablenmutation) + +## Begriffe + +### Variablendeklaration: var, const, let + +In JavaScript gibt es drei Schlüsselwörter, um eine Variable zu deklarieren, und jedes hat seine Unterschiede. Diese Schlüsselwörter sind ```var```, ```let``` und ```const```. + +#### Kurze Erklärung + +Variablen, die mit dem Schlüsselwort ```const``` deklariert sind, können nicht neu zugewiesen werden, während ```let``` und ```var``` neu zugewiesen werden können. + +Ich empfehle, deine Variablen standardmäßig mit ```const``` zu deklarieren, aber mit ```let```, wenn es eine Variable ist, die du später *mutieren* oder neu zuweisen musst. + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ScopeNeu zuzuweisendVeränderbarTemporal Dead Zone
constBlockNeinJaJa
letBlockJaJaJa
varFunktionJaJaNein
+ +#### Beispielcode + +```javascript +const person = "Nick"; +person = "John" // Wird einen Fehler werfen, person kann nicht neu zugewiesen werden +``` + +```javascript +let person = "Nick"; +person = "John"; +console.log(person) // "John", Neu-Zuweisung ist erlaubt mit let +``` + +#### Ausführliche Erklärung + +Der [*Scope*](#scope_def) einer Variable bedeutet grob "wo ist diese Variable im Code verfügbar". + +##### var + +```var``` deklarierte Variablen sind *funktionsumfänglich*, was bedeutet, dass, wenn eine Variable in einer Funktion erstellt wird, alles in dieser Funktion Zugriff auf diese Variable hat. Außerdem kann eine *funktionsumfängliche* Variable, die in einer Funktion erstellt wurde, nicht außerhalb dieser Funktion zugegriffen werden. + +Ich empfehle dir, es dir so vorzustellen, als ob eine *X-umfängliche* Variable bedeutet, dass diese Variable eine Eigenschaft von X ist. + +```javascript +function myFunction() { + var myVar = "Nick"; + console.log(myVar); // "Nick" - myVar ist innerhalb der Funktion zugänglich +} +console.log(myVar); // Wirft einen ReferenceError, myVar ist außerhalb der Funktion nicht zugänglich. +``` + +Fokussieren wir uns immer noch auf den Variablenscope, hier ist ein subtileres Beispiel: + +```javascript +function myFunction() { + var myVar = "Nick"; + if (true) { + var myVar = "John"; + console.log(myVar); // "John" + // eigentlich, da myVar funktionsumfänglich ist, haben wir einfach den vorherigen myVar-Wert "Nick" durch "John" ersetzt + } + console.log(myVar); // "John" - sieh, wie die Anweisungen im if-Block diesen Wert beeinflusst haben +} +console.log(myVar); // Wirft einen ReferenceError, myVar ist außerhalb der Funktion nicht zugänglich. +``` + +Außerdem werden *var* deklarierte Variablen zum Anfang des Scopes bei der Ausführung verschoben. Dies nennt man [var hoisting](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Statements/var#var_hoisting). + +Dieser Codeabschnitt: + +```js +console.log(myVar) // undefined -- kein Fehler geworfen +var myVar = 2; +``` + +wird bei der Ausführung so verstanden: + +```js +var myVar; +console.log(myVar) // undefined -- kein Fehler geworfen +myVar = 2; +``` + +##### let + +```var``` und ```let ``` sind ungefähr gleich, aber bei ```let``` deklarierte Variablen + +- haben einen *Block-Scope* +- sind **nicht** zugänglich, bevor sie zugewiesen werden +- können nicht im gleichen Scope neu deklariert werden + +Lasst uns die Auswirkung der Block-Scoping anhand unseres vorherigen Beispiels sehen: + +```javascript +function myFunction() { + let myVar = "Nick"; + if (true) { + let myVar = "John"; + console.log(myVar); // "John" + // eigentlich, da myVar einen Block-Scope hat, haben wir gerade eine neue Variable myVar erstellt. + // Diese Variable ist außerhalb dieses Blocks nicht zugänglich und völlig unabhängig + // von der ersten erstellten myVar! + } + console.log(myVar); // "Nick", sieh, wie die Anweisungen im if-Block diesen Wert NICHT beeinflusst haben +} +console.log(myVar); // Wirft einen ReferenceError, myVar ist außerhalb der Funktion nicht zugänglich. +``` + + Nun, was es für *let* (und *const*) Variablen bedeutet, nicht zugänglich zu sein, bevor sie zugewiesen werden: + +```js +console.log(myVar) // wirft einen ReferenceError ! +let myVar = 2; +``` + +Im Gegensatz zu *var* Variablen, wenn du versuchst, eine *let* oder *const* Variable zu lesen oder zu schreiben, bevor sie zugewiesen sind, wird ein Fehler ausgelöst. Dieses Phänomen wird oft [*Temporal Dead Zone*](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Statements/let#Temporal_Dead_Zone_and_errors_with_let) oder *TDZ* genannt. + +> **Hinweis:** Technisch gesehen werden *let* und *const* Variablendeklarationen auch gehoistet, aber nicht ihre Zuweisung. Da sie so gemacht sind, dass sie nicht vor der Zuweisung verwendet werden können, fühlt es sich intuitiv an, als ob es kein Hoisting gäbe, aber es gibt es. Finde mehr in [dieser sehr detaillierten Erklärung](http://jsrocks.org/2015/01/temporal-dead-zone-tdz-demystified) heraus, wenn du mehr wissen möchtest. + +Außerdem kannst du eine *let* Variable nicht neu deklarieren: + +```js +let myVar = 2; +let myVar = 3; // Wirft einen SyntaxError +``` + +##### const + +Mit *const* deklarierte Variablen verhalten sich wie *let*-Variablen, können aber auch nicht neu zugewiesen werden. + +Zusammenfassend können *const*-Variablen: + +- haben einen *Block-Scope* +- sind nicht zugänglich bevor sie zugewiesen werden +- können nicht im gleichen Scope neu deklariert werden +- können nicht neu zugewiesen werden + +```js +const myVar = "Nick"; +myVar = "John" // Wirft einen Fehler, Neuzuweisung ist nicht erlaubt +``` + +```js +const myVar = "Nick"; +const myVar = "John" // Wirft einen Fehler, Neudeklarierung ist nicht erlaubt +``` + + Aber es gibt eine Feinheit: ```const``` Variablen sind nicht [**unveränderbar**](#mutation_def)! Konkret bedeutet das, dass *Objekt*- und *Array*-```const``` deklarierte Variablen **verändert** werden können. + +Für Objekte: +```js +const person = { + name: 'Nick' +}; +person.name = 'John' // das funktioniert! Die Variable person wird nicht komplett neu zugewiesen, sondern verändert +console.log(person.name) // "John" +person = "Sandra" // wirft einen Fehler, da Neu-Zuweisung nicht erlaubt ist für const deklarierte Variablen +``` + +Für Arrays: +```js +const person = []; +person.push('John'); // das funktioniert! Die Variable person wird nicht komplett neu zugewiesen, sondern verändert +console.log(person[0]) // "John" +person = ["Nick"] // wirft einen Fehler, da Neu-Zuweisung nicht erlaubt ist für const deklarierte Variablen +``` + +#### Externe Ressource + +- [Wie let und const in JavaScript skopiert werden - WesBos](http://wesbos.com/javascript-scoping/) +- [Temporal Dead Zone (TDZ) Demystifiziert](http://jsrocks.org/2015/01/temporal-dead-zone-tdz-demystified) + +### Pfeilfunktion + +Das ES6 JavaScript Update hat *Pfeilfunktionen* eingeführt, die eine weitere Methode darstellen, Funktionen zu deklarieren und zu verwenden. Hier sind die Vorteile, die sie mitbringen: + +- Kürzerer Code +- *this* wird aus der Umgebung übernommen +- implizite Rückgabe + +#### Beispielcode + +- Kürze und implizite Rückgabe + +```js +function double(x) { return x * 2; } // Traditioneller Weg +console.log(double(2)) // 4 +``` + +```js +const double = x => x * 2; // Gleiche Funktion als Pfeilfunktion mit impliziter Rückgabe geschrieben +console.log(double(2)) // 4 +``` + +- *this*-Referenz + +In einer Pfeilfunktion ist *this* gleich dem *this*-Wert des umschließenden Ausführungskontextes. Im Grunde musst du mit Pfeilfunktionen nicht mehr den Trick "that = this" anwenden, bevor du eine Funktion in einer Funktion aufrufst. + +```js +function myFunc() { + this.myVar = 0; + setTimeout(() => { + this.myVar++; + console.log(this.myVar) // 1 + }, 0); +} +``` + +#### Ausführliche Erklärung + +##### Prägnanz + +Pfeilfunktionen sind in vielerlei Hinsicht prägnanter als traditionelle Funktionen. Lass uns alle möglichen Fälle überprüfen: + +- Implizite vs. explizite Rückgabe + +Eine **explizite Rückgabe** ist eine Funktion, in deren Körper das Schlüsselwort *return* verwendet wird. + +```js + function double(x) { + return x * 2; // diese Funktion gibt explizit x * 2 zurück, das Schlüsselwort *return* wird verwendet + } +``` + +Auf traditionelle Weise geschriebene Funktionen hatten immer eine explizite Rückgabe. Aber mit Pfeilfunktionen kannst du eine *implizite Rückgabe* durchführen, was bedeutet, dass du das Schlüsselwort *return* nicht verwenden musst, um einen Wert zurückzugeben. + +```js + const double = (x) => { + return x * 2; // Hier explizite Rückgabe + } +``` + +Da diese Funktion nur etwas zurückgibt (keine Anweisungen vor dem Schlüsselwort *return*), können wir eine implizite Rückgabe durchführen. + +```js + const double = (x) => x * 2; // Korrekt, gibt x*2 zurück +``` + +Um dies zu tun, müssen wir nur die **Klammern** und das Schlüsselwort **return** entfernen. Daher wird sie als *implizite* Rückgabe bezeichnet, das Schlüsselwort *return* ist nicht vorhanden, aber diese Funktion wird tatsächlich ```x * 2``` zurückgeben. + +> **Hinweis:** Wenn deine Funktion keinen Wert zurückgibt (mit *Nebenwirkungen*), führt sie weder eine explizite noch eine implizite Rückgabe durch. + +Außerdem, wenn du ein *Objekt* implizit zurückgeben möchtest, **musst du es in Klammern setzen**, da es sonst zu Konflikten mit den Blockklammern kommen würde: + +```js +const getPerson = () => ({ name: "Nick", age: 24 }) +console.log(getPerson()) // { name: "Nick", age: 24 } -- Objekt wird implizit durch Pfeilfunktion zurückgegeben +``` + +- Nur ein Argument + +Wenn deine Funktion nur einen Parameter hat, kannst du die Klammern um ihn herum weglassen. Wenn wir den oben genannten *double*-Code wieder aufgreifen: + +```js + const double = (x) => x * 2; // diese Pfeilfunktion nimmt nur einen Parameter entgegen +``` + +Klammern um den Parameter können vermieden werden: + +```js + const double = x => x * 2; // diese Pfeilfunktion nimmt nur einen Parameter entgegen +``` + +- Keine Argumente + +Wenn einer Pfeilfunktion kein Argument übergeben wird, musst du Klammern angeben, sonst ist die Syntax nicht gültig. + +```js + () => { // Klammern werden angegeben, alles ist in Ordnung + const x = 2; + return x; + } +``` + +```js + => { // Keine Klammern, das wird nicht funktionieren! + const x = 2; + return x; + } +``` + +##### *this*-Referenz + +Um diese Nuance, die mit Pfeilfunktionen eingeführt wurde, zu verstehen, muss du wissen, wie [this](#this_def) sich in JavaScript verhält. + +In einer Pfeilfunktion ist *this* gleich dem *this*-Wert des umschließenden Ausführungskontextes. Das bedeutet, dass eine Pfeilfunktion kein neues *this* erstellt, sondern es stattdessen aus seiner Umgebung übernimmt. + +Ohne Pfeilfunktion, wenn du in einer Funktion innerhalb einer Funktion auf eine Variable von *this* zugreifen wolltest, musstest du den Trick *that = this* oder *self = this* anwenden. + +Zum Beispiel beim Verwenden der setTimeout-Funktion innerhalb von myFunc: + +```js +function myFunc() { + this.myVar = 0; + var that = this; // that = this Trick + setTimeout( + function() { // Ein neues *this* wird in diesem Funktionsbereich erstellt + that.myVar++; + console.log(that.myVar) // 1 + + console.log(this.myVar) // undefiniert -- siehe Funktionsdeklaration oben + }, + 0 + ); +} +``` + +Aber mit Pfeilfunktion wird *this* aus seiner Umgebung übernommen: + +```js +function myFunc() { + this.myVar = 0; + setTimeout( + () => { // this wird aus der Umgebung übernommen, was hier myFunc bedeutet + this.myVar++; + console.log(this.myVar) // 1 + }, + 0 + ); +} +``` + +#### Nützliche Ressourcen + +- [Einführung in Pfeilfunktionen - WesBos](http://wesbos.com/arrow-functions/) +- [JavaScript Pfeilfunktion - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) +- [Pfeilfunktion und lexikalisches *this*](https://hackernoon.com/javascript-es6-arrow-functions-and-lexical-this-f2a3e2a5e8c4) + +### Standardparameterwert für Funktionen + +Ab dem ES2015 JavaScript Update kannst du deinen Funktionsparametern Standardwerte zuweisen, indem du folgende Syntax verwendest: + +```js +function myFunc(x = 10) { + return x; +} +console.log(myFunc()) // 10 -- kein Wert wird übergeben, daher wird dem x in myFunc der Standardwert 10 zugewiesen +console.log(myFunc(5)) // 5 -- ein Wert wird übergeben, daher ist x gleich 5 in myFunc + +console.log(myFunc(undefined)) // 10 -- der Wert undefined wird übergeben, daher wird der Standardwert dem x zugewiesen +console.log(myFunc(null)) // null -- ein Wert (null) wird übergeben, siehe unten für weitere Details +``` + +Der Standardparameter wird in zwei und nur zwei Situationen angewendet: + +- Kein Parameter übergeben +- *undefined* Parameter übergeben + +Anders ausgedrückt, wenn du *null* übergibst, wird der Standardparameter **nicht angewendet**. + +> **Hinweis:** Die Zuweisung eines Standardwerts kann auch mit destrukturierten Parametern verwendet werden (siehe nächste Notion, um ein Beispiel zu sehen) + +#### Externe Ressource + +- [Standardparameterwert - ES6 Features](http://es6-features.org/#DefaultParameterValues) +- [Standardparameter - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters) + +### Destrukturierung von Objekten und Arrays + +*Destrukturierung* ist eine praktische Methode, neue Variablen zu erstellen, indem einige Werte aus in Objekten oder Arrays gespeicherten Daten extrahiert werden. + +Um nur einige Anwendungsfälle zu nennen, kann die *Destrukturierung* verwendet werden, um Funktionsparameter oder *this.props* in React-Projekten zu destrukturieren. + +#### Erklärung mit Beispielcode + +- Objekt + +Betrachten wir das folgende Objekt für alle Beispiele: + +```js +const person = { + firstName: "Nick", + lastName: "Anderson", + age: 35, + sex: "M" +} +``` + +Ohne Destrukturierung + +```js +const first = person.firstName; +const age = person.age; +const city = person.city || "Paris"; +``` + +Mit Destrukturierung, alles in einer Zeile: + +```js +const { firstName: first, age, city = "Paris" } = person; // Das war's ! + +console.log(age) // 35 -- Eine neue Variable age wird erstellt und ist gleich person.age +console.log(first) // "Nick" -- Eine neue Variable first wird erstellt und ist gleich person.firstName +console.log(firstName) // ReferenceError -- person.firstName existiert, ABER die neu erstellte Variable ist als first benannt +console.log(city) // "Paris" -- Eine neue Variable city wird erstellt und da person.city undefiniert ist, ist city gleich dem bereitgestellten Standardwert "Paris". +``` + +**Hinweis:** In ```const { age } = person;``` werden die Klammern nach dem *const*-Schlüsselwort nicht verwendet, um ein Objekt oder einen Block zu deklarieren, sondern es ist die *Destrukturierungssyntax*. + +- Funktionsparameter + +*Destrukturierung* wird häufig verwendet, um Objektparameter in Funktionen zu destrukturieren. + +Ohne Destrukturierung + +```js +function joinFirstLastName(person) { + const firstName = person.firstName; + const lastName = person.lastName; + return firstName + '-' + lastName; +} + +joinFirstLastName(person); // "Nick-Anderson" +``` + +Bei der Destrukturierung des Objektparameters *person* erhalten wir eine präzisere Funktion: + +```js +function joinFirstLastName({ firstName, lastName }) { // wir erstellen Variablen firstName und lastName durch Destrukturierung des person-Parameters + return firstName + '-' + lastName; +} + +joinFirstLastName(person); // "Nick-Anderson" +``` + +Destrukturierung ist noch angenehmer mit [Pfeilfunktionen](#arrow_func_concept) zu verwenden: + +```js +const joinFirstLastName = ({ firstName, lastName }) => firstName + '-' + lastName; + +joinFirstLastName(person); // "Nick-Anderson" +``` + +- Array + +Betrachten wir das folgende Array: + +```js +const myArray = ["a", "b", "c"]; +``` + +Ohne Destrukturierung + +```js +const x = myArray[0]; +const y = myArray[1]; +``` + +Mit Destrukturierung + +```js +const [x, y] = myArray; // Das war's ! + +console.log(x) // "a" +console.log(y) // "b" +``` + +#### Nützliche Ressourcen + +- [ES6 Features - Destrukturierende Zuweisung](http://es6-features.org/#ArrayMatching) +- [Objektdestrukturierung - WesBos](http://wesbos.com/destructuring-objects/) +- [ExploringJS - Destrukturierung](http://exploringjs.com/es6/ch_destructuring.html) + +### Array-Methoden - map / filter / reduce / find + +*Map*, *filter*, *reduce* und *find* sind Array-Methoden, die aus einem Programmierparadigma namens [*funktionale Programmierung*](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0) stammen. + +Zusammengefasst: + +- **Array.prototype.map()** nimmt ein Array, führt eine Operation auf seinen Elementen durch und gibt ein Array mit den transformierten Elementen zurück. +- **Array.prototype.filter()** nimmt ein Array, entscheidet elementweise, ob es dieses behalten will oder nicht, und gibt ein Array nur mit den behaltenen Elementen zurück. +- **Array.prototype.reduce()** nimmt ein Array und aggregiert die Elemente zu einem einzelnen Wert (der zurückgegeben wird). +- **Array.prototype.find()** nimmt ein Array und gibt das erste Element zurück, das die bereitgestellte Bedingung erfüllt. + +Ich empfehle, sie so oft wie möglich zu verwenden, indem du den Prinzipien der funktionalen Programmierung folgst, da sie zusammensetzbar, prägnant und elegant sind. + +Mit diesen vier Methoden kannst du in den meisten Situationen die Verwendung von *for* und *forEach* Schleifen vermeiden. Wenn du versucht bist, eine *for*-Schleife zu machen, versuche es mit *map*, *filter*, *reduce* und *find* zusammengesetzt. Es könnte anfangs schwierig sein, da es dich zwingt, eine neue Denkweise zu erlernen, aber sobald du es beherrschst, wird alles einfacher. + +#### Beispielcode + +```js +const numbers = [0, 1, 2, 3, 4, 5, 6]; +const doubledNumbers = numbers.map(n => n * 2); // [0, 2, 4, 6, 8, 10, 12] +const evenNumbers = numbers.filter(n => n % 2 === 0); // [0, 2, 4, 6] +const sum = numbers.reduce((prev, next) => prev + next, 0); // 21 +const greaterThanFour = numbers.find((n) => n>4); // 5 +``` + +Berechne die Gesamtsumme der Noten für Schüler mit Noten 10 oder darüber, indem du map, filter und reduce zusammensetzt: + +```js +const students = [ + { name: "Nick", grade: 10 }, + { name: "John", grade: 15 }, + { name: "Julia", grade: 19 }, + { name: "Nathalie", grade: 9 }, +]; + +const aboveTenSum = students + .map(student => student.grade) // wir mappen das students Array auf ein Array ihrer Noten + .filter(grade => grade >= 10) // wir filtern das Noten-Array, um diejenigen 10 oder darüber zu behalten + .reduce((prev, next) => prev + next, 0); // wir summieren alle Noten 10 oder darüber nacheinander + +console.log(aboveTenSum) // 44 -- 10 (Nick) + 15 (John) + 19 (Julia), Nathalie unter 10 wird ignoriert +``` + +#### Erklärung + +Betrachten wir das folgende Array von Zahlen für unsere Beispiele: + +```js +const numbers = [0, 1, 2, 3, 4, 5, 6]; +``` + +##### Array.prototype.map() + +```js +const doubledNumbers = numbers.map(function(n) { + return n * 2; +}); +console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12] +``` + +Was passiert hier? Wir verwenden .map auf dem *numbers*-Array, die Map iteriert über jedes Element des Arrays und übergibt es unserer Funktion. Das Ziel der Funktion ist es, einen neuen Wert aus dem übergebenen Wert zu erzeugen und zurückzugeben, so dass map ihn ersetzen kann. + +Lasst uns diese Funktion extrahieren, um es einmal klarer zu machen: + +```js +const doubleN = function(n) { return n * 2; }; +const doubledNumbers = numbers.map(doubleN); +console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12] +``` + +**Hinweis**: Du wirst diese Methode oft in Kombination mit [Pfeilfunktionen](#-pfeilfunktion) antreffen + +```js +const doubledNumbers = numbers.map(n => n * 2); +console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12] +``` + +```numbers.map(doubleN)``` produziert ```[doubleN(0), doubleN(1), doubleN(2), doubleN(3), doubleN(4), doubleN(5), doubleN(6)]```, was gleich ```[0, 2, 4, 6, 8, 10, 12]``` ist. + +> **Hinweis:** Wenn du kein neues Array zurückgeben musst und nur eine Schleife mit Nebeneffekten machen willst, solltest du vielleicht stattdessen eine for / forEach-Schleife anstelle von map verwenden. + +##### Array.prototype.filter() + +```js +const evenNumbers = numbers.filter(function(n) { + return n % 2 === 0; // true, wenn "n" gerade ist, false, wenn "n" ungerade ist +}); +console.log(evenNumbers); // [0, 2, 4, 6] +``` + +**Hinweis**: Du wirst diese Methode oft in Kombination mit [Pfeilfunktionen](#-pfeilfunktion) antreffen + +```js +const evenNumbers = numbers.filter(n => n % 2 === 0); +console.log(evenNumbers); // [0, 2, 4, 6] +``` + +Wir verwenden .filter auf dem *numbers*-Array, filter iteriert über jedes Element des Arrays und übergibt es unserer Funktion. Das Ziel der Funktion ist es, einen Boolean zurückzugeben, der bestimmt, ob der aktuelle Wert behalten wird oder nicht. Filter gibt dann das Array nur mit den behaltenen Werten zurück. + +##### Array.prototype.reduce() + +Das Ziel der reduce-Methode ist es, alle Elemente des Arrays, über das sie iteriert, in einen einzigen Wert zu *reduzieren*. Wie diese Elemente aggregiert werden, liegt bei dir. + +```js +const sum = numbers.reduce( + function(acc, n) { + return acc + n; + }, + 0 // Wert der Akkumulatorvariablen beim ersten Iterationsschritt +); + +console.log(sum) // 21 +``` + +**Hinweis**: Du wirst diese Methode oft in Kombination mit [Pfeilfunktionen](#-pfeilfunktion) antreffen + +```js +const sum = numbers.reduce((acc, n) => acc + n, 0); +console.log(sum) // 21 +``` + +Wie bei den .map- und .filter-Methoden wird .reduce auf einem Array angewandt und nimmt eine Funktion als ersten Parameter. + +Diesmal gibt es allerdings Änderungen: + +- .reduce nimmt zwei Parameter + +Der erste Parameter ist eine Funktion, die bei jedem Iterationsschritt aufgerufen wird. + +Der zweite Parameter ist der Wert der Akkumulatorvariablen (*acc* hier) beim ersten Iterationsschritt (lies den nächsten Punkt, um dies zu verstehen). + +- Funktionsparameter + +Die Funktion, die du als ersten Parameter von .reduce übergibst, nimmt zwei Parameter entgegen. Der erste (*acc* hier) ist die Akkumulatorvariable, während der zweite Parameter (*n*) das aktuelle Element ist. + +Die Akkumulatorvariable entspricht dem Rückgabewert deiner Funktion im **vorherigen** Iterationsschritt. Beim ersten Schritt der Iteration ist *acc* gleich dem Wert, den du als zweiten Parameter von .reduce übergeben hast. + +###### Beim ersten Iterationsschritt + +```acc = 0```, weil wir 0 als zweiten Parameter für reduce übergeben haben + +```n = 0```, erstes Element des *numbers*-Arrays + +Die Funktion gibt *acc* + *n* --> 0 + 0 --> 0 zurück + +###### Beim zweiten Iterationsschritt + +```acc = 0```, weil es der Wert ist, den die Funktion im vorherigen Iterationsschritt zurückgegeben hat + +```n = 1```, zweites Element des *numbers*-Arrays + +Die Funktion gibt *acc* + *n* --> 0 + 1 --> 1 zurück + +###### Beim dritten Iterationsschritt + +```acc = 1```, weil es der Wert ist, den die Funktion im vorherigen Iterationsschritt zurückgegeben hat + +```n = 2```, drittes Element des *numbers*-Arrays + +Die Funktion gibt *acc* + *n* --> 1 + 2 --> 3 zurück + +###### Beim vierten Iterationsschritt + +```acc = 3```, weil es der Wert ist, den die Funktion im vorherigen Iterationsschritt zurückgegeben hat + +```n = 3```, viertes Element des *numbers*-Arrays + +Die Funktion gibt *acc* + *n* --> 3 + 3 --> 6 zurück + +###### [...] Beim letzten Iterationsschritt + +```acc = 15```, weil es der Wert ist, den die Funktion im vorherigen Iterationsschritt zurückgegeben hat + +```n = 6```, letztes Element des *numbers*-Arrays + +Die Funktion gibt *acc* + *n* --> 15 + 6 --> 21 zurück + +Da dies der letzte Iterationsschritt ist, gibt **.reduce** 21 zurück. + +##### Array.prototype.find() + +```js +const greaterThanZero = numbers.find(function(n) { + return n > 0; // gibt die Nummer zurück, die gerade größer als 0 ist +}); +console.log(greaterThanZero); // 1 +``` + +**Hinweis**: Du wirst diese Methode oft in Kombination mit [Pfeilfunktionen](#-pfeilfunktion) antreffen + +Wir verwenden .find auf dem *numbers*-Array, .find iteriert über jedes Element des Arrays und übergibt es unserer Funktion, bis die Bedingung erfüllt ist. Das Ziel der Funktion ist es, das Element zurückzugeben, das die aktuelle Testfunktion erfüllt. Die .find-Methode führt die Callback-Funktion einmal für jeden Index des Arrays aus, bis der Callback einen wahren Wert zurückgibt. + +**Hinweis**: Sie gibt sofort den Wert des Elements zurück (das die Bedingung erfüllt), wenn gefunden. Andernfalls gibt sie undefined zurück. + +#### Externe Ressource + +- [Verständnis von map / filter / reduce in JS](https://hackernoon.com/understanding-map-filter-and-reduce-in-javascript-5df1c7eee464) + +### Spread-Operator "..." + +Der Spread-Operator ```...``` wurde mit ES2015 eingeführt und wird verwendet, um Elemente eines iterierbaren Objekts (wie ein Array) an Stellen auszubreiten, an denen mehrere Elemente passen können. + +#### Beispielcode + +```js +const arr1 = ["a", "b", "c"]; +const arr2 = [...arr1, "d", "e", "f"]; // ["a", "b", "c", "d", "e", "f"] +``` + +```js +function myFunc(x, y, ...params) { + console.log(x); + console.log(y); + console.log(params) +} + +myFunc("a", "b", "c", "d", "e", "f") +// "a" +// "b" +// ["c", "d", "e", "f"] +``` + +```js +const { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; +console.log(x); // 1 +console.log(y); // 2 +console.log(z); // { a: 3, b: 4 } + +const n = { x, y, ...z }; +console.log(n); // { x: 1, y: 2, a: 3, b: 4 } +``` + +#### Erklärung + +##### In Iterables (wie Arrays) + +Wenn wir die folgenden zwei Arrays haben: + +```js +const arr1 = ["a", "b", "c"]; +const arr2 = [arr1, "d", "e", "f"]; // [["a", "b", "c"], "d", "e", "f"] +``` + +*arr2* das erste Element ist ein Array, weil *arr1* so, wie es ist, in *arr2* eingefügt wird. Aber was wir wollen, ist, dass *arr2* ein Array von Buchstaben ist. Um dies zu erreichen, können wir die Elemente von *arr1* in *arr2* *ausbreiten*. + +Mit dem Spread-Operator + +```js +const arr1 = ["a", "b", "c"]; +const arr2 = [...arr1, "d", "e", "f"]; // ["a", "b", "c", "d", "e", "f"] +``` + +##### Funktionsrestparameter + +In Funktionsparametern können wir den Rest-Operator verwenden, um Parameter in einem Array einzufügen, über das wir iterieren können. Es gibt bereits ein **arguments**-Objekt, das an jede Funktion gebunden ist und einem Array mit allen Parametern entspricht, die in die Funktion eingegeben wurden. + +```js +function myFunc() { + for (var i = 0; i < arguments.length; i++) { + console.log(arguments[i]); + } +} + +myFunc("Nick", "Anderson", 10, 12, 6); +// "Nick" +// "Anderson" +// 10 +// 12 +// 6 +``` + +Aber nehmen wir an, dass wir wollen, dass diese Funktion einen neuen Schüler mit seinen Noten und seinem Durchschnitt erstellt. Wäre es nicht praktischer, die ersten beiden Parameter in zwei separate Variablen zu extrahieren und dann alle Noten in einem Array zu haben, über das wir iterieren können? + +Genau das ermöglicht der Rest-Operator! + +```js +function createStudent(firstName, lastName, ...grades) { + // firstName = "Nick" + // lastName = "Anderson" + // [10, 12, 6] -- "..." nimmt alle anderen übergebenen Parameter und erzeugt eine "grades"-Arrayvariable, die sie enthält + + const avgGrade = grades.reduce((acc, curr) => acc + curr, 0) / grades.length; // berechnet den Durchschnitt aus den Noten + + return { + firstName: firstName, + lastName: lastName, + grades: grades, + avgGrade: avgGrade + } +} + +const student = createStudent("Nick", "Anderson", 10, 12, 6); +console.log(student); +// { +// firstName: "Nick", +// lastName: "Anderson", +// grades: [10, 12, 6], +// avgGrade: 9,33 +// } +``` + +> **Hinweis:** Die Funktion createStudent ist schlecht, weil wir nicht überprüfen, ob grades.length existiert oder verschieden von 0 ist. Aber so ist es einfacher zu lesen, deshalb habe ich diesen Fall nicht behandelt. + +##### Objekteigenschaften ausbreiten + +Für dieses Beispiel empfehle ich, die vorherigen Erklärungen zum Rest-Operator bei iterierbaren Objekten und Funktionsparametern zu lesen. + +```js +const myObj = { x: 1, y: 2, a: 3, b: 4 }; +const { x, y, ...z } = myObj; // Objektdestrukturierung hier +console.log(x); // 1 +console.log(y); // 2 +console.log(z); // { a: 3, b: 4 } + +// z sind die restlichen Eigenschaften des destrukturierten Objekts: myObj-Objekt minus x- und y-Eigenschaften, die destrukturiert wurden + +const n = { x, y, ...z }; +console.log(n); // { x: 1, y: 2, a: 3, b: 4 } + +// Hier werden die Eigenschaften von z-Objekten in n ausgespreitet +``` + +#### Externe Ressourcen + +- [TC39 - Objekt rest/spread](https://github.com/tc39/proposal-object-rest-spread) +- [Einführung in den Spread-Operator - WesBos](https://github.com/wesbos/es6-articles/blob/master/28%20-%20Spread%20Operator%20Introduction.md) +- [JavaScript & der Spread-Operator](https://codeburst.io/javascript-the-spread-operator-a867a71668ca) +- [6 großartige Verwendungen des Spread-Operators](https://davidwalsh.name/spread-operator) + +### Objekteigenschaften Kurzschreibweise + +Wenn man einer Objekteigenschaft eine Variable zuweist, deren Name gleich dem Eigenschaftsnamen ist, kann man folgendes tun: + +```js +const x = 10; +const myObj = { x }; +console.log(myObj.x) // 10 +``` + +#### Erklärung + +Normalerweise (vor ES2015) wenn du ein neues *Objektliteral* deklarierst und Variablen als Werte für Objekteigenschaften verwenden möchtest, würdest du diesen Code schreiben: + +```js +const x = 10; +const y = 20; + +const myObj = { + x: x, // weist x Variable Wert myObj.x zu + y: y // weist y Variable Wert myObj.y zu +}; + +console.log(myObj.x) // 10 +console.log(myObj.y) // 20 +``` + +Wie du sehen kannst, ist das ziemlich wiederholend, weil die Namen der Eigenschaften von myObj den Variablennamen entsprechen, die du diesen zuweisen willst. + +Mit ES2015 kannst du diese Abkürzung verwenden, wenn der Variablenname dem Namen der Eigenschaft entspricht: + +```js +const x = 10; +const y = 20; + +const myObj = { + x, + y +}; + +console.log(myObj.x) // 10 +console.log(myObj.y) // 20 +``` + +#### Externe Ressourcen + +- [Property shorthand - ES6 Features](http://es6-features.org/#PropertyShorthand) + + +### Promises + +Ein Promise ist ein Objekt, das synchron aus einer asynchronen Funktion zurückgegeben werden kann ([ref](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261#3cd0)). + +Promises können verwendet werden, um der [Callback-Hölle](http://callbackhell.com/) zu entkommen, und sie begegnen uns in modernen JavaScript-Projekten immer häufiger. + +#### Beispielcode + +```js +const fetchingPosts = new Promise((res, rej) => { + $.get("/posts") + .done(posts => res(posts)) + .fail(err => rej(err)); +}); + +fetchingPosts + .then(posts => console.log(posts)) + .catch(err => console.log(err)); +``` + +#### Erklärung + +Wenn du eine *Ajax-Anfrage* machst, ist die Antwort nicht synchron, weil du eine Ressource anforderst, die einige Zeit zur Antwort braucht. Sie könnte sogar nie kommen, falls die angeforderte Ressource aus irgendeinem Grund nicht verfügbar ist (404). + +Um diese Art von Situation zu handhaben, hat uns ES2015 *Promises* gegeben. Promises können drei verschiedene Zustände haben: + +- Pending (Im Warten) +- Fulfilled (Erfüllt) +- Rejected (Abgelehnt) + +Nehmen wir an, wir möchten Promises verwenden, um eine Ajax-Anfrage zu tätigen und die Ressource X zu holen. + +##### Das Promise erstellen + +Zuerst erstellen wir ein Promise. Wir verwenden die jQuery get-Methode, um unsere Ajax-Anfrage an X zu stellen. + +```js +const xFetcherPromise = new Promise( // Erstelle Promise mit dem "new" Schlüsselwort und speichere es in einer Variable + function(resolve, reject) { // Der Promise-Konstruktor nimmt eine Funktion entgegen, die selbst die Parameter resolve und reject besitzt + $.get("X") // Starte die Ajax-Anfrage + .done(function(X) { // Sobald die Anfrage beendet ist... + resolve(X); // ... erfülle das Promise mit dem Wert X als Parameter + }) + .fail(function(error) { // Falls die Anfrage fehlschlägt... + reject(error); // ... lehne das Promise mit dem Fehler als Parameter ab + }); + } +) +``` + +Wie im obigen Beispiel zu sehen ist, nimmt das Promise-Objekt eine *Executor*-Funktion entgegen, die zwei Parameter **resolve** und **reject** hat. Diese Parameter sind Funktionen, die, wenn aufgerufen, den Promise-Zustand *pending* jeweils in einen *fulfilled* und *rejected* Zustand überführen. + +Das Promise befindet sich in einem Pending-Zustand nach seiner Erstellung, und seine *Executor*-Funktion wird sofort ausgeführt. Sobald eine der Funktionen *resolve* oder *reject* in der *Executor*-Funktion aufgerufen wird, ruft das Promise seine zugehörigen Handler auf. + +##### Verwendung von Promise-Handlern + +Um das Ergebnis (oder den Fehler) des Promises zu erhalten, müssen wir ihm Handler zuweisen, indem wir Folgendes tun: + +```js +xFetcherPromise + .then(function(X) { + console.log(X); + }) + .catch(function(err) { + console.log(err) + }) +``` + +Wenn das Promise erfolgreich ist, wird *resolve* ausgeführt und die Funktion, die als `.then` Parameter übergeben wurde, wird ausgeführt. + +Wenn es fehlschlägt, wird *reject* ausgeführt und die Funktion, die als `.catch` Parameter übergeben wurde, wird ausgeführt. + +> **Hinweis :** Wenn das Promise bereits erfüllt oder abgelehnt wurde, wenn ein entsprechender Handler angehängt wird, wird der Handler aufgerufen, sodass es keinen Wettlaufzustand zwischen dem Abschluss einer asynchronen Operation und dem Anhängen ihrer Handler gibt. [(Ref: MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Description) + +#### Externe Ressourcen + +- [JavaScript Promises für Dummys - Jecelyn Yeen](https://scotch.io/tutorials/javascript-promises-for-dummies) +- [JavaScript Promise API - David Walsh](https://davidwalsh.name/promises) +- [Promises verwenden - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) +- [Was ist ein Promise - Eric Elliott](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261) +- [JavaScript Promises: Eine Einführung - Jake Archibald](https://developers.google.com/web/fundamentals/getting-started/primers/promises) +- [Promise Dokumentation - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) + +### Template Literale + +Template Literale sind eine [*Ausdrucksinterpolation*](https://en.wikipedia.org/wiki/String_interpolation) für ein- und mehrzeilige Strings. + +Anders ausgedrückt, handelt es sich um eine neue String-Syntax, in der du bequem jede JavaScript-Ausdrücke verwenden kannst (beispielsweise Variablen). + +#### Beispielcode + +```js +const name = "Nick"; +`Hallo ${name}, der folgende Ausdruck ist gleich vier : ${2+2}`; + +// Hallo Nick, der folgende Ausdruck ist gleich vier: 4 +``` + +#### Externe Ressourcen + +- [String-Interpolation - ES6 Features](http://es6-features.org/#StringInterpolation) +- [ES6 Template Literale - Addy Osmani](https://developers.google.com/web/updates/2015/01/ES6-Template-Strings) + +### Tagged Template Literale + +Template Tags sind *Funktionen, die einem [Template Literal](#template-literals) vorangestellt werden können*. Wenn eine Funktion auf diese Weise aufgerufen wird, ist der erste Parameter ein Array der *Strings*, die zwischen den interpolierten Variablen des Templates auftreten, und die nachfolgenden Parameter sind die interpolierten Werte. Verwende einen Spread-Operator `...`, um alle zu erfassen. [(Ref: MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals). + +> **Hinweis :** Eine berühmte Bibliothek namens [styled-components](https://www.styled-components.com/) setzt schwer auf dieses Feature. + +Hier ist ein Spielzeugbeispiel, wie sie funktionieren. +```js +function highlight(strings, ...values) { + const interpolation = strings.reduce((prev, current) => { + return prev + current + (values.length ? "" + values.shift() + "" : ""); + }, ""); + + return interpolation; +} + +const condiment = "Marmelade"; +const meal = "Toast"; + +highlight`Ich mag ${condiment} auf ${meal}.`; +// "Ich mag Marmelade auf Toast." +``` + +Ein interessanteres Beispiel: +```js +function comma(strings, ...values) { + return strings.reduce((prev, next) => { + let value = values.shift() || []; + value = value.join(", "); + return prev + next + value; + }, ""); +} + +const snacks = ['Äpfel', 'Bananen', 'Kirschen']; +comma`Ich mag ${snacks} zum Knabbern.`; +// "Ich mag Äpfel, Bananen, Kirschen zum Knabbern." +``` + +#### Externe Ressourcen +- [Wes Bos zu gekennzeichneten Template Literalen](http://wesbos.com/tagged-template-literals/) +- [Bibliothek von gängigen Template Tags](https://github.com/declandewet/common-tags) + +### Imports / Exports + +ES6-Module werden verwendet, um auf Variablen oder Funktionen in einem Modul zuzugreifen, die explizit von den Modulen exportiert wurden, die es importiert. + +Ich empfehle dringend, sich die Ressourcen von MDN zu Import/Export anzusehen (siehe externe Ressourcen unten), da sie sowohl unkompliziert als auch vollständig sind. + +#### Erklärung mit Beispielcode + +##### Benannte Exports + +Mit benannten Exports können mehrere Werte aus einem Modul exportiert werden. + +> **Hinweis :** Du kannst nur [Erstklassige Bürger](https://en.wikipedia.org/wiki/First-class_citizen) mit einem Namen benannt exportieren. + +```js +// mathConstants.js +export const pi = 3.14; +export const exp = 2.7; +export const alpha = 0.35; + +// ------------- + +// myFile.js +import { pi, exp } from './mathConstants.js'; // Benannter Import -- Destructuring-ähnliche Syntax +console.log(pi) // 3.14 +console.log(exp) // 2.7 + +// ------------- + +// mySecondFile.js +import * as constants from './mathConstants.js'; // Injiziere alle exportierten Werte in die Variable constants +console.log(constants.pi) // 3.14 +console.log(constants.exp) // 2.7 +``` + +Obwohl benannte Imports wie *Destructuring* aussehen, haben sie eine andere Syntax und sind nicht dasselbe. Sie unterstützen keine Standardwerte oder *tiefes* Destructuring. + +Außerdem kannst du Aliase verwenden, aber die Syntax ist anders als die, die beim Destructuring verwendet wird: + +```js +import { foo as bar } from 'myFile.js'; // foo wird importiert und in eine neue bar Variable injiziert +``` + +##### Standardimport / export + +Bezüglich des Standardexports gibt es nur einen einzigen Standardexport pro Modul. Ein Standardexport kann eine Funktion, eine Klasse, ein Objekt oder irgendetwas anderes sein. Dieser Wert wird als der "Haupt"-exportierte Wert angesehen, da er am einfachsten zu importieren ist. [Ref: MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#Description) + +```js +// coolNumber.js +const ultimateNumber = 42; +export default ultimateNumber; + +// ------------ + +// myFile.js +import number from './coolNumber.js'; +// Standardexport, unabhängig von seinem Namen, wird automatisch in die Variable number injiziert; +console.log(number) // 42 +``` + +Exportieren einer Funktion: + +```js +// sum.js +export default function sum(x, y) { + return x + y; +} +// ------------- + +// myFile.js +import sum from './sum.js'; +const result = sum(1, 2); +console.log(result) // 3 +``` + +#### Externe Ressourcen + +- [ES6-Module in Stichpunkten](https://ponyfoo.com/articles/es6#modules) +- [Export - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) +- [Import - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) +- [Verständnis von ES6-Modulen](https://www.sitepoint.com/understanding-es6-modules/) +- [Destructuring Sonderfall - Importanweisungen](https://ponyfoo.com/articles/es6-destructuring-in-depth#special-case-import-statements) +- [Missverständnisse bezüglich ES6-Modulen - Kent C. Dodds](https://medium.com/@kentcdodds/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution-ad2d5ab93ce0) +- [Module in JavaScript](http://exploringjs.com/es6/ch_modules.html#sec_modules-in-javascript) + +### JavaScript *this* + +Der *this*-Operator verhält sich anders als in anderen Sprachen und wird in den meisten Fällen durch die Art und Weise bestimmt, wie eine Funktion aufgerufen wird. ([Ref: MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this)). + +Diese Vorstellung hat viele Feinheiten und ist ziemlich schwierig, daher schlage ich vor, in die unten aufgeführten externen Ressourcen einzutauchen. Ich werde das wiedergeben, was ich persönlich im Kopf habe, um zu bestimmen, was *this* gleich ist. Ich habe diesen Tipp aus [diesem Artikel von Yehuda Katz](http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/) gelernt. + +```js +function myFunc() { + ... +} + +// Nach jeder Anweisung findest du den Wert von *this* in myFunc + +myFunc.call("myString", "hallo") // "myString" -- der erste .call-Parameterwert wird in *this* injiziert + +// Im Nicht-Strict-Modus +myFunc("hallo") // window -- myFunc() ist syntaktischer Zucker für myFunc.call(window, "hallo") + +// Im Strict-Modus +myFunc("hallo") // undefined -- myFunc() ist syntaktischer Zucker für myFunc.call(undefined, "hallo") +``` + +```js +var person = { + myFunc: function() { ... } +} + +person.myFunc.call(person, "test") // person Objekt -- der erste call-Parameter wird in *this* injiziert +person.myFunc("test") // person Objekt -- person.myFunc() ist syntaktischer Zucker für person.myFunc.call(person, "test") + +var myBoundFunc = person.myFunc.bind("hallo") // Erstellt eine neue Funktion, in der wir "hallo" in den *this*-Wert injizieren +person.myFunc("test") // person Objekt -- Die bind-Methode hat keine Auswirkung auf die Originalmethode +myBoundFunc("test") // "hallo" -- myBoundFunc ist person.myFunc mit "hallo" gebunden an *this* +``` + +#### Externe Ressourcen + +- [Verständnis von JavaScript-Funktionsaufrufen und "this" - Yehuda Katz](http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/) +- [JavaScript this - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) + +### Klasse + +JavaScript ist eine [prototypbasierte](https://en.wikipedia.org/wiki/Prototype-based_programming) Sprache (während Java beispielsweise eine [klassenbasierte](https://en.wikipedia.org/wiki/Class-based_programming) Sprache ist). ES6 hat JavaScript-Klassen eingeführt, die als syntaktischer Zucker für die prototypbasierte Vererbung gedacht sind und **kein** neues klassenbasiertes Vererbungsmodell darstellen ([ref](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)). + +Das Wort *class* ist tatsächlich irreführend, wenn du mit Klassen in anderen Sprachen vertraut bist. Wenn du es bist, vermeide Annahmen darüber, wie JavaScript-Klassen funktionieren, auf dieser Grundlage und betrachte es als eine völlig unterschiedliche Vorstellung. + +Da dieses Dokument nicht den Anspruch hat, dir die Sprache von Grund auf beizubringen, gehe ich davon aus, dass du weißt, was Prototypen sind und wie sie sich verhalten. Wenn nicht, sieh dir die unten aufgeführten externen Ressourcen nach dem Beispielcode an. + + +#### Beispiele + +Vor ES6, Prototyp-Syntax: + +```js +var Person = function(name, age) { + this.name = name; + this.age = age; +} +Person.prototype.stringSentence = function() { + return "Hallo, mein Name ist " + this.name + " und ich bin " + this.age; +} +``` + +Mit ES6-Klassensyntax: + +```js +class Person { + constructor(name, age) { + this.name = name; + this.age = age; + } + + stringSentence() { + return `Hallo, mein Name ist ${this.name} und ich bin ${this.age}`; + } +} + +const myPerson = new Person("Manu", 23); +console.log(myPerson.age) // 23 +console.log(myPerson.stringSentence()) // "Hallo, mein Name ist Manu und ich bin 23 +``` + +#### Externe Ressourcen + +Zum Verständnis von Prototypen: + +- [Understanding Prototypes in JS - Yehuda Katz](http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/) +- [A plain English guide to JS prototypes - Sebastian Porto](http://sporto.github.io/blog/2013/02/22/a-plain-english-guide-to-javascript-prototypes/) +- [Vererbung und die Prototyp-Kette - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) + +Zum Verständnis von Klassen: + +- [ES6 Klassen im Detail - Nicolas Bevacqua](https://ponyfoo.com/articles/es6-classes-in-depth) +- [ES6-Features - Klassen](http://es6-features.org/#ClassDefinition) +- [JavaScript Klassen - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) + +### `Extends` und `super` + +Das Schlüsselwort `extends` wird in Klassendeklarationen oder Klassenausdrücken verwendet, um eine Klasse zu erstellen, die ein Kind einer anderen Klasse ist ([Ref: MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends)). Die Unterklasse erbt alle Eigenschaften der Oberklasse und kann zusätzlich neue Eigenschaften hinzufügen oder die geerbten modifizieren. + +Das Schlüsselwort `super` wird verwendet, um Funktionen eines Oberobjekts aufzurufen, einschließlich seines Konstruktors. + +- Das Schlüsselwort `super` muss verwendet werden, bevor das Schlüsselwort `this` im Konstruktor verwendet wird. +- Der Aufruf von `super()` ruft den Konstruktor der Oberklasse auf. Wenn du einige Argumente im Konstruktor einer Klasse an den Konstruktor ihres Elternteils weitergeben möchtest, rufst du ihn mit `super(arguments)` auf. +- Wenn die Oberklasse eine Methode (auch statisch) namens `X` hat, kannst du mit `super.X()` diese in einer Unterklasse aufrufen. + +#### Beispielcode + +```js +class Polygon { + constructor(height, width) { + this.name = 'Polygon'; + this.height = height; + this.width = width; + } + + getHelloPhrase() { + return `Hallo, ich bin ein ${this.name}`; + } +} + +class Square extends Polygon { + constructor(length) { + // Hier wird der Konstruktor der Oberklasse mit den Längen + // für die Breite und Höhe des Polygons aufgerufen + super(length, length); + // Hinweis: In abgeleiteten Klassen muss super() aufgerufen werden, bevor du + // 'this' verwenden kannst. Wenn du dies auslässt, wird ein Referenzfehler verursacht. + this.name = 'Quadrat'; + this.length = length; + } + + getCustomHelloPhrase() { + const polygonPhrase = super.getHelloPhrase(); // Zugriff auf die Elternmethode mit super.X()-Syntax + return `${polygonPhrase} mit einer Länge von ${this.length}`; + } + + get area() { + return this.height * this.width; + } +} + +const mySquare = new Square(10); +console.log(mySquare.area) // 100 +console.log(mySquare.getHelloPhrase()) // 'Hallo, ich bin ein Quadrat' -- Square erbt von Polygon und hat Zugriff auf seine Methoden +console.log(mySquare.getCustomHelloPhrase()) // 'Hallo, ich bin ein Quadrat mit einer Länge von 10' +``` + +**Hinweis :** Wenn wir versucht hätten, `this` vor dem Aufruf von `super()` in der Square-Klasse zu verwenden, wäre ein ReferenceError ausgelöst worden: + +```js +class Square extends Polygon { + constructor(length) { + this.height; // ReferenceError, super muss zuerst aufgerufen werden! + + // Hier wird der Konstruktor der Oberklasse mit den Längen + // für die Breite und Höhe des Polygons aufgerufen + super(length, length); + + // Hinweis: In abgeleiteten Klassen muss super() aufgerufen werden bevor du + // 'this' verwenden kannst. Wenn du dies auslässt, wird ein Referenzfehler verursacht. + this.name = 'Quadrat'; + } +} +``` + +#### Externe Ressourcen + +- [Extends - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends) +- [Super-Operator - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super) +- [Vererbung - MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance) + +### Async Await + +Zusätzlich zu [Promises](#promises) gibt es eine neue Syntax, die du vielleicht antriffst, um asynchronen Code zu handhaben, nämlich *async / await*. + +Der Zweck von async/await-Funktionen ist es, die Verwendung von Promises synchron zu vereinfachen und ein Verhalten für eine Gruppe von Promises durchzuführen. Genau wie Promises ähnlich wie strukturierte Callbacks sind, ist async/await ähnlich wie die Kombination von Generatoren und Promises. Async-Funktionen geben *immer* ein Promise zurück. ([Ref: MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)) + +> **Hinweis :** Du musst verstehen, was Promises sind und wie sie funktionieren, bevor du versuchst, async / await zu verstehen, da diese darauf basieren. + +> **Hinweis 2:** [*await* muss in einer *async* Funktion verwendet werden](https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9#f3f0), was bedeutet, dass du await nicht auf der obersten Ebene unseres Codes verwenden kannst, da dies nicht innerhalb einer async-Funktion ist. + +#### Beispielcode + +```js +async function getGithubUser(username) { // das async-Schlüsselwort ermöglicht die Verwendung von await in der Funktion und bedeutet, dass die Funktion ein Promise zurückgibt + const response = await fetch(`https://api.github.com/users/${username}`); // Die Ausführung wird hier pausiert, bis das von fetch zurückgegebene Promise aufgelöst ist + return response.json(); +} + +getGithubUser('mbeaudru') + .then(user => console.log(user)) // das Nutzer-Response loggen - await-Syntax kann nicht verwendet werden, da dieser Code nicht in einer async-Funktion ist + .catch(err => console.log(err)); // wenn ein Fehler in unserer async-Funktion geworfen wird, werden wir ihn hier erwischen +``` + +#### Erklärung mit Beispielcode + +*Async / Await* basiert auf Promises, ermöglicht aber eine imperativere Code-Stil. + +Der *async*-Operator markiert eine Funktion als asynchron und wird immer ein *Promise* zurückgeben. Du kannst den *await*-Operator in einer *async*-Funktion verwenden, um die Ausführung in dieser Zeile zu pausieren, bis das vom Ausdruck zurückgegebene Promise entweder aufgelöst oder abgelehnt wird. + +```js +async function myFunc() { + // wir können den await-Operator verwenden, weil diese Funktion async ist + return "hallo welt"; +} + +myFunc().then(msg => console.log(msg)) // "hallo welt" -- der Rückgabewert von myFunc wird durch den async-Operator in ein Promise umgewandelt +``` + +Wenn das *return*-Statement einer async-Funktion erreicht wird, wird das Promise mit dem zurückgegebenen Wert erfüllt. Wenn innerhalb einer async-Funktion ein Fehler geworfen wird, wechselt der Promise-Zustand zu *abgelehnt*. Wenn aus einer async-Funktion kein Wert zurückgegeben wird, wird trotzdem ein Promise zurückgegeben und löst ohne Wert auf, wenn die Ausführung der async-Funktion abgeschlossen ist. + +Der *await*-Operator wird verwendet, um auf die Erfüllung eines *Promise* zu warten und kann nur im Körper einer *async*-Funktion verwendet werden. Beim Auftreffen wird die Codeausführung pausiert, bis das Promise erfüllt ist. + +> **Hinweis :** *fetch* ist eine Funktion, die ein Promise zurückgibt, das einen AJAX-Anfrage ermöglicht + +Sehen wir uns an, wie wir einen Github-Nutzer mit Promises zuerst abrufen würden: + +```js +function getGithubUser(username) { + return fetch(`https://api.github.com/users/${username}`).then(response => response.json()); +} + +getGithubUser('mbeaudru') + .then(user => console.log(user)) + .catch(err => console.log(err)); +``` + +Hier ist das äquivalente *async / await*: + +```js +async function getGithubUser(username) { // promise + await-Schlüsselwortverwendung erlaubt + die Antwort = await fetch(`https://api.github.com/users/${username}`); // Die Ausführung stoppt hier, bis das fetch-Promise erfüllt ist + return response.json(); +} + +getGithubUser('mbeaudru') + .then(user => console.log(user)) + .catch(err => console.log(err)); +``` + +Die *async / await*-Syntax ist besonders praktisch, wenn du Promises verketten musst, die voneinander abhängig sind. + +Wenn du beispielsweise einen Token abrufen musst, um einen Blogeintrag in einer Datenbank und dann die Autoreninformationen abzurufen: + +> **Hinweis :** *await*-Ausdrücke müssen in Klammern eingefasst werden, um Methoden und Eigenschaften des aufgelösten Werts auf derselben Zeile aufzurufen. + +```js +async function fetchPostById(postId) { + const token = (await fetch('http://github.com/token_url')).json().token; + const post = (await fetch(`/posts/${postId}?token=${token}`)).json(); + der Autor = (await fetch(`/users/${post.authorId}`)).json(); + + post.author = author; + return post; +} + +fetchPostById('gzIrzeo64') + .then(post => console.log(post)) + .catch(err => console.log(err)); +``` + +##### Fehlerbehandlung + +Sofern wir *try / catch*-Blöcke nicht um *await*-Ausdrücke herum hinzufügen, werden unerfasste Ausnahmen – unabhängig davon, ob sie im Körper Ihrer *async*-Funktion oder während ihrer Unterbrechung während *await* geworfen wurden – das Promise, das von der *async*-Funktion zurückgegeben wird, ablehnen. Die Verwendung des `throw`-Statements in einer asynchronen Funktion ist dasselbe wie die Rückgabe eines Promises, das abgelehnt wird. [(Ref: PonyFoo)](https://ponyfoo.com/articles/understanding-javascript-async-await#error-handling). + +> **Hinweis :** Promises verhalten sich gleich! + +Mit Promises sieht die Fehlerbearbeitungskette so aus: + +```js +function getUser() { // Dieses Promise wird abgelehnt! + return new Promise((res, rej) => rej("Benutzer nicht gefunden!")); +} + +function getAvatarByUsername(userId) { + return getUser(userId).then(user => user.avatar); +} + +function getUserAvatar(username) { + return getAvatarByUsername(username).then(avatar => ({ username, avatar })); +} + +getUserAvatar('mbeaudru') + .then(res => console.log(res)) + .catch(err => console.log(err)); // "Benutzer nicht gefunden!" +``` + +Das Äquivalent mit *async / await*: + +```js +async function getUser() { // Das zurückgegebene Promise wird abgelehnt sein! + wirf "Benutzer nicht gefunden!"; +} + +async function getAvatarByUsername(userId) => { + const user = await getUser(userId); + return user.avatar; +} + +async function getUserAvatar(username) { + var avatar = await getAvatarByUsername(username); + return { username, avatar }; +} + +getUserAvatar('mbeaudru') + .then(res => console.log(res)) + .catch(err => console.log(err)); // "Benutzer nicht gefunden!" +``` + +#### Externe Ressourcen + +- [Async/Await - JavaScript.Info](https://javascript.info/async-await) +- [ES7 Async/Await](http://rossboucher.com/await/#/) +- [6 Gründe, warum JavaScripts Async/Await Promises wegblasen](https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9) +- [JavaScript awaits](https://dev.to/kayis/javascript-awaits) +- [Verwendung von Async Await in Express mit Node 8](https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016) +- [Async-Funktion](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) +- [Await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) +- [Verwendung von async / await in express mit node 8](https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016) + + +### Truthy / Falsy + +In JavaScript ist ein Truthy- oder Falsy-Wert ein Wert, der in einen Boolean umgewandelt wird, wenn er in einem booleschen Kontext ausgewertet wird. Ein Beispiel für einen booleschen Kontext wäre die Auswertung einer ```if```-Bedingung: + +Jeder Wert wird zu ```true``` umgewandelt, es sei denn, sie sind gleich: + +- ```false``` +- ```0``` +- ```""``` (leerer String) +- ```null``` +- ```undefined``` +- ```NaN``` + +Hier sind Beispiele für *booleschen Kontext*: + +- Auswertung einer ```if```-Bedingung + +```js +if (myVar) {} +``` + +```myVar``` kann ein [first-class citizen](https://en.wikipedia.org/wiki/First-class_citizen) (Variable, Funktion, Boolean) sein, aber es wird in einen Boolean umgewandelt, weil es in einem booleschen Kontext ausgewertet wird. + +- Nach dem logischen **NICHT** ```!```-Operator + +Dieser Operator gibt false zurück, wenn sein einzelner Operand in true umgewandelt werden kann; andernfalls gibt er true zurück. + +```js +!0 // true -- 0 ist falsy, also gibt es true zurück +!!0 // false -- 0 ist falsy, also gibt !0 true zurück, daher liefert !(!0) false +!!"" // false -- leerer String ist falsy, also NICHT (NICHT false) ist gleich false +``` + +- Mit dem *Boolean*-Objektkonstruktor + +```js +new Boolean(0) // false +new Boolean(1) // true +``` + +- In einer ternären Auswertung + +```js +myVar ? "truthy" : "falsy" +``` + +myVar wird in einem booleschen Kontext ausgewertet. + +Sei vorsichtig beim Vergleichen von 2 Werten. Die Objektwerte (die zu true umgewandelt werden sollten) werden **nicht** in einen Boolean umgewandelt, sondern es wird gezwungen, in einen primitiven Wert mithilfe der [ToPrimitives-Spezifikation](http://javascript.info/object-toprimitive) umgewandelt. Intern, wenn ein Objekt mit einem Boolean-Wert wie `[] == true` verglichen wird, macht es `[].toString() == true`, also... + +```js +let a = [] == true // a ist false, da [].toString() "" zurückgibt. +let b = [1] == true // b ist true, da [1].toString() "1" zurückgibt. +let c = [2] == true // c ist false, da [2].toString() "2" zurückgibt. +``` + +#### Externe Ressourcen + +- [Truthy (MDN)](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) +- [Falsy (MDN)](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) +- [Truthy und Falsy-Werte in JS - Josh Clanton](http://adripofjavascript.com/blog/drips/truthy-and-falsy-values-in-javascript.html) + +### Anamorphismen und Katamorphismen + +#### Anamorphismen + +Anamorphismen sind Funktionen, die von einem Objekt auf eine komplexere Struktur abbilden, die den Typ des Objekts enthält. Es ist der Prozess des *Entfaltens* einer einfachen Struktur in eine komplexere. Betrachte das Entfalten einer Ganzzahl in eine Liste von Ganzzahlen. Die Ganzzahl ist unser Ausgangsobjekt und die Liste von Ganzzahlen ist die komplexere Struktur. + +**Beispielcode** + +```js +function downToOne(n) { + const list = []; + + for (let i = n; i > 0; --i) { + list.push(i); + } + + return list; +} + +downToOne(5) + //=> [ 5, 4, 3, 2, 1 ] +``` + +#### Katamorphismen + +Katamorphismen sind das Gegenteil von Anamorphismen, da sie Objekte einer komplexeren Struktur nehmen und sie in einfachere Strukturen *falten*. Nehme das folgende Beispiel `product`, welches eine Liste von Ganzzahlen nimmt und eine einzelne Ganzzahl zurückgibt. + +**Beispielcode** + +```js +function product(list) { + let product = 1; + + for (const n of list) { + product = product * n; + } + + return product; +} + +product(downToOne(5)) // 120 +``` + +#### Externe Ressourcen + +* [Anamorphisms in JavaScript](http://raganwald.com/2016/11/30/anamorphisms-in-javascript.html) +* [Anamorphism](https://en.wikipedia.org/wiki/Anamorphism) +* [Catamorphism](https://en.wikipedia.org/wiki/Catamorphism) + +### Generatoren + +Eine andere Möglichkeit, die Funktion `downToOne` zu schreiben, ist die Verwendung eines Generators. Um ein `Generator`-Objekt zu instanziieren, muss man die `function *`-Deklaration verwenden. Generatoren sind Funktionen, die verlassen und später mit gespeichertem Kontext (Variablenbindungen) wieder betreten werden können. + +Zum Beispiel kann die oben genannte Funktion `downToOne` wie folgt umgeschrieben werden: + +```js +function * downToOne(n) { + for (let i = n; i > 0; --i) { + yield i; + } +} + +[...downToOne(5)] // [ 5, 4, 3, 2, 1 ] +``` + +Generatoren geben ein iterierbares Objekt zurück. Wenn die `next()`-Funktion des Iterators aufgerufen wird, wird sie ausgeführt, bis zum ersten `yield`-Ausdruck, welcher den zurückzugebenden Wert vom Iterator angibt oder mit `yield*`, das an eine andere Generatorfunktion delegiert. Wenn in dem Generator eine `return`-Anweisung aufgerufen wird, wird der Generator als beendet markiert und gibt den Rückgabewert zurück. Weitere Aufrufe von `next()` geben keine neuen Werte zurück. + +**Beispielcode** + +```js +// Yield-Beispiel +function * idMaker() { + var index = 0; + while (index < 2) { + yield index; + index = index + 1; + } +} + +var gen = idMaker(); + +gen.next().value; // 0 +gen.next().value; // 1 +gen.next().value; // undefined +``` + +Der `yield*`-Ausdruck ermöglicht einem Generator, eine andere Generatorfunktion während der Iteration aufzurufen. + +```js +// Yield * Beispiel +function * genB(i) { + yield i + 1; + yield i + 2; + yield i + 3; +} + +function * genA(i) { + yield i; + yield* genB(i); + yield i + 10; +} + +var gen = genA(10); + +gen.next().value; // 10 +gen.next().value; // 11 +gen.next().value; // 12 +gen.next().value; // 13 +gen.next().value; // 20 +``` + +```js +// Generator Return-Beispiel +function* yieldAndReturn() { + yield "Y"; + return "R"; + yield "unerreichbar"; +} + +var gen = yieldAndReturn() +gen.next(); // { value: "Y", done: false } +gen.next(); // { value: "R", done: true } +gen.next(); // { value: undefined, done: true } +``` + +#### Externe Ressourcen + +* [Mozilla MDN Web Docs, Iteratoren und Generatoren](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators#Generators) + +### Statische Methoden + +#### Kurze Erklärung + +Das Schlüsselwort `static` wird in Klassen verwendet, um statische Methoden zu deklarieren. Statische Methoden sind Funktionen in einer Klasse, die zum Klassenobjekt gehören und nicht für irgendeine Instanz dieser Klasse verfügbar sind. + +#### Beispielcode + +```js +class Repo { + static getName() { + return "Repo name is modern-js-cheatsheet" + } +} + +// Beachte, dass wir keine Instanz der Repo-Klasse erstellen mussten +console.log(Repo.getName()) // Der Name des Repos ist modern-js-cheatsheet + +let r = new Repo(); +console.log(r.getName()) // Ungefangener TypeError: r.getName ist keine Funktion +``` + +#### Ausführliche Erklärung + +Statische Methoden können innerhalb einer anderen statischen Methode mithilfe des Schlüsselworts `this` aufgerufen werden, dies funktioniert jedoch nicht für nicht-statische Methoden. Nicht-statische Methoden können nicht direkt auf statische Methoden mit dem Schlüsselwort `this` zugreifen. + +##### Andere statische Methoden aus einer statischen Methode aufrufen. + +Um eine statische Methode aus einer anderen statischen Methode aufzurufen, kann das Schlüsselwort `this` verwendet werden wie folgt; + +```js +class Repo { + static getName() { + return "Repo name is modern-js-cheatsheet" + } + + static modifyName() { + return this.getName() + '-added-this' + } +} + +console.log(Repo.modifyName()) // Der Name des Repos ist modern-js-cheatsheet-added-this +``` + +##### Statische Methoden aus nicht-statischen Methoden aufrufen. + +Nicht-statische Methoden können auf zwei Weisen statische Methoden aufrufen; +1. ###### Mit dem Klassennamen. + +Um Zugriff auf eine statische Methode von einer nicht-statischen Methode zu bekommen, verwenden wir den Klassennamen und rufen die statische Methode wie eine Eigenschaft auf. z.B `ClassName.StaticMethodName` + +```js +class Repo { + static getName() { + return "Repo name is modern-js-cheatsheet" + } + + useName() { + return Repo.getName() + ' and it contains some really important stuff' + } +} + +// Wir müssen die Klasse instanziieren, um nicht-statische Methoden zu verwenden +let r = new Repo() +console.log(r.useName()) // Der Name des Repos ist modern-js-cheatsheet und enthält einige wirklich wichtige Dinge +``` + +2. ###### Mit dem Konstruktor + +Statische Methoden können als Eigenschaften des Konstruktorobjekts aufgerufen werden. + +```js +class Repo { + static getName() { + return "Repo name is modern-js-cheatsheet" + } + + useName() { + // Ruft die statische Methode als Eigenschaft des Konstruktors auf + return this.constructor.getName() + ' and it contains some really important stuff' + } +} + +// Wir müssen die Klasse instanziieren, um nicht-statische Methoden zu verwenden +let r = new Repo() +console.log(r.useName()) // Der Name des Repos ist modern-js-cheatsheet und enthält einige wirklich wichtige Dinge +``` + +#### Externe Ressourcen +- [static keyword- MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static) +- [Statische Methoden- Javascript.info](https://javascript.info/class#static-methods) +- [Statische Mitglieder in ES6- OdeToCode](http://odetocode.com/blogs/scott/archive/2015/02/02/static-members-in-es6.aspx) + +## Glossar + +### Scope + +Der Kontext, in dem Werte und Ausdrücke "sichtbar" sind oder referenziert werden können. Wenn eine Variable oder ein anderer Ausdruck "nicht im aktuellen Scope" ist, dann ist er nicht zur Verwendung verfügbar. + +Quelle: [MDN](https://developer.mozilla.org/en-US/docs/Glossary/Scope) + +### Variablenmutation + +Von einer Variablen wird gesagt, dass sie mutiert wurde, wenn ihr anfänglicher Wert sich danach geändert hat. + +```js +var myArray = []; +myArray.push("firstEl") // myArray wird mutiert +``` + +Eine Variable wird als *unveränderlich* bezeichnet, wenn sie nicht mutiert werden kann. + +[Schau dir den MDN Mutable-Artikel an](https://developer.mozilla.org/en-US/docs/Glossary/Mutable) für weitere Details. From e611067ee561f5f092c875053ea90fb083b33a0b Mon Sep 17 00:00:00 2001 From: Jffarge Date: Tue, 15 Oct 2024 16:33:12 +0200 Subject: [PATCH 15/15] Update README.md (#145) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index c8da33d..8886c85 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ When you struggle to understand a notion, I suggest you look for answers on the - [You don't know JS (book)](https://github.com/getify/You-Dont-Know-JS) - [Eloquent JavaScript (book)](https://eloquentjavascript.net) - [Douglas Crockford's blog](https://www.crockford.com/javascript/) -- [ES6 Features with examples](http://es6-features.org) - [Wes Bos blog (ES6)](https://wesbos.com/javascript) - [Javascript Basics for Beginners](https://www.udacity.com/course/javascript-basics--ud804) - a free Udacity course - [Reddit (JavaScript)](https://www.reddit.com/r/javascript/)