minor fixes
This commit is contained in:
parent
6bf5977407
commit
a592e89fdb
20 changed files with 184 additions and 96 deletions
|
@ -1,17 +0,0 @@
|
|||
**Первым выполнится обычный скрипт.**
|
||||
|
||||
Заметим, что атрибуты `defer` и `async` на обычном скрипте будут проигнорированы. То есть, он работает так же, как и без них:
|
||||
|
||||
```html
|
||||
<script>
|
||||
alert( "обычный скрипт" );
|
||||
</script>
|
||||
```
|
||||
|
||||
Далее, обычно скрипты с `async/defer` не тормозят обработку страницы. То есть, браузер начнёт их загружать, а сам пойдёт дальше показывать страницу и выполнять скрипты.
|
||||
|
||||
То есть, обычный скрипт в этом случае, очевидно, выполнится первым.
|
||||
|
||||
...Но более того, даже если скрипты `small.js` и `big.js` не нужно загружать, а браузер берёт их из кеша, то он всё равно устроен так, что выполнит их после основных скриптов страницы.
|
||||
|
||||
Таким образом, первым всегда будет обычный скрипт, а вот относительный порядок `small.js` и `big.js` здесь не регламентирован.
|
|
@ -1,17 +0,0 @@
|
|||
# Что выполнится первым из скриптов?
|
||||
|
||||
[importance 4]
|
||||
|
||||
В этой странице есть три скрипта.
|
||||
|
||||
Какой выполнится первым?
|
||||
|
||||
```html
|
||||
<script defer src="small.js"></script>
|
||||
<script async src="big.js"></script>
|
||||
|
||||
<script defer async>
|
||||
alert( "обычный скрипт" );
|
||||
</script>
|
||||
```
|
||||
|
|
@ -32,7 +32,6 @@
|
|||
Браузер скачает его только первый раз и в дальнейшем, при правильной настройке сервера, будет брать из своего [кеша](http://ru.wikipedia.org/wiki/%D0%9A%D1%8D%D1%88).
|
||||
|
||||
Благодаря этому один и тот же большой скрипт, содержащий, к примеру, библиотеку функций, может использоваться на разных страницах без полной перезагрузки с сервера.
|
||||
|
||||
[/smart]
|
||||
|
||||
|
||||
|
@ -61,7 +60,9 @@
|
|||
|
||||
## Асинхронные скрипты: defer/async
|
||||
|
||||
Обычно тег `<script>` блокирует отображение страницы.
|
||||
Браузер загружает и отображает HTML постепенно. Особенно это заметно при медленном интернет-соединении: браузер не ждёт, пока страница загрузится целиком, а показывает ту часть, которую успел загрузить.
|
||||
|
||||
Если браузер видит тег `<script>`, то он по стандарту обязан сначала выполнить его, а потом показать оставшуюся часть страницы.
|
||||
|
||||
Например, в примере ниже -- пока все кролики не будут посчитаны -- нижний `<p>` не будет показан:
|
||||
|
||||
|
@ -93,37 +94,60 @@
|
|||
</html>
|
||||
```
|
||||
|
||||
Такое поведение называют "синхронным". Как правило, оно вполне нормально, но есть один нюанс.
|
||||
Такое поведение называют "синхронным". Как правило, оно вполне нормально, но есть важное следствие.
|
||||
|
||||
**Пока браузер не загрузит и не выполнит внешний скрипт, он не покажет часть страницы под ним.**
|
||||
**Если скрипт -- внешний, то пока браузер не выполнит его, он не покажет часть страницы под ним.**
|
||||
|
||||
В ряде случаев это совсем неуместно. Например, мы подключаем внешний скрипт, который показывает рекламу или вставляет счётчик посещений, а затем идёт наша страница. Конечно, неправильно, что пока счётчик или реклама не подгрузятся -- оставшаяся часть страницы не показывается. Счётчик посещений не должен никак задерживать отображение страницы сайта. Реклама -- это дополнение к странице, она не должна как-то тормозить сайт и нарушать его функционал.
|
||||
То есть, в таком документе, пока не загрузится и не выполнится `big.js`, содержимое `<body>` будет скрыто:
|
||||
|
||||
А что, если сервер, с которого загружается внешний скрипт, перегружен? Посетитель в этом случае может ждать очень долго.
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
*!*
|
||||
<script src="big.jpg"></script>
|
||||
*/!*
|
||||
</head>
|
||||
<body>
|
||||
Этот текст не будет показан, пока браузер не выполнит big.js.
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
И здесь вопрос -- действительно ли мы этого хотим? То есть, действительно ли оставшуюся часть страницы нельзя показывать до загрузки скрипта?
|
||||
|
||||
Есть ситуации, когда мы не только НЕ хотим такой задержки, но она даже опасна.
|
||||
|
||||
Например, если мы подключаем внешний скрипт, который показывает рекламу или вставляет счётчик посещений, а затем идёт наша страница. Конечно, неправильно, что пока счётчик или реклама не подгрузятся -- оставшаяся часть страницы не показывается. Счётчик посещений не должен никак задерживать отображение страницы сайта. Реклама тоже не должна тормозить сайт и нарушать его функционал.
|
||||
|
||||
А что, если сервер, с которого загружается внешний скрипт, перегружен? Посетитель в этом случае может ждать очень долго!
|
||||
|
||||
Вот пример, с подобным скриптом (стоит искусственная задержка загрузки):
|
||||
|
||||
```html
|
||||
<!--+ run height=100 -->
|
||||
<p>Начало страницы...</p>
|
||||
<p>Важная информация не покажется, пока не загрузится скрипт.</p>
|
||||
|
||||
<script src="https://js.cx/hello/ads.js?speed=0"></script>
|
||||
|
||||
<p>...Важная информация!</p>
|
||||
```
|
||||
|
||||
В примере выше важная информация не покажется, пока не загрузится внешний скрипт. Но действительно ли он так важен, что мы хотим заставить посетителя ждать? Если это реклама или счётчик посещаемости, то вряд ли.
|
||||
Что делать?
|
||||
|
||||
Можно поставить все подобные скрипты в конец страницы -- это уменьшит проблему, но не избавит от неё полностью, поскольку скриптов может быть несколько, и если какой-то один скрипт тормозит или завис, то последующие будут его ждать.
|
||||
Можно поставить все подобные скрипты в конец страницы -- это уменьшит проблему, но не избавит от неё полностью, если скриптов несколько. Допустим, в конце страницы 3 скрипта, и первый из них тормозит -- получается, другие два его будут ждать -- тоже нехорошо.
|
||||
|
||||
Кроме того, когда скрипты в конце страницы -- то они начнут грузиться только тогда, когда вся страница загрузится. А это не всегда правильно, например счётчик посещений наиболее точно сработает, если загрузить его пораньше.
|
||||
Кроме того, браузер дойдёт до скриптов, расположенных в конце страницы, начнут грузиться только тогда, когда вся страница загрузится. А это не всегда правильно, например счётчик посещений наиболее точно сработает, если загрузить его пораньше.
|
||||
|
||||
Поэтому "расположить скрипты внизу" -- не лучший выход.
|
||||
|
||||
Кардинально решить эту проблему помогут атрибуты `async` или `defer`:
|
||||
<dl>
|
||||
<dt>Атрибут `async`</dt>
|
||||
<dd>Поддерживается всеми браузерами, кроме IE9-. Скрипт выполняется полностью асинхронно. То есть, при обнаружении `<script async src="...">` браузер не останавливает обработку страницы, а спокойно работает дальше. Когда скрипт будет загружен -- он выполнится.</dd>
|
||||
<dt>Атрибут `defer`</dt>
|
||||
<dd>Поддерживается всеми браузерами, включая самые старые IE. Скрипт выполняется асинхронно, не заставляет ждать страницу, но, в отличие от `async`, браузер гарантирует, что относительный порядок скриптов с `defer` будет сохранён.
|
||||
<dd>Поддерживается всеми браузерами, включая самые старые IE. Скрипт также выполняется асинхронно, не заставляет ждать страницу, но есть два отличия от `async`.
|
||||
|
||||
Первое -- браузер гарантирует, что относительный порядок скриптов с `defer` будет сохранён.
|
||||
|
||||
То есть, в таком коде (с `async`) первым сработает тот скрипт, который раньше загрузится:
|
||||
|
||||
|
@ -139,8 +163,21 @@
|
|||
<script src="2.js" defer></script>
|
||||
```
|
||||
|
||||
Атрибут `defer` используют в тех случаях, когда второй скрипт `2.js` зависит от первого `1.js`, к примеру -- использует что-то, описанное первым скриптом.
|
||||
Поэтому атрибут `defer` используют в тех случаях, когда второй скрипт `2.js` зависит от первого `1.js`, к примеру -- использует что-то, описанное первым скриптом.
|
||||
|
||||
Второе отличие -- скрипт с `defer` сработает, когда весь HTML-документ будет обработан браузером.
|
||||
|
||||
Например, если документ достаточно большой...
|
||||
```html
|
||||
<script src="async.js" async></script>
|
||||
<script src="defer.js" defer></script>
|
||||
|
||||
Много много много букв
|
||||
```
|
||||
|
||||
...То скрипт `async.js` выполнится, как только загрузится -- возможно, до того, как ведь документ готов. А `defer.js` подождёт готовности всего документа.
|
||||
|
||||
Это бывает удобно, когда мы в скрипте хотим работать с документом, и должны быть уверены, что он полностью получен.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
@ -158,7 +195,7 @@
|
|||
|
||||
```html
|
||||
<!--+ run height=100 -->
|
||||
<p>Начало страницы...</p>
|
||||
<p>Важная информация теперь не ждёт, пока загрузится скрипт...</p>
|
||||
|
||||
<script *!*async*/!* src="https://js.cx/hello/ads.js?speed=0"></script>
|
||||
|
||||
|
@ -173,12 +210,38 @@
|
|||
Перед вставкой внешнего тега `<script>` понимающий программист всегда проверит, есть ли у него подобный атрибут. Иначе медленный скрипт может задержать загрузку страницы.
|
||||
[/smart]
|
||||
|
||||
[smart header="Забегая вперёд"]
|
||||
Для продвинутого читателя, которые знает, что теги `<script>` можно добавлять на страницу в любой момент при помощи самого javascript, заметим, что скрипты, добавленные таким образом, ведут себя так же, как `async`. То есть, выполняются как только загрузятся, без сохранения относительного порядка.
|
||||
|
||||
Если же нужно сохранить порядок выполнения, то есть добавить несколько скриптов, которые выполнятся строго один за другим, то используется свойство `script.async = false`.
|
||||
|
||||
|
||||
Выглядит это примерно так:
|
||||
```js
|
||||
function addScript(src);
|
||||
var script = document.createElement('script');
|
||||
script.src = src;
|
||||
*!*
|
||||
script.async = false; // чтобы гарантировать порядок
|
||||
*/!*
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
addScript('1.js'); // загружаться эти скрипты начнут сразу
|
||||
addScript('2.js'); // выполнятся, как только загрузятся
|
||||
addScript('3.js'); // но, гарантированно, в порядке 1 -> 2 -> 3
|
||||
```
|
||||
|
||||
Более подобно работу со страницей мы разберём во второй части учебника.
|
||||
[/smart]
|
||||
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>Скрипты вставляются на страницу как текст в теге `<script>`, либо как внешний файл через `<script src="путь"></script>`</li>
|
||||
<li>Специальные атрибуты `async` и `defer` используются для того, чтобы пока грузится внешний скрипт -- браузер показал остальную (следующую за ним) часть страницы. Без них этого не происходит.</li>
|
||||
<li>Разница между `async` и `defer`: атрибут `defer` сохраняет относительную последовательность скриптов, а `async` -- нет.</li>
|
||||
<li>Разница между `async` и `defer`: атрибут `defer` сохраняет относительную последовательность скриптов, а `async` -- нет. Кроме того, `defer` всегда ждёт, пока весь HTML-документ будет готов, а `async` -- нет.</li>
|
||||
</ul>
|
||||
|
||||
Очень важно не только читать учебник, но делать что-то самостоятельно.
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
function pow(x, n) {
|
||||
if (n < 0) return NaN;
|
||||
if (Math.round(n) != n) return NaN;
|
||||
if (n == 0 && x == 0) return NaN;
|
||||
|
||||
var result = 1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
function pow(x, n) {
|
||||
if (n < 0) return NaN;
|
||||
if (Math.round(n) != n) return NaN;
|
||||
|
||||
var result = 1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -32,7 +32,7 @@ describe("любое число, кроме нуля, в степени 0 рав
|
|||
//+ no-beautify
|
||||
...
|
||||
it("ноль в нулевой степени даёт NaN", function() {
|
||||
assert( isNaN(pow(0,0), "0 в степени 0 не NaN");
|
||||
assert( isNaN(pow(0, 0)), "0 в степени 0 не NaN");
|
||||
});
|
||||
...
|
||||
```
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="https://js.cx/test/libs.js"></script>
|
||||
<script src="test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
|
||||
function pow(x, n) {
|
||||
if (n < 0) return NaN;
|
||||
if (Math.round(n) != n) return NaN;
|
||||
if (n == 0 && x == 0) return NaN;
|
||||
|
||||
var result = 1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="https://js.cx/test/libs.js"></script>
|
||||
<script src="test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
|
||||
function pow(x, n) {
|
||||
if (n < 0) return NaN;
|
||||
if (Math.round(n) != n) return NaN;
|
||||
|
||||
var result = 1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
26
1-js/3-writing-js/4-testing/2-pow-test-0/source.view/test.js
Normal file
26
1-js/3-writing-js/4-testing/2-pow-test-0/source.view/test.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
describe("pow", function() {
|
||||
|
||||
describe("возводит x в степень n", function() {
|
||||
|
||||
function makeTest(x) {
|
||||
var expected = x * x * x;
|
||||
it("при возведении " + x + " в степень 3 результат: " + expected, function() {
|
||||
assert.equal(pow(x, 3), expected);
|
||||
});
|
||||
}
|
||||
|
||||
for (var x = 1; x <= 5; x++) {
|
||||
makeTest(x);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it("при возведении в отрицательную степень результат NaN", function() {
|
||||
assert(isNaN(pow(2, -1)), "pow(2, -1) не NaN");
|
||||
});
|
||||
|
||||
it("при возведении в дробную степень результат NaN", function() {
|
||||
assert(isNaN(pow(2, 1.5)), "pow(2, -1.5) не NaN");
|
||||
});
|
||||
|
||||
});
|
|
@ -187,19 +187,19 @@ alert( a - b ); // "1" - "2" = -1
|
|||
|
||||
Бинарный оператор плюс `+` обычно использует числовое преобразование и метод `valueOf`. Как мы уже знаем, если подходящего `valueOf` нет (а его нет у большинства объектов), то используется `toString`, так что в итоге преобразование происходит к строке. Но если есть `valueOf`, то используется `valueOf`. Выше в примере как раз `a + b` это демонстрируют.
|
||||
|
||||
У объектов `Date` есть и `valueOf` и `toString`. Но оператор `+` для `Date` использует именно `toString` (хотя должен бы `valueOf`).
|
||||
У объектов `Date` есть и `valueOf` -- возвращает количество миллисекунд, и `toString` -- возвращает строку с датой.
|
||||
|
||||
...Но оператор `+` для `Date` использует именно `toString` (хотя должен бы `valueOf`).
|
||||
|
||||
Это и есть исключение:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
// бинарный плюс, строчное преобразование
|
||||
// бинарный плюс для даты toString, для остальных объектов valueOf
|
||||
alert( new Date + "" ); // "строка даты"
|
||||
|
||||
// унарный плюс, как и - * /, приводит к числу
|
||||
alert( +new Date ); // число миллисекунд
|
||||
```
|
||||
Других подобных исключений нет.
|
||||
|
||||
Других подобных исключений нет.
|
||||
[/warn]
|
||||
|
||||
[warn header="Как испугать Java-разработчика"]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue