init
This commit is contained in:
parent
06f61d8ce8
commit
f301cb744d
2271 changed files with 103162 additions and 0 deletions
|
@ -0,0 +1,24 @@
|
|||
Ответ: `1`.
|
||||
|
||||
```js
|
||||
//+ run untrusted refresh
|
||||
if ("a" in window) {
|
||||
var a = 1;
|
||||
}
|
||||
alert(a);
|
||||
```
|
||||
|
||||
Посмотрим, почему.
|
||||
|
||||
На стадии подготовки к выполнению, из `var a` создается `window.a`:
|
||||
|
||||
```js
|
||||
// window = {a:undefined}
|
||||
|
||||
if ("a" in window) { // в if видно что window.a уже есть
|
||||
var a = 1; // поэтому эта строка сработает
|
||||
}
|
||||
alert(a);
|
||||
```
|
||||
|
||||
В результате `a` становится `1`.
|
|
@ -0,0 +1,13 @@
|
|||
# Window и переменная
|
||||
|
||||
[importance 5]
|
||||
|
||||
Каков будет результат кода?
|
||||
|
||||
```js
|
||||
if ("a" in window) {
|
||||
var a = 1;
|
||||
}
|
||||
alert(a);
|
||||
```
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
Ответ: **ошибка**.
|
||||
|
||||
Переменной `a` нет, так что условие `"a" in window` не выполнится. В результате на последней строчке - обращение к неопределенной переменной.
|
||||
|
||||
```js
|
||||
//+ run untrusted refresh
|
||||
if ("a" in window) {
|
||||
a = 1;
|
||||
}
|
||||
alert(a); // <-- error!
|
||||
```
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# Window и переменная 2
|
||||
|
||||
[importance 5]
|
||||
|
||||
Каков будет результат (перед `a` нет `var`)?
|
||||
|
||||
```js
|
||||
if ("a" in window) {
|
||||
a = 1;
|
||||
}
|
||||
alert(a);
|
||||
```
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
Ответ: `1`.
|
||||
|
||||
Переменная `a` создается до начала выполнения кода, так что условие `"a" in window` выполнится и сработает `a = 1`.
|
||||
|
||||
```js
|
||||
//+ run untrusted refresh
|
||||
if ("a" in window) {
|
||||
a = 1;
|
||||
}
|
||||
var a;
|
||||
|
||||
alert(a); // 1
|
||||
```
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# Window и переменная 3
|
||||
|
||||
[importance 5]
|
||||
|
||||
Каков будет результат (перед `a` нет `var`, а ниже есть)?
|
||||
|
||||
```js
|
||||
if ("a" in window) {
|
||||
a = 1;
|
||||
}
|
||||
var a;
|
||||
|
||||
alert(a);
|
||||
```
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
Ответ: `5`.
|
||||
|
||||
```js
|
||||
//+ run untrusted
|
||||
var a = 5;
|
||||
|
||||
function a() { }
|
||||
|
||||
alert(a);
|
||||
```
|
||||
|
||||
Чтобы понять, почему -- разберём внимательно как работает этот код.
|
||||
<ol>
|
||||
<li>До начала выполнения создаётся переменная `a` и функция `a`. Стандарт написан так, что функция создаётся первой и переменная ее не перезаписывает. То есть, функция имеет приоритет. Но в данном случае это совершенно неважно, потому что...
|
||||
</li>
|
||||
<li>...После инициализации, когда код начинает выполняться -- срабатывает присваивание `a = 5`, перезаписывая `a`, и уже не важно, что там лежало.</li>
|
||||
<li>Объявление `Function Declaration` на стадии выполнения игнорируется (уже обработано).</li>
|
||||
<li>В результате `alert(a)` выводит 5.</li>
|
||||
</ol>
|
|
@ -0,0 +1,15 @@
|
|||
# Функция и переменная
|
||||
|
||||
[importance 3]
|
||||
|
||||
Каков будет результат кода? Почему?
|
||||
|
||||
```js
|
||||
var a = 5;
|
||||
|
||||
function a() { }
|
||||
|
||||
alert(a);
|
||||
```
|
||||
|
||||
P.S. Это задание -- учебное, на понимание процесса инициализации и выполнения. В реальной жизни мы, конечно же, не будем называть переменную и функцию одинаково.
|
242
01-js/05-functions-closures/01-global-object/article.md
Normal file
242
01-js/05-functions-closures/01-global-object/article.md
Normal file
|
@ -0,0 +1,242 @@
|
|||
# Глобальный объект
|
||||
|
||||
Механизм работы функций и переменных в JavaScript очень отличается от большинства языков.
|
||||
|
||||
Чтобы его понять, мы в этой главе рассмотрим переменные и функции в глобальной области. А в следующей -- пойдём дальше.
|
||||
|
||||
[cut]
|
||||
|
||||
## Глобальный объект
|
||||
|
||||
*Глобальными* называют переменные и функции, которые не находятся внутри какой-то функции. То есть, иными словами, если переменная или функция не находятся внутри конструкции `function`, то они -- "глобальные".
|
||||
|
||||
**В JavaScript все глобальные переменные и функции являются свойствами специального объекта, который называется *"глобальный объект"* (`global object`).**
|
||||
|
||||
В браузере этот объект явно доступен под именем `window`. Объект `window` одновременно является глобальным объектом и содержит ряд свойств и методов для работы с окном браузера, но нас здесь интересует только его роль как глобального объекта.
|
||||
|
||||
В других окружениях, например Node.JS, глобальный объект может быть недоступен в явном виде, но суть происходящего от этого не изменяется, поэтому далее для обозначения глобального объекта мы будем использовать `"window"`.
|
||||
|
||||
**Присваивая или читая глобальную переменную, мы, фактически, работаем со свойствами `window`.**
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run untrusted refresh
|
||||
var a = 5; // объявление var создаёт свойство window.a
|
||||
alert(window.a); // 5
|
||||
```
|
||||
|
||||
Создать переменную можно и явным присваиванием в `window`:
|
||||
|
||||
```js
|
||||
//+ run untrusted refresh
|
||||
window.a = 5;
|
||||
alert(a); // 5
|
||||
```
|
||||
|
||||
## Порядок инициализации
|
||||
|
||||
Выполнение скрипта происходит в две фазы:
|
||||
<ol>
|
||||
<li>На первой фазе происходит инициализация, подготовка к запуску.
|
||||
|
||||
Во время инициализации скрипт сканируется на предмет объявления функций вида [Function Declaration](/function-declaration-expression), а затем -- на предмет объявления переменных `var`. Каждое такое объявление добавляется в `window`.
|
||||
|
||||
**Функции, объявленные как Function Declaration, создаются сразу работающими, а переменные -- равными `undefined`.**
|
||||
</li>
|
||||
<li>На второй фазе -- собственно, выполнение.
|
||||
|
||||
Присваивание (`=`) значений переменных происходит на второй фазе, когда поток выполнения доходит до соответствующей строчки кода.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
В начале кода ниже указано содержание глобального объекта на момент окончания инициализации:
|
||||
|
||||
```js
|
||||
// По окончании инициализации, до выполнения кода:
|
||||
*!*
|
||||
// window = { f: function, a: undefined, g: undefined }
|
||||
*/!*
|
||||
|
||||
var a = 5; // при инициализации даёт: window.a=undefined
|
||||
|
||||
function f(arg) { /*...*/ } // при инициализации даёт: window.f = function
|
||||
|
||||
var g = function(arg) { /*...*/ }; // при инициализации даёт: window.g = undefined
|
||||
```
|
||||
|
||||
Кстати, тот факт, что к началу выполнения кода переменные и функции *уже* содержатся в `window`, можно легко проверить:
|
||||
|
||||
```js
|
||||
//+ run untrusted refresh
|
||||
|
||||
alert("a" in window); // *!*true*/!*, т.к. есть свойство window.a
|
||||
alert(a); // равно *!*undefined*/!*, присваивание будет выполнено далее
|
||||
alert(f); // *!*function ...*/!*, готовая к выполнению функция
|
||||
alert(g); // *!*undefined*/!*, т.к. это переменная, а не Function Declaration
|
||||
|
||||
var a = 5;
|
||||
function f() { /*...*/ }
|
||||
var g = function() { /*...*/ };
|
||||
```
|
||||
|
||||
[smart header="Присвоение переменной без объявления"]
|
||||
В старом стандарте JavaScript переменную можно было создать и без объявления `var`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
a = 5;
|
||||
|
||||
alert(a); // 5
|
||||
```
|
||||
|
||||
Такое присвоение, как и `var a = 5`, создает свойство `window.a = 5`. Отличие от `var a = 5` -- в том, что переменная будет создана не на этапе входа в область видимости, а в момент присвоения.
|
||||
|
||||
Сравните два кода ниже.
|
||||
|
||||
Первый выведет `undefined`, так как переменная была добавлена в `window` на фазе инициализации:
|
||||
|
||||
```js
|
||||
//+ run untrusted refresh
|
||||
*!*
|
||||
alert(a); // undefined
|
||||
*/!*
|
||||
|
||||
var a = 5;
|
||||
```
|
||||
|
||||
Второй код выведет ошибку, так как переменной ещё не существует:
|
||||
|
||||
```js
|
||||
//+ run untrusted refresh
|
||||
*!*
|
||||
alert(a); // error, a is not defined
|
||||
*/!*
|
||||
|
||||
a = 5;
|
||||
```
|
||||
|
||||
**Вообще, рекомендуется всегда объявлять переменные через `var`.**
|
||||
|
||||
В современном стандарте присваивание без `var` вызовет ошибку:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
'use strict';
|
||||
a = 5; // error, a is not defined
|
||||
```
|
||||
|
||||
[/smart]
|
||||
|
||||
[smart header="Конструкции `for, if...` не влияют на видимость переменных"]
|
||||
Фигурные скобки, которые используются в `for, while, if`, в отличие от объявлений функции, имеют "декоративный" характер.
|
||||
|
||||
В JavaScript нет разницы между объявлением вне блока:
|
||||
|
||||
```js
|
||||
*!*var*/!* i;
|
||||
{
|
||||
i = 5;
|
||||
}
|
||||
```
|
||||
|
||||
...И внутри него:
|
||||
|
||||
```js
|
||||
i = 5;
|
||||
{
|
||||
*!*var*/!* i;
|
||||
}
|
||||
```
|
||||
|
||||
**Также нет разницы между объявлением в цикле и вне его:**
|
||||
|
||||
```js
|
||||
//+ run untrusted refresh
|
||||
for (*!*var*/!* i=0; i<5; i++) { }
|
||||
```
|
||||
|
||||
Идентичный по функциональности код:
|
||||
|
||||
```js
|
||||
//+ run untrusted refresh
|
||||
*!*var i;*/!*
|
||||
for (i=0; i<5; i++) { }
|
||||
```
|
||||
|
||||
В обоих случаях переменная будет создана до выполнения цикла, на стадии инициализации, и ее значение будет сохранено после окончания цикла.
|
||||
|
||||
[/smart]
|
||||
|
||||
[smart header="Не важно, где и сколько раз объявлена переменная"]
|
||||
|
||||
Объявлений `var` может быть сколько угодно:
|
||||
|
||||
```js
|
||||
var i = 10;
|
||||
|
||||
for (var i=0; i<20; i++) {
|
||||
...
|
||||
}
|
||||
|
||||
var i = 5;
|
||||
```
|
||||
|
||||
**Все `var` будут обработаны один раз, на фазе инициализации.**
|
||||
|
||||
На фазе исполнения объявления `var` будут проигнорированы: они уже были обработаны. Зато будут выполнены присваивания.
|
||||
[/smart]
|
||||
|
||||
|
||||
[warn header="Ошибки при работе с `window` в IE8-"]
|
||||
|
||||
В старых IE есть две забавные ошибки при работе с переменными в `window`:
|
||||
|
||||
<ol>
|
||||
<li>Переопределение переменной, у которой такое же имя, как и `id` элемента, приведет к ошибке:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<div id="a">...</div>
|
||||
<script>
|
||||
a = 5; // ошибка в IE<9! Правильно будет "var a = 5"
|
||||
alert(a); // никогда не сработает
|
||||
</script>
|
||||
```
|
||||
|
||||
А если сделать через `var`, то всё будет хорошо.
|
||||
|
||||
Это была реклама того, что надо везде ставить `var`.
|
||||
|
||||
</li>
|
||||
<li>Ошибка при рекурсии через функцию-свойство `window`. Следующий код "умрет" в IE<9:
|
||||
|
||||
```html
|
||||
<!--+ run height=0 -->
|
||||
<script>
|
||||
// рекурсия через функцию, явно записанную в window
|
||||
window.recurse = function(times) {
|
||||
if (times !== 0) recurse(times-1);
|
||||
}
|
||||
|
||||
recurse(13);
|
||||
</script>
|
||||
```
|
||||
|
||||
Проблема здесь возникает из-за того, что функция напрямую присвоена в `window.recurse = ...`. Ее не будет при обычном объявлении функции.
|
||||
|
||||
**Этот пример выдаст ошибку только в настоящем IE8!** Не IE9 в режиме эмуляции. Вообще, режим эмуляции позволяет отлавливать где-то 95% несовместимостей и проблем, а для оставшихся 5% вам нужен будет настоящий IE8 в виртуальной машине.
|
||||
</li>
|
||||
</ol>
|
||||
[/warn]
|
||||
|
||||
|
||||
## Итого
|
||||
|
||||
В результате инициализации, к началу выполнения кода:
|
||||
<ol>
|
||||
<li>Функции, объявленные как `Function Declaration`, создаются полностью и готовы к использованию.</li>
|
||||
<li>Переменные объявлены, но равны `undefined`. Присваивания выполнятся позже, когда выполнение дойдет до них.</li>
|
||||
</ol>
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue