javascript

Кодировка JSON

Я всегда полагал, что кодировка данных в формате JSON («джейсон») должна быть ЮТФ-8. Оказывается в стандарте указано только, что она должна быть Юникодом. Более того, в стандарте указаны способы определения разных типов кодировок.

Так как первые два символа данных в формате «джейсон» всегда будут в первой половине таблицы ASCII (в самом деле, как не крути, первый символ — «{» или «[», а далее кавычка, буква «f» («false»), «n» («null»), «t» («true») или цифра), то легко угадать в какой именно кодировке встретился Юникод:

00 00 00 xx  UTF-32BE
           00 xx 00 xx  UTF-16BE
           xx 00 00 00  UTF-32LE
           xx 00 xx 00  UTF-16LE
           xx xx xx xx  UTF-8

Как видите каких-либо маркеров для ЮТФ-8 не выделено, то есть во всех остальных случаях считается, что кодировка ЮТФ-8. Я посмотрел встроенные парсеры ПХП и Пайтона, они ни с какими другими кодировками Юникода, помимо ЮТФ-8, по всей видимости, не работают, то есть нарушают стандарт.

Оставить комментарий

Комментарии в формате JSON

Наткнулся случайно на ссылку, где автора очень изящно решает проблему комментариев в формате «джейсон». Это лучшее решение, которое я только видел (обычно либо выделяют специальный ключ, либо расширяют формат). Только поглядите:

{
  "api_host" : "The hostname of your API server. You may also specify the port.",
  "api_host" : "hodorhodor.com",

  "retry_interval" : "The interval in seconds between retrying failed API calls",
  "retry_interval" : 10,

  "auth_token" : "The authentication token. It is available in your developer dashboard under 'Settings'",
  "auth_token" : "5ad0eb93697215bc0d48a7b69aa6fb8b",

  "favorite_numbers": "An array containing my all-time favorite numbers",
  "favorite_numbers": [19, 13, 53]
}

Он полностью синтаксически верен и разбирается правильным образом во всех языках, по всей видимости.

38 комментариев

Короткий способ определить есть ли поддержка dataURI

Придумал короткий способ определить поддержку data URI в браузере:

<script src="data:text/javascript,self.dataURI=1"></script>

Потом просто проверяете у window свойство dataURI, если оно есть, то есть и поддержка. Способ не работает в IE8 и IE9 — тот и другой не умеют загружать Джаваскрипт этим образом. Собственно, меня это не волнует — мне как раз и нужно определить, что Джаваскрипт так работает, но если вам нужно не это, то должен работать вот такой способ:

<link rel="stylesheet" href="data:text/css,html{font-size:99px}">

То есть устанавливаем у тега HTML какое-нибудь свойство, которое потом в BODY перекроем, а позже, чтобы убедиться, что поддержка дата-ури есть, смоторим что вышло (тут я при помощи jQuery это делаю):

/99/.test($('html').css('font')); // true, если есть поддержка dataURI

Оставить комментарий

Странное с Флешем

Я с «Флешем» в последние годы не сталкивался, но и прежде подобного поведения не наблюдал. В общем, решил я вчера обновить в нашем продукте библиотеку ВебКамДжиЭс, она у нас для получения фотографии с камеры используется. Прежняя была 0.9, а тут я случайно обнаружил, что релиз вышел.

В прежней изображение с камеры получалось через Флеш, а релизная умеет использовать АПИ браузера, если таковое имеется. Правда, в «Сафари», которым я пользуюсь, этого АПИ нет и всё равно используется Флеш. Вот тут-то и столкнулся с трудностями — прежняя версия работала прекрасно, а новая отказывалась — в окне Флеша изображение с камеры есть, но ДжаваСкрипт был уверен, что всё плохо и, самое главное, не мог достучаться до Флеша.

В принципе, можно было откатиться на предыдущую версию, но смущало, что на демо-сайте ВебКама в «Сафари» всё работало нормально. Я попробовал несколько вариантов, прежде чем мне пришла в голову идея посмотреть заголовки запросов. И заметил разницу, показавшуюся несущественной, но всё-таки я решил попробовать её устранить — mime type различался.

У нас сервер отдавал Флеш с типом «application/x-shockwave-flash», а в демо-примере был «binary/octet-stream». Стоило поменять этот заголовок, и всё заработало.

Беглое гугление света не пролило, но если столкнётесь с похожей проблемой, имейте ввиду, возможное решение — смена типа для отдаваемого флеш-ролика.

Оставить комментарий

Короткий способ определять IE

А какой сейчас самый короткий способ определять Эксплорер? Я понимаю, что привязываться к браузеру нехорошо, но у меня конкретная задача, которую никак не обойти — нужно проиграть звук в браузере (PCM wave), а ИЕ на запрос «сможешь проиграть?» к тегу AUDIO, говорит maybe. Т. е. давайте сюда свой звук, может и смогу. Я-то знаю, что не сможет — аудиотег в этом браузере «вавки» не играет, нужен EMBED.

Все короткие способы, которые раньше работали (условная компиляция, сравнение вертикального пробела с буквой v, хитрый хак с массивом и прочее) в 11-й версии работать перестали. Даже строку агента поменяли — там теперь нет MSIE.

В общем, я пока остановился на такой версии:

if (top.VBArray) {
    // IE
} else {
    // остальные браузеры
}

Но буду признателен за что-то более короткое или очевидное.

8 комментариев

Почему у null в JS тип «object»?

На «Хабре» вышла статья, проливающая свет на загадку — почему же у null в Джаваскрипте тип «объект». Причём, если попытаться этому «объекту» добавить какое-то свойство, то браузер воспротивится: скажет, что null объектом не является.

Null как объект (13.48КиБ)

Вообще, я этому особого значения не придавал. Ну да, примитивное значение, имеет тип объект, хотя по факту им не является, смысла я в этом не вижу, это мешает, но поменять это нельзя.

Оказывается, это баг. Точнее даже не баг, а просто «так получилось». В ранних интерпретаторах Джаваскрипта тип хранился в первых трёх битах значения. Для типа «а это у нас null» значения не хватило, ему дали тип «объект», а под значение выделили нулевой указатель. Ну typeof, особо не заморачиваясь, выдавала object.

К сожалению, программисты поленились сделать дополнительную проверку. Можно было при наличии нулевого указателя попросить typeof выводить «null» и дело в шляпе. Сейчас, как оказалось, куча кода завязано на это поведение и ломать уже ничего никто не будет.

9 комментариев

«Яндекс.Фотки» и Ретина

Мне нравятся «Яндекс.Фотки». Не за архаичный дизайн и вёрстку таблицами, конечно, а за отсутствие ненужных мне сервисов и безразмерное (действительно безразмерное) хранилище.

Не нравится только, что я на Ретине не могу выдеть свои фотографии в том качестве, в каком мог бы. Я сейчас болею, кашляю, чтобы никого не будить, ушёл на кухню и сделал userjs для браузера «Сафари», который меняет дизайн «Фоток» и просмотр фотографий так, чтобы им было приятнее пользовать на Ретине.

Может ещё кому-нибудь пригодится:

// ==UserScript==
// @name      Retina Yandex Fotki
// @include   http://fotki.yandex.ru/*
// ==/UserScript==
(function () {
    function $(sel, attrs) {
        var os = typeof sel == "object" ? [sel] : document.querySelectorAll(sel);
        for (var e = 0, l = os.length; e < l; e++) {
            var o = os[e];
            for (var i in attrs) {
                if (attrs.hasOwnProperty(i)) {
                    if (typeof attrs[i] == 'number') {
                        attrs[i] *= o[i] || document.defaultView.getComputedStyle(o, null)[i];
                    }
                    o[i] = o.style[i] = attrs[i];
                }
            }
        }
        return os[0];
    }
    var img = document.querySelector(".js-img-link img");
    if (img) {
        $(".js-img-link img", {
            marginLeft: 1280 - img.width * 2 + 'px',
            zoom: '.5',
        });
        $(".b-preview__size_xxxl", {
            width: Math.max(640, img.width / 2) + 'px',
            height: img.height / 2 + 'px',
            overflow: 'hidden',
        });
    }
    $(".b-head-logo__img", {
        src: 'http://yandex.st/morda-logo/i/logo.svg',
        width: '89px',
        height: '60px',
        marginBottom: '-30px',
        position: 'relative',
        zIndex: '-1',
    });
    $(".b-head-menu .an-upload", {
        width: '80px',
        height: '17px',
    });
    var img = $(":-webkit-any(.user100, .b-userpic-small) img", {
        width: '40px',
        height: '40px',
        float: 'left',
    });

    if (img && img.src.substr(-6) == '-small') {
        img.src = img.src.substr(0, 84) + 'normal';
        $(img, {
            width: '20px',
            height: '20px',
        });
    }
    $(".b-round", {
        display: 'none',
    });
    $(":-webkit-any(.b-preview, .b-foto-actions, .b-foto-listing-i)", {
        borderRadius: '0px',
        webkitBorderRadius: '0px',
        backgroundColor: 'inherit',
    });
    var imgs = document.getElementsByTagName('img');
    for (var i = 0, l = imgs.length; i < l; i++) {
        imgs[i].src = imgs[i].src.replace(/S$/, 'M').replace(/XM$/, 'S').replace(/XS$/, 'S');
    }
})();

Сложнее всего было что-то сделать с тем, что часть атрибутов элементов на экране рассчитывается «Фотками» через ДжаваСкрипт, минимум в одном месте — показе большого фото, пришлось серьёзно подумать.

4 комментария

Как узнать, что пользователь печатает веб-страницу на принтере?

У нас тут задача в процессе производства продукта появилась — занести в лог, что пользователь сделал попытку печати чего-либо в браузере. Есть событие onbeforeprint, но оно есть только в Эксплорере и Файерфоксе.

Придумал такое решение — перед печатью браузер накладывает стиль CSS для печати, если он есть, этим можно воспользоваться:

@media print {
    body {
        background: url(http://example.org/user-tries-to-print.php);
    }
}

Браузер попытается загрузить фоновое изображение (и загружает, я попробовал), выполнится серверный скрипт и положит в логи то, что нужно.

15 комментариев

JavaScript, совмещённый с Brainf*ck: «hello friends» ★

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

Я давно уже заметил, что это сильно похоже на язык Брейнфак. Давно хотелось попробовать смешать эти два языка в одном листинге. Со стороны Брейнфака всё довольно гладко — надо только балансировать квадратные скобки (но Джаваскрипт требует того же) и избегать вечных циклов (тут уже сложнее, так как листинги на запутанном Джаваскрипте часто требуют пустых циклов), незнакомые символы этот язык пропускает. В Джаваскрипте синтаксис построже, там нужно было помучаться больше.

Получилась следующая программа (используется расцветка для Брейнфака, чтобы было видно какие части программы он пропускает):

($=!{}[/-/+/\+@+@+!+/)-!{$:/>/+/!>/+/</}+
(_=!-{}-$+/>/)-~(/</)+
(_$=-$-{}-$+/>/)[$$=-(~-~$+~-~$-!+{$:/!</})+
_[$$+=~-~{$:/]/}+
_$-~{}+
_$[$$+~{_:-$>/]/}+
$-$$]

Оба интерпретатора выполняют её с разным результатом, Брейнфак выводит «hello», а Джаваскрипт — «friends»:

bolk@Bolk ~$ ./brainfuck <<<'($=!{}+[/-/]+/\+@+@+!+/)[-!{$:/>/+/!+>/+/<</}]+(_=!-{}+[-$]+/>/)[-~(/>[->+>+<<]>>[-<+<+>>]<<</)]+(_$=[-$][-{}]+[-$]+/>>/)
[$$=-(~-~$+~-~$-!+{$:/!+>+>+>+>+>+<<<<</})]+_[$$+=~-~{$:/[->++++>+>++++++++>++++++++>+++++++++++[<]]/}]+_$[-~{}]+_$[$$+~{_:-$>/[.>]/}]+$[-+-$$]'

hello

bolk@Bolk ~$ v8 -e 'print(($=!{}+[/-/]+/+@+@+!+/)[-!{$:/>/+/!+>/+/<</}]+(_=!-{}+[-$]+/>/)[-~(/>[->+>+<<]>>[-<+<+>>]<<</)]+(_$=[-$][-{}]+[-$]+/>>/)
[$$=-(~-~$+~-~$-!+{$:/!+>+>+>+>+>+<<<<</})]+_[$$+=~-~{$:/[->++++>+>++++++++>++++++++>+++++++++++[<]]/}]+_$[-~{}]+_$[$$+~{_:-$>/[.>]/}]+$[-+-$$])'

friends

Повозиться пришлось прилично, если честно, некоторые конструкции ДжЭс делит с Брейфаком (большинство квадратных скобок), а кое-где торчат чистые инструкции Брейнфака, засунутые в те места программы, где они не могут помешать своему соседу. Пустые циклы, которые образуются квадратными скобками я старался сводить к операции зануления ячейки („[-]“), сильно мешали плюсы, используемые в Джаваскрипте для объединения букв — они увеличивали содержимое ячейки в Брейнфаке на единицу, это приходилось учитывать.

Слово «friends» я выбрал потому что его легко записать:

"false"[0]+"true"[1]+"undefined"[5]+"true"[3]+"undefined"[1]+"undefined"[2]+"false"[3]

Принцип я когда-то более-менее подробно разбирал.

3 комментария

Юникодные часы

В Юникоде есть несколько символов часов (не знаю, отобразятся ли они у вас, вот они): «🕐🕜🕑🕝🕒🕞🕓🕟🕔🕠🕕🕡🕖🕢🕗🕣🕘🕤🕙🕥🕚🕦🕛🕧.»

Тут, как видите, шаг полчаса. Я вчера за завтраком сделал Юникодные часы, которые «ходят» поворотом символа (через ЦСС-трансформации). Часовая стрелка очень некрасиво скачет (я только для Вебкита делал и испытывал только в Сафари), потому что мне приходится вращать символ, чтобы минутная стрелка двигалась, а потом переходить на следующий символ. В общем, просто разминка для пальцев.

Юникодные часы (3.72КиБ)

Рассказать я хотел не об этом. Я и раньше встречался с тем, что часть Юникода не воспринимается многими программами нормально, особенно что касается раздела «Эмодзи, например „Яндекс.Фотки“ не так давно порадовали меня следующим (стоило переименовать картинку, всё стало в порядке):

«Яндекс.Фотки» (52.51КиБ)

Редактор „Сублиме Текст 2“ часто падает, если в тексте программы встречаются эти символы, а вот „Сафари“ удивил больше всех. Он их, вроде бы, понимает, но как-то не до конца:

"🕐".length; // 2
'🕐'[0]; // строка со «сломанным» символом
escape('🕐'); // "%uD83D%uDD50"

То есть, он считает символы Эмодзи не одним символом, а двумя. Такой вот баг. Мне пришлось в коде на ДжаваСкрипте это учитывать.

Дополнение: в комментариях подсказывают, что вроде так и задумано. Так как этим символы представлены четырьмя байтами (суррогатной парой), ДжаваСкрипт, который не умеет работать с такой парой как с одним символом, разделяет его на два.

6 комментариев

Скрипт для перевода даты из старого стиля в новый

Часто перевожу даты из  календаря в григорианский (со старого стиля на новый), так как занимаюсь исследованием своих предков. Устал использовать для этого всякие веб-сервисы, написал по-быстрому JS, который переводит одну дату в другую, надо будет оформить как виджет или скриптлет и прицепить к своему браузеру.

function Jul2Greg(day, month, year) {
    // «сахар» для создания даты
    var d = function(day, month, year) {
        return new Date(year, month - 1, day, 12, 12, 12);
    }

    var date = d(day, month, year);

    // коррекция дней исходя из года
    var correction = date <= d(28, 2, 1700) ? 10 : 0 | (year / 100 - 17 + 11);

    // корректируем левый конец диапазона
    if (year % 100 == 0 && month < 3) {
        correction--;
    }

    return d(day + correction, month, year);
}

4 комментария

Brainf★ck в highlight.js

Я как-то писал, что ко Дню программиста сделал подсветку синтаксиса языка Brainfuck для библиотеки highlight.js.

Оказывается, я пропустил выход версии 7.3, куда вошла моя подсветка, теперь Brainfuck входит в список поддерживаемых языков.

6 комментариев

Побочный эффект оператора «delete» в JavaScript

Как говорится «век живи, век учись, дураком помрёшь»:

Объяснение (по крайней мере для Хрома) очень простое: когда вы делаете delete testedObject[i].obj, V8 нормализует объект testedObject[i] — трансформирует его из быстрого компактного представления в медленное и раздутое представление на основе словаря, который еще и выделяется с запасом по размеру. При этом V8 не замечает, что после удаления в словаре будет пусто — и словарь (800 байтов) остается болтаться в воздухе. И так для каждого из ваших объектов.

Из коментария к статье на «Хабре», которая описывает странный эффект — при использовании оператора delete что-то продолжает занимать память.

2 комментария

ICC-профили в GIF

Формат ГИФ почему-то окружён каким-то нереальным количеством мифов, хотя до недавнего времени он был очень популярен, а для анимации до сих пор остаётся единственным универсальным форматом.

Например, считается, что анимация появилась только в версии 89a (неправда, она была уже в версии 87a, в 89а её возможности были расширены), что ГИФ может использовать не более 256 цветов (не совсем так — не более 256 цветов на кадр, кадры могут накладываться друг на друга, давая любое отображаемое компьютером количество цветов).

Другое распространённое заблуждение — что ГИФ не поддерживает цветовые профили (ICC).

В самом деле, если посмотреть спецификацию формата, то слово «ICC» там не найдётся, но не найдётся там и слова «loop» (здесь: количество повторов), тем не менее, вы видели анимации, где указано определённое количество повторений и браузеры как-то это понимают.

Дело в том, что формат ГИФ поддерживает расширения. Расширения в спецификацию формата не входят.

Упомянутое количество повторов анимации задаётся в расширении, которое было когда-то предложено фирмой «Нетскейп» и потому имеет заголовок «NETSCAPE2.0», у расширения, которое содержит цветовой профиль заголовок другой — «ICCRGBG1012». ГИФ всегда разбит на блоки, и блок расширения (любого) помечен специальным флагом; заголовок расширения позволяет просмотрщику (в нашем случае — браузеру) решить с каким из расширений формата он имеет дело.

Вот начало файла ГИФ с внедрённым профилем:

00000000 47 49 46 38 39 61 01 00 01 00 f0 00 00 fe 00 02 |GIF89a……….|
00000010 00 00 00 21 f9 04 00 00 00 00 00 21 ff 0b 49 43 |…!…….!..IC|
00000020 43 52 47 42 47 31 30 31 32 ff 00 00 03 f0 55 43 |CRGBG1012…..UC|
00000030 43 4d 02 40 00 00 6d 6e 74 72 52 47 42 20 58 59 |CM.@..mntrRGB XY|

Пока не все браузеры умеют поддерживать цветовые профили ГИФ (как и профили любого другого формата), так ФФ не научился делать это до сих пор, а «Опера» научится это делать в новой версии 12.50.

Проверить поддерживает ли браузер цветовые профили можно и через Джаваскрипт. Принцип такой: я загружаю ГИФ-изображение 1×1 с цветовым профилем в тег CANVAS, потом проверяю цвет точки, он должен быть определённого цвета. Проверить поддерживает ли ваш браузер цветовой профиль четвёртой версии ГИФ можно у меня на сайте, в разделе «Храню».

7 комментариев

Приватный режим в Сафари под iOS

Честно сказать, не знаю для чего это может быть нужно, но это одна из вещей, которая если понадобится, то потом не найдёшь. Джаваскрипт, который позволяет определить запущен ли мобильный Сафари (под Айос) в приватном режиме:

function isPrivateMode() {
    var s = window.sessionStorage, key = '';

    try {
        s.setItem(key, '');
        s.removeItem(key);
    } catch (e) {
        if (e.code === DOMException.QUOTA_EXCEEDED_ERR && !s.length) {
            return true;
        } else {
            throw e;
        }
    }
    return false;
}

// приватный режим включен?
alert(isPrivateMode());

Оригинальный код основан на следующем факте — в приватном режиме мобильный Сафари запрещает доступ к sessionStorage. Я только обернул скрипт в функцию и переименовал переменные.

2 комментария

Замыкания и Джаваскрипт

Все знают, что зона видимости в JS создаётся только внутри функций (если не брать в расчёт конструкцию «let» из JS 1.7):

var v = 5;

!(function() {
    var v = 6;
})();

alert(v); // будет 5

Я тут подумал, что ведь есть и второй способ (я как-то уже о нём упоминал, и хотя внутри происходят немного другие вещи, внешне выглядит похоже), который даже немного похож на вышеупомянутую конструкцию «let», вот сравните:

var x = 1, y = 0;

// конструкция «let», JS 1.7+
let (x = x+10, y = 5) {
    alert(x+y); // 16
}
alert(x+y); // 1

// конструкция «with»
with ({ x: x+10, y: 5 }) {
    alert(x+y); // 16
}
alert(x+y); // 1

Не так уж и много отличий, только выражением «with» быть не умеет, но можно было бы и научить.

7 комментариев

Как определить модель iPad через JavaScript?

На «Хабре» наткнулся на любопытный код на JS, который определяет с каким Айпадом мы имеем дело — первым, вторым или третьим. Третий ото всех остальных отличить просто — у него в свойстве «devicePixelRatio» значение „2“ записано. А первый от второго как?

Оказывается способ есть — надо в событии «devicemotion» проверить есть ли у устройства акселерометр. Не знаю для чего это всё может пригодиться (разве что отключать какие-то ресурсоёмкие спецэффекты), но знать о том, что такая возможность проверки есть, не помешает.

Я немного переписал код, сделал из него функцию, чуть упростил и навёл порядок. Получился вот такой код:

// функция для определения модели Айпада
function detectIPadVersion(callback, undefined) {
    if ('ondevicemotion' in window) {
        window.addEventListener('devicemotion', function self(event) {
            if (~navigator.platform.indexOf('iPad')) {
                var version = event.acceleration ? (window.devicePixelRatio === 2) + 2 : 1;
            }
            window.removeEventListener('devicemotion', self);

            callback(version);
        }, false);
    } else {
        callback(undefined);
    }
};

// пример вызова, в callback будет передан номер модели (1,2 или 3) или undefined, в случае, если это не iPad
detectIPadVersion(alert);

26 комментариев

Как в JS узнать что объект — массив?

Узнал на семинаре Кантора такую штуку. Как узнать, что объект, который пришёл на вход — массив? Конечно, лучше использовать утиную типизацию, но если нам нужен именно массив, со всей полнотой методов, не проверять же каждый?

Кстати, если кто-то думает, что ответ «typeof», то он просто не знает JS. Эта конструкция вернёт «object».

Я до недавнего времени думал, что правильный способ такой:

console.log([] instanceof Array); // true

А редко его используют, потому что конструкция «instanceof» не везде реализована. Я заблуждался. Вот правильный способ:

console.log(Object.prototype.toString.call([]) == '[object Array]'); // true

И вот почему. Оказывается, у каждого фрейма своя иерархия типов, если один фрейм передаёт во второй какой-то объект, то «instanceof» даст «false», так как сравнивает c «Array», локальным для этого фрейма. Проблему иллюстрирует следующий пример:

<script>
function Up(arr, iframeWindow) {
    alert(arr instanceof Array); // false
    alert(arr instanceof iframeWindow.Array); // true

    alert(Object.prototype.toString.call([]) == '[object Array]'); // true
}
</script>
<iframe src="data:text/html, <script>parent.Up([], window)</script>"></iframe>

15 комментариев

Логичность шаблона «объявили и вызвали» в JS

Вчера в комменариях возникло обсуждение семантики синтаксиса «!function () {}()». Мне так кажется, что семантика здесь побоку, использовать функцию для создания зоны видимости — уже костыль (в JS 1.7 есть конструкция «let» для этого), но мне тут подумалось, что ведь можно сделать эту конструкцию семантичной, если использовать забытую конструкцию «with»:

with (function () {
    // тело функции
}) call();

На мой взгляд, весьма и весьма семантично. Что скажете?

5 комментариев

Декларация-вызов в JS

Сегодня на первой части семинара Ильи Кантора по JS присутствовал (к сожалению, на второй части не получится, буду в Москве это время). Немного поговорили о частом паттерне «объявили функцию и тут же вызвали». Используется этот приём (если кто не знаком с ДжаваСкриптом) для изоляции — в этом языке функции единственное, что может создавать зоны видимости.

Я вскользь затронул свой любимый способ — описание функции и вызов через восклицательный знак или тильду, вместо оборачивания скобками. Вообще, в JS непосредственный вызов функции сразу по месту определения возможен, только если декларация находится в контексте выражения. Для этого и нужны традиционные скобки:

(function () {
    // скобки вокруг функции говорят интерпретатору, что это выражение
})();

Вообще говоря, можно использовать любые средства, чтобы декларация стала частью выражения. Например, можно что-то прибавить к декларации. Или использовать любую унарную операцию.

Использование тильды или восклицательного знака гораздо лучше скобок по трём причинам:

!function () {
    // 
}();

Во-первых, на один байт меньше. Если таких функций много, экономия существенная. Во-вторых, скобки надо балансировать, а унарный оператор — нет.

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

var a = 5
(function() {})()

Причём ошибку, которую найти иногда довольно трудно, да и сообщение о ней не самое дружественное: «TypeError: 5 is not a function». На самом деле, тут пропущена точка с запятой. Интерпретатор считает, что ему встретились две части выражения, между которыми пропущен какой-то знак. Эта ситуация часто встречается при автоматическом объединении двух скриптов в один.

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

Но если использовать унарный оператор, то этой проблемы просто нет (поэтому, на самом деле, мы экономим два байта, ещё и точка с запятой не обязательна):

var a = 5
!function () {}()

Тут интерпретатор, встретив унарный оператор, понимает, что предыдущее выражение закончилось и началась новая конструкция.

33 комментария

← 1 2 3 4 5 6 →