fixes
This commit is contained in:
parent
2ca9d97df5
commit
1b858a03f2
3 changed files with 41 additions and 29 deletions
|
@ -14,7 +14,6 @@ The latest draft is at <https://tc39.es/ecma262/>.
|
||||||
|
|
||||||
To read about bleeding-edge features, that are not yet widely supported, see proposals at <https://github.com/tc39/proposals>.
|
To read about bleeding-edge features, that are not yet widely supported, see proposals at <https://github.com/tc39/proposals>.
|
||||||
|
|
||||||
|
|
||||||
Also, if you're in developing for the browser, then there are other specs covered in the [second part](info:browser-environment) of the tutorial.
|
Also, if you're in developing for the browser, then there are other specs covered in the [second part](info:browser-environment) of the tutorial.
|
||||||
|
|
||||||
## Manuals
|
## Manuals
|
||||||
|
|
|
@ -45,7 +45,19 @@ alert(test); // true, the variable lives after if
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
If we used `let test` on the 2nd line, then it wouldn't be visible to `alert`. But `var` ignores code blocks, so we've got a global `test`.
|
`var` ignores code blocks, so we've got a global variable `test`.
|
||||||
|
|
||||||
|
If we used `let test` instead of `var test`, then the variable would only be visible inside `if`:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
if (true) {
|
||||||
|
let test = true; // use "let"
|
||||||
|
}
|
||||||
|
|
||||||
|
*!*
|
||||||
|
alert(test); // Error: test is not defined
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
The same thing for loops: `var` cannot be block- or loop-local:
|
The same thing for loops: `var` cannot be block- or loop-local:
|
||||||
|
|
||||||
|
@ -76,7 +88,7 @@ alert(phrase); // Error: phrase is not defined (Check the Developer Console)
|
||||||
|
|
||||||
As we can see, `var` pierces through `if`, `for` or other code blocks. That's because a long time ago in JavaScript blocks had no Lexical Environments. And `var` is a remnant of that.
|
As we can see, `var` pierces through `if`, `for` or other code blocks. That's because a long time ago in JavaScript blocks had no Lexical Environments. And `var` is a remnant of that.
|
||||||
|
|
||||||
## "var" are processed at the function start
|
## "var" declarations are processed at the function start
|
||||||
|
|
||||||
`var` declarations are processed when the function starts (or script starts for globals).
|
`var` declarations are processed when the function starts (or script starts for globals).
|
||||||
|
|
||||||
|
@ -178,11 +190,11 @@ In both examples above `alert` runs without an error, because the variable `phra
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
There are two main differences of `var`:
|
There are two main differences of `var` compared to `let/const`:
|
||||||
|
|
||||||
1. Variables have no block scope, they are visible minimum at the function level.
|
1. `var` variables have no block scope, they are visible minimum at the function level.
|
||||||
2. Variable declarations are processed at function start.
|
2. `var` declarations are processed at function start (script start for globals).
|
||||||
|
|
||||||
There's one more minor difference related to the global object, we'll cover that in the next chapter.
|
There's one more minor difference related to the global object, we'll cover that in the next chapter.
|
||||||
|
|
||||||
These differences are actually a bad thing most of the time. Block-level variables is such a great thing. That's why `let` was introduced in the standard long ago, and is now a major way (along with `const`) to declare a variable.
|
These differences make `var` worse than `let` most of the time. Block-level variables is such a great thing. That's why `let` was introduced in the standard long ago, and is now a major way (along with `const`) to declare a variable.
|
||||||
|
|
|
@ -99,23 +99,25 @@ Please note that the call to the parent method `super.say()` from `sayHiMixin` l
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
That's because methods from `sayHiMixin` have `[[HomeObject]]` set to it. So `super` actually means `sayHiMixin.__proto__`, not `User.__proto__`.
|
That's because methods `sayHi` and `sayBye` were initially created in `sayHiMixin`. So their `[[HomeObject]]` internal property references `sayHiMixin`, as shown on the picture above.
|
||||||
|
|
||||||
|
As `super` looks for parent methods in `[[HomeObject]].__proto__`, that means it searches `sayHiMixin.__proto__` for `super.say`, not `User.__proto__`.
|
||||||
|
|
||||||
## EventMixin
|
## EventMixin
|
||||||
|
|
||||||
Now let's make a mixin for real life.
|
Now let's make a mixin for real life.
|
||||||
|
|
||||||
The important feature of many objects is working with events.
|
The important feature of many browser objects (not only) is working with events. Events is a way to "broadcast information" to anyone who wants it. So let's make a mixin that allows to easily add event-related functions to any class/object.
|
||||||
|
|
||||||
That is: an object should have a method to "generate an event" when something important happens to it, and other objects should be able to "listen" to such events.
|
- The mixin will provide a method `.trigger(name, [data])` to "generate an event" when something important happens to it. The `name` argument is a name of the event, and the optional additional data may follow.
|
||||||
|
- Also the method `.on(name, handler)` that adds `handler` function as the listener to events with the given name. It will be called when the event triggers.
|
||||||
|
- ...And the method `.off(name, handler)` that removes `handler` listener.
|
||||||
|
|
||||||
An event must have a name and, optionally, bundle some additional data.
|
After adding the mixin, an object `user` will become able to generate an event `"login"` when the visitor logs in. And another object `calendar` may want to receive such events to load the calendar for the logged-in person.
|
||||||
|
|
||||||
For instance, an object `user` can generate an event `"login"` when the visitor logs in. And another object `calendar` may want to receive such events to load the calendar for the logged-in person.
|
Or, a `menu` can generate the event `"select"` when a menu item is selected, and other objects may want to get that information and react on that event. And so on.
|
||||||
|
|
||||||
Or, a `menu` can generate the event `"select"` when a menu item is selected, and other objects may want to get that information and react on that event.
|
Here's the code:
|
||||||
|
|
||||||
Events is a way to "share information" with anyone who wants it. They can be useful in any class, so let's make a mixin for them:
|
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let eventMixin = {
|
let eventMixin = {
|
||||||
|
@ -146,7 +148,7 @@ let eventMixin = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the event and attach the data to it
|
* Generate an event with the given name and data
|
||||||
* this.trigger('select', data1, data2);
|
* this.trigger('select', data1, data2);
|
||||||
*/
|
*/
|
||||||
trigger(eventName, ...args) {
|
trigger(eventName, ...args) {
|
||||||
|
@ -160,12 +162,10 @@ let eventMixin = {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
There are 3 methods here:
|
|
||||||
|
|
||||||
1. `.on(eventName, handler)` -- assigns function `handler` to run when the event with that name happens. The handlers are stored in the `_eventHandlers` property.
|
|
||||||
2. `.off(eventName, handler)` -- removes the function from the handlers list.
|
|
||||||
3. `.trigger(eventName, ...args)` -- generates the event: all assigned handlers are called and `args` are passed as arguments to them.
|
|
||||||
|
|
||||||
|
- `.on(eventName, handler)` -- assigns function `handler` to run when the event with that name happens. Technically, there's `_eventHandlers` property, that stores an array of handlers for each event name. So it just adds it to the list.
|
||||||
|
- `.off(eventName, handler)` -- removes the function from the handlers list.
|
||||||
|
- `.trigger(eventName, ...args)` -- generates the event: all handlers from `_eventHandlers[eventName]` are called, with a list of arguments `...args`.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
|
@ -176,30 +176,31 @@ class Menu {
|
||||||
this.trigger("select", value);
|
this.trigger("select", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add the mixin
|
// Add the mixin with event-related methods
|
||||||
Object.assign(Menu.prototype, eventMixin);
|
Object.assign(Menu.prototype, eventMixin);
|
||||||
|
|
||||||
let menu = new Menu();
|
let menu = new Menu();
|
||||||
|
|
||||||
// call the handler on selection:
|
// add a handler, to be called on selection:
|
||||||
*!*
|
*!*
|
||||||
menu.on("select", value => alert(`Value selected: ${value}`));
|
menu.on("select", value => alert(`Value selected: ${value}`));
|
||||||
*/!*
|
*/!*
|
||||||
|
|
||||||
// triggers the event => shows Value selected: 123
|
// triggers the event => the handler above runs and shows:
|
||||||
menu.choose("123"); // value selected
|
// Value selected: 123
|
||||||
|
menu.choose("123");
|
||||||
```
|
```
|
||||||
|
|
||||||
Now if we have the code interested to react on user selection, we can bind it with `menu.on(...)`.
|
Now if we'd like any code to react on menu selection, we can listen to it with `menu.on(...)`.
|
||||||
|
|
||||||
And the `eventMixin` can add such behavior to as many classes as we'd like, without interfering with the inheritance chain.
|
And `eventMixin` mixin makes it easy to add such behavior to as many classes as we'd like, without interfering with the inheritance chain.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
*Mixin* -- is a generic object-oriented programming term: a class that contains methods for other classes.
|
*Mixin* -- is a generic object-oriented programming term: a class that contains methods for other classes.
|
||||||
|
|
||||||
Some other languages like e.g. python allow to create mixins using multiple inheritance. JavaScript does not support multiple inheritance, but mixins can be implemented by copying them into the prototype.
|
Some other languages like e.g. Python allow to create mixins using multiple inheritance. JavaScript does not support multiple inheritance, but mixins can be implemented by copying methods into prototype.
|
||||||
|
|
||||||
We can use mixins as a way to augment a class by multiple behaviors, like event-handling as we have seen above.
|
We can use mixins as a way to augment a class by multiple behaviors, like event-handling as we have seen above.
|
||||||
|
|
||||||
Mixins may become a point of conflict if they occasionally overwrite native class methods. So generally one should think well about the naming for a mixin, to minimize such possibility.
|
Mixins may become a point of conflict if they occasionally overwrite native class methods. So generally one should think well about the naming methods of a mixin, to minimize the probability of that.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue