И снова Dart

С некоторыми перерывами я перевёл весь свой клиентский код на Dart. Кроме того, я стал использовать Dart для воплощения других своих идей и даже успел отправить пару патчей разработчикам одной из библиотек. В связи с этим делюсь некоторым количеством впечатлений от языка.

  1. Поддержка Dart в продуктах JetBrains просто отличная. Писать на Dart гораздо удобнее, чем на JavaScript, а ведь плагин для JavaScript разрабатывается и улучшается намного дольше. И всё это из-за более строгой структуры языка. Она позволяет IDE лучше просчитывать зависимости и в нужное время дополнять необходимые конструкции. Но не обошлось без ложки дёгтя. Иногда плагин просто перестаёт дополнять что бы то ни было, спасает только перезагрузка среды. И ещё не хватает привычных intentions, вроде склеить два if в один или заменить одинарные кавычки на двойные, но я думаю, что это будет реализовано со временем.

  2. В Dart есть приватные члены класса, но нет зарезервированного слова private. Всё потому, что все идентификаторы, начинающиеся с подчёркивания (_), считаются приватными. И это правильно.

  3. Видно, что язык разрабатывался с учётом опыта создания сложных приложений. Например, я нигде больше не встречал поддержки factory-конструкторов на уровне языка.

    class Singleton {
      static final Singleton _singleton = new Singleton._internal();
      factory Singleton() {
        return _singleton;
      }
      Singleton._internal();
    }
    
    var singleton = new Singleton();

    В этом же примере можно видеть ещё одну особенность Dart: именованные конструкторы. Так как в динамическом языке нельзя сделать перегрузку методов по типам параметров, то для конструкторов авторы решили добавить возможность выбора конструктора по имени. А для остальных случаев вполне хватает именованных необязательных параметров.

  4. В языке довольно много различного синтаксического сахара: тот же cascade operator (..). Крайне удобная штука для работы с DOM и стилями.

    element.style
      ..visibility = 'visible'
      ..display = 'block';
  5. Ещё прекрасно сделана подстановка значений переменных и выражений в строку. Это выглядит частью языка, а не afterthought, как, например, в PHP.

    previousLink.attributes['title'] += ' (${isMac ? '⌥←' : 'Ctrl + ←'})';

    Обращаем внимание на то, что внутри ${...} может быть любое выражение точно в таком же виде, в каком вы бы его увидели и без строки. Кавычки внутри выражения не закрывают строку, и их не нужно экранировать, что вполне логично, но в том же PHP этого нет.

  6. Dart — язык с необязательной типизацией. То есть если хочешь, можно указать тип переменной, а можно просто написать var и не париться. Всё это будет работать до тех пор, пока не попадётся, например, функция с указанным типом параметров. Вот тут-то тип твоей переменной будет проверен. И будет выведена ошибка, если типы не совпадают. Но когда вместо var указываешь реальный тип переменной, то проверки происходят гораздо чаще, буквально в каждом операторе, везде, где может возникнуть несовместимость типов.

    Кроме того, в Dart нет неявного приведения типов. Например, вот этот код выведет сообщение об ошибке потому, что нельзя просто так привести строку к булевому значению:

    String str = '';
    if (!str) {
      // do something
    }

    Но не стоит особо волноваться: о большинстве потенциальных проблем статический анализатор языка (то есть IDE) сообщит заранее.

    Все эти проверки работают в так называемом «checked mode». Чтобы этот режим включить, виртуальная машина должна быть запущена со специальным параметром, например:

    dart --checked main.dart

    Или в случае с сайтом сам браузер нужно запускать так (пример для Linux):

    DART_FLAGS='--checked' ./chrome

    Если этого не сделать, то виртуальная машина будет работать в production-режиме и ни одной дополнительной проверки не будет сделано. Зато выполняться скрипт будет очень быстро! Поэтому можно думать о типах, как об автоматических инструкциях assert в языке в помощь разработчику. Кстати, просто assert тоже присутствует и тоже не работает в production-режиме.

    А ещё в Dart есть Generics. И это позволяет создавать весьма сложные зависимости типов и опять же проверки. К сожалению, Generics можно применить только к классам, может, в будущем добавят поддержку отдельных функций.

    class ParseContext<S extends Iterable> {
      final S source;
    
      // More definitions
    }
  7. Переопределение операторов. Не думаю, что нужно рассказывать про плюсы и минусы этого решения. Все, кто когда-либо сталкивался с ними в любом другом языке, и так о них знают. Динамический Dart может добавить к этому ещё немного проблем. И хотелось бы иметь возможность создания произвольных операторов, как в Haskell. Было бы круто.

  8. Из недостатков можно выделить то, что интерфейсы визуально не отличаются от классов. То есть нет зарезивированного слова interface, вместо него используется тот же class. Но при этом есть слова и extends, и implements. Всё различие заключается в том, что может присутствовать в классе, но не может присутствовать в интерфейсе, и наоборот. К примеру, в интерфейсе может быть factory-конструктор, который будет возвращать реализацию этого интерфейса по-умолчанию. Но в этом случае не получится унаследоваться от такого интерфейса при помощи extends. Компилятор выдаст «The generative constructor expected, but factory found». А это не особо очевидно, особенно когда видишь это сообщение первый раз.

  9. Ещё один минус — размер js-файла на выходе. Пока не у всех есть браузер с нативной поддержкой Dart, и многим придётся загружать сконвертированный код. Ладно, будем честными: браузера пока ни у кого нет, кроме разработчиков, которые на Dart пишут (он называется Dartium, его можно скачать на сайте проекта). Так вот, сгенерированный JavaScript для этого блога весит 207 Кб (сравните с 8 Кб минифицированного Dart-кода). А всё потому, что в js-файл компилятору приходится встраивать всю ту огромную прослойку для поддержки разницы в DOM и событиях между языками.

