JavaScript и приведение типов, продолжение

Помните, пару дней назад я задавался вопросом приведения типов? Я тогда не нашел объяснения, почему {}+[] дает 0, a {}+{} дает NaN. Теперь я могу это объяснить.

Оказывается, левый операнд воспринимается не как пустой объект, а как пустой блок кода. И именно это главная составляющая, которой мне не хватило для объяснения. Получается, что выражение делится на два: {};+[]. Дальше [] приводится к примитивному типу и становится равным пустой строке. А так как унарный плюс требует числовой операнд, то пустая строка переводится в число и получается 0.

Такая же история с {}+{}. Это выражение тоже делится на два: {};+{}. При переводе {} к примитивному типу мы получаем строку ”[object Object]”, которая при приведении к числу и дает NaN. Так что никакой мистики.

Вот такая магия. Правильный ответ был найден на Stack Overflow.

P.S. Пример в прошлой статье я поправил на более верный.

Самый быстрый JavaScript-фреймворк

Нашел еще один JavaScript-фреймворк (спасибо Виктору за наводку), который на несколько порядков быстрее всех остальных. Прошу любить и жаловать vanilla.js! Если верить создателям, то получение всех элементов по имени тега отрабатывает быстрее jQuery в 425 раз. Я думаю, одного этого достаточно, чтобы глянуть на фреймворк.

AngularJS

Вы, наверное, слышали об этом JavaScript-фреймворке. Модный тренд, если позволите так выразиться. Superheroic JavaScript MVW1 Framework, как утвержают сами разработчики. Не удержался и посмотрел на него, тем более что выдалась возможность сделать это в рабочее время и за счет заказчика. Мы выбираем основу для будущего проекта, вот и решили глянуть современные js фреймворки, и мне достался AngularJS. Поделюсь своими впечатлениями.

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

<table class="table table-striped table-hover" ng-controller="TestController">
    <thead>
        <tr>
            <th>Action</th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="action in actions">
            <td>{{action.action}}</td>
        </tr>
    </tbody>
</table>

Дальше мы подключаем JavaScript:

function TestController($scope) {
    $scope.actions = [
        {action: 'Action1'},
        {action: 'Action2'}
    ];
}

Ну и всё. У нас готова страница с таблицей, в которой 2 строки: «Action1» и «Action2». Но это еще не все. Если мы потом добавим в поле actions еще чего-нибудь, то оно тоже автоматически отобразится на странице.

Единственное, мне потребовалось некоторое время, чтобы разобраться с некоторыми более сложными вещами. Например, создание независимых компонентов. В моей задаче требовалось, чтобы родительский компонент передавал данные для дочернего. В результате оказалось, что на момент вызова родительского контроллера дочерний еще не инициализирован и нужно немного подождать. И лучше всего использовать для этого событие $viewContentLoaded. Чтобы это понять, официального сайта было недостаточно, и пришлось провести пару-тройку часов в обнимку с дебаггером и гуглом.

В общем, впечатление от фреймворка очень положительное, надо будет попробовать написать что-нибудь более-менее серьезное.

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


  1. Model-View-Whatever

JavaScript и приведение типов

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

[]+[] 
> ""
[]+{}
> "[object Object]"
{}+[]
> 0
{}+{}
> NaN

Почему так? Это кажется бредом, пока не разберешься с системой типов в JavaScript. Ладно-ладно, это кажется бредом в любом случае, но тем не менее попробуем хоть чуть разобраться.

Самое главное, что нужно знать — операция + работает только с примитивными типами. Т.е. что бы вы ни написали, выражение будет приведено к этим типам. Даже больше: если хотя бы один операнд — строка, то и результат будет строкой. Поэтому, чтобы понять причину происходящего выше, нужно узнать, как объекты и массивы приводятся к примитивным типам.

У каждого объекта и массива (ведь массив тоже объект) есть 2 метода: valueOf и toString. И именно они будут использоваться для преобразования типов. Сначала интерпретатор вызывает valueOf, и если он вернул не примитивное значение, то вызывается toString. Теперь нужно выяснить, что же возвращают эти методы у {} и [].

console.log({}.valueOf());  // Возвращает себя
console.log([].valueOf());  // [] (тоже себя)
console.log([].toString()); // ""
console.log({}.toString()); // "[object Object]"

Засада. Каким образом {}+[] дает 0, ведь по логике должно быть "[object Object]"? Ковыряние в спецификации пока ни к чему не привело. Если кто знает правильный ответ, не томите, поделитесь наблюдениями.

Ну и совсем напоследок видео, из-за которого я начал разбираться в этой теме. Посмотрите, не пожалеете.

UPD: Исправлен верхний листинг.

UPD2: Разгадка.

Оптимизация

Блог подвергся тотальной оптимизации. Теперь всё должно работать еще быстрее и еще лучше. Теперь на каждой странице осталось только по одному js и css файлу, конечно, если не считать сторонние сервисы — Disqus и Google Analytics. И вообще везде царствует гармония и фен-шуй.

Весь javascript был переписан с использованием Google Closurе. Попутно в него были внесены мелкие, но приятные дополнения. Например, навигация с помощью комбинаций клавиш Ctrl+← и Ctrl+→ или эмуляция переходов между состояниями (transitions) для старых браузеров, не поддерживающих CSS Transitions. Конечно, для этого пришлось написать часть кода из плагинов Twitter Bootstrap. Но в любом случае я доволен: суммарный размер всех js фалов на рабочем сервере уменьшился в 3 раза: со 180 до 60 килобайт. Да и прокачанный навык в Google Closure многого стоит.

С CSS было проще: я просто выкинул неиспользуемые части из Twitter Bootstrap и добавил свой код в результирующий файл. Это не заняло много времени.

А еще теперь при наведении на стоку в листинге с кодом слева подписывается ее номер. Не особо нужная штука, но может кому пригодиться.

Весь код, как обычно, на GitHub, включая новые большие скрипты для сборки проекта. Смотрим, изучаем, комментируем.

← СтаршеМоложе →