Skip to content

Commit 6f2828a

Browse files
committed
up
1 parent f2c68d1 commit 6f2828a

File tree

40 files changed

+341
-112
lines changed

40 files changed

+341
-112
lines changed

1-js/10-es-modern/10-set-map/article.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,12 @@ visitsCountMap.set(user, 123);
7171
alert( visitsCountMap.get(user) ); // 123
7272
```
7373

74+
Использование объектов в качестве ключей -- как раз тот случай, когда `Map` сложно заменить обычными объектами `Object`. Ведь для обычных объектов ключ может быть только строкой.
75+
7476
```smart header="Как map сравнивает ключи"
7577
Для проверки значений на эквивалентность используется алгоритм [SameValueZero](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-samevaluezero). Он аналогичен строгому равенству `===`, отличие -- в том, что `NaN` считается равным `NaN`. Поэтому значение `NaN` также может быть использовано в качестве ключа.
7678
77-
Этот алгоритм жёстко фиксирован в стандарте, его нельзя изменять или задавать свою функцию сравнения.
79+
Этот алгоритм нельзя изменять или задавать свою функцию сравнения.
7880
```
7981

8082
Методы для удаления записей:
@@ -263,5 +265,11 @@ activeUsers.splice(0, 1); // Петя более не активный поль
263265

264266
- `Map` -- коллекция записей вида `ключ: значение`, лучше `Object` тем, что перебирает всегда в порядке вставки и допускает любые ключи.
265267
- `Set` -- коллекция уникальных элементов, также допускает любые ключи.
268+
269+
Основная область применения `Map` -- ситуации, когда строковых ключей не хватает (нужно хранить соответствия для ключей-объектов), либо когда строковый ключ может быть совершенно произвольным.
270+
271+
К примеру, в обычном объекте `Object` нельзя использовать "совершенно любые" ключи. Есть встроенные методы, и уж точно есть свойство с названием `__proto__`, которое зарезервировано системой. Если название ключа даётся посетителем сайта, то он может попытаться использовать такое свойство, заменить прототип, а это, при запуске JavaScript на сервере, уже может привести к серьёзным ошибкам.
272+
266273
- `WeakMap` и `WeakSet` -- "урезанные" по функционалу варианты `Map/Set`, которые позволяют только "точечно" обращаться элементам (по конкретному ключу или значению). Они не препятствуют сборке мусора, то есть если ссылка на объект осталась только в `WeakSet/WeakMap` -- он будет удалён.
267274

275+

1-js/10-es-modern/2-let-const/article.md

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ let a = 5;
2222
Например, переменная через `var`:
2323

2424
```js run
25-
'use strict';
26-
2725
var apples = 5;
2826