Я думаю, что Google не оставит своих планов выпустить nextgen язык для Web. Меня лично эти планы вполне устраивают, так как я отлично представляю, чем этот язык будет лучше и удобнее. Как минимум он позволит выбросить весь тот backward-compatibility хлам, который уже давно никто не использует, но который обязан присутствовать в каждом браузере, потому что его могут использовать сайты, созданные 15 лет назад.

И не надо говорить, что всякие надстройки используют те, кто не осилил выучить нативный JavaScript.

P. S. К сожалению, нормальной поддержки подсветки Dart в блоге пока нет, но я надеюсь это в скором времени исправить. Исправил.

Sencha Architect

Случилось мне тут по долгу службы столкнуться с Sencha Architect. Если кто не в курсе — это IDE, разрабатываемая Sencha для облегчения работы с её же фреймворками Sencha Touch и ExtJS. Из заявленных «killer features» в первую очередь выделяются визуальный редактор компонентов и интеллектуальная работа с кодом.

В памяти совсем юных бойцов айтишного фронта уже вероятно нет такой среды разработки, как Borland Delphi, хотя программисты постарше должны её помнить. Так вот, Sencha Architect весьма на неё похожа. Так же можно накликать себе приличный интерфейс и навешать всяких обработчиков событий на всевозможные его элементы. Но на этом сходство заканчивается.

Sencha Architect

Sencha Architect

Delphi, в отличие от Sencha Architect, позволяла сделать два шага в сторону, открыть редактор кода и сделать всё то же самое, только не прикасаясь к мышке. А тут максимум, который тебе доступен, — внутренности функции-обработчика события. Ладно-ладно, можно кликнуть в том же дереве объектов (не в коде!) и добавить произвольную функцию, но её заголовок и текст придётся править отдельно.

Редактор кода

Редактор кода

Конечно же всё это накладывает заметные ограничения на навигацию. Если ты видишь в коде свойство, которое нужно подправить, это совершенно не означает, что вот так сразу возьмёшь его и исправишь. Нет уж, будь добр, найди соответствующий объект в дереве, потом найди свойство в списке свойств этого объекта и уже там пиши своё значение. Возможно это и удобно для разработчика, который видит Sencha Touch первый раз в жизни: всё-таки не нужно долго и упорно ковыряться в документации, выискивая, как пишутся нужные свойства. Но я гораздо быстрее открою IntelliJ IDEA или WebStorm и сделаю всё то же самое, написав пару строк. Да, у меня не будет мгновенного визуального представления, да, мне придётся нажимать F5 каждый раз, чтобы проверить изменения, и да, у меня будет открыта ещё и вкладка с документацией в браузере, но это всё равно будет быстрее, чем работать с подобной системой.

Когда работаешь с IDE, специально заточенной под работу с определённым фреймворком, то ожидаешь, что эта IDE знает фреймворк от и до, но в случае с Sencha Architect это не всегда так. Например, при переименовании идентификатора связанные места не всегда обновляются автоматически, иногда придётся пробежаться ручками. Та же IntelliJ IDEA понимает зависимости между идентификаторами не в пример лучше, что уж говорить про поддержку JavaScript в целом.

