From a9222196ae0426f65d8862e1bca8a0ec582f7cfd Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Thu, 7 Jul 2016 19:00:12 +0300 Subject: [PATCH] work --- 1-js/2-first-steps/07-types/article.md | 10 +- .../solution.md | 4 +- .../task.md | 2 - .../08-type-conversions/article.md | 114 ++- 1-js/2-first-steps/10-comparison/article.md | 16 +- .../19-function-create-advanced/article.md | 70 +- .../20-object-methods/article.md | 718 +++++------------- .../21-object-tostring-valueof/article.md | 63 ++ .../22-primitives-methods/article.md | 4 +- 1-js/4-data-structures/4-object/article.md | 10 +- 10 files changed, 362 insertions(+), 649 deletions(-) rename 1-js/2-first-steps/{09-operators/3-primitive-conversions-questions => 08-type-conversions/1-primitive-conversions-questions}/solution.md (59%) rename 1-js/2-first-steps/{09-operators/3-primitive-conversions-questions => 08-type-conversions/1-primitive-conversions-questions}/task.md (76%) create mode 100644 1-js/2-first-steps/21-object-tostring-valueof/article.md diff --git a/1-js/2-first-steps/07-types/article.md b/1-js/2-first-steps/07-types/article.md index 7a4f0824..a35ad46f 100644 --- a/1-js/2-first-steps/07-types/article.md +++ b/1-js/2-first-steps/07-types/article.md @@ -15,6 +15,8 @@ n = 12.345; A *number* type serves both for integer and floating point numbers. +There are many operations for numbers, e.g. multiplication `*`, division `/`, addition `+`, substraction `-` and so on. + Besides regular numbers there are so-called "special numeric values" which also belong to that type: `Infinity`, `-Infinity` and `NaN`. - `Infinity` represents the mathematical [Infinity](https://en.wikipedia.org/wiki/Infinity). It is a value that's greater than any number. @@ -33,13 +35,13 @@ Besides regular numbers there are so-called "special numeric values" which also - `NaN` represents a computational error. It is a result of an incorrect or an undefined mathematical operation, for instance: ```js run - alert( "not a number" * 2 ); // NaN + alert( "not a number" / 2 ); // NaN ``` `NaN` is sticky. Any further operation on `NaN` would give `NaN`: ```js run - alert( "not a number" * 2 + 5 - 9 ); // NaN + alert( "not a number" / 2 + 5 ); // NaN ``` So, in a long mathematical expression if we have `NaN` in one place, it propagates to the whole result. @@ -232,6 +234,8 @@ Here we have a variable `key` which contains the property name, probably evaluat Most of time, the dot is used to access object properties, but when we need a complex property name or to pass the name as a variable, then -- we go square brackets. +Javascript supports object inheritance. There are many types that are based on objects: `Date` for dates, `Array` for ordered data, `Error` for error-reporting and so on. So the word "object" is applicable to a variety of things. The term *plain objects* is used to represent "basic" objects, the ones we create with `{ ... }`. + Objects in JavaScript are very powerful. Here we've just scratched the surface of the topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial. ## Arrays @@ -263,7 +267,7 @@ alert( fruits[2] ); // Plum alert( fruits.length ); // 3 ``` -Please note that arrays do not form a separate language type. They are based on objects, but have many features of their own including methods to add, remove, extract elements from the array, to sort arrays and more. We'll cover them in the chapter . +Please note that arrays do not form a separate language type. They are based on objects. But they greatly extend them with features of their own, methods to add, remove, extract elements from the array, to sort arrays and more. We'll cover them in the chapter . ## Symbol type diff --git a/1-js/2-first-steps/09-operators/3-primitive-conversions-questions/solution.md b/1-js/2-first-steps/08-type-conversions/1-primitive-conversions-questions/solution.md similarity index 59% rename from 1-js/2-first-steps/09-operators/3-primitive-conversions-questions/solution.md rename to 1-js/2-first-steps/08-type-conversions/1-primitive-conversions-questions/solution.md index 5ce77f86..1678b441 100644 --- a/1-js/2-first-steps/09-operators/3-primitive-conversions-questions/solution.md +++ b/1-js/2-first-steps/08-type-conversions/1-primitive-conversions-questions/solution.md @@ -16,7 +16,7 @@ null + 1 = 1 // (3) undefined + 1 = NaN // (4) ``` -1. The plus `"+"` operator in this case first converts `1` to a string: `"" + 1 = "1"`, and then adds `0`. -2. The minus `"-"` operator only works with numbers, it converts an empty string `""` to zero immediately. +1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied. +2. The substruction `"-"` (like most math operations) only works with numbers, it converts an empty string `""` to zero immediately. 3. `null` becomes `0` after the numeric conversion. 4. `undefined` becomes `NaN` after the numeric conversion. diff --git a/1-js/2-first-steps/09-operators/3-primitive-conversions-questions/task.md b/1-js/2-first-steps/08-type-conversions/1-primitive-conversions-questions/task.md similarity index 76% rename from 1-js/2-first-steps/09-operators/3-primitive-conversions-questions/task.md rename to 1-js/2-first-steps/08-type-conversions/1-primitive-conversions-questions/task.md index c8505637..36e89b4a 100644 --- a/1-js/2-first-steps/09-operators/3-primitive-conversions-questions/task.md +++ b/1-js/2-first-steps/08-type-conversions/1-primitive-conversions-questions/task.md @@ -4,8 +4,6 @@ importance: 5 # Type conversions -Let's recap type conversions given in the [previous capter](/types) in the context of operators. - What will be the result of these evaluation? ```js no-beautify 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 0559a058..9b0ac980 100644 --- a/1-js/2-first-steps/08-type-conversions/article.md +++ b/1-js/2-first-steps/08-type-conversions/article.md @@ -1,4 +1,4 @@ -# Type conversions +# Type Conversions A variable in JavaScript can contain any data. A variable can at one moment be a string and later recieve a numeric value: @@ -8,21 +8,11 @@ let message = "hello"; message = 123456; ``` -...But sometimes we need to convert a value from one type to another. For example, `alert` automatically converts any value to a string, to show it. Or, so to say, an `if(value)` test converts the `value` to boolean type to see if it's `true` or `false`. +...But some operations implicitly convert a value from one type to another. For example, `alert` automatically converts any value to a string, to show it. Or mathematical operations convert values to numbers. That is called *type coercion*. There are also cases when we need to explicitly convert between types to ensure that we store the right data the right way or to use special features of a certain type. -There are many type conversions in JavaScript, fully listed in [the specification](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-type-conversion). - -Three conversions that happen the most often: - -1. String conversion. -2. Numeric conversion. -3. Boolean conversion. - -Let's see how they work and when they happen. - -## String conversion +## ToString The string conversion happens when we need a string form of a value. @@ -46,36 +36,19 @@ alert(typeof a); // string */!* ``` -The string conversion is obvious. A `false` becomes `"false"`, `null` becomes `"null"` etc. +The string conversion is mostly 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 +## ToNumber Numeric conversion happens in mathematical functions and expressions automatically. -For example, a mathematical operation like division '/' can be applied to non-numbers: +For example, when division '/' is applied to non-numbers: ```js run -alert( "6" / "2" ); // 3, strings become numbers +alert( "6" / "2" ); // 3, strings are converted to numbers ``` -We can use a `Number(value)` function to convert any `value` to a number: +We can use a `Number(value)` function to explicitly convert a `value`: ```js run let str = "123"; @@ -86,9 +59,9 @@ let n = Number(str); // becomes a number 123 alert(typeof n); // number ``` -The conversion is usually applied when we have a numeric value coming from a text form field or another string-based source. +The explicit conversion is usually required when we read a value coming from a text form field or another string-based source, but we expect a number to be entered. -If the string is not a number, the result of such conversion is `NaN`, for instance: +If the string is not a valid number, the result of such conversion is `NaN`, for instance: ```js run let age = Number("an arbitrary string instead of a number"); @@ -116,7 +89,22 @@ alert( Number(false) ); // 0 Please note that `null` and `undefined` behave differently here: `null` becomes a zero, while `undefined` becomes `NaN`. -## Boolean conversion +````smart header="Addition '+' concatenates strings" +Almost all mathematical operations convert values to numbers. With a notable exception of the addition `+`. If one of the added values is a string, then another one is also converted to a string. + +Then it concatenates (joins) them: + +```js run +alert( 1 + '2' ); // '12' (string to the right) +alert( '1' + 2 ); // '12' (string to the left) + +alert( 1 + 2 ); // 3, numbers (for the contrast) +``` + +That only happens when one of arguments is a string, in other cases values are converted to numbers. +```` + +## ToBoolean Boolean conversion is the simplest one. @@ -125,7 +113,7 @@ It happens in logical operations (later we'll meet `if` tests and other kinds), The conversion rule: - Values that are intuitively "empty", like `0`, an empty string, `null`, `undefined` and `NaN` become `false`. -- Other values become `true`. +- Other values become `true`. ````warn header="Please note: the string with zero `\"0\"` is `true`" Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript a non-empty string is always `true`. @@ -136,6 +124,49 @@ alert( Boolean(" ") ); // any non-empty string, even whitespaces are true ``` ```` +````warn header="Empty objects and arrays are truthy" +All objects become `true`: + +```js run +alert( Boolean([]) ); // true +alert( Boolean({}) ); // true +``` +```` + + +## ToPrimitive + +A string or numeric conversion of an object is a two-stage process. The object is first converted to a primitive value, and then ToString/ToNumber rules are applied to it. + +The conversion is customizable on a per-object basis, so we'd better deal with it later when we know more about objects. + +For now, let's just see two common rules that we often meet when showing objects. + +- When a plain object is converted into a string, is becomes `[object Object]`: + + ```js run + alert( {} ); // [object Object] + alert( {name: "John"} ); // [object Object] + ``` + +- An array becomes a comma-delimited list of items: + + ```js run + let arr = [1, 2, 3]; + + alert( arr ); // 1,2,3 + alert( String(arr) === '1,2,3' ); // true + ``` + +We'll return to it in the chapter [todo]. + +```smart header="It was only about ToString/ToNumber" +For ToBoolean, there is no complexity neither customizability. + +The rule is simple: all objects are truthy. +``` + + ## Summary There exist three most widely used type conversions: to string, to number and to boolean. @@ -158,11 +189,10 @@ To boolean: |`0`, `null`, `undefined`, `NaN`, `""` |`false`| |any other value| `true` | -Objects provide advanced means to specify how they are converted to string and number, we'll see them later, when study objects in-depth. - Most of these rules are easy to understand and memorize. The notable exceptions where people usually make mistakes are: - `undefined` is `NaN` as a number. - `"0"` is true as a boolean. -In the next chapter we'll study operators. You will find enough tasks for type conversion there. \ No newline at end of file +Objects can define their own methods of converting to a string or a number, we'll see them later. But they can't redefine the conversion to boolean. + diff --git a/1-js/2-first-steps/10-comparison/article.md b/1-js/2-first-steps/10-comparison/article.md index 1bf8041e..2dff9746 100644 --- a/1-js/2-first-steps/10-comparison/article.md +++ b/1-js/2-first-steps/10-comparison/article.md @@ -69,7 +69,7 @@ For instance, the case matters. A capital letter `"A"` is not equal to the lower ## Comparison of different types -When compared values belong to different types, they get autoconverted to numbers. +When compared values belong to different types, they are coerced to numbers. For example: @@ -187,7 +187,7 @@ We've got such result, because: - Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN`. And `NaN` is a special numeric value which returns `false` for all comparisons. - The equality check `(3)` returns `false`, because `undefined` only equals `null` and no other value. -### How to live a good life +### Evade problems Why did we observe these examples? Should we remember these pecularities all the time? Well, not really. Actually, these tricky things will gradually become familiar over the time, but there's a solid way to evade any problems with them. @@ -195,6 +195,18 @@ Just treat any comparison with `undefined/null` except the strict equality `===` Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you are really sure what you're doing. If a variable can have such values, then check it separately. +## Comparison with objects + +For equality checks two objects are always treated as non-equal. + +```js run +alert( {} == {} ); // false +alert( [] == [] ); // false +``` + +Note that Javascript does not try to compare the objects by content. The rule is simple: different objects are not equal. + +For other comparisons like `< >` operators, or when an object is compared with a primitive, objects are first converted to primitives, and then the comparison runs as usual. ## Summary diff --git a/1-js/2-first-steps/19-function-create-advanced/article.md b/1-js/2-first-steps/19-function-create-advanced/article.md index 6ffb7cab..fe575f2d 100644 --- a/1-js/2-first-steps/19-function-create-advanced/article.md +++ b/1-js/2-first-steps/19-function-create-advanced/article.md @@ -156,78 +156,12 @@ A function can be perceived as an *action*. We can copy it between variables and run when we want. We can even add properties to it if we wish. ``` -## Function Expression as a method - -Now let's step back and reconsider. We have two ways of declaring a function. Do we really need both? What's so good about Function Expressions that makes it useful? - -Actually, yes, we do. For example, we can assign functions to object properties using function expressions. - -As we remember from the chapter , objects are data structures meant to store collections of data. Most often, we create objects to represent entities of the real world, like users, goodies and so on: - -```js -let user = { - name: "John", - age: 30 -}; -``` - -In the real world, a user can `act`: to select something from the shopping cart, to login, to logout etc. For the start, let's teach him to say hello: - -```js run -let user = { - name: "John", - age: 30 -}; - -*!* -user.sayHi = function() { - alert("Hello!"); -}; -*/!* - -user.sayHi(); // Hello! -``` - -You see? We've just used a Function Expression to create the function and assign it to the property `user.sayHi` of the object. - -Then we can call it any time. The user now can speak! - -That is how a so-called "object-oriented code" is written. We make objects which reflect entities of the real world: like a user, or a document, or a button that is clickable etc. - -An object stores its data in regular properties (like `name`, `age` etc) and has functions to express itself. Function properties are usually called *methods*. So, one can say that in the code above "`sayHi` is a method of the object `user`". - -Of course we could use a Function Declaration for the same purpose: - -```js run -let user = { - // ... -}; - -*!* -function sayHi() { - alert("Hello!"); -}; - -user.sayHi = sayHi; -*/!* - -user.sayHi(); // Hello! -``` - -That would also work, but is longer. Also we get an "extra" function `sayHi` outside of the `user` object. Here we don't want it. - -```smart header="Object-oriented programming" -When we write our code using objects to represent entities, that's called an [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), in short: "OOP". - -As of now, we already know how to create an object with `{...}` and how to store data and add a method to it. But we will study it in detail later when we get enough familarity with basic functions of the language. -``` - ## Function Expression vs Function Declaration Let's formulate the key differences between Function Declarations and Expressions. -Here's the syntax distinction between these two. +First, the syntax: how to see what is what in the code. - *Function Declaration:* a function, declared as a separate statement, in the main code flow. @@ -247,7 +181,7 @@ Here's the syntax distinction between these two. } ``` -Another difference is when they are actualy created by the JavaScript engine. +The more subtle difference is when they are actualy created by the JavaScript engine. **Function Expressions are created when the execution reaches them and are usable since then.** diff --git a/1-js/2-first-steps/20-object-methods/article.md b/1-js/2-first-steps/20-object-methods/article.md index 6ffb7cab..3b07e45c 100644 --- a/1-js/2-first-steps/20-object-methods/article.md +++ b/1-js/2-first-steps/20-object-methods/article.md @@ -1,168 +1,6 @@ -# Function expressions and more +# Object methods, "this" -In JavaScript, a function is a value. - -We can declare it as we did before: - -```js -function sayHi() { - alert( "Hello" ); -} -``` - -This syntax is called a "Function Declaration". - -...But there is another way of creating a function: - -```js -let sayHi = function() { - alert( "Hello" ); -}; -``` - -The latter syntax is called a "Function Expression". - -The meaning of these code samples is the same: "create a function and put it into the variable `sayHi`". - -Let's stress: a function is not a "magical language structure", but a kind of value. Both syntaxes mean the same: create a special "function" value and put it into the variable. - -No matter, how the function is defined -- it's just a value, stored in the variable `sayHi`. - -We can even print out that value using `alert`: - -```js run -function sayHi() { - alert( "Hello" ); -} - -*!* -alert( sayHi ); // shows the function code -*/!* -``` - -Note that there are no brackets after `sayHi` in the last line, because we do not intend to run the function. There are programming languages where any mention of a function name causes it's call, but JavaScript is not like that. In JavaScript, a function is a value and we can deal with that as a value. The code above shows its string representation, that is its source code. - -It is a special value of course, in the sense that we can call it using brackets: `"sayHi()"`. - -But it's still a value. So we can work with it like with other kinds of values. - -We can copy a function to another variable: - -```js run no-beautify -function sayHi() { // (1) create - alert( "Hello" ); -} - -let func = sayHi; // (2) copy - -func(); // Hello // (3) call the copy (it works)! -sayHi(); // Hello // this works too (why wouldn't it) -``` - -That's what happens above in detail: - -1. Function Declaration `(1)` creates the function and puts it into the variable named `sayHi`. -2. Line `(2)` copies it into variable `func`. - - Please note again: there are no brackets after `sayHi`. If they were, then `func = sayHi()` would write *the result of the call* `sayHi()` into `func`, not *the function* `sayHi` itself. -3. Now the function can be called both as `sayHi()` and `func()`. - -Note, that we could also have used a Function Expression to declare `sayHi`, in the first line: - -```js -let sayHi = function() { ... }; - -let func = sayHi; -// ... -``` - -Everything would work the same. Even more obvious what's going on, right? - - -````smart header="Why semicolon?" -There might be a question, why Function Expression has a semicolon `;` at the end, and Function Declaration does not: - -```js -function sayHi() { - // ... -} - -let sayHi = function() { - // ... -}*!*;*/!* -``` - -The answer is simple: -- There's no need in `;` at the end of code blocks and syntax structures that use them like `if { ... }`, `for { }`, `function f { }` and alike. -- A Function Expression appears in the context of the statement: `let sayHi = value;`. It's not a code block. The semicolon `;` is recommended at the end of statements, no matter what is the `value`. So the semicolon here is not related to Function Expression itself in any way, it just terminates the statement. -```` - - - -## Function is an object - -Every value in Javascript has the type. What type of value is a function? - -In Javascript, a function is an object. - -For example, all functions have property `name` (function name) and `length` (number of arguments): - -```js run -function sayHi() { - alert("Hi"); -} - -alert( sayHi.name ); // sayHi -alert( sayHi.length ); // 0 -``` - -We can add our own properties to it as well: - -```js run -function sayHi() { - alert("Hi"); - - *!* - // let's count how many times we run - sayHi.counter++; - */!* -} -sayHi.counter = 0; // initial value - -sayHi(); // Hi -sayHi(); // Hi - -alert( `Called ${sayHi.counter} times` ); // Called 2 times -``` - - -```warn header="A property is not a variable" -A property assigned to a function like `sayHi.counter = 0` does *not* define a local variable `counter` inside it. In other words, a property `sayHi.counter` and `let counter` inside the function (if we have it) are two unrelated things. - -We can treat a function as an object for convenience, store properties in it, that has no effect on its execution. -``` - -There are many well-known Javascript libraries that make a great use of custom function properties. - -They create a "main" function and attach many other "helper" functions to it. For instance, the [jquery](https://jquery.com) library creates a function named `$`. The [lodash](https://lodash.com) library creates a function `_`. And then adds `_.clone`, `_.keyBy` and other properties to (see the [docs](https://lodash.com/docs) when you want learn more about them). - -So, a function can do a useful job by itself and also carry a bunch of other functionality in properties. - -```smart header="A function is a value representing an \"action\"" -Regular values like strings or numbers represent the *data*. - -A function can be perceived as an *action*. - -We can copy it between variables and run when we want. We can even add properties to it if we wish. -``` - -## Function Expression as a method - -Now let's step back and reconsider. We have two ways of declaring a function. Do we really need both? What's so good about Function Expressions that makes it useful? - -Actually, yes, we do. For example, we can assign functions to object properties using function expressions. - -As we remember from the chapter , objects are data structures meant to store collections of data. Most often, we create objects to represent entities of the real world, like users, goodies and so on: +As we remember from the chapter , objects to store keyed collections of data. We usually create objects to represent entities of the real world, like users, orders and so on: ```js let user = { @@ -171,7 +9,13 @@ let user = { }; ``` -In the real world, a user can `act`: to select something from the shopping cart, to login, to logout etc. For the start, let's teach him to say hello: +And, in the real world, a user can `act`: to select something from the shopping cart, to login, to logout etc. + +Let's implement the same in Javascript. + +[cut] + +For the start, let's teach the `user` to say hello: ```js run let user = { @@ -188,13 +32,21 @@ user.sayHi = function() { user.sayHi(); // Hello! ``` -You see? We've just used a Function Expression to create the function and assign it to the property `user.sayHi` of the object. +Here we've just used a Function Expression to create the function and assign it to the property `user.sayHi` of the object. -Then we can call it any time. The user now can speak! +Then we can call it. The user now can speak! -That is how a so-called "object-oriented code" is written. We make objects which reflect entities of the real world: like a user, or a document, or a button that is clickable etc. +A function that is the property of an object is called its *method*. -An object stores its data in regular properties (like `name`, `age` etc) and has functions to express itself. Function properties are usually called *methods*. So, one can say that in the code above "`sayHi` is a method of the object `user`". +So, here we've got a method `sayHi` of the object `user`. + +```smart header="Object-oriented programming" +When we write our code using objects to represent entities, that's called an [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), in short: "OOP". + +OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the code and the interaction between them? That's an architecture. + +We will make use of OOP further when we get enough familarity with basic functions of the language. +``` Of course we could use a Function Declaration for the same purpose: @@ -216,448 +68,276 @@ user.sayHi(); // Hello! That would also work, but is longer. Also we get an "extra" function `sayHi` outside of the `user` object. Here we don't want it. -```smart header="Object-oriented programming" -When we write our code using objects to represent entities, that's called an [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), in short: "OOP". +## Method syntax -As of now, we already know how to create an object with `{...}` and how to store data and add a method to it. But we will study it in detail later when we get enough familarity with basic functions of the language. +In an object literal, there's a shorter syntax for methods: + +```js +// these objects do the same + +let user = { + sayHi: function() { + alert("Hello"); + } +}; + +let user = { +*!* + sayHi() { // same as "sayHi: function()" +*/!* + alert("Hello"); + } +}; ``` +As demonstrated, we can omit a colon with the word `"function"`. And it actually looks better. -## Function Expression vs Function Declaration +To say 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. -Let's formulate the key differences between Function Declarations and Expressions. +## "this" in methods -Here's the syntax distinction between these two. +It's common that an object method needs to access the information stored in the object to do its job. -- *Function Declaration:* a function, declared as a separate statement, in the main code flow. +For instance, `user.sayHi()` may need to mention the name. - ```js - // Function Declaration - function sum(a, b) { - return a + b; - } - ``` -- *Function Expression:* a function, created in the context of an expression. +**To access the object, a method can use the `this` keyword.** - Here the function is created in the context of an "assignment expression =": - ```js - // Function Expression - let sum = function(a, b) { - return a + b; - } - ``` +The value of `this` is the object "before dot", the one used to call the method. -Another difference is when they are actualy created by the JavaScript engine. +For instance: -**Function Expressions are created when the execution reaches them and are usable since then.** +```js run +let user = { + name: "John", + age: 30, -That's kind of obvious. Once the execution flow passes to the right side of the assignment `let sum = function` -- here we go, the function is created and can be used (assigned, called etc) from now on. - -Function Declarations are different. - -**Function Declarations are usable in the whole script/code block.** - -In other words, when JavaScript *prepares* to run the script/code block, it first looks for Function Declarations in it and creates the functions. We can think of it as an "initialization stage". - -And after all Function Declarations are processed, it actually executes it. - -As a natural effect, a function declared as Function Declaration can be called earlier than it is defined. - -For example, this works: - -```js run refresh untrusted + sayHi() { *!* -sayHi("John"); // Hello, John + alert( this.name ); // "this" means "this object" */!* + } -function sayHi(name) { - alert( `Hello, ${name}` ); -} -``` - -Function Declaration `sayHi` is created when JavaScript is preparing to start the script and is visible everywhere in it. - -...And if there were Function Expression, then it wouldn't work: - -```js run refresh untrusted -*!* -sayHi("John"); // error! -*/!* - -let sayHi = function(name) { // (*) no magic any more - alert( `Hello, ${name}` ); }; + +user.sayHi(); // John ``` -Function Expressions are created when the execution reaches them. That would happen only in the line `(*)`. Too late. +Here during the execution of `user.sayHi()`, the value of `this` will be `user`. +Technically, it's also possible to access the object without `this`: -### Function Declaration in a block +```js +... + sayHi() { + alert( *!*user.name*/!* ); + } +... +``` -When Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it. +...But such code is unreliable. If we decide to copy `user` to another variable, e.g. `admin = user` and overwrite `user` with something else, then it will access the wrong object. -Sometimes that's handy to declare a local function, only needed in that only block. But can also be a problem. - -For instance, let's imagine that we need to declare a function `welcome()` depending on the `age` variable that we get in run time. And then use it sometimes later. - -The code below doesn't work: +That's demonstrated below: ```js run -let age = prompt("What is your age?", 18); +let user = { + name: "John", + age: 30, -if (age < 18) { - - function welcome() { - alert("Hello!"); + sayHi() { +*!* + alert( user.name ); // leads to an error +*/!* } -} else { +}; - function welcome() { - alert("Greetings!"); - } -} +let admin = user; +user = null; // overwrite to make things obvious -*!* -welcome(); // Error: welcome is not defined -*/!* +admin.sayHi(); // wops! inside sayHi(), the old name is used! error! ``` -The Function Declaration is visible only inside the code block where it resides. +The variant with `this.name` instead of `user.name` would work fine here. -We can call it from within the block, but not from outside: +## "this" is free -```js run -let age = 16; // take 16 as an example +In Javascript, "this" keyword behaves unlike most other programming languages. -if (age < 18) { -*!* - welcome(); // \ (runs) -*/!* - // | - function welcome() { // | - alert("Hello!"); // | Function Declaration is available - } // | everywhere in the block when it's declared - // | -*!* - welcome(); // / (runs) -*/!* - -} else { - // \ - function welcome() { // | - alert("Greetings!"); // | in this if test we don't enter this block, - } // | so this "welcome" is never created - // / -} - -// Now we're out of figure brackets, -// so we can not see Function Declarations made inside of them. - -*!* -welcome(); // Error: welcome is not defined -*/!* -``` - -What can we do to make `welcome` visible outside? - -The right thing would be to use a Function Expression and assign `welcome` to the variable which is declared outside of `if` and has the proper visibility: - -```js run -let age = prompt("What is your age?", 18); - -let welcome; - -if (age < 18) { - - welcome = function() { - alert("Hello!"); - } - -} else { - - welcome = function() { - alert("Greetings!"); - } - -} - -*!* -welcome(); // ok now -*/!* -``` - -Or we could go on to simplify it even further using a question mark operator `?`: - -```js run -let age = prompt("What is your age?", 18); - -let welcome = (age < 18) ? - function() { alert("Hello!"); } : function() { alert("Greetings!"); } - -*!* -welcome(); // ok now -*/!* -``` - - -```smart header="What to choose: a Declaration or an Expression?" -As a rule of thumb, a Function Declaration is prefered. It gives more freedom in how to organize our code, because we can call it both above and below. - -It's also a little bit easier to look up Function Declarations in the code, they increase readability of the code. - -But if a Function Declaration does not fit for some reason (we've seen an example), then a Function Expression should be used. -``` - - -## Named Function Expression - -Now the feature that exists for a Function Expression, but not for Function Declaration, and hence may be a reason to use them. - -**We can specify an "internal" name for a Function Expression.** - -Compare these two variants: +The "this" keyword can be used in any function. There's no syntax error in the code like that: ```js -let sayHi = function() { // (1) - alert('Hello'); -}; - -let sayHi = function *!*func*/!*() { // (2) - alert('Hello'); -}; +function sayHi() { + alert( this.name ); +} ``` -Both create a function and put it into the variable `sayHi`. And usually we don't specify anything in the `function()`, so the 1st variant works fine. +It is fully syntactically free. The function does not yet know what "this" value will be. It will be evaluated during the run-time. -Although the function can move to another variable. As we've seen it's easy to copy a function and maybe replace the previous value with something else: +For instance, it may have different "this" when called in the context of different objects: ```js run -let sayHi = function() { - alert('Hello'); -}; +let user = { name: "John" }; +let admin = { name: "Admin" }; -// oh maybe that's better? -let oldSayHi = sayHi; // keep the old variant here -sayHi = function() { // replace with a newer one - alert("What's up dude?"); -}; +function sayHi() { + alert( this.name ); +} +user.f = sayHi; +admin.g = sayHi; -oldSayHi(); // Hello -sayHi(); // What's up dude? +// "this" inside the function is the object "before the dot" +user.f(); // John +admin.g(); // Admin +admin['g'](); // Admin (dot or square brackets to access the method – doesn't matter) ``` -The problem may occur if a function references *itself* from inside. It happens when the function wants to access its properties (`sayHi.counter` in the example above), or sometimes it wants to call itself one more time. - - -But if the function has moved, then the old name is not relevant any move! - - -Let's see: +Actually, we can call the function without an object at all: ```js run -// create a function -let sayHi = function() { - sayHi.counter++; - alert('Hi ' + sayHi.counter); +function sayHi() { + alert(this); +} + +sayHi(); +``` + +In this case `this` is `undefined`. If we try to access `this.name`, there will be an error. + +Such calls are often a result of the programming mistake. If a function has `this`, then it is usually to be called in the context of an object. + +```smart header="And without `use strict`..." +If you forget `use strict`, then you may see something like "window" in the example above. + +That's one of the odd things of the previous standard that `"use strict"` fixes. +``` + +## "this" is for direct calls + +The value of `this` is only passed the right way if the function is called using a dot `'.'` or square brackets. + +A more intricate call would lead to losing `this`, for instance: + +```js run +let user = { + name: "John", + hi() { alert(this.name); }, + bye() { alert("Bye"); } }; -sayHi.counter = 0; -// move it -let movedSayHi = sayHi; - -// overwrite the old name to make things more obvious -sayHi = null; +user.hi(); // John (the simple call works) *!* -movedSayHi(); // Error: Cannot read property 'counter' of null +// now let's call user.hi or user.bye depending on the name +(user.name == "John" ? user.hi : user.bye)(); // undefined */!* ``` -The optional `name` which we can put into `function name()` is exactly meant to solve this kind of problems. +On the last line the method is retrieved during the execution of the ternary `?`, and immediately called. But `"this"` is lost, the result is not `"John"` how it should be. -- It is only visible from inside the function. -- It always references the current function. +If we want to understand why it happens -- the reason is in the details of how `obj.method()` works. -Let's use it to fix the code: +The method call has two independant operations in it: a dot `'.'` to access the property and brackets `()` to execute it (assuming that's a function). -```js run -// now with the internal name "say" -*!* -let sayHi = function say() { - say.counter++; - alert('Hi ' + say.counter); // and use it everywhere inside -}; -*/!* -sayHi.counter = 0; +As we've already seen, the function is a value of its own. It does not memorize the object by itself. So to "carry" it to the brackets, Javascript uses a trick -- the dot `'.'` returns not a function, but a value of the special Reference Type. -let movedSayHi = sayHi; +The Reference Type is a "specification type". It does not exist in real, but used internally to explain how some language features work. -sayHi = null; +The value of the Reference Type is a tuple `(base, name, strict)`, where: -movedSayHi(); // Hi 1 -movedSayHi(); // Hi 2 (works) +- `base` is the object. +- `name` is the property. +- `strict` is true if `use strict` is in effect. -alert(say); // Error (say is undefined, that's normal) +The result of a property access `'.'` is a value of the Reference Type. For `user.sayHi` in strict mode it is: + +```js +// base name strict +(user, "sayHi", true) ``` -Please note that: +Any operation on the Reference Type immediately "resolves" it: -- The name `say` references the current function no matter where it is. That's why it works. -- The name `say` exists only inside the function. The last line demonstrates that. +- Brackets `()` get the property `base[name]` and execute it with `this = base`. +- Other operators just get `base[name]` and use it. -So the outer code still uses `sayHi` (or `movedSayHi` later). It doesn't see `say`, actually it doesn't need to see it. The `say` is an "internal function name", how it calls itself privately. +So any operation on the result of dot `'.'` except a direct call discards `this`. -A Function Expression with a name is called *Named Function Expression*. +## Explicit "this" with "call/apply" -The "internal name" feature described here is only available for Function Expressions, not to Function Declarations. For Function Declarations, the `function name()` behaves like `sayHi`, there's just no syntax possibility to add a one more "internal" name for them. - - - -## Arrow functions [#arrow-functions] - -Enough with the complexities for now. Let's relax with another syntax of functions that can make our code shorter. - -Arrow functions act like a function expression, but look a little bit differently. +We can call a function explicitly providing the value of `"this"`. The syntax is: ```js -let func = (arg1, arg2, ...argN) => expression +func.call(context, arg1, arg2, ...) ``` -...This creates a function `func` that has arguments `arg1..argN`, evaludates the `expression` on the right side with their use and returns its result. - -In other words, it's roughly the same as: - -```js -let func = function(arg1, arg2, ...argN) { - return expression; -} -``` - -Let's see the example: +For instance: ```js run -let sum = (a, b) => a + b; - -alert( sum(1, 2) ); // 3 -``` - -Here the function is same as: - -```js -let sum = function(a, b) { - return a + b; -} -``` - -If we have only one argument, then brackets can be omitted, making that even shorter: - -```js run -// same as -// let double = function(n) { return n*2 } -let double = n => n*2; - -alert( double(3) ); // 6 -``` - -If there are no arguments, we can put empty brackets. For instance, here's the rewritten example with `welcome()`: - -```js run -let age = prompt("What is your age?", 18); - -let welcome = (age < 18) ? () => alert('Hello') : () => alert("Greetings!"); - -welcome(); // ok now -``` - -The syntax may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure. - -Arrow functions are very convenient for simple one-line actions, when we're just lazy to write many words. - -```smart header="Multiline arrow functions" - -The examples above took arguments from the left of `=>` and evaluate the right-side expression with them. - -Sometimes we need something a little bit more complex, like multiple expressions or statements. It is also possible, but we should enclose them in figure brackets. Then use a normal `return` within them. - -Like this: - -```js run -let sum = (a, b) => { // the figure bracket opens a multiline function - let result = a + b; -*!* - return result; // if we use figure brackets, must use return -*/!* +function sayHi() { + alert(this.name); } -alert( sum(1, 2) ); // 3 +let user = { name: "John" }; +let admin = { name: "Admin" }; + +// use call to pass different objects as "this" +sayHi.call( user ); // John +sayHi.call( admin ); // Admin ``` -```smart header="More to come" -Here we praised arrow functions for shortness. But that's not all! Arrow functions have other interesting features in them. We'll return to them later and see where else they shine. +The first parameter of `call` is the intended value of `"this"`, the latter are arguments. -As for now, we can already use them for one-line actions. -``` - -## new Function - -And the last syntax for the functions: +These calls are roughly equivalent: ```js -let func = new Function('a, b', 'return a + b'); +func(1, 2, 3); +func.call(obj, 1, 2, 3) ``` -The major difference is that it creates a function literally from a string, at run time. See, both arguments are strings. The first one lists the arguments, while the second one is the function body. +...Except that the `call` sets "this" of course! -All previous declarations required us, programmers, to write the function code in the script. +That's handy when we want to use a function in the context of different objects, but do not want to actually assign it to them. -But `new Function` allows to turn any string into a function, for example we can receive a new function from the server and then execute it: +### "func.apply" + +There's also a similar syntax: ```js -let str = ... receive the code from the server dynamically ... - -let func = new Function('', str); -func(); +func.apply(context, args) ``` -It is used in very specific cases, like when we receive the code from the server, or to dynamically compile a function from a template. The need for such uses arises at advanced stages of the development. +It does the same as `call`: executes the function providing `context` as `this`, but where `call` awaits a list of arguments, `apply` awaits a single array of arguments. + +These two calls do the same: + +```js +func.call(obj, 1, 2, 3); +func.apply(obj, [1, 2, 3]); +``` + +In old times `apply` was more powerful, because it allows to form the array of arguments dynamically. + +But in the modern language, we have the spread operator `'...'` and can use it to unfurl an array into the list of for `call`, so these two are equal: + +```js +let args = [1, 2, 3]; + +func.call(obj, ...args); +func.apply(obj, args); +``` + +So the use of `apply` over `call` is mainly a metter of personal preference. And it's somewhat better optimized than the spread operator, because it exists longer. + +## Binding "this" with "bind" + +There's still a way to bind "this" to a function. + +[todo] ???? ## Summary -- Functions are values. They can be assigned, copied or declared in any place of the code. -- If the function is declared as a separate statement, in the main code flow -- that's called a Function Declaration. -- If the function is created as a part of an expression -- it's a Function Expression. -- Function Declarations are processed before the code block is executed. They are visible everywhere in the block (or the script). -- Function Expressions are created when the execution flow reaches them. -- Function Expressions allow to specify an optional name for internal needs (Named Function Expression). - - -If we simple want to create a function, then in most cases Function Declaration is preferable. - -Novice programmers sometimes tend to overuse Function Expression by creating many functions with `let func = function()`, but compare, which code is more readable: - -```js no-beautify -let f = function() { /* expression */ } - -function f() { /* declaration */ } -``` - -Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the actual declaration. - -**Use Function Expression only when Function Declaration does not fit the task.** - -We've seen a couple of examples of that in the chapter. And will see more in the future. - -We also touched two other ways to create a function: - -- Arrow functions are handy for one-liners. The come in two flavours: - 1. Without figure brackets: `(...args) => expression` -- returns the evaluated `expression`. - 2. With brackets: `(...args) => { body }` -- need an explicit `return` statement to return something, but can be more complex. - -- `new Function(args, body)` - - This syntax allows to create a function from a string, that may be composed dynamically during the execution. +[todo] \ No newline at end of file diff --git a/1-js/2-first-steps/21-object-tostring-valueof/article.md b/1-js/2-first-steps/21-object-tostring-valueof/article.md new file mode 100644 index 00000000..72e3cb0d --- /dev/null +++ b/1-js/2-first-steps/21-object-tostring-valueof/article.md @@ -0,0 +1,63 @@ + +# Object to primitive conversion + +In the chapter we've seen the rules for numeric, string and boolean conversions. + +But we left the gap for objects. Now let's close it. + +[cut] + +The operation that converts an object to a primitive is called [ToPrimitive](https://tc39.github.io/ecma262/#sec-toprimitive). + +Some build-in language objects have their own implementation of it, but for most objects, including our own, it comes in two flavours: + +- string +- number + +TODO + + + +The method `toString` is automatically called by Javascript when the object is converted to a string: + +```js run +let user = { + + name: 'John', + +*!* + toString() { + return `User ${this.firstName}`; + } +*/!* + +}; + +*!* +alert( user ); // User John +*/!* +``` + +Looks much better than the default `[object Object]`, right? + + +The similar thing with the method `valueOf`. It is called when the object is converted to a number. + +```js run +let room = { + number: 777, + + valueOf() { + return this.number; + }, +}; + +alert( +room ); // 777, valueOf is called +``` + +What really sounds strange -- is the name of the method. Why is it called "valueOf", why not "toNumber"? + +The reason is that `valueOf` is used by default to convert an object to a primitive for operations where a primitive value is required. + + + diff --git a/1-js/2-first-steps/22-primitives-methods/article.md b/1-js/2-first-steps/22-primitives-methods/article.md index 94e1d4cb..3a382960 100644 --- a/1-js/2-first-steps/22-primitives-methods/article.md +++ b/1-js/2-first-steps/22-primitives-methods/article.md @@ -90,6 +90,6 @@ alert(null.test); // error ## Summary -- Primitives except `null` and `undefined` provide various methods. We plan to study those in the next chapters. -- Primitives are not objects, but still have methods. Formally, these methods work via temporary objects, but JavaScript engines are tuned to optimize that internally, so they are not expensive to call. +- Primitives except `null` and `undefined` provide many helpful methods. We plan to study those in the next chapters. +- Formally, these methods work via temporary objects, but JavaScript engines are tuned to optimize that internally, so they are not expensive to call. diff --git a/1-js/4-data-structures/4-object/article.md b/1-js/4-data-structures/4-object/article.md index 1094d879..5cc1f17b 100644 --- a/1-js/4-data-structures/4-object/article.md +++ b/1-js/4-data-structures/4-object/article.md @@ -235,17 +235,9 @@ Property value shorthands ``` Methods definitions -: For properties that are functions, there's also a shorter syntax. +: For properties that are functions, we've already seen a shorter syntax. ```js - // these two objects are equal - - let user = { - sayHi: function() { - alert("Hello"); - } - }; - let user = { sayHi() { // same as "sayHi: function()" alert("Hello");