Во всех известных поисковиках!
Все ваши комментарии!
Да, именно так, я сделал синхронизацию комментариев с Disqus. Теперь, даже если в вашем браузере отключен JavaScript, вы всё равно сможете прочитать всё.
Могу с уверенностью сказать, что Haskell — не самый подходящий язык для подобных вещей. На каком-нибудь php всё это можно было бы реализовать гораздо быстрее, но мы ведь не ищем легких путей. Самая большая проблема — работа с JSON. Библиотека для работы есть, и она неплохо реализована, но сказывается специфика Haskell и его статической типизации. Ну например,
{
"code":0,
"response":[
{
"forum":"dikmax",
"parent":667510883,
"author":{
"username":"google-1b379da98470c64a0c4bc8c2230d616d",
"about":"",
"name":"...",
"url":"",
"joinedAt":"2012-09-25T03:14:42",
"avatar":{
"permalink":"...",
...
},
...
},
"message":"...",
...
},
...
]
}
Приблизительный процесс разбора:
- Что делать, если результат не является правильным JSON.
- Что делать, если корневой узел — не объект.
- Что делать, если у объекта нет свойства
code
. - Если
code
— не является числом. - Если
code
всё-таки число, но не 0. - Если у объекта нет свойства
response
. - Если
response
— не массив. - Преобазовываем
response
в два массива: первый с комментариями, второй с ошибками разбора комментариев.
И когда какого-то «если» нет, компилятор выдаст ошибку и придется искать, какой вариант ты еще не предусмотрел. Т.е. компилятор заботится, чтобы твоя программа умела работать при любых данных. И это, конечно, огромный плюс программирования на Haskell.
Весь код для синхронизации. Получилось не очень много, но времени на него было потрачено прилично.
Есть такая вещь в современных и не очень процессорах, называется предсказатель переходов (branch predictor). Он позволяет значительно увеличить скорость работы процессора. Недавно мне Антон прислал ссылку на обсуждение предсказателя переходов на Stack Overflow. И там есть очень хорошее объяснение, обязательно посмотрите.
Так вот, я решил проверить, насколько всё это применимо к JavaScript. Ведь, хотя это и интерпретируемый язык, во все последние браузеры встроен JIT-компилятор. И логично предположить, что производительность будет страдать от плохо предсказываемых переходов. Как оказалось, идея такого исследования не мне первому пришла в голову и на jsPerf нашелся соответствующий тест. Я добавил еще несколько интересных вариантов и готов поделиться результатами.
Сначала о самом тесте. У нас есть 2 одинаковых случайных массива с числами от 0 до 255. Потом один из них мы сортируем. Ну и смотрим, с какой скоростью посчитается сумма всех элементов массива больших или равных 128.
Есть 2 основных способа это сделать: с помощью if
и с помощью тернарного оператора.
var sum = 0;
for (var j = 0; j < arraySize; ++j) {
if (unsorted[j] > 128) {
sum += unsorted[j];
}
}
var sum = 0;
for (var j = 0; j < arraySize; ++j) {
sum += unsorted[j] >=128 ? unsorted[j] : 0;
}
Ну и третий способ — небольшая оптимизация второго:
var sum = 0, item;
for (var j = 0; j < arraySize; ++j) {
item = unsorted[j];
sum += item >=128 ? item : 0;
}
Что хочется отметить. У Google Chrome действительно хороший JIT-компилятор, и разница в скорости выполнения хорошо заметна. В случае Firefox не всё так гладко. В оригинальном варианте с if
разница в скорости в 2 раза, как и в случае Chrome. А вот тернарный оператор почему-то работает значительно медленнее, и разница между отсортированным и неотсортированным массивом не так заметна. Opera вообще всё равно, хотя проход по отсортированному массиву все-таки немного быстрее. И еще для Opera имеет значение, что мы сделали 1 обращение к элементу массива вместо двух. Подозреваю, что такая оптимизация в ее компиляторе не предусмотрена.
Про Internet Explorer ничего не скажу, у меня его нет. Если не трудно, перейдите по ссылке в IE и нажмите Run Tests.
Наследование в JavaScript — одна из самых сложных и запутанных тем. Бо́льшая часть программистов даже не пытается лезть в неё, часть поменьше кое-что слышала и даже пытается что-то делать. И лишь небольшая часть разбирается в этой теме.
Итак, наследование.
var ParentClass = function () {}; // ParentClass constructor
ParentClass.prototype.method1 = function () {};
var ChildClass = function () {}; // ChildClass constructor
ChildClass.prototype = ParentClass.prototype;
ChildClass.prototype.method2 = function () {};
Почему-то некоторые уверены, что просто скопировав прототип, мы получим правильное наследование. Так вот, пример выше неверен. Если мы скопируем прототип таким образом, то ChildClass
будет не просто наследником, а родительским классом, но с другим конструктором. А значит method2
, который мы добавили в 5-й сточке, будет так же в прототипе у ParentClass
. Это получается потому, что присваивание объектов происходит по ссылке, а значит ChildClass.prototype
после 4-й строчки будет ссылаться на тот же объект, что и ParentClass.prototype
.
Вариант, увиденный совсем недавно.
var ParentClass = function () {}; // ParentClass constructor
ParentClass.prototype.method1 = function () {};
var ChildClass = function () {}; // ChildClass constructor
ChildClass.prototype = ParentClass.prototype;
var child = new ChildClass();
child.method2 = function () {};
Очень похож на первый. Только расширять предлагается не прототип, а уже экземпляр класса. Да, код будет работать, но это плохой вариант. В таком случае нам нужно расширять класс каждый раз, когда мы создаём экземпляр класса.
— А давайте поместим расширение класса в конструктор, — скажете вы. — Например, вот так:
var ChildClass = function () { // ChildClass constructor
this.method2 = function () {};
};
Хм. Да, так тоже можно. Но у нас получится по одному экземпляру метода method2
на каждый экземпляр класса, а не один на всех, как в случае прототипа. Поэтому — нет.
Вариант 3.
var ParentClass = function () {}; // ParentClass constructor
ParentClass.prototype.method1 = function () {};
var ChildClass = function () {}; // ChildClass constructor
for (var prop in ParentClass.prototype) {
if (ParentClass.prototype.hasOwnProperty(prop)) {
ChildClass.prototype[prop] = ParentClass.prototype[prop];
}
}
ChildClass.prototype.method2 = function () {};
Ну, этот вариант по крайней мере работает. И что самое интересное, работает правильно. Но есть несколько вещей, которые не очень хороши. Если у нас в прототипе родительского класса 100500 полей и методов, то в цикле нам придётся пробежаться по ним всем и скопировать в прототип дочернего класса. А если при этом у нас много дочерних классов, то получится вообще кошмар. В памяти расплодится множество одинаковых объектов, содержащих перечисление всех методов и полей.
И ещё, если мы добавим в самом конце один метод в родительский класс:
ParentClass.prototype.method3 = function () {};
то он попадёт только в него, а не в оба, т.к. связь между классами была потеряна на этапе копирования.
Правильный вариант.
var ParentClass = function () {}; // ParentClass contructor
ParentClass.prototype.method1 = function () {};
var ChildClass = function () {}; // ChildClass constructor
var tempConstructor = function() {};
tempConstructor.prototype = ParentClass.prototype;
ChildClass.prototype = new tempConstructor();
ChildClass.prototype.constructor = ChildClass;
ChildClass.prototype.method2 = function () {};
Нам нужно скопировать прототип, а самый быстрый способ это сделать — создать экземпляр класса. И даже лучше! В этом случае родительский прототип уйдёт на уровень глубже, а прототип дочернего класса окажется пустым. Плюс, если мы добавим в родительский прототип ещё метод после того, как инициализировали дочерний, то он тоже будет в дочернем классе.
Что же происходит в примере выше? Мы создаём временный класс, такой же, как родительский, но у него пустой конструктор. А потом инициализируем прототип дочернего класса этим временным классом. И последний этап: нужно вернуть на место свойство constructor
из дочернего класса.
Всё просто? Наверное так.
И ещё правильный вариант, но не всегда.
var ParentClass = function () {}; // ParentClass contructor
ParentClass.prototype.method1 = function () {};
var ChildClass = function () {}; // ChildClass constructor
ChildClass.prototype.__proto__ = ParentClass.prototype;
ChildClass.prototype.method2 = function () {};
Этот вариант делает всё то же, что и предыдущий, только без создания временного класса. Мы просто берём и записываем прототип родителя в иерархию. Возникает вопрос, а зачем же тогда выбирать более сложный вариант, если есть попроще? Надеюсь, никто не сомневается, что причина выбора — Internet Explorer. Наш любимый IE не поддерживает свойство __proto__
, а значит код не будет работать. Но если ваш код никогда не будет запускаться в IE (может, вы под node.js или Phonegap пишете), тогда этот вариант ваш.
Итог.
JavaScript очень гибкий язык, и в нём есть множество способов достичь одинакового результата. Как напишете, так и будет работать. Я просто предложил самый логичный и быстрый способ. Если у вас есть ещё варианты или просто вопросы — милости прошу в комментарии.
Google представил новый сайт — Webplatform.org, который должен помочь Web-разработчикам. По сути это wiki по web-разработке, попытка собрать всю информацию о современных технологиях в одном месте. Я, как человек тесно связанный с разработкой, могу только поприветствовать это начинание. Потому что да, есть множество разрозненной информации, с которой приходится периодически разбираться.
Пока на сайте немного информации, но это же гугл, а значит дело не должно заглохнуть, не успев даже начаться. Нужно только немного подождать. Или даже помочь проекту, написав несколько статей-страниц.
А пока сайт не готов, я поделюсь с вами ссылкой на ресурсы, где я чаще всего смотрю нужную мне информацию. Большая часть этих ссылок находится в закладках на расстоянии 2х кликов.
- Mozilla Developer Network — самый крупный и полезный сайт по разработке. Зачастую мой поиск дальше этого сайта и не уходит.
- jQuery, ExtJs, Closure Library, любойДругойФреймворк — официальная документация по js-фреймворкам.
- Stack Overflow — если в официальных источниках ответ не нашелся.
- Google — последняя инстанция.
UPD: Оказывает это инициатива не только Google. Список компаний создающих этот сайт гораздо больше: Apple, Adobe, Facebook, Google, HP, Microsoft, Mozilla, Nokia, и Opera.
UPD2: И еще полезный сайт: caniuse.com. Можно посмотреть какие браузеры поддерживают определенное API.
Помните, пару дней назад я задавался вопросом приведения типов? Я тогда не нашел объяснения, почему {}+[]
дает 0, a {}+{}
дает NaN
. Теперь я могу это объяснить.
Оказывается, левый операнд воспринимается не как пустой объект, а как пустой блок кода. И именно это главная составляющая, которой мне не хватило для объяснения. Получается, что выражение делится на два: {};+[]
. Дальше []
приводится к примитивному типу и становится равным пустой строке. А так как унарный плюс требует числовой операнд, то пустая строка переводится в число и получается 0.
Такая же история с {}+{}
. Это выражение тоже делится на два: {};+{}
. При переводе {}
к примитивному типу мы получаем строку ”[object Object]”
, которая при приведении к числу и дает NaN
. Так что никакой мистики.
Вот такая магия. Правильный ответ был найден на Stack Overflow.
P.S. Пример в прошлой статье я поправил на более верный.