За всё это Sencha хочет по 400 долларов с разработчика в год. И в цену не входят лицензии на сами фреймворки.

Итак, выводы: Sencha Architect — неплохая среда для новичков или для быстрого создания набросков. Для более сложных вещей даже Sublime Text может оказаться куда удобнее и продуктивнее.

Dart

Очередной эксперимент. В этот раз я решил переписать весь JavaScript-код этого блога на новомодном Dart. И если результат я не выложу в итоге на рабочий сервер, это всё равно неплохая возможность взглянуть на новый язык. Почему именно Dart? Даже и не знаю. Мне подход с написанием языка с нуля нравится больше, чем попытки исправить недостатки JavaScript с помощью всяких надстроек, как в случае TypeScript или CoffeeScript. Я не говорю, что это плохие языки, просто они пытаются обойтись «малой кровью», а Google, создатель Dart, играет по крупному.

Итак, я обратил внимание на более лаконичный способ записи функций. Ну ведь правда, зачем писать каждый раз function, если можно без этого обойтись.

var sum = (a, b) {
     return a + b;
};

Или даже ещё короче:

var sum = (a, b) => a + b;

Но это мелочи. Гораздо интереснее исправленные области видимости. Например, переменная, объявленная внутри цикла for, не видна вне его. Очень понравилось, как в Dart переделали всю структуру DOM-объектов. Например, у элементов свойство textContent превратилось в text, а childNodes в nodes. Наконец-то attributes — это явный ассоциативный массив, а не NamedNodeMap плюс набор методов вроде getAttribute и setAttribute. И classes — это множество, а не строка. Красота!

События тоже претерпели сильные изменения. Нет больше element.onclick и element.addEventListener (attachEvent в старых IE). Теперь события представляют собой потоки, на которые можно подписываться. Теперь нужно писать:

button.onClick.listen((event) {
    // do something
});

А если нужно, чтобы обработчик сработал только один раз:

button.onClick.first.then((event) {
    // do something
});

Свойство first в примере выше является Future, что по сути то же, что и Promise. Я, кстати, не знаю, в чём разница между Futures и Promises, буду рад, если кто-нибудь объяснит.

Конечно, вся эта красота не приходит просто так. И если текущая сжатая версия скриптов для блога на языке Dart занимает 13 кб (по большей части из-за подключённой библиотеки интернационализации), то она же, но скомпилированная в JavaScript, все 134.

Текущая версия скриптов тут. Комментарии не возбраняются, а очень даже приветствуются.

CSS Masking

Допустим, у нас есть набор иконок png чёрного цвета с прозрачностью. Как с помощью CSS реализовать возможность изменять цвет иконки произвольным образом? Тут нам на помощь придёт свежий стандарт CSS Masking.

Для достижения результата нужно наложить выбранную иконку в виде маски на фон с нужным цветом. Тогда атрибут прозрачности будет перенесён с иконки на фон.

.icon {
    -webkit-mask-image: url(icon.png);
    -webkit-mask-size: 100%; /* Масштабируем маску под нужный размер */
    background-color: #63b4ec;
}

Небольшая демонстрация. Ну ещё много интересного можно найти на html5rocks.

К сожалению, вся эта красота пока поддерживается только браузерами на основе WebKit, поэтому можно использовать в мобильных приложениях, но следует десять раз подумать перед тем, как включать данные свойства на обычном сайте. Лично для меня оно понадобилось, чтобы добавить растровые иконки к Sencha Touch.

JavaScript patterns

Просматривал старые заметки в Evernote и набрел на список паттернов написания кода для JavaScript. Вот они.

Для повышения качества кода и улучшения понимания специфики JavaScript привожу тут весьма вольный перевод первой, не связанной с jQuery, части. Некоторые вещи могут показаться тривиальными, но большая часть программистов почему-то забывает о них, когда они действительно нужны.

Очень надеюсь, что этот пост кому-нибудь пригодится и вы найдёте для себя что-нибудь новое.

1. Объявление функций

Создание анонимных функций и присваивание их переменным.

Плохо

function getData() {
}

Хорошо

var getData = function () {
};

Преимущества:

  1. Улучшает понимание «функций как объектов».
  2. Навязывает хорошую привычку ставить точки с запятой.
  3. Нет навязанного предыдущим опытом представления о функциях и областях видимости.

