diff --git a/.gitignore b/.gitignore index 6f90fd190..1a71fb7c8 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ sftp-config.json Thumbs.db +/svgs \ No newline at end of file diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index c048430b3..f99bd0a35 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -29,20 +29,40 @@ من الجيد تذكر المصطلحات الواردة أعلاه لأنها مستخدمة في مقالات المطورين على الإنترنت. سنستخدمهم أيضًا. على سبيل المثال ، إذا كانت "الميزة إكس مدعومة بواسطة في8" ، فمن المحتمل أنها تعمل في كروم و أوبرا. +<<<<<<< HEAD ```smart header="كيف تعمل المحركات؟" المحركات معقدة. ولكن الأساسيات بسيطه . +======= +- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome, Opera and Edge. +- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox. +- ...There are other codenames like "Chakra" for IE, "JavaScriptCore", "Nitro" and "SquirrelFish" for Safari, etc. + +The terms above are good to remember because they are used in developer articles on the internet. We'll use them too. For instance, if "a feature X is supported by V8", then it probably works in Chrome, Opera and Edge. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ١. يقرأ ("يحلّل") المحرك (المثبت إذا كان متصفحًا) السكريبت. ٢. ثم يحول ("يترجم") السكربت الى لغة الآلة. +<<<<<<< HEAD ٣. وبعد ذلك يتم تشغيل رموز الآلة، بسرعة كبيرة. +======= +1. The engine (embedded if it's a browser) reads ("parses") the script. +2. Then it converts ("compiles") the script to machine code. +3. And then the machine code runs, pretty fast. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يطبق المحرك التحسينات في كل خطوة من العملية. حتى أنه يراقب النص المترجم أثناء تشغيله ، ويحلل البيانات التي تتدفق من خلاله ، ويزيد من تحسين رموز الآلة بناءً على تلك المعرفة. ``` +<<<<<<< HEAD ## ما الذي يمكن أن يفعله جافا سكريبت في المتصفح؟ +======= +## What can in-browser JavaScript do? + +Modern JavaScript is a "safe" programming language. It does not provide low-level access to memory or the CPU, because it was initially created for browsers which do not require it. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 جافا سكريبت الحديثة هي لغة برمجة "آمنة". لا توفر وصولاً منخفض المستوى إلى الذاكرة أو وحدة المعالجة المركزية، لأنه تم إنشاؤها في البداية للمتصفحات التي لا تتطلب ذلك. @@ -59,7 +79,11 @@ - الحصول على ملفات تعريف الارتباط وتعيينها ، وطرح الأسئلة على الزائر ، وإظهار الرسائل. - تذكر البيانات الموجودة على جانب العميل ("التخزين المحلي"). +<<<<<<< HEAD ## ما الذي لا يمكن لجافا سكريبت في المتصفح فعله؟ +======= +JavaScript's abilities in the browser are limited to protect the user's safety. The aim is to prevent an evil webpage from accessing private information or harming the user's data. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 إمكانيات جافا سكريبت في المتصفح محدودة من أجل سلامة المستخدم. الهدف هو منع صفحة ويب شريرة من الوصول إلى المعلومات الخاصة أو الإضرار ببيانات المستخدم. @@ -67,6 +91,7 @@ - جافا سكريبت على صفحة الويب قد لا تقرأ/تكتب ملفات عشوائية على القرص الصلب أو تنسخها أو تنفذ برامج. ليس لديها وصول مباشر إلى وظائف نظام التشغيل. +<<<<<<< HEAD تسمح المتصفحات الحديثة له بالعمل مع الملفات ، ولكن الوصول محدود ويتم توفيره فقط إذا قام المستخدم بإجراءات معينة ، مثل "إسقاط" ملف في نافذة المتصفح أو تحديده عبر علامة ``. هناك طرق للتفاعل مع الكاميرا / الميكروفون والأجهزة الأخرى ، لكنها تتطلب إذنًا صريحًا من المستخدم. لذلك قد لا تعمل الصفحة التي تم تمكين جافا سكريبت فيها بشكل خفي على تمكين كاميرا الويب ومراقبة المناطق المحيطة وإرسال المعلومات إلى [آن آس أيه](https://ar.wikipedia.org/wiki/وكالة_الأمن_القومي_الأمريكية) @@ -80,22 +105,45 @@ ![](limitations.svg) لا توجد مثل هذه الحدود إذا تم استخدام جافا سكريبت خارج المتصفح ، على سبيل المثال على الخادم. تسمح المتصفحات الحديثة أيضًا الإضافات التي قد تطلب تصريحات ممتدة. +======= + There are ways to interact with the camera/microphone and other devices, but they require a user's explicit permission. So a JavaScript-enabled page may not sneakily enable a web-camera, observe the surroundings and send the information to the [NSA](https://en.wikipedia.org/wiki/National_Security_Agency). +- Different tabs/windows generally do not know about each other. Sometimes they do, for example when one window uses JavaScript to open the other one. But even in this case, JavaScript from one page may not access the other page if they come from different sites (from a different domain, protocol or port). + + This is called the "Same Origin Policy". To work around that, *both pages* must agree for data exchange and must contain special JavaScript code that handles it. We'll cover that in the tutorial. + + This limitation is, again, for the user's safety. A page from `http://anysite.com` which a user has opened must not be able to access another browser tab with the URL `http://gmail.com`, for example, and steal information from there. +- JavaScript can easily communicate over the net to the server where the current page came from. But its ability to receive data from other sites/domains is crippled. Though possible, it requires explicit agreement (expressed in HTTP headers) from the remote side. Once again, that's a safety limitation. + +![](limitations.svg) + +Such limitations do not exist if JavaScript is used outside of the browser, for example on a server. Modern browsers also allow plugins/extensions which may ask for extended permissions. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## ما الذي يجعل جافا سكريبت فريدًا؟ هناك على الأقل *ثلاثة* أشياء رائعة حول جافا سكريبت: ```compare +<<<<<<< HEAD + تكامل تام مع HTML / CSS. + الأشياء البسيطة تتم ببساطة. + مدعوم من قبل جميع المتصفحات الرائدة وتمكينه تلقائيا. +======= ++ Full integration with HTML/CSS. ++ Simple things are done simply. ++ Supported by all major browsers and enabled by default. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` جافا سكريبت هي تقنية المتصفح الوحيدة التي تجمع بين هذه الأشياء الثلاثة. هذا ما يجعل جافا سكريبت فريدًا. هذا هو السبب في أنها الأداة الأكثر انتشارًا لإنشاء واجهات المتصفح. +<<<<<<< HEAD ومع ذلك، تسمح جافا سكريبت أيضًا بإنشاء خوادم وتطبيقات الجوال، إلخ. +======= +That said, JavaScript can be used to create servers, mobile applications, etc. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## لغات "فوق" جافا سكريبت @@ -103,12 +151,17 @@ هذا أمر متوقع ، لأن المشاريع والمتطلبات تختلف من شخص لآخر. +<<<<<<< HEAD ظهرت في الآونة الأخيرة عدد كبير من اللغات الجديدة ، والتي *تم تحويلها* إلى جافا سكريبت قبل تشغيلها في المتصفح. +======= +So, recently a plethora of new languages appeared, which are *transpiled* (converted) to JavaScript before they run in the browser. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 الأدوات الحديثة تجعل الترجمة سريعة وشفافة للغاية ، مما يسمح للمطورين في الواقع بالتشفير بلغة أخرى وتحويلها تلقائيًا "خلف الكواليس". أمثلة على هذه اللغات: +<<<<<<< HEAD - [كوفي سكريبت](http://coffeescript.org/) هو "سكر نحوي" لجافا سكريبت. إنه يقدم بناء جمل أقصر ، مما يسمح لنا بكتابة كود أكثر وضوحًا ودقة. عادة ،مطورو روبي يحبونها. - [تايب سكريبت](http://www.typescriptlang.org/) يركز على إضافة "كتابة بيانات صارمة" لتبسيط تطوير ودعم الأنظمة المعقدة. تم تطويره بواسطة ميكروسوفت. - [فلو](http://flow.org/) يضيف أيضًا كتابة البيانات ، ولكن بطريقة مختلفة. تم تطويره بواسطة فايسبوك. @@ -117,9 +170,25 @@ - [كوتلن](https://kotlinlang.org/docs/reference/js-overview.html) هي لغة برمجة حديثة وموجزة وآمنة يمكنها استهداف المتصفح أو نود. هناك أكثر. بالطبع ، حتى لو استخدمنا إحدى اللغات المترجمة ، يجب أن نعرف أيضًا جافا سكريبت لفهم ما نقوم به حقًا. +======= +- [CoffeeScript](https://coffeescript.org/) is "syntactic sugar" for JavaScript. It introduces shorter syntax, allowing us to write clearer and more precise code. Usually, Ruby devs like it. +- [TypeScript](https://www.typescriptlang.org/) is concentrated on adding "strict data typing" to simplify the development and support of complex systems. It is developed by Microsoft. +- [Flow](https://flow.org/) also adds data typing, but in a different way. Developed by Facebook. +- [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps), but also can be transpiled to JavaScript. Developed by Google. +- [Brython](https://brython.info/) is a Python transpiler to JavaScript that enables the writing of applications in pure Python without JavaScript. +- [Kotlin](https://kotlinlang.org/docs/reference/js-overview.html) is a modern, concise and safe programming language that can target the browser or Node. + +There are more. Of course, even if we use one of these transpiled languages, we should also know JavaScript to really understand what we're doing. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## ملخص +<<<<<<< HEAD - تم إنشاء جافا سكريبت في البداية كلغة للمتصفح فقط ، ولكنها تُستخدم الآن في العديد من البيئات الأخرى أيضًا. - تتمتع جافا سكريبت اليوم بمكانة فريدة باعتبارها لغة المتصفح الأكثر استخدامًا مع تكاملها التام مع HTML / CSS. -- هناك العديد من اللغات التي يتم "تحويلها" إلى جافا سكريبت وتوفر ميزات معينة. يوصى بإلقاء نظرة عليهم ، على الأقل لفترة وجيزة ، بعد إتقان جافا سكريبت. \ No newline at end of file +- هناك العديد من اللغات التي يتم "تحويلها" إلى جافا سكريبت وتوفر ميزات معينة. يوصى بإلقاء نظرة عليهم ، على الأقل لفترة وجيزة ، بعد إتقان جافا سكريبت. +======= +- JavaScript was initially created as a browser-only language, but it is now used in many other environments as well. +- Today, JavaScript has a unique position as the most widely-adopted browser language, fully integrated with HTML/CSS. +- There are many languages that get "transpiled" to JavaScript and provide certain features. It is recommended to take a look at them, at least briefly, after mastering JavaScript. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/01-getting-started/2-manuals-specifications/article.md b/1-js/01-getting-started/2-manuals-specifications/article.md index 4310dd262..5fff4873f 100644 --- a/1-js/01-getting-started/2-manuals-specifications/article.md +++ b/1-js/01-getting-started/2-manuals-specifications/article.md @@ -2,7 +2,11 @@ هذا الكتاب هو _دورة تعليمية_. يهدف الى تعليمك اللغه تدريجيا. و لكن إذا كنت علي علم بالأساسيات فسوف تحتاج إلى مصدر آخر. +<<<<<<< HEAD ## الوصف +======= +This book is a *tutorial*. It aims to help you gradually learn the language. But once you're familiar with the basics, you'll need other resources. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 [وصف The ECMA-262](https://www.ecma-international.org/publications/standards/Ecma-262.htm) يحتوي على المعلومات الأكثر عمقاً وتفصيلاً ورسميةً عن جافا سكريبت. وهي تقوم بتعريف اللغة. @@ -10,7 +14,11 @@ نسخة وصف جديدة تُصدر كل عام. فيما بين هذه الإصدارات، آخر مسودة وصف توجد في . +<<<<<<< HEAD لكي تقرأ عن خصائص التطور الحاد الجديد، بما فى ذلك "المعايير التقريبية" (ما يسمي "المرحلة 3")، انظر للمقترحات في . +======= +A new specification version is released every year. Between these releases, the latest specification draft is at . +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 أيضاً، إذا كنت تطور من أجل المتصفح، إذا هناك مواصفات أخرى مشمولة في [الجزء الثاني](info:browser-environment) من الدورة التعليمية. @@ -20,18 +28,31 @@ يمكن أن تجدها في . +<<<<<<< HEAD أيضاً، غالباً ما يكون من الأفضل استخدام البحث عبر الإنترنت بدلاً من ذلك. فقط استخدم "[مصطلح] MDN" للإستعلام، مثال لتبحث عن دالة `parseInt`. +======= + You can find it at . + +Although, it's often best to use an internet search instead. Just use "MDN [term]" in the query, e.g. to search for the `parseInt` function. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## جدول التوافق جافا سكريبت لغة فى سياق متطور، تضاف إليها خصائص جديدة بانتظام. +<<<<<<< HEAD لتَّـحَقّـق من الدعم ما بين المتصفحات والمحركات الأخرى، انظر: - - جدول الدعم لكل خاصية، مثال: لترى أيًا من المحركات يدعم دوال التشفير الحديث: . - - جدول بمواصفات ومحركات اللغة وقابلية دعم كل محرك لكل خاصية. +======= +- - per-feature tables of support, e.g. to see which engines support modern cryptography functions: . +- - a table with language features and engines that support those or don't support. + +All these resources are useful in real-life development, as they contain valuable information about language details, their support, etc. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 كل هذه المصادر مفيدة في تطوير الحياة الواقعية، لأنها تحتوي علي معلومات قيّمة عن تفاصيل ودعم اللغة. diff --git a/1-js/01-getting-started/3-code-editors/article.md b/1-js/01-getting-started/3-code-editors/article.md index 43b7fdba8..5d957d3a5 100644 --- a/1-js/01-getting-started/3-code-editors/article.md +++ b/1-js/01-getting-started/3-code-editors/article.md @@ -12,8 +12,13 @@ إذا لم تقم باختيار IDE بعد، ففكر في الخيارات التالية: +<<<<<<< HEAD - [فيجوال ستديو كود](https://code.visualstudio.com/) (يعمل على أكثر من نظام تشغيل، مجاني). - [ويب ستورم](http://www.jetbrains.com/webstorm/) (يعمل على أكثر من نظام تشغيل، مدفوع). +======= +- [Visual Studio Code](https://code.visualstudio.com/) (cross-platform, free). +- [WebStorm](https://www.jetbrains.com/webstorm/) (cross-platform, paid). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 بالنسبة للويندوز، يمكن استخدام برنامج فيجوال ستديو، لايجب الخلط بينه وبين الفيجوال ستديو كود، فيجوال ستديو هو محرر مدفوع يعمل على نظام ويندوز فقط، ومناسب تماماً لبيئة الـ.NET. أنه أيضاً جيد لجافا سكريبت. كما يوجد إصدار مجاني منه [Visual Studio Community](https://www.visualstudio.com/vs/community/). @@ -35,7 +40,13 @@ - [Notepad++](https://notepad-plus-plus.org/) (الويندوز، مجاني). - [Vim](http://www.vim.org/) و [Emacs](https://www.gnu.org/software/emacs/) رائعة إذا كنت تعرف كيفية استخدامها. +<<<<<<< HEAD ## دعونا لا نتشاجر +======= +- [Sublime Text](http://www.sublimetext.com) (cross-platform, shareware). +- [Notepad++](https://notepad-plus-plus.org/) (Windows, free). +- [Vim](http://www.vim.org/) and [Emacs](https://www.gnu.org/software/emacs/) are also cool if you know how to use them. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 المحررات أعلاه هي التي نقوم باستخدامها أنا وأصدقائي، والذين أعتقد أنهم مطورون جيدون يستخدمونها منذ وقت طويل وتسعدهم. diff --git a/1-js/01-getting-started/4-devtools/article.md b/1-js/01-getting-started/4-devtools/article.md index 3dded1566..73243209f 100644 --- a/1-js/01-getting-started/4-devtools/article.md +++ b/1-js/01-getting-started/4-devtools/article.md @@ -8,7 +8,11 @@ يفضل معظم المطورون العمل على متصفِّحي Chrome أو FireFox لاحتوائهما على أفضل أدوات المطوّر. توفر المتصفِّحات الأخرى أيضًا مجموعة أدوات للمطوّر والتي من الممكن أن تحتوي على مزايا خاصة. لكن عادةً ما تحاول اللحاق بمتصفِّحي Chrome و FireFox الأفضل من هذه الناحية. يفضل المطوّرون بشكل عام العمل على متصفِّح واحد وينتقلون إلى متصفِّح آخر عندما تكون المشكلة التي يعملون عليها محدَّدة بهذا المتصفِّح. +<<<<<<< HEAD بناءً على ذلك، نجد أنَّ أدوات المطور مهمة للغاية لما تمتلكه من مزايا تساعدك أثناء مسيرتك في تطوير الويب عبر JavaScript. سنتعلم في البداية طريقة فتحها، واستخدامها لاستكشاف الأخطاء، وتشغيل تعليمات JavaScript ضمنها. +======= +Developer tools are potent; they have many features. To start, we'll learn how to open them, look at errors, and run JavaScript commands. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 أدوات المطور قوية, لديها العديد من الميزات. للبدء, سنتعلم كيفية فتحها ، والنظر في الأخطاء ، وتشغيل أوامر جافا سكريبت. diff --git a/1-js/02-first-steps/01-hello-world/article.md b/1-js/02-first-steps/01-hello-world/article.md index c414004ce..c980a63c2 100644 --- a/1-js/02-first-steps/01-hello-world/article.md +++ b/1-js/02-first-steps/01-hello-world/article.md @@ -73,7 +73,11 @@ ``` +<<<<<<< HEAD هنا، `/path/to/script.js` هو مسار منفرد للنص البرمجي من جذر الموقع. يمكن أيضاً توفير مسار نسبي من خلال الصفحة الحالية. على سبيل المثال، `src="script.js"` تعني أن الملف `"script.js"` في نفس المجلد. +======= +Here, `/path/to/script.js` is an absolute path to the script from the site root. One can also provide a relative path from the current page. For instance, `src="script.js"`, just like `src="./script.js"`, would mean a file `"script.js"` in the current folder. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يمكن أن نعطي المسار الكامل أيضاً. على سبيل المثال : diff --git a/1-js/02-first-steps/02-structure/article.md b/1-js/02-first-steps/02-structure/article.md index 6285c198d..f7185970c 100644 --- a/1-js/02-first-steps/02-structure/article.md +++ b/1-js/02-first-steps/02-structure/article.md @@ -46,6 +46,7 @@ alert(3+ قريبًا خلال الشيفرات التي ستكتبها). إذا كنت ترغب في الاطلاع على مثال واقعي عن هذه الحالة، إليك الشيفرة البرمجية التالية: +<<<<<<< HEAD ``` [1, 2].foreach(alert) ``` @@ -55,10 +56,36 @@ alert(3+ ``` alert("There will be an error") [1 ,2].forEach(alert) +======= +The code outputs `6` because JavaScript does not insert semicolons here. It is intuitively obvious that if the line ends with a plus `"+"`, then it is an "incomplete expression", so a semicolon there would be incorrect. And in this case, that works as intended. + +**But there are situations where JavaScript "fails" to assume a semicolon where it is really needed.** + +Errors which occur in such cases are quite hard to find and fix. + +````smart header="An example of an error" +If you're curious to see a concrete example of such an error, check this code out: + +```js run +alert("Hello"); + +[1, 2].forEach(alert); +``` + +No need to think about the meaning of the brackets `[]` and `forEach` yet. We'll study them later. For now, just remember the result of running the code: it shows `Hello`, then `1`, then `2`. + +Now let's remove the semicolon after the `alert`: + +```js run no-beautify +alert("Hello") + +[1, 2].forEach(alert); +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` عند تنفيذ الشيفرة البرمجية آنذاك، سيتم إظهار التنبيه الأول فقط ثم سنحصل على خطأ. تعود الشيفرة البرمجية للعمل بشكل صحيح مرة أخرى عند إضافة الفاصلة المنقوطة بعد التنبيه الأول: +<<<<<<< HEAD ``` alert("All fine now"); [1 ,2].forEach(alert) @@ -72,6 +99,18 @@ alert("All fine now"); ``` alert("There will be an error")[1, 2].forEach(alert) +======= +The difference compared to the code above is only one character: the semicolon at the end of the first line is gone. + +If we run this code, only the first `Hello` shows (and there's an error, you may need to open the console to see it). There are no numbers any more. + +That's because JavaScript does not assume a semicolon before square brackets `[...]`. So, the code in the last example is treated as a single statement. + +Here's how the engine sees it: + +```js run no-beautify +alert("Hello")[1, 2].forEach(alert); +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ولكنهما عبارتين برمجيتين منفصلتين وليستا عبارة واحدة، وبالتالي عملية الدمج في هذه الحالة خطأ. من الممكن أن تتكرر هذه الحالة ضمن شروط أخرى. @@ -87,7 +126,24 @@ alert("There will be an error")[1, 2].forEach(alert) المائلين على نفس السطر تعليقًا. ومن الممكن أن يشغل التعليق سطرًا كاملًا أو يأتي التعليق بعد العبارة البرمجية. إليك المثال التالي الذي يشرح ما سبق: +<<<<<<< HEAD ``` +======= +Looks weird, right? Such merging in this case is just wrong. We need to put a semicolon after `alert` for the code to work correctly. + +This can happen in other situations also. +```` + +We recommend putting semicolons between statements even if they are separated by newlines. This rule is widely adopted by the community. Let's note once again -- *it is possible* to leave out semicolons most of the time. But it's safer -- especially for a beginner -- to use them. + +## Comments [#code-comments] + +As time goes on, programs become more and more complex. It becomes necessary to add *comments* which describe what the code does and why. + +Comments can be put into any place of a script. They don't affect its execution because the engine simply ignores them. + +**One-line comments start with two forward slash characters `//`.** +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 // يمتد هذا التعليق على كامل السطر فقط diff --git a/1-js/02-first-steps/04-variables/article.md b/1-js/02-first-steps/04-variables/article.md index 89d21f287..236f66a54 100644 --- a/1-js/02-first-steps/04-variables/article.md +++ b/1-js/02-first-steps/04-variables/article.md @@ -26,7 +26,11 @@ let message; let message; *!* +<<<<<<< HEAD message = 'Hello'; // تخزين النص +======= +message = 'Hello'; // store the string 'Hello' in the variable named message +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 */!* ``` @@ -66,7 +70,12 @@ let age = 25; let message = 'Hello'; ``` +<<<<<<< HEAD بعض الناس ايضا يُعرفون المتغيرات بهذه الطريقه: +======= +Some people also define multiple variables in this multiline style: + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js no-beautify let user = 'John', age = 25, @@ -103,7 +112,12 @@ let user = 'John' نستطيع أن نضع أي قيمة بداخل الصندوق. +<<<<<<< HEAD ونستطيع أيضا تغيير قيمته أكثر من مره كما نريد. +======= +We can also change it as many times as we want: + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let message; @@ -192,8 +206,13 @@ let 1a; // لا نستطيع نبدأ الاسم برقم let my-name; // الواصلات '-' غير مسموح بها في التسمية ``` +<<<<<<< HEAD ```smart header="الملاحظه المهمه" المتغيرات التي تُسمي `apple` و `AppLE` يكونوا متغيريين مختلفين تماما +======= +```smart header="Case matters" +Variables named `apple` and `APPLE` are two different variables. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ````smart header="يُسمح باستخدام الأحرف غير اللاتينية ، ولكن لا يُنصح بها" @@ -262,8 +281,12 @@ myBirthday = '01.01.2001'; // خطأ, لاتستطيع تغيير قيمة ال عندما المبرمج يكون متأكد أن المتغير لن يتغير أبداً, فيجب عليه تعريف المتغير بأستخدام `const` لضمان هذه الحقيقة وإبلاغها بوضوح للجميع. +<<<<<<< HEAD ### ثوابت ذات الحروف الكبيره +======= +### Uppercase constants +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 هناك ممارسة شائعة لاستخدام الثوابت كأسماء مستعارة للقيم التي يصعب تذكرها والمعروفة قبل التنفيذ. @@ -292,14 +315,23 @@ alert(color); // #FF7F00 الثابت يعني أن قيمة المتغير لن تتغير أبداً. ولكن هناك ثوابت معرفة قبل التنفيذ (مثل الرقم السداسي العشري للون الاحمر) وهناك ثوابت *محسوبة* في حالة التشغيل, أثناء التنفيذ, ولكن لا تتغير قيمتها الاولية. +<<<<<<< HEAD علي سبيل المثال: +======= +For instance: + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js const pageLoadTime = /* الوقت اللازم لتحضير صفحة الويب */; ``` قيمة `pageLoadTime` غير معرفه في بداية تحضير الصفحة, لذلك من الطبيعي تسميتها. ولكنها مازالت ثابت لانها لم تتغير بعد التعريف. +<<<<<<< HEAD بمعنى آخر ، تُستخدم الثوابت التي تحمل أسماء كبيرة فقط كأسماء مستعارة لقيم "الثابت الترميز". +======= +In other words, capital-named constants are only used as aliases for "hard-coded" values. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## تسمية الاشياء بشكل صحيح diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index d555a8c24..767b7d424 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -47,13 +47,23 @@ n = 12.345; alert( "not a number" / 2 ); // NaN مثل هذه القسمة خاطئة ``` +<<<<<<< HEAD `NaN` لزجة. أي عملية تتم على `NaN` ترجع `NaN`: +======= + `NaN` is sticky. Any further mathematical operation on `NaN` returns `NaN`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run - alert( "not a number" / 2 + 5 ); // NaN + alert( NaN + 1 ); // NaN + alert( 3 * NaN ); // NaN + alert( "not a number" / 2 - 1 ); // NaN ``` +<<<<<<< HEAD لذلك إذا وجدت `NaN` في أي مكان في تعبير حسابي تنتشر في النتيجة بأكملها. +======= + So, if there's a `NaN` somewhere in a mathematical expression, it propagates to the whole result (there's only one exception to that: `NaN ** 0` is `1`). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```smart header="العمليات الرياضية أمنة" القيام بالرياضيات "أمن" في جافا سكربت. نستطيع القيام بأي شئ: القسمة على صفر و معاملة النصوص الغير الرقمية على أنها أرقام و إلخ. @@ -65,11 +75,28 @@ n = 12.345; سنرى المزيد من التعامل مع الأرقام خلال هذا الفصل . -## BigInt +## BigInt [#bigint-type] +<<<<<<< HEAD في جافا سكريبت، النوع "number" لا يمثل الأعداد الصحيحة أكبر من (253-1) (و هو `9007199254740991`)، أو أقل من -(-253-1) للأرقام السالبة. إنها قيود فنية ناتجة عن تمثيلهم الداخلي. لمعظم الأغراض هذا يكفي، لكن في بعض الأحيان نحتاج لأرقام كبيرة حقاً ، على سبيل المثال. للتشفير أو الطوابع الزمنية الدقيقة للميكرو ثانية. +======= +In JavaScript, the "number" type cannot safely represent integer values larger than (253-1) (that's `9007199254740991`), or less than -(253-1) for negatives. + +To be really precise, the "number" type can store larger integers (up to 1.7976931348623157 * 10308), but outside of the safe integer range ±(253-1) there'll be a precision error, because not all digits fit into the fixed 64-bit storage. So an "approximate" value may be stored. + +For example, these two numbers (right above the safe range) are the same: + +```js +console.log(9007199254740991 + 1); // 9007199254740992 +console.log(9007199254740991 + 2); // 9007199254740992 +``` + +So to say, all odd integers greater than (253-1) can't be stored at all in the "number" type. + +For most purposes ±(253-1) range is quite enough, but sometimes we need the entire range of really big integers, e.g. for cryptography or microsecond-precision timestamps. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 `BigInt` تمت إضافة النوع مؤخرًا إلى اللغة لتمثيل الأعداد الصحيحة ذات الطول الكبير. @@ -213,6 +240,7 @@ alert(age); // "undefined" معامل `typeof` يرجع نوع قيمة المدخلات. إنه مفيد عندما نريد معالجة قيم من أنواع مختلفة بإختلاف أو لمجرد إجراد فحص سريع للنوع . +<<<<<<< HEAD إنه يدعم نوعين من بناء الكود: 1. كمعامل: `typeof x`. @@ -221,6 +249,9 @@ alert(age); // "undefined" بكلمات أخرى ، إنها تعمل بأقواس أو بدون أقواس. النتيجة ستكون واحدة. إستدعاء `typeof x` يرجع نص بإسم نوع القيمة: +======= +A call to `typeof x` returns a string with the type name: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js typeof undefined // "undefined" @@ -250,14 +281,33 @@ typeof alert // "function" (3) الثلاث سطور الأخيرة قد تحتاج لتوضيح إضافي: +<<<<<<< HEAD 1. `Math` كائن مدمج داخلياً لتدعيم العمليات الرياضية. سنتعلمه في الفصل . هنا، يخدم فقط كمثال للكائن. 2. نتيجة `typeof null` هي `"object"`. هذا رسمياً يعتبر خطأ في سلوك `typeof` ، يأتي من الأيام الأولى لجافا سكربت وتم الحفاظ عليه من أجل التوافقية. قطعاً `null` ليس كائن. إنه قيمة خاصة بنوع منفصل خاص. 3. نتيجة `typeof alert` هي `"function"`، لأن `alert` دالة. سندرس الدوال في الفصول القادمة وهناك سنرى أنه لا توجد نوع خاص "دالة" في جافا سكربت. الدوال الدوال تنتمي للنوع كائن. لكن `typeof` تعاملهم بشكل مختلف، يرجع `"دالة"`. هذا أيضاً يأتي من الأيام الأولى لجافا سكربت. فنياً، مثل هذا السلوك غير صحيح، لكن قد يكون ملائم في الممارسة. ## خلاصة +======= +1. `Math` is a built-in object that provides mathematical operations. We will learn it in the chapter . Here, it serves just as an example of an object. +2. The result of `typeof null` is `"object"`. That's an officially recognized error in `typeof`, coming from very early days of JavaScript and kept for compatibility. Definitely, `null` is not an object. It is a special value with a separate type of its own. The behavior of `typeof` is wrong here. +3. The result of `typeof alert` is `"function"`, because `alert` is a function. We'll study functions in the next chapters where we'll also see that there's no special "function" type in JavaScript. Functions belong to the object type. But `typeof` treats them differently, returning `"function"`. That also comes from the early days of JavaScript. Technically, such behavior isn't correct, but can be convenient in practice. + +```smart header="The `typeof(x)` syntax" +You may also come across another syntax: `typeof(x)`. It's the same as `typeof x`. + +To put it clear: `typeof` is an operator, not a function. The parentheses here aren't a part of `typeof`. It's the kind of parentheses used for mathematical grouping. + +Usually, such parentheses contain a mathematical expression, such as `(2 + 2)`, but here they contain only one argument `(x)`. Syntactically, they allow to avoid a space between the `typeof` operator and its argument, and some people like it. + +Some people prefer `typeof(x)`, although the `typeof x` syntax is much more common. +``` + +## Summary +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يوجد 8 أنواع للبيانات في جافا سكربت. +<<<<<<< HEAD - `number` للأرقام من أي نوع: صحيح أو عشري، الأعداد الصحيحة محدودة ب ±(253-1). - `bigint` هو عدد صحيح طوله كبير. - `string` للنصوص. النص قد يحتوي على صفر حرف أو أكثر، لا يوجد نوع منفصل للحرف الواحد. @@ -266,11 +316,29 @@ typeof alert // "function" (3) - `undefined` للقيم غير المعينة -- نوع قائم بذاته له قيمة واحدة فقط `undefined`. - `object` من أجل هياكل بيانات معقدة. - `symbol` من أجل معرفات فريدة. +======= +- Seven primitive data types: + - `number` for numbers of any kind: integer or floating-point, integers are limited by ±(253-1). + - `bigint` for integer numbers of arbitrary length. + - `string` for strings. A string may have zero or more characters, there's no separate single-character type. + - `boolean` for `true`/`false`. + - `null` for unknown values -- a standalone type that has a single value `null`. + - `undefined` for unassigned values -- a standalone type that has a single value `undefined`. + - `symbol` for unique identifiers. +- And one non-primitive data type: + - `object` for more complex data structures. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 معامل `typeof` يسمح لنا بمعرفة نوع البيانات الموجودة بداخل المتغيرة. +<<<<<<< HEAD - له شكلان: `typeof x` أو `typeof(x)`. - يرجع نص بإسم نوع البيانات، مثل `"string"`. - من أجل `null` يرجع `"object"` - هذا خطأ في اللغة، إنه ليس في الحقيقة كائن. +======= +- Usually used as `typeof x`, but `typeof(x)` is also possible. +- Returns a string with the name of the type, like `"string"`. +- For `null` returns `"object"` -- this is an error in the language, it's not actually an object. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 في الفصول القادمة سنركز على القيم البدائية وعندما نكون متألفين معاهم، سنتجه للكائنات. diff --git a/1-js/02-first-steps/07-type-conversions/article.md b/1-js/02-first-steps/07-type-conversions/article.md index 1e0807856..afbfa3d68 100644 --- a/1-js/02-first-steps/07-type-conversions/article.md +++ b/1-js/02-first-steps/07-type-conversions/article.md @@ -7,7 +7,11 @@ هناك أيضاً حالات نحتاج إلى تصريح تحويل القيمة إلى النوع المطلوب. ```smart header="Not talking about objects yet" +<<<<<<< HEAD في هذا الفصل، لن نغطي الكائنات. الآن سوف نتحدث عن الأنواع الأساسية. +======= +In this chapter, we won't cover objects. For now, we'll just be talking about primitives. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 فيما بعد، بعد أن نتعلم عن الكائنات، في هذا الفصل سنرى كيف تتلائم الكائنات فيه. ``` @@ -70,7 +74,11 @@ alert(age); // NaN، التحويل فشل |`undefined`|`NaN`| |`null`|`0`| |true and false | `1` and `0` | +<<<<<<< HEAD | `string` | المساحات البيضاء في البداية والنهاية يتم إزالتها. لو باقي النص فارغ، النتيجة هي `0`. غير ذلك، الرقم "يتم قرائته" من النص. أي خطأ يعطي`NaN`. | +======= +| `string` | Whitespaces (includes spaces, tabs `\t`, newlines `\n` etc.) from the start and end are removed. If the remaining string is empty, the result is `0`. Otherwise, the number is "read" from the string. An error gives `NaN`. | +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 أمثلة: @@ -130,7 +138,11 @@ alert( Boolean(" ") ); // المسافات، أيضاً true (أي نص غير |`undefined`|`NaN`| |`null`|`0`| |true / false | `1 / 0` | +<<<<<<< HEAD | `string` | يتم قراءة النص "كما هو"،المسافات البيضاء من الجانبين يتم تجاهلها. النص الفارغ يصبح `0`. الخطأ `NaN`. | +======= +| `string` | The string is read "as is", whitespaces (includes spaces, tabs `\t`, newlines `\n` etc.) from both sides are ignored. An empty string becomes `0`. An error gives `NaN`. | +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 **`التحويلات المنطقية`** -- يحدث في المعاملات المنطقية. يتم تنفيذه عن طريق `Boolean(value)`. diff --git a/1-js/02-first-steps/08-operators/article.md b/1-js/02-first-steps/08-operators/article.md index f3b7469c2..bf7980d6b 100644 --- a/1-js/02-first-steps/08-operators/article.md +++ b/1-js/02-first-steps/08-operators/article.md @@ -56,18 +56,30 @@ alert( 8 % 3 ); // 2, a remainder of 8 divided by 3 ### الضرب الأسي ** +<<<<<<< HEAD عامل الضرب الأسي `a ** b` يقوم بضرب الرقم `a` في نفسه عدد `b` من المرات +======= +The exponentiation operator `a ** b` raises `a` to the power of `b`. + +In school maths, we write that as ab. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال: ```js run -alert( 2 ** 2 ); // 4 (2 multiplied by itself 2 times) -alert( 2 ** 3 ); // 8 (2 * 2 * 2, 3 times) -alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2, 4 times) +alert( 2 ** 2 ); // 2² = 4 +alert( 2 ** 3 ); // 2³ = 8 +alert( 2 ** 4 ); // 2⁴ = 16 ``` +<<<<<<< HEAD من الناحية الرياضياتية الضرب الأسي يستخدم أيضا مع الأرقام غير الصحيحة. على سبيل المثال الجذر التربيعي هو ضرب أسي بقيمة `1/2`: +======= +Just like in maths, the exponentiation operator is defined for non-integer numbers as well. + +For example, a square root is an exponentiation by ½: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root) @@ -198,6 +210,7 @@ alert( +apples + +oranges ); // 5 | الأسبقية | الاسم | تسجيل | | ------------ | ------ | ------ | | ... | ... | ... | +<<<<<<< HEAD | 17 | أحادي زائد | `+` | | 17 | نفي أحادي | `-` | | 16 | الأسي | `**` | @@ -210,10 +223,28 @@ alert( +apples + +oranges ); // 5 | ... | ... | ... | كما نرى ، فإن "unary plus" لها أولوية "17" وهي أعلى من "13" لـ "add" (ثنائي زائد). لهذا السبب ، في تعبير "+ apples + + oranges" ، تعمل الإيجابيات الأحادية قبل الإضافة. +======= +| 14 | unary plus | `+` | +| 14 | unary negation | `-` | +| 13 | exponentiation | `**` | +| 12 | multiplication | `*` | +| 12 | division | `/` | +| 11 | addition | `+` | +| 11 | subtraction | `-` | +| ... | ... | ... | +| 2 | assignment | `=` | +| ... | ... | ... | + +As we can see, the "unary plus" has a priority of `14` which is higher than the `11` of "addition" (binary plus). That's why, in the expression `"+apples + +oranges"`, unary pluses work before the addition. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## التعيين +<<<<<<< HEAD دعنا نلاحظ أن المهمة `=` هي أيضًا عامل. وهي مدرجة في جدول الأسبقية بأولوية منخفضة جدًا لـ `3`. +======= +Let's note that an assignment `=` is also an operator. It is listed in the precedence table with the very low priority of `2`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لهذا السبب ، عندما نقوم بتعيين متغير ، مثل `x = 2 * 2 + 1` ، تتم الحسابات أولاً ثم يتم تقييم` = `، وتخزين النتيجة في` x`. @@ -309,9 +340,9 @@ alert( n ); // 14 ```js run let n = 2; -n *= 3 + 5; +n *= 3 + 5; // right part evaluated first, same as n *= 8 -alert( n ); // 16 (right part evaluated first, same as n *= 8) +alert( n ); // 16 ``` ## الزيادة / النقصان @@ -442,7 +473,13 @@ counter++; - RIGHT SHIFT ( `>>` ) - ZERO-FILL RIGHT SHIFT ( `>>>` ) +<<<<<<< HEAD نادرًا ما يتم استخدام عوامل التشغيل هذه ، عندما نحتاج إلى التلاعب بالأرقام على أدنى مستوى (أحادي البتات). لن نحتاج إلى هؤلاء المشغلين في أي وقت قريب ، لأن تطوير الويب لا يستخدمهم كثيرًا ، ولكن في بعض المجالات الخاصة ، مثل التشفير ، فهي مفيدة. يمكنك قراءة مقالة [عوامل تشغيل Bitwise] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise) مقالة حول MDN عند الحاجة. +======= +These operators are used very rarely, when we need to fiddle with numbers on the very lowest (bitwise) level. We won't need these operators any time soon, as web development has little use of them, but in some special areas, such as cryptography, they are useful. You can read the [Bitwise Operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#bitwise_operators) chapter on MDN when a need arises. + +## Comma +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## الفاصلة diff --git a/1-js/02-first-steps/09-comparison/article.md b/1-js/02-first-steps/09-comparison/article.md index 3dba72cc5..520995623 100644 --- a/1-js/02-first-steps/09-comparison/article.md +++ b/1-js/02-first-steps/09-comparison/article.md @@ -4,10 +4,17 @@ : وفي لغة الجافسكريبت تكتب كما يلي +<<<<<<< HEAD - أكبر/أصغر من: a > b, a < b. - أكبر/أصغر من او يساوي: a >= b, a <= b. - يساوي: `a == b` ، يرجى ملاحظة أن علامة المساواة المزدوجة` = `تعني اختبار المساواة ، في حين أن كلمة واحدة` a = b` تعني تعيين أو مساواة . - لا تساوي. في الرياضيات يكون الترميز ،لكن في JavaScript تكتب هكذا a != b. +======= +- Greater/less than: a > b, a < b. +- Greater/less than or equals: a >= b, a <= b. +- Equals: `a == b`, please note the double equality sign `==` means the equality test, while a single one `a = b` means an assignment. +- Not equals: In maths the notation is , but in JavaScript it's written as a != b. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 في هذه المقالة سنتعلم المزيد عن الأنواع المختلفة من المقارنات ، وكيف تجعلها JavaScript، بما في ذلك الخصائص المهمة. diff --git a/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md b/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md index 1983d4b2f..7fef5add7 100644 --- a/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md +++ b/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md @@ -1,6 +1,6 @@ الإجابة: `null` لأنها أول قيمة falsy في القائمة. ```js run -alert( 1 && null && 2 ); +alert(1 && null && 2); ``` diff --git a/1-js/02-first-steps/11-logical-operators/article.md b/1-js/02-first-steps/11-logical-operators/article.md index 422fd59da..56c054828 100644 --- a/1-js/02-first-steps/11-logical-operators/article.md +++ b/1-js/02-first-steps/11-logical-operators/article.md @@ -124,7 +124,11 @@ alert(undefined || null || 0); // 0 (الكل falsy, ترجع آخر قيمة) ويعني أن `||` ينفذ العمليات الممررة له حتى أول قيمة truthy وبعد ذلك يتم إرجاع القيمة مباشرة بدون لمس باقي العمليات. +<<<<<<< HEAD أهمية هذه الميزة تصبح ملحوظة إذا كانت القيمة الممررة عبارة عن عملية لها آثار جانبية مثل تخصيص قيمة متغير أو استدعاء دالة +======= + The importance of this feature becomes obvious if an operand isn't just a value, but an expression with a side effect, such as a variable assignment or a function call. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 في هذا المثال سيتم طباعة الرسالة الأولى ولن يتم طباعة الثانية: diff --git a/1-js/02-first-steps/12-nullish-coalescing-operator/article.md b/1-js/02-first-steps/12-nullish-coalescing-operator/article.md index 644553c54..0206c3aa2 100644 --- a/1-js/02-first-steps/12-nullish-coalescing-operator/article.md +++ b/1-js/02-first-steps/12-nullish-coalescing-operator/article.md @@ -4,7 +4,7 @@ The nullish coalescing operator is written as two question marks `??`. -As it treats `null` and `undefined` similarly, we'll use a special term here, in this article. We'll say that an expression is "defined" when it's neither `null` nor `undefined`. +As it treats `null` and `undefined` similarly, we'll use a special term here, in this article. For brevity, we'll say that a value is "defined" when it's neither `null` nor `undefined`. The result of `a ?? b` is: - if `a` is defined, then `a`, @@ -22,9 +22,9 @@ result = (a !== null && a !== undefined) ? a : b; Now it should be absolutely clear what `??` does. Let's see where it helps. -The common use case for `??` is to provide a default value for a potentially undefined variable. +The common use case for `??` is to provide a default value. -For example, here we show `user` if defined, otherwise `Anonymous`: +For example, here we show `user` if its value isn't `null/undefined`, otherwise `Anonymous`: ```js run let user; @@ -42,9 +42,9 @@ alert(user ?? "Anonymous"); // John (user defined) We can also use a sequence of `??` to select the first value from a list that isn't `null/undefined`. -Let's say we have a user's data in variables `firstName`, `lastName` or `nickName`. All of them may be not defined, if the user decided not to enter a value. +Let's say we have a user's data in variables `firstName`, `lastName` or `nickName`. All of them may be not defined, if the user decided not to fill in the corresponding values. -We'd like to display the user name using one of these variables, or show "Anonymous" if all of them aren't defined. +We'd like to display the user name using one of these variables, or show "Anonymous" if all of them are `null/undefined`. Let's use the `??` operator for that: @@ -108,11 +108,11 @@ In practice, the zero height is often a valid value, that shouldn't be replaced ## الأولوية -The precedence of the `??` operator is about the same as `||`, just a bit lower. It equals `5` in the [MDN table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table), while `||` is `6`. +The precedence of the `??` operator is the same as `||`. They both equal `3` in the [MDN table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table). That means that, just like `||`, the nullish coalescing operator `??` is evaluated before `=` and `?`, but after most other operations, such as `+`, `*`. -So if we'd like to choose a value with `??` in an expression with other operators, consider adding parentheses: +So we may need to add parentheses in expressions like this: ```js run let height = null; @@ -130,7 +130,7 @@ Otherwise, if we omit parentheses, then as `*` has the higher precedence than `? // without parentheses let area = height ?? 100 * width ?? 50; -// ...works the same as this (probably not what we want): +// ...works this way (not what we want): let area = height ?? (100 * width) ?? 50; ``` diff --git a/1-js/02-first-steps/13-while-for/article.md b/1-js/02-first-steps/13-while-for/article.md index 3dd3a9db0..5fde3bf69 100644 --- a/1-js/02-first-steps/13-while-for/article.md +++ b/1-js/02-first-steps/13-while-for/article.md @@ -6,7 +6,24 @@ _الحلقات التكرارية_ هي طريقة لتكرار الأوامر. +<<<<<<< HEAD ## حلقة "while" +======= +```smart header="The for..of and for..in loops" +A small announcement for advanced readers. + +This article covers only basic loops: `while`, `do..while` and `for(..;..;..)`. + +If you came to this article searching for other types of loops, here are the pointers: + +- See [for..in](info:object#forin) to loop over object properties. +- See [for..of](info:array#loops) and [iterables](info:iterable) for looping over arrays and iterable objects. + +Otherwise, please read on. +``` + +## The "while" loop +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 إن حلقة `while` تكتب بالطريقة التالية: @@ -106,12 +123,21 @@ for (let i = 0; i < 3; i++) { لنشرح `for` جزء بجزء: +<<<<<<< HEAD | الجزء | | | | --------- | ---------- | ----------------------------------------------------------- | | begin | `i = 0` | ينفذ مرة واحدة فقط في البداية. | | condition | `i < 3` | يتم اختباره قبل كل عملية تكرار وإذا لم يتحقق يتوقف التكرار. | | body | `alert(i)` | تنفذ طالما الشرط محقق. | | step | `i++` | ينفذ بعد body في كل عملية تكرار. | +======= +| part | | | +|-------|----------|----------------------------------------------------------------------------| +| begin | `let i = 0` | Executes once upon entering the loop. | +| condition | `i < 3`| Checked before every loop iteration. If false, the loop stops. | +| body | `alert(i)`| Runs again and again while the condition is truthy. | +| step| `i++` | Executes after the body on each iteration. | +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 الخوارزمية العامة للتكرار تعمل كالتالي: @@ -173,12 +199,17 @@ for (i = 0; i < 3; i++) { // استخدام متغير معرف مسبقًا alert(i); // 3, يمكن التعامل معه لأنه معرف خارج الحلقة ``` - ```` +<<<<<<< HEAD ### أجزاء يمكن تخطيها أي جزء من `for` يمكن الاستغناء عنه. +======= +### Skipping parts + +Any part of `for` can be skipped. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 مثلًا إذا حذفنا `begin` لن يكون لدينا ما نفعله في بداية الحلقة. @@ -277,9 +308,14 @@ for (let i = 0; i < 10; i++) { هذا الكود مطابق تمامًا للسابق. يمكننا فقط وضع الكود داخل `if` بدلًا من استخدام `continue`. +<<<<<<< HEAD ولكن هذا ينتج مستوى آخر من التداخل (استدعاء `alert` داخل أقواس معقوفة). إذا كان ما بداخل `if` سطور كثيرة فهذا سيقلل من إمكانية قراءة الكود بوضوح. ````` +======= +But as a side effect, this created one more level of nesting (the `alert` call inside the curly braces). If the code inside of `if` is longer than a few lines, that may decrease the overall readability. +```` +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ````warn header="لا يمكن استخدام `break/continue` في الجانب الأيمن من '?'" لا يمكن استخدام هذه التعبيرات `break/continue` مع العامل الشرطي `?`. @@ -296,7 +332,6 @@ if (i > 5) { ...وكتبناه باستخدام علامة الاستفهام: - ```js no-beautify (i > 5) ? alert(i) : *!*continue*/!*; // continue لا يسمح باستخدامها هنا ``` @@ -330,6 +365,11 @@ alert("Done!"); إن _label_ يقوم بتعريف الحلقة باستخدام نقطتين قبلها: +<<<<<<< HEAD +======= +A *label* is an identifier with a colon before a loop: + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js labelName: for (...) { ... @@ -351,6 +391,7 @@ labelName: for (...) { // أفعل شئ ما بالقيمة... } } + alert('Done!'); ``` @@ -370,16 +411,37 @@ for (let i = 0; i < 3; i++) { ... } ````warn header="Labels لا تستخدم للإنتقال إلى أي مكان" Labels لا تسمح لنا بالإنتقال إلى أي مكان داخل الكود. +<<<<<<< HEAD فعلى سبيل المثال لا يمكننا فعل التالي: +======= +For example, it is impossible to do this: + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js break label; // تنتقل إلى الحقل بالأسفل (لا تعمل) label: for (...) ``` +<<<<<<< HEAD إستخدام `continue` يكون ممكنًا فقط من داخل الحلقه. `break` ربما يمكن وضعه قبل الشيفرة ايضًا, as `label: { ... }`, لكنها لا تستخدم أبدًا بهذه الطريقة. وهي تعمل أيضًا من الداخل إلى الخارج فقط. +======= +A `break` directive must be inside a code block. Technically, any labelled code block will do, e.g.: + +```js +label: { + // ... + break label; // works + // ... +} +``` + +...Although, 99.9% of the time `break` is used inside loops, as we've seen in the examples above. + +A `continue` is only possible from inside a loop. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```` ## ملخص diff --git a/1-js/02-first-steps/14-switch/article.md b/1-js/02-first-steps/14-switch/article.md index 1d63ee61d..5a24aca1e 100644 --- a/1-js/02-first-steps/14-switch/article.md +++ b/1-js/02-first-steps/14-switch/article.md @@ -139,7 +139,11 @@ switch (a) { الآن كل من `3` و `5` يظهرون الرسالة. +<<<<<<< HEAD إمكانية تجميع الحالات هي تأثير جانبي لطريقة عمل `switch/case` بدون `break`. هنا يبدأ التنفيذ من `case 3` في السطر `(*)` وينتقل إلى `case 5` لعدم وجود `break`. +======= +The ability to "group" cases is a side effect of how `switch/case` works without `break`. Here the execution of `case 3` starts from the line `(*)` and goes through `case 5`, because there's no `break`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## الأنواع مهمة diff --git a/1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md b/1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md index 033be1578..88f78f434 100644 --- a/1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md +++ b/1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md @@ -1 +1,7 @@ -لا اختلاف. \ No newline at end of file +<<<<<<< HEAD +لا اختلاف. +======= +No difference! + +In both cases, `return confirm('Did parents allow you?')` executes exactly when the `if` condition is falsy. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md b/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md index 191d03c97..4bd636069 100644 --- a/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md +++ b/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md @@ -14,4 +14,8 @@ function checkAge(age) { } ``` +<<<<<<< HEAD لاحظ أن الأقواس حول `age > 18` غير مطلوبة ولكن تم وضعها لزيادة القدرة على قراءة الكود. +======= +Note that the parentheses around `age > 18` are not required here. They exist for better readability. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/02-first-steps/15-function-basics/article.md b/1-js/02-first-steps/15-function-basics/article.md index 0c7b690e3..f43809953 100644 --- a/1-js/02-first-steps/15-function-basics/article.md +++ b/1-js/02-first-steps/15-function-basics/article.md @@ -20,11 +20,15 @@ function showMessage() { } ``` +<<<<<<< HEAD كلمة `function` تكتب أولا ثم يكتب _اسم الدالة_ ثم قائمة _parameters_ بين القوسين (يفصل بينهم بفاصلة وهي فارغة في المثال السابق) وأخيرا الكود الذي ينفذ ويسمى "the function body" بين القوسين المعقوفين. +======= +The `function` keyword goes first, then goes the *name of the function*, then a list of *parameters* between the parentheses (comma-separated, empty in the example above, we'll see examples later) and finally the code of the function, also named "the function body", between curly braces. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js -function name(parameters) { - ...body... +function name(parameter1, parameter2, ... parameterN) { + // body } ``` @@ -137,25 +141,30 @@ alert( userName ); // *!*John*/!*, لم يتغير, الدالة لن تصل ل ## Parameters +<<<<<<< HEAD يمكننا تمرير أي قيم إلى الدالة باستخدام parameters (أيضًا تسمى _function arguments_) . +======= +We can pass arbitrary data to functions using parameters. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 في هذا المثال الدالة لديها معاملين: `from` و `text`. ```js run -function showMessage(*!*from, text*/!*) { // arguments: from, text +function showMessage(*!*from, text*/!*) { // parameters: from, text alert(from + ': ' + text); } -*!* -showMessage('Ann', 'Hello!'); // Ann: Hello! (*) -showMessage('Ann', "What's up?"); // Ann: What's up? (**) -*/!* +*!*showMessage('Ann', 'Hello!');*/!* // Ann: Hello! (*) +*!*showMessage('Ann', "What's up?");*/!* // Ann: What's up? (**) ``` عند استدعاء الدالة في السطر `(*)` و `(**)` فإن القيم الممررة تنسخ إلى المتغيرات المحلية `from` و `text`. ثم تقوم الدالة باستخدامهم. +<<<<<<< HEAD هنا مثال آخر حيث لدينا المتغير `from` وقمنا بتمريره إلى الدالة. لاحظ أن الدالة قامت بتغيير قيمة `from` ولكن التغيير لا يؤثر في المتغير الممرر لأن الدالة تحصل على نسخة من القيمة: +======= +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run function showMessage(from, text) { @@ -174,9 +183,27 @@ showMessage(from, "Hello"); // *Ann*: Hello alert( from ); // Ann ``` +<<<<<<< HEAD ## القيم الإفتراضية إذا لم يتم تمرير قيمة Parameter يأخذ القيمة `undefined`. +======= +When a value is passed as a function parameter, it's also called an *argument*. + +In other words, to put these terms straight: + +- A parameter is the variable listed inside the parentheses in the function declaration (it's a declaration time term). +- An argument is the value that is passed to the function when it is called (it's a call time term). + +We declare functions listing their parameters, then call them passing arguments. + +In the example above, one might say: "the function `showMessage` is declared with two parameters, then called with two arguments: `from` and `"Hello"`". + + +## Default values + +If a function is called, but an argument is not provided, then the corresponding value becomes `undefined`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال الفنكشن السابقة `showMessage(from, text)` يمكن استدعائها وتمرير قيمة واحدة فقط: @@ -184,9 +211,15 @@ alert( from ); // Ann showMessage("Ann"); ``` +<<<<<<< HEAD هذا ليس خطأ ولكن سينتج `"Ann: undefined"`. لم يتم تمرير `text` لذلك يتم افتراض أن `text === undefined`. إذا أردت تخصيص قيمة إفتراضية ل `text` يمكن وضعها بعد `=`: +======= +That's not an error. Such a call would output `"*Ann*: undefined"`. As the value for `text` isn't passed, it becomes `undefined`. + +We can specify the so-called "default" (to use if omitted) value for a parameter in the function declaration, using `=`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run function showMessage(from, *!*text = "no text given"*/!*) { @@ -196,7 +229,17 @@ function showMessage(from, *!*text = "no text given"*/!*) { showMessage("Ann"); // Ann: no text given ``` +<<<<<<< HEAD إذا لم يتم تمرير قيمة `text` سيتم إعطائه القيمة `"no text given"` +======= +Now if the `text` parameter is not passed, it will get the value `"no text given"`. + +The default value also jumps in if the parameter exists, but strictly equals `undefined`, like this: + +```js +showMessage("Ann", undefined); // Ann: no text given +``` +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 هنا استخدمنا النص `"no text given"` ولكن يمكن أن تكون القيمة معقدة أكثر من: @@ -210,19 +253,65 @@ function showMessage(from, text = anotherFunction()) { ```smart header="تنفيذ القيم الإفتراضية" في جافا سكريبت يتم تنفيذ القيم الإفتراضية في كل مرة يتم استدعاء الدالة دون تمرير قيمة. +<<<<<<< HEAD في المثال السابق سيتم تنفيذ `anotherFunction()` في كل مرة يتم استدعا `showMessage()` دون تمرير قيمة `text`. +======= +In the example above, `anotherFunction()` isn't called at all, if the `text` parameter is provided. + +On the other hand, it's independently called every time when `text` is missing. +``` + +````smart header="Default parameters in old JavaScript code" +Several years ago, JavaScript didn't support the syntax for default parameters. So people used other ways to specify them. + +Nowadays, we can come across them in old scripts. + +For example, an explicit check for `undefined`: + +```js +function showMessage(from, text) { +*!* + if (text === undefined) { + text = 'no text given'; + } +*/!* + + alert( from + ": " + text ); +} +``` + +...Or using the `||` operator: + +```js +function showMessage(from, text) { + // If the value of text is falsy, assign the default value + // this assumes that text == "" is the same as no text at all + text = text || 'no text given'; + ... +} +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` +```` + ### بديل القيم الإفتراضية +<<<<<<< HEAD أحيانا نريدتحديد قيمة لإفتراضية ولكن ليس في تعريف الدالة بل في وقت لاحق أثناء التنفيذ. لمعرفة المتغير الذي لم يمرر قيمته يمكننا مقارنته مع `undefined`: +======= +Sometimes it makes sense to assign default values for parameters at a later stage after the function declaration. + +We can check if the parameter is passed during the function execution, by comparing it with `undefined`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run function showMessage(text) { + // ... + *!* - if (text === undefined) { + if (text === undefined) { // if the parameter is missing text = 'empty message'; } */!* @@ -236,19 +325,32 @@ showMessage(); // empty message ...أو نستخدم العامل `||`: ```js +<<<<<<< HEAD // إذا لم يتم تمرير قيمة text أو تم تمرير "" يجعل قيمته 'empty' +======= +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 function showMessage(text) { + // if text is undefined or otherwise falsy, set it to 'empty' text = text || 'empty'; ... } ``` +<<<<<<< HEAD محركات جافا سكريبت الحديثة تدعم [nullish coalescing operator](info:nullish-coalescing-operator) `??`وهوأفضل في التعامل مع falsy values مثل `0`: ```js run // إذا لم يوجد قيمة "count" يعرض "unknown" function showCount(count) { alert(count ?? "unknown"); +======= +Modern JavaScript engines support the [nullish coalescing operator](info:nullish-coalescing-operator) `??`, it's better when most falsy values, such as `0`, should be considered "normal": + +```js run +function showCount(count) { + // if count is undefined or null, show "unknown" + alert(count ?? "unknown"); +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 } showCount(0); // 0 @@ -413,7 +515,11 @@ checkPermission(..) // checks a permission, returns true/false على سبيل المثال مكتبة [jQuery](http://jquery.com) تعرف دالة اسمها `$`. ومكتبة [Lodash](http://lodash.com/) لديها دالة اسمها `_`. +<<<<<<< HEAD هذه مجرد استثناءات ففي العموم يجب أن يكون اسم الدالة معبرًا. +======= +These are exceptions. Generally function names should be concise and descriptive. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ## الدوال == تعليقات @@ -478,7 +584,11 @@ function name(parameters, delimited, by, comma) { لجعل الكود أفضل وأسهل ينصح باستخدام المتغيرات المحلية وتجبن استخدام المتغيرات الخارجية. +<<<<<<< HEAD من السهل فهم الدوال التي تحصل على قيم وتعمل عليها وترجع نتيجة أكثر من الدوال التي تعمل على متغيرات خارجها وتعدل عليهم. +======= +It is always easier to understand a function which gets parameters, works with them and returns a result than a function which gets no parameters, but modifies outer variables as a side effect. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 تسمية الدوال: diff --git a/1-js/02-first-steps/16-function-expressions/article.md b/1-js/02-first-steps/16-function-expressions/article.md index f546a28ad..c39dd69e4 100644 --- a/1-js/02-first-steps/16-function-expressions/article.md +++ b/1-js/02-first-steps/16-function-expressions/article.md @@ -12,7 +12,13 @@ function sayHi() { هناك طريقة أخرى لعمل دالة وتسمى _Function Expression_. +<<<<<<< HEAD كالتالي: +======= +It allows us to create a new function in the middle of any expression. + +For example: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js let sayHi = function () { @@ -20,9 +26,25 @@ let sayHi = function () { }; ``` +<<<<<<< HEAD هنا تم عمل الدالة وتخزينها في متغير مثل أي قيمة أخرى ولا يهم كيف تم تعريفها. هي فقط تخزن في متغير اسمه `sayHi`. معنى هذا الكود كالآتي: "إنشئ دالةوضعها في المتغير `sayHi`". +======= +Here we can see a variable `sayHi` getting a value, the new function, created as `function() { alert("Hello"); }`. + +As the function creation happens in the context of the assignment expression (to the right side of `=`), this is a *Function Expression*. + +Please note, there's no name after the `function` keyword. Omitting a name is allowed for Function Expressions. + +Here we immediately assign it to the variable, so the meaning of these code samples is the same: "create a function and put it into the variable `sayHi`". + +In more advanced situations, that we'll come across later, a function may be created and immediately called or scheduled for a later execution, not stored anywhere, thus remaining anonymous. + +## Function is a value + +Let's reiterate: no matter how the function is created, a function is a value. Both examples above store a function in the `sayHi` variable. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يمكننا حتى طباعة هذه القيمة باستخدام `alert`: @@ -64,11 +86,19 @@ sayHi(); // Hello // this still works too (why wouldn't it) 2. السطر `(2)` ينسخها إلى متغير اسمه `func`. لاحظ عدم وجود أقواس بعد `sayHi`. إذا وجدت الأقواس `func = sayHi()` سيتم وضع نتيجة تنفيذ `sayHi()` داخل `func` وليس الدالة `sayHi` نفسها. 3. الآن يمكننا استدعاء الدالة عن طريق `sayHi()` أو `func()`. +<<<<<<< HEAD لاحظ أنه يمكننا استخدام Function Expression لتعريف `sayHi` في السطر الأول: ```js let sayHi = function () { alert("Hello"); +======= +We could also have used a Function Expression to declare `sayHi`, in the first line: + +```js +let sayHi = function() { // (1) create + alert( "Hello" ); +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 }; let func = sayHi; @@ -77,8 +107,13 @@ let func = sayHi; كل شئ يعمل بنفس الطريقة. +<<<<<<< HEAD ````smart header="لماذا يوجد فاصلة منقوطة في النهاية ?" ربما تتسائل لماذا يوجد فاصلة منقوطة في نهاية Function Expression ولا يوجد مع Function Declaration: +======= +````smart header="Why is there a semicolon at the end?" +You might wonder, why do Function Expressions have a semicolon `;` at the end, but Function Declarations do not: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js function sayHi() { @@ -90,9 +125,15 @@ let sayHi = function() { }*!*;*/!* ``` +<<<<<<< HEAD الإجابة بسيطة: - لا حاجة للفاصلة المنقوطة `;` في نهاية code blocks والهياكل المشابهة مثل `if { ... }`, `for { }`, `function f { }` الخ. - يتم استخدام Function Expression داخل التعبير: `let sayHi = ...;` كقيمة وليس code block. يفضل استخدام الفاصلة المنقوطة `;` في نهاية التعبيرات مهما كانت القيمة. لذلك فالفاصلة المنقوطة هنا لا تخص Function Expression نفسه ولكنها فقط تنهي التعبير. +======= +The answer is simple: a Function Expression is created here as `function(…) {…}` inside the assignment statement: `let sayHi = …;`. The semicolon `;` is recommended at the end of the statement, it's not a part of the function syntax. + +The semicolon would be there for a simpler assignment, such as `let sayHi = 5;`, and it's also there for a function assignment. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```` ## Callback functions @@ -132,13 +173,21 @@ function showCancel() { ask("Do you agree?", showOk, showCancel); ``` +<<<<<<< HEAD هذه الدوال مفيدة إلى حد ما. الفرق الأساسي بين `ask` في الواقع والمثال السابق هو أن في الواقع يتم استخدام طرق أكثر تعقيدًا للتعامل مع المستخدم بدلًا من مجرد `confirm`. ربما يتم رسم نافذة سؤال بشكل لطيف ولكن هذه قصة أخرى. +======= +In practice, such functions are quite useful. The major difference between a real-life `ask` and the example above is that real-life functions use more complex ways to interact with the user than a simple `confirm`. In the browser, such functions usually draw a nice-looking question window. But that's another story. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 **المعاملات `showOk` و `showCancel` الخاصين ب `ask` يسمون _callback functions_ أو فقط _callbacks_.** الفكرة هي أننا نقوم بتمرير دالة ونتوقع أن يتم استدعائها لاحقًا إذا لزم الأمر. وفي حالتنا فإن `showOk` تصبح رد على الإجابة "yes" answer و `showCancel` للإجابة "no". +<<<<<<< HEAD يمكن استخدام Function Expressions لكتابة نفس الدالة بشكل أقصر: +======= +We can use Function Expressions to write an equivalent, shorter function: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run no-beautify function ask(question, yes, no) { @@ -173,7 +222,11 @@ ask( أولا طريقة الكتابة: كيف تفرق بينهم في الكود. +<<<<<<< HEAD - _Function Declaration:_ يتم تعريف الدالة كجزء منفصل في سريان البرنامج. +======= +- *Function Declaration:* a function, declared as a separate statement, in the main code flow: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js // Function Declaration @@ -181,8 +234,12 @@ ask( return a + b; } ``` +<<<<<<< HEAD - _Function Expression:_ يتم إنشاء الدالة داخل تعبير أو جزء آخر. هنا تم إنشاء الدالة في الجزء الأيمن من "assignment expression" `=`: +======= +- *Function Expression:* a function, created inside an expression or inside another syntax construct. Here, the function is created on the right side of the "assignment expression" `=`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js // Function Expression @@ -349,7 +406,14 @@ welcome(); // ok now ```smart header="متى نستخدم Function Declaration أو Function Expression?" عندما نريد عمل دالة فأول ما يجب أن نفكر فيه هو Function Declaration فهو يعطينا حرية أكثر لتنظيم الكود لأن يمكننا استخدام الدالة قبل تعريفها. +<<<<<<< HEAD وهذا أفضل من ناحية قراءة الكود فمن الأسهل ملاحظة `function f(…) {…}` عن `let f = function(…) {…};`.. +======= +```smart header="When to choose Function Declaration versus Function Expression?" +As a rule of thumb, when we need to declare a function, the first thing to consider is Function Declaration syntax. It gives more freedom in how to organize our code, because we can call such functions before they are declared. + +That's also better for readability, as it's easier to look up `function f(…) {…}` in the code than `let f = function(…) {…};`. Function Declarations are more "eye-catching". +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ...ولكن إذا كان Function Declaration غير مناسب لسبب ما أو نريد تعريف دالة بناءًا على شرط معين كما رأينا سابقًا فعندها يجب استخدام Function Expression. ``` diff --git a/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md index 935ed23f8..516d76cec 100644 --- a/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md +++ b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md @@ -1,6 +1,6 @@ ```js run function ask(question, yes, no) { - if (confirm(question)) yes() + if (confirm(question)) yes(); else no(); } diff --git a/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md index ef8576c11..357f5235f 100644 --- a/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md +++ b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md @@ -4,8 +4,13 @@ ```js run function ask(question, yes, no) { +<<<<<<< HEAD if (confirm(question)) yes(); else no(); +======= + if (confirm(question)) yes(); + else no(); +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 } ask( diff --git a/1-js/02-first-steps/17-arrow-functions-basics/article.md b/1-js/02-first-steps/17-arrow-functions-basics/article.md index 578d205bc..21a33d7b5 100644 --- a/1-js/02-first-steps/17-arrow-functions-basics/article.md +++ b/1-js/02-first-steps/17-arrow-functions-basics/article.md @@ -5,10 +5,14 @@ تسمى "arrow functions" لأنها تشبه السهم: ```js -let func = (arg1, arg2, ..., argN) => expression +let func = (arg1, arg2, ..., argN) => expression; ``` +<<<<<<< HEAD ...هذا ينشئ دالة `func` تأخذ قيم `arg1..argN` وتنفذ `expression` الطرف الأيمن باستخدامهم وترجع النتيجة. +======= +This creates a function `func` that accepts arguments `arg1..argN`, then evaluates the `expression` on the right side with their use and returns its result. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 بصيغة أخرى فهي إختصار ل: @@ -33,7 +37,11 @@ let sum = function(a, b) { alert(sum(1, 2)); // 3 ``` +<<<<<<< HEAD كما ترى فإن `(a, b) => a + b` تعني أن الدالة تستقبل قيمتين `a` و `b`. وتنفذ التعبير `a + b` وترجع نتيجته. +======= +As you can see, `(a, b) => a + b` means a function that accepts two arguments named `a` and `b`. Upon the execution, it evaluates the expression `a + b` and returns the result. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 - إذا كان لديك معامل واحد فقط فيمكن حذف الأقواس الدائرية من حوله لجعل التعبير أقصر. @@ -48,7 +56,11 @@ alert(sum(1, 2)); // 3 alert( double(3) ); // 6 ``` +<<<<<<< HEAD - إذا لم يوجد معاملات يتم ترك الأقواس فارغة (ولكن يجب كتابتها): +======= +- If there are no arguments, parentheses are empty, but they must be present: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let sayHi = () => alert("Hello!"); @@ -63,7 +75,13 @@ alert(sum(1, 2)); // 3 ```js run let age = prompt("What is your age?", 18); +<<<<<<< HEAD let welcome = age < 18 ? () => alert("Hello") : () => alert("Greetings!"); +======= +let welcome = (age < 18) ? + () => alert('Hello!') : + () => alert("Greetings!"); +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 welcome(); ``` @@ -74,9 +92,15 @@ welcome(); ## Multiline arrow functions +<<<<<<< HEAD المثال بالأعلى يأخذ القيم على يسار `=>` وينفذ التعبير على اليمين باستخدامهم. أحيانًا نريد شئ أكثر تعقيدًا كتنفيذ عدة أوامر. عندها يمكن وصعهم داخل أقواس معقوفة ولكن يجب استخدام `return` الطبيعية معهم. +======= +The arrow functions that we've seen so far were very simple. They took arguments from the left of `=>`, evaluated and returned the right-side expression with them. + +Sometimes we need a more complex function, with multiple expressions and statements. In that case, we can enclose them in curly braces. The major difference is that curly braces require a `return` within them to return a value (just like a regular function does). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 Like this: @@ -103,7 +127,14 @@ Arrow functions لديها العديد من المميزات الشيقة. ## ملخص +<<<<<<< HEAD Arrow functions تأتي بصيغتين: 1. بدون أقواس معقوفة: `(...args) => expression` -- تقوم الدالة بتنفيذ التعبير الموجود بالجزء الأيمن وترجع نتيحته. 2. مع أقواس معقوفة: `(...args) => { body }` -- تسمح لنا بتنفيذ أكثر من أمر ولكن يجب وضع `return` لكي نرجع قيمة ما. +======= +Arrow functions are handy for simple actions, especially for one-liners. They come in two flavors: + +1. Without curly braces: `(...args) => expression` -- the right side is an expression: the function evaluates it and returns the result. Parentheses can be omitted, if there's only a single argument, e.g. `n => n*2`. +2. With curly braces: `(...args) => { body }` -- brackets allow us to write multiple statements inside the function, but we need an explicit `return` to return something. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/02-first-steps/18-javascript-specials/article.md b/1-js/02-first-steps/18-javascript-specials/article.md index 7b3fbcd8c..2d0fd917c 100644 --- a/1-js/02-first-steps/18-javascript-specials/article.md +++ b/1-js/02-first-steps/18-javascript-specials/article.md @@ -55,7 +55,11 @@ for(;;) { يجب وضع هذه التعليمة أو التوجيه في أعلى النص البرمجي أو في بداية جسم التابع. +<<<<<<< HEAD وبدون وضع التعليمة `"use strict"`، سيعمل كل شيء على ما يرام، ولكن ستعمل بعض الميزات بأسلوبها القديم المتوافق مع السلوك الحديث. لذلك يُفضل عموماً استخدام هذا الأسلوب الأحدث. +======= +Without `"use strict"`, everything still works, but some features behave in the old-fashioned, "compatible" way. We'd generally prefer the modern behavior. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 جديرٌ بالذكر، أن بعضاً من هذه الميزات (كالصفوف التي سندرسها في المستقبل) تقوم بتفعيل هذا الوضع الحديث (الدقيق) بشكل ضمني حتى لو لم يتم كتابة التعليمة بشكل صريح. @@ -143,8 +147,13 @@ alert( "Tea wanted: " + isTeaWanted ); // true الإسناد : الإسناد البسيط هو من الشكل: `a = b` أما الإسناد المركّب فهو من الشكل `a *= 2`. +<<<<<<< HEAD عوامل البِتّات (Bitwise) : تقوم عوامل الـ Bitwise بالعمل مع الأعداد الصحيحة من فئة 32-بِت على الأقل، فهي تعمل على مستوى البِت، وللاطلاع عليها يمكن مراجعة [التوثيق](mdn:/JavaScript/Guide/Expressions_and_Operators#Bitwise) عند الحاجة. +======= +Bitwise +: Bitwise operators work with 32-bit integers at the lowest, bit-level: see the [docs](mdn:/JavaScript/Guide/Expressions_and_Operators#bitwise_operators) when they are needed. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 العوامل الشرطية : والعامل الشرطي هو العامل الوحيد الذي يأخذ ثلاث معاملات: `cond ? resultA : resultB`. فإذا كان الشرط `cond` صحيحاً، سيتمّ إرجاع `resultA` وإلا سيتم إرجاع `resultB`. @@ -256,7 +265,11 @@ switch (age) { 3. الدوال كأسهم: (حيث تُرسم الدوال بطريقة تشبه شكل السهم) ```js +<<<<<<< HEAD // توضع التعابير في الطرف اليميني +======= + // expression on the right side +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 let sum = (a, b) => a + b; // أو يمكن استخدام أكثر من سطر مع أقواس الكتل {...} diff --git a/1-js/03-code-quality/01-debugging-chrome/article.md b/1-js/03-code-quality/01-debugging-chrome/article.md index 1f969765a..30a4ae4f8 100644 --- a/1-js/03-code-quality/01-debugging-chrome/article.md +++ b/1-js/03-code-quality/01-debugging-chrome/article.md @@ -1,4 +1,8 @@ +<<<<<<< HEAD # تصحيح الأخطاء في كروم +======= +# Debugging in the browser +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 قبل كتابة أي كود معقد , فلنتحدث قليلا عن تصحيح الأخطاء. @@ -40,7 +44,11 @@ The Sources panel has 3 parts: بعد ان يتم تنفيذ الأمر, الناتج يظهر اسفله. +<<<<<<< HEAD كمثال, هنا `1+2` ينتج عنها `3` و `hello("debugger")` لا ينتج عنها شئ, لذا فالناتج يكون `undefined`. +======= +For example, here `1+2` results in `3`, while the function call `hello("debugger")` returns nothing, so the result is `undefined`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ![](chrome-sources-console.svg) @@ -64,13 +72,22 @@ The Sources panel has 3 parts: - قم بإزالة نقطة التوقف بالنقر بزر الماوس الأيمن واختيار إزالة. - ...و هكذا. +<<<<<<< HEAD ```smart header="نقاط التوقف المشروطة" *النقر بزر الماوس الأيمن* على رقم السطر يسمح بإنشاء نقطة توقف *مشروطة*. يتم تشغيلها فقط عندما يكون الشرط المعطى محقق. +======= +```smart header="Conditional breakpoints" +*Right click* on the line number allows to create a *conditional* breakpoint. It only triggers when the given expression, that you should provide when you create it, is truthy. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يكون هذا مفيدا عند الحاجة للتوقف فقط تبعا لمتغير معين أو معاملات دالة معينة. ``` +<<<<<<< HEAD ## أمر مصحح الخطأ(Debugger) +======= +## The command "debugger" +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يمكننا أيضًا إيقاف الكود مؤقتًا باستخدام الأمر `debugger` الموجود فيه ، كالتالي: @@ -87,7 +104,13 @@ function hello(name) { ``` هذا الأمر مريح للغاية عندما نكون في محرر أكواد ولا نريد التبديل إلى المتصفح والبحث عن النص في أدوات المطور لتعيين نقطة التوقف. +<<<<<<< HEAD ## انتظر قليلا وانظر حولك +======= +Such command works only when the development tools are open, otherwise the browser ignores it. + +## Pause and look around +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 في مثالنا ، `hello()` يتم النداء عليها عند تحميل الصفحة, لذا اسهل طريقة لتفعيل مصحح الأخطاء (بعد وضع نقطة التوقف) هي اعادة تحميل الصفحة. لذا نضغط `key:F5` (Windows, Linux) أو `key:Cmd+R` (Mac). @@ -99,7 +122,11 @@ function hello(name) { 1. **`Watch` -- يعرض القيم الحالية لأي تعبيرات.** +<<<<<<< HEAD يمكننا النقر فوق علامة زائد `+` وإدخال تعبير. سيظهر مصحح الأخطاء قيمته في أي لحظة ، ويعيد حسابه تلقائيًا في عملية التنفيذ. +======= + You can click the plus `+` and input an expression. The debugger will show its value, automatically recalculating it in the process of execution. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 2. **`Call Stack` -- يعرض سلسلة من الاستدعاءات المترابطة.** @@ -145,11 +172,20 @@ function hello(name) { هذا أمر جيد إذا لم نرغب في معرفة ما يحدث داخل استدعاء الدالة. +<<<<<<< HEAD -- "خطوة للداخل", زره السريع `key:F11`. : مشابه لـ "خطوة" ، ولكنه يتصرف بشكل مختلف في حالة نداءات دوال غير متزامنة. إذا كنت تبدأ فقط في تعلم JavaScript ، فيمكنك تجاهل الاختلاف ، حيث لا تتوفر لدينا نداءات غير متزامنة حتى الآن. للمعرفة مستقبلا ، لاحظ فقط أن الأمر "خطوة" يتجاهل الإجراءات غير المتزامنة ، مثل `setTimeout` (نداء الدوال المجدولة) ، التي يتم تنفيذها لاحقًا. تدخل "الخطوة للداخل" في الكود الخاص بهم ،و تنتظرهم إذا لزم الأمر. +======= + -- "Step over": run the next command, but *don't go into a function*, hotkey `key:F10`. +: Similar to the previous "Step" command, but behaves differently if the next statement is a function call (not a built-in, like `alert`, but a function of our own). + + If we compare them, the "Step" command goes into a nested function call and pauses the execution at its first line, while "Step over" executes the nested function call invisibly to us, skipping the function internals. + + The execution is then paused immediately after that function call. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لمزيد من التفاصيل , اتطلع علي [DevTools manual](https://developers.google.com/web/updates/2018/01/devtools#async). @@ -163,7 +199,12 @@ function hello(name) { : عند تفعيله ، وفتح أدوات المطورين ، يؤدي خطأ النص إلى إيقاف التنفيذ تلقائيًا. ثم يمكننا تحليل المتغيرات لمعرفة الخطأ الذي حدث. لذلك إذا توقف النص بسبب وجود خطأ ، فيمكننا فتح المصحح وتمكين هذا الخيار وإعادة تحميل الصفحة لمعرفة مكان المشكلة وما هو السياق(context) في هذه اللحظة. +<<<<<<< HEAD ```smart header="متابعة الي هنا (Continue to here)" +======= + -- enable/disable automatic pause in case of an error. +: When enabled, if the developer tools is open, an error during the script execution automatically pauses it. Then we can analyze variables in the debugger to see what went wrong. So if our script dies with an error, we can open debugger, enable this option and reload the page to see where it dies and what's the context at that moment. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 انقر بزر الماوس الأيمن على سطر الكود فتفتح قائمة السياق مع خيار رائع يسمى "متابعة إلى هنا". @@ -195,7 +236,11 @@ for (let i = 0; i < 5; i++) { 2. عبارة أمر مصحح الخطأ `debugger`. 3. خطأ (اذا كانت ادوات المطور مفتوحة و زر مفعل اي قيمته "on"). +<<<<<<< HEAD عند الإيقاف المؤقت ، يمكننا تصحيح الأخطاء - فحص المتغيرات وتتبع الكود لمعرفة المكان الذي يذهب فيه التنفيذ بشكل خاطئ. +======= +When paused, we can debug: examine variables and trace the code to see where the execution goes wrong. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 هناك العديد من الخيارات في أدوات المطورين أكثر من تلك المغطاة هنا. الدليل الكامل في . diff --git a/1-js/03-code-quality/02-coding-style/article.md b/1-js/03-code-quality/02-coding-style/article.md index a497a57b0..276b4e5c7 100644 --- a/1-js/03-code-quality/02-coding-style/article.md +++ b/1-js/03-code-quality/02-coding-style/article.md @@ -302,11 +302,19 @@ Linters هي أدوات يمكنها التحقق تلقائيًا من اسلو فيما يلي بعض أدوات الفحص الشهيرة: +<<<<<<< HEAD - [JSLint](http://www.jslint.com/) -- أحد أوائل ادوات الفحص. - [JSHint](http://www.jshint.com/) -- به خصائص اكثر من JSLint. - [ESLint](http://eslint.org/) -- غالبا هو الأحدث. يمكن لجميعهم القيام بهذه المهمة. المؤلف يستخدم [ESLint](http://eslint.org/). +======= +- [JSLint](https://www.jslint.com/) -- one of the first linters. +- [JSHint](https://jshint.com/) -- more settings than JSLint. +- [ESLint](https://eslint.org/) -- probably the newest one. + +All of them can do the job. The author uses [ESLint](https://eslint.org/). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 تتكامل معظم أدوات فحص الكود مع العديد من محررات الأكواد الشهيرة: ما عليك سوى تمكين الاضافة )(Plugin) في المحرر وتكوين الكود. @@ -336,7 +344,11 @@ Linters هي أدوات يمكنها التحقق تلقائيًا من اسلو هنا التوجيه `"extends"` يشير إلى أن التكوين يستند إلى مجموعة اعدادات "eslint:recommended" . بعد ذلك يمكننا تحديد الاعدادات الخاصة بنا. +<<<<<<< HEAD من الممكن أيضًا تنزيل مجموعات من ارشادات الأسلوب و الاضافة عليها بدلاً من ذلك. See لمعلومات أكثر عن التنزيل. +======= +It is also possible to download style rule sets from the web and extend them instead. See for more details about installation. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 كما أن بعض بيئة تطوير متكاملة (IDEs) تحتوي على أدوات فحص الكود مدمجة، وهو أمر مريح ولكنه غير قابل للتخصيص مثل ESLint. diff --git a/1-js/03-code-quality/03-comments/article.md b/1-js/03-code-quality/03-comments/article.md index dad7ef37d..1a90869e5 100644 --- a/1-js/03-code-quality/03-comments/article.md +++ b/1-js/03-code-quality/03-comments/article.md @@ -145,7 +145,11 @@ function pow(x, n) { بالمناسبة ، يمكن للعديد من المحررين مثل [WebStorm] (https://www.jetbrains.com/webstorm/) فهمهم أيضًا واستخدامهم لتوفير الإكمال التلقائي وبعض التحقق التلقائي من التعليمات البرمجية. +<<<<<<< HEAD أيضًا ، هناك أدوات مثل [JSDoc 3] (https://github.com/jsdoc3/jsdoc) يمكنها إنشاء وثائق HTML من التعليقات. يمكنك قراءة المزيد من المعلومات حول JSDoc على . +======= +Also, there are tools like [JSDoc 3](https://github.com/jsdoc/jsdoc) that can generate HTML-documentation from the comments. You can read more information about JSDoc at . +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لماذا تحل المهمة بهذه الطريقة؟ : ما هو مكتوب مهم. لكن ما هو * غير * مكتوب قد يكون أكثر أهمية لفهم ما يحدث. لماذا يتم حل المهمة بهذه الطريقة بالضبط؟ الكود لا يعطي إجابة. diff --git a/1-js/03-code-quality/05-testing-mocha/article.md b/1-js/03-code-quality/05-testing-mocha/article.md index 7e0885492..de4a8e82d 100644 --- a/1-js/03-code-quality/05-testing-mocha/article.md +++ b/1-js/03-code-quality/05-testing-mocha/article.md @@ -2,7 +2,11 @@ يُستخدَم الاختبار الآلي في الكثير من المهام، كما يستخدم بكثرة في المشاريع الحقيقية. +<<<<<<< HEAD ## لم نحتاج الاختبارات؟ +======= +## Why do we need tests? +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 عند كتابة دالة، يمكننا تخيل ما يجب أن تقوم به: ما هي المعاملات التي تعطي نتائج معينة. يمكننا فحص الدالة أثناء التطوير من خلال تشغيلها وموازنة مخرجاتها مع ما هو متوقع. مثلا يمكننا القيام بذلك في الطرفية. @@ -53,7 +57,11 @@ describe("pow", function() { تحتوي المواصفات على 3 أجزاء رئيسية كما ترى في الأعلى: `describe("title", function() { ... })` +<<<<<<< HEAD : ماهي الوظيفة التي نصفها، في هذه الحالة، نحن نصف الدالة pow. تستخدم بواسطة العاملين- أجزاء it. +======= +: What functionality we're describing? In our case we're describing the function `pow`. Used to group "workers" -- the `it` blocks. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 `it("use case description", function() { ... })` : نصف (نحن بطريقة مقروءة للبشر) حالة الاستخدام المخصصة في عنوان it، والمعامل الآخر عبارة عن دالة تفحص هذه الدالة. @@ -81,7 +89,11 @@ describe("pow", function() { لنُجرب تدفق التطوير هذا على حالتنا العملية. +<<<<<<< HEAD الخطوة 1 أصبحت جاهزة: لدينا وصفًا مبدئيًّا للدالة `pow`. الآن وقبل التنفيذ، لِنستخدم بعض مكاتب جافا سكريبت لتشغيل الاختبار حتى نتأكد من إن كانت تعمل (لن تعمل). +======= +The first step is already complete: we have an initial spec for `pow`. Now, before making the implementation, let's use a few JavaScript libraries to run the tests, just to see that they are working (they will all fail). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## المواصفات أثناء التنفيذ diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index 9b86c4f36..53546fa90 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -6,7 +6,13 @@ لذا فمن الشائع تمامًا أن يقوم المحرك بتطبيق الجزء القياسي فقط. +<<<<<<< HEAD صفحة جيدة لمعرفة الحالة الحالية لدعم ميزات اللغة هي (إنها ضخمه, لدينا الكثير لندرسه بعد). +======= +So it's quite common for an engine to implement only part of the standard. + +A good page to see the current state of support for language features is (it's big, we have a lot to study yet). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 As programmers, we'd like to use most recent features. The more good stuff - the better! @@ -21,7 +27,7 @@ Here, in this chapter, our purpose is to get the gist of how they work, and thei ## Transpilers -A [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler) is a special piece of software that can parse ("read and understand") modern code, and rewrite it using older syntax constructs, so that the result would be the same. +A [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler) is a special piece of software that translates source code to another source code. It can parse ("read and understand") modern code and rewrite it using older syntax constructs, so that it'll also work in outdated engines. E.g. JavaScript before year 2020 didn't have the "nullish coalescing operator" `??`. So, if a visitor uses an outdated browser, it may fail to understand the code like `height = height ?? 100`. @@ -39,15 +45,15 @@ Now the rewritten code is suitable for older JavaScript engines. Usually, a developer runs the transpiler on their own computer, and then deploys the transpiled code to the server. -Speaking of names, [Babel](https://babeljs.io) is one of the most prominent transpilers out there. +Speaking of names, [Babel](https://babeljs.io) is one of the most prominent transpilers out there. -Modern project build systems, such as [webpack](http://webpack.github.io/), provide means to run transpiler automatically on every code change, so it's very easy to integrate into development process. +Modern project build systems, such as [webpack](https://webpack.js.org/), provide a means to run a transpiler automatically on every code change, so it's very easy to integrate into the development process. ## Polyfills New language features may include not only syntax constructs and operators, but also built-in functions. -For example, `Math.trunc(n)` is a function that "cuts off" the decimal part of a number, e.g `Math.trunc(1.23) = 1`. +For example, `Math.trunc(n)` is a function that "cuts off" the decimal part of a number, e.g `Math.trunc(1.23)` returns `1`. In some (very outdated) JavaScript engines, there's no `Math.trunc`, so such code will fail. @@ -68,9 +74,9 @@ if (!Math.trunc) { // if no such function } ``` -JavaScript is a highly dynamic language, scripts may add/modify any functions, even including built-in ones. +JavaScript is a highly dynamic language. Scripts may add/modify any function, even built-in ones. -Two interesting libraries of polyfills are: +Two interesting polyfill libraries are: - [core js](https://github.com/zloirock/core-js) that supports a lot, allows to include only needed features. - [polyfill.io](http://polyfill.io) service that provides a script with polyfills, depending on the features and user's browser. @@ -79,9 +85,9 @@ Two interesting libraries of polyfills are: In this chapter we'd like to motivate you to study modern and even "bleeding-edge" language features, even if they aren't yet well-supported by JavaScript engines. -Just don't forget to use transpiler (if using modern syntax or operators) and polyfills (to add functions that may be missing). And they'll ensure that the code works. +Just don't forget to use a transpiler (if using modern syntax or operators) and polyfills (to add functions that may be missing). They'll ensure that the code works. -For example, later when you're familiar with JavaScript, you can setup a code build system based on [webpack](http://webpack.github.io/) with [babel-loader](https://github.com/babel/babel-loader) plugin. +For example, later when you're familiar with JavaScript, you can setup a code build system based on [webpack](https://webpack.js.org/) with the [babel-loader](https://github.com/babel/babel-loader) plugin. Good resources that show the current state of support for various features: - - for pure JavaScript. diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index 2834171ad..25a3d367d 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -44,7 +44,11 @@ let user = { // an object كائن ![user object](object-user.svg) +<<<<<<< HEAD يمكننا إضافة، وحذف، وقراءة الملفات من الخزانة في أي وقت. +======= +We can add, remove and read files from it at any time. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يمكن الوصول إلى قيم الخاصيات باستخدام الصيغة النُقَطية (dot notation): @@ -62,7 +66,11 @@ user.isAdmin = true; ![user object 2](object-user-isadmin.svg) +<<<<<<< HEAD يمكننا استخدام المُعامِل `delete` لحذف خاصية: +======= +To remove a property, we can use the `delete` operator: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js delete user.age; @@ -225,13 +233,21 @@ let bag = { }; ``` +<<<<<<< HEAD الأقواس المربعة أقوى بكثير من استخدام الصيغة النُقطية. حيث تسمح باستخدام أي أسماء خصائص ومتغيرات. لكنها أيضا أكثر إرهاقاً في الكتابة. +======= +Square brackets are much more powerful than dot notation. They allow any property names and variables. But they are also more cumbersome to write. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لذلك، معظم الوقت، حينما يكون اسم خاصية معروفا أو غير مركب، تستخدم الصيغة النُقطية. وإذا أردنا شيئاً أكثر تعقيدا، ننتقل إلى استخدام الأقواس المربعة. ## اختصار قيمة الخاصية (Property value shorthand) +<<<<<<< HEAD في الشيفرة الحقيقية، غالبًا ما نستخدم المتغيرات الموجودة بصفتها قيَمًا لأسماء الخصائص. +======= +In real code, we often use existing variables as values for property names. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 مثلاً: @@ -276,7 +292,11 @@ let user = { ## قيود أسماء الخصائص Property names limitations +<<<<<<< HEAD كما نعلم، لا يمكن للمتغير أن يمتلك اسماً يساوي واحداً من الكلمات المحفوظة للغة (language-reserved words) مثل "for", "let", "return" إلخ. +======= +As we already know, a variable cannot have a name equal to one of the language-reserved words like "for", "let", "return" etc. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لكن بالنسبة لخاصية في كائن، لا توجد مثل هذه القيود: @@ -351,7 +371,11 @@ alert( "blabla" in user ); // false, user.blabla غير موجود يرجى ملاحظة أنه في الجهة اليسرى من `in` يجب أن يكون هناك *اسم خاصية*. يكون عادة نصًا بين علامتي تنصيص. +<<<<<<< HEAD إذا حذفنا علامات التنصيص، فهذا يعني متغيرًا، يجب أن يحتوي على الاسم الفعلي المراد اختباره. على سبيل المثال: +======= +If we omit quotes, that means a variable should contain the actual name to be tested. For instance: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let user = { age: 30 }; @@ -381,7 +405,7 @@ alert( "test" in obj ); // true, الخاصية موجودة بالفعل! مواقف مثل هذه تحدث نادراً، لأن `undefined` لا ينبغي تعيينها بشكل ذاتي. عادة ما نستخدم `null` للقيم غير المعروفة أو الفارغة. لذا معامل `in` يعتبر ضيفاً غريباً في الشيفرة. -## The "for..in" loop +## The "for..in" loop [#forin] للمرور على كل مفاتيح الكائن، يوجد شكل خاص آخر للحلقة loop: `for..in`. هذه الحلقة مختلفة تمامًا عما درسناه سابقًا، أي الحلقة `for(;;)`. @@ -438,7 +462,11 @@ for (let code in codes) { */!* ``` +<<<<<<< HEAD قد يستخد الكائن لاقتراح قائمة من الخيارات للمستخدم. إن كنا نقوم بعمل الموقع بشكل رئيسي للزوار الألمان فإننا نريد أن يظهر `49` في أول القائمة. +======= +The object may be used to suggest a list of options to the user. If we're making a site mainly for a German audience then we probably want `49` to be the first. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لكن إذا قمنا بتشغيل الكود, فإننا نرى صورة مختلفة تماماً: @@ -450,6 +478,7 @@ for (let code in codes) { ````smart header="خصائص عددية؟ ما هذا؟" "الخصائص الرقمية integer property" مصطلح يعني هنا نصًا يمكن تحويله من وإلى عدد دون أن يتغير. +<<<<<<< HEAD لذا, "49" هو اسم خاصية عددي, لأنه عند تحويله إلى عدد وإرجاعه لنص, يبقى كما هو. لكن "+49" و "1.2" are ليسا كذلك: ```js run @@ -457,6 +486,16 @@ for (let code in codes) { alert( String(Math.trunc(Number("49"))) ); // "49", الخاصية العددية ذاتها alert( String(Math.trunc(Number("+49"))) ); // "49" مختلفة عن "49+" => إذًا ليست خاصية عددية alert( String(Math.trunc(Number("1.2"))) ); // "1" مختلفة عن "1.2" => إذًا ليست خاصية عددية +======= +So, `"49"` is an integer property name, because when it's transformed to an integer number and back, it's still the same. But `"+49"` and `"1.2"` are not: + +```js run +// Number(...) explicitly converts to a number +// Math.trunc is a built-in function that removes the decimal part +alert( String(Math.trunc(Number("49"))) ); // "49", same, integer property +alert( String(Math.trunc(Number("+49"))) ); // "49", not same "+49" ⇒ not integer property +alert( String(Math.trunc(Number("1.2"))) ); // "1", not same "1.2" ⇒ not integer property +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ```` @@ -505,9 +544,15 @@ for (let code in codes) { - مفاتيح الخواص يجب أن تكون نصاً أو رمزاً (عادة ما تكون نصاً). - القيم يمكن أن تكون من أي نوع. +<<<<<<< HEAD للوصول إلى خاصية, يمكننا استخدام: - رمز النقطة: `obj.property`. - رمز الأقواس المربعة `obj["property"]`. تسمح الأقواس المربعة بأخذ المفتاح من متغير, مثل `obj[varWithKey]`. +======= +To access a property, we can use: +- The dot notation: `obj.property`. +- Square brackets notation `obj["property"]`. Square brackets allow taking the key from a variable, like `obj[varWithKey]`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 معاملات إضافية Additional operators: - لحذف خاصية: `delete obj.prop`. diff --git a/1-js/04-object-basics/02-object-copy/article.md b/1-js/04-object-basics/02-object-copy/article.md index 12e260d65..770508c7d 100644 --- a/1-js/04-object-basics/02-object-copy/article.md +++ b/1-js/04-object-basics/02-object-copy/article.md @@ -37,7 +37,7 @@ And here's how it's actually stored in memory: The object is stored somewhere in memory (at the right of the picture), while the `user` variable (at the left) has a "reference" to it. -We may think of an object variable, such as `user`, as like a sheet of paper with the address of the object on it. +We may think of an object variable, such as `user`, like a sheet of paper with the address of the object on it. When we perform actions with the object, e.g. take a property `user.name`, the JavaScript engine looks at what's at that address and performs the operation on the actual object. @@ -100,15 +100,45 @@ alert(a == b); // false For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons are needed very rarely -- usually they appear as a result of a programming mistake. +````smart header="Const objects can be modified" +An important side effect of storing objects as references is that an object declared as `const` *can* be modified. + +For instance: + +```js run +const user = { + name: "John" +}; + +*!* +user.name = "Pete"; // (*) +*/!* + +alert(user.name); // Pete +``` + +It might seem that the line `(*)` would cause an error, but it does not. The value of `user` is constant, it must always reference the same object, but properties of that object are free to change. + +In other words, the `const user` gives an error only if we try to set `user=...` as a whole. + +That said, if we really need to make constant object properties, it's also possible, but using totally different methods. We'll mention that in the chapter . +```` + ## Cloning and merging, Object.assign [#cloning-and-merging-object-assign] نسخ المتغير ينشئ مؤشر آخر لنفس الكائن. +<<<<<<< HEAD لكن ماذا إذا أردنا نسخ الكائن نفسه كنسخة منفصلة ؟ That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. But there is rarely a need -- copying by reference is good most of the time. لكن إذا أردنا ذلك حقًا يمكننا فعل ذلك عن طريق عمل كائن آخر والمرور على خواص الكائن الحالي ونسخها واحدة تلو الأخرى. +======= +But what if we need to duplicate an object? + +We can create a new object and replicate the structure of the existing one, by iterating over its properties and copying them on the primitive level. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 كالتالي: @@ -133,12 +163,16 @@ clone.name = "Pete"; // تغيير البيانات alert( user.name ); // تبقى John في الكائن الأصلي ``` +<<<<<<< HEAD أيضًا يمكننا استخدام [Object.assign](mdn:js/Object/assign) لذلك. +======= +We can also use the method [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 The syntax is: ```js -Object.assign(dest, [src1, src2, src3...]) +Object.assign(dest, src1[, src2, src3...]) ``` - المعامل الأول `dest` هو الكائن المراد. @@ -193,7 +227,11 @@ There are also other methods of cloning an object, e.g. using the [spread syntax ## Nested cloning +<<<<<<< HEAD مثل هذا: +======= +Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let user = { @@ -207,9 +245,13 @@ let user = { alert(user.sizes.height); // 182 ``` +<<<<<<< HEAD الآن ليس كافيًا نسخ `clone.sizes = user.sizes` لأن `user.sizes` هو كائن وسيتم نسخ المؤشر ويكون `clone` و `user` لهما نفس الخاصية sizes: مثل هذا: +======= +Now it's not enough to copy `clone.sizes = user.sizes`, because `user.sizes` is an object, and will be copied by reference, so `clone` and `user` will share the same sizes: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let user = { @@ -224,38 +266,78 @@ let clone = Object.assign({}, user); alert(user.sizes === clone.sizes); // true, نفس الكائن +<<<<<<< HEAD // user و clone يتشاركان sizes user.sizes.width++; // تغيير الخاصية من مكان alert(clone.sizes.width); // 51, يجعل التغيير مئي في المكان الآخر +======= +// user and clone share sizes +user.sizes.width = 60; // change a property from one place +alert(clone.sizes.width); // 60, get the result from the other one +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` -To fix that, we should use a cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its structure as well. That is called a "deep cloning". +To fix that and make `user` and `clone` truly separate objects, we should use a cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its structure as well. That is called a "deep cloning" or "structured cloning". There's [structuredClone](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) method that implements deep cloning. -We can use recursion to implement it. Or, to not reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com). -````smart header="Const objects can be modified" -An important side effect of storing objects as references is that an object declared as `const` *can* be modified. +### structuredClone -For instance: +The call `structuredClone(object)` clones the `object` with all nested properties. + +Here's how we can use it in our example: ```js run -const user = { - name: "John" +let user = { + name: "John", + sizes: { + height: 182, + width: 50 + } }; *!* -user.name = "Pete"; // (*) +let clone = structuredClone(user); */!* -alert(user.name); // Pete +alert( user.sizes === clone.sizes ); // false, different objects + +// user and clone are totally unrelated now +user.sizes.width = 60; // change a property from one place +alert(clone.sizes.width); // 50, not related ``` -It might seem that the line `(*)` would cause an error, but it does not. The value of `user` is constant, it must always reference the same object, but properties of that object are free to change. +The `structuredClone` method can clone most data types, such as objects, arrays, primitive values. -In other words, the `const user` gives an error only if we try to set `user=...` as a whole. +It also supports circular references, when an object property references the object itself (directly or via a chain or references). -That said, if we really need to make constant object properties, it's also possible, but using totally different methods. We'll mention that in the chapter . -```` +For instance: + +```js run +let user = {}; +// let's create a circular reference: +// user.me references the user itself +user.me = user; + +let clone = structuredClone(user); +alert(clone.me === clone); // true +``` + +As you can see, `clone.me` references the `clone`, not the `user`! So the circular reference was cloned correctly as well. + +Although, there are cases when `structuredClone` fails. + +For instance, when an object has a function property: + +```js run +// error +structuredClone({ + f: function() {} +}); +``` + +Function properties aren't supported. + +To handle such complex cases we may need to use a combination of cloning methods, write custom code or, to not reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com). ## ملخص @@ -263,4 +345,8 @@ Objects are assigned and copied by reference. In other words, a variable stores كل العمليات التي تتم بواسطة النسخة (مثل إضافة وحذف الخواص) تحدث على نفس الكائن. +<<<<<<< HEAD لعمل نسخة حقيقية يمكننا استخدام `Object.assign` لما يسمى "shallow copy" (الكائنات الداخلية تنسخ بالمؤشر) أو دالة "deep cloning" مثل [\_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). +======= +To make a "real copy" (a clone) we can use `Object.assign` for the so-called "shallow copy" (nested objects are copied by reference) or a "deep cloning" function `structuredClone` or use a custom cloning implementation, such as [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/04-object-basics/03-garbage-collection/article.md b/1-js/04-object-basics/03-garbage-collection/article.md index cb9af14ff..23b2aa506 100644 --- a/1-js/04-object-basics/03-garbage-collection/article.md +++ b/1-js/04-object-basics/03-garbage-collection/article.md @@ -76,7 +76,11 @@ Now if we do the same: user = null; ``` +<<<<<<< HEAD ...في هذه الحالة ما زال بالامكان الوصول الي الكائن عن طريق المتغير العام `admin`, لذا فهو مخزن بالذاكرة. اذا تم استخدام المتغير `admin` في مكان أخر ايضا هنا يمكن ازالة الكائن . +======= +...Then the object is still reachable via `admin` global variable, so it must stay in memory. If we overwrite `admin` too, then it can be removed. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## الكائنات المترابطة @@ -178,11 +182,19 @@ The basic garbage collection algorithm is called "mark-and-sweep". ![](garbage-collection-2.svg) +<<<<<<< HEAD ثم وضع علامة علي كل المراجع المرتبطة به: ![](garbage-collection-3.svg) ...و مراجعم كذلك ان امكن +======= +Then we follow their references and mark referenced objects: + +![](garbage-collection-3.svg) + +...And continue to follow further references, while possible: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ![](garbage-collection-4.svg) @@ -191,6 +203,7 @@ The basic garbage collection algorithm is called "mark-and-sweep". ![](garbage-collection-5.svg) +<<<<<<< HEAD يمكننا تخيل العملية كصب دلو كبير من الطلاء من الجذر و الذي سيسري خلال كل المراجع و يضع علامة علي كل الكائنات التي يمكن الوصول اليها, و الكائنات التي لا يمكن الوصول اليها يتم ازالتها. تلك هي المبادئ التي يعمل علي اساسها جامع القمامة. محرك ال JavaScript يطبق العديد من التحسينات لجعله يعمل بشكل اسرع و الا يؤثر علي الآداء. @@ -205,10 +218,19 @@ The basic garbage collection algorithm is called "mark-and-sweep". - **مجموعة وقت الخمول** -- يحاول جامع القمامة ان يعمل في حالة ان وحدة المعالجة المركزية (CPU) في حالة خمول حتي لا يؤثر علي عملية التنفيذ. هنالك العديد من التحسينات في خوارزميات جامع القمامة. و علي قدر ما اود ان اشرحها هنا,يجب ان نتوقف و ذلك لأن المحركات الختلفة تتبني طرق و حلول مختلفة و الأهم من ذلك ان الأشياء تتغير بتتطور المحركات, لذا ادرس أكثر "مقدما" فبدون الحاجة الحقيقية لمعرفتها فهي لا تستحق العناء الا ان كنت و بالطبع تمتلك الشغف للمعرفة فالروابط بالأسفل ستساعدك بالتأكيد. +======= +That's the concept of how garbage collection works. JavaScript engines apply many optimizations to make it run faster and not introduce any delays into the code execution. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## الملخص +<<<<<<< HEAD اهم النقاط لتعرفها: +======= +- **Generational collection** -- objects are split into two sets: "new ones" and "old ones". In typical code, many objects have a short life span: they appear, do their job and die fast, so it makes sense to track new objects and clear the memory from them if that's the case. Those that survive for long enough, become "old" and are examined less often. +- **Incremental collection** -- if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays in the execution. So the engine splits the whole set of existing objects into multiple parts. And then clear these parts one after another. There are many small garbage collections instead of a total one. That requires some extra bookkeeping between them to track changes, but we get many tiny delays instead of a big one. +- **Idle-time collection** -- the garbage collector tries to run only while the CPU is idle, to reduce the possible effect on the execution. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 - جامع القمامة يعمل بشكل تلقائي, لا يمكن اجباره علي العمل او ايقافه - الكائنات تظل في الذاكرة طالما كان بالمكان الوصول اليها @@ -218,14 +240,28 @@ The basic garbage collection algorithm is called "mark-and-sweep". كتاب `"The Garbage Collection Handbook: The Art of Automatic Memory Management"(R.Jones et al)` يجمع بعضها +<<<<<<< HEAD اذا كنت علي علم بالمستويات العميقة من البرمجيات , فهنالك المزيد من المعلومات عن جـامع القمامة `V8` في هذا المقال +======= +- Garbage collection is performed automatically. We cannot force or prevent it. +- Objects are retained in memory while they are reachable. +- Being referenced is not the same as being reachable (from a root): a pack of interlinked objects can become unreachable as a whole, as we've seen in the example above. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection) +<<<<<<< HEAD تنشر ايضا [V8 blog](https://v8.dev/) مقالات حول تنظيم الذاكرة من آن الي أخر, بطبيعة الحال, لتعلم جامع القمامة يفضل ان تتهيأ عن طريق تعلم مكونات `V8` بشكل عام و قراءة مدونة [Vyacheslav Egorov](http://mrale.ph) و الذي عمل كأحد مهندسي `V8`. استطيع ان أقول `V8` تحديدا لأنه الأكثر تغطية عن طريق المقالات علي الانترنت. و بالنسبة للمحركات الأخري, العديد من الطرق متشابهة, و لكن جامع القمامة يختلف في نقاط عديدة. المعرفة المتعمقة للمحركات ضرورية في حين الحاجة الي تحسينات ذات مستوي متطور, فأن تخطط لمعرفتها بعد ان تصبح علي معرفة جيدة باللغة لهي بالتأكيد خطوة حكيمة. +======= +If you are familiar with low-level programming, more detailed information about V8's garbage collector is in the article [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection). + +The [V8 blog](https://v8.dev/) also publishes articles about changes in memory management from time to time. Naturally, to learn more about garbage collection, you'd better prepare by learning about V8 internals in general and read the blog of [Vyacheslav Egorov](http://mrale.ph) who worked as one of the V8 engineers. I'm saying: "V8", because it is best covered by articles on the internet. For other engines, many approaches are similar, but garbage collection differs in many aspects. + +In-depth knowledge of engines is good when you need low-level optimizations. It would be wise to plan that as the next step after you're familiar with the language. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/04-object-basics/04-object-methods/7-calculator/task.md b/1-js/04-object-basics/04-object-methods/7-calculator/task.md index 522e6403c..c1f24dba4 100644 --- a/1-js/04-object-basics/04-object-methods/7-calculator/task.md +++ b/1-js/04-object-basics/04-object-methods/7-calculator/task.md @@ -6,9 +6,15 @@ أنشئ كائنًا باسم `calculator` يحوي الدوال الثلاث التالية: +<<<<<<< HEAD - `read()‎` تطلب قيمتين وتحفظها كخصائص الكائن. - `sum()‎` تُرجِع مجموع القيم المحفوظة. - `mul()‎` تضرب القيم المحفوظة وتُرجِع النتيجة. +======= +- `read()` prompts for two values and saves them as object properties with names `a` and `b` respectively. +- `sum()` returns the sum of saved values. +- `mul()` multiplies saved values and returns the result. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js let calculator = { @@ -21,4 +27,3 @@ alert( calculator.mul() ); ``` [demo] - diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/solution.js b/1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/solution.js index e98fe6410..a35c009cc 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/solution.js +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/solution.js @@ -11,5 +11,6 @@ let ladder = { }, showStep: function() { alert(this.step); + return this; } }; \ No newline at end of file diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/test.js b/1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/test.js index a2b17fcc4..b4f2459b7 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/test.js +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/test.js @@ -32,6 +32,14 @@ describe('Ladder', function() { it('down().up().up().up() ', function() { assert.equal(ladder.down().up().up().up().step, 2); }); + + it('showStep() should return this', function() { + assert.equal(ladder.showStep(), ladder); + }); + + it('up().up().down().showStep().down().showStep()', function () { + assert.equal(ladder.up().up().down().showStep().down().showStep().step, 0) + }); after(function() { ladder.step = 0; diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md index 5546be9f6..31876a26b 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md @@ -23,7 +23,7 @@ let ladder = { } }; -ladder.up().up().down().up().down().showStep(); // 1 +ladder.up().up().down().showStep().down().showStep(); // shows 1 then 0 ``` يمكننا أيضا كتابة استدعاء مستقل في كل سطر ليصبح سهل القراءة بالنسبة للسلاسل الأطول @@ -33,7 +33,7 @@ ladder .up() .up() .down() - .up() + .showStep() // 1 .down() - .showStep(); // 1 + .showStep(); // 0 ``` diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md index fdf5d8020..ae538ef00 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md @@ -28,12 +28,14 @@ ladder.up(); ladder.up(); ladder.down(); ladder.showStep(); // 1 +ladder.down(); +ladder.showStep(); // 0 ``` عَدِّل الشيفرة الخاصة بالدوال `up`، و `down`، و `showStep` لجعل الاستدعاءات متسلسلة كما يلي: ```js -ladder.up().up().down().showStep(); // 1 +ladder.up().up().down().showStep().down().showStep(); // shows 1 then 0 ``` يُستخدم هذا النمط بنطاق واسع في مكتبات JavaScript diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index acbb2e8b8..8accc9a9e 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -50,7 +50,7 @@ let user = { // أولا، نعرف دالة function sayHi() { alert("Hello!"); -}; +} // أضِف الدالة للخاصية لإنشاء تابع user.sayHi = sayHi; @@ -80,14 +80,22 @@ user = { // يبدو شكل الدالة المختصر أفضل، أليس كذلك؟ user = { *!* - sayHi() { // same as "sayHi: function()" + sayHi() { // same as "sayHi: function(){...}" */!* alert("Hello"); } }; ``` +<<<<<<< HEAD يمكننا حذف الكلمة `"function"` وكتابة `sayHi()‎ ` كما هو موضح. حقيقةً، التعبيرين ليسا متطابقين تمامًا، يوجد اختلافات خفية متعلقة بالوراثة في الكائنات (سيتم شرحها لاحقًا)، لكن لا يوجد مشكلة الآن. يفضل استخدام الصياغة الأقصر في كل الحالات تقريبًا. +======= +As demonstrated, we can omit `"function"` and just write `sayHi()`. + +To tell the truth, the notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter. In almost all cases, the shorter syntax is preferred. + +## "this" in methods +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## الكلمة "this" في الدوال diff --git a/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md b/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md index 8c1fea8eb..d80113acc 100644 --- a/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md +++ b/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md @@ -4,7 +4,7 @@ importance: 2 # Two functions – one object -Is it possible to create functions `A` and `B` such as `new A()==new B()`? +Is it possible to create functions `A` and `B` so that `new A() == new B()`? ```js no-beautify function A() { ... } diff --git a/1-js/04-object-basics/06-constructor-new/article.md b/1-js/04-object-basics/06-constructor-new/article.md index 6c0fa9039..a387494ff 100644 --- a/1-js/04-object-basics/06-constructor-new/article.md +++ b/1-js/04-object-basics/06-constructor-new/article.md @@ -1,8 +1,12 @@ # الباني والعامل "new" +<<<<<<< HEAD نُنشِئ الكائنات باستخدام الصيغة الاعتيادية المختصرة `{...}`. لكننا نحتاج لإنشاء العديد من الكائنات المتشابهة غالبًا، مثل العديد من المستخدمين، أو عناصر لقائمة وهكذا. يمكن القيام بذلك باستخدام الدوال البانية (constructor functions) لكائن والمُعامِل `"new"`. +======= +The regular `{...}` syntax allows us to create one object. But often we need to create many similar objects, like multiple users or menu items and so on. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## الدالة البانية @@ -55,6 +59,7 @@ isAdmin: false لاحظ أنَّه يمكن استخدام أي دالة لتكون دالة بانية تقنيًا. يعني أنه يمكن تنفيذ أي دالة مع `new`، وستُنَفَّذ باستخدام الخوارزمية أعلاه. استخدام الأحرف الكبيرة في البداية هو اتفاق شائع لتمييز الدالة البانية من غيرها وأنَّه يجب استدعاؤها مع `new`. +<<<<<<< HEAD ### `**new function() { … }‎**` إن كان لدينا العديد من الأسطر البرمجية، وجميعها عن إنشاء كائن واحد مُعَقَّد، فبإمكاننا تضمينها في دالة بانية، هكذا: @@ -70,6 +75,27 @@ this.isAdmin = false; ``` لا يمكن استدعاء المُنشِئ مجددًا، لأنه غير محفوظ في أي مكان، يُنشَأ ويُستدعى فقط. لذا فإن الخدعة تهدف إلى تضمين الشيفرة التي تُنشِئ كائنًا واحدًا، دون إعادة الاستخدام وتكرار العملية مستقبلًا. +======= +Let's note once again -- technically, any function (except arrow functions, as they don't have `this`) can be used as a constructor. It can be run with `new`, and it will execute the algorithm above. The "capital letter first" is a common agreement, to make it clear that a function is to be run with `new`. + +````smart header="new function() { ... }" +If we have many lines of code all about creation of a single complex object, we can wrap them in an immediately called constructor function, like this: + +```js +// create a function and immediately call it with new +let user = new function() { + this.name = "John"; + this.isAdmin = false; + + // ...other code for user creation + // maybe complex logic and statements + // local variables etc +}; +``` + +This constructor can't be called again, because it is not saved anywhere, just created and called. So this trick aims to encapsulate the code that constructs the single object, without future reuse. +```` +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## وضع اختبار الباني: `new.target` diff --git a/1-js/04-object-basics/07-optional-chaining/article.md b/1-js/04-object-basics/07-optional-chaining/article.md index 6df6d0c2a..80a8e2a83 100644 --- a/1-js/04-object-basics/07-optional-chaining/article.md +++ b/1-js/04-object-basics/07-optional-chaining/article.md @@ -25,14 +25,14 @@ That's the expected result. JavaScript works like this. As `user.address` is `un In many practical cases we'd prefer to get `undefined` instead of an error here (meaning "no street"). -...And another example. In the web development, we can get an object that corresponds to a web page element using a special method call, such as `document.querySelector('.elem')`, and it returns `null` when there's no such element. +...and another example. In Web development, we can get an object that corresponds to a web page element using a special method call, such as `document.querySelector('.elem')`, and it returns `null` when there's no such element. ```js run // document.querySelector('.elem') is null if there's no element let html = document.querySelector('.elem').innerHTML; // error if it's null ``` -Once again, if the element doesn't exist, we'll get an error accessing `.innerHTML` of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result. +Once again, if the element doesn't exist, we'll get an error accessing `.innerHTML` property of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result. How can we do this? @@ -44,11 +44,19 @@ let user = {}; alert(user.address ? user.address.street : undefined); ``` -It works, there's no error... But it's quite inelegant. As you can see, the `"user.address"` appears twice in the code. For more deeply nested properties, that becomes a problem as more repetitions are required. +It works, there's no error... But it's quite inelegant. As you can see, the `"user.address"` appears twice in the code. -E.g. let's try getting `user.address.street.name`. +Here's how the same would look for `document.querySelector`: -We need to check both `user.address` and `user.address.street`: +```js run +let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null; +``` + +We can see that the element search `document.querySelector('.elem')` is actually called twice here. Not good. + +For more deeply nested properties, it becomes even uglier, as more repetitions are required. + +E.g. let's get `user.address.street.name` in a similar fashion. ```js let user = {}; // user has no address @@ -58,7 +66,7 @@ alert(user.address ? user.address.street ? user.address.street.name : null : nul That's just awful, one may even have problems understanding such code. -Don't even care to, as there's a better way to write it, using the `&&` operator: +There's a little better way to write it, using the `&&` operator: ```js run let user = {}; // غرض لمستخدم لا يملك عنوان @@ -92,6 +100,12 @@ alert( user?.address?.street ); // سيظهر لنا بدون حدوث خطأ un The code is short and clean, there's no duplication at all. +Here's an example with `document.querySelector`: + +```js run +let html = document.querySelector('.elem')?.innerHTML; // will be undefined, if there's no element +``` + Reading the address with `user?.address` works even if `user` object doesn't exist: ```js run @@ -107,9 +121,9 @@ E.g. in `user?.address.street.name` the `?.` allows `user` to safely be `null/un ولكن إذا كان الغرض `user` موجوداً بالفعل، فيجب أن تكون الخصائص الوسيطة موجودة ونقصد بالخصائص الوسيطة `user.address` مثلاً. -For example, if according to our coding logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`. +For example, if according to our code logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`. -So, if `user` happens to be undefined due to a mistake, we'll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug. +Then, if `user` happens to be undefined, we'll see a programming error about it and fix it. Otherwise, if we overuse `?.`, coding errors can be silenced where not appropriate, and become more difficult to debug. ``` ````warn header="المتحول الواقع قبل التركيب `.?` يجب أن يكون معرّفاً" @@ -126,7 +140,7 @@ The variable must be declared (e.g. `let/const/var user` or as a function parame كما تمّ ذكره آنفاً، يقوم التركيب `.?` بإيقاف عملية تقييم الكود البرمجي (يختصر الطريق) إذا لم يكن القسم اليساري (على يسار التركيب) موجوداً. -So, if there are any further function calls or side effects, they don't occur. +So, if there are any further function calls or operations to the right of `?.`, they won't be made. For instance: @@ -134,7 +148,7 @@ For instance: let user = null; let x = 0; -user?.sayHi(x++); // no "sayHi", so the execution doesn't reach x++ +user?.sayHi(x++); // no "user", so the execution doesn't reach sayHi call and x++ alert(x); // لا يتم زيادة القيمة 0 ``` @@ -161,13 +175,13 @@ userAdmin.admin?.(); // I am admin */!* *!* -userGuest.admin?.(); // nothing (no such method) +userGuest.admin?.(); // nothing happens (no such method) */!* ``` -Here, in both lines we first use the dot (`userAdmin.admin`) to get `admin` property, because we assume that the user object exists, so it's safe read from it. +Here, in both lines we first use the dot (`userAdmin.admin`) to get `admin` property, because we assume that the `user` object exists, so it's safe read from it. -Then `?.()` checks the left part: if the admin function exists, then it runs (that's so for `userAdmin`). Otherwise (for `userGuest`) the evaluation stops without errors. +Then `?.()` checks the left part: if the `admin` function exists, then it runs (that's so for `userAdmin`). Otherwise (for `userGuest`) the evaluation stops without errors. في حال الرغبة باستخدام الأقواس المربّعة `[]` بدلاً من النقطة `.` للوصول للخواص ضمن غرض أو كائن ما، سيفي التعبير `[].?` بالغرض أيضاً. وبشكل مشابه للحالات السابقة، يسمح هذا التعبير بشكل آمن قراءةَ خاصية أو حقل ضمن غرض معيّن قد لا يكون موجوداً. @@ -178,7 +192,7 @@ let user1 = { firstName: "John" }; -let user2 = null; +let user2 = null; alert( user1?.[key] ); // John alert( user2?.[key] ); // undefined @@ -191,18 +205,22 @@ delete user?.name; // سيقوم بحذف اسم المستخدم في حال ك ``` ````warn header="We can use `?.` for safe reading and deleting, but not writing" -The optional chaining `?.` has no use at the left side of an assignment. +The optional chaining `?.` has no use on the left side of an assignment. For example: ```js run let user = null; +<<<<<<< HEAD user?.name = "John"; // فسيحدث خطأ، لأن هذه الطريقة لا تعمل // لأنه سيتم تقييمها على أن // undefined = "John" +======= +user?.name = "John"; // Error, doesn't work +// because it evaluates to: undefined = "John" +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` -It's just not that smart. ```` ## Summary @@ -217,4 +235,4 @@ The optional chaining `?.` syntax has three forms: وإذا كان لدينا خصائص متداخلة فيما بينها، فيسمح تسلسل من التركيب `.?` بقرائتها بشكلٍ آمن. -Still, we should apply `?.` carefully, only where it's acceptable that the left part doesn't exist. So that it won't hide programming errors from us, if they occur. +Still, we should apply `?.` carefully, only where it's acceptable, according to our code logic, that the left part doesn't exist. So that it won't hide programming errors from us, if they occur. diff --git a/1-js/04-object-basics/08-symbol/article.md b/1-js/04-object-basics/08-symbol/article.md index 23e6a0580..d1aa2e676 100644 --- a/1-js/04-object-basics/08-symbol/article.md +++ b/1-js/04-object-basics/08-symbol/article.md @@ -8,9 +8,22 @@ (symbol) . ليست رقما أو قيمه منطقيه (boolean) وإنما عباره عن نصوص أو رموز, فقط هذين النوعين. +<<<<<<< HEAD لقد استخدمنا حتى الآن النص فقط. فهيا نرى الفوائد التى يمكن أن توفرها لنا الرموز. ## الرموز +======= +By specification, only two primitive types may serve as object property keys: + +- string type, or +- symbol type. + +Otherwise, if one uses another type, such as number, it's autoconverted to string. So that `obj[1]` is the same as `obj["1"]`, and `obj[true]` is the same as `obj["true"]`. + +Until now we've been using only strings. + +Now let's explore symbols, see what they can do for us. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 كلمة رمز فى الإنجليزيه تعنى معَرٌِف فريد من نوعه أى لا شئ مماثل له Unique Identifier. @@ -18,18 +31,25 @@ Unique Identifier. يمكن إنشاء قيمه من نوع الرمز باستخدام الداله `Symbol()`: ```js -// id is a new symbol let id = Symbol(); ``` +<<<<<<< HEAD عند الإنشاء, يمكننا إعطاء الرمز وصفًا (ويمكن تسميته أيضا إسم الرمز), وهذا مفيد غالبا فى البحث عن الأخطاء وحلها. +======= +Upon creation, we can give symbols a description (also called a symbol name), mostly useful for debugging purposes: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js // id is a symbol with the description "id" let id = Symbol('id'); ``` +<<<<<<< HEAD إن الرموز مضمون بتفرُّدها. حتى فى حالة إنشاء عدة رموز بنفس الوصف, ولكنهم مختلفين فى القيمه. فالوصف مجرد وَسْم لا يؤثر على أى شيء. +======= +Symbols are guaranteed to be unique. Even if we create many symbols with exactly the same description, they are different values. The description is just a label that doesn't affect anything. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال, هذان الرمزان لهما نفس الوصف -- ولكنهما غير متساويين: @@ -46,10 +66,17 @@ alert(id1 == id2); // false (Ruby) أو أى لغة برمجة أخرى لديها شئ قريب من الرموز فلا تحتار +<<<<<<< HEAD ````warn header="الرموز لا تتحول إلى نص تلقائياً" أغلب القيم فى جافا سكريبت يمكن تحويلها ضمنيًا إلى نص (string). على سبيل المثال, يمكننا أن نعرض أى قيمه فى دالة التنبيه (`alert()`), وستعمل, ولكن الرموز (Symbols) لها طابع خاص. فلا تسرى عليهم القاعده نفسها. +======= +So, to summarize, a symbol is a "primitive unique value" with an optional description. Let's see where we can use them. + +````warn header="Symbols don't auto-convert to a string" +Most values in JavaScript support implicit conversion to a string. For instance, we can `alert` almost any value, and it will work. Symbols are special. They don't auto-convert. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال, هذا الكود سيؤدى إلى ظهور خطأ: @@ -62,9 +89,14 @@ alert(id); // TypeError: Cannot convert a Symbol value to a string هذا لمنع الأخطاء غير المقصوده, لأن النصوص والرموز مختلفين تمام ولا يجب أن يتم تغيير واحد إلى الآخر عن طريق الخطأ. +<<<<<<< HEAD إذا كنا نريد أن نعرض الرمز كما هو, فإننا نحتاج إلى أن نستدعى الداله `.toString()` مع هذا الرمز, كالمثال أدناه: +======= +If we really want to show a symbol, we need to explicitly call `.toString()` on it, like here: + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let id = Symbol("id"); *!* @@ -72,9 +104,14 @@ alert(id.toString()); // Symbol(id), now it works */!* ``` +<<<<<<< HEAD أو نستدعى الخاصيه `symbol.description` لعرض الوصف فقط: +======= +Or get `symbol.description` property to show the description only: + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let id = Symbol("id"); *!* @@ -86,9 +123,14 @@ alert(id.description); // id ## الخصائص المخفيه +<<<<<<< HEAD باستخدام الرموز يمكننا أن ننشئ خصائص مخفيه لكائن ما (object) حيث لا يمكن لأى جزء آخر فى الكود أن يصل إليها ولا أن يعدل قيمتها عن طريق الخطأ. +======= + +Symbols allow us to create "hidden" properties of an object, that no other part of code can accidentally access or overwrite. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال, إذا كنا نعمل على كائن `user` @@ -114,9 +156,15 @@ alert(user[id]); // we can access the data using the symbol as the key بدلا من النص `"id"` ؟ +<<<<<<< HEAD حيث أن الكائن `user` ينتمي لكود خارجي وهذا الكود يعمل جيدا, إذا فلا يصح أن نضيف أى خاصيه لهذا الكائن. ولكن الرمز لا يمكن الوصول إليه عن طريق الخطأ مثل النص حيث أن الكود الخارجى لا يمكن أن يراه من الأساس, ولذلك هذه الطريقه تُعد صحيحه. تخيل أيضا لو أن هناك برنامج (script) آخر يريد أن يضيف خاصية بداخل الكائن `user` لأغراضه الخاصه, هذا البرنامج الآخر يمكنه أن يكون مكتبه مبنية بجافا سكريبت ولذلك فإن هذه البرامج لا تعرف شيئا عن بعضها البعض. +======= +As `user` objects belong to another codebase, it's unsafe to add fields to them, since we might affect pre-defined behavior in that other codebase. However, symbols cannot be accessed accidentally. The third-party code won't be aware of newly defined symbols, so it's safe to add symbols to the `user` objects. + +Also, imagine that another script wants to have its own identifier inside `user`, for its own purposes. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لذلك هذا البرنامج يمكنه أن ينشئ `Symbol("id")` @@ -183,11 +231,15 @@ for (let key in user) alert(key); // name, age (no symbols) */!* // the direct access by the symbol works -alert( "Direct: " + user[id] ); +alert( "Direct: " + user[id] ); // Direct: 123 ``` +<<<<<<< HEAD وأيضا يتم تجاهل الخصائص من نوع الرمز عند استخدام `Object.keys(user)`. لأن هذا جزء من المبدأ العام "إخفاء الخصائص الرمزيه" "hiding symbolic properties". وبالمثل إذا كان هناك أى برنامج آخر يقوم أو مكتبه تقوم بالتكرار على الخصائص فى هذا الكائن فإنها لن تستطيع أن تصل إلى الخاصيه من نوع الرمز. +======= +[Object.keys(user)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) also ignores them. That's a part of the general "hiding symbolic properties" principle. If another script or a library loops over our object, it won't unexpectedly access a symbolic property. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على النقيض تماما فإن [Object.assign](mdn:js/Object/assign) @@ -237,12 +289,20 @@ alert(id === idAgain); // true ```smart header="هذا يبدو مثل لغة البرمجه Ruby" فى بعض لغات البرمجه مثل Ruby فإن هناك رمزًا لكل إسم. +<<<<<<< HEAD فى جافا سكريبت كما نرى فإن هذا صحيح بالنسبة إلى الرموز العامه. +======= +In JavaScript, as we can see, that's true for global symbols. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ### Symbol.keyFor +<<<<<<< HEAD بالنسبة إلى الرموز العامه فإنه لايوجد `Symbol.for(key)` التى تقوم بإرجاع الرمز باستخدام الإسم فقط، ولكن يوجد أيضا العكس `Symbol.keyFor(sym)` الذى يقوم بإرجاع الإسم باستخدام الرمز العام. +======= +We have seen that for global symbols, `Symbol.for(key)` returns a symbol by name. To do the opposite -- return a name by global symbol -- we can use: `Symbol.keyFor(sym)`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال: @@ -258,7 +318,11 @@ alert(Symbol.keyFor(sym2)); // id الداله `Symbol.keyFor` عندما تعمل تقوم باستخدام مكان تسجيل الرموز العام للبحث عن إسم للرمز, ولذلك فإنها لا تعمل إلا مع الرموز العامه. فإذا كان الرمز غير عام فلن تستطيع إيجاده وستقوم بإرجاع `undefined`. +<<<<<<< HEAD يقال بأن كل رمز يملك الخاصيه `description`. +======= +That said, all symbols have the `description` property. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال: @@ -300,10 +364,20 @@ alert(localSymbol.description); // name 1. خصائص الكائن المخفيه. +<<<<<<< HEAD إذا كنا نريد أن نضيف خاصيه إلى كائن لا ينتمى إلى هذا الكود بل إلى برنامج آخر أو مكتبه، فعندئذ يمكننا إنشاء رمز واستخدامه كخاصيه. والخاصيه من نوع الرمز لا تظهر فى التكرار `for .. in`, ولذلك لا يمكن الوصول إلى الخاصيه عن طريق الخطأ أو أن تتعارض مع أى خاصية أخرى وذلك لأن البرنامج الآخر لا يملك الرمز الخاص بنا. وبالتالى ستظل الخاصيه محميه من الوصول إليها أو التعديل عليها. +======= +1. "Hidden" object properties. + + If we want to add a property into an object that "belongs" to another script or a library, we can create a symbol and use it as a property key. A symbolic property does not appear in `for..in`, so it won't be accidentally processed together with other properties. Also it won't be accessed directly, because another script does not have our symbol. So the property will be protected from accidental use or overwrite. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ولذلك يمكننا أن نُخفى أى شئ بداخل كائنات نحتاجها ولا يستطيع أى برنامج الوصول إليها باستخدام الخصائص من نوع الرموز. 2. هناك الكثير من الرموز الموجوده بالفعل فى جافا سكريبت والتى يمكن الوصول إليها عن طريق `Symbol.*`. ويمكننا استخدامهم لتغيير بعض السلوك الموجود بالفعل فى اللغه. على سبيل المثال فإننا فى موضوع مقبل سنستخدم `Symbol.iterator` من أجل التكراريات [iterables](info:iterable) وغيرها. +<<<<<<< HEAD عمليًا، فإن الرموز لا تكون مخفية بالكامل. ولكن هناك داله موجوده تسمى [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) والتى تمكننا من الوصول إلى كل الرموز. ,توجد أيضًا دالة تسمي [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) والتى تقوم بإرجاع _كل_ الخصائص بداخل كائن معين بما فيها الخصائص التى من نوع الرمز. ولذلك فإن هذه الخصائص ليست مخفية بالكامل. ولكن أعلب المكتبات والدوال لا تستخدم هذه الوسائل. +======= +Technically, symbols are not 100% hidden. There is a built-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows us to get all symbols. Also there is a method named [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys of an object including symbolic ones. But most libraries, built-in functions and syntax constructs don't use these methods. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/04-object-basics/09-object-toprimitive/article.md b/1-js/04-object-basics/09-object-toprimitive/article.md index 2ad596136..7a1ab38c3 100644 --- a/1-js/04-object-basics/09-object-toprimitive/article.md +++ b/1-js/04-object-basics/09-object-toprimitive/article.md @@ -4,21 +4,58 @@ فى هذه الحالة، تتحول الكائنات إلى قيم فردية تلقائيًا، ثم يتم تنفيذ هذه العملية الحسابية. +<<<<<<< HEAD فى قسم (تحويل الأنواع) رأينا كيف يمكن تحويل النصوص (strings) والأرقام والقيَم المنطقيه (booleans) إلى قيم فردية. ولكننا تركنا مساحة فارغة من أجل الكائنات. والآن بعد أن عرفنا الكثير عن الدوال (methods) والرموز (symbols)، أصبح الآن ممكنًا أن نملأ هذه المساحه. +======= +JavaScript doesn't allow you to customize how operators work on objects. Unlike some other programming languages, such as Ruby or C++, we can't implement a special object method to handle addition (or other operators). + +In case of such operations, objects are auto-converted to primitives, and then the operation is carried out over these primitives and results in a primitive value. + +That's an important limitation: the result of `obj1 + obj2` (or another math operation) can't be another object! + +E.g. we can't make objects representing vectors or matrices (or achievements or whatever), add them and expect a "summed" object as the result. Such architectural feats are automatically "off the board". + +So, because we can't technically do much here, there's no maths with objects in real projects. When it happens, with rare exceptions, it's because of a coding mistake. + +In this chapter we'll cover how an object converts to primitive and how to customize it. + +We have two purposes: + +1. It will allow us to understand what's going on in case of coding mistakes, when such an operation happened accidentally. +2. There are exceptions, where such operations are possible and look good. E.g. subtracting or comparing dates (`Date` objects). We'll come across them later. + +## Conversion rules +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 1. كل الكائنات عند تحويلها إلى قيمه منطقيه (boolean) فإن قيمتها تساوى `true`. وبالتالى فإن التحويلات المتاحة هي التحويل إلى نص أو رقم. +<<<<<<< HEAD 2. يحدث التحويل إلى رقم عند طرح كائنين أو استخدام دالة حسابية. على سبيل المثال، الكائنات من نوع `Date` (سيتم شرحها فى قسم التاريخ) يمكن طرحها، ونتيجة طرح `date1 - date2` هي الفرق بين التاريخين. 3. وبالنسبه إلى التحويل إلى نص -- فإنه يحدث عادة عند طباعة الكائن باستخدام دالة التنبيه `alert(obj)` والدوال المشابهة. +======= +1. There's no conversion to boolean. All objects are `true` in a boolean context, as simple as that. There exist only numeric and string conversions. +2. The numeric conversion happens when we subtract objects or apply mathematical functions. For instance, `Date` objects (to be covered in the chapter ) can be subtracted, and the result of `date1 - date2` is the time difference between two dates. +3. As for the string conversion -- it usually happens when we output an object with `alert(obj)` and in similar contexts. + +We can implement string and numeric conversion by ourselves, using special object methods. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 -## ToPrimitive +Now let's get into technical details, because it's the only way to cover the topic in-depth. +<<<<<<< HEAD يمكننا التحكم فى التحويل إلى نص أو رقم، باستخدام بعض دوال الكائنات. هناك ثلاث ملاحظات مختلفه على تحويل الأنواع ويطلق عليها "hints" وتم ذكرها فى [المصدر](https://tc39.github.io/ecma262/#sec-toprimitive): `"النص"` +======= +## Hints + +How does JavaScript decide which conversion to apply? + +There are three variants of type conversion, that happen in various situations. They're called "hints", as described in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive): +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 : يحدث التحويل إلى نص عندما نقوم بعملية معينه على كائن تتوقع نصًا لا كائنًا مثل دالة التنبيه `alert`: @@ -46,9 +83,16 @@ let greater = user1 > user2; ``` +<<<<<<< HEAD `"التصرف الإفتراضي"` +======= + Most built-in mathematical functions also include such conversion. + +`"default"` +: Occurs in rare cases when the operator is "not sure" what type to expect. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 - For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them), so both strings and numbers would do. So if a binary plus gets an object as an argument, it uses the `"default"` hint to convert it. + For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them). So if a binary plus gets an object as an argument, it uses the `"default"` hint to convert it. على سبيل المثال، العلامه `+` يمكن أن تعمل مع النصوص (حيث تقوم بالإضافه) أو الأرقام (حيث تقوم بالجمع)، ولذلك فإنه يمكن التحويل إلى نصوص أو أرقام. ولذلك إذا استقبلت علامة ال `+` كائنا فإنها تستخدم `"التصرف الإفتراضي"`. @@ -62,6 +106,7 @@ let total = obj1 + obj2; if (user == 1) { ... }; ``` +<<<<<<< HEAD المقارنه باستخدام علامات الأكبر من أو الأصغر من مثل `<` `>`، يمكنها التعامل مع الأرقام والنصوص أيضا ولكنها مع ذلك تستخدم التحويل إلى رقم وليس الطريقه الافتراضيه، وهذا لأسباب متأصله historical reasons. لا نحتاج إلى تذكر كل هذه التفاصيل الغريبه لأن كل الكائنات الموجوده عدا (`Date` والذي سيتم شرحه قريبا) يتم تحويلها باستخدام `"الطريقه الإفتراضيه"` مثل طريقة التحويل إلى رقم. @@ -72,14 +117,29 @@ if (user == 1) { ... }; لا توجد طريقة التحويل إلى "قيمه منطقيه" (لأن كل الكائنات قيمتها `true` عن تحويلها إلى قيمه منطقيه). وإذا تعاملنا مع `"الطريقه الإفتراضيه"` و `"الرقم"` بطريقة مشابهة مثل كل الطرق الموجوده فسيكون هناك طريقتين فقط. ```` +======= +In practice though, things are a bit simpler. + +All built-in objects except for one case (`Date` object, we'll learn it later) implement `"default"` conversion the same way as `"number"`. And we probably should do the same. + +Still, it's important to know about all 3 hints, soon we'll see why. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 **عند القيام بالتحويل، تقوم جافا سكريبت باستدعاء ثلاث دوال:** +<<<<<<< HEAD 1. استدعاء `obj[Symbol.toPrimitive](hint)` - وهو رمز موجود بالفعل (built-in)، وهذا فى حالة وجود هذه الدالة. 2. فى حالة عدم وجودها وطانت الطريقه هى التحويل إلى نص - استخدام `obj.toString()` و `obj.valueOf()`، أيهم موجود. 3. غير ذلك، إذا كانت الطريقه هي `"الطريقه الإفتراضيه"` أو `"الرقم"` - استخدام `obj.valueOf()` و `obj.toString()`، أيهم موجود. +======= +1. Call `obj[Symbol.toPrimitive](hint)` - the method with the symbolic key `Symbol.toPrimitive` (system symbol), if such method exists, +2. Otherwise if hint is `"string"` + - try calling `obj.toString()` or `obj.valueOf()`, whatever exists. +3. Otherwise if hint is `"number"` or `"default"` + - try calling `obj.valueOf()` or `obj.toString()`, whatever exists. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## Symbol.toPrimitive @@ -87,12 +147,19 @@ if (user == 1) { ... }; ```js obj[Symbol.toPrimitive] = function(hint) { - // must return a primitive value + // here goes the code to convert this object to a primitive + // it must return a primitive value // hint = one of "string", "number", "default" }; ```` +<<<<<<< HEAD على سبيل المثال, يطبق هذه الطريقه الكائن `user`: +======= +If the method `Symbol.toPrimitive` exists, it's used for all hints, and no more methods are needed. + +For instance, here `user` object implements it: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let user = { @@ -111,6 +178,7 @@ alert(+user); // hint: number -> 1000 alert(user + 500); // hint: default -> 1500 ``` +<<<<<<< HEAD كما نرى من المثال، فإن الكائن `user` يتحول إلى نص معبر أو إلى كم النقود بناءًا على طريقة التحويل نفسها. فإن الطريقه `user[Symbol.toPrimitive]` تتعامل مع كل طرق التحويل. ## toString/valueOf @@ -121,6 +189,18 @@ alert(user + 500); // hint: default -> 1500 - `toString -> valueOf` فى الطريقه النصيه. - `valueOf -> toString` غير ذلك. +======= +As we can see from the code, `user` becomes a self-descriptive string or a money amount, depending on the conversion. The single method `user[Symbol.toPrimitive]` handles all conversion cases. + +## toString/valueOf + +If there's no `Symbol.toPrimitive` then JavaScript tries to find methods `toString` and `valueOf`: + +- For the `"string"` hint: call `toString` method, and if it doesn't exist or if it returns an object instead of a primitive value, then call `valueOf` (so `toString` has the priority for string conversions). +- For other hints: call `valueOf`, and if it doesn't exist or if it returns an object instead of a primitive value, then call `toString` (so `valueOf` has the priority for maths). + +Methods `toString` and `valueOf` come from ancient times. They are not symbols (symbols did not exist that long ago), but rather "regular" string-named methods. They provide an alternative "old-style" way to implement the conversion. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 هذه الدوال لابد أن تقوم بإرجاع قيمه فردية. فإذا قامت هاتان الدالتان بإرجاع كائن فسيتم تجاهله. @@ -140,9 +220,15 @@ alert(user.valueOf() === user); // true لذلك إذا حاولنا أن نستخدم الكائن كنص، كما فى حالة استخدام الداله النصيه `alert` سنرى بشكل افتراضي `[object object]`. +<<<<<<< HEAD الداله `valueOf` تم ذكرها هنا فقط لإكمال المعلومات ولتجنب أى التباس. فكما ترى فإن هذه الداله تقوم بإرجاع الكائن نفسه وبالتالى يتم تجاهله. لا تسأل لماذا فهذا لأسباب متأصله historical reasons. ولذلك يمكننا اعتبار أنها غير موجوده. هيا نقوم باستخدام هذه الدوال. +======= +The default `valueOf` is mentioned here only for the sake of completeness, to avoid any confusion. As you can see, it returns the object itself, and so is ignored. Don't ask me why, that's for historical reasons. So we can assume it doesn't exist. + +Let's implement these methods to customize the conversion. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال، فإن الكائن `user` هنا يقوم بنفس التصرف أعلاه عند استخدام خليط من `toString` و `valueOf` بدلًا من `Symbol.toPrimitive`: @@ -186,11 +272,19 @@ alert(user + 500); // toString -> John500 فى حالة غياب `Symbol.toPrimitive` و `valueOf` فإن `toString` ستقوم بالتعامل مع كل حالات التحويل إلى قيم فرديه. +<<<<<<< HEAD ## أنواع القيم المسترجعه +======= +### A conversion can return any primitive type +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 هناك شئ مهم يجب أن تعرفه وهو أن كل طرق التحويل إلى قيم مفرده لا يجب بالضروره أن تقوم بإرجاع نفس نوع القيمه المفرده المحوَّله إليه. +<<<<<<< HEAD فلا يوجد ضمانه إذا كانت `toString` ستقوم بإرجاع نص بالتحديد أو حتى `Symbol.toPrimitive` ستقوم بإرجاع رقم فى طريقة `"الرقم"`. +======= +There is no control whether `toString` returns exactly a string, or whether `Symbol.toPrimitive` method returns a number for the hint `"number"`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 الأمر الوحيد الذى يمكن ضمانه والإلزامى هو أن هذه الدوال يجب أن تقوم بإرجاع يمة مفردة لا كائنًا. @@ -199,7 +293,11 @@ alert(user + 500); // toString -> John500 `toString` or `valueOf` قامت بإرجاع كائن، فلا يوجد خطأ يظهر، بل يتم تجاه النتيجه فقط كأن شيئًا لم يكن. وذلك لأنه فى الماضي لم يكن هناك مفهوم جيد للخطأ فى جافا سكريبت. +<<<<<<< HEAD على النقيض، فإن `Symbol.toPrimitive` *يجب* أن تقوم بإرجاع قيمة مفرده، وإلا سيكون هناك خطأ. +======= +In contrast, `Symbol.toPrimitive` is stricter, it *must* return a primitive, otherwise there will be an error. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ## التحويلات الإضافيه @@ -208,8 +306,14 @@ alert(user + 500); // toString -> John500 إذا استخدمنا كائنين كعملين رياضيين فسيكون هناك مرحلتين: +<<<<<<< HEAD 1. تحويل إلى الكائن إلى قيمه مفردة. 2. إذا كانت نتيجة التحويل ليست من النوع الصحيح فسيتم تحويلها. +======= +If we pass an object as an argument, then there are two stages of calculations: +1. The object is converted to a primitive (using the rules described above). +2. If the necessary for further calculations, the resulting primitive is also converted. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال: @@ -245,6 +349,7 @@ alert(obj + 2); // 22 ("2" + 2), conversion to primitive returned a string => co هناك 3 أنواع من طرق التحويل: +<<<<<<< HEAD - `"النص"` (ويحدث ذلك عند استخدام دالة التنبيه `alert` والتى تتوقع نصًا). - `"الرقم"` (فى العمليات الحسابيه). - `"الطريقة الإفتراضيه"` (فى بعض العمليات). @@ -252,9 +357,18 @@ alert(obj + 2); // 22 ("2" + 2), conversion to primitive returned a string => co يوضح المصدر أى عملية تستخدم أى طريقه. وهناك القليل من العمليات التي "لا تعلم ما نوع العامل الذي ستستقبله" وتستخدم `"الطريقه الإفتراضيه"`. وعادةً ما يتم استخدام `"الطريقة الإفتراضيه"` مع الكائنات الموجوده بالفعل كما يتم التعامل مع `"الأرقام"`, ولذلك عمليا فإن الطريقتين الأخيرتين يمكن ضمهما معًا. +======= +There are 3 types (hints) of it: +- `"string"` (for `alert` and other operations that need a string) +- `"number"` (for maths) +- `"default"` (few operators, usually objects implement it the same way as `"number"`) + +The specification describes explicitly which operator uses which hint. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 تتم طريقة التحويل كالآتى: +<<<<<<< HEAD 1. استدعاء الداله `obj[Symbol.toPrimitive](hint)` فى حالة وجودها, 2. غير ذلك إذا كانت الظريقه `"نصًا"` - استخدام `obj.toString()` و `obj.valueOf()` فى حالة وجود أي منهم. @@ -262,3 +376,14 @@ alert(obj + 2); // 22 ("2" + 2), conversion to primitive returned a string => co - استخدام `obj.valueOf()` أو `obj.toString()` فى حالة وجود أى منهم. ويكفى عمليًا استخدام `obj.toString()` لكل التحويلات والتى تقوم بإرجاع قيمة يمكن قرائتها من أجل الطباعة أو البحث عن الأخطاء. +======= +1. Call `obj[Symbol.toPrimitive](hint)` if the method exists, +2. Otherwise if hint is `"string"` + - try calling `obj.toString()` or `obj.valueOf()`, whatever exists. +3. Otherwise if hint is `"number"` or `"default"` + - try calling `obj.valueOf()` or `obj.toString()`, whatever exists. + +All these methods must return a primitive to work (if defined). + +In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for string conversions that should return a "human-readable" representation of an object, for logging or debugging purposes. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md b/1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md index 614d98912..7ff0a1dac 100644 --- a/1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md +++ b/1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md @@ -15,5 +15,9 @@ str.test = 5; alert(str.test); ``` +<<<<<<< HEAD كيف تعتقد أنها ستنجح؟ ماذا سيتضح؟ +======= +What do you think, will it work? What will be shown? +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/05-data-types/01-primitives-methods/article.md b/1-js/05-data-types/01-primitives-methods/article.md index 7c7671d2d..f96d8c590 100644 --- a/1-js/05-data-types/01-primitives-methods/article.md +++ b/1-js/05-data-types/01-primitives-methods/article.md @@ -40,8 +40,13 @@ Objects "اثقل" من الأساليب البدائية. وهي تتطلب م هنا التناقض الذي واجه صانع جافا سكريبت: +<<<<<<< HEAD - هناك العديد من الأشياء التي يمكن أن يفعلها الشخص بالأسلوب البدائي مثل string أو number. سيكون من الرائع استخدامهم كا methods. - الأساليب البدائية يجب أن تكون سريعة وخفيفة بقدر الإمكان. +======= +- There are many things one would want to do with a primitive, like a string or a number. It would be great to access them using methods. +- Primitives must be as fast and lightweight as possible. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 إن الحل يبدو غريبا بعض الشيء، ولكن ها هو: @@ -49,7 +54,11 @@ Objects "اثقل" من الأساليب البدائية. وهي تتطلب م 2. تسمح اللغة بالوصول الي ال methods و الخصائص الخاصة ب strings, numbers, booleans و symbols. 3. ولكي يعمل ذلك، يتم إنشاء "object wrapper" خاص يوفر الوظائف الإضافية، ثم يتم تدميره. +<<<<<<< HEAD ال "object wrappers" تختلف لكل نوع بدائي و تدعى: `String`, `Number`, `Boolean` و `Symbol`. وبالتالي، فإنها توفر مجموعات مختلفة من methods. +======= +The "object wrappers" are different for each primitive type and are called: `String`, `Number`, `Boolean`, `Symbol` and `BigInt`. Thus, they provide different sets of methods. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال , هنالك a string method [()str.toUpperCase](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) التي ترجع capitalized `str`. @@ -106,9 +115,16 @@ if (zero) { // zero is true, because it's an object } ``` +<<<<<<< HEAD من ناحية أخرى, استخدام نفس ال functions `String/Number/Boolean` بدون `new` هو شيء سَليم و مفيد. يحولون قيمة إلى النوع المقابل: إلي a string, a number, or a boolean (primitive - نوع بدائي). علي سبيل المثال, هذا صحيح تماما: +======= +On the other hand, using the same functions `String/Number/Boolean` without `new` is totally fine and useful thing. They convert a value to the corresponding type: to a string, a number, or a boolean (primitive). + +For example, this is entirely valid: + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js let num = Number("123"); // convert a string to number ``` diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index 0198ff2ee..a8031b24e 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -2,10 +2,16 @@ يوجد نوعان من الأعداد في JavaScript: +<<<<<<< HEAD 1. أعداد عادية تخزَّن بصيغة 64-بت [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision)، تُعرف أيضًا ب "الأعداد العشرية مضاعفة الدقة" (double precision floating point numbers). هذا النوع هو ما سنستعلمه أغلب الوقت وسنسلط عليه الضوء في هذا الفصل. 2. أعداد صحيحة كبيرة (BigInt numbers) تمثِّل عددًا صحيحًا متغير الحجم، إذ قد نلجأ إليها أحيانًا لأن النوع السابق لا يمكن أن يتجاوز القيمة 2^53 أو أن تقل عن -2^53، وسنخصص لهذا النوع فصلًا خاصًا به نظرًا للحاجة إليه في حالات خاصة. حاليًا، لِنتوسع عن ما نعرفه عنها، وننتقل إلى الحديث عن النوع الأول، الأعداد العادية. +======= +1. Regular numbers in JavaScript are stored in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), also known as "double precision floating point numbers". These are numbers that we're using most of the time, and we'll talk about them in this chapter. + +2. BigInt numbers represent integers of arbitrary length. They are sometimes needed because a regular integer number can't safely exceed (253-1) or be less than -(253-1), as we mentioned earlier in the chapter . As bigints are used in few special areas, we devote them a special chapter . +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## طرق أخرى لكتابة عدد @@ -21,7 +27,7 @@ We also can use underscore `_` as the separator: let billion = 1_000_000_000; ``` -Here the underscore `_` plays the role of the "syntactic sugar", it makes the number more readable. The JavaScript engine simply ignores `_` between digits, so it's exactly the same one billion as above. +Here the underscore `_` plays the role of the "[syntactic sugar](https://en.wikipedia.org/wiki/Syntactic_sugar)", it makes the number more readable. The JavaScript engine simply ignores `_` between digits, so it's exactly the same one billion as above. In real life though, we try to avoid writing long sequences of zeroes. We're too lazy for that. We'll try to write something like `"1bn"` for a billion or `"7.3bn"` for 7 billion 300 million. The same is true for most large numbers. @@ -35,12 +41,19 @@ let billion = 1e9; // بليون، حرفيًا: 1 وجانبه 9 أصفار In other words, `e` multiplies the number by `1` with the given zeroes count. ```js +<<<<<<< HEAD 1e3 = 1 * 1000 // e3 means *1000 1.23e6 = 1.23 * 1000000 // e6 means *1000000 ```` +======= +1e3 === 1 * 1000; // e3 means *1000 +1.23e6 === 1.23 * 1000000; // e6 means *1000000 +``` +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لنكتب الآن شيئَا صغيرًا جدًا. مثلًا، جزء من المليون من الثانية: +<<<<<<< HEAD ``` let ms = 0.000001; ``` @@ -52,15 +65,40 @@ let ms = 1e-6; // ستة أصفار على يسار 1 ``` إن قمنا بعد الأصفار في `0.000001`، سنجد عددها 6. لذا يكون الرقم `1e-6`. +======= +```js +let mсs = 0.000001; +``` + +Just like before, using `"e"` can help. If we'd like to avoid writing the zeroes explicitly, we could write the same as: + +```js +let mcs = 1e-6; // five zeroes to the left from 1 +``` + +If we count the zeroes in `0.000001`, there are 6 of them. So naturally it's `1e-6`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 بمعنى آخر، وجود رقم سالب بعد `"e"` يعني القسمة على 1 متبوعًا بِعدد الأصفار المعطى: +<<<<<<< HEAD ``` // -3 بالقسمة على 1 متبوعًا ب 3 أصفار 1e-3 = 1 / 1000 (=0.001) // -6 بالقسمة على 1 متبوعًا ب 6 أصفار 1.23e-6 = 1.23 / 1000000 (=0.00000123) +======= +```js +// -3 divides by 1 with 3 zeroes +1e-3 === 1 / 1000; // 0.001 + +// -6 divides by 1 with 6 zeroes +1.23e-6 === 1.23 / 1000000; // 0.00000123 + +// an example with a bigger number +1234e-2 === 1234 / 100; // 12.34, decimal point moves 2 times +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ### الأعداد الست عشرية، والثنائية والثمانية @@ -102,8 +140,22 @@ alert( num.toString(2) ); // 11111111 - `base=2`: يستخدم بكثرة في تصحيح العمليات الدقيقة، يمكن أن يحوي الرقمين `0` أو `1`. - `base=36`: هو الحد الأعلى، يمكن أن يحوي الأرقام `0..9` أو الأحرُف `A..Z`. يمكن استخدام جميع الأحرف اللاتينية لتمثيل عدد. قد يبدو أمرًا ممتعًا لكن يكون مفيدًا في حال احتجنا لتحويل معرف عددي طويل إلى عدد أقصر، مثلًا، لتقصير رابط url. يمكن تمثيله بالنظام العددي ذي الأساس `36`: +<<<<<<< HEAD ``` alert( 123456..toString(36) ); // 2n9c +======= + ```js run + alert( 123456..toString(36) ); // 2n9c + ``` + +```warn header="Two dots to call a method" +Please note that two dots in `123456..toString(36)` is not a typo. If we want to call a method directly on a number, like `toString` in the example above, then we need to place two dots `..` after it. + +If we placed a single dot: `123456.toString(36)`, then there would be an error, because JavaScript syntax implies the decimal part after the first dot. And if we place one more dot, then JavaScript knows that the decimal part is empty and now goes the method. + +Also could write `(123456).toString(36)`. + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ## Rounding @@ -141,10 +193,16 @@ For instance, we have `1.2345` and want to round it to 2 digits, getting only `1 يمكن كتابتها بهذه الطريقة أيضًا `‎(123456).toString(36)‎`. +<<<<<<< HEAD ```` ## التقريب (Rounding) أحد الخصائص الأكثر استخدامًا عند التعامل مع الأعداد هي التقريب. يوجد العديد من الدوال المدمجة للتقريب: +======= + For example, to round the number to the 2nd digit after the decimal, we can multiply the number by `100`, call the rounding function and then divide it back. + ```js run + let num = 1.23456; +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 alert( Math.round(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23 ``` @@ -162,19 +220,33 @@ For instance, we have `1.2345` and want to round it to 2 digits, getting only `1 مثلًا، لدينا العدد `1.2345` ونريد تقريب إلى خانتين لنحصل على `1.23` فقط. يوجد طريقتين للقيام بذلك: +<<<<<<< HEAD 1- الضرب والقسمة: +======= + Please note that the result of `toFixed` is a string. If the decimal part is shorter than required, zeroes are appended to the end: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 مثلًا، لتقريب الرقم إلى الخانة الثانية بعد الفاصلة العشرية، يمكننا ضرب العدد في `100`، ثم نستدعي تابع التقريب ثم نقسم على نفس العدد. +<<<<<<< HEAD ```` +======= + We can convert it to a number using the unary plus or a `Number()` call, e.g write `+num.toFixed(5)`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 let num = 1.23456; +<<<<<<< HEAD alert( Math.floor(num \* 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23 ``` 2- يقرب التابع [`toFixed(n)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) العدد المستدعى معه إلى الخانة `n` بعد الفاصلة العشرية ويُرجِع تمثيلًا نصيًا للنتيجة. +======= +Internally, a number is represented in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), so there are exactly 64 bits to store a number: 52 of them are used to store the digits, 11 of them store the position of the decimal point, and 1 bit is for the sign. + +If a number is really huge, it may overflow the 64-bit storage and become a special numeric value `Infinity`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` @@ -183,7 +255,11 @@ alert( num.toFixed(1) ); // "12.3" ``` +<<<<<<< HEAD يعمل التابع على تقريب العدد للأكبر أو الأصغر وفقًا إلى أقرب قيمة، مثل التابع `Math.round`: +======= +Consider this (falsy!) equality test: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` @@ -196,8 +272,12 @@ alert( num.toFixed(1) ); // "12.4" ``` +<<<<<<< HEAD let num = 12.34; alert( num.toFixed(5) ); // "12.34000", أصفار مضافة لجعل عدد الخانات 5 +======= +Ouch! Imagine you're making an e-shopping site and the visitor puts `$0.10` and `$0.20` goods into their cart. The order total will be `$0.30000000000000004`. That would surprise anyone. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` @@ -205,7 +285,11 @@ alert( num.toFixed(5) ); // "12.34000", أصفار مضافة لجعل عدد ا ## حسابات غير دقيقة يُمَثَّل العدد داخليًا بصيغة 64-بِت [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision)، لذا يوجد 64 بِت لتخزين العدد: تستخدم 52 منها لتخزين أرقام العدد، و 11 منها لتخزين مكان الفاصلة العشرية (تكون أصفارًا للاعداد الصحيحة)، و 1 بِت لإشارة العدد. +<<<<<<< HEAD إن كان العدد كبيرًا جدًا، فَسيزداد عن مساحة التخزين 64-بِت، معطيًا ما لا نهاية: +======= +What is `0.1`? It is one divided by ten `1/10`, one-tenth. In decimal numeral system such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` @@ -261,7 +345,11 @@ alert( 0.1.toFixed(20) ); // 0.10000000000000000555 ```js run let sum = 0.1 + 0.2; +<<<<<<< HEAD alert(sum.toFixed(2)); // 0.30 +======= +alert( sum.toFixed(2) ); // "0.30" +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` يرجى ملاحظة أن `toFixed` تُرجِع نصًا دائمًا. وتتأكد من وجود خانتين فقط بعد العلامة العشرية. هذا يجعل الأمر مريحًا إن كان لدينا موقع تسوق إلكتروني وأردنا عرض `$0.30`. يمكننا استخدام الجمع الأحادي في الحالات الأخرى لتحويله إلى عدد: @@ -314,10 +402,21 @@ alert( 9999999999999999 ); // يظهر 10000000000000000 - `isNaN(value)‎` يُحوِّل المُعامل إلى عدد ثم يفحص ما إن كان `NaN`: +<<<<<<< HEAD ``` alert( isNaN(NaN) ); // true alert( isNaN("str") ); // true ``` +======= +- `isNaN(value)` converts its argument to a number and then tests it for being `NaN`: + + ```js run + alert( isNaN(NaN) ); // true + alert( isNaN("str") ); // true + ``` + + But do we need this function? Can't we just use the comparison `=== NaN`? Unfortunately not. The value `NaN` is unique in that it does not equal anything, including itself: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لكن هل نحتاج لهذا التابع؟ أليس من الممكن استخدام الموازنة فقط `=== NaN`? الإجابة للأسف هي لا. القيمة `NaN` هي فريدة ولا يمكن أن تساوي أي شيء، حتى نفسها: @@ -342,16 +441,57 @@ let num = +prompt("Enter a number", ''); alert( isFinite(num) ); ``` +<<<<<<< HEAD يرجى ملاحظة أن الفراغ أو المسافة الواحدة تُعامل معاملة الصفر `0` في جميع التوابع العددية بما فيها `isFinite`. ```smart header="المقارنة باستخدام `Object.is`" +======= +Please note that an empty or a space-only string is treated as `0` in all numeric functions including `isFinite`. + +````smart header="`Number.isNaN` and `Number.isFinite`" +[Number.isNaN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) and [Number.isFinite](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) methods are the more "strict" versions of `isNaN` and `isFinite` functions. They do not autoconvert their argument into a number, but check if it belongs to the `number` type instead. + +- `Number.isNaN(value)` returns `true` if the argument belongs to the `number` type and it is `NaN`. In any other case it returns `false`. + + ```js run + alert( Number.isNaN(NaN) ); // true + alert( Number.isNaN("str" / 2) ); // true + + // Note the difference: + alert( Number.isNaN("str") ); // false, because "str" belongs to the string type, not the number type + alert( isNaN("str") ); // true, because isNaN converts string "str" into a number and gets NaN as a result of this conversion + ``` + +- `Number.isFinite(value)` returns `true` if the argument belongs to the `number` type and it is not `NaN/Infinity/-Infinity`. In any other case it returns `false`. + + ```js run + alert( Number.isFinite(123) ); // true + alert( Number.isFinite(Infinity) ); //false + alert( Number.isFinite(2 / 0) ); // false + + // Note the difference: + alert( Number.isFinite("123") ); // false, because "123" belongs to the string type, not the number type + alert( isFinite("123") ); // true, because isFinite converts string "123" into a number 123 + ``` + +In a way, `Number.isNaN` and `Number.isFinite` are simpler and more straightforward than `isNaN` and `isFinite` functions. In practice though, `isNaN` and `isFinite` are mostly used, as they're shorter to write. +```` + +```smart header="Comparison with `Object.is`" +There is a special built-in method `Object.is` that compares values like `===`, but is more reliable for two edge cases: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يوجد تابع خاص مدمج في اللغة يدعى [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) يوزان القيم كما `===` لكنه أكثر موثوقية لسببين: 1. أنه يعمل مع `NaN`: أي `Object.is(NaN, NaN) === true` وهذا أمر جيد. 2. القيمتان `0` و `-0` مختلفتان: `Object.is(0, -0) === false`، الأمر صحيح تقنيًا، لأن العدد لديه إشارة داخليًا مما يجعل القيم مختلفة حتى لو كانت باقي الخانات أصفارًا. +<<<<<<< HEAD يكون التابع `Object.is(a, b)‎` نفس `a === b` في باقي الحالات. +======= +We mention `Object.is` here, because it's often used in JavaScript specification. When an internal algorithm needs to compare two values for being exactly the same, it uses `Object.is` (internally called [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)). +``` +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 تُستخدم طريقة الموازنة هذه غالبًا في توصيف JavaScript. عندما تحتاج خوارزمية لموازنة كون قيمتين متطابقتان تمامًا فإنها تستخدم `Object.is` (تُسَمَّى داخليًا القيمة ذاتها "[SameValue](https://tc39.github.io/ecma262/#sec-samevalue)"). @@ -404,8 +544,13 @@ alert(parseInt('2n9c', 36)); // 123456 alert( Math.random() ); // ... (أي رقم عشوائي) ``` +<<<<<<< HEAD `Math.max(a, b, c...)` / `Math.min(a, b, c...)` : تُرجِع القيمة الأكبر أو الأصغر من المُعامِلات +======= +`Math.max(a, b, c...)` and `Math.min(a, b, c...)` +: Returns the greatest and smallest from the arbitrary number of arguments. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run alert( Math.max(3, 5, -10, 0, 1) ); // 5 @@ -434,7 +579,18 @@ For different numeral systems: - `parseInt(str, base)` parses the string `str` into an integer in numeral system with given `base`, `2 ≤ base ≤ 36`. - `num.toString(base)` converts a number to a string in the numeral system with the given `base`. +<<<<<<< HEAD ## الملخص +======= +For regular number tests: + +- `isNaN(value)` converts its argument to a number and then tests it for being `NaN` +- `Number.isNaN(value)` checks whether its argument belongs to the `number` type, and if so, tests it for being `NaN` +- `isFinite(value)` converts its argument to a number and then tests it for not being `NaN/Infinity/-Infinity` +- `Number.isFinite(value)` checks whether its argument belongs to the `number` type, and if so, tests it for not being `NaN/Infinity/-Infinity` + +For converting values like `12pt` and `100px` to a number: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لكتابة أعداد كبيرة: - أضِف `"e"` مع عدد الأصفار الخاصة بالعدد المطلوب، مثل: `123e6` هو `123` مع 6 أصفار. diff --git a/1-js/05-data-types/03-string/article.md b/1-js/05-data-types/03-string/article.md index e83e5aa67..909f7c59d 100644 --- a/1-js/05-data-types/03-string/article.md +++ b/1-js/05-data-types/03-string/article.md @@ -47,7 +47,11 @@ let guestList = "Guests: // Error: Unexpected token ILLEGAL * John"; ``` +<<<<<<< HEAD أتى استخدام علامات الاقتباس الفردية والثنائية في أوقات مبكرة من إنشاء اللغة، عندما لم يُؤخَذ بالحسبان الحاجة إلى نص متعدد الأسطر. ظهرت الفاصلة العلوية المائلة مؤخرًا ولذا فإنها متعددة الاستعمالات. +======= +Single and double quotes come from ancient times of language creation, when the need for multiline strings was not taken into account. Backticks appeared much later and thus are more versatile. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 Backticks also allow us to specify a "template function" before the first backtick. The syntax is: func`string`. The function `func` is called automatically, receives the string and embedded expressions and can process them. This is called "tagged templates". This feature makes it easier to implement custom templating, but is rarely used in practice. You can read more about it in the [manual](mdn:/JavaScript/Reference/Template_literals#Tagged_templates). @@ -92,7 +96,7 @@ alert(str1 == str2); // true | Character | Description | |-----------|-------------| |`\n`|New line| -|`\r`|Carriage return: not used alone. Windows text files use a combination of two characters `\r\n` to represent a line break. | +|`\r`|In Windows text files a combination of two characters `\r\n` represents a new break, while on non-Windows OS it's just `\n`. That's for historical reasons, most Windows software also understands `\n`. | |`\'`, `\"`|Quotes| |`\\`|Backslash| |`\t`|Tab| @@ -222,7 +226,7 @@ alert( 'Interface'.toLowerCase() ); // interface أو إن أردنا بتغيير حالة حرف واحد فقط: -```js +```js run alert( 'Interface'[0].toLowerCase() ); // 'i' ``` @@ -370,8 +374,8 @@ alert( "Widget".includes("id", 3) ); // false, from position 3 there is no "id" يعمل التابعان [str.startsWith](mdn:js/String/startsWith) و [str.endsWith](mdn:js/String/endsWith) بما هو واضح من مسمياتهما، "سلسلة نصية تبدأ بـ"، و "سلسلة نصية تنتهي بـ" على التوالي: ```js run -alert( "Widget".startsWith("Wid") ); // true, "Widget" starts with "Wid" -alert( "Widget".endsWith("get") ); // true, "Widget" ends with "get" +alert( "*!*Wid*/!*get".startsWith("Wid") ); // true, "Widget" starts with "Wid" +alert( "Wid*!*get*/!*".endsWith("get") ); // true, "Widget" ends with "get" ``` ## جلب جزء من نص @@ -450,7 +454,13 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and alert( str.substr(-4, 2) ); // حرفين ابتداءًا من الموضع الرابع ``` +<<<<<<< HEAD لِنُلَخِّص هذه التوابع لتجنب الخلط بينها: +======= + This method resides in the [Annex B](https://tc39.es/ecma262/#sec-string.prototype.substr) of the language specification. It means that only browser-hosted Javascript engines should support it, and it's not recommended to use it. In practice, it's supported everywhere. + +Let's recap these methods to avoid any confusion: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 | التابع | يقتطع ... | المواضع السالبة | | ----------------------- | ------------------------------------------------------- | ---------------------- | @@ -532,7 +542,12 @@ alert( str ); - تأتي الأحرف الصغيرة بعد الأحرف الكبيرة دائمًا لأن رموزها العددية دائمًا أكبر. - تكون بعض الأحرف مثل `Ö` بعيدة عن الأحرف الهجائية. هنا، قيمة الحرف هذا أكبر من أي حرف بين `a` و `z`. +<<<<<<< HEAD ### موازنات صحيحة +======= +- All lowercase letters go after uppercase letters because their codes are greater. +- Some letters like `Ö` stand apart from the main alphabet. Here, its code is greater than anything from `a` to `z`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ### Correct comparisons [#correct-comparisons] @@ -612,7 +627,11 @@ alert( '𝒳'.charCodeAt(1).toString(16) ); // dcb3, between 0xdc00 and 0xdfff ### علامات التشكيل وتوحيد الترميز +<<<<<<< HEAD يوجد حروف مركبة في الكثير من اللغات والتي تتكون من الحرف الرئيسي مع علامة فوقه/تحته. مثلًا، يمكن للحرف `a` أن يكون أساسًا للأحرف التالية: `àáâäãåā`. لدى معظم الحروف المركبة رمزها الخاص بها في جدول UTF-16. لكن ليس جميعها، وذلك لوجود الكثير من الاحتمالات. +======= +In many languages, there are symbols that are composed of the base character with a mark above/under it. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 To support arbitrary compositions, UTF-16 allows us to use several Unicode characters: the base character followed by one or many "mark" characters that "decorate" it. diff --git a/1-js/05-data-types/04-array/3-call-array-this/task.md b/1-js/05-data-types/04-array/3-call-array-this/task.md index 2910d2b31..80d828491 100644 --- a/1-js/05-data-types/04-array/3-call-array-this/task.md +++ b/1-js/05-data-types/04-array/3-call-array-this/task.md @@ -11,7 +11,7 @@ let arr = ["a", "b"]; arr.push(function() { alert( this ); -}) +}); arr[2](); // ? ``` diff --git a/1-js/05-data-types/04-array/article.md b/1-js/05-data-types/04-array/article.md index 29a027ded..a46dead4b 100644 --- a/1-js/05-data-types/04-array/article.md +++ b/1-js/05-data-types/04-array/article.md @@ -90,6 +90,38 @@ let fruits = [ يسهّل نمط "الفاصلة اللاحقة" إدراج / إزالة العناصر ، لأن جميع الخطوط متشابهة.```` +## Get last elements with "at" + +[recent browser="new"] + +Let's say we want the last element of the array. + +Some programming languages allow to use negative indexes for the same purpose, like `fruits[-1]`. + +Although, in JavaScript it won't work. The result will be `undefined`, because the index in square brackets is treated literally. + +We can explicitly calculate the last element index and then access it: `fruits[fruits.length - 1]`. + +```js run +let fruits = ["Apple", "Orange", "Plum"]; + +alert( fruits[fruits.length-1] ); // Plum +``` + +A bit cumbersome, isn't it? We need to write the variable name twice. + +Luckily, there's a shorter syntax: `fruits.at(-1)`: + +```js run +let fruits = ["Apple", "Orange", "Plum"]; + +// same as fruits[fruits.length-1] +alert( fruits.at(-1) ); // Plum +``` + +In other words, `arr.at(i)`: +- is exactly the same as `arr[i]`, if `i >= 0`. +- for negative values of `i`, it steps back from the end of the array. ## وسائل pop/push, shift/unshift @@ -117,9 +149,15 @@ let fruits = [ بالنسبه للكومه(stacks), يتم استلام أحدث عنصر مدفوع أولاً ، وهذا ما يسمى بمبدأ LIFO (Last-In-First-Out). بالنسبة لقوائم الانتظار ، لدينا FIFO (First-In-First-Out). +<<<<<<< HEAD يمكن أن تعمل المصفوفات في JavaScript كقائمة انتظار وكمجموعة(stack). تتيح لك إضافة / إزالة عناصر من / إلى البداية أو النهاية. في علم الحاسوب يسمى هيكل البيانات الذي يسمح بذلك [deque](https://en.wikipedia.org/wiki/Double-ended_queue). +======= +Arrays in JavaScript can work both as a queue and as a stack. They allow you to add/remove elements, both to/from the beginning or the end. + +In computer science, the data structure that allows this, is called [deque](https://en.wikipedia.org/wiki/Double-ended_queue). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 **الأساليب التي تعمل مع نهاية المصفوفه:** @@ -134,6 +172,8 @@ let fruits = [ ;alert( fruits ) // البرتقال, التفاح ``` + Both `fruits.pop()` and `fruits.at(-1)` return the last element of the array, but `fruits.pop()` also modifies the array by removing it. + `push` : ألحق العنصر بنهاية المصفوفة: @@ -240,7 +280,11 @@ fruits.age = 25; // أنشئ الخاصيه باسم افتراضي fruits.shift(); // قم بأخذ عنصر من البدايه ``` +<<<<<<< HEAD لا يكفي أخذ العنصر وإزالته بالرقم `0`.يجب إعادة ترقيم العناصر الأخرى أيضًا. +======= +It's not enough to take and remove the element with the index `0`. Other elements need to be renumbered as well. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 `shift` هذه العمليه يجب ان تفعل 3 أشياء: @@ -357,11 +401,19 @@ alert( arr[3] ); // غيرمعرف: لذالك القيم لن تعد let arr = *!*new Array*/!*("الخ", "الكمثري", "التفاح"); ``` +<<<<<<< HEAD نادرًا ما يتم استخدامه ، لأن الأقواس المربعة `[] أقصر. أيضا هناك ميزة صعبة معها. +======= +It's rarely used, because square brackets `[]` are shorter. Also, there's a tricky feature with it. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 إذا تم استدعاء `مصفوفه جديده` باستخدام وسيله واحدة عبارة عن رقم ، فإنه ينشئ مصفوفة * بدون عناصر ، ولكن بالطول المحدد *. +<<<<<<< HEAD دعونا نرى كيف يمكن للمرء أن يطلق النار على قدمه: +======= +Let's see how one can shoot themselves in the foot: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let arr = new Array(2); //هل سينشئ مصفوفه مكونه من [2] ? @@ -371,9 +423,13 @@ alert( arr[0] ); // غير معرف! لا توجد عناصر. alert( arr.length ); // الطول 2 ``` +<<<<<<< HEAD في الكود أعلاه, `مصفوفه جديده(رقم)` تكون لديها كل العناصر `غير معرفه`. للتهرب من هذه المفاجآت ، نستخدم عادةً الأقواس المربعة ، إلا إذا كنا نعرف حقًا ما نقوم به. +======= +To avoid such surprises, we usually use square brackets, unless we really know what we're doing. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## مصفوفات متعدده الأبعاد @@ -432,7 +488,7 @@ Let's recall the rules: - If one of the arguments of `==` is an object, and the other one is a primitive, then the object gets converted to primitive, as explained in the chapter . - ...With an exception of `null` and `undefined` that equal `==` each other and nothing else. -The strict comparison `===` is even simpler, as it doesn't convert types. +The strict comparison `===` is even simpler, as it doesn't convert types. So, if we compare arrays with `==`, they are never the same, unless we compare two variables that reference exactly the same array. @@ -452,7 +508,7 @@ alert( 0 == [] ); // true alert('0' == [] ); // false ``` -Here, in both cases, we compare a primitive with an array object. So the array `[]` gets converted to primitive for the purpose of comparison and becomes an empty string `''`. +Here, in both cases, we compare a primitive with an array object. So the array `[]` gets converted to primitive for the purpose of comparison and becomes an empty string `''`. Then the comparison process goes on with the primitives, as described in the chapter : @@ -471,6 +527,7 @@ That's simple: don't use the `==` operator. Instead, compare them item-by-item i المصفوفات هو نوع خاص من الكائنات ، مناسب لتخزين وإدارة عناصر البيانات المطلوبة. +<<<<<<< HEAD - الإعلان: ```js @@ -482,11 +539,33 @@ That's simple: don't use the `==` operator. Instead, compare them item-by-item i ``` يؤدي استدعاء "مصفوفه جديده (رقم) " إلى إنشاء مصفوفة بطول معين ، ولكن بدون عناصر.. +======= +The declaration: + +```js +// square brackets (usual) +let arr = [item1, item2...]; + +// new Array (exceptionally rare) +let arr = new Array(item1, item2...); +``` + +The call to `new Array(number)` creates an array with the given length, but without elements. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 - الخاصية `length` هي طول المصفوفة أو ، على وجه الدقة ، آخر فهرس رقمي بالإضافة إلى واحد. يتم ضبطه تلقائيًا بواسطة طرق للمصفوفه. - إذا اختصرنا "الطول" يدويًا ، فسيتم اقتطاع المصفوفة. +<<<<<<< HEAD يمكننا استخدام مصفوفة كمادة مع العمليات التالية: +======= +Getting the elements: + +- we can get element by its index, like `arr[0]` +- also we can use `at(i)` method that allows negative indexes. For negative values of `i`, it steps back from the end of the array. If `i >= 0`, it works same as `arr[i]`. + +We can use an array as a deque with the following operations: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 - `push(...عناصر)`تضيف `العناصر` إلى النهاية. - `pop()`إزالة العنصر من النهاية وإعادته. diff --git a/1-js/05-data-types/05-array-methods/12-reduce-object/task.md b/1-js/05-data-types/05-array-methods/12-reduce-object/task.md index f89f17263..7f9320ac0 100644 --- a/1-js/05-data-types/05-array-methods/12-reduce-object/task.md +++ b/1-js/05-data-types/05-array-methods/12-reduce-object/task.md @@ -4,7 +4,11 @@ importance: 4 # انشاء مفاتيح خاصة بكائنات المصفوفة +<<<<<<< HEAD دعنا نقول أننا نستقبل مصفوفة خاصة بالمستخدمين داخل form مكونة `{id:..., name:..., age... }` +======= +Let's say we received an array of users in the form `{id:..., name:..., age:... }`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 اكتب دالة `groupById(arr)` لانشاء كائن منها يحتوى على `id` كمفتاح و عناصر المصفوفة كقيم diff --git a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/test.js b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/test.js index db32d9a11..241b74c6e 100644 --- a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/test.js +++ b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/test.js @@ -4,13 +4,13 @@ describe("filterRangeInPlace", function() { let arr = [5, 3, 8, 1]; - filterRangeInPlace(arr, 1, 4); + filterRangeInPlace(arr, 2, 5); - assert.deepEqual(arr, [3, 1]); + assert.deepEqual(arr, [5, 3]); }); it("doesn't return anything", function() { assert.isUndefined(filterRangeInPlace([1,2,3], 1, 4)); }); -}); \ No newline at end of file +}); diff --git a/1-js/05-data-types/05-array-methods/article.md b/1-js/05-data-types/05-array-methods/article.md index a706499bb..c0b1b70b9 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -231,11 +231,22 @@ arr.forEach(function(item, index, array) { للتوابِع arr.indexOf و arr.lastIndexOf و arr.includes نفس الصياغة ووظيفتها هي ذات وظيفة تلك بنسخة النصوص النصية، الفرق أنها هنا تتعامل مع العناصر بدل المحارف: +<<<<<<< HEAD - `arr.indexOf(item, from)` -- يبحث عن العنصر item بدءًا من الفهرس from، ويُعيد فهرسه حيث وجده. ولو لم يجده، يُعيد -1. - `arr.lastIndexOf(item, from)` -- نفسه، ولكن البحث يبدأ من اليمين وينتهي في اليسار.. - `arr.includes(item, from)` -- يبحث عن العنصر item بدءًا من الفهرس from، ويُعيد true إن وجدته. مثال: +======= +The methods [arr.indexOf](mdn:js/Array/indexOf) and [arr.includes](mdn:js/Array/includes) have the similar syntax and do essentially the same as their string counterparts, but operate on items instead of characters: + +- `arr.indexOf(item, from)` -- looks for `item` starting from index `from`, and returns the index where it was found, otherwise `-1`. +- `arr.includes(item, from)` -- looks for `item` starting from index `from`, returns `true` if found. + +Usually these methods are used with only one argument: the `item` to search. By default, the search is from the beginning. + +For instance: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let arr = [1, 0, false]; @@ -247,6 +258,7 @@ alert(arr.indexOf(null)); // -1 alert(arr.includes(1)); // true ``` +<<<<<<< HEAD لاحظ أنّ التوابِع تستعمل الموازنة بِـ ===. لذا لو كنّا نبحث عن false، فستبحث هي عن false نفسها وليس الصفر. لو أردت معرفة فيما كانت تحتوي المصفوفة على عنصر معيّن، ولا تريد معرفة فهرسه، فدالة arr.includes مناسبة لك. @@ -257,9 +269,37 @@ alert(arr.includes(1)); // true const arr = [NaN]; alert(arr.indexOf(NaN)); // ‫يُعيد ‎-1 (الصحيح هو 0 إلّا أنّ الموازنة === لا تعمل مع NaN) alert(arr.includes(NaN)); // true (الآن صحيح) +======= +Please note that `indexOf` uses the strict equality `===` for comparison. So, if we look for `false`, it finds exactly `false` and not the zero. + +If we want to check if `item` exists in the array, and don't need the exact index, then `arr.includes` is preferred. + +The method [arr.lastIndexOf](mdn:js/Array/lastIndexOf) is the same as `indexOf`, but looks for from right to left. + +```js run +let fruits = ['Apple', 'Orange', 'Apple'] + +alert( fruits.indexOf('Apple') ); // 0 (first Apple) +alert( fruits.lastIndexOf('Apple') ); // 2 (last Apple) +``` + +````smart header="The `includes` method handles `NaN` correctly" +A minor, but noteworthy feature of `includes` is that it correctly handles `NaN`, unlike `indexOf`: + +```js run +const arr = [NaN]; +alert( arr.indexOf(NaN) ); // -1 (wrong, should be 0) +alert( arr.includes(NaN) );// true (correct) +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` +That's because `includes` was added to JavaScript much later and uses the more up to date comparison algorithm internally. +```` +<<<<<<< HEAD ### البحث عبر find و findIndex +======= +### find and findIndex/findLastIndex +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لنقل أنّ لدينا مصفوفة من الكائنات، كيف نجد الكائن حسب شرط معيّن؟ @@ -300,7 +340,32 @@ alert(user.name); // John يمكنك ملاحظة بأنّا في المثال مرّرنا للتابِع find الدالة item => item.id == 1 وفيها وسيط واحد. هذا طبيعي فنادرًا ما نستعمل الوُسطاء البقية في هذه الدالة +<<<<<<< HEAD يتشابه التابِع [arr.findIndex](mdn:js/Array/findIndex) كثيرًا مع هذا، عدا على أنّه يُعيد فهرس العنصر الذي وجده بدل العنصر نفسه، ويُعيد ‎-1 لو لم يجد شيئًا. +======= +The [arr.findIndex](mdn:js/Array/findIndex) method has the same syntax, but returns the index where the element was found instead of the element itself. The value of `-1` is returned if nothing is found. + +The [arr.findLastIndex](mdn:js/Array/findLastIndex) method is like `findIndex`, but searches from right to left, similar to `lastIndexOf`. + +Here's an example: + +```js run +let users = [ + {id: 1, name: "John"}, + {id: 2, name: "Pete"}, + {id: 3, name: "Mary"}, + {id: 4, name: "John"} +]; + +// Find the index of the first John +alert(users.findIndex(user => user.name == 'John')); // 0 + +// Find the index of the last John +alert(users.findLastIndex(user => user.name == 'John')); // 3 +``` + + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ### الترشيح filter @@ -380,6 +445,11 @@ alert(arr); // *!*1, 15, 2*/!* على الدالة موازنة قيمتين اثنتين (أيًا كانتا) وإعادة الناتج: +<<<<<<< HEAD +======= +The function should compare two arbitrary values and return: + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js function compare(a, b) { if (a > b) return 1; // if the first value is greater than the second @@ -614,15 +684,18 @@ arr.reduce((sum, current) => sum + current); الشيفرة السابقة ستطلق خطأ، إذ لا يمكن استدعاء reduce مع مصفوفة فارغة دون قيمة أولية، وتحل المشكلة بتوفير قيمة أولية، وستعاد آنذاك. لذا خُذ هذه النصيحة وحدّد قيمة أولية دومًا. +<<<<<<< HEAD لا يختلف التابِع [arr.reduceRight](mdn:js/Array/reduceRight)عن هذا أعلاه إلا بأنّه يبدأ من اليمين وينتهي على اليسار. +======= +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## Array.isArray المصفوفات ليست نوعًا منفصلًا في اللغة، بل هي مبنيّة على الكائنات. لذا typeof لن تفيدك في التفريق بين الكائن العادي والمصفوفة: ```js run alert(typeof {}); // object -alert(typeof []); // same +alert(typeof []); // object (same) ``` …ولكن، المصفوفات تستعمل كثيرًا جدًا لدرجة تقديم تابِع خاص لهذا الغرض: Array.isArray(value)‎. يُعيد هذا التابِع true لو كانت value مصفوفة حقًا، وfalse لو لم تكن. @@ -715,11 +788,16 @@ A call to `users.filter(army.canJoin, army)` can be replaced with `users.filter( - `split/join` -- convert a string to array and back. - `reduce/reduceRight(func, initial)` -- calculate a single value over the array by calling `func` for each element and passing an intermediate result between the calls. +<<<<<<< HEAD - `map(func)` -- أنشِئ مصفوفة جديدة من نتائج استدعاء func لكلّ من عناصر المصفوفة. - `sort(func)` -- افرز المصفوفة كما هي وأعِد ناتج الفرز. - `reverse()` -- اعكس عناصر المصفوفة كما هي وأعِد ناتج العكس. - `split/join` -- حوّل المصفوفة إلى سلسلة نصية، والعكس أيضًا. - `reduce(func, initial)`-- احسب قيمة من المصفوفة باستدعاء func على كلّ عنصر فيها وتمرير الناتج بين كلّ استدعاء وآخر. +======= +- Additionally: + - `Array.isArray(value)` checks `value` for being an array, if so returns `true`, otherwise `false`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 * Additionally: - `Array.isArray(arr)` ‎ يفحص لو كانت `arr` مصفوفة أم لا. @@ -734,6 +812,7 @@ A call to `users.filter(army.canJoin, army)` can be replaced with `users.filter( These methods behave sort of like `||` and `&&` operators: if `fn` returns a truthy value, `arr.some()` immediately returns `true` and stops iterating over the rest of items; if `fn` returns a falsy value, `arr.every()` immediately returns `false` and stops iterating over the rest of items as well. We can use `every` to compare arrays: + ```js run function arraysEqual(arr1, arr2) { return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]); diff --git a/1-js/05-data-types/06-iterable/article.md b/1-js/05-data-types/06-iterable/article.md index c27c0f771..a4d159af9 100644 --- a/1-js/05-data-types/06-iterable/article.md +++ b/1-js/05-data-types/06-iterable/article.md @@ -33,7 +33,7 @@ To make the `range` object iterable (and thus let `for..of` work) we need to add 1. When `for..of` starts, it calls that method once (or errors if not found). The method must return an *iterator* -- an object with the method `next`. 2. Onward, `for..of` works *only with that returned object*. 3. When `for..of` wants the next value, it calls `next()` on that object. -4. The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the iteration is finished, otherwise `value` is the next value. +4. The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the loop is finished, otherwise `value` is the next value. Here's the full implementation for `range` with remarks: @@ -43,10 +43,18 @@ let range = { to: 5, }; +<<<<<<< HEAD // 1. عند تشغيل التكرار for..of فهي تقوم باستدعائ هذه الدالة range[Symbol.iterator] = function () { // ... وهذه الدالة تقوم بإرجاع الكائن المتكرر: // 2. بعد ذلك، يعمل التكرار for..of على هذا المتكرر فقط باحثًا عن القيم التالية +======= +// 1. call to for..of initially calls this +range[Symbol.iterator] = function() { + + // ...it returns the iterator object: + // 2. Onward, for..of works only with the iterator object below, asking it for next values +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 return { current: this.from, last: this.to, @@ -226,8 +234,13 @@ alert(arr.pop()); // World وهذا مايحدث أيضا للمتكرر: +<<<<<<< HEAD ```js // على فرض أن الكائن range مأخوذ من المثال السابق +======= +```js run +// assuming that range is taken from the example above +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 let arr = Array.from(range); alert(arr); // 1,2,3,4,5 (يحدث التحويل من مصفوفة إلى نص باستخدام toString) ``` @@ -242,8 +255,13 @@ Array.from(obj[, mapFn, thisArg]) على سبيل المثال: +<<<<<<< HEAD ```js // على فرض أن الكائن range مأخوذ من المثال السابق +======= +```js run +// assuming that range is taken from the example above +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 // تربيع كل رقم let arr = Array.from(range, (num) => num * num); @@ -279,7 +297,11 @@ for (let char of str) { alert(chars); ``` +<<<<<<< HEAD ...ولكن هذا أقصر. +======= +...But it is shorter. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 حتى أنه يمكننا أن نبنى دالة `slice` متوافقة مع الأشكال أيضًا: diff --git a/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md b/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md index 6a4c20baf..e2147ccfa 100644 --- a/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md +++ b/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md @@ -25,7 +25,7 @@ messages.shift(); // now readMessages has 1 element (technically memory may be cleaned later) ``` -The `WeakSet` allows to store a set of messages and easily check for the existance of a message in it. +The `WeakSet` allows to store a set of messages and easily check for the existence of a message in it. It cleans up itself automatically. The tradeoff is that we can't iterate over it, can't get "all read messages" from it directly. But we can do it by iterating over all messages and filtering those that are in the set. diff --git a/1-js/05-data-types/08-weakmap-weakset/article.md b/1-js/05-data-types/08-weakmap-weakset/article.md index b9336405e..a5817df80 100644 --- a/1-js/05-data-types/08-weakmap-weakset/article.md +++ b/1-js/05-data-types/08-weakmap-weakset/article.md @@ -1,7 +1,11 @@  # النوع WeakMap والنوع WeakSet: الخرائط والأطقم ضعيفة الإشارة +<<<<<<< HEAD كما عرفنا من فصل «كنس المهملات»، فمُحرّك جافا سكريبت يخُزّن القيمة في الذاكرة طالما يمكن أن يصل لها شيء (أي يمكن استعمالها لاحقًا). هكذا: +======= +As we know from the chapter , JavaScript engine keeps a value in memory while it is "reachable" and can potentially be used. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` let john = { name: "John" }; diff --git a/1-js/05-data-types/09-keys-values-entries/article.md b/1-js/05-data-types/09-keys-values-entries/article.md index ed4ad83e5..4463b3792 100644 --- a/1-js/05-data-types/09-keys-values-entries/article.md +++ b/1-js/05-data-types/09-keys-values-entries/article.md @@ -68,7 +68,15 @@ for (let value of Object.values(user)) { 2. استعمل توابِع المصفوفات على تلك المصفوفة (مثلًا map). 3. استعمل Object.fromEntries(array)‎ على المصفوفة الناتج لتُحوّلها ثانيةً إلى كائن. +<<<<<<< HEAD إليك مثالًا لدينا كائنًا فيه تسعير البضائع، ونريد مضاعفتها (إذ ارتفع الدولار): +======= +1. Use `Object.entries(obj)` to get an array of key/value pairs from `obj`. +2. Use array methods on that array, e.g. `map`, to transform these key/value pairs. +3. Use `Object.fromEntries(array)` on the resulting array to turn it back into an object. + +For example, we have an object with prices, and would like to double them: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let prices = { @@ -79,12 +87,22 @@ let prices = { *!* let doublePrices = Object.fromEntries( +<<<<<<< HEAD // ‫نحوّله إلى مصفوفة، ثمّ نستعمل الطقم، ثمّ يُعيد إلينا fromEntries الكائن المطلوب Object.entries(prices).map(([key, value]) => [key, value * 2]) +======= + // convert prices to array, map each key/value pair into another pair + // and then fromEntries gives back the object + Object.entries(prices).map(entry => [entry[0], entry[1] * 2]) +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ); */!* alert(doublePrices.meat); // 8 ``` +<<<<<<< HEAD ربّما تراه صعبًا أوّل وهلة، ولكن لا تقلق فسيصير أسهل أكثر متى ما بدأت استعمالها مرّة واثنتان وثلاث. يمكن أن نصنع سلسلة فعّالة من التعديلات بهذه الطريقة: +======= +It may look difficult at first sight, but becomes easy to understand after you use it once or twice. We can make powerful chains of transforms this way. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/05-data-types/10-destructuring-assignment/article.md b/1-js/05-data-types/10-destructuring-assignment/article.md index fb9346aa2..41e36db2c 100644 --- a/1-js/05-data-types/10-destructuring-assignment/article.md +++ b/1-js/05-data-types/10-destructuring-assignment/article.md @@ -2,12 +2,12 @@ The two most used data structures in JavaScript are `Object` and `Array`. -- Objects allow us to create a single entity that stores data items by key. +- Objects allow us to create a single entity that stores data items by key. - Arrays allow us to gather data items into an ordered list. -Although, when we pass those to a function, it may need not an object/array as a whole. It may need individual pieces. +Although, when we pass those to a function, it may need not be an object/array as a whole. It may need individual pieces. -*Destructuring assignment* is a special syntax that allows us to "unpack" arrays or objects into a bunch of variables, as sometimes that's more convenient. +*Destructuring assignment* is a special syntax that allows us to "unpack" arrays or objects into a bunch of variables, as sometimes that's more convenient. Destructuring also works great with complex functions that have a lot of parameters, default values, and so on. Soon we'll see that. @@ -76,12 +76,12 @@ In the code above, the second element of the array is skipped, the third one is let [a, b, c] = "abc"; // ["a", "b", "c"] let [one, two, three] = new Set([1, 2, 3]); ``` -That works, because internally a destructuring assignment works by iterating over the right value. It's kind of syntax sugar for calling `for..of` over the value to the right of `=` and assigning the values. +That works, because internally a destructuring assignment works by iterating over the right value. It's a kind of syntax sugar for calling `for..of` over the value to the right of `=` and assigning the values. ```` ````smart header="Assign to anything at the left-side" -We can use any "assignables" at the left side. +We can use any "assignables" on the left side. For instance, an object property: ```js run @@ -176,7 +176,7 @@ alert(rest.length); // 2 */!* ``` -The value of `rest` is the array of the remaining array elements. +The value of `rest` is the array of the remaining array elements. We can use any other variable name in place of `rest`, just make sure it has three dots before it and goes last in the destructuring assignment. @@ -234,7 +234,7 @@ The basic syntax is: let {var1, var2} = {var1:…, var2:…} ``` -We should have an existing object at the right side, that we want to split into variables. The left side contains an object-like "pattern" for corresponding properties. In the simplest case, that's a list of variable names in `{...}`. +We should have an existing object on the right side, that we want to split into variables. The left side contains an object-like "pattern" for corresponding properties. In the simplest case, that's a list of variable names in `{...}`. For instance: @@ -254,7 +254,7 @@ alert(width); // 100 alert(height); // 200 ``` -Properties `options.title`, `options.width` and `options.height` are assigned to the corresponding variables. +Properties `options.title`, `options.width` and `options.height` are assigned to the corresponding variables. The order does not matter. This works too: @@ -420,7 +420,7 @@ alert( title ); // Menu If an object or an array contain other nested objects and arrays, we can use more complex left-side patterns to extract deeper portions. -In the code below `options` has another object in the property `size` and an array in the property `items`. The pattern at the left side of the assignment has the same structure to extract values from them: +In the code below `options` has another object in the property `size` and an array in the property `items`. The pattern on the left side of the assignment has the same structure to extract values from them: ```js run let options = { @@ -429,7 +429,7 @@ let options = { height: 200 }, items: ["Cake", "Donut"], - extra: true + extra: true }; // destructuring assignment split in multiple lines for clarity diff --git a/1-js/05-data-types/11-date/1-new-date/solution.md b/1-js/05-data-types/11-date/1-new-date/solution.md index fa8947196..a3f361ee9 100644 --- a/1-js/05-data-types/11-date/1-new-date/solution.md +++ b/1-js/05-data-types/11-date/1-new-date/solution.md @@ -13,6 +13,6 @@ We could also create a date from a string, like this: ```js run //new Date(datastring) -let d2 = new Date("February 20, 2012 03:12:00"); +let d2 = new Date("2012-02-20T03:12"); alert( d2 ); ``` diff --git a/1-js/05-data-types/11-date/article.md b/1-js/05-data-types/11-date/article.md index 8d5bcd01b..f8059077e 100644 --- a/1-js/05-data-types/11-date/article.md +++ b/1-js/05-data-types/11-date/article.md @@ -52,10 +52,17 @@ _`new Date(datestring)‎`_ _`new Date(year, month, date, hours, minutes, seconds, ms)‎`_ يُنشئ تاريخًا بالمكوّنات الممرّرة حسب المنطقة الزمنية المحلية. أوّل وسيطين إلزاميين أما البقية اختيارية. +<<<<<<< HEAD يجب أن يكون العام `year` بأربع خانات: `2013` صح، `98` خطأ. يبدأ الشهر `month` بالرقم `0` (يناير) وينتهي بالعدد `11` (ديسمبر). مُعامل التاريخ `date` هو رقم اليوم من الشهر. لو لم يكن موجودًا فسيعدّه الكائن `1`. لو لم تكن مُعاملات الساعة والدقيقة والثانية والمليثانية `hours/minutes/seconds/ms` موجودة، فسيعدّها الكائن `0`. +======= + - The `year` should have 4 digits. For compatibility, 2 digits are also accepted and considered `19xx`, e.g. `98` is the same as `1998` here, but always using 4 digits is strongly encouraged. + - The `month` count starts with `0` (Jan), up to `11` (Dec). + - The `date` parameter is actually the day of month, if absent then `1` is assumed. + - If `hours/minutes/seconds/ms` is absent, they are assumed to be equal `0`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 مثال: @@ -318,7 +325,12 @@ function bench(f) { let time1 = 0; let time2 = 0; +<<<<<<< HEAD // ‫نشغّل bench(upperSlice)‎ وbench(upperLoop)‎ عشر مرات مرّة بمرّة +======= +*!* +// run bench(diffSubtract) and bench(diffGetTime) each 10 times alternating +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 for (let i = 0; i < 10; i++) { time1 += bench(diffSubtract); time2 += bench(diffGetTime); diff --git a/1-js/05-data-types/12-json/article.md b/1-js/05-data-types/12-json/article.md index 1879928db..5424cebf8 100644 --- a/1-js/05-data-types/12-json/article.md +++ b/1-js/05-data-types/12-json/article.md @@ -27,10 +27,14 @@ alert(user); // {name: "John", age: 30} ## JSON.stringify +<<<<<<< HEAD إن الكائن [JSON](http://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) هو شكل عام لعرض الكائنات والقيم. وقد تم وصفه كما فى ال [RFC 4627](http://tools.ietf.org/html/rfc4627). لقد صُنع فى البداية من أجل جافا سكريبت، ولكن هناك لغات ومكتبات أخرى للتعامل معه أيضًا. ولذلك من السهل استخدام الكائن JSON لتبادل البيانات عندما تكون الواجهة بجافا سكريبت والسيرفر / الباك اند بلغة مثل Ruby/PHP/Java أو أيًا كان. +======= +The [JSON](https://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) is a general format to represent values and objects. It is described as in [RFC 4627](https://tools.ietf.org/html/rfc4627) standard. Initially it was made for JavaScript, but many other languages have libraries to handle it as well. So it's easy to use JSON for data exchange when the client uses JavaScript and the server is written on Ruby/PHP/Java/Whatever. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 تحتوى جافا سكريبت على دوال للتعامل مع الكائن JSON: @@ -45,7 +49,7 @@ let student = { age: 30, isAdmin: false, courses: ['html', 'css', 'js'], - wife: null + spouse: null }; *!* @@ -62,7 +66,7 @@ alert(json); "age": 30, "isAdmin": false, "courses": ["html", "css", "js"], - "wife": null + "spouse": null } */ */!* diff --git a/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md b/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md index 00bc45bdb..35781677d 100644 --- a/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md +++ b/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md @@ -37,4 +37,8 @@ alert( sumTo(100) ); الدالة المتكررة تأتي في المرتبة الاخيرة في السرعة ببساطة لأنها نفذت الكثير من النداءات و ذلك تطلب الكثير من سياقات التنفيذ و كومة سياقات التنفيذ لذلك فإنها الأبطأ +<<<<<<< HEAD 2. يعض المحركات تدعم تحسين "tail call": أذا كان النداء المتكرر هو الأخير في الدالة (مثلما في`sumTo` ) إذا فالدالة الخارجية لن تحتاج إلي مواصلة التنفيذ وبالتالي فإن المحرك لا يحتاج إلي تذكر سياق التنفيذ. ذلك يزبل العبء عن الذاكرة لذلك العد إلي `sumTo(100000)` ممكناً. لكن محرك جافا سكريبت لا يدعم هذا التحسين أو المعظم لا يدعم, لذلك سيكون هناك خطأ: لقد تخطيت الحجم الأقصي لكومة سياق التنفيذ. +======= +P.P.S. Some engines support the "tail call" optimization: if a recursive call is the very last one in the function, with no other calculations performed, then the outer function will not need to resume the execution, so the engine doesn't need to remember its execution context. That removes the burden on memory. But if the JavaScript engine does not support tail call optimization (most of them don't), there will be an error: maximum stack size exceeded, because there's usually a limitation on the total stack size. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md b/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md index 8660aa30e..f67bebf2b 100644 --- a/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md +++ b/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md @@ -36,7 +36,11 @@ printReverseList(list); حل الحلقة هو الاخر يعتبر معقد قليلاً بالنسبة إلي الطباعة المباشرة. +<<<<<<< HEAD نحن لا نمتلك طرييقة للحصول علي القيمة الأخيرة في القائمة ولا نستطيع العودة للخلف. +======= +The loop variant is also a little bit more complicated than the direct output. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 إذا ماذا نفعل؟ نستطيع أولاً أن نمر خلال العناصر بالطريقة المباشرة ونتذكرهم عن طريق تخزينهم في ترتيب Array, ثم نطبع الترتيب الذي تذكرناه ولكن بشكل عكسي: diff --git a/1-js/06-advanced-functions/01-recursion/article.md b/1-js/06-advanced-functions/01-recursion/article.md index ea5da247c..09cc2fa0f 100644 --- a/1-js/06-advanced-functions/01-recursion/article.md +++ b/1-js/06-advanced-functions/01-recursion/article.md @@ -549,9 +549,13 @@ list.next = list.next.next; مثلاً القائمة المتصلة يمكن تعريفها علي أنها هيكل بيانات تتكون من شئ object يشير إلي القائمة نفسها. +<<<<<<< HEAD ```js list = { value, next -> list } ``` +======= + Trees like HTML elements tree or the department tree from this chapter are also naturally recursive: they have branches and every branch can have other branches. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 **الشجر** مثل عناصر الـ HTML أو الأقسام مثل ما ذكرنا أعلي هذا الفصل. diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md index b718b3668..ca12b9440 100644 --- a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md +++ b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md @@ -44,6 +44,7 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco ``` 3. The array is returned from the function. +<<<<<<< HEAD Then, later, the call to any member, e.g. `army[5]()` will get the element `army[5]` from the array (which is a function) and calls it. @@ -138,6 +139,102 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco That's essentially the same, because `for` on each iteration generates a new lexical environment, with its own variable `i`. So `shooter` generated in every iteration references its own `i`, from that very iteration. ![](lexenv-makearmy-for-fixed.svg) +======= + + Then, later, the call to any member, e.g. `army[5]()` will get the element `army[5]` from the array (which is a function) and calls it. + + Now why do all such functions show the same value, `10`? + + That's because there's no local variable `i` inside `shooter` functions. When such a function is called, it takes `i` from its outer lexical environment. + + Then, what will be the value of `i`? + + If we look at the source: + + ```js + function makeArmy() { + ... + let i = 0; + while (i < 10) { + let shooter = function() { // shooter function + alert( i ); // should show its number + }; + shooters.push(shooter); // add function to the array + i++; + } + ... + } + ``` + + We can see that all `shooter` functions are created in the lexical environment of `makeArmy()` function. But when `army[5]()` is called, `makeArmy` has already finished its job, and the final value of `i` is `10` (`while` stops at `i=10`). + + As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `i=10`. + + ![](lexenv-makearmy-empty.svg) + + As you can see above, on each iteration of a `while {...}` block, a new lexical environment is created. So, to fix this, we can copy the value of `i` into a variable within the `while {...}` block, like this: + + ```js run + function makeArmy() { + let shooters = []; + + let i = 0; + while (i < 10) { + *!* + let j = i; + */!* + let shooter = function() { // shooter function + alert( *!*j*/!* ); // should show its number + }; + shooters.push(shooter); + i++; + } + + return shooters; + } + + let army = makeArmy(); + + // Now the code works correctly + army[0](); // 0 + army[5](); // 5 + ``` + + Here `let j = i` declares an "iteration-local" variable `j` and copies `i` into it. Primitives are copied "by value", so we actually get an independent copy of `i`, belonging to the current loop iteration. + + The shooters work correctly, because the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds to the current loop iteration: + + ![](lexenv-makearmy-while-fixed.svg) + + Such a problem could also be avoided if we used `for` in the beginning, like this: + + ```js run demo + function makeArmy() { + + let shooters = []; + + *!* + for(let i = 0; i < 10; i++) { + */!* + let shooter = function() { // shooter function + alert( i ); // should show its number + }; + shooters.push(shooter); + } + + return shooters; + } + + let army = makeArmy(); + + army[0](); // 0 + army[5](); // 5 + ``` + + That's essentially the same, because `for` on each iteration generates a new lexical environment, with its own variable `i`. So `shooter` generated in every iteration references its own `i`, from that very iteration. + + ![](lexenv-makearmy-for-fixed.svg) +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 Now, as you've put so much effort into reading this, and the final recipe is so simple - just use `for`, you may wonder -- was it worth that? diff --git a/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md index ae8b582db..c62d0c368 100644 --- a/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md +++ b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md @@ -1,8 +1,14 @@ +importance: 5 +<<<<<<< HEAD ### دالة في شرط if طالِع الشيفرة أسفله. ما ناتج الاستدعاء في آخر سطر؟ +======= +--- +# Function in if +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run diff --git a/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md index 975c14d2e..25907caf5 100644 --- a/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md +++ b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md @@ -24,8 +24,14 @@ In this example we can observe the peculiar difference between a "non-existing" ```js function func() { *!* +<<<<<<< HEAD // المتغير المحلي X يعتبر معروف للمحرك من البداية, لكن **غير معرف بقيمة** تظل حتي let // لذلك هناك خطأ +======= + // the local variable x is known to the engine from the beginning of the function, + // but "uninitialized" (unusable) until let ("dead zone") + // hence the error +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 */!* console.log(x); // ReferenceError: لا نستطيع الوصول لـ 'x' قبل إعطائها قيمة diff --git a/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/test.js b/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/test.js index e3c335e03..802f28c4d 100644 --- a/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/test.js +++ b/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/test.js @@ -23,7 +23,7 @@ describe("byField", function(){ { name: "John", age: 20, surname: "Johnson"}, ]; let ageSortedAnswer = users.sort(byField("age")); - assert.deepEqual(ageSortedKey, ageSortedKey); + assert.deepEqual(ageSortedKey, ageSortedAnswer); }); it("sorts users by surname", function(){ diff --git a/1-js/06-advanced-functions/03-closure/article.md b/1-js/06-advanced-functions/03-closure/article.md index 55cbb0902..92bb41129 100644 --- a/1-js/06-advanced-functions/03-closure/article.md +++ b/1-js/06-advanced-functions/03-closure/article.md @@ -8,7 +8,7 @@ We already know that a function can access variables outside of it ("outer" vari But what happens if outer variables change since a function is created? Will the function get newer values or the old ones? -And what if a function is passed along as a parameter and called from another place of code, will it get access to outer variables at the new place? +And what if a function is passed along as an argument and called from another place of code, will it get access to outer variables at the new place? Let's expand our knowledge to understand these scenarios and more complex ones. @@ -158,7 +158,11 @@ alert(counter()); // 2 فهم هذه الأشياء يعد عظيماً للمعلومات الشاملة لجافا سكريبت ومفيد جداً في حالة السيناريوهات المعقدة. لذلك هيا نتعمق أكثر في أمور أكثر صعوبة وتحتاج إلي تركيز. +<<<<<<< HEAD ## البيئات المعجمية +======= +Understanding such things is great for the overall knowledge of JavaScript and beneficial for more complex scenarios. So let's go a bit in-depth. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```warn header="هنا يجب أن تكون شديد التركيز!" diff --git a/1-js/06-advanced-functions/04-var/article.md b/1-js/06-advanced-functions/04-var/article.md index 8b01fb9b0..814f7b072 100644 --- a/1-js/06-advanced-functions/04-var/article.md +++ b/1-js/06-advanced-functions/04-var/article.md @@ -3,6 +3,13 @@ ```smart header="هذه المقالة من أجل فهم النصوص القديمة" المعلومات داخل هذه المقالة تساعدنا فى فهم النصوص القديمة أكثر ولا تحتوي علي أي معلومات عن كيفية كتابة كود جديد +<<<<<<< HEAD +======= +```smart header="This article is for understanding old scripts" +The information in this article is useful for understanding old scripts. + +That's not how we write new code. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ذكرنا في أوائل الفصول حين تكلمنا عن [المتغيرات](info:variables), ذكرنا ثلاث طرائق للتصريح عنها: @@ -268,11 +275,11 @@ function go() { ```js run // Ways to create IIFE -(function() { +*!*(*/!*function() { alert("Parentheses around the function"); }*!*)*/!*(); -(function() { +*!*(*/!*function() { alert("Parentheses around the whole thing"); }()*!*)*/!*; diff --git a/1-js/06-advanced-functions/05-global-object/article.md b/1-js/06-advanced-functions/05-global-object/article.md index 24fe68844..ce2aed158 100644 --- a/1-js/06-advanced-functions/05-global-object/article.md +++ b/1-js/06-advanced-functions/05-global-object/article.md @@ -24,7 +24,7 @@ var gVar = 5; alert(window.gVar); // ‫5 (تصير خاصية من خاصيات الكائن العمومي) ``` -The same effect have function declarations (statements with `function` keyword in the main code flow, not function expressions). +Function declarations have the same effect (statements with `function` keyword in the main code flow, not function expressions). Please don't rely on that! This behavior exists for compatibility reasons. Modern scripts use [JavaScript modules](info:modules) where such a thing doesn't happen. diff --git a/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/solution.md b/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/solution.md index e66eecae8..d3e8657df 100644 --- a/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/solution.md +++ b/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/solution.md @@ -56,6 +56,10 @@ function f(b) { } ``` +<<<<<<< HEAD وستُستعمل `‎f‎` هذه في الاستدعاء التالي، وتُعيد نفسها ثانيةً مهما لزم. وبعدها حين نستعمل العدد أو السلسلة النصية، يُعيد التابِع `‎toString‎` المجموع `‎currentSum‎`. يمكن أيضًا أن نستعمل `‎Symbol.toPrimitive‎` أو `‎valueOf‎` لإجراء عملية التحويل. ترجمة -وبتصرف- للفصل [Function object, NFE](https://javascript.info/function-object) من كتاب [The JavaScript language](https://javascript.info/js) +======= +This `f` will be used in the next call, again return itself, as many times as needed. Then, when used as a number or a string -- the `toString` returns the `currentSum`. We could also use `Symbol.toPrimitive` or `valueOf` here for the conversion. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/06-advanced-functions/06-function-object/article.md b/1-js/06-advanced-functions/06-function-object/article.md index 42f3b5602..52c2f129e 100644 --- a/1-js/06-advanced-functions/06-function-object/article.md +++ b/1-js/06-advanced-functions/06-function-object/article.md @@ -334,7 +334,7 @@ welcome(); // Hello, Guest (nested call works) ```smart header="لا يوجد هذا الشيء في تعريف الدالة العادية" -The outer code still has its variable `sayHi` or `welcome`. And `func` is an "internal function name", how the function can call itself internally. +The outer code still has its variable `sayHi` or `welcome`. And `func` is an "internal function name", the way for the function to can call itself reliably. في بعض الأحيان عندما نحتاج إلى اسم داخلي يكون هذا السبب لتحويل تعريف الدالة إلى NFE أو تعبير الدالة المُسمى. diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md index 8afeb08f7..c87be1445 100644 --- a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md +++ b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md @@ -235,7 +235,11 @@ setTimeout(function() {...}, 100); For `setInterval` the function stays in memory until `clearInterval` is called. +<<<<<<< HEAD ولكن هناك تأثير جانبي لذلك كالعادة، فالدوال تُشير إلى بيئتها المُعجمية الخارجية. لذا طالما «تعيش»، تعيش معها المتغيرات الخارجية أيضًا، وهي أحيانًا كبيرة تأخذ ذاكرة أكبر من الدالة ذاتها. لذا، متى ما لم ترد تلك الدالة المُجدولة فالأفضل أن تُلغيها حتّى لو كانت صغيرة جدًا. +======= +There's a side effect. A function references the outer lexical environment, so, while it lives, outer variables live too. They may take much more memory than the function itself. So when we don't need the scheduled function anymore, it's better to cancel it, even if it's very small. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```` ## جدولة setTimeout بتأخير صفر @@ -299,8 +303,15 @@ For server-side JavaScript, that limitation does not exist, and there exist othe فمثلًا يمكن أن تكون مؤقّتات المتصفّحات أبطأ لأسباب عديدة: +<<<<<<< HEAD - المعالج مُثقل بالعمليات. - المتصفّح يعمل في الخلفية. - يعمل الحاسوب المحمول على البطارية. +======= +For example, the in-browser timer may slow down for a lot of reasons: +- The CPU is overloaded. +- The browser tab is in the background mode. +- The laptop is on battery saving mode. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يمكن لهذا كله رفع دقّة المؤقّت الدنيا (أي أدنى تأخير ممكن) لتصير 300 مليثانية أو حتى 1000 مليثانية حسب المتصفّح وإعدادات الأداء في نظام التشغيل. diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md index cf851f771..6950664be 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md @@ -12,11 +12,10 @@ function throttle(func, ms) { savedThis = this; return; } + isThrottled = true; func.apply(this, arguments); // (1) - isThrottled = true; - setTimeout(function() { isThrottled = false; // (3) if (savedArgs) { diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md index 6df7af132..cbd473196 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md @@ -8,7 +8,7 @@ Create a "throttling" decorator `throttle(f, ms)` -- that returns a wrapper. When it's called multiple times, it passes the call to `f` at maximum once per `ms` milliseconds. -The difference with debounce is that it's completely different decorator: +Compared to the debounce decorator, the behavior is completely different: - `debounce` runs the function once after the "cooldown" period. Good for processing the final result. - `throttle` runs it not more often than given `ms` time. Good for regular updates that shouldn't be very often. diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/article.md b/1-js/06-advanced-functions/09-call-apply-decorators/article.md index 6bd5650fc..319fa20a3 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/article.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/article.md @@ -288,19 +288,34 @@ func.apply(context, args) أي أنّ الاستدعاءين الآتين متساويين تقريبًا: +<<<<<<< HEAD ``` func.call(context, ...args); // نمرّر الكائن قائمةً بمُعامل التوزيع func.apply(context, args); // ‫نفس الفكرة باستعمال apply ``` ولكن هناك فرق بسيط واحد: +======= +```js +func.call(context, ...args); +func.apply(context, args); +``` + +They perform the same call of `func` with given context and arguments. + +There's only a subtle difference regarding `args`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 - يُتيح لنا مُعامل التوزيع `‎...‎` تمرير _المُتعدَّد_ `‎args‎` قائمةً إلى `‎call‎`. - لا يقبل `‎apply‎` إلّا مُعامل `‎args‎` _شبيه بالمصفوفات_. +<<<<<<< HEAD أي أنّ هذين الاستدعاءين يُكمّلان بعضهما البعض. لو توقّعنا وصول مُتعدَّد فنستعمل `‎call‎`، ولو توقّعنا شبيهًا بالمصفوفات نستعمل `‎apply‎`. أمّا الكائنات المُتعدَّدة والشبيهة بالمصفوفات (مثل المصفوفات الحقيقية)، فيمكننا نظريًا استعمال أيّ من الاثنين، إلّا أنّ `‎apply‎` سيكون أسرع غالبًا إذ أنّ مُعظم محرّكات جافا سكريبت تحسّن أدائه داخليًا أكثر من `‎call‎`. +======= +...And for objects that are both iterable and array-like, such as a real array, we can use any of them, but `apply` will probably be faster, because most JavaScript engines internally optimize it better. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يُدى تمرير كافة المُعاملات (مع السياق) من دالة إلى أخرى _بتمرير الاستدعاء_. diff --git a/1-js/06-advanced-functions/10-bind/article.md b/1-js/06-advanced-functions/10-bind/article.md index 3d8cd1f25..7d4848b7f 100644 --- a/1-js/06-advanced-functions/10-bind/article.md +++ b/1-js/06-advanced-functions/10-bind/article.md @@ -170,8 +170,13 @@ let user = { let say = user.say.bind(user); +<<<<<<< HEAD say("Hello"); // ‫Hello, John!‎ (مُرّر المُعامل "Hello" إلى say) say("Bye"); // ‫Bye, John!‎ (مُرّر المعامل "Bye" إلى say) +======= +say("Hello"); // Hello, John! ("Hello" argument is passed to say) +say("Bye"); // Bye, John! ("Bye" is passed to say) +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` **تابِع مفيد: `‎bindAll‎`** diff --git a/1-js/07-object-properties/01-property-descriptors/article.md b/1-js/07-object-properties/01-property-descriptors/article.md index 57f71f3cc..7401540c8 100644 --- a/1-js/07-object-properties/01-property-descriptors/article.md +++ b/1-js/07-object-properties/01-property-descriptors/article.md @@ -20,7 +20,11 @@ الطريقة [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) تسمح بالإستعلام _الكامل_ عن المعلومات الخاصة بأيّ خاصية. +<<<<<<< HEAD و صياغتها تكون كالآتي: +======= +The method [Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor) allows to query the *full* information about a property. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); @@ -54,7 +58,11 @@ alert(JSON.stringify(descriptor, null, 2)); */ ``` +<<<<<<< HEAD لتغيير الرايات, يمكننا إستخدام [Object.defineProperty](mdn:js/Object/defineProperty). +======= +To change the flags, we can use [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 و صياغتها تكون كالآتي: @@ -194,7 +202,11 @@ alert(Object.keys(user)); // name راية عدم الضبط (`configurable:false`) احياناً يتم إعدادها مسبقاً في بعض الكائنات والخصائص المضمّنة في اللغة. +<<<<<<< HEAD الخاصية الغير قابلة للإحصاء لا يمكن حذفها. +======= +A non-configurable property can't be deleted, its attributes can't be modified. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 فمثلاً, `Math.PI` غير قابلة للتعديل, غير قابلة للإحصاء و غير قابلة لإعادة الضبط: @@ -215,11 +227,16 @@ alert(JSON.stringify(descriptor, null, 2)); لذا, لن يستطيع المبرمج تغيير قيمة `Math.PI` أو التعديل عليها. ```js run +<<<<<<< HEAD Math.PI = 3; // خطأ +======= +Math.PI = 3; // Error, because it has writable: false +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 // delete Math.PI لن تعمل أيضًا ``` +<<<<<<< HEAD إن تفعيل خاصيّة منع قابلية إعادة الضبط هو قرار لا عودة فيه. فلا يمكننا تغيير الراية (إتاحة قابلية إعادة الضبط) باستعمال `defineProperty`. وللدقّة فهذا المنع يضع تقييدات أخرى على `defineProperty`: @@ -228,8 +245,20 @@ Math.PI = 3; // خطأ 2. منع تغيير راية قابلية الإحصاء `enumerable`. 3. منع تغيير راية قابلية التعديل `writable: false` الي `true` (و لكن العكس ممكن). 4. منع تغيير ضابط وجالب واصف الوصول `get/set` (ولكن يمكن إسناد قيم إليه). +======= +We also can't change `Math.PI` to be `writable` again: -**The idea of "configurable: false" is to prevent changes of property flags and its deletion, while allowing to change its value.** +```js run +// Error, because of configurable: false +Object.defineProperty(Math, "PI", { writable: true }); +``` + +There's absolutely nothing we can do with `Math.PI`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 + +Making a property non-configurable is a one-way road. We cannot change it back with `defineProperty`. + +**Please note: `configurable: false` prevents changes of property flags and its deletion, while allowing to change its value.** Here `user.name` is non-configurable, but we can still change it (as it's writable): @@ -246,7 +275,7 @@ user.name = 'Pete'; // works fine delete user.name; // Error ``` -And here we make `user.name` a "forever sealed" constant: +And here we make `user.name` a "forever sealed" constant, just like the built-in `Math.PI`: ```js run let user = { @@ -265,9 +294,21 @@ delete user.name; Object.defineProperty(user, 'name', { value: 'Pete' }); ``` +<<<<<<< HEAD ## Object.defineProperties يوجد طريقة [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties) و التي تسمح بتعريف كثير من الخصائص مره واحده. +======= +```smart header="The only attribute change possible: writable true -> false" +There's a minor exception about changing flags. + +We can change `writable: true` to `false` for a non-configurable property, thus preventing its value modification (to add another layer of protection). Not the other way around though. +``` + +## Object.defineProperties + +There's a method [Object.defineProperties(obj, descriptors)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) that allows to define many properties at once. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 و صياغتها تكون كالآتي: @@ -293,7 +334,11 @@ Object.defineProperties(user, { ## Object.getOwnPropertyDescriptors +<<<<<<< HEAD لجلب كلّ واصفات الخصائص معًا, يمكننا إستعمال الطريقة [Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors). +======= +To get all property descriptors at once, we can use the method [Object.getOwnPropertyDescriptors(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptors). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 بدمجه مع `Object.defineProperties` يمكن إستخدامها لنسخ الكائنات "ونحن على علمٍ براياتها": @@ -311,7 +356,11 @@ for (let key in user) { ...و لكن هذا لا ينسخ الرايات. لذا إذا كنا نريد نسخ "أفضل" سيكون إستخدام `Object.defineProperties` أفضل. +<<<<<<< HEAD إختلاف آخر و ذلك أن `for..in` تتجاهل الخصائص الرمزية (Symbolic Properties), و لكن `Object.getOwnPropertyDescriptors` تُعيد _كل_ واصِفات الخصائص بما فيها الرمزية. +======= +Another difference is that `for..in` ignores symbolic and non-enumerable properties, but `Object.getOwnPropertyDescriptors` returns *all* property descriptors including symbolic and non-enumerable ones. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## إغلاق الكائنات على المستوى العام @@ -319,6 +368,7 @@ for (let key in user) { يوجد ايضاً تحدد الدخول الى الكائن _كله_ : +<<<<<<< HEAD [Object.preventExtensions(obj)](mdn:js/Object/preventExtensions) : يمنع إضافة خصائص جديدة إلى الكائن. @@ -327,9 +377,20 @@ for (let key in user) { [Object.freeze(obj)](mdn:js/Object/freeze) : يمنع إضافة الخصائص أو إزالتها أو تغييرها. يقوم بوضع `configurable: false, writable: false` لكل الخصائص الموجودة. +======= +[Object.preventExtensions(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions) +: Forbids the addition of new properties to the object. + +[Object.seal(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) +: Forbids adding/removing of properties. Sets `configurable: false` for all existing properties. + +[Object.freeze(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) +: Forbids adding/removing/changing of properties. Sets `configurable: false, writable: false` for all existing properties. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 كما أنّ هناك توابِع أخرى تفحص تلك المزايا: +<<<<<<< HEAD [Object.isExtensible(obj)](mdn:js/Object/isExtensible) : يُعيد `false` لو كان ممنوعًا إضافة الخصائص, غير ذلك `true`. @@ -338,5 +399,15 @@ for (let key in user) { [Object.isFrozen(obj)](mdn:js/Object/isFrozen) : يُعيد `true` إذا كان إضافة/حذف/تعديل الخصائص ممنوعاً, و كل الخصائص الحالية `configurable: false, writable: false`. +======= +[Object.isExtensible(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible) +: Returns `false` if adding properties is forbidden, otherwise `true`. + +[Object.isSealed(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed) +: Returns `true` if adding/removing properties is forbidden, and all existing properties have `configurable: false`. + +[Object.isFrozen(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen) +: Returns `true` if adding/removing/changing properties is forbidden, and all current properties are `configurable: false, writable: false`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 أمّا على أرض الواقع، فنادرًا ما نستعمل تلك الطرق. diff --git a/1-js/07-object-properties/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md index fde204b06..2118b4053 100644 --- a/1-js/07-object-properties/02-property-accessors/article.md +++ b/1-js/07-object-properties/02-property-accessors/article.md @@ -6,7 +6,15 @@ ## الجالبات والضابطات +<<<<<<< HEAD خصائص الوصول تمثل بـ "getter" و "setter". يشار إليهم داخل الكائن بـ `get` and `set`: +======= +The second type of property is something new. It's an *accessor property*. They are essentially functions that execute on getting and setting a value, but look like regular properties to an external code. + +## Getters and setters + +Accessor properties are represented by "getter" and "setter" methods. In an object literal they are denoted by `get` and `set`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js let obj = { diff --git a/1-js/08-prototypes/01-prototype-inheritance/article.md b/1-js/08-prototypes/01-prototype-inheritance/article.md index 0db392e7f..1712ff7e3 100644 --- a/1-js/08-prototypes/01-prototype-inheritance/article.md +++ b/1-js/08-prototypes/01-prototype-inheritance/article.md @@ -10,7 +10,7 @@ _الوراثة النموذجية_ (تدعى أيضًا الوراثة عبر لكائنات جافا سكريبت خاصية مخفية أخرى باسم `[[Prototype]]` (هذا اسمها في المواصفات القياسية للغة جافا سكريبت)، وهي إمّا أن تكون `null` أو أن تشير إلى كائن آخر. نسمّي هذا الكائن بِـ”prototype“ (نموذج أولي). -When we read a property from `object`, and it's missing, JavaScript automatically takes it from the prototype. In programming, such thing is called "prototypal inheritance". And soon we'll study many examples of such inheritance, as well as cooler language features built upon it. +When we read a property from `object`, and it's missing, JavaScript automatically takes it from the prototype. In programming, this is called "prototypal inheritance". And soon we'll study many examples of such inheritance, as well as cooler language features built upon it. إن كائن النموذج الأولي ”سحريٌ“ إن صحّ القول، فحين نريد قراءة خاصية من كائن `object` ولا يجدها محرّك جافا سكريبت، يأخذها تلقائيًا من كائن النموذج الأولي لذاك الكائن. يُسمّى هذا في علم البرمجة ”بالوراثة النموذجية“ (‏Prototypal inheritance)، وهناك العديد من المزايا الرائعة في اللغة وفي التقنيات البرمجية مبنية عليها. @@ -52,7 +52,11 @@ alert( rabbit.eats ); // true (**) alert( rabbit.jumps ); // true ``` +<<<<<<< HEAD هنا نضبط (في السطر `(*)`) كائن `animal` ليكون النموذج الأولي (Prototype) للكائن `rabbit`. +======= +Here the line `(*)` sets `animal` to be the prototype of `rabbit`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 بعدها متى ما حاولت التعليمة `alert` قراءة الخاصية `rabbit.eats` (انظر `(**)`)، ولم يجدها في كائن `rabbit` ستتبع لغة جافا سكريبت الخاصية `[[Prototype]]` لمعرفة ما هو كائن النموذج الأولي لكائن `rabbit`، وسيجده كائن `animal` (البحث من أسفل إلى أعلى): @@ -126,7 +130,11 @@ There are only two limitations: ومن الواضح جليًا أيضًا أي كائن سيرث كائن `[[Prototype]]` واحد وواحد فقط، لا يمكن للكائن وراثة كائنين. +<<<<<<< HEAD ```smart header="`**proto**`is a historical getter/setter for`[[Prototype]]`" +======= +```smart header="`__proto__` is a historical getter/setter for `[[Prototype]]`" +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 It's a common mistake of novice developers not to know the difference between these two. Please note that `__proto__` is _not the same_ as the internal `[[Prototype]]` property. It's a getter/setter for `[[Prototype]]`. Later we'll see situations where it matters, for now let's just keep it in mind, as we build our understanding of JavaScript language. @@ -281,7 +289,11 @@ for(let prop in rabbit) alert(prop); // jumps, then eats */!* ``` +<<<<<<< HEAD لو لم تكن هذه النتيجة ما نريد (أي نريد استثناء الخاصيات الموروثة)، فيمكن استعمال التابِع [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty) المضمّن في اللغة: إذ يُعيد `true` لو كان للكائن `obj` نفسه (وليس للموروث منه) خاصية بالاسم `key`. +======= +If that's not what we want, and we'd like to exclude inherited properties, there's a built-in method [obj.hasOwnProperty(key)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 بهذا يمكننا ترشيح الخاصيات الموروثة (ونتعامل معها على حدة): diff --git a/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md index 0073e252e..372d50dd6 100644 --- a/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md +++ b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md @@ -38,7 +38,12 @@ Why `user2.name` is `undefined`? Here's how `new user.constructor('Pete')` works: 1. First, it looks for `constructor` in `user`. Nothing. -2. Then it follows the prototype chain. The prototype of `user` is `User.prototype`, and it also has nothing. -3. The value of `User.prototype` is a plain object `{}`, its prototype is `Object.prototype`. And there is `Object.prototype.constructor == Object`. So it is used. +2. Then it follows the prototype chain. The prototype of `user` is `User.prototype`, and it also has no `constructor` (because we "forgot" to set it right!). +3. Going further up the chain, `User.prototype` is a plain object, its prototype is the built-in `Object.prototype`. +4. Finally, for the built-in `Object.prototype`, there's a built-in `Object.prototype.constructor == Object`. So it is used. -At the end, we have `let user2 = new Object('Pete')`. The built-in `Object` constructor ignores arguments, it always creates an empty object, similar to `let user2 = {}`, that's what we have in `user2` after all. +Finally, at the end, we have `let user2 = new Object('Pete')`. + +Probably, that's not what we want. We'd like to create `new User`, not `new Object`. That's the outcome of the missing `constructor`. + +(Just in case you're curious, the `new Object(...)` call converts its argument to an object. That's a theoretical thing, in practice no one calls `new Object` with a value, and generally we don't use `new Object` to make objects at all). \ No newline at end of file diff --git a/1-js/08-prototypes/03-native-prototypes/article.md b/1-js/08-prototypes/03-native-prototypes/article.md index b3949a9be..d3fafc9d5 100644 --- a/1-js/08-prototypes/03-native-prototypes/article.md +++ b/1-js/08-prototypes/03-native-prototypes/article.md @@ -2,7 +2,11 @@ إن الخاصية `"prototype"` مستخدمة بشكل واسع من جافا سكريبت نفسها، حيث أن كل الدوال البانية (constructor functions) تستخدمها. +<<<<<<< HEAD أولًا سنرى التفاصيل، ثم نتعلم كيف نستخدمها لإضافة إمكانيات جديدة للكائنات الموجودة بالفعل (built-in objects). +======= +First we'll look at the details, and then how to use it for adding new capabilities to built-in objects. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## Object.prototype diff --git a/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md index 950b4151e..21c2b6dc9 100644 --- a/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md +++ b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md @@ -28,4 +28,8 @@ alert(dictionary); // "apple,__proto__" عند إنشاء خاصية بواصف فإن مُعرِّفاتها تكون قيمها `false`. ولذلك فى الكود أعلاه فإن `dictionary.toString` هي غير معدودة (non-enumerable). +<<<<<<< HEAD أنظر فصل [](info:property-descriptors) للمراجعة. +======= +See the chapter [](info:property-descriptors) for review. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/08-prototypes/04-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md index 82871b94e..4d3e99388 100644 --- a/1-js/08-prototypes/04-prototype-methods/article.md +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -4,16 +4,27 @@ تعتبر الخاصية `__proto__` قديمة وغير مدعومة (فى عمل جافا سكريبت فى المتصفحات فقط). +<<<<<<< HEAD الدوال الحديثة هي: +======= +Setting or reading the prototype with `obj.__proto__` is considered outdated and somewhat deprecated (moved to the so-called "Annex B" of the JavaScript standard, meant for browsers only). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 -The modern methods are: +The modern methods to get/set a prototype are: -- [Object.create(proto, [descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors. - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]` of `obj`. - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- تقوم بإرجاع الخاصية `[[Prototype]]` من الكائن `obj`. - [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- تجعل الخاصية `[[Prototype]]` من الكائن `obj` تشير إلى `proto`. +<<<<<<< HEAD وهذه الدوال يجب استخدامها بلًا من `__proto__`. +======= +The only usage of `__proto__`, that's not frowned upon, is as a property when creating a new object: `{ __proto__: ... }`. + +Although, there's a special method for this too: + +- [Object.create(proto, [descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 عل ىسبيل المثال: @@ -24,7 +35,7 @@ let animal = { // تقوم بإنشاء كان جديد حيث أن الكائن animal نموذج له *!* -let rabbit = Object.create(animal); +let rabbit = Object.create(animal); // same as {__proto__: animal} */!* alert(rabbit.eats); // true @@ -38,7 +49,13 @@ Object.setPrototypeOf(rabbit, {}); // تغيير نموذج الكائن rabbit */!* ``` +<<<<<<< HEAD تستقبل الدالة `Object.create` متغيرًا إضافيًا بشكل اختيارى وهو واصف الخاصية (property descriptors) حيث يمكننا إضافة خصائص إضافية للكائن الجديد كالآتى: +======= +The `Object.create` method is a bit more powerful, as it has an optional second argument: property descriptors. + +We can provide additional properties to the new object there, like this: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run let animal = { @@ -59,20 +76,34 @@ alert(rabbit.jumps); // true يمكننا استخدام `Object.create` للقيام بنسخ كائن بشكل أفضل من نسخ الخصائص باستخدام التكرار `for..in`: ```js +<<<<<<< HEAD // كائن جديد مماثل تمامًا let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); +======= +let clone = Object.create( + Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj) +); +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` هذا الإستدعاء يقوم بإنشاء نسخه طبق الأصل من الكائن `obj` بما فيه من خصائص سواءًا كانت معدودة (enumerable) أم لا وكذلك الجالبات والمغيرات (getters & setters) -- كل شيئ وبالخاصية `[[Prototype]]` الصحيحة. +<<<<<<< HEAD ## نبذة من التاريخ إذا عددنا كل الطرق للتحكم فى `[[Prototype]]`، فهناك الكثير! توجد الكثير من الطرق للقيام بنفس الشيئ! لماذا؟ +======= + +## Brief history + +There're so many ways to manage `[[Prototype]]`. How did that happen? Why? +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 هذا لأسباب تاريخية متأصّلة. +<<<<<<< HEAD - خاصية ال`"prototype"` لدالة بانية (constructor function) موجودة من زمان بعيد. - لاحقًا فى عام 2012 ظهرت الدالة `Object.create`. حيث تمكِّن من إنشاء كائنات بنموذج مُعطي ولكن لا تعطي الإمكانية لجلب أو تعديل الخصائص، ولذلك قامت المتصفحات بإضافة الخاصية `__proto__` الغير موثقة فى المصدر والتى تسمح للمستخدم أن يجلب أو يعدل النموذج فى أى وقت. - لاحقًا فى عام 2015 ظهرت الدالتين `Object.setPrototypeOf` و `Object.getPrototypeOf` للقيام بنفس وظيفة الخاصية `__proto__` وحيث أن الخاصية `__proto__` موجودة فى كل مكان تقريبًا فقد أصبحت قديمة وأصبحت فى طريقها إلى (Annex B) من المصدر وبالتالى أصبحت اختيارية لبيئة جافا سكريبت غير المتصفحات. @@ -80,6 +111,22 @@ let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescr والآن أصبح فى تصرفنا كل هذه الطرق. لماذا تم استبدال الخاصية `__proto__` بالدوال `getPrototypeOf/setPrototypeOf`؟ هذا سؤال مهم ويستدعينا أن نفهم لماذا تعد الخاصية `__proto__` سيئة. أكمل القراءة لتحصل على الإجابة. +======= +The prototypal inheritance was in the language since its dawn, but the ways to manage it evolved over time. + +- The `prototype` property of a constructor function has worked since very ancient times. It's the oldest way to create objects with a given prototype. +- Later, in the year 2012, `Object.create` appeared in the standard. It gave the ability to create objects with a given prototype, but did not provide the ability to get/set it. Some browsers implemented the non-standard `__proto__` accessor that allowed the user to get/set a prototype at any time, to give more flexibility to developers. +- Later, in the year 2015, `Object.setPrototypeOf` and `Object.getPrototypeOf` were added to the standard, to perform the same functionality as `__proto__`. As `__proto__` was de-facto implemented everywhere, it was kind-of deprecated and made its way to the Annex B of the standard, that is: optional for non-browser environments. +- Later, in the year 2022, it was officially allowed to use `__proto__` in object literals `{...}` (moved out of Annex B), but not as a getter/setter `obj.__proto__` (still in Annex B). + +Why was `__proto__` replaced by the functions `getPrototypeOf/setPrototypeOf`? + +Why was `__proto__` partially rehabilitated and its usage allowed in `{...}`, but not as a getter/setter? + +That's an interesting question, requiring us to understand why `__proto__` is bad. + +And soon we'll get the answer. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```warn header="لا تغير الخاصية `[[Prototype]]`فى كائن موجود إذا كانت السرعة تهمك" عمليًا يمكننا أن نجلب أو نعدّل الخاصية`[[Prototype]]`فى أى وقت، ولكن عادة ما نضع ليها قيمة فقط عند إنشاء الكائن ولا نعدلها بعد ذلك: يرث الكائن`rabbit`من الكائن`animal` وهذا لن يتغير. @@ -104,25 +151,58 @@ obj[key] = "some value"; alert(obj[key]); // [object Object], وليست "some value"! ```` +<<<<<<< HEAD هنا إذا قام المستخدم بكتابة `__proto__`، فإن ماكتبه سيتم تجاهله! هذا لا يجب أن يفاجئنا، فالخاصية `__proto__` لها تعامل خاص: لأنها يجب أن تكون كائنًا أو `null`، ولا يمكن أن يكون النص نموذجًا. +======= +Here, if the user types in `__proto__`, the assignment in line 4 is ignored! + +That could surely be surprising for a non-developer, but pretty understandable for us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype. That's why an assignment a string to `__proto__` is ignored. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ولكننا لم نقصد أن نفعل ذلك، أليس كذلك؟ نريد أن نخزن خاصية بقيمتها واسم الخاصية `"__proto__"` لم يتم حفظه. فهذا إذن خلل! +<<<<<<< HEAD الآثار هنا ليست كارثية، ولكن فى حالات أخرى يمكن أن نضع خاصية بقيمتها ثم يتغير النموذج بالفعل. ونتيجة لذلك سيعطى التنفيذ نتائج غير صحيحة وغير متوقعة. +======= +Here the consequences are not terrible. But in other cases we may be storing objects instead of strings in `obj`, and then the prototype will indeed be changed. As a result, the execution will go wrong in totally unexpected ways. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 وأسوأ من ذلك -- لا يفكر المطورون عادة عن إمكانية كهذه أبدًا. وهذا يجعل الخطأ صعب الملاحظة ويمكن أن يتحول إلى ثغرة خصوصًا إذا كان البرنامج يعمل على السيرفر. +<<<<<<< HEAD ويمكن أن تحدث أيضًا أشياء غير متوقعة عند وضع قيمة للدالة `toString` والتى هي دالة بطبيعتها وكذلك لدوال أخرى. +======= +Unexpected things also may happen when assigning to `obj.toString`, as it's a built-in object method. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 كيف يمكننا تجنب هذه المشكلة؟ +<<<<<<< HEAD أولًا، يمكننا أن نتحوّل لاستخدام الـ`Map` للتخزين بدلًا من الكائنات العادية وسيكون كل شيئ بخير. ولكن يمكن للـ `Object` أن يخدمنا بشكل جيد هنا، لأن صنّاع اللغة أعطو اهتمامًا لهذه المشكلة من وقت طويل. إن الخاصية `__proto__` ليست بخاصية عادية وإنما موصّل للخاصية `Object.prototype`: +======= +First, we can just switch to using `Map` for storage instead of plain objects, then everything's fine: + +```js run +let map = new Map(); + +let key = prompt("What's the key?", "__proto__"); +map.set(key, "some value"); + +alert(map.get(key)); // "some value" (as intended) +``` + +...But `Object` syntax is often more appealing, as it's more concise. + +Fortunately, we *can* use objects, because language creators gave thought to that problem long ago. + +As we know, `__proto__` is not a property of an object, but an accessor property of `Object.prototype`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ![](object-prototype-2.svg) @@ -135,6 +215,7 @@ alert(obj[key]); // [object Object], وليست "some value"! ```js run *!* let obj = Object.create(null); +// or: obj = { __proto__: null } */!* let key = prompt("What's the key?", "__proto__"); @@ -175,6 +256,7 @@ alert(Object.keys(chineseDictionary)); // hello,bye ## الملخص +<<<<<<< HEAD الدوال الحديثة لإنشاء نموذج و الوصول إليه هي: - [Object.create(proto, [descriptors])](mdn:js/Object/create) -- creates an empty object with a given `proto` as `[[Prototype]]` (can be `null`) and optional property descriptors. @@ -186,11 +268,22 @@ alert(Object.keys(chineseDictionary)); // hello,bye لذا يمكننا استخدام `Object.create(null)` لإنشاء كائن عادى جدًا "very plain" بدون `__proto__` أو استخدام الـ `Map` لهذا. وأيضًا، تعطي الدالة `Object.create` طريقة سهل لنسخ الكائن بكل الواصفات (descriptors): +======= +- To create an object with the given prototype, use: -```js -let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); -``` + - literal syntax: `{ __proto__: ... }`, allows to specify multiple properties + - or [Object.create(proto, [descriptors])](mdn:js/Object/create), allows to specify property descriptors. + + The `Object.create` provides an easy way to shallow-copy an object with all descriptors: + + ```js + let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); + ``` +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 + +- Modern methods to get/set the prototype are: +<<<<<<< HEAD وقد أوضحنا أيضًا أن `__proto__` هو جالب أو معدّل للخاصية `[[Prototype]]` ويوجد فى `Object.prototype` مثل غيره من الدوال. ويمكننا أن ننشئ كائنًا من غير نموذج باستخدام `Object.create(null)`، وهذه الكائنات تستخدم ككائنات عادية "pure dictionaries" حيث لا توجد لديها أى مشاكل إذا قام المستخدم بإدخال `"__proto__"` كإسم للخاصية. @@ -205,3 +298,15 @@ let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescr - [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): تقوم بإرجاع `true` إذا احتوي الكائن وليس نموذجه على خاصية تسمى `key`. كل الدوال التى تقوم بإرجاع خصائص الكائن (مثل `Object.keys` وغيرها) -- تقوم بإرجاع الخصائص الموجودة فى الكائن فقط وليست الموجودة فى نموذجه (its prototype). فإذا كنا نريد إرجاع الموجودة فى النموذج أيضًا فيمكننا استخدام التكرار `for..in`. +======= + - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]` of `obj` (same as `__proto__` getter). + - [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto` (same as `__proto__` setter). + +- Getting/setting the prototype using the built-in `__proto__` getter/setter isn't recommended, it's now in the Annex B of the specification. + +- We also covered prototype-less objects, created with `Object.create(null)` or `{__proto__: null}`. + + These objects are used as dictionaries, to store any (possibly user-generated) keys. + + Normally, objects inherit built-in methods and `__proto__` getter/setter from `Object.prototype`, making corresponding keys "occupied" and potentially causing side effects. With `null` prototype, objects are truly empty. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/1-js/09-classes/01-class/article.md b/1-js/09-classes/01-class/article.md index 19711656e..98bea9f76 100644 --- a/1-js/09-classes/01-class/article.md +++ b/1-js/09-classes/01-class/article.md @@ -112,7 +112,7 @@ alert(typeof User); // function alert(User === User.prototype.constructor); // true // The methods are in User.prototype, e.g: -alert(User.prototype.sayHi); // alert(this.name); +alert(User.prototype.sayHi); // the code of the sayHi method // there are exactly two methods in the prototype alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi @@ -120,7 +120,11 @@ alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi ## ليس مجرد سكر نحوي +<<<<<<< HEAD أحيانًا يقول الناس أن "class" عبارة عن "سكر نحوي" (بنية تم تصميمها لتسهيل قراءة الأشياء ، ولكن لا تقدم أي شيء جديد) ، لأنه يمكننا في الواقع أن نعلن الشيء نفسه بدون كلمة "class" على الإطلاق: +======= +Sometimes people say that `class` is a "syntactic sugar" (syntax that is designed to make things easier to read, but doesn't introduce anything new), because we could actually declare the same thing without using the `class` keyword at all: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run // rewriting class User in pure functions @@ -146,7 +150,11 @@ user.sayHi(); لا تزال هناك اختلافات مهمة. +<<<<<<< HEAD 1. أولاً ، يتم تصنيف دالة تم إنشاؤها بواسطة "class" بواسطة خاصية داخلية خاصة `[[FunctionKind]]:" classConstructor "`. لذلك فهي ليست تمامًا مثل إنشائها يدويًا. +======= +1. First, a function created by `class` is labelled by a special internal property `[[IsClassConstructor]]: true`. So it's not entirely the same as creating it manually. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 تقوم اللغة بالتحقق من هذه الخاصية في أماكن متنوعة. على سبيل المثال ، على عكس الوظيفة العادية ، يجب أن يتم استدعاؤها بـ `new`: diff --git a/1-js/09-classes/02-class-inheritance/article.md b/1-js/09-classes/02-class-inheritance/article.md index 29d26c1fd..eb8ecf58b 100644 --- a/1-js/09-classes/02-class-inheritance/article.md +++ b/1-js/09-classes/02-class-inheritance/article.md @@ -106,7 +106,11 @@ class Rabbit extends Animal { } ``` +<<<<<<< HEAD عادة لا نريد استبدال طريقة رئيسية تمامًا ، ولكن بدلاً من ذلك نبني عليها لاستبدالها أو توسيع وظائفها. نفعل شيئًا في طريقتنا ، ولكن استدعاء الطريقة الأم قبل / بعدها أو في العملية. +======= +Usually, however, we don't want to totally replace a parent method, but rather to build on top of it to tweak or extend its functionality. We do something in our method, but call the parent method before/after it or in the process. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 توفر الفصول كلمة رئيسية `` فائقة '' لذلك. @@ -161,6 +165,11 @@ rabbit.stop(); // White Rabbit stands still. White Rabbit hides! إذا تم الوصول إليه ، فهو مأخوذ من الوظيفة الخارجية. على سبيل المثال: +<<<<<<< HEAD +======= +If accessed, it's taken from the outer function. For instance: + +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js class Rabbit extends Animal { stop() { @@ -177,9 +186,13 @@ setTimeout(function() { super.stop() }, 1000); ``` ```` +<<<<<<< HEAD ## تجاوز constructor مع المنشئين يصبح الأمر صعبًا بعض الشيء. +======= +## Overriding constructor +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 حتى الآن ، لم يكن لدى "الأرنب" "مُنشئ" خاص به. @@ -315,13 +328,13 @@ new Rabbit(); // animal */!* ``` -Here, class `Rabbit` extends `Animal` and overrides `name` field with its own value. +Here, class `Rabbit` extends `Animal` and overrides the `name` field with its own value. There's no own constructor in `Rabbit`, so `Animal` constructor is called. What's interesting is that in both cases: `new Animal()` and `new Rabbit()`, the `alert` in the line `(*)` shows `animal`. -**In other words, parent constructor always uses its own field value, not the overridden one.** +**In other words, the parent constructor always uses its own field value, not the overridden one.** What's odd about it? @@ -358,10 +371,14 @@ And that's what we naturally expect. When the parent constructor is called in th ...But for class fields it's not so. As said, the parent constructor always uses the parent field. -Why is there the difference? +Why is there a difference? +<<<<<<< HEAD Well, the reason is in the field initialization order. The class field is initialized: +======= +Well, the reason is the field initialization order. The class field is initialized: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 - Before constructor for the base class (that doesn't extend anything), - Immediately after `super()` for the derived class. @@ -369,16 +386,20 @@ In our case, `Rabbit` is the derived class. There's no `constructor()` in it. As So, `new Rabbit()` calls `super()`, thus executing the parent constructor, and (per the rule for derived classes) only after that its class fields are initialized. At the time of the parent constructor execution, there are no `Rabbit` class fields yet, that's why `Animal` fields are used. -This subtle difference between fields and methods is specific to JavaScript +This subtle difference between fields and methods is specific to JavaScript. Luckily, this behavior only reveals itself if an overridden field is used in the parent constructor. Then it may be difficult to understand what's going on, so we're explaining it here. If it becomes a problem, one can fix it by using methods or getters/setters instead of fields. +<<<<<<< HEAD ## Super: الأجزاء الداخلية ، [[HomeObject]] ````warn header="معلومات متقدمة" إذا كنت تقرأ البرنامج التعليمي لأول مرة - فقد يتم تخطي هذا القسم. +======= +## Super: internals, [[HomeObject]] +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 إنه يتعلق بالآليات الداخلية الكامنة وراء الميراث و "السوبر". `` diff --git a/1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md b/1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md index 8109bb549..04a198c11 100644 --- a/1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md +++ b/1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md @@ -21,14 +21,22 @@ alert( rabbit.hasOwnProperty('name') ); // true لكن هذا ليس كل شيء بعد. +<<<<<<< HEAD حتى بعد الإصلاح ، لا يزال هناك اختلاف مهم في "class rabbit يوسع الكائن" "مقابل" class Rabbit ". +======= +Even after the fix, there's still an important difference between `"class Rabbit extends Object"` and `class Rabbit`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 كما نعلم ، فإن الصيغة "الممتدة" تضع نموذجين أوليين: 1. بين "النموذج" لوظائف المنشئ (للطرق). 2. بين وظائف المنشئ أنفسهم (للأساليب الثابتة). +<<<<<<< HEAD في حالتنا ، تعني كلمة "أرنب يمتد الكائن" ما يلي: +======= +In the case of `class Rabbit extends Object` it means: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run class Rabbit extends Object {} @@ -37,7 +45,11 @@ alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true alert( Rabbit.__proto__ === Object ); // (2) true ``` +<<<<<<< HEAD إذن يوفر "الأرنب" الآن إمكانية الوصول إلى الأساليب الثابتة لـ "الكائن" عبر "الأرنب" ، على النحو التالي: +======= +So `Rabbit` now provides access to the static methods of `Object` via `Rabbit`, like this: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run class Rabbit extends Object {} @@ -67,7 +79,11 @@ alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error لذا `Rabbit` لا يوفر الوصول إلى الأساليب الثابتة لـ "الكائن" في هذه الحالة. +<<<<<<< HEAD بالمناسبة ، يحتوي `Function.prototype` على طرق وظيفية" عامة "، مثل` call` و` bind` وما إلى ذلك. وهي متاحة في النهاية في كلتا الحالتين ، لأن مُنشئ `Object` المدمج ،` Object .__ proto__ = == Function.prototype`. +======= +By the way, `Function.prototype` also has "generic" function methods, like `call`, `bind` etc. They are ultimately available in both cases, because for the built-in `Object` constructor, `Object.__proto__ === Function.prototype`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ها هي الصورة: diff --git a/1-js/09-classes/03-static-properties-methods/article.md b/1-js/09-classes/03-static-properties-methods/article.md index 56d6e483b..b98cba42e 100644 --- a/1-js/09-classes/03-static-properties-methods/article.md +++ b/1-js/09-classes/03-static-properties-methods/article.md @@ -2,7 +2,13 @@ كما يمكننا تعيين خاصية لدالة الclass ذاتها, وليس لـ `"prototype"` الخاص بها. مثل هذه الدوال تسمى بـ*static*. +<<<<<<< HEAD في الـ class, يتم إلحاقهم بكلمة رئيسية `static`'' ، مثل هذا: +======= +We can also assign a method to the class as a whole. Such methods are called *static*. + +In a class declaration, they are prepended by `static` keyword, like this: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run class User { @@ -30,9 +36,17 @@ User.staticMethod(); // true قيمة `this` في`User.staticMethod ()`هي مُنشئ الفئة` المستخدم` نفسه (قاعدة "object قبل النقطة"). +<<<<<<< HEAD عادة ، يتم استخدام الأساليب الثابتة لتنفيذ الوظائف التي تنتمي إلى الفئة ، ولكن ليس لأي object معين منها. على سبيل المثال ، لدينا objects `Article` ونحتاج إلى وظيفة لمقارنتها. الحل الطبيعي هو إضافة طريقة `Article.compare` ، على النحو التالي: +======= +Usually, static methods are used to implement functions that belong to the class as a whole, but not to any particular object of it. + +For instance, we have `Article` objects and need a function to compare them. + +A natural solution would be to add `Article.compare` static method: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run class Article { @@ -62,9 +76,17 @@ articles.sort(Article.compare); alert( articles[0].title ); // CSS ``` +<<<<<<< HEAD هنا "Article.compare" تقف المقالات "أعلاه" ، كوسيلة لمقارنتها. إنها ليست دالة لـ `article` ، ولكن بدلاً من الـ `class` بأكمله. مثال آخر هو ما يسمى طريقة "المصنع". تخيل ، نحن بحاجة إلى طرق قليلة لإنشاء مقال: +======= +Here `Article.compare` method stands "above" articles, as a means to compare them. It's not a method of an article, but rather of the whole class. + +Another example would be a so-called "factory" method. + +Let's say, we need multiple ways to create an article: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 1. إنشاء بواسطة معلمات معينة (`العنوان` ،` التاريخ` وما إلى ذلك). 2. إنشاء مقال فارغ بتاريخ اليوم. @@ -72,7 +94,11 @@ alert( articles[0].title ); // CSS يمكن تنفيذ الطريقة الأولى من قبل المنشئ. وللثاني يمكننا عمل طريقة ثابتة للفئة. +<<<<<<< HEAD مثل `Article.createTodays()` هنا: +======= +Such as `Article.createTodays()` here: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run class Article { @@ -100,9 +126,25 @@ alert( article.title ); // Today's digest ```js // assuming Article is a special class for managing articles +<<<<<<< HEAD // static method to remove the article: Article.remove({ id: 12345 }); +======= +// static method to remove the article by id: +Article.remove({id: 12345}); +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 +``` + +````warn header="Static methods aren't available for individual objects" +Static methods are callable on classes, not on individual objects. + +E.g. such code won't work: + +```js +// ... +article.createTodays(); /// Error: article.createTodays is not a function ``` +```` ## Static properties diff --git a/1-js/09-classes/04-private-protected-properties-methods/article.md b/1-js/09-classes/04-private-protected-properties-methods/article.md index 223926857..043f170b3 100644 --- a/1-js/09-classes/04-private-protected-properties-methods/article.md +++ b/1-js/09-classes/04-private-protected-properties-methods/article.md @@ -113,7 +113,7 @@ class CoffeeMachine { let coffeeMachine = new CoffeeMachine(100); // add water -coffeeMachine.waterAmount = -10; // Error: Negative water +coffeeMachine.waterAmount = -10; // _waterAmount will become 0, not -10 ``` Now the access is under control, so setting the water amount below zero becomes impossible. @@ -188,7 +188,11 @@ new CoffeeMachine().setWaterAmount(100); يجب أن يبدأ الأفراد بـ `#`. يمكن الوصول إليها فقط من داخل الفصل. +<<<<<<< HEAD على سبيل المثال ، إليك خاصية `# waterLimit` الخاصة والطريقة الخاصة لفحص المياه` # checkWater`: +======= +For instance, here's a private `#waterLimit` property and the water-checking private method `#fixWaterAmount`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run class CoffeeMachine { diff --git a/1-js/09-classes/06-instanceof/article.md b/1-js/09-classes/06-instanceof/article.md index b50c937b7..4dbc8e977 100644 --- a/1-js/09-classes/06-instanceof/article.md +++ b/1-js/09-classes/06-instanceof/article.md @@ -87,9 +87,17 @@ alert(arr instanceof Object); // نعم أمّا لو كنّا في حالة وراثة، فستتوقّف عملية المطابقة عند الخطوة الثانية: +<<<<<<< HEAD ```js run class Animal {} class Rabbit extends Animal {} +======= + // rabbit.__proto__ === Animal.prototype (no match) + *!* + // rabbit.__proto__.__proto__ === Animal.prototype (match!) + */!* + ``` +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 let rabbit = new Rabbit(); *!* diff --git a/1-js/09-classes/07-mixins/article.md b/1-js/09-classes/07-mixins/article.md index 3ae2bfba4..b7ee9904b 100644 --- a/1-js/09-classes/07-mixins/article.md +++ b/1-js/09-classes/07-mixins/article.md @@ -103,7 +103,11 @@ new User("Dude").sayHi(); // مرحباً Dude! هذا بسبب الدالة `sayHi` و `sayBye` الذي تم إنشاؤهما فى `sayHiMixin`. لذا علي الرغم من انه تم نسخهم, `[[HomeObject]]` الخاص بهم هو مرجع الخاصية الداخلية `sayHiMixin`, كما هو موضح فى الصورة اعلاه. +<<<<<<< HEAD كما أن `super` يبحث عن الدالة الأب في `[[HomeObject]].[[Prototype]]`, هذا يعني انه يبحث في `sayHiMixin.[[Prototype]]`, و ليس `User.[[Prototype]]`. +======= +As `super` looks for parent methods in `[[HomeObject]].[[Prototype]]`, that means it searches `sayHiMixin.[[Prototype]]`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ## EventMixin diff --git a/1-js/10-error-handling/2-custom-errors/article.md b/1-js/10-error-handling/2-custom-errors/article.md index ead4734ea..bf77d4d43 100644 --- a/1-js/10-error-handling/2-custom-errors/article.md +++ b/1-js/10-error-handling/2-custom-errors/article.md @@ -15,6 +15,7 @@ ``` let json = `{ "name": "John", "age": 30 }`; ``` +<<<<<<< HEAD سنستعمل في الشيفرة التابِع `JSON.parse`، وإن استلم كائن `json` معطوب رمى خطأ `SyntaxError`. ولكن، حتّى لو كان الكائن صحيحًا صياغيًا، فلا يعني هذا أنّ المستخدم صالحًا أيضًا، أم لا؟ لربّما لا يحتوي بعض البيانات مثل خاصيتي الاسم `json` والعمر `name` الضروريتين للمستخدمين. @@ -26,6 +27,19 @@ let json = `{ "name": "John", "age": 30 }`; لنعرف ما نحاول توسعته: ``` // شيفرة مبسّطة لصنف الخطأ ‫Error المضمّن في لغة جافا سكريبت نفسها +======= + +Internally, we'll use `JSON.parse`. If it receives malformed `json`, then it throws `SyntaxError`. But even if `json` is syntactically correct, that doesn't mean that it's a valid user, right? It may miss the necessary data. For instance, it may not have `name` and `age` properties that are essential for our users. + +Our function `readUser(json)` will not only read JSON, but check ("validate") the data. If there are no required fields, or the format is wrong, then that's an error. And that's not a `SyntaxError`, because the data is syntactically correct, but another kind of error. We'll call it `ValidationError` and create a class for it. An error of that kind should also carry the information about the offending field. + +Our `ValidationError` class should inherit from the `Error` class. + +The `Error` class is built-in, but here's its approximate code so we can understand what we're extending: + +```js +// The "pseudocode" for the built-in Error class defined by JavaScript itself +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 class Error { constructor(message) { this.message = message; @@ -100,6 +114,7 @@ throw err; // خطأ لا نعرفه، علينا إعادة رميه (**) } else if (err.name == "SyntaxError") { // (*) // ... ``` +<<<<<<< HEAD ولكنّ استعمال `instanceof` أفضل بكثير إذ يحدث ونوسّع مستقبلًا الصنف `ValidationError` بأصناف فرعية منه مثل `PropertyRequiredError`، والفحص عبر `instanceof` سيظلّ يعمل للأصناف الموروثة منه، كما من المهمّ أن تُعيد كتلة `catch` رمي الأخطاء التي لا تفهمها، كما في السطر `(**)`. ليس على هذه الكتلة إلّا التعامل مع @@ -110,6 +125,18 @@ throw err; // خطأ لا نعرفه، علينا إعادة رميه (**) موجودة أو كان نسقها خطأ (مثل تقديم سلسلة نصية قيمةً للعمر `age`). لنصنع الصنف .... `PropertyRequiredError` ونستعمله فقط للخاصيات غير الموجودة، وسيحتوي على أيّة معلومات إضافية عن الخاصية الناقصة. ``` +======= + +The `instanceof` version is much better, because in the future we are going to extend `ValidationError`, make subtypes of it, like `PropertyRequiredError`. And `instanceof` check will continue to work for new inheriting classes. So that's future-proof. + +Also it's important that if `catch` meets an unknown error, then it rethrows it in the line `(**)`. The `catch` block only knows how to handle validation and syntax errors, other kinds (caused by a typo in the code or other unknown reasons) should fall through. + +## Further inheritance + +The `ValidationError` class is very generic. Many things may go wrong. The property may be absent or it may be in a wrong format (like a string value for `age` instead of a number). Let's make a more concrete class `PropertyRequiredError`, exactly for absent properties. It will carry additional information about the property that's missing. + +```js run +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 class ValidationError extends Error { constructor(message) { diff --git a/1-js/11-async/01-callbacks/article.md b/1-js/11-async/01-callbacks/article.md index 37412d2fb..1f959da69 100644 --- a/1-js/11-async/01-callbacks/article.md +++ b/1-js/11-async/01-callbacks/article.md @@ -28,7 +28,11 @@ function loadScript(src) { } ``` +<<<<<<< HEAD يتم إلحاق المستند الجديد ، الذي تم إنشاؤه ديناميكيًا ، العنصر ` ``` +<<<<<<< HEAD ولو أردنا أن ننشئ متغير عام على مستوى النافذة يمكننا تعيينه صراحة للمتغيّر `window` ويمكننا الوصول إليه هكذا `window.user`. ولكن لابد من وجود سبب وجيهٍ لذلك. +======= +```smart +In the browser, we can make a variable window-level global by explicitly assigning it to a `window` property, e.g. `window.user = "John"`. + +Then all scripts will see it, both with `type="module"` and without it. + +That said, making such global variables is frowned upon. Please try to avoid them. +``` +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ### تقييم شيفرة الوحدة لمرة واحدة فقط +<<<<<<< HEAD لو استوردتَ نفس الوحدة في أكثر من مكان، فلا تُنفّذ شيفرتها إلّا مرة واحدة، وبعدها تُصدّر إلى من استوردها. ولهذا توابع مهمّ معرفتها. لنرى بعض الأمثلة. +======= +If the same module is imported into multiple other modules, its code is executed only once, upon the first import. Then its exports are given to all further importers. + +The one-time evaluation has important consequences, that we should be aware of. + +Let's see a couple of examples. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 أولًا، لو كان لشيفرة الوحدة التي ستُنفّذ أيّ تأثيرات (مثل عرض رسالة أو ما شابه)، فاستيرادها أكثر من مرّة سيشغّل ذلك التأثير مرة واحدة، وهي أول مرة فقط: @@ -134,9 +179,17 @@ import `./alert.js`; // ‫نُفّذت شيفرة الوحدة! import `./alert.js`; // (لا نرى شيئًا هنا) ``` +<<<<<<< HEAD في الواقع، فشيفرات الوحدات عالية المستوى في بنية البرمجية لا تُستعمل إلّا لتمهيد بنى البيانات الداخلية وإنشائها. ولو أردنا شيئًا نُعيد استعماله، نُصدّر الوحدة. الآن حان وقت مثال مستواه متقدّم أكثر. +======= +The second import shows nothing, because the module has already been evaluated. + +There's a rule: top-level module code should be used for initialization, creation of module-specific internal data structures. If we need to make something callable multiple times - we should export it as a function, like we did with `sayHi` above. + +Now, let's consider a deeper example. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لنقل بأنّ هناك وحدة تُصدّر كائنًا: @@ -161,6 +214,7 @@ import {admin} from './admin.js'; alert(admin.name); // Pete *!* +<<<<<<< HEAD // ‫كِلا الملفين ‎1.js و ‎2.js سيستوردان نفس الكائن // ‫التغييرات الّتي ستحدثُ في الملف ‎1.js ستكون مرئية في الملف ‎2.js */!* @@ -171,45 +225,90 @@ alert(admin.name); // Pete يتيح لنا هذا السلوك ”ضبط“ الوحدة عند أوّل استيراد لها، فنضبط خاصياتها المرة الأولى، ومتى ما استوُردت مرة أخرى تكون جاهزة. فمثلًا قد تقدّم لنا وحدة `admin.js` بعض المزايا ولكن تطلب أن تأتي امتيازات الإدارة من خارج كائن `admin` إلى داخله: +======= +// Both 1.js and 2.js reference the same admin object +// Changes made in 1.js are visible in 2.js +*/!* +``` + +As you can see, when `1.js` changes the `name` property in the imported `admin`, then `2.js` can see the new `admin.name`. + +That's exactly because the module is executed only once. Exports are generated, and then they are shared between importers, so if something changes the `admin` object, other importers will see that. + +**Such behavior is actually very convenient, because it allows us to *configure* modules.** + +In other words, a module can provide a generic functionality that needs a setup. E.g. authentication needs credentials. Then it can export a configuration object expecting the outer code to assign to it. + +Here's the classical pattern: +1. A module exports some means of configuration, e.g. a configuration object. +2. On the first import we initialize it, write to its properties. The top-level application script may do that. +3. Further imports use the module. + +For instance, the `admin.js` module may provide certain functionality (e.g. authentication), but expect the credentials to come into the `config` object from outside: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` // 📁 admin.js -export let admin = { }; +export let config = { }; export function sayHi() { - alert(`Ready to serve, ${admin.name}!`); + alert(`Ready to serve, ${config.user}!`); } ``` +<<<<<<< HEAD نضبط في `init.js` (أوّل نص برمجي لتطبيقنا) المتغير `admin.name`. بعدها سيراه كلّ من أراد بما في ذلك الاستدعاءات من داخل وحدة `admin.js` نفسها: +======= +Here, `admin.js` exports the `config` object (initially empty, but may have default properties too). + +Then in `init.js`, the first script of our app, we import `config` from it and set `config.user`: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` // 📁 init.js -import {admin} from './admin.js'; -admin.name = "Pete"; +import {config} from './admin.js'; +config.user = "Pete"; ``` +<<<<<<< HEAD ويمكن لوحدة أخرى استعمال `admin.name`: ``` // 📁 other.js import {admin, sayHi} from './admin.js'; +======= +...Now the module `admin.js` is configured. + +Further importers can call it, and it correctly shows the current user: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 -alert(admin.name); // *!*Pete*/!* +```js +// 📁 another.js +import {sayHi} from './admin.js'; sayHi(); // Ready to serve, *!*Pete*/!*! ``` + ### import.meta يحتوي الكائن `import.meta` على معلومات الوحدة الحالية. +<<<<<<< HEAD ويعتمد محتواها على البيئة الحالية، ففي المتصفّحات يحتوي على عنوان النص البرمجي أو عنوان صفحة الوِب الحالية لو كان داخل HTML: +======= +Its content depends on the environment. In the browser, it contains the URL of the script, or a current webpage URL if inside HTML: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` html run height=0 ``` @@ -238,7 +337,11 @@ html run height=0 لو كنت تقرأ هذا الفصل لأول مرة، أو لم تكن تستعمل المحرّك في المتصفّح فيمكنك تخطّي هذا القسم. +<<<<<<< HEAD ### سكربتات الوحدات مؤجلة +======= +You may want to skip this section for now if you're reading for the first time, or if you don't use JavaScript in a browser. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 دائمًا ما تكون سكربتات الوحدات مؤجلة، ومشابهة لتأثير السِمة `defer` (الموضحة في هذا [المقال](https://javascript.info/script-async-defer))، لكل من السكربتات المضمّنة والخارجية. @@ -248,7 +351,11 @@ html run height=0 - تنتظر السكربتات المعتمدة على الوحدات حتّى يجهز مستند HTML تمامًا (حتّى لو كانت صغيرة وحُمّلت بنحوٍ أسرع من HTML) وتُشغّل عندها. - تحافظ على الترتيب النسبي للسكربتات: فالسكربت ذو الترتيب الأول ينفذّ أولًا. +<<<<<<< HEAD ويسبّب هذا بأن ”ترى“ السكربتات المعتمدة على الوحدات صفحة HTML المحمّلة كاملة بما فيه عناصر الشجرة أسفلها. +======= +As a side effect, module scripts always "see" the fully loaded HTML-page, including HTML elements below them. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 مثال: diff --git a/1-js/13-modules/02-import-export/article.md b/1-js/13-modules/02-import-export/article.md index 3b5ba80da..3f3692b4b 100644 --- a/1-js/13-modules/02-import-export/article.md +++ b/1-js/13-modules/02-import-export/article.md @@ -95,12 +95,16 @@ say.sayBye('John'); لنقل مثلًا بأنّا أضفنا مكتبة خارجية اسمها `say.js` إلى مشروعنا، وفيها دوالّ عديدة: +<<<<<<< HEAD ``` // 📁 say.js export function sayHi() { ... } export function sayBye() { ... } export function becomeSilent() { ... } ``` +======= +1. Modern build tools ([webpack](https://webpack.js.org/) and others) bundle modules together and optimize them to speedup loading and remove unused stuff. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 هكذا نستعمل واحدة فقط من دوالّ `say.js` في مشروعنا: @@ -406,10 +410,18 @@ We can come across two problems with it: إذا رغبنا في إعادة تصدير التصديرات المبدئية والتي لها أسماء أيضًا، فسنحتاج إلى العبارتين: +<<<<<<< HEAD ``` export * from './user.js'; // لإعادة تصدير التصديرات الّتي لها أسماء export {default} from './user.js'; // لإعادة تصدير التصديرات المبدئية ``` +======= + If we'd like to re-export both named and default exports, then two statements are needed: + ```js + export * from './user.js'; // to re-export named exports + export {default} from './user.js'; // to re-export the default export + ``` +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 Such oddities of re-exporting a default export are one of the reasons why some developers don't like default exports and prefer named ones. diff --git a/1-js/99-js-misc/04-reference-type/article.md b/1-js/99-js-misc/04-reference-type/article.md index c291abc05..27641762e 100644 --- a/1-js/99-js-misc/04-reference-type/article.md +++ b/1-js/99-js-misc/04-reference-type/article.md @@ -3,7 +3,14 @@ ```warn header="خصائص متقدمه فى اللغه" هذه المقالة تقوم بتغطية موضوع متقدم, لفهم بعض الحالات بشكل أفضل. +<<<<<<< HEAD إنها ليست مهمة. يعيش العديد من المطورين ذوي الخبرة بشكل جيد دون معرفة ذلك. تابع القراءة إذا كنت تريد معرفة كيفية عمل الأشياء خلف الكواليس. +======= +```warn header="In-depth language feature" +This article covers an advanced topic, to understand certain edge-cases better. + +It's not important. Many experienced developers live fine without knowing it. Read on if you want to know how things work under the hood. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` قد تفقد استدعاء تابع تم تقييمه بشكل ديناميكي `this`. @@ -60,7 +67,7 @@ user.hi(); let user = { name: "John", hi() { alert(this.name); } -} +}; *!* // تقسيم الحصول على واستدعاء التابع في سطرين diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md index e61f89e66..0a27b6acc 100644 --- a/2-ui/1-document/01-browser-environment/article.md +++ b/2-ui/1-document/01-browser-environment/article.md @@ -1,10 +1,18 @@ # بيئة المتصفح ومواصفاته +<<<<<<< HEAD تم إنشاء لغة JavaScript فى البداية لمتصفحات الويب، منذ ذلك الحين أصبح لها استخدمات ومنصات عديدة. قد تكون تلك المنصة متصفح، خادوم ويب او *مضيف* آخر ولكن لكل واحد منهم وظيفته الخاصة التى يقوم بها .خصائص الـ JavaScript تسمى هذا *ببيئة المضيف*. توفر بيئة العمل كائنات و وظائف خاصة إلى نواة اللغة. تعتبر المتصفحات وسيلة للتحكم بصفحات الويب. وتوفر الـ Node.js خواص من جانب الخادم, وما إلى ذلك. +======= +The JavaScript language was initially created for web browsers. Since then, it has evolved into a language with many uses and platforms. + +A platform may be a browser, or a web-server or another *host*, or even a "smart" coffee machine if it can run JavaScript. Each of these provides platform-specific functionality. The JavaScript specification calls that a *host environment*. + +A host environment provides its own objects and functions in addition to the language core. Web browsers give a means to control web pages. Node.js provides server-side features, and so on. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 إليك نظرة شاملة لما لدينا عند تشغيل جافا سكريبت في متصفح ويب: @@ -12,11 +20,18 @@ لدينا كائن "الجذر" `window` . له دورين: +<<<<<<< HEAD 1. أولا، هو كائن عام لشفرة JavaScript، كما وصِف فى فصل 2. ثانيًا، يمثل "نافذة المتصفح" ويوفر طرقا للتحكم فيها. على سبيل المثال، نستخدمها هنا ككائن عام: +======= +1. First, it is a global object for JavaScript code, as described in the chapter . +2. Second, it represents the "browser window" and provides methods to control it. -```js run +For instance, we can use it as a global object: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 + +```js run global function sayHi() { alert("Hello"); } @@ -25,12 +40,17 @@ function sayHi() { window.sayHi(); ``` +<<<<<<< HEAD وهنا نستخدمها كنافذة متصفح لرؤية ارتفاع النافذة: +======= +And we can use it as a browser window, to show the window height: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```js run alert(window.innerHeight); // ارتفاع النافذة الداخلية ``` +<<<<<<< HEAD هناك المزيد من الأساليب والخصائص الخاصة بالنافذة، وسنغطيها لاحقًا. ## DOM (Document Object Model) @@ -38,6 +58,13 @@ alert(window.innerHeight); // ارتفاع النافذة الداخلية يمثل نموذج كائن المستند، أو اختصار DOM، محتوى الصفحة بالكامل ككائنات يمكن تعديلها. كائن `document` هو "نقطة الدخول" الرئيسية للصفحة. يمكننا تغيير أو إنشاء أي شيء على الصفحة باستخدامه. +======= +There are more window-specific methods and properties, which we'll cover later. + +## DOM (Document Object Model) + +The Document Object Model, or DOM for short, represents all page content as objects that can be modified. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال: @@ -49,18 +76,30 @@ document.body.style.background = "red"; setTimeout(() => document.body.style.background = "", 1000); ``` +<<<<<<< HEAD استخدمنا هنا `document.body.style`، ولكن هناك الكثير والكثير. يتم وصف الخصائص والأساليب في المواصفات: **DOM الحالة القياسية** فى +======= +Here, we used `document.body.style`, but there's much, much more. Properties and methods are described in the specification: [DOM Living Standard](https://dom.spec.whatwg.org). +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```smart header="DOM ليس فقط للمتصفحات" توضح مواصفات الـ DOM بنية المستند وتوفر كائنات لمعالجته. هناك أدوات غير المتصفح تستخدم الـ DOM أيضًا. +<<<<<<< HEAD على سبيل المثال، يمكن للبرامج النصية من جانب الخادوم التي تقوم بتنزيل صفحات HTML ومعالجتها أيضًا استخدام الـDOM. قد يدعمون جزءًا فقط من المواصفات بالرغم من ذلك. +======= +For instance, server-side scripts that download HTML pages and process them can also use the DOM. They may support only a part of the specification though. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` يتم استخدام CSSOM مع الـ DOM عند تعديل قواعد النمط للمستند. من الناحية العملية، نادرًا ما تكون CSSOM مطلوبة، لأن قواعد CSS عادة ما تكون ثابتة. نادرًا ما نحتاج إلى إضافة/إزالة قواعد الـ CSS من الـ JavaScript، ولكن هذا ممكن أيضًا. +<<<<<<< HEAD ```smart header="CSSOM للتصنيف" يتم تنظيم قواعد CSS وأوراق الأنماط بطريقة مختلفة عن HTML. هناك مواصفات منفصلة، [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/), يشرح كيف يتم تمثيلها ككائنات، وكيفية قراءتها وكتابتها. +======= +The CSSOM is used together with the DOM when we modify style rules for the document. In practice though, the CSSOM is rarely required, because we rarely need to modify CSS rules from JavaScript (usually we just add/remove CSS classes, not modify their CSS rules), but that's also possible. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ## BOM (Browser Object Model) @@ -69,8 +108,13 @@ setTimeout(() => document.body.style.background = "", 1000); على سبيل المثال: +<<<<<<< HEAD - يوفر كائن [navigator](mdn:api/Window/navigator) معلومات أساسية حول المتصفح ونظام التشغيل. هناك العديد من الخصائص ، لكن الأكثر شهرة هما: `navigator.userAgent` -- حول المتصفح الحالي، و `navigator.platform` -- حول النظام الأساسي (يمكن أن يساعد على تحديد الاختلاف بين Windows/Linux/Mac إلخ). - يسمح لنا كائن [location](mdn:api/Window/location) بقراءة عنوان URL الحالي ويمكنه إعادة توجيه المتصفح إلى عنوان جديد. +======= +- The [navigator](mdn:api/Window/navigator) object provides background information about the browser and the operating system. There are many properties, but the two most widely known are: `navigator.userAgent` -- about the current browser, and `navigator.platform` -- about the platform (can help to differentiate between Windows/Linux/Mac etc). +- The [location](mdn:api/Window/location) object allows us to read the current URL and can redirect the browser to a new one. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 إليك كيفية استخدام كائن `location`: @@ -81,33 +125,60 @@ if (confirm("Go to Wikipedia?")) { } ``` +<<<<<<< HEAD تعد الدوال `alert/confirm/prompt` جزءًا من الـ BOM: فهي لا تتعلق مباشرة بالمستند، ولكنها تمثل طرق متصفح خالصة للتواصل مع المستخدم. ```smart header="مواصفات" BOM هو جزء من مواصفات[HTML specification](https://html.spec.whatwg.org). نعم سمعت ذلك جيدا. مواصفات الـ HTML في لا تتعلق فقط بـ "لغة HTML" (العلامات، السمات) ،ولكنه يغطي أيضًا مجموعة من الكائنات والأساليب وإضافات DOM الخاصة بالمتصفح. هذا هو "الـ HTML بعبارات عامة".أيضًا، تحتوي بعض الأجزاء على مواصفات إضافية مدرجة في . +======= +The functions `alert/confirm/prompt` are also a part of the BOM: they are not directly related to the document, but represent pure browser methods for communicating with the user. + +```smart header="Specifications" +The BOM is a part of the general [HTML specification](https://html.spec.whatwg.org). + +Yes, you heard that right. The HTML spec at is not only about the "HTML language" (tags, attributes), but also covers a bunch of objects, methods, and browser-specific DOM extensions. That's "HTML in broad terms". Also, some parts have additional specs listed at . +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ``` ## ملخص بالحديث عن المعايير، لدينا: +<<<<<<< HEAD مواصفات الـ DOM : يصف هيكل الوثيقة والتلاعب والأحداث، راجع . مواصفات الـ CSSOM : يصف أوراق الأنماط وقواعد الأنماط والتلاعب بها وربطها بالمستندات، راجع . +======= +DOM specification +: Describes the document structure, manipulations, and events, see . + +CSSOM specification +: Describes stylesheets and style rules, manipulations with them, and their binding to documents, see . +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 مواصفات الـ HTML : تصف لغة HTML (مثل العلامات) وكذلك BOM (طراز كائن المتصفح) - وظائف المتصفح المختلفة: `setTimeout`, `alert`, `location` وما إلى ذلك، راجع . يأخذ مواصفات الـ DOM ويوسعها بالعديد من الخصائص والأساليب الإضافية. بالإضافة إلى ذلك، يتم وصف بعض الفئات بشكل منفصل في . +<<<<<<< HEAD يرجى ملاحظة هذه الروابط، حيث أن هناك الكثير من الأشياء لمعرفة أنه من المستحيل تغطية وتذكر كل شيء. عندما ترغب في القراءة عن خاصية أو طريقة ما، فإن دليل Mozilla على هو أيضًا مورد جيد، ولكن المواصفات المقابلة قد تكون أفضل: إنها أكثر تعقيدًا وأطول وقتًا للقراءة، ولكنها ستجعل معرفتك الأساسية سليمة وكاملة. +======= +Please note these links, as there's so much to learn that it's impossible to cover everything and remember it all. + +When you'd like to read about a property or a method, the Mozilla manual at is also a nice resource, but the corresponding spec may be better: it's more complex and longer to read, but will make your fundamental knowledge sound and complete. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 للعثور على شيء ما، غالبًا ما يكون من الملائم استخدام البحث على الإنترنت "WHATWG [مصطلح]" او "MDN [مصطلح]", مثل , . +<<<<<<< HEAD سنبدأ الآن في تعلم الـ DOM, لأن المستند يلعب الدور المركزي في واجهة المستخدم. +======= +Now, we'll get down to learning the DOM, because the document plays the central role in the UI. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md index 126816e5c..78cd05f06 100644 --- a/2-ui/1-document/02-dom-nodes/article.md +++ b/2-ui/1-document/02-dom-nodes/article.md @@ -52,7 +52,7 @@ setTimeout(() => (document.body.style.background = ''), 3000); // لإزالة
diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md index f7123d70d..b5f03098c 100644 --- a/2-ui/1-document/03-dom-navigation/article.md +++ b/2-ui/1-document/03-dom-navigation/article.md @@ -214,7 +214,7 @@ alert( document.body.previousSibling ); // HTMLHeadElement ## Element-only navigation -Navigation properties listed above refer to *all* nodes. For instance, in `childNodes` we can see both text nodes, element nodes, and even comment nodes if there exist. +Navigation properties listed above refer to *all* nodes. For instance, in `childNodes` we can see both text nodes, element nodes, and even comment nodes if they exist. But for many tasks we don't want text or comment nodes. We want to manipulate element nodes that represent tags and form the structure of the page. diff --git a/2-ui/1-document/04-searching-elements-dom/article.md b/2-ui/1-document/04-searching-elements-dom/article.md index 5af6435ce..de47eac9f 100644 --- a/2-ui/1-document/04-searching-elements-dom/article.md +++ b/2-ui/1-document/04-searching-elements-dom/article.md @@ -55,7 +55,7 @@ Also, there's a global variable named by `id` that references the element: ``` ```warn header="Please don't use id-named global variables to access elements" -This behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), so it's kind of standard. But it is supported mainly for compatibility. +This behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), so it's a kind of standard. But it is supported mainly for compatibility. The browser tries to help us by mixing namespaces of JS and DOM. That's fine for simple scripts, inlined into HTML, but generally isn't a good thing. There may be naming conflicts. Also, when one reads JS code and doesn't have HTML in view, it's not obvious where the variable comes from. @@ -116,7 +116,7 @@ In other words, the result is the same as `elem.querySelectorAll(css)[0]`, but t Previous methods were searching the DOM. -The [elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`. +The [elem.matches(css)](https://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`. The method comes in handy when we are iterating over elements (like in an array or something) and trying to filter out those that interest us. @@ -154,7 +154,7 @@ For instance:
  • Chapter 1
  • -
  • Chapter 1
  • +
  • Chapter 2
diff --git a/2-ui/1-document/05-basic-dom-node-properties/article.md b/2-ui/1-document/05-basic-dom-node-properties/article.md index d9a2afa0c..b0c559e91 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/article.md +++ b/2-ui/1-document/05-basic-dom-node-properties/article.md @@ -10,7 +10,11 @@ تنتمي كل عقدة DOM إلى الفئة المضمنة المقابلة. +<<<<<<< HEAD جذر التسلسل الهرمي هو [EventTarget] (https://dom.spec.whatwg.org/#eventtarget) ، الموروث بواسطة [Node] (http://dom.spec.whatwg.org/#interface-node ) ، وترث العقد الأخرى منه. +======= +The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](https://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 هذه هي الصورة والتفسيرات التي يجب اتباعها: @@ -18,6 +22,7 @@ الclasses هي: +<<<<<<< HEAD - [EventTarget] (https://dom.spec.whatwg.org/#eventtarget) - هي فئة الجذر "المجردة". لا يتم إنشاء كائنات هذه الفئة أبدًا. إنه بمثابة قاعدة ، بحيث تدعم جميع عقد DOM ما يسمى "الأحداث" ، وسندرسها لاحقًا. - [Node] (http://dom.spec.whatwg.org/#interface-node) - هي أيضًا فئة "مجردة" ، تعمل كقاعدة لعقد DOM. يوفر وظائف الشجرة الأساسية: `motherNode` و` nextSibling` و` childNodes` وما إلى ذلك (فهي عبارة عن حروف). لا يتم إنشاء كائنات فئة "العقدة" مطلقًا. ولكن هناك فئات عقدة محددة ترث منه ، وهي: `Text` للعقد النصية و` Element` لعقد العناصر والمزيد من الأنواع الغريبة مثل `Comment` لعقد التعليق. - [Element] (http://dom.spec.whatwg.org/#interface-element) - هي فئة أساسية لعناصر DOM. يوفر التنقل على مستوى العنصر مثل `nextElementSibling` و` children` وطرق البحث مثل `getElementsByTagName` و` querySelector`. لا يدعم المتصفح HTML فحسب ، بل يدعم أيضًا XML و SVG. تعمل فئة `Element` كقاعدة لفئات أكثر تحديدًا:` SVGElement` و `XMLElement` و` HTMLElement`. @@ -28,6 +33,41 @@     - ... وهكذا ، كل علامة لها فئة خاصة بها قد توفر خصائص وأساليب معينة. لذلك ، فإن المجموعة الكاملة من الخصائص والأساليب لعقدة معينة تأتي نتيجة الميراث. +======= +- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root "abstract" class for everything. + + Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called "events", we'll study them later. + +- [Node](https://dom.spec.whatwg.org/#interface-node) -- is also an "abstract" class, serving as a base for DOM nodes. + + It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are other classes that inherit from it (and so inherit the `Node` functionality). + +- [Document](https://dom.spec.whatwg.org/#interface-document), for historical reasons often inherited by `HTMLDocument` (though the latest spec doesn't dictate it) -- is a document as a whole. + + The `document` global object belongs exactly to this class. It serves as an entry point to the DOM. + +- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) -- an "abstract" class, inherited by: + - [Text](https://dom.spec.whatwg.org/#interface-text) -- the class corresponding to a text inside elements, e.g. `Hello` in `

Hello

`. + - [Comment](https://dom.spec.whatwg.org/#interface-comment) -- the class for comments. They are not shown, but each comment becomes a member of DOM. + +- [Element](https://dom.spec.whatwg.org/#interface-element) -- is the base class for DOM elements. + + It provides element-level navigation like `nextElementSibling`, `children` and searching methods like `getElementsByTagName`, `querySelector`. + + A browser supports not only HTML, but also XML and SVG. So the `Element` class serves as a base for more specific classes: `SVGElement`, `XMLElement` (we don't need them here) and `HTMLElement`. + +- Finally, [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) is the basic class for all HTML elements. We'll work with it most of the time. + + It is inherited by concrete HTML elements: + - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- the class for `` elements, + - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- the class for `` elements, + - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- the class for `` elements, + - ...and so on. + +There are many other tags with their own classes that may have specific properties and methods, while some elements, such as ``, `
`, `
` do not have any specific properties, so they are instances of `HTMLElement` class. + +So, the full set of properties and methods of a given node comes as the result of the chain of inheritance. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 على سبيل المثال ، لنفكر في كائن DOM لعنصر ``. وهي تنتمي إلى فئة [HTMLInputElement] (https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement). @@ -133,10 +173,10 @@ alert(document.body instanceof EventTarget); // true ``` +<<<<<<< HEAD ولكن هناك استثناءات ، على سبيل المثال ، تتم مزامنة `input.value` فقط من السمة -> إلى الخاصية ، ولكن ليس مرة أخرى: +======= +But there are exclusions, for instance `input.value` synchronizes only from attribute -> property, but not back: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```html run diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html index de8ec9aee..84ee26f19 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html @@ -45,7 +45,7 @@ function clockStart() { // set a new interval only if the clock is stopped // otherwise we would rewrite the timerID reference to the running interval and wouldn't be able to stop the clock ever again - if (!timerId) { + if (!timerId) { timerId = setInterval(update, 1000); } update(); // <-- start right now, don't wait 1 second till the first setInterval works @@ -56,7 +56,6 @@ timerId = null; // <-- clear timerID to indicate that the clock has been stopped, so that it is possible to start it again in clockStart() } - clockStart(); diff --git a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md index 6b85168b9..3d1f6698f 100644 --- a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md +++ b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md @@ -1,9 +1,9 @@ The HTML in the task is incorrect. That's the reason of the odd thing. -The browser has to fix it automatically. But there may be no text inside the ``: according to the spec only table-specific tags are allowed. So the browser adds `"aaa"` *before* the `
`. +The browser has to fix it automatically. But there may be no text inside the `
`: according to the spec only table-specific tags are allowed. So the browser shows `"aaa"` *before* the `
`. Now it's obvious that when we remove the table, it remains. -The question can be easily answered by exploring the DOM using the browser tools. It shows `"aaa"` before the `
`. +The question can be easily answered by exploring the DOM using the browser tools. You'll see `"aaa"` before the `
`. The HTML standard specifies in detail how to process bad HTML, and such behavior of the browser is correct. diff --git a/2-ui/1-document/07-modifying-document/5-why-aaa/task.md b/2-ui/1-document/07-modifying-document/5-why-aaa/task.md index f87074dba..861f70503 100644 --- a/2-ui/1-document/07-modifying-document/5-why-aaa/task.md +++ b/2-ui/1-document/07-modifying-document/5-why-aaa/task.md @@ -22,6 +22,6 @@ Why does that happen? alert(table); // the table, as it should be table.remove(); - // why there's still aaa in the document? + // why there's still "aaa" in the document? ``` diff --git a/2-ui/1-document/08-styles-and-classes/article.md b/2-ui/1-document/08-styles-and-classes/article.md index ff491fa83..9b0643549 100644 --- a/2-ui/1-document/08-styles-and-classes/article.md +++ b/2-ui/1-document/08-styles-and-classes/article.md @@ -129,8 +129,21 @@ setTimeout(() => document.body.style.display = "", 1000); // العودة الى إذا وضعنا `style.display` إلى سلسلة فارغة ، ثم يطبق المتصفح فئات CSS وأنماطه المضمنة بشكل طبيعي ، كما لو لم تكن هناك خاصية `style.display` على الإطلاق. +<<<<<<< HEAD ````smart header="إعادة كتابة كاملة مع `style.cssText`" عادة نستخدم `style.*` لتعيين خصائص النمط الفردية. لا يمكننا تعيين النمط الكامل مثل `div.style="color: red; width: 100px"`, لان `div.style` هو كائن ، وهو للقراءة فقط. +======= +Also there is a special method for that, `elem.style.removeProperty('style property')`. So, We can remove a property like this: + +```js run +document.body.style.background = 'red'; //set background to red + +setTimeout(() => document.body.style.removeProperty('background'), 1000); // remove background after 1 second +``` + +````smart header="Full rewrite with `style.cssText`" +Normally, we use `style.*` to assign individual style properties. We can't set the full style like `div.style="color: red; width: 100px"`, because `div.style` is an object, and it's read-only. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لتعيين النمط الكامل كسلسلة ، هناك خاصية خاصة `style.cssText`: diff --git a/2-ui/1-document/09-size-and-scroll/article.md b/2-ui/1-document/09-size-and-scroll/article.md index 61cd89cba..8c05d3b9c 100644 --- a/2-ui/1-document/09-size-and-scroll/article.md +++ b/2-ui/1-document/09-size-and-scroll/article.md @@ -17,8 +17,8 @@ width: 300px; height: 200px; border: 25px solid #E8C48F; - padding: 20px; - overflow: auto; + padding: 20px; + overflow: auto; } ``` @@ -106,7 +106,11 @@ You can [open the document in the sandbox](sandbox:metric). إذا كان أحد العناصر (أو أي من أسلافه) يحتوي على "عرض: لا شيء" أو غير موجود في المستند ، فإن جميع الخصائص الهندسية تكون صفرية (أو "خالية" لـ "offsetParent"). +<<<<<<< HEAD على سبيل المثال ، `offsetParent` هي` null` ، و `offsetWidth` ،` offsetHeight` هي `0` عندما أنشأنا عنصرًا ، ولكننا لم نقم بإدراجه في المستند حتى الآن ، أو (أو أصله) يحتوي على` عرض : لا شيء. +======= +For example, `offsetParent` is `null`, and `offsetWidth`, `offsetHeight` are `0` when we created an element, but haven't inserted it into the document yet, or it (or its ancestor) has `display:none`. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 يمكننا استخدام هذا للتحقق مما إذا كان العنصر مخفيًا ، مثل هذا: @@ -116,7 +120,11 @@ function isHidden(elem) { } ``` +<<<<<<< HEAD يرجى ملاحظة أن مثل `isHidden` يعرض` true` للعناصر التي تظهر على الشاشة ، ولكن بدون أحجام (مثل `
` فارغ). +======= +Please note that such `isHidden` returns `true` for elements that are on-screen, but have zero sizes. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```` ## clientTop/Left diff --git a/2-ui/1-document/10-size-and-scroll-window/article.md b/2-ui/1-document/10-size-and-scroll-window/article.md index d0985f3ad..8dc62f939 100644 --- a/2-ui/1-document/10-size-and-scroll-window/article.md +++ b/2-ui/1-document/10-size-and-scroll-window/article.md @@ -77,7 +77,17 @@ alert('Current scroll from the left: ' + window.pageXOffset); هذه الخصائص للقراءة فقط. +<<<<<<< HEAD ## التمرير: التمرير إلى التمرير التمرير التمرير العرضي [# window-تمرير] +======= +```smart header="Also available as `window` properties `scrollX` and `scrollY`" +For historical reasons, both properties exist, but they are the same: +- `window.pageXOffset` is an alias of `window.scrollX`. +- `window.pageYOffset` is an alias of `window.scrollY`. +``` + +## Scrolling: scrollTo, scrollBy, scrollIntoView [#window-scroll] +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```warn To scroll the page with JavaScript, its DOM must be fully built. diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/index.html b/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/index.html index 2c6073316..baf867664 100644 --- a/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/index.html +++ b/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/index.html @@ -10,7 +10,7 @@
` داخل جدول، مأخوذ من المواصفات: +======= +Here's the picture, taken from the specification, of the capturing `(1)`, target `(2)` and bubbling `(3)` phases for a click event on a `` inside a table: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ![](eventflow.svg) وهذا هو: بالنقر فوق `` يمر الحدث أولاً عبر سلسلة الأجداد نزولاً إلى العنصر (مرحلة الالتقاط), ثم تصل إلى الهدف وتتسبب في تشغيل ذلك الهدف (مرحلة الهدف), ثم يرتفع لأعلي (مرحلة التدفق), مناديا للمعالجين في طريقه. +<<<<<<< HEAD **قبل أن نتحدث عن التدفق فقط، لأن مرحلة الالتقاط نادراً ما تستخدم. عادة ما تكون غير مرئية بالنسبة لنا.** تمت إضافة معالجات باستخدام خاصية `on`-او باستخدام خواص HTML او باستخدام two-argument `addEventListener(event, handler)` لا تعرف أي شيء عن الالتقاط, وهي تعمل فقط على المرحلتين الثانية والثالثة. +======= +Until now, we only talked about bubbling, because the capturing phase is rarely used. + +In fact, the capturing phase was invisible for us, because handlers added using `on`-property or using HTML attributes or using two-argument `addEventListener(event, handler)` don't know anything about capturing, they only run on the 2nd and 3rd phases. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 لالتقاط حدث في مرحلة الالتقاط, يجب أن نضبط اختيار المعالج `capture` الي `true`: ```js elem.addEventListener(..., {capture: true}) +<<<<<<< HEAD // {capture: true} هو اسم مستعار لـ "true" أو فقط +======= + +// or, just "true" is an alias to {capture: true} +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 elem.addEventListener(..., true) ``` @@ -183,9 +198,16 @@ elem.addEventListener(..., true) إذا نقرت على `

`, ثم يكون التسلسل: +<<<<<<< HEAD 1. `HTML` -> `BODY` -> `FORM` -> `DIV` (مرحلة الالتقاط, المستمع الأول): 2. `P` (مرحلة الهدف, يتم تشغيلها مرتين, كما وضعنا مستمعين: الالتقاط والتدفق) 3. `DIV` -> `FORM` -> `BODY` -> `HTML` (مرحلة التدفق, المستمع الثاني). +======= +1. `HTML` -> `BODY` -> `FORM` -> `DIV -> P` (capturing phase, the first listener): +2. `P` -> `DIV` -> `FORM` -> `BODY` -> `HTML` (bubbling phase, the second listener). + +Please note, the `P` shows up twice, because we've set two listeners: capturing and bubbling. The target triggers at the end of the first and at the beginning of the second phase. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 توجد خاصية`event.eventPhase` وهي تخبرنا بعدد المرحلة التي يتم فيها وقوع الحدث. ولكنها نادرًا ما يتم استخدامها، لأننا نعرفه عادةً في المعالج. @@ -203,6 +225,12 @@ elem.addEventListener("click", e => alert(2)); ``` +```smart header="The `event.stopPropagation()` during the capturing also prevents the bubbling" +The `event.stopPropagation()` method and its sibling `event.stopImmediatePropagation()` can also be called on the capturing phase. Then not only the futher capturing is stopped, but the bubbling as well. + +In other words, normally the event goes first down ("capturing") and then up ("bubbling"). But if `event.stopPropagation()` is called during the capturing phase, then the event travel stops, no bubbling will occur. +``` + ## الخلاصة @@ -220,7 +248,11 @@ elem.addEventListener("click", e => alert(2)); يمكن أن يوقف معالج الأحداث الحدث باستخدام `event.stopPropagation()`, ولكن هذا غير موصى به, لأننا لا نستطيع أن نتأكد من أننا لن نحتاج إليها أعلاه، ربما لأشياء مختلفة تماماً. +<<<<<<< HEAD تُستخدم مرحلة الالتقاط نادرًا جدًا, وعادة ما نتعامل مع الأحداث الجارية في مرحلة التدفق. وهناك منطق وراء ذلك. +======= +The capturing phase is used very rarely, usually we handle events on bubbling. And there's a logical explanation for that. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 في العالم الحقيقي، حين يقع حادث ما, فالسلطات المحلية ترد أولاً. فهم يعرفون المنطقة التي حدث فيا جيدا. ثم سلطات أعلى مستوى إذا لزم الأمر. diff --git a/2-ui/2-events/03-event-delegation/article.md b/2-ui/2-events/03-event-delegation/article.md index 41df9f079..881183740 100644 --- a/2-ui/2-events/03-event-delegation/article.md +++ b/2-ui/2-events/03-event-delegation/article.md @@ -1,7 +1,7 @@ # Event delegation -Capturing and bubbling allow us to implement one of most powerful event handling patterns called *event delegation*. +Capturing and bubbling allow us to implement one of the most powerful event handling patterns called *event delegation*. The idea is that if we have a lot of elements handled in a similar way, then instead of assigning a handler to each of them -- we put a single handler on their common ancestor. diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css index 4522006ae..8d6472ee6 100644 --- a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css +++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css @@ -4,16 +4,6 @@ body { font: 75%/120% sans-serif; } -h2 { - font: bold 190%/100% sans-serif; - margin: 0 0 .2em; -} - -h2 em { - font: normal 80%/100% sans-serif; - color: #999999; -} - #largeImg { border: solid 1px #ccc; width: 550px; diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css b/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css index b6e523014..8d6472ee6 100644 --- a/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css +++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css @@ -4,16 +4,6 @@ body { font: 75%/120% sans-serif; } -h2 { - font: bold 190%/100% sans-serif; - margin: 0 0 .2em; -} - -h2 em { - font: normal 80%/100% sans-serif; - color: #999999; -} - #largeImg { border: solid 1px #ccc; width: 550px; @@ -32,4 +22,13 @@ h2 em { #thumbs a:hover { border-color: #FF9900; +} + +#thumbs li { + list-style: none; +} + +#thumbs { + margin: 0; + padding: 0; } \ No newline at end of file diff --git a/2-ui/2-events/04-default-browser-action/article.md b/2-ui/2-events/04-default-browser-action/article.md index 0ca4ef636..75a3c69db 100644 --- a/2-ui/2-events/04-default-browser-action/article.md +++ b/2-ui/2-events/04-default-browser-action/article.md @@ -17,7 +17,11 @@ - الطريقة الرئيسية هي استخدام كائن الحدث `event`. هناك طريقة `event.preventDefault()`. - إذا تم تعيين المعالج handler باستخدام `on` (وليس عن طريق `addEventListener`)، عندئذ فإن ارجاع القيمة `false` يعمل بنفس الطريقة. +<<<<<<< HEAD :في هذه الصفحة النقر على الرابط لا يؤدي إلى التنقل، ولا يقوم المتصفح بأي شيئ +======= +In this HTML, a click on a link doesn't lead to navigation; the browser doesn't do anything: +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 ```html autorun height=60 no-beautify Click here @@ -96,7 +100,11 @@ menu.onclick = function(event) { الاختيار الغير إلزامي `passive: true` خيار `addEventListener` يشير إلى المتصفح بأن المعالج لن يستدعي الأمر `preventDefault()`. +<<<<<<< HEAD لماذا قد تكون هناك حاجة لذلك؟ +======= +Why might that be needed? +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 هناك بعض الأحداث مثل `touchmove` على أجهزة المحمول (عندما يحرك المستخدم إصبعه عبر الشاشة)، هذا يتسبب في scrolling افتراضي ، ولكن يمكن منع هذا باستخدام ` PreventionDefault () ` في المعالج. diff --git a/2-ui/2-events/05-dispatch-events/article.md b/2-ui/2-events/05-dispatch-events/article.md index b38719f85..77a49490f 100644 --- a/2-ui/2-events/05-dispatch-events/article.md +++ b/2-ui/2-events/05-dispatch-events/article.md @@ -210,7 +210,7 @@ Please note: the event must have the flag `cancelable: true`, otherwise the call ## Events-in-events are synchronous -Usually events are processed in a queue. That is: if the browser is processing `onclick` and a new event occurs, e.g. mouse moved, then it's handling is queued up, corresponding `mousemove` handlers will be called after `onclick` processing is finished. +Usually events are processed in a queue. That is: if the browser is processing `onclick` and a new event occurs, e.g. mouse moved, then its handling is queued up, corresponding `mousemove` handlers will be called after `onclick` processing is finished. The notable exception is when one event is initiated from within another one, e.g. using `dispatchEvent`. Such events are processed immediately: the new event handlers are called, and then the current event handling is resumed. diff --git a/2-ui/3-event-details/1-mouse-events-basics/article.md b/2-ui/3-event-details/1-mouse-events-basics/article.md index 63a6d402c..d82c25be6 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/article.md +++ b/2-ui/3-event-details/1-mouse-events-basics/article.md @@ -1,4 +1,9 @@ +<<<<<<< HEAD # أحداث الماوس +======= + +# Mouse events +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 في هذا الفصل سنتناول المزيد حول أحداث الماوس وخصائصها. @@ -44,8 +49,14 @@ In cases when a single action initiates multiple events, their order is fixed. T ```online لمعاينة الأحداث بوضوح أنقر فوق الزر أدناه. جرب النقر مرتين أيضاً. +<<<<<<< HEAD في التجربة التالية ، يتم تسجيل جميع أحداث الماوس ، وإذا كان هناك تأخير لأكثر من ثانية واحدة يتم فصلها بخط أفقي متقطع. +======= +On the teststand below, all mouse events are logged, and if there is more than a 1 second delay between them, they are separated by a horizontal rule. + +Also, we can see the `button` property that allows us to detect the mouse button; it's explained below. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 كما يمكننا أن نرى `button` الذي يسمح لنا باكتشاف زر الماوس ، كما هو موضح أدناه. @@ -58,7 +69,7 @@ In cases when a single action initiates multiple events, their order is fixed. T We usually don't use it for `click` and `contextmenu` events, because the former happens only on left-click, and the latter -- only on right-click. -From the other hand, `mousedown` and `mouseup` handlers may need `event.button`, because these events trigger on any button, so `button` allows to distinguish between "right-mousedown" and "left-mousedown". +On the other hand, `mousedown` and `mouseup` handlers may need `event.button`, because these events trigger on any button, so `button` allows to distinguish between "right-mousedown" and "left-mousedown". القيم المحتملة لـ `event.button` هي: @@ -160,7 +171,11 @@ Also there's `event.buttons` property that has all currently pressed buttons as ## منع الإختيار في الماوس +<<<<<<< HEAD النقر المزدوج بزر الماوس له تأثير جانبي قد يكون غير ملائم في بعض الواجهات: فهو يحدد النص. +======= +Double mouse click has a side effect that may be disturbing in some interfaces: it selects text. +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 For instance, double-clicking on the text below selects it in addition to our handler: diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html index e998165fd..84d52b18c 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html @@ -54,7 +54,7 @@

Once upon a time there was a mother pig who had three little pigs.

-

The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you." +

The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."

The three little pigs set off. "We will take care that the wolf does not catch us," they said.

diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html index 2dc4394e7..774e24a21 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html @@ -54,7 +54,7 @@

Once upon a time there was a mother pig who had three little pigs.

-

The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you." +

The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."

The three little pigs set off. "We will take care that the wolf does not catch us," they said.

diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js index 4e6e2a3e9..7503ca9c2 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js @@ -88,7 +88,7 @@ class HoverIntent { if (speed < this.sensitivity) { clearInterval(this.checkSpeedInterval); this.isHover = true; - this.over.call(this.elem, event); + this.over.call(this.elem); } else { // speed fast, remember new coordinates as the previous ones this.prevX = this.lastX; diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js index 6d87199c2..5752e83ae 100755 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js @@ -3,7 +3,7 @@ parent.onmouseover = parent.onmouseout = parent.onmousemove = handler; function handler(event) { let type = event.type; - while (type < 11) type += ' '; + while (type.length < 11) type += ' '; log(type + " target=" + event.target.id) return false; diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md index 6cb1152c1..4c928eef1 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md @@ -18,19 +18,19 @@ The basic Drag'n'Drop algorithm looks like this: 2. Then on `mousemove` move it by changing `left/top` with `position:absolute`. 3. On `mouseup` - perform all actions related to finishing the drag'n'drop. -These are the basics. Later we'll see how to other features, such as highlighting current underlying elements while we drag over them. +These are the basics. Later we'll see how to add other features, such as highlighting current underlying elements while we drag over them. Here's the implementation of dragging a ball: ```js -ball.onmousedown = function(event) { +ball.onmousedown = function(event) { // (1) prepare to moving: make absolute and on top by z-index ball.style.position = 'absolute'; ball.style.zIndex = 1000; // move it out of any current parents directly into body // to make it positioned relative to the body - document.body.append(ball); + document.body.append(ball); // centers the ball at (pageX, pageY) coordinates function moveAt(pageX, pageY) { @@ -93,14 +93,14 @@ So we should listen on `document` to catch it. ## Correct positioning -In the examples above the ball is always moved so, that it's center is under the pointer: +In the examples above the ball is always moved so that its center is under the pointer: ```js ball.style.left = pageX - ball.offsetWidth / 2 + 'px'; ball.style.top = pageY - ball.offsetHeight / 2 + 'px'; ``` -Not bad, but there's a side-effect. To initiate the drag'n'drop, we can `mousedown` anywhere on the ball. But if "take" it from its edge, then the ball suddenly "jumps" to become centered under the mouse pointer. +Not bad, but there's a side effect. To initiate the drag'n'drop, we can `mousedown` anywhere on the ball. But if "take" it from its edge, then the ball suddenly "jumps" to become centered under the mouse pointer. It would be better if we keep the initial shift of the element relative to the pointer. @@ -124,7 +124,7 @@ Let's update our algorithm: ```js // onmousemove - // ball has position:absoute + // ball has position:absolute ball.style.left = event.pageX - *!*shiftX*/!* + 'px'; ball.style.top = event.pageY - *!*shiftY*/!* + 'px'; ``` @@ -219,7 +219,7 @@ That's why the initial idea to put handlers on potential droppables doesn't work So, what to do? -There's a method called `document.elementFromPoint(clientX, clientY)`. It returns the most nested element on given window-relative coordinates (or `null` if given coordinates are out of the window). +There's a method called `document.elementFromPoint(clientX, clientY)`. It returns the most nested element on given window-relative coordinates (or `null` if given coordinates are out of the window). If there are multiple overlapping elements on the same coordinates, then the topmost one is returned. We can use it in any of our mouse event handlers to detect the potential droppable under the pointer, like this: diff --git a/2-ui/3-event-details/6-pointer-events/article.md b/2-ui/3-event-details/6-pointer-events/article.md index 3e751a4af..b8873e9d8 100644 --- a/2-ui/3-event-details/6-pointer-events/article.md +++ b/2-ui/3-event-details/6-pointer-events/article.md @@ -9,16 +9,16 @@ Let's make a small overview, so that you understand the general picture and the - Long ago, in the past, there were only mouse events. Then touch devices became widespread, phones and tablets in particular. For the existing scripts to work, they generated (and still generate) mouse events. For instance, tapping a touchscreen generates `mousedown`. So touch devices worked well with web pages. - + But touch devices have more capabilities than a mouse. For example, it's possible to touch multiple points at once ("multi-touch"). Although, mouse events don't have necessary properties to handle such multi-touches. - So touch events were introduced, such as `touchstart`, `touchend`, `touchmove`, that have touch-specific properties (we don't cover them in detail here, because pointer events are even better). - Still, it wasn't enough, as there are many other devices, such as pens, that have their own features. Also, writing code that listens for both touch and mouse events was cumbersome. + Still, it wasn't enough, as there are many other devices, such as pens, that have their own features. Also, writing code that listens for both touch and mouse events was cumbersome. - To solve these issues, the new standard Pointer Events was introduced. It provides a single set of events for all kinds of pointing devices. -As of now, [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) specification is supported in all major browsers, while the newer [Pointer Events Level 3](https://w3c.github.io/pointerevents/) is in the works and is mostly compartible with Pointer Events level 2. +As of now, [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) specification is supported in all major browsers, while the newer [Pointer Events Level 3](https://w3c.github.io/pointerevents/) is in the works and is mostly compatible with Pointer Events level 2. Unless you develop for old browsers, such as Internet Explorer 10, or for Safari 12 or below, there's no point in using mouse or touch events any more -- we can switch to pointer events. @@ -43,12 +43,12 @@ Pointer events are named similarly to mouse events: | `gotpointercapture` | - | | `lostpointercapture` | - | -As we can see, for every `mouse`, there's a `pointer` that plays a similar role. Also there are 3 additional pointer events that don't have a corresponding `mouse...` counterpart, we'll explain them soon. +As we can see, for every `mouse`, there's a `pointer` that plays a similar role. Also there are 3 additional pointer events that don't have a corresponding `mouse...` counterpart, we'll explain them soon. ```smart header="Replacing `mouse` with `pointer` in our code" We can replace `mouse` events with `pointer` in our code and expect things to continue working fine with mouse. -The support for touch devices will also "magically" improve. Although, we may need to add `touch-action: none` in some places in CSS. We'll cover it below in the section about `pointercancel`. +The support for touch devices will also "magically" improve. Although, we may need to add `touch-action: none` in some places in CSS. We'll cover it below in the section about `pointercancel`. ``` ## Pointer event properties @@ -56,16 +56,16 @@ The support for touch devices will also "magically" improve. Although, we may ne Pointer events have the same properties as mouse events, such as `clientX/Y`, `target`, etc., plus some others: - `pointerId` - the unique identifier of the pointer causing the event. - + Browser-generated. Allows us to handle multiple pointers, such as a touchscreen with stylus and multi-touch (examples will follow). -- `pointerType` - the pointing device type. Must be a string, one of: "mouse", "pen" or "touch". +- `pointerType` - the pointing device type. Must be a string, one of: "mouse", "pen" or "touch". We can use this property to react differently on various pointer types. - `isPrimary` - is `true` for the primary pointer (the first finger in multi-touch). Some pointer devices measure contact area and pressure, e.g. for a finger on the touchscreen, there are additional properties for that: -- `width` - the width of the area where the pointer (e.g. a finger) touches the device. Where unsupported, e.g. for a mouse, it's always `1`. +- `width` - the width of the area where the pointer (e.g. a finger) touches the device. Where unsupported, e.g. for a mouse, it's always `1`. - `height` - the height of the area where the pointer touches the device. Where unsupported, it's always `1`. - `pressure` - the pressure of the pointer tip, in range from 0 to 1. For devices that don't support pressure must be either `0.5` (pressed) or `0`. - `tangentialPressure` - the normalized tangential pressure. @@ -102,16 +102,16 @@ Please note: you must be using a touchscreen device, such as a phone or a tablet ## Event: pointercancel -The `pointercancel` event fires when there's an ongoing pointer interaction, and then something happens that causes it to be aborted, so that no more pointer events are generated. +The `pointercancel` event fires when there's an ongoing pointer interaction, and then something happens that causes it to be aborted, so that no more pointer events are generated. -Such causes are: +Such causes are: - The pointer device hardware was physically disabled. -- The device orientation changed (tablet rotated). +- The device orientation changed (tablet rotated). - The browser decided to handle the interaction on its own, considering it a mouse gesture or zoom-and-pan action or something else. We'll demonstrate `pointercancel` on a practical example to see how it affects us. -Let's say we're impelementing drag'n'drop for a ball, just as in the beginning of the article . +Let's say we're implementing drag'n'drop for a ball, just as in the beginning of the article . Here is the flow of user actions and the corresponding events: @@ -126,7 +126,7 @@ Here is the flow of user actions and the corresponding events: So the issue is that the browser "hijacks" the interaction: `pointercancel` fires in the beginning of the "drag-and-drop" process, and no more `pointermove` events are generated. ```online -Here's the drag'n'drop demo with loggin of pointer events (only `up/down`, `move` and `cancel`) in the `textarea`: +Here's the drag'n'drop demo with loggin of pointer events (only `up/down`, `move` and `cancel`) in the `textarea`: [iframe src="ball" height=240 edit] ``` @@ -141,7 +141,7 @@ We need to do two things: - We can do this by setting `ball.ondragstart = () => false`, just as described in the article . - That works well for mouse events. 2. For touch devices, there are other touch-related browser actions (besides drag'n'drop). To avoid problems with them too: - - Prevent them by setting `#ball { touch-action: none }` in CSS. + - Prevent them by setting `#ball { touch-action: none }` in CSS. - Then our code will start working on touch devices. After we do that, the events will work as intended, the browser won't hijack the process and doesn't emit `pointercancel`. @@ -163,7 +163,7 @@ Pointer capturing is a special feature of pointer events. The idea is very simple, but may seem quite odd at first, as nothing like that exists for any other event type. The main method is: -- `elem.setPointerCapture(pointerId)` - binds events with the given `pointerId` to `elem`. After the call all pointer events with the same `pointerId` will have `elem` as the target (as if happened on `elem`), no matter where in document they really happened. +- `elem.setPointerCapture(pointerId)` -- binds events with the given `pointerId` to `elem`. After the call all pointer events with the same `pointerId` will have `elem` as the target (as if happened on `elem`), no matter where in document they really happened. In other words, `elem.setPointerCapture(pointerId)` retargets all subsequent events with the given `pointerId` to `elem`. @@ -172,29 +172,43 @@ The binding is removed: - automatically when `elem` is removed from the document, - when `elem.releasePointerCapture(pointerId)` is called. +Now what is it good for? It's time to see a real-life example. + **Pointer capturing can be used to simplify drag'n'drop kind of interactions.** -As an example, let's recall how one can implement a custom slider, described in the . +Let's recall how one can implement a custom slider, described in the . + +We can make a `slider` element to represent the strip and the "runner" (`thumb`) inside it: + +```html +
+
+
+``` + +With styles, it looks like this: + +[iframe src="slider-html" height=40 edit] -We make a slider element with the strip and the "runner" (`thumb`) inside it. +

-Then it works like this: +And here's the working logic, as it was described, after replacing mouse events with similar pointer events: -1. The user presses on the slider `thumb` - `pointerdown` triggers. -2. Then they move the pointer - `pointermove` triggers, and we move the `thumb` along. - - ...As the pointer moves, it may leave the slider `thumb`: go above or below it. The `thumb` should move strictly horizontally, remaining aligned with the pointer. +1. The user presses on the slider `thumb` -- `pointerdown` triggers. +2. Then they move the pointer -- `pointermove` triggers, and our code moves the `thumb` element along. + - ...As the pointer moves, it may leave the slider `thumb` element, go above or below it. The `thumb` should move strictly horizontally, remaining aligned with the pointer. -So, to track all pointer movements, including when it goes above/below the `thumb`, we had to assign `pointermove` event handler on the whole `document`. +In the mouse event based solution, to track all pointer movements, including when it goes above/below the `thumb`, we had to assign `mousemove` event handler on the whole `document`. -That solution looks a bit "dirty". One of the problems is that pointer movements around the document may cause side effects, trigger other event handlers, totally not related to the slider. +That's not a cleanest solution, though. One of the problems is that when a user moves the pointer around the document, it may trigger event handlers (such as `mouseover`) on some other elements, invoke totally unrelated UI functionality, and we don't want that. -Pointer capturing provides a means to bind `pointermove` to `thumb` and avoid any such problems: +This is the place where `setPointerCapture` comes into play. - We can call `thumb.setPointerCapture(event.pointerId)` in `pointerdown` handler, -- Then future pointer events until `pointerup/cancel` will be retargeted to `thumb`. +- Then future pointer events until `pointerup/cancel` will be retargeted to `thumb`. - When `pointerup` happens (dragging complete), the binding is removed automatically, we don't need to care about it. -So, even if the user moves the pointer around the whole document, events handlers will be called on `thumb`. Besides, coordinate properties of the event objects, such as `clientX/clientY` will still be correct - the capturing only affects `target/currentTarget`. +So, even if the user moves the pointer around the whole document, events handlers will be called on `thumb`. Nevertheless, coordinate properties of the event objects, such as `clientX/clientY` will still be correct - the capturing only affects `target/currentTarget`. Here's the essential code: @@ -202,15 +216,23 @@ Here's the essential code: thumb.onpointerdown = function(event) { // retarget all pointer events (until pointerup) to thumb thumb.setPointerCapture(event.pointerId); -}; -thumb.onpointermove = function(event) { - // moving the slider: listen on the thumb, as all pointer events are retargeted to it - let newLeft = event.clientX - slider.getBoundingClientRect().left; - thumb.style.left = newLeft + 'px'; + // start tracking pointer moves + thumb.onpointermove = function(event) { + // moving the slider: listen on the thumb, as all pointer events are retargeted to it + let newLeft = event.clientX - slider.getBoundingClientRect().left; + thumb.style.left = newLeft + 'px'; + }; + + // on pointer up finish tracking pointer moves + thumb.onpointerup = function(event) { + thumb.onpointermove = null; + thumb.onpointerup = null; + // ...also process the "drag end" if needed + }; }; -// note: no need to call thumb.releasePointerCapture, +// note: no need to call thumb.releasePointerCapture, // it happens on pointerup automatically ``` @@ -218,15 +240,27 @@ thumb.onpointermove = function(event) { The full demo: [iframe src="slider" height=100 edit] + +

+ +In the demo, there's also an additional element with `onmouseover` handler showing the current date. + +Please note: while you're dragging the thumb, you may hover over this element, and its handler *does not* trigger. + +So the dragging is now free of side effects, thanks to `setPointerCapture`. ``` + + At the end, pointer capturing gives us two benefits: 1. The code becomes cleaner as we don't need to add/remove handlers on the whole `document` any more. The binding is released automatically. -2. If there are any `pointermove` handlers in the document, they won't be accidentally triggered by the pointer while the user is dragging the slider. +2. If there are other pointer event handlers in the document, they won't be accidentally triggered by the pointer while the user is dragging the slider. ### Pointer capturing events -There are two associated pointer events: +There's one more thing to mention here, for the sake of completeness. + +There are two events associated with pointer capturing: - `gotpointercapture` fires when an element uses `setPointerCapture` to enable capturing. - `lostpointercapture` fires when the capture is released: either explicitly with `releasePointerCapture` call, or automatically on `pointerup`/`pointercancel`. @@ -237,7 +271,7 @@ Pointer events allow handling mouse, touch and pen events simultaneously, with a Pointer events extend mouse events. We can replace `mouse` with `pointer` in event names and expect our code to continue working for mouse, with better support for other device types. -For drag'n'drops and complex touch interactions that the browser may decide to hijack and handle on its own - remember to cancel the default action on events and set `touch-events: none` in CSS for elements that we engage. +For drag'n'drops and complex touch interactions that the browser may decide to hijack and handle on its own - remember to cancel the default action on events and set `touch-action: none` in CSS for elements that we engage. Additional abilities of pointer events are: diff --git a/2-ui/3-event-details/6-pointer-events/slider-html.view/index.html b/2-ui/3-event-details/6-pointer-events/slider-html.view/index.html new file mode 100644 index 000000000..781016f52 --- /dev/null +++ b/2-ui/3-event-details/6-pointer-events/slider-html.view/index.html @@ -0,0 +1,6 @@ + + + +
+
+
diff --git a/2-ui/3-event-details/6-pointer-events/slider-html.view/style.css b/2-ui/3-event-details/6-pointer-events/slider-html.view/style.css new file mode 100644 index 000000000..9b3d3b82d --- /dev/null +++ b/2-ui/3-event-details/6-pointer-events/slider-html.view/style.css @@ -0,0 +1,19 @@ +.slider { + border-radius: 5px; + background: #E0E0E0; + background: linear-gradient(left top, #E0E0E0, #EEEEEE); + width: 310px; + height: 15px; + margin: 5px; +} + +.thumb { + width: 10px; + height: 25px; + border-radius: 3px; + position: relative; + left: 10px; + top: -5px; + background: blue; + cursor: pointer; +} diff --git a/2-ui/3-event-details/6-pointer-events/slider.view/index.html b/2-ui/3-event-details/6-pointer-events/slider.view/index.html index 2c2a69ec7..b29e646a1 100644 --- a/2-ui/3-event-details/6-pointer-events/slider.view/index.html +++ b/2-ui/3-event-details/6-pointer-events/slider.view/index.html @@ -5,22 +5,33 @@
+

Mouse over here to see the date

+ diff --git a/2-ui/3-event-details/6-pointer-events/slider.view/style.css b/2-ui/3-event-details/6-pointer-events/slider.view/style.css index 9b3d3b82d..a84cd5e7e 100644 --- a/2-ui/3-event-details/6-pointer-events/slider.view/style.css +++ b/2-ui/3-event-details/6-pointer-events/slider.view/style.css @@ -8,6 +8,7 @@ } .thumb { + touch-action: none; width: 10px; height: 25px; border-radius: 3px; diff --git a/2-ui/3-event-details/7-keyboard-events/article.md b/2-ui/3-event-details/7-keyboard-events/article.md index 54bde42b4..12fe63201 100644 --- a/2-ui/3-event-details/7-keyboard-events/article.md +++ b/2-ui/3-event-details/7-keyboard-events/article.md @@ -107,7 +107,7 @@ So, `event.code` may match a wrong character for unexpected layout. Same letters To reliably track layout-dependent characters, `event.key` may be a better way. -On the other hand, `event.code` has the benefit of staying always the same, bound to the physical key location, even if the visitor changes languages. So hotkeys that rely on it work well even in case of a language switch. +On the other hand, `event.code` has the benefit of staying always the same, bound to the physical key location. So hotkeys that rely on it work well even in case of a language switch. Do we want to handle layout-dependant keys? Then `event.key` is the way to go. @@ -139,22 +139,25 @@ For instance, the `` below expects a phone number, so it does not accept ```html autorun height=60 run ``` -Please note that special keys, such as `key:Backspace`, `key:Left`, `key:Right`, `key:Ctrl+V`, do not work in the input. That's a side-effect of the strict filter `checkPhoneKey`. +The `onkeydown` handler here uses `checkPhoneKey` to check for the key pressed. If it's valid (from `0..9` or one of `+-()`), then it returns `true`, otherwise `false`. -Let's relax it a little bit: +As we know, the `false` value returned from the event handler, assigned using a DOM property or an attribute, such as above, prevents the default action, so nothing appears in the `` for keys that don't pass the test. (The `true` value returned doesn't affect anything, only returning `false` matters) +Please note that special keys, such as `key:Backspace`, `key:Left`, `key:Right`, do not work in the input. That's a side effect of the strict filter `checkPhoneKey`. These keys make it return `false`. + +Let's relax the filter a little bit by allowing arrow keys `key:Left`, `key:Right` and `key:Delete`, `key:Backspace`: ```html autorun height=60 run @@ -162,7 +165,9 @@ function checkPhoneKey(key) { Now arrows and deletion works well. -...But we still can enter anything by using a mouse and right-click + Paste. So the filter is not 100% reliable. We can just let it be like that, because most of time it works. Or an alternative approach would be to track the `input` event -- it triggers after any modification. There we can check the new value and highlight/modify it when it's invalid. +Even though we have the key filter, one still can enter anything using a mouse and right-click + Paste. Mobile devices provide other means to enter values. So the filter is not 100% reliable. + +The alternative approach would be to track the `oninput` event -- it triggers *after* any modification. There we can check the new `input.value` and modify it/highlight the `` when it's invalid. Or we can use both event handlers together. ## Legacy diff --git a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html index 401062830..a0d5a4f40 100644 --- a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html +++ b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html @@ -28,7 +28,7 @@ - + diff --git a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js index 5eba24c7a..d97f7a7b5 100644 --- a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js +++ b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js @@ -5,6 +5,8 @@ let lastTime = Date.now(); function handle(e) { if (form.elements[e.type + 'Ignore'].checked) return; + area.scrollTop = 1e6; + let text = e.type + ' key=' + e.key + ' code=' + e.code + diff --git a/2-ui/4-forms-controls/1-form-elements/article.md b/2-ui/4-forms-controls/1-form-elements/article.md index 689301e4d..f22518d9d 100644 --- a/2-ui/4-forms-controls/1-form-elements/article.md +++ b/2-ui/4-forms-controls/1-form-elements/article.md @@ -155,7 +155,7 @@ Let's talk about form controls. ### input and textarea -We can access their value as `input.value` (string) or `input.checked` (boolean) for checkboxes. +We can access their value as `input.value` (string) or `input.checked` (boolean) for checkboxes and radio buttons. Like this: diff --git a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md index 2cccea020..378bd1f54 100644 --- a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md +++ b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md @@ -6,7 +6,7 @@ importance: 5 Make table cells editable on click. -- On click -- the cell should became "editable" (textarea appears inside), we can change HTML. There should be no resize, all geometry should remain the same. +- On click -- the cell should become "editable" (textarea appears inside), we can change HTML. There should be no resize, all geometry should remain the same. - Buttons OK and CANCEL appear below the cell to finish/cancel the editing. - Only one cell may be editable at a moment. While a `
` is in "edit mode", clicks on other cells are ignored. - The table may have many cells. Use event delegation. diff --git a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md index fc48c21ff..644d814d9 100644 --- a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md +++ b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md @@ -9,4 +9,5 @@ Focus on the mouse. Then use arrow keys to move it: [demo src="solution"] P.S. Don't put event handlers anywhere except the `#mouse` element. + P.P.S. Don't modify HTML/CSS, the approach should be generic and work with any element. diff --git a/2-ui/4-forms-controls/2-focus-blur/article.md b/2-ui/4-forms-controls/2-focus-blur/article.md index d4348d25b..c253dc11d 100644 --- a/2-ui/4-forms-controls/2-focus-blur/article.md +++ b/2-ui/4-forms-controls/2-focus-blur/article.md @@ -90,6 +90,8 @@ If we enter something into the input and then try to use `key:Tab` or click away Please note that we can't "prevent losing focus" by calling `event.preventDefault()` in `onblur`, because `onblur` works *after* the element lost the focus. +In practice though, one should think well, before implementing something like this, because we generally *should show errors* to the user, but *should not prevent their progress* in filling our form. They may want to fill other fields first. + ```warn header="JavaScript-initiated focus loss" A focus loss can occur for many reasons. @@ -104,7 +106,7 @@ The best recipe is to be careful when using these events. If we want to track us ``` ## Allow focusing on any element: tabindex -By default many elements do not support focusing. +By default, many elements do not support focusing. The list varies a bit between browsers, but one thing is always correct: `focus/blur` support is guaranteed for elements that a visitor can interact with: ` + ## Selection properties -Similar to a range, a selection has a start, called "anchor", and the end, called "focus". +As said, a selection may in theory contain multiple ranges. We can get these range objects using the method: + +- `getRangeAt(i)` -- get i-th range, starting from `0`. In all browsers except Firefox, only `0` is used. + +Also, there exist properties that often provide better convenience. + +Similar to a range, a selection object has a start, called "anchor", and the end, called "focus". The main selection properties are: @@ -294,36 +352,39 @@ The main selection properties are: - `isCollapsed` -- `true` if selection selects nothing (empty range), or doesn't exist. - `rangeCount` -- count of ranges in the selection, maximum `1` in all browsers except Firefox. -````smart header="Usually, the selection end `focusNode` is after its start `anchorNode`, but it's not always the case" -There are many ways to select the content, depending on the user agent: mouse, hotkeys, taps on a mobile etc. +```smart header="Selection end/start vs Range" + +There's an important differences of a selection anchor/focus compared with a `Range` start/end. + +As we know, `Range` objects always have their start before the end. -Some of them, such as a mouse, allow the same selection can be created in two directions: "left-to-right" and "right-to-left". +For selections, that's not always the case. -If the start (anchor) of the selection goes in the document before the end (focus), this selection is said to have "forward" direction. +Selecting something with a mouse can be done in both directions: either "left-to-right" or "right-to-left". + +In other words, when the mouse button is pressed, and then it moves forward in the document, then its end (focus) will be after its start (anchor). E.g. if the user starts selecting with mouse and goes from "Example" to "italic": ![](selection-direction-forward.svg) -Otherwise, if they go from the end of "italic" to "Example", the selection is directed "backward", its focus will be before the anchor: +...But the same selection could be done backwards: starting from "italic" to "Example" (backward direction), then its end (focus) will be before the start (anchor): ![](selection-direction-backward.svg) - -That's different from `Range` objects that are always directed forward: the range start can't be after its end. -```` +``` ## Selection events There are events on to keep track of selection: -- `elem.onselectstart` -- when a selection starts on `elem`, e.g. the user starts moving mouse with pressed button. - - Preventing the default action makes the selection not start. -- `document.onselectionchange` -- whenever a selection changes. - - Please note: this handler can be set only on `document`. +- `elem.onselectstart` -- when a selection *starts* specifically on element `elem` (or inside it). For instance, when the user presses the mouse button on it and starts to move the pointer. + - Preventing the default action cancels the selection start. So starting a selection from this element becomes impossible, but the element is still selectable. The visitor just needs to start the selection from elsewhere. +- `document.onselectionchange` -- whenever a selection changes or starts. + - Please note: this handler can be set only on `document`, it tracks all selections in it. ### Selection tracking demo -Here's a small demo that shows selection boundaries dynamically as it changes: +Here's a small demo. It tracks the current selection on the `document` and shows its boundaries: ```html run height=80

Select me: italic and bold

@@ -331,21 +392,25 @@ Here's a small demo that shows selection boundaries dynamically as it changes: From – To ``` -### Selection getting demo +### Selection copying demo + +There are two approaches to copying the selected content: -To get the whole selection: -- As text: just call `document.getSelection().toString()`. -- As DOM nodes: get the underlying ranges and call their `cloneContents()` method (only first range if we don't support Firefox multiselection). +1. We can use `document.getSelection().toString()` to get it as text. +2. Otherwise, to copy the full DOM, e.g. if we need to keep formatting, we can get the underlying ranges with `getRangesAt(...)`. A `Range` object, in turn, has `cloneContents()` method that clones its content and returns as `DocumentFragment` object, that we can insert elsewhere. -And here's the demo of getting the selection both as text and as DOM nodes: +Here's the demo of copying the selected content both as text and as DOM nodes: ```html run height=100

Select me: italic and bold

@@ -373,15 +438,15 @@ As text: ## Selection methods -Selection methods to add/remove ranges: +We can work with the selection by adding/removing ranges: -- `getRangeAt(i)` -- get i-th range, starting from `0`. In all browsers except firefox, only `0` is used. +- `getRangeAt(i)` -- get i-th range, starting from `0`. In all browsers except Firefox, only `0` is used. - `addRange(range)` -- add `range` to selection. All browsers except Firefox ignore the call, if the selection already has an associated range. - `removeRange(range)` -- remove `range` from the selection. - `removeAllRanges()` -- remove all ranges. - `empty()` -- alias to `removeAllRanges`. -Also, there are convenience methods to manipulate the selection range directly, without `Range`: +There are also convenience methods to manipulate the selection range directly, without intermediate `Range` calls: - `collapse(node, offset)` -- replace selected range with a new one that starts and ends at the given `node`, at position `offset`. - `setPosition(node, offset)` -- alias to `collapse`. @@ -393,7 +458,7 @@ Also, there are convenience methods to manipulate the selection range directly, - `deleteFromDocument()` -- remove selected content from the document. - `containsNode(node, allowPartialContainment = false)` -- checks whether the selection contains `node` (partially if the second argument is `true`) -So, for many tasks we can call `Selection` methods, no need to access the underlying `Range` object. +For most tasks these methods are just fine, there's no need to access the underlying `Range` object. For example, selecting the whole contents of the paragraph `

`: @@ -420,10 +485,10 @@ The same thing using ranges: ``` -```smart header="To select, remove the existing selection first" -If the selection already exists, empty it first with `removeAllRanges()`. And then add ranges. Otherwise, all browsers except Firefox ignore new ranges. +```smart header="To select something, remove the existing selection first" +If a document selection already exists, empty it first with `removeAllRanges()`. And then add ranges. Otherwise, all browsers except Firefox ignore new ranges. -The exception is some selection methods, that replace the existing selection, like `setBaseAndExtent`. +The exception is some selection methods, that replace the existing selection, such as `setBaseAndExtent`. ``` ## Selection in form controls diff --git a/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg b/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg new file mode 100644 index 000000000..2951607a2 --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg @@ -0,0 +1 @@ +<p>Hello</p>p.firstChild \ No newline at end of file diff --git a/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/solution.md b/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/solution.md new file mode 100644 index 000000000..3a0daa974 --- /dev/null +++ b/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/solution.md @@ -0,0 +1,50 @@ +The console output is: 1 7 3 5 2 6 4. + +The task is quite simple, we just need to know how microtask and macrotask queues work. + +Let's see what's going on, step by step. + +```js +console.log(1); +// The first line executes immediately, it outputs `1`. +// Macrotask and microtask queues are empty, as of now. + +setTimeout(() => console.log(2)); +// `setTimeout` appends the callback to the macrotask queue. +// - macrotask queue content: +// `console.log(2)` + +Promise.resolve().then(() => console.log(3)); +// The callback is appended to the microtask queue. +// - microtask queue content: +// `console.log(3)` + +Promise.resolve().then(() => setTimeout(() => console.log(4))); +// The callback with `setTimeout(...4)` is appended to microtasks +// - microtask queue content: +// `console.log(3); setTimeout(...4)` + +Promise.resolve().then(() => console.log(5)); +// The callback is appended to the microtask queue +// - microtask queue content: +// `console.log(3); setTimeout(...4); console.log(5)` + +setTimeout(() => console.log(6)); +// `setTimeout` appends the callback to macrotasks +// - macrotask queue content: +// `console.log(2); console.log(6)` + +console.log(7); +// Outputs 7 immediately. +``` + +To summarize, + +1. Numbers `1` и `7` show up immediately, because simple `console.log` calls don't use any queues. +2. Then, after the main code flow is finished, the microtask queue runs. + - It has commands: `console.log(3); setTimeout(...4); console.log(5)`. + - Numbers `3` и `5` show up, while `setTimeout(() => console.log(4))` adds the `console.log(4)` call to the end of the macrotask queue. + - The macrotask queue is now: `console.log(2); console.log(6); console.log(4)`. +3. After the microtask queue becomes empty, the macrotask queue executes. It outputs `2`, `6`, `4`. + +Finally, we have the output: `1 7 3 5 2 6 4`. \ No newline at end of file diff --git a/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/task.md b/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/task.md new file mode 100644 index 000000000..ad406b3be --- /dev/null +++ b/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/task.md @@ -0,0 +1,21 @@ +importance: 5 + +--- + +# What will be the output of this code? + +```js +console.log(1); + +setTimeout(() => console.log(2)); + +Promise.resolve().then(() => console.log(3)); + +Promise.resolve().then(() => setTimeout(() => console.log(4))); + +Promise.resolve().then(() => console.log(5)); + +setTimeout(() => console.log(6)); + +console.log(7); +``` diff --git a/3-frames-and-windows/01-popup-windows/article.md b/3-frames-and-windows/01-popup-windows/article.md index d32715f87..01da2b304 100644 --- a/3-frames-and-windows/01-popup-windows/article.md +++ b/3-frames-and-windows/01-popup-windows/article.md @@ -87,7 +87,7 @@ Settings for `params`: There is also a number of less supported browser-specific features, which are usually not used. Check window.open in MDN for examples. -## Example: a minimalistic window +## Example: a minimalistic window Let's open a window with minimal set of features, just to see which of them browser allows to disable: @@ -120,7 +120,7 @@ Rules for omitted settings: ## Accessing popup from window -The `open` call returns a reference to the new window. It can be used to manipulate it's properties, change location and even more. +The `open` call returns a reference to the new window. It can be used to manipulate its properties, change location and even more. In this example, we generate popup content from JavaScript: @@ -239,7 +239,7 @@ There's also `window.onscroll` event. Theoretically, there are `window.focus()` and `window.blur()` methods to focus/unfocus on a window. And there are also `focus/blur` events that allow to catch the moment when the visitor focuses on a window and switches elsewhere. -Although, in practice they are severely limited, because in the past evil pages abused them. +Although, in practice they are severely limited, because in the past evil pages abused them. For instance, look at this code: @@ -257,10 +257,10 @@ Still, there are some use cases when such calls do work and can be useful. For instance: -- When we open a popup, it's might be a good idea to run a `newWindow.focus()` on it. Just in case, for some OS/browser combinations it ensures that the user is in the new window now. +- When we open a popup, it might be a good idea to run `newWindow.focus()` on it. Just in case, for some OS/browser combinations it ensures that the user is in the new window now. - If we want to track when a visitor actually uses our web-app, we can track `window.onfocus/onblur`. That allows us to suspend/resume in-page activities, animations etc. But please note that the `blur` event means that the visitor switched out from the window, but they still may observe it. The window is in the background, but still may be visible. -## Summary +## Summary Popup windows are used rarely, as there are alternatives: loading and displaying information in-page, or in iframe. diff --git a/3-frames-and-windows/03-cross-window-communication/article.md b/3-frames-and-windows/03-cross-window-communication/article.md index 091a0cecb..4d4e320e4 100644 --- a/3-frames-and-windows/03-cross-window-communication/article.md +++ b/3-frames-and-windows/03-cross-window-communication/article.md @@ -116,6 +116,13 @@ document.domain = 'site.com'; That's all. Now they can interact without limitations. Again, that's only possible for pages with the same second-level domain. +```warn header="Deprecated, but still working" +The `document.domain` property is in the process of being removed from the [specification](https://html.spec.whatwg.org/multipage/origin.html#relaxing-the-same-origin-restriction). The cross-window messaging (explained soon below) is the suggested replacement. + +That said, as of now all browsers support it. And the support will be kept for the future, not to break old code that relies on `document.domain`. +``` + + ## Iframe: wrong document pitfall When an iframe comes from the same origin, and we may access its `document`, there's a pitfall. It's not related to cross-origin things, but important to know. @@ -268,7 +275,7 @@ Arguments: `targetOrigin` : Specifies the origin for the target window, so that only a window from the given origin will get the message. -The `targetOrigin` is a safety measure. Remember, if the target window comes from another origin, we can't read it's `location` in the sender window. So we can't be sure which site is open in the intended window right now: the user could navigate away, and the sender window has no idea about it. +The `targetOrigin` is a safety measure. Remember, if the target window comes from another origin, we can't read its `location` in the sender window. So we can't be sure which site is open in the intended window right now: the user could navigate away, and the sender window has no idea about it. Specifying `targetOrigin` ensures that the window only receives the data if it's still at the right site. Important when the data is sensitive. diff --git a/3-frames-and-windows/06-clickjacking/article.md b/3-frames-and-windows/06-clickjacking/article.md index 1daa87dd0..34d0a91ae 100644 --- a/3-frames-and-windows/06-clickjacking/article.md +++ b/3-frames-and-windows/06-clickjacking/article.md @@ -154,7 +154,7 @@ Depending on your browser, the `iframe` above is either empty or alerting you th ## Showing with disabled functionality -The `X-Frame-Options` header has a side-effect. Other sites won't be able to show our page in a frame, even if they have good reasons to do so. +The `X-Frame-Options` header has a side effect. Other sites won't be able to show our page in a frame, even if they have good reasons to do so. So there are other solutions... For instance, we can "cover" the page with a `

` with styles `height: 100%; width: 100%;`, so that it will intercept all clicks. That `
` is to be removed if `window == top` or if we figure out that we don't need the protection. diff --git a/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js b/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js index 2f51384ef..00c37bb94 100644 --- a/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js +++ b/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js @@ -2,9 +2,9 @@ function concat(arrays) { // sum of individual array lengths let totalLength = arrays.reduce((acc, value) => acc + value.length, 0); - if (!arrays.length) return null; - let result = new Uint8Array(totalLength); + + if (!arrays.length) return result; // for each array - copy it over result // next array is copied right after the previous one diff --git a/4-binary/01-arraybuffer-binary-arrays/article.md b/4-binary/01-arraybuffer-binary-arrays/article.md index accd3c505..2827e277e 100644 --- a/4-binary/01-arraybuffer-binary-arrays/article.md +++ b/4-binary/01-arraybuffer-binary-arrays/article.md @@ -30,7 +30,7 @@ Let's eliminate a possible source of confusion. `ArrayBuffer` has nothing in com **To manipulate an `ArrayBuffer`, we need to use a "view" object.** -A view object does not store anything on it's own. It's the "eyeglasses" that give an interpretation of the bytes stored in the `ArrayBuffer`. +A view object does not store anything on its own. It's the "eyeglasses" that give an interpretation of the bytes stored in the `ArrayBuffer`. For instance: @@ -71,7 +71,7 @@ for(let num of view) { ## TypedArray -The common term for all these views (`Uint8Array`, `Uint32Array`, etc) is [TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects). They share the same set of methods and properities. +The common term for all these views (`Uint8Array`, `Uint32Array`, etc) is [TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects). They share the same set of methods and properties. Please note, there's no constructor called `TypedArray`, it's just a common "umbrella" term to represent one of views over `ArrayBuffer`: `Int8Array`, `Uint8Array` and so on, the full list will soon follow. @@ -126,9 +126,9 @@ new TypedArray(); We can create a `TypedArray` directly, without mentioning `ArrayBuffer`. But a view cannot exist without an underlying `ArrayBuffer`, so gets created automatically in all these cases except the first one (when provided). -To access the `ArrayBuffer`, there are properties: -- `arr.buffer` -- references the `ArrayBuffer`. -- `arr.byteLength` -- the length of the `ArrayBuffer`. +To access the underlying `ArrayBuffer`, there are following properties in `TypedArray`: +- `buffer` -- references the `ArrayBuffer`. +- `byteLength` -- the length of the `ArrayBuffer`. So, we can always move from one view to another: ```js diff --git a/4-binary/02-text-decoder/article.md b/4-binary/02-text-decoder/article.md index 3d836e6c0..a0c80145c 100644 --- a/4-binary/02-text-decoder/article.md +++ b/4-binary/02-text-decoder/article.md @@ -2,7 +2,7 @@ What if the binary data is actually a string? For instance, we received a file with textual data. -The build-in [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder) object allows to read the value into an actual JavaScript string, given the buffer and the encoding. +The built-in [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder) object allows one to read the value into an actual JavaScript string, given the buffer and the encoding. We first need to create it: ```js diff --git a/4-binary/03-blob/article.md b/4-binary/03-blob/article.md index a44308f6f..fc0150577 100644 --- a/4-binary/03-blob/article.md +++ b/4-binary/03-blob/article.md @@ -74,7 +74,7 @@ link.href = URL.createObjectURL(blob); We can also create a link dynamically in JavaScript and simulate a click by `link.click()`, then download starts automatically. -Here's the similar code that causes user to download the dynamicallly created `Blob`, without any HTML: +Here's the similar code that causes user to download the dynamically created `Blob`, without any HTML: ```js run let link = document.createElement('a'); @@ -101,7 +101,7 @@ For each URL generated by `URL.createObjectURL` the browser stores a URL -> `Blo A generated URL (and hence the link with it) is only valid within the current document, while it's open. And it allows to reference the `Blob` in ``, ``, basically any other object that expects a URL. -There's a side-effect though. While there's a mapping for a `Blob`, the `Blob` itself resides in the memory. The browser can't free it. +There's a side effect though. While there's a mapping for a `Blob`, the `Blob` itself resides in the memory. The browser can't free it. The mapping is automatically cleared on document unload, so `Blob` objects are freed then. But if an app is long-living, then that doesn't happen soon. @@ -186,7 +186,7 @@ let context = canvas.getContext('2d'); context.drawImage(img, 0, 0); // we can context.rotate(), and do many other things on canvas -// toBlob is async opereation, callback is called when done +// toBlob is async operation, callback is called when done canvas.toBlob(function(blob) { // blob ready, download it let link = document.createElement('a'); @@ -211,21 +211,44 @@ For screenshotting a page, we can use a library such as /* process the ArrayBuffer */); ``` +## From Blob to stream + +When we read and write to a blob of more than `2 GB`, the use of `arrayBuffer` becomes more memory intensive for us. At this point, we can directly convert the blob to a stream. + +A stream is a special object that allows to read from it (or write into it) portion by portion. It's outside of our scope here, but here's an example, and you can read more at . Streams are convenient for data that is suitable for processing piece-by-piece. + +The `Blob` interface's `stream()` method returns a `ReadableStream` which upon reading returns the data contained within the `Blob`. + +Then we can read from it, like this: + +```js +// get readableStream from blob +const readableStream = blob.stream(); +const stream = readableStream.getReader(); + +while (true) { + // for each iteration: value is the next blob fragment + let { done, value } = await stream.read(); + if (done) { + // no more data in the stream + console.log('all blob processed.'); + break; + } + + // do something with the data portion we've just read from the blob + console.log(value); +} +``` ## Summary @@ -235,7 +258,9 @@ That makes Blobs convenient for upload/download operations, that are so common i Methods that perform web-requests, such as [XMLHttpRequest](info:xmlhttprequest), [fetch](info:fetch) and so on, can work with `Blob` natively, as well as with other binary types. -We can easily convert betweeen `Blob` and low-level binary data types: +We can easily convert between `Blob` and low-level binary data types: + +- We can make a `Blob` from a typed array using `new Blob(...)` constructor. +- We can get back `ArrayBuffer` from a Blob using `blob.arrayBuffer()`, and then create a view over it for low-level binary processing. -- We can make a Blob from a typed array using `new Blob(...)` constructor. -- We can get back `ArrayBuffer` from a Blob using `FileReader`, and then create a view over it for low-level binary processing. +Conversion streams are very useful when we need to handle large blob. You can easily create a `ReadableStream` from a blob. The `Blob` interface's `stream()` method returns a `ReadableStream` which upon reading returns the data contained within the blob. diff --git a/5-network/01-fetch/article.md b/5-network/01-fetch/article.md index 688db2ba5..4669fc451 100644 --- a/5-network/01-fetch/article.md +++ b/5-network/01-fetch/article.md @@ -195,7 +195,7 @@ To make a `POST` request, or a request with another method, we need to use `fetc - **`method`** -- HTTP-method, e.g. `POST`, - **`body`** -- the request body, one of: - a string (e.g. JSON-encoded), - - `FormData` object, to submit the data as `form/multipart`, + - `FormData` object, to submit the data as `multipart/form-data`, - `Blob`/`BufferSource` to send binary data, - [URLSearchParams](info:url), to submit the data in `x-www-form-urlencoded` encoding, rarely used. @@ -298,13 +298,13 @@ fetch(url, options) Response properties: - `response.status` -- HTTP code of the response, -- `response.ok` -- `true` is the status is 200-299. +- `response.ok` -- `true` if the status is 200-299. - `response.headers` -- Map-like object with HTTP headers. Methods to get response body: - **`response.text()`** -- return the response as text, - **`response.json()`** -- parse the response as JSON object, -- **`response.formData()`** -- return the response as `FormData` object (form/multipart encoding, see the next chapter), +- **`response.formData()`** -- return the response as `FormData` object (`multipart/form-data` encoding, see the next chapter), - **`response.blob()`** -- return the response as [Blob](info:blob) (binary data with type), - **`response.arrayBuffer()`** -- return the response as [ArrayBuffer](info:arraybuffer-binary-arrays) (low-level binary data), diff --git a/5-network/02-formdata/article.md b/5-network/02-formdata/article.md index a3d924528..a73d554b1 100644 --- a/5-network/02-formdata/article.md +++ b/5-network/02-formdata/article.md @@ -168,7 +168,7 @@ The server reads form data and the file, as if it were a regular form submission [FormData](https://xhr.spec.whatwg.org/#interface-formdata) objects are used to capture HTML form and submit it using `fetch` or another network method. -We can either create `new FormData(form)` from an HTML form, or create a object without a form at all, and then append fields with methods: +We can either create `new FormData(form)` from an HTML form, or create an object without a form at all, and then append fields with methods: - `formData.append(name, value)` - `formData.append(name, blob, fileName)` diff --git a/5-network/03-fetch-progress/article.md b/5-network/03-fetch-progress/article.md index f5a64da54..76b05d514 100644 --- a/5-network/03-fetch-progress/article.md +++ b/5-network/03-fetch-progress/article.md @@ -5,11 +5,11 @@ The `fetch` method allows to track *download* progress. Please note: there's currently no way for `fetch` to track *upload* progress. For that purpose, please use [XMLHttpRequest](info:xmlhttprequest), we'll cover it later. -To track download progress, we can use `response.body` property. It's `ReadableStream` -- a special object that provides body chunk-by-chunk, as it comes. Readable streams are described in the [Streams API](https://streams.spec.whatwg.org/#rs-class) specification. +To track download progress, we can use `response.body` property. It's a `ReadableStream` -- a special object that provides body chunk-by-chunk, as it comes. Readable streams are described in the [Streams API](https://streams.spec.whatwg.org/#rs-class) specification. Unlike `response.text()`, `response.json()` and other methods, `response.body` gives full control over the reading process, and we can count how much is consumed at any moment. -Here's the sketch of code that reads the reponse from `response.body`: +Here's the sketch of code that reads the response from `response.body`: ```js // instead of response.json() and other methods diff --git a/5-network/04-fetch-abort/article.md b/5-network/04-fetch-abort/article.md index af66c37cc..eadc5aac2 100644 --- a/5-network/04-fetch-abort/article.md +++ b/5-network/04-fetch-abort/article.md @@ -24,7 +24,7 @@ When `abort()` is called: - `controller.signal` emits the `"abort"` event. - `controller.signal.aborted` property becomes `true`. -Generally, we have two parties in the process: +Generally, we have two parties in the process: 1. The one that performs a cancelable operation, it sets a listener on `controller.signal`. 2. The one that cancels: it calls `controller.abort()` when needed. @@ -34,7 +34,7 @@ Here's the full example (without `fetch` yet): let controller = new AbortController(); let signal = controller.signal; -// The party that performs a cancelable operation +// The party that performs a cancelable operation // gets the "signal" object // and sets the listener to trigger when controller.abort() is called signal.addEventListener('abort', () => alert("abort!")); @@ -143,6 +143,6 @@ let results = await Promise.all([...fetchJobs, ourJob]); ## Summary -- `AbortController` is a simple object that generates an `abort` event on it's `signal` property when the `abort()` method is called (and also sets `signal.aborted` to `true`). +- `AbortController` is a simple object that generates an `abort` event on its `signal` property when the `abort()` method is called (and also sets `signal.aborted` to `true`). - `fetch` integrates with it: we pass the `signal` property as the option, and then `fetch` listens to it, so it's possible to abort the `fetch`. - We can use `AbortController` in our code. The "call `abort()`" -> "listen to `abort` event" interaction is simple and universal. We can use it even without `fetch`. diff --git a/5-network/05-fetch-crossorigin/article.md b/5-network/05-fetch-crossorigin/article.md index d8d9bbb87..d45ee391d 100644 --- a/5-network/05-fetch-crossorigin/article.md +++ b/5-network/05-fetch-crossorigin/article.md @@ -207,10 +207,11 @@ Some time ago no one could even imagine that a webpage could make such requests. So, to avoid misunderstandings, any "unsafe" request -- that couldn't be done in the old times, the browser does not make such requests right away. First, it sends a preliminary, so-called "preflight" request, to ask for permission. -A preflight request uses the method `OPTIONS`, no body and two headers: +A preflight request uses the method `OPTIONS`, no body and three headers: - `Access-Control-Request-Method` header has the method of the unsafe request. - `Access-Control-Request-Headers` header provides a comma-separated list of its unsafe HTTP-headers. +- `Origin` header tells from where the request came. (such as `https://javascript.info`) If the server agrees to serve the requests, then it should respond with empty body, status 200 and headers: diff --git a/5-network/06-fetch-api/article.md b/5-network/06-fetch-api/article.md index e57689c75..c3f75175f 100644 --- a/5-network/06-fetch-api/article.md +++ b/5-network/06-fetch-api/article.md @@ -52,7 +52,7 @@ Usually that header is set automatically and contains the url of the page that m **The `referrer` option allows to set any `Referer` (within the current origin) or remove it.** -To send no referer, set an empty string: +To send no referrer, set an empty string: ```js fetch('/page', { *!* @@ -147,7 +147,7 @@ This option may be useful when the URL for `fetch` comes from a 3rd-party, and w The `credentials` option specifies whether `fetch` should send cookies and HTTP-Authorization headers with the request. - **`"same-origin"`** -- the default, don't send for cross-origin requests, -- **`"include"`** -- always send, requires `Accept-Control-Allow-Credentials` from cross-origin server in order for JavaScript to access the response, that was covered in the chapter , +- **`"include"`** -- always send, requires `Access-Control-Allow-Credentials` from cross-origin server in order for JavaScript to access the response, that was covered in the chapter , - **`"omit"`** -- never send, even for same-origin requests. ## cache @@ -171,7 +171,7 @@ The `redirect` option allows to change that: - **`"follow"`** -- the default, follow HTTP-redirects, - **`"error"`** -- error in case of HTTP-redirect, -- **`"manual"`** -- allows to process HTTP-redirects manually. In case of redirect, we'll get a special response object, with `response.type="opaqueredirect"` and and zeroed/empty status and most other properies. +- **`"manual"`** -- allows to process HTTP-redirects manually. In case of redirect, we'll get a special response object, with `response.type="opaqueredirect"` and zeroed/empty status and most other properies. ## integrity @@ -179,7 +179,7 @@ The `integrity` option allows to check if the response matches the known-ahead c As described in the [specification](https://w3c.github.io/webappsec-subresource-integrity/), supported hash-functions are SHA-256, SHA-384, and SHA-512, there might be others depending on the browser. -For example, we're downloading a file, and we know that it's SHA-256 checksum is "abcdef" (a real checksum is longer, of course). +For example, we're downloading a file, and we know that its SHA-256 checksum is "abcdef" (a real checksum is longer, of course). We can put it in the `integrity` option, like this: diff --git a/5-network/08-xmlhttprequest/article.md b/5-network/08-xmlhttprequest/article.md index 7dbc405a0..43d816cab 100644 --- a/5-network/08-xmlhttprequest/article.md +++ b/5-network/08-xmlhttprequest/article.md @@ -2,7 +2,7 @@ `XMLHttpRequest` is a built-in browser object that allows to make HTTP requests in JavaScript. -Despite of having the word "XML" in its name, it can operate on any data, not only in XML format. We can upload/download files, track progress and much more. +Despite having the word "XML" in its name, it can operate on any data, not only in XML format. We can upload/download files, track progress and much more. Right now, there's another, more modern method `fetch`, that somewhat deprecates `XMLHttpRequest`. diff --git a/5-network/11-websocket/article.md b/5-network/11-websocket/article.md index aab6d31be..25aead5f3 100644 --- a/5-network/11-websocket/article.md +++ b/5-network/11-websocket/article.md @@ -1,6 +1,6 @@ # WebSocket -The `WebSocket` protocol, described in the specification [RFC 6455](http://tools.ietf.org/html/rfc6455) provides a way to exchange data between browser and server via a persistent connection. The data can be passed in both directions as "packets", without breaking the connection and additional HTTP-requests. +The `WebSocket` protocol, described in the specification [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455), provides a way to exchange data between browser and server via a persistent connection. The data can be passed in both directions as "packets", without breaking the connection and the need of additional HTTP-requests. WebSocket is especially great for services that require continuous data exchange, e.g. online games, real-time trading systems and so on. @@ -19,7 +19,7 @@ The `wss://` protocol is not only encrypted, but also more reliable. That's because `ws://` data is not encrypted, visible for any intermediary. Old proxy servers do not know about WebSocket, they may see "strange" headers and abort the connection. -On the other hand, `wss://` is WebSocket over TLS, (same as HTTPS is HTTP over TLS), the transport security layer encrypts the data at sender and decrypts at the receiver. So data packets are passed encrypted through proxies. They can't see what's inside and let them through. +On the other hand, `wss://` is WebSocket over TLS, (same as HTTPS is HTTP over TLS), the transport security layer encrypts the data at the sender and decrypts it at the receiver. So data packets are passed encrypted through proxies. They can't see what's inside and let them through. ``` Once the socket is created, we should listen to events on it. There are totally 4 events: @@ -72,11 +72,11 @@ Now let's talk more in-depth. When `new WebSocket(url)` is created, it starts connecting immediately. -During the connection the browser (using headers) asks the server: "Do you support Websocket?" And if the server replies "yes", then the talk continues in WebSocket protocol, which is not HTTP at all. +During the connection, the browser (using headers) asks the server: "Do you support Websocket?" And if the server replies "yes", then the talk continues in WebSocket protocol, which is not HTTP at all. ![](websocket-handshake.svg) -Here's an example of browser headers for request made by `new WebSocket("wss://javascript.info/chat")`. +Here's an example of browser headers for a request made by `new WebSocket("wss://javascript.info/chat")`. ``` GET /chat @@ -88,10 +88,10 @@ Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q== Sec-WebSocket-Version: 13 ``` -- `Origin` -- the origin of the client page, e.g. `https://javascript.info`. WebSocket objects are cross-origin by nature. There are no special headers or other limitations. Old servers are unable to handle WebSocket anyway, so there are no compabitility issues. But `Origin` header is important, as it allows the server to decide whether or not to talk WebSocket with this website. +- `Origin` -- the origin of the client page, e.g. `https://javascript.info`. WebSocket objects are cross-origin by nature. There are no special headers or other limitations. Old servers are unable to handle WebSocket anyway, so there are no compatibility issues. But the `Origin` header is important, as it allows the server to decide whether or not to talk WebSocket with this website. - `Connection: Upgrade` -- signals that the client would like to change the protocol. - `Upgrade: websocket` -- the requested protocol is "websocket". -- `Sec-WebSocket-Key` -- a random browser-generated key for security. +- `Sec-WebSocket-Key` -- a random browser-generated key, used to ensure that the server supports WebSocket protocol. It's random to prevent proxies from caching any following communication. - `Sec-WebSocket-Version` -- WebSocket protocol version, 13 is the current one. ```smart header="WebSocket handshake can't be emulated" @@ -107,9 +107,9 @@ Connection: Upgrade Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g= ``` -Here `Sec-WebSocket-Accept` is `Sec-WebSocket-Key`, recoded using a special algorithm. The browser uses it to make sure that the response corresponds to the request. +Here `Sec-WebSocket-Accept` is `Sec-WebSocket-Key`, recoded using a special algorithm. Upon seeing it, the browser understands that the server really does support the WebSocket protocol. -Afterwards, the data is transfered using WebSocket protocol, we'll see its structure ("frames") soon. And that's not HTTP at all. +Afterwards, the data is transferred using the WebSocket protocol, we'll see its structure ("frames") soon. And that's not HTTP at all. ### Extensions and subprotocols @@ -117,9 +117,9 @@ There may be additional headers `Sec-WebSocket-Extensions` and `Sec-WebSocket-Pr For instance: -- `Sec-WebSocket-Extensions: deflate-frame` means that the browser supports data compression. An extension is something related to transferring the data, functionality that extends WebSocket protocol. The header `Sec-WebSocket-Extensions` is sent automatically by the browser, with the list of all extensions it supports. +- `Sec-WebSocket-Extensions: deflate-frame` means that the browser supports data compression. An extension is something related to transferring the data, functionality that extends the WebSocket protocol. The header `Sec-WebSocket-Extensions` is sent automatically by the browser, with the list of all extensions it supports. -- `Sec-WebSocket-Protocol: soap, wamp` means that we'd like to transfer not just any data, but the data in [SOAP](http://en.wikipedia.org/wiki/SOAP) or WAMP ("The WebSocket Application Messaging Protocol") protocols. WebSocket subprotocols are registered in the [IANA catalogue](http://www.iana.org/assignments/websocket/websocket.xml). So, this header describes data formats that we're going to use. +- `Sec-WebSocket-Protocol: soap, wamp` means that we'd like to transfer not just any data, but the data in [SOAP](https://en.wikipedia.org/wiki/SOAP) or WAMP ("The WebSocket Application Messaging Protocol") protocols. WebSocket subprotocols are registered in the [IANA catalogue](https://www.iana.org/assignments/websocket/websocket.xml). So, this header describes the data formats that we're going to use. This optional header is set using the second parameter of `new WebSocket`. That's the array of subprotocols, e.g. if we'd like to use SOAP or WAMP: @@ -173,7 +173,7 @@ In the browser, we directly work only with text or binary frames. **WebSocket `.send()` method can send either text or binary data.** -A call `socket.send(body)` allows `body` in string or a binary format, including `Blob`, `ArrayBuffer`, etc. No settings required: just send it out in any format. +A call `socket.send(body)` allows `body` in string or a binary format, including `Blob`, `ArrayBuffer`, etc. No settings are required: just send it out in any format. **When we receive the data, text always comes as string. And for binary data, we can choose between `Blob` and `ArrayBuffer` formats.** @@ -221,7 +221,7 @@ socket.close([code], [reason]); - `code` is a special WebSocket closing code (optional) - `reason` is a string that describes the reason of closing (optional) -Then the other party in `close` event handler gets the code and the reason, e.g.: +Then the other party in the `close` event handler gets the code and the reason, e.g.: ```js // closing party: @@ -249,7 +249,7 @@ There are other codes like: The full list can be found in [RFC6455, §7.4.1](https://tools.ietf.org/html/rfc6455#section-7.4.1). -WebSocket codes are somewhat like HTTP codes, but different. In particular, any codes less than `1000` are reserved, there'll be an error if we try to set such a code. +WebSocket codes are somewhat like HTTP codes, but different. In particular, codes lower than `1000` are reserved, there'll be an error if we try to set such a code. ```js // in case connection is broken @@ -321,8 +321,8 @@ Server-side code is a little bit beyond our scope. Here we'll use Node.js, but y The server-side algorithm will be: 1. Create `clients = new Set()` -- a set of sockets. -2. For each accepted websocket, add it to the set `clients.add(socket)` and setup `message` event listener to get its messages. -3. When a message received: iterate over clients and send it to everyone. +2. For each accepted websocket, add it to the set `clients.add(socket)` and set `message` event listener to get its messages. +3. When a message is received: iterate over clients and send it to everyone. 4. When a connection is closed: `clients.delete(socket)`. ```js @@ -359,7 +359,7 @@ Here's the working example: [iframe src="chat" height="100" zip] -You can also download it (upper-right button in the iframe) and run locally. Just don't forget to install [Node.js](https://nodejs.org/en/) and `npm install ws` before running. +You can also download it (upper-right button in the iframe) and run it locally. Just don't forget to install [Node.js](https://nodejs.org/en/) and `npm install ws` before running. ## Summary @@ -383,6 +383,6 @@ Events: WebSocket by itself does not include reconnection, authentication and many other high-level mechanisms. So there are client/server libraries for that, and it's also possible to implement these capabilities manually. -Sometimes, to integrate WebSocket into existing project, people run WebSocket server in parallel with the main HTTP-server, and they share a single database. Requests to WebSocket use `wss://ws.site.com`, a subdomain that leads to WebSocket server, while `https://site.com` goes to the main HTTP-server. +Sometimes, to integrate WebSocket into existing projects, people run a WebSocket server in parallel with the main HTTP-server, and they share a single database. Requests to WebSocket use `wss://ws.site.com`, a subdomain that leads to the WebSocket server, while `https://site.com` goes to the main HTTP-server. Surely, other ways of integration are also possible. diff --git a/5-network/11-websocket/demo.view/server.js b/5-network/11-websocket/demo.view/server.js index 111a7ce75..bff739938 100644 --- a/5-network/11-websocket/demo.view/server.js +++ b/5-network/11-websocket/demo.view/server.js @@ -21,6 +21,7 @@ function accept(req, res) { function onConnect(ws) { ws.on('message', function (message) { + message = message.toString(); let name = message.match(/([\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]+)$/gu) || "Guest"; ws.send(`Hello from server, ${name}!`); diff --git a/6-data-storage/01-cookie/article.md b/6-data-storage/01-cookie/article.md index 9fc5f6955..b88849457 100644 --- a/6-data-storage/01-cookie/article.md +++ b/6-data-storage/01-cookie/article.md @@ -39,7 +39,7 @@ We leave it as an exercise for the reader. Also, at the end of the chapter you'l ## Writing to document.cookie -We can write to `document.cookie`. But it's not a data property, it's an accessor (getter/setter). An assignment to it is treated specially. +We can write to `document.cookie`. But it's not a data property, it's an [accessor (getter/setter)](info:property-accessors). An assignment to it is treated specially. **A write operation to `document.cookie` updates only cookies mentioned in it, but doesn't touch other cookies.** @@ -96,28 +96,32 @@ Usually, we should set `path` to the root: `path=/` to make the cookie accessibl A domain defines where the cookie is accessible. In practice though, there are limitations. We can't set any domain. -By default, a cookie is accessible only at the domain that set it. So, if the cookie was set by `site.com`, we won't get it at `other.com`. +**There's no way to let a cookie be accessible from another 2nd-level domain, so `other.com` will never receive a cookie set at `site.com`.** + +It's a safety restriction, to allow us to store sensitive data in cookies that should be available only on one site. + +By default, a cookie is accessible only at the domain that set it. -...But what's more tricky, we also won't get the cookie at a subdomain `forum.site.com`! +Please note, by default a cookie is also not shared to a subdomain as well, such as `forum.site.com`. ```js -// at site.com +// if we set a cookie at site.com website... document.cookie = "user=John" -// at forum.site.com +// ...we won't see it at forum.site.com alert(document.cookie); // no user ``` -**There's no way to let a cookie be accessible from another 2nd-level domain, so `other.com` will never receive a cookie set at `site.com`.** +...But this can be changed. If we'd like to allow subdomains like `forum.site.com` to get a cookie set at `site.com`, that's possible. -It's a safety restriction, to allow us to store sensitive data in cookies, that should be available only on one site. +For that to happen, when setting a cookie at `site.com`, we should explicitly set the `domain` option to the root domain: `domain=site.com`. Then all subdomains will see such cookie. -...But if we'd like to allow subdomains like `forum.site.com` to get a cookie, that's possible. When setting a cookie at `site.com`, we should explicitly set the `domain` option to the root domain: `domain=site.com`: +For example: ```js // at site.com // make the cookie accessible on any subdomain *.site.com: -document.cookie = "user=John; domain=site.com" +document.cookie = "user=John; *!*domain=site.com*/!*" // later @@ -125,9 +129,9 @@ document.cookie = "user=John; domain=site.com" alert(document.cookie); // has cookie user=John ``` -For historical reasons, `domain=.site.com` (a dot before `site.com`) also works the same way, allowing access to the cookie from subdomains. That's an old notation and should be used if we need to support very old browsers. +For historical reasons, `domain=.site.com` (with a dot before `site.com`) also works the same way, allowing access to the cookie from subdomains. That's an old notation and should be used if we need to support very old browsers. -So, the `domain` option allows to make a cookie accessible at subdomains. +To summarize, the `domain` option allows to make a cookie accessible at subdomains. ## expires, max-age @@ -180,7 +184,7 @@ With this option, if a cookie is set by `https://site.com`, then it doesn't appe // assuming we're on https:// now // set the cookie to be secure (only accessible over HTTPS) document.cookie = "user=John; secure"; -``` +``` ## samesite @@ -247,8 +251,11 @@ But anything more complicated, like a network request from another site or a for If that's fine for you, then adding `samesite=lax` will probably not break the user experience and add protection. -Overall, `samesite` is a great option, but it has an important drawback: -- `samesite` is ignored (not supported) by old browsers, year 2017 or so. +Overall, `samesite` is a great option. + +There's a drawback: + +- `samesite` is ignored (not supported) by very old browsers, year 2017 or so. **So if we solely rely on `samesite` to provide protection, then old browsers will be vulnerable.** @@ -275,7 +282,6 @@ Here's a small set of functions to work with cookies, more convenient than a man There exist many cookie libraries for that, so these are for demo purposes. Fully working though. - ### getCookie(name) The shortest way to access a cookie is to use a [regular expression](info:regular-expressions). @@ -412,10 +418,10 @@ GDPR is not only about cookies, it's about other privacy-related issues too, but ## Summary -`document.cookie` provides access to cookies -- write operations modify only cookies mentioned in it. -- name/value must be encoded. -- one cookie must not exceed 4KB, 20+ cookies per site (depends on the browser). +`document.cookie` provides access to cookies. +- Write operations modify only cookies mentioned in it. +- Name/value must be encoded. +- One cookie may not exceed 4KB in size. The number of cookies allowed on a domain is around 20+ (varies by browser). Cookie options: - `path=/`, by default current path, makes the cookie visible only under that path. diff --git a/6-data-storage/01-cookie/cookie.js b/6-data-storage/01-cookie/cookie.js index cd3e17c02..653ea5b7f 100644 --- a/6-data-storage/01-cookie/cookie.js +++ b/6-data-storage/01-cookie/cookie.js @@ -1,6 +1,6 @@ function getCookie(name) { let matches = document.cookie.match(new RegExp( - "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" + "(?:^|; )" + name.replace(/([.$?*|{}()[\]\\/+^])/g, '\\$1') + "=([^;]*)" )); return matches ? decodeURIComponent(matches[1]) : undefined; } diff --git a/6-data-storage/02-localstorage/article.md b/6-data-storage/02-localstorage/article.md index e2e2751db..e4a4b3558 100644 --- a/6-data-storage/02-localstorage/article.md +++ b/6-data-storage/02-localstorage/article.md @@ -6,7 +6,7 @@ What's interesting about them is that the data survives a page refresh (for `ses We already have cookies. Why additional objects? -- Unlike cookies, web storage objects are not sent to server with each request. Because of that, we can store much more. Most browsers allow at least 2 megabytes of data (or more) and have settings to configure that. +- Unlike cookies, web storage objects are not sent to server with each request. Because of that, we can store much more. Most modern browsers allow at least 5 megabytes of data (or more) and have settings to configure that. - Also unlike cookies, the server can't manipulate storage objects via HTTP headers. Everything's done in JavaScript. - The storage is bound to the origin (domain/protocol/port triplet). That is, different protocols or subdomains infer different storage objects, they can't access data from each other. @@ -64,6 +64,7 @@ delete localStorage.test; That's allowed for historical reasons, and mostly works, but generally not recommended, because: 1. If the key is user-generated, it can be anything, like `length` or `toString`, or another built-in method of `localStorage`. In that case `getItem/setItem` work fine, while object-like access fails: + ```js run let key = 'length'; localStorage[key] = 5; // Error, can't assign length @@ -119,7 +120,6 @@ for(let key of keys) { The latter works, because `Object.keys` only returns the keys that belong to the object, ignoring the prototype. - ## Strings only Please note that both key and value must be strings. @@ -127,17 +127,17 @@ Please note that both key and value must be strings. If were any other type, like a number, or an object, it gets converted to string automatically: ```js run -sessionStorage.user = {name: "John"}; -alert(sessionStorage.user); // [object Object] +localStorage.user = {name: "John"}; +alert(localStorage.user); // [object Object] ``` We can use `JSON` to store objects though: ```js run -sessionStorage.user = JSON.stringify({name: "John"}); +localStorage.user = JSON.stringify({name: "John"}); // sometime later -let user = JSON.parse( sessionStorage.user ); +let user = JSON.parse( localStorage.user ); alert( user.name ); // John ``` @@ -148,7 +148,6 @@ Also it is possible to stringify the whole storage object, e.g. for debugging pu alert( JSON.stringify(localStorage, null, 2) ); ``` - ## sessionStorage The `sessionStorage` object is used much less often than `localStorage`. @@ -180,7 +179,7 @@ That's exactly because `sessionStorage` is bound not only to the origin, but als ## Storage event -When the data gets updated in `localStorage` or `sessionStorage`, [storage](https://www.w3.org/TR/webstorage/#the-storage-event) event triggers, with properties: +When the data gets updated in `localStorage` or `sessionStorage`, [storage](https://html.spec.whatwg.org/multipage/webstorage.html#the-storageevent-interface) event triggers, with properties: - `key` – the key that was changed (`null` if `.clear()` is called). - `oldValue` – the old value (`null` if the key is newly added). @@ -202,7 +201,7 @@ If both windows are listening for `window.onstorage`, then each one will react o ```js run // triggers on updates made to the same storage from other documents -window.onstorage = event => { // same as window.addEventListener('storage', event => { +window.onstorage = event => { // can also use window.addEventListener('storage', event => { if (event.key != 'now') return; alert(event.key + ':' + event.newValue + " at " + event.url); }; @@ -221,6 +220,7 @@ Modern browsers also support [Broadcast channel API](mdn:/api/Broadcast_Channel_ ## Summary Web storage objects `localStorage` and `sessionStorage` allow to store key/value in the browser. + - Both `key` and `value` must be strings. - The limit is 5mb+, depends on the browser. - They do not expire. diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md index a4fb78c1e..70493e9e1 100644 --- a/6-data-storage/03-indexeddb/article.md +++ b/6-data-storage/03-indexeddb/article.md @@ -5,7 +5,7 @@ libs: # IndexedDB -IndexedDB is a database that is built into browser, much more powerful than `localStorage`. +IndexedDB is a database that is built into a browser, much more powerful than `localStorage`. - Stores almost any kind of values by keys, multiple key types. - Supports transactions for reliability. @@ -50,7 +50,7 @@ Unlike server-side databases, IndexedDB is client-side, the data is stored in th If the local database version is less than specified in `open`, then a special event `upgradeneeded` is triggered, and we can compare versions and upgrade data structures as needed. -The `upgradeneeded` event also triggers when the database doesn't yet exist (technically, it's version is `0`), so we can perform the initialization. +The `upgradeneeded` event also triggers when the database doesn't yet exist (technically, its version is `0`), so we can perform the initialization. Let's say we published the first version of our app. @@ -95,7 +95,7 @@ openRequest.onupgradeneeded = function(event) { }; ``` -Please note: as our current version is `2`, `onupgradeneeded` handler has a code branch for version `0`, suitable for users that are accessing for the first time and have no database, and also for version `1`, for upgrades. +Please note: as our current version is `2`, the `onupgradeneeded` handler has a code branch for version `0`, suitable for users that are accessing for the first time and have no database, and also for version `1`, for upgrades. And then, only if `onupgradeneeded` handler finishes without errors, `openRequest.onsuccess` triggers, and the database is considered successfully opened. @@ -106,7 +106,7 @@ let deleteRequest = indexedDB.deleteDatabase(name) // deleteRequest.onsuccess/onerror tracks the result ``` -```warn header="We can't open an older version of the database" +```warn header="We can't open a database using an older open call version" If the current user database has a higher version than in the `open` call, e.g. the existing DB version is `3`, and we try to `open(...2)`, then that's an error, `openRequest.onerror` triggers. That's rare, but such a thing may happen when a visitor loads outdated JavaScript code, e.g. from a proxy cache. So the code is old, but his database is new. @@ -156,7 +156,7 @@ openRequest.onsuccess = function() { openRequest.onblocked = function() { // this event shouldn't trigger if we handle onversionchange correctly - // it means that there's another open connection to same database + // it means that there's another open connection to the same database // and it wasn't closed after db.onversionchange triggered for it }; */!* @@ -171,7 +171,7 @@ We can handle things more gracefully in `db.onversionchange`, prompt the visitor Or, an alternative approach would be to not close the database in `db.onversionchange`, but instead use the `onblocked` handler (in the new tab) to alert the visitor, tell him that the newer version can't be loaded until they close other tabs. -These update collisions happen rarely, but we should at least have some handling for them, at least `onblocked` handler, to prevent our script from dying silently. +These update collisions happen rarely, but we should at least have some handling for them, at least an `onblocked` handler, to prevent our script from dying silently. ## Object store @@ -189,17 +189,16 @@ An example of an object that can't be stored: an object with circular references **There must be a unique `key` for every value in the store.** -A key must be one of the these types - number, date, string, binary, or array. It's a unique identifier, so we can search/remove/update values by the key. +A key must be one of these types - number, date, string, binary, or array. It's a unique identifier, so we can search/remove/update values by the key. ![](indexeddb-structure.svg) - As we'll see very soon, we can provide a key when we add a value to the store, similar to `localStorage`. But when we store objects, IndexedDB allows setting up an object property as the key, which is much more convenient. Or we can auto-generate keys. But we need to create an object store first. - The syntax to create an object store: + ```js db.createObjectStore(name[, keyOptions]); ``` @@ -214,6 +213,7 @@ Please note, the operation is synchronous, no `await` needed. If we don't supply `keyOptions`, then we'll need to provide a key explicitly later, when storing an object. For instance, this object store uses `id` property as the key: + ```js db.createObjectStore('books', {keyPath: 'id'}); ``` @@ -223,6 +223,7 @@ db.createObjectStore('books', {keyPath: 'id'}); That's a technical limitation. Outside of the handler we'll be able to add/remove/update the data, but object stores can only be created/removed/altered during a version update. To perform a database version upgrade, there are two main approaches: + 1. We can implement per-version upgrade functions: from 1 to 2, from 2 to 3, from 3 to 4 etc. Then, in `upgradeneeded` we can compare versions (e.g. old 2, now 4) and run per-version upgrades step by step, for every intermediate version (2 to 3, then 3 to 4). 2. Or we can just examine the database: get a list of existing object stores as `db.objectStoreNames`. That object is a [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) that provides `contains(name)` method to check for existance. And then we can do updates depending on what exists and what doesn't. @@ -242,7 +243,6 @@ openRequest.onupgradeneeded = function() { }; ``` - To delete an object store: ```js @@ -253,9 +253,10 @@ db.deleteObjectStore('books') The term "transaction" is generic, used in many kinds of databases. -A transaction is a group operations, that should either all succeed or all fail. +A transaction is a group of operations, that should either all succeed or all fail. For instance, when a person buys something, we need to: + 1. Subtract the money from their account. 2. Add the item to their inventory. @@ -276,7 +277,7 @@ db.transaction(store[, type]); - `readonly` -- can only read, the default. - `readwrite` -- can only read and write the data, but not create/remove/alter object stores. -There's also `versionchange` transaction type: such transactions can do everything, but we can't create them manually. IndexedDB automatically creates a `versionchange` transaction when opening the database, for `updateneeded` handler. That's why it's a single place where we can update the database structure, create/remove object stores. +There's also `versionchange` transaction type: such transactions can do everything, but we can't create them manually. IndexedDB automatically creates a `versionchange` transaction when opening the database, for `upgradeneeded` handler. That's why it's a single place where we can update the database structure, create/remove object stores. ```smart header="Why are there different types of transactions?" Performance is the reason why transactions need to be labeled either `readonly` and `readwrite`. @@ -347,9 +348,9 @@ Usually, we can assume that a transaction commits when all its requests are comp So, in the example above no special call is needed to finish the transaction. -Transactions auto-commit principle has an important side effect. We can't insert an async operation like `fetch`, `setTimeout` in the middle of transaction. IndexedDB will not keep the transaction waiting till these are done. +Transactions auto-commit principle has an important side effect. We can't insert an async operation like `fetch`, `setTimeout` in the middle of a transaction. IndexedDB will not keep the transaction waiting till these are done. -In the code below, `request2` in line `(*)` fails, because the transaction is already committed, and can't make any request in it: +In the code below, `request2` in the line `(*)` fails, because the transaction is already committed, and can't make any request in it: ```js let request1 = books.add(book); @@ -370,7 +371,7 @@ That's because `fetch` is an asynchronous operation, a macrotask. Transactions a Authors of IndexedDB spec believe that transactions should be short-lived. Mostly for performance reasons. -Notably, `readwrite` transactions "lock" the stores for writing. So if one part of application initiated `readwrite` on `books` object store, then another part that wants to do the same has to wait: the new transaction "hangs" till the first one is done. That can lead to strange delays if transactions take a long time. +Notably, `readwrite` transactions "lock" the stores for writing. So if one part of the application initiated `readwrite` on `books` object store, then another part that wants to do the same has to wait: the new transaction "hangs" till the first one is done. That can lead to strange delays if transactions take a long time. So, what to do? @@ -614,6 +615,7 @@ The `delete` method looks up values to delete by a query, the call format is sim - **`delete(query)`** -- delete matching values by query. For instance: + ```js // delete the book with id='js' books.delete('js'); @@ -632,6 +634,7 @@ request.onsuccess = function() { ``` To delete everything: + ```js books.clear(); // clear the storage. ``` @@ -651,6 +654,7 @@ Cursors provide the means to work around that. As an object store is sorted internally by key, a cursor walks the store in key order (ascending by default). The syntax: + ```js // like getAll, but with a cursor: let request = store.openCursor(query, [direction]); @@ -748,7 +752,6 @@ try { } catch(err) { console.log('error', err.message); } - ``` So we have all the sweet "plain async code" and "try..catch" stuff. @@ -771,10 +774,8 @@ window.addEventListener('unhandledrejection', event => { ### "Inactive transaction" pitfall - As we already know, a transaction auto-commits as soon as the browser is done with the current code and microtasks. So if we put a *macrotask* like `fetch` in the middle of a transaction, then the transaction won't wait for it to finish. It just auto-commits. So the next request in it would fail. - For a promise wrapper and `async/await` the situation is the same. Here's an example of `fetch` in the middle of the transaction: @@ -792,7 +793,8 @@ await inventory.add({ id: 'js', price: 10, created: new Date() }); // Error The next `inventory.add` after `fetch` `(*)` fails with an "inactive transaction" error, because the transaction is already committed and closed at that time. -The workaround is same as when working with native IndexedDB: either make a new transaction or just split things apart. +The workaround is the same as when working with native IndexedDB: either make a new transaction or just split things apart. + 1. Prepare the data and fetch all that's needed first. 2. Then save in the database. diff --git a/7-animation/1-bezier-curve/article.md b/7-animation/1-bezier-curve/article.md index cfc80df00..e5a5b91fa 100644 --- a/7-animation/1-bezier-curve/article.md +++ b/7-animation/1-bezier-curve/article.md @@ -4,7 +4,17 @@ هناك اشياء بسيطة تستحق الدراسة مرة واحدة ثم بعد ذلك تشعر بالارتياح في عالم الرسومات المتجهةو علوم الرسوم المتحركة المتقدمة. +<<<<<<< HEAD ## نقاط التحكم +======= +```smart header="Some theory, please" +This article provides a theoretical, but very needed insight into what Bezier curves are, while [the next one](info:css-animations#bezier-curve) shows how we can use them for CSS animations. + +Please take your time to read and understand the concept, it'll serve you well. +``` + +## Control points +>>>>>>> 53b35c16835b7020a0a5046da5a47599d313bbb8 [منحنى بيزير](https://ar.wikipedia.org/wiki/%D9%85%D9%86%D8%AD%D9%86%D9%89_%D8%A8%D9%8A%D8%B2%D9%8A%D9%87) يتم تعريفه من خلال نقاط التحكم. diff --git a/7-animation/2-css-animations/3-animate-circle/solution.view/index.html b/7-animation/2-css-animations/3-animate-circle/solution.view/index.html index 88b154254..43b927e8a 100644 --- a/7-animation/2-css-animations/3-animate-circle/solution.view/index.html +++ b/7-animation/2-css-animations/3-animate-circle/solution.view/index.html @@ -5,7 +5,7 @@ + +``` + +Here's a more complex example, with `@keyframes`: + +```html run height=80 autorun no-beautify +

click me to start / stop

+ +``` + ## Summary -CSS animations allow smoothly (or not) animated changes of one or multiple CSS properties. +CSS animations allow smoothly (or step-by-step) animated changes of one or multiple CSS properties. They are good for most animation tasks. We're also able to use JavaScript for animations, the next chapter is devoted to that. @@ -422,6 +525,8 @@ Limitations of CSS animations compared to JavaScript animations: - Not just property changes. We can create new elements in JavaScript as part of the animation. ``` +In early examples in this chapter, we animate `font-size`, `left`, `width`, `height`, etc. In real life projects, we should use `transform: scale()` and `transform: translate()` for better performance. + The majority of animations can be implemented using CSS as described in this chapter. And the `transitionend` event allows JavaScript to be run after the animation, so it integrates fine with the code. But in the next chapter we'll do some JavaScript animations to cover more complex cases. diff --git a/7-animation/3-js-animation/1-animate-ball/solution.view/index.html b/7-animation/3-js-animation/1-animate-ball/solution.view/index.html index 7e031e8d1..146033cf7 100644 --- a/7-animation/3-js-animation/1-animate-ball/solution.view/index.html +++ b/7-animation/3-js-animation/1-animate-ball/solution.view/index.html @@ -21,7 +21,7 @@ } function bounce(timeFraction) { - for (let a = 0, b = 1, result; 1; a += b, b /= 2) { + for (let a = 0, b = 1; 1; a += b, b /= 2) { if (timeFraction >= (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) } diff --git a/7-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html b/7-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html index b246f422f..f587ff607 100644 --- a/7-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html +++ b/7-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html @@ -21,7 +21,7 @@ } function bounce(timeFraction) { - for (let a = 0, b = 1, result; 1; a += b, b /= 2) { + for (let a = 0, b = 1; 1; a += b, b /= 2) { if (timeFraction >= (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) } diff --git a/7-animation/3-js-animation/article.md b/7-animation/3-js-animation/article.md index 517b2481b..be6483145 100644 --- a/7-animation/3-js-animation/article.md +++ b/7-animation/3-js-animation/article.md @@ -96,7 +96,7 @@ The returned value `requestId` can be used to cancel the call: cancelAnimationFrame(requestId); ``` -The `callback` gets one argument -- the time passed from the beginning of the page load in microseconds. This time can also be obtained by calling [performance.now()](mdn:api/Performance/now). +The `callback` gets one argument -- the time passed from the beginning of the page load in milliseconds. This time can also be obtained by calling [performance.now()](mdn:api/Performance/now). Usually `callback` runs very soon, unless the CPU is overloaded or the laptop battery is almost discharged, or there's another reason. @@ -159,7 +159,7 @@ Function `animate` accepts 3 parameters that essentially describes the animation } ``` - It's graph: + Its graph: ![](linear.svg) That's just like `transition-timing-function: linear`. There are more interesting variants shown below. @@ -283,7 +283,7 @@ The `bounce` function does the same, but in the reverse order: "bouncing" starts ```js function bounce(timeFraction) { - for (let a = 0, b = 1, result; 1; a += b, b /= 2) { + for (let a = 0, b = 1; 1; a += b, b /= 2) { if (timeFraction >= (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) } diff --git a/7-animation/3-js-animation/bounce-easeinout.view/index.html b/7-animation/3-js-animation/bounce-easeinout.view/index.html index 837c50db1..aed3d9d08 100644 --- a/7-animation/3-js-animation/bounce-easeinout.view/index.html +++ b/7-animation/3-js-animation/bounce-easeinout.view/index.html @@ -26,7 +26,7 @@ function bounce(timeFraction) { - for (let a = 0, b = 1, result; 1; a += b, b /= 2) { + for (let a = 0, b = 1; 1; a += b, b /= 2) { if (timeFraction >= (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) } diff --git a/7-animation/3-js-animation/bounce-easeout.view/index.html b/7-animation/3-js-animation/bounce-easeout.view/index.html index e52eae8de..69dbb7ce0 100644 --- a/7-animation/3-js-animation/bounce-easeout.view/index.html +++ b/7-animation/3-js-animation/bounce-easeout.view/index.html @@ -22,7 +22,7 @@ } function bounce(timeFraction) { - for (let a = 0, b = 1, result; 1; a += b, b /= 2) { + for (let a = 0, b = 1; 1; a += b, b /= 2) { if (timeFraction >= (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) } diff --git a/7-animation/3-js-animation/bounce.view/index.html b/7-animation/3-js-animation/bounce.view/index.html index 1be2580d9..3575ed820 100644 --- a/7-animation/3-js-animation/bounce.view/index.html +++ b/7-animation/3-js-animation/bounce.view/index.html @@ -19,7 +19,7 @@ animate({ duration: 3000, timing: function bounce(timeFraction) { - for (let a = 0, b = 1, result; 1; a += b, b /= 2) { + for (let a = 0, b = 1; 1; a += b, b /= 2) { if (timeFraction >= (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) } diff --git a/7-animation/3-js-animation/text.view/index.html b/7-animation/3-js-animation/text.view/index.html index e404fe5c4..4947e4cd4 100644 --- a/7-animation/3-js-animation/text.view/index.html +++ b/7-animation/3-js-animation/text.view/index.html @@ -29,14 +29,14 @@ timing: bounce, draw: function(progress) { let result = (to - from) * progress + from; - textArea.value = text.substr(0, Math.ceil(result)) + textArea.value = text.slice(0, Math.ceil(result)) } }); } function bounce(timeFraction) { - for (let a = 0, b = 1, result; 1; a += b, b /= 2) { + for (let a = 0, b = 1; 1; a += b, b /= 2) { if (timeFraction >= (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) } diff --git a/8-web-components/1-webcomponents-intro/article.md b/8-web-components/1-webcomponents-intro/article.md index 3279cb133..c3522dea9 100644 --- a/8-web-components/1-webcomponents-intro/article.md +++ b/8-web-components/1-webcomponents-intro/article.md @@ -26,9 +26,9 @@ The International Space Station: ...And this thing flies, keeps humans alive in space! -How such complex devices are created? +How are such complex devices created? -Which principles we could borrow to make our development same-level reliable and scalable? Or, at least, close to it. +Which principles could we borrow to make our development same-level reliable and scalable? Or, at least, close to it? ## Component architecture diff --git a/8-web-components/2-custom-elements/article.md b/8-web-components/2-custom-elements/article.md index 13af80b8c..a84ed1192 100644 --- a/8-web-components/2-custom-elements/article.md +++ b/8-web-components/2-custom-elements/article.md @@ -365,7 +365,7 @@ Our new button extends the built-in one. So it keeps the same styles and standar ## References - HTML Living Standard: . -- Compatiblity: . +- Compatiblity: . ## Summary @@ -397,4 +397,4 @@ Custom elements can be of two types: /*