diff --git a/1-js/05-data-types/12-json/1-serialize-object/task.md b/1-js/05-data-types/12-json/1-serialize-object/task.md index 53343e4c3..583dc58fd 100644 --- a/1-js/05-data-types/12-json/1-serialize-object/task.md +++ b/1-js/05-data-types/12-json/1-serialize-object/task.md @@ -1,10 +1,10 @@ -importance: 5 +درجة الأهمية: 5 --- -# Turn the object into JSON and back +# تحويل الكائن لجيسون وإرجاعه مرة أخرى -Turn the `user` into JSON and then read it back into another variable. +قم بتحويل الكائن `user` إلى JSON ثم قم بإضافته ككائن إلى متغير آخر. ```js let user = { diff --git a/1-js/05-data-types/12-json/2-serialize-event-circular/solution.md b/1-js/05-data-types/12-json/2-serialize-event-circular/solution.md index 7a3a533b0..b2780a375 100644 --- a/1-js/05-data-types/12-json/2-serialize-event-circular/solution.md +++ b/1-js/05-data-types/12-json/2-serialize-event-circular/solution.md @@ -26,5 +26,5 @@ alert( JSON.stringify(meetup, function replacer(key, value) { */ ``` -Here we also need to test `key==""` to exclude the first call where it is normal that `value` is `meetup`. +نحتاج أن نفحص أيضًا `key==""` لاستثناء أول استدعاء لأن قيمته الطبيعية هي الكائن `meetup`. diff --git a/1-js/05-data-types/12-json/2-serialize-event-circular/task.md b/1-js/05-data-types/12-json/2-serialize-event-circular/task.md index 3755a24aa..8fea33049 100644 --- a/1-js/05-data-types/12-json/2-serialize-event-circular/task.md +++ b/1-js/05-data-types/12-json/2-serialize-event-circular/task.md @@ -1,14 +1,14 @@ -importance: 5 +درجة الأهمية: 5 --- -# Exclude backreferences +# استثناء المراجع لكائنات أخرى -In simple cases of circular references, we can exclude an offending property from serialization by its name. +فى بعض الحالات البسيطة من المرجعية الثنائية (circular references)، يمكننا أن نستبعد الخاصية الغير مرغوبة من التحويل عن طريق اسمها. -But sometimes we can't just use the name, as it may be used both in circular references and normal properties. So we can check the property by its value. +ولكن فى بعض الأوقات لا يمكننا أن نستخدم الإسم فقط، حيث أنها يمكن أن تكون مستخدمه فى مرجعية ثنائية وفى خاصية عادية، ولذلك يمكننا أن نختبر الخاصية عن طريق اسمها. -Write `replacer` function to stringify everything, but remove properties that reference `meetup`: +قم بإنشاء الدالة `replacer` لتحويل كل شيئ إلى نص ولكن تقوم بحذف الخصائص التى تحتوى على مرجع للكائن `meetup`: ```js run let room = { @@ -28,10 +28,10 @@ meetup.self = meetup; */!* alert( JSON.stringify(meetup, function replacer(key, value) { - /* your code */ + /* الحل الخاص بك */ })); -/* result should be: +/* يجب أن تكون النتيجة كالآتي: { "title":"Conference", "occupiedBy":[{"name":"John"},{"name":"Alice"}], diff --git a/1-js/05-data-types/12-json/article.md b/1-js/05-data-types/12-json/article.md index a5f2974af..7d632f12c 100644 --- a/1-js/05-data-types/12-json/article.md +++ b/1-js/05-data-types/12-json/article.md @@ -1,10 +1,10 @@ -# JSON methods, toJSON +# الكائن json وكيفية استخدامه -Let's say we have a complex object, and we'd like to convert it into a string, to send it over a network, or just to output it for logging purposes. +دعنا نقول أن لدينا كائن معقد ونريد تحويله إلى نص (string) لإرساله عن طريق شبكة الإنترنت أو أن نطبعه فقط. -Naturally, such a string should include all important properties. +من المفترض أن يحتوى النص هذا على كل الخصائص المهمه للكائن. -We could implement the conversion like this: +يمكننا إجراء التحويل كالآتى: ```js run let user = { @@ -21,20 +21,24 @@ let user = { alert(user); // {name: "John", age: 30} ``` -...But in the process of development, new properties are added, old properties are renamed and removed. Updating such `toString` every time can become a pain. We could try to loop over properties in it, but what if the object is complex and has nested objects in properties? We'd need to implement their conversion as well. +...ولكن عند التطبيق العملى يتم إضافة خصائص جديده أو إعادة تسمية خصائص قديمه أو إزالتها. فتحديث النص عن طريق الدالة `toString` كل مرة سيكون صعبًا. يمكننا أن نقوم بالتكرار على كل الخصائص فى هذا الكائن ولكن ماذا إذا كان هذا الكائن معقدًا ويحتوى على كائنات أخرى بداخله؟ يجب أن نقوم بتحويلهم أيضًا. -Luckily, there's no need to write the code to handle all this. The task has been solved already. +لحسن الحظ، ليس هناك حاجه لكتابة كود يقوم بالتعامل مع ذلك. فقد تم حل هذه المشكله بالفعل. ## JSON.stringify -The [JSON](http://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) is a general format to represent values and objects. It is described as in [RFC 4627](http://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. +إن الكائن +[JSON](http://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) +هو شكل عام لعرض الكائنات والقيم. وقد تم وصفه كما فى ال [RFC 4627](http://tools.ietf.org/html/rfc4627). +لقد صُنع فى البداية من أجل الجافاسكريبت، ولكن هناك لغات ومكتبات أخرى للتعامل معه أيضًا. ولذلك من السهل استخدام الكائن JSON لتبادل البيانات عندما تكون الواجهة بالجافاسكريبت والسيرفر / الباك اند بلغة مثل Ruby/PHP/Java أو أيًا كان. -JavaScript provides methods: +تحتوى الجافاسكريبت على دوال للتعامل مع الكائن JSON: -- `JSON.stringify` to convert objects into JSON. -- `JSON.parse` to convert JSON back into an object. +- `JSON.stringify` لتحويل الكائن إلى جيسون. +- `JSON.parse` لتحويل الجيسون مرة أخرى إلى كائن. + +على سبيل المثال، هنا قمنا بتحويل الكائن `student` باستخدام `JSON.stringify`: -For instance, here we `JSON.stringify` a student: ```js run let student = { name: 'John', @@ -48,7 +52,7 @@ let student = { let json = JSON.stringify(student); */!* -alert(typeof json); // we've got a string! +alert(typeof json); // لدينا نص! alert(json); *!* @@ -64,67 +68,67 @@ alert(json); */!* ``` -The method `JSON.stringify(student)` takes the object and converts it into a string. - -The resulting `json` string is called a *JSON-encoded* or *serialized* or *stringified* or *marshalled* object. We are ready to send it over the wire or put into a plain data store. +تستقبل الدالة `JSON.stringify(student)` كائنًا وتحوّله إلى نص. +والجيسون الناتج عن ذلك يسمى _JSON-encoded object_ أو _serialized object_ أو _stringified object_ أو _marshalled object_. والآن يمكننا أن نرسله عن طريق الشبكة أو نضعه فى مكان ما لتخزين البيانات. -Please note that a JSON-encoded object has several important differences from the object literal: +لاحظ أن الكائن المحوَّل يختلف عن الكائن العادى فى عدة نقاط: -- Strings use double quotes. No single quotes or backticks in JSON. So `'John'` becomes `"John"`. -- Object property names are double-quoted also. That's obligatory. So `age:30` becomes `"age":30`. +- النصوص تستخدم العلامات النصيه `""` ولا توجد العلامه المفرده `''` أو هذه ` `. فإن `'John'` يتحول إلى `"John"`. +- أسماء الخواص فى الكائن تكون محاطه بالعلامه النصيه الثنائية أيضًا `""` . ولذلك فإن `age:30` يتحول إلى `"age":30`. -`JSON.stringify` can be applied to primitives as well. +ويمكن استخادم `JSON.stringify` مع القيم المفردة أيضًا وليس مع الكائنات فقط. -JSON supports following data types: +يدعم الجيسون أنواع البيانات الآتية: -- Objects `{ ... }` -- Arrays `[ ... ]` -- Primitives: - - strings, - - numbers, - - boolean values `true/false`, - - `null`. +- الكائنات `{ ... }` +- القوائم (arrays) `[ ... ]` +- القيم المفردة (Primitives): + - النصوص (strings), + - الأرقام, + - القيم المنطقيه (booleans) `true/false`, + - `null`. -For instance: +على سبيل المثال: ```js run -// a number in JSON is just a number -alert( JSON.stringify(1) ) // 1 +// الرقم المحوِّل إلى جيسون هو رقم أيضًا +alert(JSON.stringify(1)); // 1 -// a string in JSON is still a string, but double-quoted -alert( JSON.stringify('test') ) // "test" +// النص المحوَّل إلى جيسون هو نصٌ أيضًا ولكن محاط بعلامة التنصيص الثنائية "" +alert(JSON.stringify("test")); // "test" -alert( JSON.stringify(true) ); // true +alert(JSON.stringify(true)); // true -alert( JSON.stringify([1, 2, 3]) ); // [1,2,3] +alert(JSON.stringify([1, 2, 3])); // [1,2,3] ``` -JSON is data-only language-independent specification, so some JavaScript-specific object properties are skipped by `JSON.stringify`. +الجيسون يستخدم خصيصًا لعرض البيانات فقط ولذلك فإن بعض خصائص الكائنات فى الجافاسكريبت يتم تخطيها عند استخدام `JSON.stringify`. -Namely: +وهى: -- Function properties (methods). -- Symbolic properties. -- Properties that store `undefined`. +- الخصائص التى هي عباره عن دوال (Functions). +- الخصائص من نوع الرمز (Symbol). +- الخصائص التى قيمتها `undefined`. ```js run let user = { - sayHi() { // ignored + sayHi() { + // يتم تجاهلها alert("Hello"); }, - [Symbol("id")]: 123, // ignored - something: undefined // ignored + [Symbol("id")]: 123, // يتم تجاهلها + something: undefined, // يتم تجاهلها }; -alert( JSON.stringify(user) ); // {} (empty object) +alert(JSON.stringify(user)); // {} (كائن فارغ) ``` -Usually that's fine. If that's not what we want, then soon we'll see how to customize the process. +عادةً ما يكون هذا جيدً. وإذا لم يكن هذا مانريده، إذا سنرى قريبًا كيف يمكن أن نعدل هذه العملية لصالحنا. -The great thing is that nested objects are supported and converted automatically. +الشئ الجيد هنا أن الكائنات الموجوده بداخل أخرى مدعومة ويتم تحويلها تلقائيًا. -For instance: +على سبيل المثال: ```js run let meetup = { @@ -138,7 +142,7 @@ let meetup = { }; alert( JSON.stringify(meetup) ); -/* The whole structure is stringified: +/* الشكل بالكامل تم تحويله إلى نص: { "title":"Conference", "room":{"number":23,"participants":["john","ann"]}, @@ -146,9 +150,9 @@ alert( JSON.stringify(meetup) ); */ ``` -The important limitation: there must be no circular references. +هناك شئ مهم يمنع هذا: وهو أنه لابد أن لا يحتوى الكائن على كائن هو أيضًا يحتوى على الكائن الأول. -For instance: +على سبيل المثال: ```js run let room = { @@ -160,41 +164,40 @@ let meetup = { participants: ["john", "ann"] }; -meetup.place = room; // meetup references room -room.occupiedBy = meetup; // room references meetup +meetup.place = room; // meetup يحتوى على room +room.occupiedBy = meetup; // room يحتوى على meetup *!* -JSON.stringify(meetup); // Error: Converting circular structure to JSON +JSON.stringify(meetup); // خطأ: Converting circular structure to JSON */!* ``` -Here, the conversion fails, because of circular reference: `room.occupiedBy` references `meetup`, and `meetup.place` references `room`: +فى هذه الحالة يفضل التحويل وهذا بسبب احتواء كل كائن على آخر (circular reference): `room.occupiedBy` تحتوى على `meetup`، و `meetup.place` تحتوى على `room` ![](json-meetup.svg) - ## Excluding and transforming: replacer -The full syntax of `JSON.stringify` is: +كيفية استخدام `JSON.stringify` هو كالآتى: ```js let json = JSON.stringify(value[, replacer, space]) ``` value -: A value to encode. +: هي القيمة التى ستحوَّل. replacer -: Array of properties to encode or a mapping function `function(key, value)`. +: قائمة (array) من الخصائص لتحويلها أو دالة لتنفيذعا على كل خاصية. space -: Amount of space to use for formatting +: كم المسافه التى يجب استخدامها من أجل التنظيم. -Most of the time, `JSON.stringify` is used with the first argument only. But if we need to fine-tune the replacement process, like to filter out circular references, we can use the second argument of `JSON.stringify`. +فى أغلب الوقت تُستخدم `JSON.stringify` مع أول متغير فقط، ولكن إذا أردنا أن نتحكم فى كيفية عمل هذه الدالة، مثل التحقق من وجود كائن بداخل آخر يحتوى على هذا الكائن والعكس (circular references)، إذًا يمكننا استخدام المتغير الثاني للدالة `JSON.stringify`. -If we pass an array of properties to it, only these properties will be encoded. +إذا مررنا قائمة من الخصائص للدالة، فسيتم تحويل قيم هذه الخصائص فقط. -For instance: +على سبيل المثال: ```js run let room = { @@ -204,18 +207,18 @@ let room = { let meetup = { title: "Conference", participants: [{name: "John"}, {name: "Alice"}], - place: room // meetup references room + place: room // meetup يحتوى علي room }; -room.occupiedBy = meetup; // room references meetup +room.occupiedBy = meetup; // room يحتوى علي meetup alert( JSON.stringify(meetup, *!*['title', 'participants']*/!*) ); // {"title":"Conference","participants":[{},{}]} ``` -Here we are probably too strict. The property list is applied to the whole object structure. So the objects in `participants` are empty, because `name` is not in the list. +هنا نحن محَدَّدين. حيث أن القائمة المُمرَّرة هي للكائن بأكمله ولذلك فإن الكائنات بداخل `participants` فارغة لأن الخاصية `name` ليست فى القائمة. -Let's include in the list every property except `room.occupiedBy` that would cause the circular reference: +هيا نحِّل كل الخصائص عدا `room.occupiedBy` والتى يمكن أن تسبب المرجع الدائرى (circular reference): ```js run let room = { @@ -225,10 +228,10 @@ let room = { let meetup = { title: "Conference", participants: [{name: "John"}, {name: "Alice"}], - place: room // meetup references room + place: room // meetup يحتوى علي room }; -room.occupiedBy = meetup; // room references meetup +room.occupiedBy = meetup; // room يحتوى علي meetup alert( JSON.stringify(meetup, *!*['title', 'participants', 'place', 'name', 'number']*/!*) ); /* @@ -240,31 +243,33 @@ alert( JSON.stringify(meetup, *!*['title', 'participants', 'place', 'name', 'num */ ``` -Now everything except `occupiedBy` is serialized. But the list of properties is quite long. +كل شيئ الآن تم تحويله عدا `occupiedBy`، ولكن القائمة طويلة قليلًا. -Fortunately, we can use a function instead of an array as the `replacer`. +لحسن الحظ، يمكننا أن نستعمل دالة بدلًا من القائمة وتسمّي `replacer`. -The function will be called for every `(key, value)` pair and should return the "replaced" value, which will be used instead of the original one. Or `undefined` if the value is to be skipped. +هذه الدالة سيتم استدعاؤها لكل خاصية وقيمتها وستقوم بإرجاع القيمة "البديلة" والتى ستستخدم بلًا من الأصلية أو ستقوم بإرجاع `undefined` للقيمة التى سيتم تخطيها. -In our case, we can return `value` "as is" for everything except `occupiedBy`. To ignore `occupiedBy`, the code below returns `undefined`: +فى حالتنا هذه، يمكننا أن نقوم بإرجاع القيمة "كما هي" لكل شئ عدا `occupiedBy`. ولتجاهل `occupiedBy`، سيقوم الكود التالى بإرجاع `undefinrd`: ```js run let room = { - number: 23 + number: 23, }; let meetup = { title: "Conference", - participants: [{name: "John"}, {name: "Alice"}], - place: room // meetup references room + participants: [{ name: "John" }, { name: "Alice" }], + place: room, // meetup يحتوى علي room }; -room.occupiedBy = meetup; // room references meetup +room.occupiedBy = meetup; // room يحتوى علي meetup -alert( JSON.stringify(meetup, function replacer(key, value) { - alert(`${key}: ${value}`); - return (key == 'occupiedBy') ? undefined : value; -})); +alert( + JSON.stringify(meetup, function replacer(key, value) { + alert(`${key}: ${value}`); + return key == "occupiedBy" ? undefined : value; + }) +); /* key:value pairs that come to replacer: : [object Object] @@ -279,20 +284,23 @@ number: 23 */ ``` -Please note that `replacer` function gets every key/value pair including nested objects and array items. It is applied recursively. The value of `this` inside `replacer` is the object that contains the current property. - -The first call is special. It is made using a special "wrapper object": `{"": meetup}`. In other words, the first `(key, value)` pair has an empty key, and the value is the target object as a whole. That's why the first line is `":[object Object]"` in the example above. +لاحظ أن الدالة `replacer` تستقبل كل خاصية وقيمتها بما فيها من كائنات مُضَمّنة وكذلك عناصر القائمة. حيث أنه سيتم تنفيذه بشكل متكرر. وقيمة +`this` + داخل الدالة + `replacer` + هو كائن يحتوى على الخاصية الحالية. -The idea is to provide as much power for `replacer` as possible: it has a chance to analyze and replace/skip even the whole object if necessary. +الإستدعاء الأول للدالة خاص. لأنه يتم بكائن خاص يحتويه `{"": meetup}`. بصيغة أخرى يمكن أن نقول أن أول خاصية بقيمتها تحتوى على خاصية فارغة والقيمة هي الكائن المستهدف تحويله بأكمله.لهذا قيمة أول سطر هي `":[object Object]"` قى المثال أعلاه. +الفكرة هي أن تعطي صلاحيات للدالة `replacer` على قدر المستطاع: حيث أن لديها الفرصه للتحليل وكذلك الإستبدال أو التخطي حتي للكائن بأكمله إذا كان هذا ضروريًا. -## Formatting: space +## التنظيم: المسافات -The third argument of `JSON.stringify(value, replacer, space)` is the number of spaces to use for pretty formatting. +المتغير الثالث للدالة `JSON.stringify(value, replacer, space)` هو عدد المسافات لاستخدامها فى التنظيم. -Previously, all stringified objects had no indents and extra spaces. That's fine if we want to send an object over a network. The `space` argument is used exclusively for a nice output. +سابقًا، كانت كل الكائنات المُحوَّلة إلى نصوص لا تحتوى على أى تنظيم. وهذا كافى إذا كنا نريد أن نرسل الكائن عبر الشبكة. والمتغير `space` يستخدم تحديدًا لطباعة الكائن بشكل منظم. -Here `space = 2` tells JavaScript to show nested objects on multiple lines, with indentation of 2 spaces inside an object: +هنا `space = 2` تُخبر الجافاسكريبت أن تعرض الكائنات الداخلية على سطور عدة مع مسافات فارغة تقدر بمسافتين داخل الكائن: ```js run let user = { @@ -300,8 +308,8 @@ let user = { age: 25, roles: { isAdmin: false, - isEditor: true - } + isEditor: true, + }, }; alert(JSON.stringify(user, null, 2)); @@ -316,7 +324,7 @@ alert(JSON.stringify(user, null, 2)); } */ -/* for JSON.stringify(user, null, 4) the result would be more indented: +/* عند استخدام JSON.stringify(user, null, 4) فإن النتيجة تحتوى على مزيد من المسافات الفارغة: { "name": "John", "age": 25, @@ -327,14 +335,13 @@ alert(JSON.stringify(user, null, 2)); } */ ``` +يُستخدم المتغير `space` لوحده من أجل الطباعة بشكل منظم. -The `space` parameter is used solely for logging and nice-output purposes. +## دالة "toJson" مخصصه -## Custom "toJSON" +كما أنه توجد دالة `toString` للتحويل إلى نص، يمكن لأى كائن أن يحتوى علي دالة `toJSON` للتحويل إلى جيسون. وتستدعيها الدالة `JSON.stringify` تلقائيًا إذا كانت موجودة. -Like `toString` for string conversion, an object may provide method `toJSON` for to-JSON conversion. `JSON.stringify` automatically calls it if available. - -For instance: +على سبيل المثال: ```js run let room = { @@ -359,9 +366,9 @@ alert( JSON.stringify(meetup) ); */ ``` -Here we can see that `date` `(1)` became a string. That's because all dates have a built-in `toJSON` method which returns such kind of string. +يمكننا أن نري هنا أن `date` `(1)` أصبحت نصًا وهذا لأن كل التواريخ تحتوى على دالة `toJSON` موجودة بالفعل والتى تقوم بإرجاع هذا النوع من النصوص. -Now let's add a custom `toJSON` for our object `room` `(2)`: +هيا نقوم بإنشاء دالة `toJSON` مخصصة للكائن `room` `(2)`: ```js run let room = { @@ -393,48 +400,49 @@ alert( JSON.stringify(meetup) ); */ ``` -As we can see, `toJSON` is used both for the direct call `JSON.stringify(room)` and when `room` is nested in another encoded object. - +كما نرى هنا فإن الدالة `toJSON` يمكن استخدامها عن طريق استدعاء `JSON.stringify(room)` مباشرةً أو عندما يكون الكائن `room` بداخل كائن آخر. ## JSON.parse -To decode a JSON-string, we need another method named [JSON.parse](mdn:js/JSON/parse). +لإرجاع الكائن المحوَّل إلى جيسون مرةً أخرى سنحتاج إلى دالة أخرى تسمى [JSON.parse](mdn:js/JSON/parse). + +كيفية استخدامها: -The syntax: ```js let value = JSON.parse(str, [reviver]); ``` str -: JSON-string to parse. +: النص المحوَّل لجيسون لإرجاعه. reviver -: Optional function(key,value) that will be called for each `(key, value)` pair and can transform the value. +: دالة اختيارية تستقبل الخاصية وقيمتها والتى سيتم استدعاؤها لكل خاصية وقيمتها ويمكنها تحويل القمة. -For instance: +على سبيل المثال: ```js run -// stringified array +// قائمة محوَّلة let numbers = "[0, 1, 2, 3]"; numbers = JSON.parse(numbers); -alert( numbers[1] ); // 1 +alert(numbers[1]); // 1 ``` -Or for nested objects: +أو لكائنات مُضَمَّنة (nested objects): ```js run -let userData = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }'; +let userData = + '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }'; let user = JSON.parse(userData); -alert( user.friends[1] ); // 1 +alert(user.friends[1]); // 1 ``` -The JSON may be as complex as necessary, objects and arrays can include other objects and arrays. But they must obey the same JSON format. +يمكن أن يكون الجيسون معقدًا حيث يمكن أن تحتوى الكائنات علي كائنات أو قوائم أخرى، ولكنهم يجب أن يتبعو نفس تنظيم الكائن جيسون. -Here are typical mistakes in hand-written JSON (sometimes we have to write it for debugging purposes): +هنا بعض الأخطاء لجيسون تمت كتابته باليد (أحيانًا نحتاج لكتابته لأغراض البحث عن الأخطاء): ```js let json = `{ @@ -446,26 +454,26 @@ let json = `{ }`; ``` -Besides, JSON does not support comments. Adding a comment to JSON makes it invalid. +إلى جانب ذلك، لا يدعم الجيسون التعليقات (comments)، حيث أن إضافة تعليق للكائن جيسون سيجعله غير صالح. -There's another format named [JSON5](http://json5.org/), which allows unquoted keys, comments etc. But this is a standalone library, not in the specification of the language. +هناك بُنية أخري تسمي [JSON5](http://json5.org/) والتى تسمح بالخصائص الغير محاطة بعلامة التنصيص الثنائية والتعليقات وغيرها، ولكن هذه مكتبة أخرى وليست فى مواصفات اللغة. -The regular JSON is that strict not because its developers are lazy, but to allow easy, reliable and very fast implementations of the parsing algorithm. +إن الكائن JSON هو محدَّد وذلك ليس لأن المطوِّرين كسولين ولكن للسماح بسرعة وسهولة خطوات التحويل. -## Using reviver +## استخدام الدالة reviver -Imagine, we got a stringified `meetup` object from the server. +تخيل أن لدينا كائنًا محوَّل إلى نص يسمي `meetup` قادم من السيرفر\الخادم. -It looks like this: +سيكون بهذا الشكل: ```js // title: (meetup title), date: (meetup date) let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}'; ``` -...And now we need to *deserialize* it, to turn back into JavaScript object. +...والآن سنحتاج إلى تحويله إلى كائن عادى. -Let's do it by calling `JSON.parse`: +هيا نفعل ذلك باستدعاء `JSON.parse`: ```js run let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}'; @@ -473,15 +481,16 @@ let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}'; let meetup = JSON.parse(str); *!* -alert( meetup.date.getDate() ); // Error! +alert( meetup.date.getDate() ); // خطأ! */!* ``` -Whoops! An error! +يا إلهى! خطأ! -The value of `meetup.date` is a string, not a `Date` object. How could `JSON.parse` know that it should transform that string into a `Date`? +إن قيمة `meetup.date` هي نص وليست تاريخ، كيف يمكن للدالة `JSON.parse` أن تعلم أنها يجب أن تحول هذا النص إلى تاريخ؟ -Let's pass to `JSON.parse` the reviving function as the second argument, that returns all values "as is", but `date` will become a `Date`: +هيا نمرر الدالة `reviver` إلى الدالة `JSON.parse` كمتغير ثانٍ والتى تقوم بإرجاع كل شيئ كما هو عدا +`date` ستتحول إلى `تاريخ`: ```js run let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}'; @@ -493,10 +502,10 @@ let meetup = JSON.parse(str, function(key, value) { }); */!* -alert( meetup.date.getDate() ); // now works! +alert( meetup.date.getDate() ); // لا يوجد خطأ الآن! ``` -By the way, that works for nested objects as well: +وهي تعمل مع الكائنات المُضَمَّنة أيضًا: ```js run let schedule = `{ @@ -512,16 +521,14 @@ schedule = JSON.parse(schedule, function(key, value) { }); *!* -alert( schedule.meetups[1].date.getDate() ); // works! +alert( schedule.meetups[1].date.getDate() ); // تعمل جيدًا! */!* ``` +## الملخص - -## Summary - -- JSON is a data format that has its own independent standard and libraries for most programming languages. -- JSON supports plain objects, arrays, strings, numbers, booleans, and `null`. -- JavaScript provides methods [JSON.stringify](mdn:js/JSON/stringify) to serialize into JSON and [JSON.parse](mdn:js/JSON/parse) to read from JSON. -- Both methods support transformer functions for smart reading/writing. -- If an object has `toJSON`, then it is called by `JSON.stringify`. +- الكائن JSON هو شكل من أشكال البيانات والتى تحتوى على قواعدها ومكتباتها الخاصة لأغلب لغات البرمجة. +- الكائن JSON يدعم الكائنات العادية والقوائم والأرقام والقيم المنطقية والنصوص و القيمة `null`. +- توجد دوال فى الجافاسكريبت [JSON.stringify](mdn:js/JSON/stringify) لتحويل الكائن إلى جيسون و أيضًا [JSON.parse](mdn:js/JSON/parse) لتحويله مرة أخري إلى كائن. +- كلا الدالتين تدعم الدوال التى تحوِّل من وإلى بشكل ذكي. +- إذا كان الكائن يحتوى على الدالة `toJSON`، إذا سيتم استدعاؤها عن طريق الدالة `JSON.stringify`. diff --git a/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md index ebd2f44e9..cd10a28cb 100644 --- a/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md +++ b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md @@ -9,5 +9,5 @@ function f() { alert("Hello!"); } -f.defer(1000); // shows "Hello!" after 1 sec +f.defer(1000); // تعرض كلمة "Hello!" بعد 1 ثانية ``` diff --git a/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md index d3b3a51c2..d382a0ab4 100644 --- a/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md +++ b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md @@ -1,17 +1,17 @@ -importance: 5 +درجة الأهمية: 5 --- -# Add method "f.defer(ms)" to functions +# إضافة الدالة "f.defer(ms)" للدوال -Add to the prototype of all functions the method `defer(ms)`, that runs the function after `ms` milliseconds. +أضف إلى نموذج الدوال الدالة `defer(ms)` والتى تقوم بتشغيل الدالة بعد `ms` مللى ثانية. -After you do it, such code should work: +بعد أن تفعلها، سيعمل الكود التالى: ```js function f() { alert("Hello!"); } -f.defer(1000); // shows "Hello!" after 1 second +f.defer(1000); // تعرض كلمة "Hello!" بعد 1 ثانية ``` diff --git a/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md index 99c358c9b..bd8a8bedf 100644 --- a/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md +++ b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md @@ -8,17 +8,17 @@ Function.prototype.defer = function(ms) { } }; -// check it +// اختبر function f(a, b) { alert( a + b ); } -f.defer(1000)(1, 2); // shows 3 after 1 sec +f.defer(1000)(1, 2); // تعرض 3 بعد ثانية واحدة ``` -Please note: we use `this` in `f.apply` to make our decoration work for object methods. +لاحظ: استخدمنا `this` فى `f.apply` لتعمل مع دوال الكائنات. -So if the wrapper function is called as an object method, then `this` is passed to the original method `f`. +ولذلك إذا تم استدعاء دالة كدالة كائن (method) فإن `this` سيتم تمرريرها إلى الدالة الأصلية `f`. ```js run Function.prototype.defer = function(ms) { diff --git a/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md index 4d3823bb8..d0628f7a4 100644 --- a/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md +++ b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md @@ -1,19 +1,19 @@ -importance: 4 +درجة الأهمية: 4 --- -# Add the decorating "defer()" to functions +# إضافة الدالة المعدلة للدوال -Add to the prototype of all functions the method `defer(ms)`, that returns a wrapper, delaying the call by `ms` milliseconds. +أضف إلى النموذج الخاص بالدوال الدالة `defer(ms)`، والتى تقوم بإرجاع حاوى (wrapper) وتؤخر التنفيذ بعد `ms` مللى ثانية. -Here's an example of how it should work: +هاك مثال لكيفية استخدامها: ```js function f(a, b) { alert( a + b ); } -f.defer(1000)(1, 2); // shows 3 after 1 second +f.defer(1000)(1, 2); // تعرض 3 بعد ثانية واحدة ``` -Please note that the arguments should be passed to the original function. +لاحظ أن المتغيرات يجب أن تُمرر إلى الدالة الأصلية. diff --git a/1-js/08-prototypes/03-native-prototypes/article.md b/1-js/08-prototypes/03-native-prototypes/article.md index 378936c9a..8179cc340 100644 --- a/1-js/08-prototypes/03-native-prototypes/article.md +++ b/1-js/08-prototypes/03-native-prototypes/article.md @@ -1,33 +1,33 @@ -# Native prototypes +# النماذج البدائية Native prototypes -The `"prototype"` property is widely used by the core of JavaScript itself. All built-in constructor functions use it. +إن الخاصية `"prototype"` مستخدمة بشكل واسع من الجافاسكريبت نفسها، حيث أن كل الدوال البانية (constructor functions) تستخدمها. -First we'll see at the details, and then how to use it for adding new capabilities to built-in objects. +أولًا سنرى التفاصيل، ثم نتعلم كيف نستخدمها لإضافة إمكانيات جديدة للكائنات الموجودة بالفعل (built-in objects). ## Object.prototype -Let's say we output an empty object: +دعنا نقول أننا سنطبع كائنًا فارغًا: ```js run let obj = {}; -alert( obj ); // "[object Object]" ? +alert(obj); // "[object Object]" ? ``` -Where's the code that generates the string `"[object Object]"`? That's a built-in `toString` method, but where is it? The `obj` is empty! +أين الكود المسؤول عن التحويل إلى النص `"[object Object]"`؟ إنها الدالة `toString` الموجودة بالفعل ، ولكن أين هى؟ الكائن `obj` فارغ ! -...But the short notation `obj = {}` is the same as `obj = new Object()`, where `Object` is a built-in object constructor function, with its own `prototype` referencing a huge object with `toString` and other methods. +...ولكن الصيغة `obj = {}` هي نفسها هذه الصيغة `obj = new Object()`، حيث أن `Object` هو دالة بانية للكائنات موجودة بالفعل والتى تحتوى على الخاصية `prototype` التى تحتوى على مرجع لكائن ضخم يحتوى على الدالة `toString` ودوال أخري. -Here's what's going on: +هاهنا ما يحدث: ![](object-prototype.svg) -When `new Object()` is called (or a literal object `{...}` is created), the `[[Prototype]]` of it is set to `Object.prototype` according to the rule that we discussed in the previous chapter: +عندما يتم استدعاء `new Object()` (أو إنشاء الكائن العادى `{...}`)، ستكون قيمة `[[Prototype]]` للكائن الناتج تشير إلى `Object.prototype` طبقًا للقاعدة التى ناقشناها فى الفصل السابق: ![](object-prototype-1.svg) -So then when `obj.toString()` is called the method is taken from `Object.prototype`. +لذلك عندما يتم استدعاء `obj.toString()` فإن الدالة مأخوذة من `Object.prototype`. -We can check it like this: +يمكننا أن نختبر ذلك هكذا: ```js run let obj = {}; @@ -36,80 +36,79 @@ alert(obj.__proto__ === Object.prototype); // true // obj.toString === obj.__proto__.toString == Object.prototype.toString ``` -Please note that there is no more `[[Prototype]]` in the chain above `Object.prototype`: +لاحظ أنه لا يوجد المزيد من `[[Prototype]]` فى السلسة فوق `Object.prototype`: ```js run alert(Object.prototype.__proto__); // null ``` -## Other built-in prototypes +## نماذج أخرى موجودة بالفعل (built-in prototypes) -Other built-in objects such as `Array`, `Date`, `Function` and others also keep methods in prototypes. +إن الكائنات الأخرى الموجوده بالفعل مثل `Array`, `Date`, `Function` وغيرهم يحتفظون بدوال فى النماذج (prototypes). -For instance, when we create an array `[1, 2, 3]`, the default `new Array()` constructor is used internally. So `Array.prototype` becomes its prototype and provides methods. That's very memory-efficient. +علي سبيل المثال، عندما نقوم بإنشاء قائمة `[1, 2, 3]`، فإن الدالة البانية الموجودة بالفعل `new Array()` يتم استخدامها داخليًا. ولذلك تصبح `Array.prototype` النموذج الخاص بها وتمنحها دوال خاصة وهذا شيئ جيد جدًّا للذاكرة. -By specification, all of the built-in prototypes have `Object.prototype` on the top. That's why some people say that "everything inherits from objects". +كما ذُكر فى المصدر، فإن كل النماذج (prototypes) الموجودة بالفعل لديها `Object.prototype` على القمة فى الأعلى. وهذا مايدفع بعض الأشخاص للقول بأن "كل شيئ يرث من الكائنات". -Here's the overall picture (for 3 built-ins to fit): +هنا الصورة الكاملة: ![](native-prototypes-classes.svg) -Let's check the prototypes manually: +هيا نختبر الخاصية يدويًا: ```js run let arr = [1, 2, 3]; -// it inherits from Array.prototype? -alert( arr.__proto__ === Array.prototype ); // true +// هل ترث من Array.prototype? +alert(arr.__proto__ === Array.prototype); // true -// then from Object.prototype? -alert( arr.__proto__.__proto__ === Object.prototype ); // true +// ثم من Object.prototype? +alert(arr.__proto__.__proto__ === Object.prototype); // true -// and null on the top. -alert( arr.__proto__.__proto__.__proto__ ); // null +// والقيمة null فى الأعلى. +alert(arr.__proto__.__proto__.__proto__); // null ``` -Some methods in prototypes may overlap, for instance, `Array.prototype` has its own `toString` that lists comma-delimited elements: +بعض الدوال فى النماذج يمكن أن تتداخل، فعلى سبيل المثال، تملك `Array.prototype` الدالة `toString` الخاصة بها والتى تقوم بإرجاع نص يحوى عناصر القائمة وبينها الفاصلة: ```js run -let arr = [1, 2, 3] -alert(arr); // 1,2,3 <-- the result of Array.prototype.toString +let arr = [1, 2, 3]; +alert(arr); // 1,2,3 <-- نتيجة Array.prototype.toString ``` -As we've seen before, `Object.prototype` has `toString` as well, but `Array.prototype` is closer in the chain, so the array variant is used. - +كما رأينا سابقًا، تملك `Object.prototype` أيضًا الدالة `toString` ولكن `Array.prototype` هي الأقرب فى السلسلة ولذلك يتم استخدام الدالة الخاصة بالقائمة. ![](native-prototypes-array-tostring.svg) - -In-browser tools like Chrome developer console also show inheritance (`console.dir` may need to be used for built-in objects): +تعرض الأدوات الموجودة فى المتصفح أيضًا الوراثة (يمكن أن يتم استخدام `console.dir` مع بعض الكائنات الموجودة بالفعل ): ![](console_dir_array.png) -Other built-in objects also work the same way. Even functions -- they are objects of a built-in `Function` constructor, and their methods (`call`/`apply` and others) are taken from `Function.prototype`. Functions have their own `toString` too. +تعمل الكائنات الموجودة بالفعل الأخرى بنفس الطريقة. حتى الدوال -- هي عبارة عن كائنات مبنية عن طريق الدالة البانية `Function` والدوال الخاصه بها (مثل `call`/`apply` وغيرها) مأخوذة من `Function.prototype`. وتحتوى الدوال على الدالة `toString`الخاصة بها أيضًا. ```js run function f() {} alert(f.__proto__ == Function.prototype); // true -alert(f.__proto__.__proto__ == Object.prototype); // true, inherit from objects +alert(f.__proto__.__proto__ == Object.prototype); // true, ترث من الكائنات ``` -## Primitives +## القيم المفردة Primitives -The most intricate thing happens with strings, numbers and booleans. +أكثر الأشياء المعقدة تحدث مع النصوص والأرقام والقيم المطلقة. -As we remember, they are not objects. But if we try to access their properties, temporary wrapper objects are created using built-in constructors `String`, `Number` and `Boolean`. They provide the methods and disappear. +كما نتذكر فإنهم ليسو عبارة عن كائنات، ولكن إذا حاولنا أن نصل إلى خصائصهم فسيتم إحاطتها بكائن باستخدام الدوال البانية `String` و `Number` و `Boolean`، حيث يمنحونهم الدوال ثم يختفون. -These objects are created invisibly to us and most engines optimize them out, but the specification describes it exactly this way. Methods of these objects also reside in prototypes, available as `String.prototype`, `Number.prototype` and `Boolean.prototype`. +هذه الكائنات تم إنشاؤها لنا خفيةً وأغلب المحركات تقوم بتحسين ذلك، ولكن يصفها المصدر بهذه الطريقة بالضبط. ودوال هذه الكائنات توجد أيضًا فى النماذج وتكون متاحة كـ `String.prototype` و `Number.prototype` و `Boolean.prototype`. -```warn header="Values `null` and `undefined` have no object wrappers" -Special values `null` and `undefined` stand apart. They have no object wrappers, so methods and properties are not available for them. And there are no corresponding prototypes either. -``` +```warn header="القيم `null` و `undefined` ليس لها كائنات حاوية" +القيم الخاصة `null` و `undefined` تقف بعيدًا عن هذا. حيث أنهم ليس لديهم كائنات حاوية (object wrappers)، ولذلك فإن الدوال والخصائص غير متاحة لهم وليس هناك نماذج لهم. -## Changing native prototypes [#native-prototype-change] +```` -Native prototypes can be modified. For instance, if we add a method to `String.prototype`, it becomes available to all strings: +## التعديل على النماذج البدائية (native prototypes) [#native-prototype-change] + +يمكن تعديل النماذج البدائية. فعلى سبيل المثال، إذا أضفنا دالة إلى `String.prototype` ستصبح متاحة إلى كل النصوص: ```js run String.prototype.show = function() { @@ -117,53 +116,53 @@ String.prototype.show = function() { }; "BOOM!".show(); // BOOM! -``` +```` -During the process of development, we may have ideas for new built-in methods we'd like to have, and we may be tempted to add them to native prototypes. But that is generally a bad idea. +وخلال التطبيق العملى يمكن أن تخطر لنا أفكار لدوال أخرى نريد أن ننشئها ويمكننا إضافتها للنماذج، ولكن هذا يُعد فكرة سيئة بشكل عام. ```warn -Prototypes are global, so it's easy to get a conflict. If two libraries add a method `String.prototype.show`, then one of them will be overwriting the method of the other. +إن النماذج متاحة بشكل عام، ولذلك فإنه من السهل أن يحدث تعارض. فإذا كان هناك مكتبتان أضافتا نفس الدالة `String.prototype.show`، إذن فإن واحدة منهن ستستبدل عمل الأخرى. -So, generally, modifying a native prototype is considered a bad idea. +ولذلك، بشكل عام، فإن تعديل النماذج البدائية يُعد فكرة سيئة. ``` -**In modern programming, there is only one case where modifying native prototypes is approved. That's polyfilling.** +**فى لغات البرمجة الحديثة، توجد حالة واحدة لتعديل النماذج البدائية. وهي تعدد الأشكال polyfilling** -Polyfilling is a term for making a substitute for a method that exists in the JavaScript specification, but is not yet supported by a particular JavaScript engine. +تعدد الأشكال هو مصطلح يعني إنشاء نسخه من دالة موجودة فى مصدر الجافاسكريبت ولكنها غير مدعومة بعد من محرك جافاسكريبت معين. -We may then implement it manually and populate the built-in prototype with it. +يمكننا إذن كتابتها يدويّاً وإضافتها للنموذج. -For instance: +على سبيل المثال: ```js run -if (!String.prototype.repeat) { // if there's no such method - // add it to the prototype +if (!String.prototype.repeat) { + // إذا لم توجد هذه الدالة + // أضفها للنموذج - String.prototype.repeat = function(n) { - // repeat the string n times + String.prototype.repeat = function (n) { + // كرر النص n من المرات - // actually, the code should be a little bit more complex than that + // فى الحقيقة، يجب أن يكون الكود أكثر تعقيدًا بقليل من هذا // (the full algorithm is in the specification) - // but even an imperfect polyfill is often considered good enough + // ولكن حتي تعدد الأشكال الغير كامل غالبًا ما يكون كافيًا return new Array(n + 1).join(this); }; } -alert( "La".repeat(3) ); // LaLaLa +alert("La".repeat(3)); // LaLaLa ``` +## الإستعارة من النماذج -## Borrowing from prototypes - -In the chapter we talked about method borrowing. +فى فصل تحدثنا عن استعارة الدوال. -That's when we take a method from one object and copy it into another. +وهذا يكون عندما نأخذ دالةً ما من كائن وننسخها لكائن آخر. -Some methods of native prototypes are often borrowed. +بعض الدوال غالبًا ما يتم استعارتها من النماذج البدائية. -For instance, if we're making an array-like object, we may want to copy some `Array` methods to it. +على سبيل المثال، إذا كنا ننشئ كائنًا شبيهًا بالقائمة، فإننا يمكن أن نريد أن ننسخ بعذ دوال الكائن `Array` إليه. -E.g. +مثال: ```js run let obj = { @@ -179,18 +178,18 @@ obj.join = Array.prototype.join; alert( obj.join(',') ); // Hello,world! ``` -It works because the internal algorithm of the built-in `join` method only cares about the correct indexes and the `length` property. It doesn't check if the object is indeed an array. Many built-in methods are like that. +هذا يعمل لأن الدالة الموجودة بالفعل `join` تهتم فقط بالأرقام الصحيحه و الخاصية `length`، ولا تفحص إذا كان الكائن هو قائمة بالفعل. والكثير من الدوال تعمل بنفس الطريقة. -Another possibility is to inherit by setting `obj.__proto__` to `Array.prototype`, so all `Array` methods are automatically available in `obj`. +وهناك إمكانية أخرى وهي الوراثة وذلك عن طريق أن نجعل `obj.__proto__` تشير إلى `Array.prototype` ولذلك فإن كل دوال الكائن `Array` ستكون متاحة تلقائيًا للكائن `obj`. -But that's impossible if `obj` already inherits from another object. Remember, we only can inherit from one object at a time. +ولكن هذا مستحيل إذا كان الكائن `obj` يرث بالفعل من كائن آخر، تذكر أننا يمكننا أن نرث من كائن واحد فقط فى المرة الواحدة. -Borrowing methods is flexible, it allows to mix functionalities from different objects if needed. +إن استعارة الدوال مرن ويسمح بمزج الوظائف من كائنات مختلفة إذا أردنا ذلك. -## Summary +## الملخص -- All built-in objects follow the same pattern: - - The methods are stored in the prototype (`Array.prototype`, `Object.prototype`, `Date.prototype`, etc.) - - The object itself stores only the data (array items, object properties, the date) -- Primitives also store methods in prototypes of wrapper objects: `Number.prototype`, `String.prototype` and `Boolean.prototype`. Only `undefined` and `null` do not have wrapper objects -- Built-in prototypes can be modified or populated with new methods. But it's not recommended to change them. The only allowable case is probably when we add-in a new standard, but it's not yet supported by the JavaScript engine +- تتبع كل الكائنات الموجودة بالفعل نفس النمط: + - يتم الإحتفاظ بالدوال فى النموذج (prototype) (`Array.prototype`, `Object.prototype`, `Date.prototype`, إلخ.) + - يتم تخزين البيانات فقط فى الكائن (عناصر قائمة أو خصائص كائن أو تاريخ) +- القيم المفردة (Primitives) تخزن الدوال فى نموذج خاص بالكائن الحاوي (wrapper object): `Number.prototype` و `String.prototype` و `Boolean.prototype`. ولا يوجد كائن حاوى للقيمتين `undefined` و `null`. +- النماذج الموجودة بالفعل (Built-in prototypes) يمكن تعديلها أو إضافة دوال جديدة لها، ولكن هذا غير موصيً به، وإن الحالة الوحيدة المسموح فيها بذلك هي عندما نريد أن نذيف وظيفة جديدة موجودة فى المصدر ولكنها مازالت غير مدعومة من محرك جافاسكريبت معين. diff --git a/1-js/08-prototypes/04-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md index f86f2604e..b05231ad6 100644 --- a/1-js/08-prototypes/04-prototype-methods/article.md +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -1,5 +1,4 @@ - -# Prototype methods, objects without __proto__ +# Prototype methods, objects without **proto** In the first chapter of this section, we mentioned that there are modern methods to setup a prototype. @@ -40,13 +39,13 @@ Object.setPrototypeOf(rabbit, {}); // change the prototype of rabbit to {} ```js run let animal = { - eats: true + eats: true, }; let rabbit = Object.create(animal, { jumps: { - value: true - } + value: true, + }, }); alert(rabbit.jumps); // true @@ -58,7 +57,10 @@ We can use `Object.create` to perform an object cloning more powerful than copyi ```js // fully identical shallow clone of obj -let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); +let clone = Object.create( + Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj) +); ``` This call makes a truly exact copy of `obj`, including all properties: enumerable and non-enumerable, data properties and setters/getters -- everything, and with the right `[[Prototype]]`. @@ -83,7 +85,8 @@ Why was `__proto__` replaced by the functions `getPrototypeOf/setPrototypeOf`? T Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time and don't modify it anymore: `rabbit` inherits from `animal`, and that is not going to change. And JavaScript engines are highly optimized for this. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation as it breaks internal optimizations for object property access operations. So avoid it unless you know what you're doing, or JavaScript speed totally doesn't matter for you. -``` + +```` ## "Very plain" objects [#very-plain] @@ -100,13 +103,13 @@ let key = prompt("What's the key?", "__proto__"); obj[key] = "some value"; alert(obj[key]); // [object Object], not "some value"! -``` +```` Here, if the user types in `__proto__`, the assignment is ignored! That shouldn't surprise us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype. -But we didn't *intend* to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug! +But we didn't _intend_ to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug! Here the consequences are not terrible. But in other cases we may be assigning object values, and then the prototype may indeed be changed. As a result, the execution will go wrong in totally unexpected ways. @@ -163,7 +166,6 @@ alert(obj); // Error (no toString) Note that most object-related methods are `Object.something(...)`, like `Object.keys(obj)` -- they are not in the prototype, so they will keep working on such objects: - ```js run let chineseDictionary = Object.create(null); chineseDictionary.hello = "你好"; @@ -187,7 +189,10 @@ So we can either use `Object.create(null)` to create a "very plain" object witho Also, `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)); +let clone = Object.create( + Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj) +); ``` We also made it clear that `__proto__` is a getter/setter for `[[Prototype]]` and resides in `Object.prototype`, just like other methods.