Именованное функциональное выражение

var getData = function getData () {
};

Преимущества:

  1. Даёт отладчику определённое имя функции. Это упрощает изучение стека вызовов.
  2. Позволяет делать рекурсивные вызовы: getData может вызывать саму себя по имени.

Недостатки: не работает в IE, и CoffeeScript не понимает подобные выражения (https://github.com/jashkenas/coffee-script/issues/366).

Именованное функциональное выражение + «F»

var getData = function getDataF () {
};

Преимущества:

  1. Избавление от (anonymous function) в стеке вызовов.
  2. Возможность рекурсивного вызова при использовании имя + «F».
  3. Работает в IE (по крайней мере до тех пор пока нет коллизии имён, как описано здесь: https://github.com/jashkenas/coffee-script/issues/366#issuecomment-242134).

Ссылки

  1. http://ejohn.org/blog/javascript-as-a-first-language/
  2. http://kangax.github.com/nfe/

2. Условия

Cпособы использования if-else.

Стандартный способ

if (type === 'foo' || type === 'bar') {
}

Альтернативный способ 1: регулярное выражение

if (/^(foo|bar)$/.test(type)) {
}

Альтернативный способ 2: поиск в объекте

Этот способ будет короче, когда в условии менее пяти элементов.

if (({foo:1, bar:1})[type]) {
}

Альтернативный способ 3: подход как в двоичном поиске

Этот подход лучше применять, когда нужно проверять диапазоны значений.

До:

if (value == 0) {
    return result0;
} else if (value == 1) {
    return result1;
} else if (value == 2) {
    return result2;
} else if (value == 3) {
    return result3;
} else if (value == 4) {
    return result4;
} else if (value == 5) {
    return result5;
} else if (value == 6) {
    return result6;
} else if (value == 7) {
    return result7;
} else if (value == 8) {
    return result8;
} else if (value == 9) {
    return result9;
} else {
    return result10;
}

После:

if (value < 6) {
    if (value < 3) {
        if (value == 0) {
            return result0;
        } else if (value == 1) {
            return result1;
        } else {
            return result2;
        }
    } else {
        if (value == 3) {
            return result3;
        } else if (value == 4) {
            return result4;
        } else {
            return result5;
        }
    }
} else {
    if (value < 8) {
        if (value == 6) {
            return result6;
        } else {
            return result7;
        }
    } else {
        if (value == 8) {
            return result8;
        } else if (value == 9) {
            return result9;
        } else {
            return result10;
        }
    }
}

Альтернативный способ 4: Таблицы поиска

Таблицы поиска наиболее полезны, когда есть соответствие между ключом и значением.

До:

if (value == 0) {
    return result0;
} else if (value == 1) {
    return result1;
} else if (value == 2) {
    return result2;
}

После:

// Определяем массив результатов.
var results = [result0, result1, result2];
// Возвращаем правильный результат.
return results[value];

Альтернативный способ 5: только логические операторы

Более короткий способ записи операторов.

var
    type = 'foo',
    type2 = 'bar',
    result = 0;

type == 'foo' && result++;
console.log(result); // 1
!type == 'foo' || result++;
console.log(result); // 2
type == 'foo' && type2 == 'bar' && result++;
console.log(result); // 3
type == 'foo' && type2 == 'bar' && result == 3 && (result=0);
// Скобки нужны, чтобы избежать ошибки с неверной левой частью у присваивания
console.log(result); // 0
type == 'OOF' || result++; // Эквивалентно type != 'OOF' && result++;
console.log(result); // 1

Ссылки

http://paulirish.com/2009/perf/.

Paul Irish отмечает, что первый вариант (стандартный способ) не слишком походит, когда требуется уменьшить размер исходного кода, например, для кода-закладки (bookmarklet). Стандартный способ для небольшого количества условий обычно в цикле работает быстрее, чем регулярное выражение (альтернативный способ 1), и быстрее поиска по объекту (альтернативный способ 2). Скорость выравнивается примерно на десяти условиях. Смотрите http://jsperf.com/if-this-or-that.

3. Доступ к глобальному объекту

Доступ к глобальному объекту без указания идентификатора window напрямую. Должно работать в ES3, ES5 и ES5-strict.

var global = (function () {
    return this || (1, eval)('this');
}());

Тесты: http://jsperf.com/globalx.

4. Одно объявление var

Нужно использовать одно определение var на функцию.

Преимущества:

  1. Единственное место в коде, где будут объявлены все локальные переменные, требуемые для функции.
  2. Защищает от логических ошибок, когда переменная используется до того, как она объявлена.
  3. Напоминает о том, что нужно объявлять переменные, и таким образом уменьшает количество глобальных переменных.
  4. Меньше букв (чтобы набирать и передавать).
function func() {
    var a = 1
        , b = 2
        , sum = a + b
        , myobject = {}
        , i
        , j;

    // Тело функции...
}

function updateElement() {
    var el = document.getElementById("result")
        , style = el.style;

    // Сделать что-нибудь с el и style...
}

От себя добавлю, что я не пользуюсь этим паттерном. Мне кажется, что современные IDE вроде WebStore могут позаботиться о локальных переменных вместо тебя, главное обращать внимание на их замечания.

5. Hoisting (подъём)

Объявление var где-либо в функции действует так, как если бы переменные были объявлены в самом верху.

Неправильно:

myname = "global"; // глобальная переменная
function func() {
    alert(myname); // "undefined"
    var myname = "local";
    alert(myname); // "local"
}
func();

Этот кусок кода будет себя вести будто написан так:

myname = "global"; // глобальная переменная
function func() {
    var myname; // то же, что и var myname = undefined;
    alert(myname); // "undefined"
    myname = "local";
    alert(myname); // "local"
}
func();

6. Оптимизация циклов for

Стандартный вариант

for (var i = 0; i < myarray.length; i++) {
    // сделать что-нибудь с myarray[i]
}

Оптимизация 1

Кешировать размер массива, используя max.

for (var i = 0, max = myarray.length; i < max; i++) {
    // сделать что-нибудь с myarray[i]
}

Оптимизация 2

Использовать только одно объявление var для соответствия предыдущим советам.

Замечание: недостаток в том, что станет немного сложнее копировать циклы целиком во время рефакторинга.

var i = 0,
    max,
    myarray = [];

for (i = 0, max = myarray.length; i < max; i++) {
    // сделать что-нибудь с myarray[i]
}

Оптимизация 3

Заменить i++ на i = i + 1 или i += 1, чтобы избежать излишней сложности.

var i = 0,
    max,
    myarray = [];

for (i = 0, max = myarray.length; i < max; i += 1) {
    // сделать что-нибудь с myarray[i]
}

Предпочтительный вариант 1

var i, myarray = [];
for (i = myarray.length; i--;) {
    // сделать что-нибудь с myarray[i]
}

Предпочтительный вариант 2

var myarray = [],
    i = myarray.length;
while (i--) {
    // сделать что-нибудь с myarray[i]
}

7. Оптимизация циклов for-in

Объект:

var man = {
    hands:2,
    legs:2,
    heads:1
};

Где-то в коде был добавлен метод ко всем объектам:

if (typeof Object.prototype.clone === 'undefined') {
    Object.prototype.clone = function () {
    };
}

Неправильно

Цикл for-in без проверки hasOwnProperty.

for (var i in man) {
    console.log(i, ":", man[i]);
}

Результат в консоли:

hands : 2
legs : 2
heads : 1
clone: function()

Предпочтительный вариант 1

for (var i in man) {
    if (man.hasOwnProperty(i)) { // фильтр
        console.log(i, ":", man[i]);
    }
}

Результат в консоли:

hands : 2
legs : 2
heads : 1

Предпочтительный вариант 2

Преимущество этого варианта в том, что можно избежать коллизий имён, если в объекте man переопределено свойство hasOwnProperty.

for (var i in man) {
    if (Object.prototype.hasOwnProperty.call(man, i)) { // фильтр
        console.log(i, ":", man[i]);
    }
}

Предпочтительный вариант 3

Используем локальную переменную, чтобы закешировать Object.prototype.hasOwnProperty.

var i,
    hasOwn = Object.prototype.hasOwnProperty;
for (i in man) {
    if (hasOwn.call(man, i)) { // фильтр
        console.log(i, ":", man[i]);
    }
}

8. (Не) изменение встроенных прототипов

Этот паттерн может сильно навредить поддерживаемости кода, потому что сделает его менее предсказуемым. Но можно сделать исключение, когда следующие условия выполняются:

  1. Ожидается, что будущие версии ECMAScript или JavaScript добавят эту функциональность. Например, можно добавить методы, описанные в ECMAScript 5, пока не все браузеры реализовали эту функциональность. В этом случае вы просто определяете необходимые методы заранее.
  2. Вы убеждаетесь, что ваше новое свойство ещё не существует. Ведь оно может являться частью JavaScript-движка в одном из браузеров или, возможно, было уже где-то добавлено в код.
  3. Вы задокументируете и обсудите с командой это изменение.
if (typeof Object.prototype.myMethod !== "function") {
    Object.prototype.myMethod = function () {
        // реализация...
    };
}

9. switch

Улучшение читаемости и надёжности операторов switch.

Правила:

  1. Выравнивайте каждый case с switch (исключение к правилам отступов после фигурной скобки)
  2. Делайте отступы внутри каждого case.
  3. Заканчивайте каждый case явным break;.
  4. Избегайте fall-through (случай, когда вы специально убираете break). Если вы совершенно уверены, что fall-through — наилучшее решение, убедитесь, что задокументировали подобные случаи. Они могут выглядеть как ошибки для тех, кто будет читать код впоследствии.
  5. Пишите default в конце switch, чтобы конструкция возвращала нормальный результат даже если ни один из case не сработал.
var inspect_me = 0,
    result = '';
switch (inspect_me) {
case 0:
    result = "zero";
    break;
case 1:
    result = "one";
    break;
default:
    result = "unknown";
}

10. Неявное приведение типов

Нужно избегать неявного приведения типов.

var zero = 0;

Плохо

JavaScript неявно приводит типы переменных, когда сравнивает их. Поэтому сравнения вроде false == 0 или "" == 0 возвращают true.

if (zero == false) {
    // Этот блок выполнится...
}

Хорошо

Чтобы избежать непонятных ситуаций, связанных с неявным приведением типов, всегда используйте операторы === и !==. Эти операторы сравнивают не только значения, но и типы.

if (zero === false) {
    // Не выполнится, потому что zero равно 0, а не false
}

Замечание: есть и другое мнение, заключающееся в том, что использование === является излишним, когда == достаточно. Например, когда вы используете typeof, вы знаете, что оно возвращает строку, поэтому нет необходимости в стогом сравнении. Однако JSLint требует строгого равенства. Это делает код более последовательным и уменьшает умственное усилие, требуемое для чтения кода: это == специально поставили, или забыли поставить ===.

11. Избегайте eval

Плохо 1

var property = "name";
alert(eval("obj." + property));

Хорошо 1

var property = "name";
alert(obj[property]);

Плохо 2

Важно понимать, что передача строк в setInterval(), setTimeout() и конструктор Function() в большей части схожи с использованием eval, и, следовательно, нужно этого избегать.

setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);

Хорошо 2

setTimeout(myFunc, 1000);
setTimeout(function () {
    myFunc(1, 2, 3);
}, 1000);
setTimeout(myFunc, 1000, 1, 2, 3); // в некоторых браузерах (т.е. не в IE)

12. Конвертирование чисел с помощью parseInt

Нужно использовать второй параметр — основание.

Вариант 1

Если опустить в этом примере второй параметр (написать parseInt(year)), то в результате получится 0. Это потому что «09» предполагает восьмеричное число, как если бы вы выполняли parseInt(year, 8), а «09» не является допустимым числом по основанию 8.

var month = "06",
    year = "09";
month = parseInt(month, 10);
year = parseInt(year, 10);

Вариант 2

Если вы ожидаете данные вроде «08 hello», то parseInt() вернёт число, в то время как остальные варианты вернут NaN.

+"08" // Результат: 8
Number("08") // 8

13. Глобальные переменные

Глобальные переменные — это переменные, объявленные вне всех функций, или просто использованные без объявления.

myglobal = "hello"; // плохо
console.log(myglobal); // "hello"
console.log(window.myglobal); // "hello"
console.log(window["myglobal"]); // "hello"
console.log(this.myglobal); // "hello"

14. Проблемы с глобальными переменными

Плохо 1

function sum(x, y) {
    // Неявная глобальная переменная
    result = x + y;
    return result;
}

Хорошо 1

function sum(x, y) {
    // Переменная, объявленная внутри функции, недоступна вне ее.
    var result = x + y;
    return result;
}

Плохо 2

function foo() {
    var a = b = 0;
    // ...
}

Этот код будет вести себя так, как если бы вы написали:

var a = (b = 0);

Хорошо 2

function foo() {
    var a, b;
    // ...
    a = b = 0; // обе переменные локальные
}
← СтаршеМоложе →