diff --git a/1-js/2-first-steps/07-types/1-string-quotes/solution.md b/1-js/2-first-steps/07-types/1-string-quotes/solution.md
new file mode 100644
index 00000000..cca7eecd
--- /dev/null
+++ b/1-js/2-first-steps/07-types/1-string-quotes/solution.md
@@ -0,0 +1,15 @@
+
+Backticks embed the expression inside `${...}` into the string.
+
+```js run
+let name = "Ilya";
+
+// the expression is a number 1
+alert( `hello ${1}` ); // Hello, 1
+
+// the expression is a variable, embed it
+alert( `hello ${name}` ); // Hello, Ilya
+
+// the expression is a string "name"
+alert( `hello ${"name"}` ); // Hello, name
+```
\ No newline at end of file
diff --git a/1-js/2-first-steps/07-types/1-string-quotes/task.md b/1-js/2-first-steps/07-types/1-string-quotes/task.md
new file mode 100644
index 00000000..14ea6b4d
--- /dev/null
+++ b/1-js/2-first-steps/07-types/1-string-quotes/task.md
@@ -0,0 +1,17 @@
+importance: 5
+
+---
+
+# String quotes
+
+What is the output of the script?
+
+```js
+let name = "Ilya";
+
+alert( `hello ${1}` ); // ?
+
+alert( `hello ${"name"}` ); // ?
+
+alert( `hello ${name}` ); // ?
+```
\ No newline at end of file
diff --git a/1-js/4-data-structures/4-object/1-hello-object/solution.md b/1-js/2-first-steps/07-types/2-hello-object/solution.md
similarity index 100%
rename from 1-js/4-data-structures/4-object/1-hello-object/solution.md
rename to 1-js/2-first-steps/07-types/2-hello-object/solution.md
diff --git a/1-js/4-data-structures/4-object/1-hello-object/task.md b/1-js/2-first-steps/07-types/2-hello-object/task.md
similarity index 95%
rename from 1-js/4-data-structures/4-object/1-hello-object/task.md
rename to 1-js/2-first-steps/07-types/2-hello-object/task.md
index 8482d3b4..b898112c 100644
--- a/1-js/4-data-structures/4-object/1-hello-object/task.md
+++ b/1-js/2-first-steps/07-types/2-hello-object/task.md
@@ -1,4 +1,4 @@
-importance: 3
+importance: 5
---
diff --git a/1-js/2-first-steps/07-types/3-sum-object/solution.md b/1-js/2-first-steps/07-types/3-sum-object/solution.md
new file mode 100644
index 00000000..b4607014
--- /dev/null
+++ b/1-js/2-first-steps/07-types/3-sum-object/solution.md
@@ -0,0 +1,17 @@
+
+
+```js run
+let salaries = {
+ John: 100,
+ Ann: 160,
+ Pete: 130
+}
+
+let sum = 0;
+for(let key in salaries) {
+ sum += salaries[key];
+}
+
+alert(sum); // 390
+```
+
diff --git a/1-js/2-first-steps/07-types/3-sum-object/task.md b/1-js/2-first-steps/07-types/3-sum-object/task.md
new file mode 100644
index 00000000..0c0e252c
--- /dev/null
+++ b/1-js/2-first-steps/07-types/3-sum-object/task.md
@@ -0,0 +1,19 @@
+importance: 5
+
+---
+
+# Sum object properties
+
+We have an object storing salaries of our team:
+
+```js
+let salaries = {
+ John: 100,
+ Ann: 160,
+ Pete: 130
+}
+```
+
+Write the code to sum all salaries and store in the variable `sum`. Should be `390` in the example above.
+
+Use `for..in` loop to iterate over the object.
diff --git a/1-js/2-first-steps/07-types/4-sum-array/solution.md b/1-js/2-first-steps/07-types/4-sum-array/solution.md
new file mode 100644
index 00000000..00b8cf79
--- /dev/null
+++ b/1-js/2-first-steps/07-types/4-sum-array/solution.md
@@ -0,0 +1,13 @@
+
+
+```js run
+let salaries = [ 100, 160, 130 ];
+
+let sum = 0;
+for(let salary of salaries) {
+ sum += salary;
+}
+
+alert( sum ); // 390
+```
+
diff --git a/1-js/2-first-steps/07-types/4-sum-array/task.md b/1-js/2-first-steps/07-types/4-sum-array/task.md
new file mode 100644
index 00000000..05e8869a
--- /dev/null
+++ b/1-js/2-first-steps/07-types/4-sum-array/task.md
@@ -0,0 +1,13 @@
+importance: 5
+
+---
+
+# Sum the array
+
+Write the code to get the sum of the array using `for..of` loop and store in the variable `sum`:
+
+```js
+let salaries = [ 100, 160, 130 ];
+```
+
+Should be `390` here.
diff --git a/1-js/2-first-steps/07-types/article.md b/1-js/2-first-steps/07-types/article.md
index 177d9510..b7e05eb7 100644
--- a/1-js/2-first-steps/07-types/article.md
+++ b/1-js/2-first-steps/07-types/article.md
@@ -39,7 +39,7 @@ Besides regular numbers there are so-called "special numeric values" which also
`NaN` is sticky. Any further operation on `NaN` would give `NaN`:
```js run
- alert( "not a number" * 2 + 5 - 9 ); // still NaN
+ alert( "not a number" * 2 + 5 - 9 ); // NaN
```
So, in a long mathematical expression if we have `NaN` in one place, it propagates to the whole result.
@@ -66,7 +66,7 @@ In JavaScript, there are 3 types of quotes.
1. Double quotes: `"Hello"`.
2. Single quotes: `'Hello'`.
-3. Backtricks: `Hello`.
+3. Backticks: `Hello`.
Double and single quotes are similar, "simple" quotes.
@@ -178,7 +178,7 @@ let user = {
};
```
-The `user` object can be imagined as an information shelf with two items labelled "name" and "age".
+The `user` object can be imagined as a cabinet with two signed files labelled "name" and "age".

diff --git a/1-js/2-first-steps/08-type-conversions/article.md b/1-js/2-first-steps/08-type-conversions/article.md
index 57525e33..0559a058 100644
--- a/1-js/2-first-steps/08-type-conversions/article.md
+++ b/1-js/2-first-steps/08-type-conversions/article.md
@@ -48,6 +48,23 @@ alert(typeof a); // string
The string conversion is obvious. A `false` becomes `"false"`, `null` becomes `"null"` etc.
+For objects it's a little bit trickier. By default, regular objects are converted like this:
+
+```js run
+alert( {} ); [object Object]
+```
+
+Although, some object subtypes have their own way of formatting, for instance, arrays turn into the comma-delimited list of items:
+
+```js run
+let arr = [1,2,3];
+
+alert( arr ); // 1,2,3
+alert( String(arr) === '1,2,3' ); // true
+```
+
+Later we'll see how to create custom rules for string conversions for our objects.
+
## Numeric conversion
Numeric conversion happens in mathematical functions and expressions automatically.
@@ -123,7 +140,7 @@ alert( Boolean(" ") ); // any non-empty string, even whitespaces are true
There exist three most widely used type conversions: to string, to number and to boolean.
-The conversion to string is usully obvious.
+The conversion to string is usully obvious for primitive values and depends on the object type for objects. For instance, arrays turn into a comma-delimited list of elements.
To number follows the rules:
diff --git a/1-js/2-first-steps/15-while-for/article.md b/1-js/2-first-steps/15-while-for/article.md
index e2544c2f..f6ebf2b0 100644
--- a/1-js/2-first-steps/15-while-for/article.md
+++ b/1-js/2-first-steps/15-while-for/article.md
@@ -378,7 +378,7 @@ for(let key in user) {
}
```
-Note that all "for" constructs allow to declare the looping variable inside the loop, like `let key` here. The name "key" for the variable is not mandatory, we could use any variable name here, usually "key" or "prop" names are used for such iterations.
+Note that all "for" constructs allow to declare the looping variable inside the loop, like `key` here. We could use another variable name here instead of `key`, for instance, "prop" is also widely used for iterations.
## The "for..of" loop
@@ -445,3 +445,5 @@ If we don't want to do anything more on this iteration and would like to forward
`Break/continue` support labels before the loop. A label is the only way for `break/continue` to escape the nesting and go to the outer loop.
+To get an array of object property names, there is a method `Object.keys(obj)`.
+
diff --git a/1-js/2-first-steps/20-function-parameters/article.md b/1-js/2-first-steps/20-function-parameters/article.md
index 88bec5ca..3145bf5f 100644
--- a/1-js/2-first-steps/20-function-parameters/article.md
+++ b/1-js/2-first-steps/20-function-parameters/article.md
@@ -135,12 +135,12 @@ showName("Julius", "Caesar");
showName("Ilya");
```
-The downside is that `arguments` looks like an array, but it's not. It does not support many useful array features. It only exists for backwards compatibility. The rest operator is better.
+The downside is that `arguments` looks like an array, but it's not. It does not support many useful array features. It mainly exists for backwards compatibility, usually the rest operator is better.
````
-## Destructuring in parameters
+## Destructuring parameters
-There are times when a function may have many parameters. Imagine a function that creates a menu. It may have a width, a height, a title, items list and so on.
+There are times when a function may have many parameters, most of which are optional. That's especially true for user interfaces. Imagine a function that creates a menu. It may have a width, a height, a title, items list and so on.
Here's a bad way to write such function:
@@ -158,7 +158,7 @@ Like this?
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])
```
-That's ugly. And becomes unreadable if we have not 4 but 10 parameters.
+That's ugly. And becomes unreadable if we deal with more parameters.
Destructuring comes to the rescue!
@@ -221,7 +221,7 @@ showMenu({});
showMenu();
```
-We can fix this of course, by making an empty object a value by default for the whole destructuring thing:
+We can fix this of course, by making `{}` a value by default for the whole destructuring thing:
```js run
@@ -281,8 +281,7 @@ In short:
Together they help to travel between a list and an array of parameters with ease.
-
-## Итого
+## Summary TODO
Основные улучшения в функциях:
diff --git a/1-js/4-data-structures/2-number/article.md b/1-js/4-data-structures/2-number/article.md
index 6b9f2c1d..5686544c 100644
--- a/1-js/4-data-structures/2-number/article.md
+++ b/1-js/4-data-structures/2-number/article.md
@@ -267,6 +267,16 @@ The reason is the same: loss of precision. There are 64 bits for the number, 52
JavaScript doesn't trigger an error in such case. It does the best to fit the number into the format. Unfortunately, the format is not big enough.
````
+```smart header="Two zeroes"
+Another funny consequence of the internal representation is there are actually two zeroes: `0` and `-0`.
+
+That's because a sign is represented by a single bit, so every number can be positive or negative, including the zero.
+
+In most cases the distinction is unnoticeable, because operators are suited to treat them as the same.
+```
+
+
+
## Tests: isFinite and isNaN
Remember the two special numeric values?
@@ -310,6 +320,17 @@ alert( isFinite(num) );
Please note that an empty or a space-only string is treated as `0` in all numeric functions. If it's not what's needed, then additional checks are required.
+```smart header="Compare with `Object.is`"
+
+There is a special built-in method [Object.is](mdn:js/Object/is) to compare values in "even stricter way" than `===`.
+
+The call `Object.is(value1, value2)` returns the same result as `value1 === value2` with two exceptions:
+
+1. It can compare with `NaN`, e.g. `Object.is(NaN, NaN) === true`.
+2. Values `0` and `-0` are different: `Object.is(0, -0) === false`.
+```
+
+
## parseInt and parseFloat
The numeric conversion using a plus `+` or `Number()` is strict. If a value is not exactly a number, it fails:
diff --git a/1-js/4-data-structures/3-string/article.md b/1-js/4-data-structures/3-string/article.md
index d812af1a..0319657e 100644
--- a/1-js/4-data-structures/3-string/article.md
+++ b/1-js/4-data-structures/3-string/article.md
@@ -49,29 +49,8 @@ let guestList = "Guests: // Error: Unexpected token ILLEGAL
Single and double quotes come from ancient times of language creation, and the need for multiline strings was not taken into account. Backticks appeared much later and thus are more versatile.
-Backticks also allow to specify a "template function" at the beginning.
+Backticks also allow 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. You can read more in the [docs](mdn:JavaScript/Reference/Template_literals#Tagged_template_literals). That is called "tagged templates". This feature makes it easier to wrap strings into custom templating or other functionality, but is rarely used.
-Its name is put before the first backtick. Then it receives the string and embedded expressions can can process them.
-
-The syntax is:
-```js run
-function love(string, value1, value2) {
- alert( string[0] ); // Hello
- alert( string[1] ); // and
- alert( value1 ); // Ilya
- alert( value2 ); // Julia
- return value1 + ' ♥ ' + value2;
-}
-
-let mom = "Julia";
-let dad = "Ilya";
-
-let str = love`Hello ${mom} and ${dad}`;
-
-alert(str); // 'Julia ♥ Ilya'
-```
-
-In the example above, `love` is the name for the function. It is called with an array
## Special characters
@@ -554,7 +533,7 @@ For instance:
alert( 'Österreich'.localeCompare('Zealand') ); // -1
```
-The method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), that allow to specify the language (by default taken from the environment) and setup additional rules like case sensivity or should `a` and `á` be treated as the same etc.
+The method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), that allow to specify the language (by default taken from the environment) and setup additional rules like case sensivity or should `"a"` and `"á"` be treated as the same etc.
## Internal encoding
diff --git a/1-js/4-data-structures/4-object/3-sum-salaries/_js.view/solution.js b/1-js/4-data-structures/4-object/3-sum-salaries/_js.view/solution.js
index 7e980ba0..509b3589 100644
--- a/1-js/4-data-structures/4-object/3-sum-salaries/_js.view/solution.js
+++ b/1-js/4-data-structures/4-object/3-sum-salaries/_js.view/solution.js
@@ -1,8 +1,8 @@
function sumSalaries(salaries) {
let sum = 0;
- for (let name in salaries) {
- sum += salaries[name];
+ for (let salary of Object.values(salaries)) {
+ sum += salary;
}
return sum;
diff --git a/1-js/4-data-structures/4-object/3-sum-salaries/task.md b/1-js/4-data-structures/4-object/3-sum-salaries/task.md
index 2ccdc27e..211357d0 100644
--- a/1-js/4-data-structures/4-object/3-sum-salaries/task.md
+++ b/1-js/4-data-structures/4-object/3-sum-salaries/task.md
@@ -6,7 +6,7 @@ importance: 5
There is a `salaries` object with arbitrary number of salaries.
-Write the function `sumSalaries(salaries)` that returns the sum of all salaries.
+Write the function `sumSalaries(salaries)` that returns the sum of all salaries using `Object.values` and the `for..of` loop.
If `salaries` is empty, then the result must be `0`.
diff --git a/1-js/4-data-structures/4-object/4-max-salary/_js.view/solution.js b/1-js/4-data-structures/4-object/4-max-salary/_js.view/solution.js
index fa32d1a7..d95cf1b1 100644
--- a/1-js/4-data-structures/4-object/4-max-salary/_js.view/solution.js
+++ b/1-js/4-data-structures/4-object/4-max-salary/_js.view/solution.js
@@ -3,9 +3,9 @@ function topSalary(salaries) {
let max = 0;
let maxName = null;
- for (let name in salaries) {
- if (max < salaries[name]) {
- max = salaries[name];
+ for(let [name, salary] of Object.entries(salaries)) {
+ if (max < salary) {
+ max = salary;
maxName = name;
}
}
diff --git a/1-js/4-data-structures/4-object/4-max-salary/task.md b/1-js/4-data-structures/4-object/4-max-salary/task.md
index 89052aea..a1f6e380 100644
--- a/1-js/4-data-structures/4-object/4-max-salary/task.md
+++ b/1-js/4-data-structures/4-object/4-max-salary/task.md
@@ -19,3 +19,4 @@ Create the function `topSalary(salaries)` that returns the name of the top-paid
- If `salaries` is empty, it shoul return `null`.
- If there are multiple top-paid persons, return any of them.
+P.S. Use `Object.entries` and destructuring to iterate over key/value pairs.
diff --git a/1-js/4-data-structures/4-object/5-count-properties/_js.view/solution.js b/1-js/4-data-structures/4-object/5-count-properties/_js.view/solution.js
new file mode 100644
index 00000000..1853d20e
--- /dev/null
+++ b/1-js/4-data-structures/4-object/5-count-properties/_js.view/solution.js
@@ -0,0 +1,4 @@
+function count(obj) {
+ return Object.keys(obj).length;
+}
+
diff --git a/1-js/4-data-structures/4-object/5-count-properties/_js.view/test.js b/1-js/4-data-structures/4-object/5-count-properties/_js.view/test.js
new file mode 100644
index 00000000..e568c320
--- /dev/null
+++ b/1-js/4-data-structures/4-object/5-count-properties/_js.view/test.js
@@ -0,0 +1,13 @@
+describe("count", function() {
+ it("counts the number of properties", function() {
+ assert.equal( count({a: 1, b: 2}), 2 );
+ });
+
+ it("returns 0 for an empty object", function() {
+ assert.equal( count({}), 0 );
+ });
+
+ it("ignores symbolic properties", function() {
+ assert.equal( count({ [Symbol('id')]: 1 }), 0 );
+ });
+});
\ No newline at end of file
diff --git a/1-js/4-data-structures/4-object/5-multiply-numeric/solution.md b/1-js/4-data-structures/4-object/5-count-properties/solution.md
similarity index 100%
rename from 1-js/4-data-structures/4-object/5-multiply-numeric/solution.md
rename to 1-js/4-data-structures/4-object/5-count-properties/solution.md
diff --git a/1-js/4-data-structures/4-object/5-count-properties/task.md b/1-js/4-data-structures/4-object/5-count-properties/task.md
new file mode 100644
index 00000000..d7aebb1f
--- /dev/null
+++ b/1-js/4-data-structures/4-object/5-count-properties/task.md
@@ -0,0 +1,21 @@
+importance: 5
+
+---
+
+# Count properties
+
+Write a function `count(obj)` that returns the number of properties in the object:
+
+```js
+let user = {
+ name: 'John',
+ age: 30
+};
+
+alert( count(user) ); // 2
+```
+
+Try to make the code as short as possible.
+
+P.S. Ignore symbolic properties, count only "regular" ones.
+
diff --git a/1-js/4-data-structures/4-object/5-multiply-numeric/_js.view/solution.js b/1-js/4-data-structures/4-object/6-multiply-numeric/_js.view/solution.js
similarity index 100%
rename from 1-js/4-data-structures/4-object/5-multiply-numeric/_js.view/solution.js
rename to 1-js/4-data-structures/4-object/6-multiply-numeric/_js.view/solution.js
diff --git a/1-js/4-data-structures/4-object/5-multiply-numeric/_js.view/source.js b/1-js/4-data-structures/4-object/6-multiply-numeric/_js.view/source.js
similarity index 100%
rename from 1-js/4-data-structures/4-object/5-multiply-numeric/_js.view/source.js
rename to 1-js/4-data-structures/4-object/6-multiply-numeric/_js.view/source.js
diff --git a/1-js/4-data-structures/4-object/5-multiply-numeric/_js.view/test.js b/1-js/4-data-structures/4-object/6-multiply-numeric/_js.view/test.js
similarity index 69%
rename from 1-js/4-data-structures/4-object/5-multiply-numeric/_js.view/test.js
rename to 1-js/4-data-structures/4-object/6-multiply-numeric/_js.view/test.js
index 1c0d6cf9..7a538ff8 100644
--- a/1-js/4-data-structures/4-object/5-multiply-numeric/_js.view/test.js
+++ b/1-js/4-data-structures/4-object/6-multiply-numeric/_js.view/test.js
@@ -5,9 +5,14 @@ describe("multiplyNumeric", function() {
height: 300,
title: "My menu"
};
- multiplyNumeric(menu);
+ let result = multiplyNumeric(menu);
assert.equal(menu.width, 400);
assert.equal(menu.height, 600);
assert.equal(menu.title, "My menu");
});
+
+ it("returns nothing", function() {
+ assert.isUndefined( multiplyNumeric({}) );
+ });
+
});
\ No newline at end of file
diff --git a/1-js/4-data-structures/4-object/6-multiply-numeric/solution.md b/1-js/4-data-structures/4-object/6-multiply-numeric/solution.md
new file mode 100644
index 00000000..e69de29b
diff --git a/1-js/4-data-structures/4-object/5-multiply-numeric/task.md b/1-js/4-data-structures/4-object/6-multiply-numeric/task.md
similarity index 100%
rename from 1-js/4-data-structures/4-object/5-multiply-numeric/task.md
rename to 1-js/4-data-structures/4-object/6-multiply-numeric/task.md
diff --git a/1-js/4-data-structures/4-object/article.md b/1-js/4-data-structures/4-object/article.md
index ef20fbc3..ed3eeae9 100644
--- a/1-js/4-data-structures/4-object/article.md
+++ b/1-js/4-data-structures/4-object/article.md
@@ -5,7 +5,7 @@ Objects in JavaScript combine two functionalities.
1. First -- they are "associative arrays": a structure for storing keyed data.
2. Second -- they provide features for object-oriented programming.
-Here we concentrate on the first part: using objects as a data store. That's the required base for studying the second part.
+Here we concentrate on the first part: using objects as a data store, and we will study it in-depth. That's the required base for studying the second part.
An [associative array](https://en.wikipedia.org/wiki/Associative_array), also called "a hash" or "a dictionary" -- is a data structure for storing arbitrary data in the key-value format.
@@ -15,9 +15,10 @@ We can imagine it as a cabinet with signed files. Every piece of data is stored

-## Creation
-An empty object ("empty cabinet") can be created using one of two syntaxes:
+## Object literals
+
+An empty object ("empty cabinet") can be created using one of to syntaxes:
```js
let user = new Object(); // works the same as below
@@ -34,52 +35,12 @@ We can set properties immediately:
let user = {
name: "John",
age: 30,
- "likes birds": true
+ "likes birds": true // multiword property name must be quoted
};
```

-A property name can be only a string (or a symbol, but we do not consider them here). We can try using boolean/numeric names, but they will be treated as strings automatically. Note that "complex" property names, like multiword ones, need to be quoted, to evade syntax errors.
-
-
-````smart header="Any word or a number can be a property"
-If something can be a variable name, then it can be used as a property name without quotes.
-
-But for variables, there are additional limitations:
-
-- A variable cannot start with a number.
-- Language-reserved words like `let`, `return`, `function` etc are disallowed.
-
-These are lifted from literal objects. See:
-
-```js run
-let example = {
- let: 1, // reserved words can be properties
- return: 2, // they even don't need quotes!
- function: 3
-};
-
-// working fine:
-alert(example.let + example.return + example.function); // 6
-```
-
-So, actually, any number or a valid variable name (even reserved) can be a property and needs no quotes. Quotes allow to use arbitrary strings.
-````
-
-
-## Add/remove properties
-
-Now we can read/write new properties using the dot notation and remove using the `delete` operator:
-
-```js
-user.surname = "Smith";
-
-delete user.age;
-```
-
-## Square brackets
-
To access a property, there are two syntaxes:
- The dot notation: `user.name`
@@ -104,7 +65,9 @@ alert( user[key] ); // John (if enter "name"), 30 for the "age"
The square brackets literally say: "take the property name from the variable".
-Also it is possible to use square brackets in object definition when the property name is stored in a variable or computed:
+Also it is handy to use square brackets in an object literal, when the property name is stored in a variable.
+
+That's called a *computed property*:
```js run
let fruit = prompt("Which fruit to buy?", "apple");
@@ -124,19 +87,68 @@ let bag = {};
bag[fruit] = 5;
```
-...But one statement instead of two.
-
We could have used a more complex expression inside square brackets or a quoted string. Anything that would return a property name:
```js
+let fruit = 'apple';
let bag = {
- [ fruit.toLowerCase() ]: 5 // if fruit is "APPLE" then bag.apple = 5
+ [ fruit.toUpperCase() ]: 5 // bag.APPLE = 5
};
```
-## Check for existance
-A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns `undefined`. It's actually a common way to test whether the property exists -- to get it and compare vs undefined:
+````smart header="Property name must be either a string or a symbol"
+We can only use strings or symbols as property names.
+
+Other values are converted to strings, for instance:
+
+```js run
+let obj = {
+ 0: "test" // same as "0": "test"
+}
+
+alert( obj["0"] ); // test
+```
+
+````
+
+````smart header="Reserved words are allowed as property names"
+A variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc.
+
+But for an object property, there's no such restruction. Any name is fine:
+
+```js run
+let obj = {
+ for: 1,
+ let: 2,
+ return: 3
+}
+
+alert( obj.for + obj.let + obj.return ); // 6
+```
+
+Basically, any name is allowed. With one exclusion. There's a built-in property named `__proto__` with a special functionality (we'll cover it later), which can't be set to a non-object value:
+
+```js run
+let obj = {};
+obj.__proto__ = 5;
+alert(obj.__proto__); // [object Object], didn't work as intended
+```
+
+If we want to store *arbitrary* (user-provided) keys, then this can be a source of bugs. There's another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in a few chapters, it can support arbitrary keys.
+````
+
+## Removing a property
+
+There's a `delete` operator for that:
+
+```js
+delete user.name;
+```
+
+## Existance check
+
+A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns `undefined`. It provides a very common way to test whether the property exists -- to get it and compare vs undefined:
```js run
let user = {};
@@ -171,8 +183,10 @@ let key = "age";
alert( key in user ); // true, takes the value of key and checks for such property
```
-The `in` operator works in the certain case when the previous method doesn't. That is: when an object property stores `undefined`.
+````smart header="The property which equals `undefined`"
+Thee is a case when `"=== undefined"` check fails, but the `"in"` operator works correctly.
+It's when an object property stores `undefined`:
```js run
let obj = { test: undefined };
@@ -180,155 +194,135 @@ let obj = { test: undefined };
alert( obj.test ); // undefined, no such property?
alert( "test" in obj ); // true, the property does exist!
-alert( "no-such-property" in obj ); // false, no such property (just for the contrast)
```
-In the code above, the property `obj.test` stores `undefined`, so the first check fails. But the `in` operator puts things right.
+In the code above, the property `obj.test` stores `undefined`, so the first check fails.
+But the `in` operator puts things right.
Situations like this happen very rarely, because `undefined` is usually not assigned. We mostly use `null` for unknown values. So the `in` operator is an exotic guest in the code.
-
-
-## The "for..in" loop [#for..in]
-
-To process every object property, there's a special loop: `for..in`.
-
-This syntax construct is a little bit different from the `for(;;)` that we've covered before:
-
-```js
-for (key in obj) {
- /* ... do something with obj[key] ... */
-}
-```
-
-The loop iterates over properties of `obj`. For each property it's name is writen in the `key` variable and the loop body is called.
-
-````smart header="Inline variable declaration: `for (let key in obj)`"
-A variable for property names can be declared right in the loop:
-
-```js
-for (*!*let key*/!* in menu) {
- // ...
-}
-```
-
-The variable `key` will be only visible inside the loop body. Also we could use another variable name, like: `for(let prop in menu)`.
````
-An example of the iteration:
+## Property shorthands
-```js run
-let menu = {
- width: 300,
- height: 200,
- title: "Menu"
-};
+There are two more syntax features to write a shorter code.
-for (let key in menu) {
- // the code will be called for each menu property
- // ...and show its name and value
+Property value shorthands
+: To create a property from a variable:
-*!*
- alert( `Key:${key}, value:${menu[key]}` );
-*/!*
+ ```js
+ let name = "John";
+
+ // same as { name: name }
+ let user = { name };
+ ```
+
+ If we have a variable and want to add a same-named property, that's the way to write it shorter.
+
+ ```js
+ // can combine normal properties and shorthands
+ let user = { name, age: 30 };
+ ```
+
+Methods definitions
+: For properties that are functions, there's also a shorter syntax.
+
+ ```js
+ // these two objects are equal
+
+ let user = {
+ sayHi: function() {
+ alert("Hello");
+ }
+ };
+
+ let user = {
+ sayHi() { // same as "sayHi: function()"
+ alert("Hello");
+ }
+ };
+ ```
+
+ To say the truth, these notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter.
+
+## Loops
+
+We've already seen one of the most popular loops: `for..in`
+
+```js
+for(let key in obj) {
+ // key iterates over object keys
}
```
-Note that we're using the square brackets: `menu[key]`. As we've seen before, if the property name is stored in a variable, then we must use square brackets, not the dot notation.
+But there are also ways to get keys, values or or key/value pairs as arrays:
-### Counting properties
+- [Object.keys(obj)](mdn:js/Object/keys) -- returns the array of keys.
+- [Object.values(obj)](mdn:js/Object/values) -- returns the array of values.
+- [Object.entries(obj)](mdn:js/Object/entries) -- returns the array of `[key, value]` pairs.
-How to see how many properties are stored in the object? There's no method for that.
+For instance:
-Although, we can count:
-
-```js run
-let menu = {
- width: 300,
- height: 200,
- title: "Menu"
-};
-
-*!*
-let counter = 0;
-
-for (let key in menu) {
- counter++;
-}
-*/!*
-
-alert( `Total ${counter} properties` ); // Total 3 properties
-```
-
-In the next chapter we'll study arrays and see that there's a shorter way: `Object.keys(menu).length`.
-
-### Are objects ordered?
-
-As an example, let's consider an object with the phone codes:
-
-```js run
-let codes = {
- "49": "Germany",
- "41": "Switzerland",
- "44": "Great Britain",
- // ..,
- "1": "USA"
-};
-
-for(let code in codes) alert(code); // 1, 41, 44, 49
-```
-
-The object is used to generate HTML `