2927
if (true) {
@@ -40,8 +38,6 @@ let a = 5;
4038
То же самое с `let` будет работать по-другому:
4139

4240
```js run
43-
'use strict';
44-
4541
let apples = 5; // (*)
4642
4743
if (true) {
@@ -60,8 +56,6 @@ let a = 5;
6056
Заметим, что если объявление `let apples` в первой строке `(*)` удалить, то в последнем `alert` будет ошибка: переменная неопределена:
6157

6258
```js run
63-
'use strict';
64-
6559
if (true) {
6660
let apples = 10;
6761
@@ -79,8 +73,6 @@ let a = 5;
7973
Как мы помним, переменные `var` существуют и до объявления. Они равны `undefined`:
8074

8175
```js run
82-
'use strict';
83-
8476
alert(a); // undefined
8577
8678
var a = 5;
@@ -90,8 +82,6 @@ let a = 5;
9082

9183
Такой доступ приведёт к ошибке:
9284
```js run
93-
'use strict';
94-
9585
*!*
9686
alert(a); // ошибка, нет такой переменной
9787
*/!*
@@ -102,16 +92,12 @@ let a = 5;
10292
Заметим также, что переменные `let` нельзя повторно объявлять. То есть, такой код выведет ошибку:
10393

10494
```js run
105-
'use strict';
106-
10795
let x;
10896
let x; // ошибка: переменная x уже объявлена
10997
```
11098

11199
Это -- хоть и выглядит ограничением по сравнению с `var`, но на самом деле проблем не создаёт. Например, два таких цикла совсем не конфликтуют:
112100
```js run
113-
'use strict';
114-
115101
// каждый цикл имеет свою переменную i
116102
for(let i = 0; i<10; i++) { /* … */ }
117103
for(let i = 0; i<10; i++) { /* … */ }
@@ -137,8 +123,6 @@ let a = 5;
137123
Это позволяет легко решить классическую проблему с замыканиями, описанную в задаче <info:task/make-army>.
138124

139125
```js run
140-
'use strict';
141-
142126
function makeArmy() {
143127
144128
let shooters = [];
@@ -167,14 +151,25 @@ let a = 5;
167151
Объявление `const` задаёт константу, то есть переменную, которую нельзя менять:
168152

169153
```js run
170-
'use strict';
171-
172154
const apple = 5;
173155
apple = 10; // ошибка
174156
```
175157

176158
В остальном объявление `const` полностью аналогично `let`.
177159

160+
Заметим, что если в константу присвоен объект, то от изменения защищена сама константа, но не свойства внутри неё:
161+
162+
```js
163+
const user = {
164+
name: "Вася"
165+
};
166+
167+
user.name = "Петя"; // допустимо
168+
user = 5; // нельзя, будет ошибка
169+
```
170+
171+
То же самое верно, если константе присвоен массив или другое объектное значение.
172+
178173
```smart header="константы и КОНСТАНТЫ"
179174
Константы, которые жёстко заданы всегда, во время всей программы, обычно пишутся в верхнем регистре. Например: `const ORANGE = "#ffa500"`.
180175

1-js/2-first-steps/2-external-script/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
Можно указать и полный URL, например:
1414

1515
```html
16-
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js"></script>
16+
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script>
1717
```
1818

1919
Вы также можете использовать путь относительно текущей страницы. Например, `src="lodash.js"` обозначает файл из текущей директории.

1-js/5-functions-closures/5-closures-module/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ function work() {
161161

162162
```html run
163163
<p>Подключим библиотеку</p>
164-
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js"></script>
164+
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script>
165165

166166
<p>Функция <code>_.defaults()</code> добавляет отсутствующие свойства.</p>
167167
<script>

1-js/6-objects-more/2-object-conversion/4-object-types-conversion-questions/task.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ importance: 5
99
```js no-beautify
1010
new Date(0) - 0
1111
new Array(1)[0] + ""
12-
({})[0]
12+
({})[0]
1313
[1] + 1
1414
[1,2] + [3,4]
1515
[] + null + 1
1616
[[0]][0][0]
1717
({} + {})
1818
```
19-

1-js/6-objects-more/4-descriptors-getters-setters/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ alert( pete.age ); // и возраст
387387
: Запрещает добавление, удаление и изменение свойств, все текущие свойства делает `configurable: false, writable: false`.
388388

389389
[Object.isExtensible(obj)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isExtensible)
390-
: Возвращает `false`, если добавление свойств объекта запрещено.
390+
: Возвращает `false`, если добавление свойств объекта было запрещено вызовом метода `Object.preventExtensions`.
391391

392392
[Object.isSealed(obj)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isSealed)
393393
: Возвращает `true`, если добавление и удаление свойств объекта запрещено, и все текущие свойства являются `configurable: false`.

1-js/9-prototypes/6-instanceof/article.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
## Алгоритм работы instanceof [#ref-instanceof]
88

9-
Вызов `obj instanceof Constructor` возвращает `true`, если объект принадлежит классу `Constructor` или его родителям.
9+
Вызов `obj instanceof Constructor` возвращает `true`, если объект принадлежит классу `Constructor` или классу, наследующему от него.
1010

1111
Пример использования:
1212

@@ -32,7 +32,7 @@ alert( arr instanceof Array ); // true
3232
alert( arr instanceof Object ); // true
3333
```
3434

35-
Как это часто бывает в JavaScript, здесь есть ряд тонкостей. В некоторых ситуациях, проверка может даже ошибаться!
35+
Как это часто бывает в JavaScript, здесь есть ряд тонкостей. Проверка происходит через сравнение прототипов, поэтому в некоторых ситуациях может даже ошибаться!
3636

3737
**Алгоритм проверки `obj instanceof Constructor`:**
3838

2-ui/3-event-details/11-onload-onerror/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ document.body.appendChild(script);
3232

3333
```js run
3434
var script = document.createElement('script');
35-
script.src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js"
35+
script.src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"
3636
document.body.appendChild(script);
3737

3838
*!*

2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
В браузере нет способа "просто получить" текущие координаты. Это может сделать обработчик события, в данном случае `mousemove`. Поэтому нужно будет поставить обработчик на `mousemove` и при каждом движении запоминать текущие координаты, чтобы `setInterval` мог раз в 100 мс сравнивать их.
66

7-
Можно обойтись и без `setInterval` -- сравнивать координаты при каждом срабатывании `mousemove`. Если передвинулись на маленькое расстояние с последнего `mousemove` -- это "наведение на элемент", а на большое -- игнорируем. Вариант с `setInterval` лучше с точки зрения производительности -- `mousemove` происходит уж очень часто, но если проверка несложная, то и `mousemove` подойдёт.
8-
97
Имеет смысл начинать анализ координат и отслеживание `mousemove` при заходе на элемент, а заканчивать -- при выходе с него.
108

119
Чтобы точно отловить момент входа и выхода, без учёта подэлементов (во избежание мигания), можно использовать `mouseenter/mouseleave`.

2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use strict';
2+
13
function HoverIntent(options) {
24

35
options = Object.create(options); // not to modify the object
@@ -16,7 +18,11 @@ function HoverIntent(options) {
1618
var isHover;
1719

1820
// A private function for handling mouse 'hovering'
19-
elem.addEventListener("mouseover", function(event) {
21+
elem.addEventListener("mouseover", onMouseOver);
22+
23+
elem.addEventListener("mouseout", onMouseOut);
24+
25+
function onMouseOver(event) {
2026

2127
if (isOverElement) {
2228
// если мы и так над элементом, то это всплывший переход внутри него
@@ -27,41 +33,52 @@ function HoverIntent(options) {
2733
isOverElement = true;
2834

2935
// при каждом движении мыши mousemove мы будем вычислять расстояние между
30-
// предыдущими и текущими координатами курсора
36+
// предыдущими и текущими координатами курсора
3137
// если оно меньше sensivity, то скорость маленькая и это наведение на элемент
3238
// pX, pY - "предыдущие" координаты
3339
pX = event.pageX;
3440
pY = event.pageY;
3541
pTime = Date.now();
3642

3743
elem.addEventListener('mousemove', onMouseMove);
38-
});
39-
40-
elem.addEventListener("mouseout", function(event) {
44+
checkSpeedInterval = setInterval(trackSpeed, options.interval);
45+
}
46+
47+
function onMouseOut(event) {
4148
// если ушли вовне элемента
42-
if (event.relatedTarget && !elem.contains(event.relatedTarget)) {
49+
if (!event.relatedTarget || !elem.contains(event.relatedTarget)) {
4350
isOverElement = false;
4451
elem.removeEventListener('mousemove', onMouseMove);
52+
clearInterval(checkSpeedInterval);
4553
if (isHover) {
4654
// если была остановка над элементом
4755
options.out.call(elem, event);
4856
isHover = false;
4957
}
5058
}
51-
});
59+
}
5260

5361
function onMouseMove(event) {
5462
cX = event.pageX;
5563
cY = event.pageY;
5664
cTime = Date.now();
65+
}
66+
67+
function trackSpeed() {
5768

58-
if (pTime == cTime) return; // когда mousemove вместе с mouseover
59-
60-
var speed = Math.sqrt(Math.pow(pX - cX, 2) + Math.pow(pY - cY, 2)) / (cTime - pTime);
69+
let speed;
70+
71+
if (!cTime || cTime == pTime) {
72+
// нет измерений скорости (событий mousemove)
73+
// значит курсор не двигался
74+
speed = 0;
75+
} else {
76+
speed = Math.sqrt(Math.pow(pX - cX, 2) + Math.pow(pY - cY, 2)) / (cTime - pTime);
77+
}
6178

6279
if (speed < options.sensitivity) {
6380
// если с предыдущей позиции меньше sensivity дистанция, то "остановка на элементе"
64-
elem.removeEventListener("mousemove", onMouseMove);
81+
clearInterval(checkSpeedInterval);
6582
isHover = true;
6683
options.over.call(elem, event);
6784
} else {
@@ -71,5 +88,11 @@ function HoverIntent(options) {
7188
pTime = cTime;
7289
}
7390
}
91+
92+
this.destroy = function() {
93+
elem.removeEventListener('mousemove', onMouseMove);
94+
elem.removeEventListener('mouseover', onMouseOver);
95+
elem.removeEventListener('mouseout', onMouseOut);
96+
};
7497

75-
}
98+
}

0 commit comments

Comments
 (0)