Collection v4
Доделал 4-ю версию своего эпичного велосипеда:) Создал новый тред, т.к. по сравнению с прошлой версией было сделано оч много конфликтующих изменений.
https://github.com/kobezzza/Collection Что это и зачем: В JavaScript для массивов есть встроенные методы-итераторы, например, forEach, reduce и map и т.д. Их очень удобно использовать и они делают наш код нагляднее. Данная либа позволяет юзать все те же методы + дополнительные для любых типов данных, а не только для массивов. Также итераторы в Collection работают значительно быстрее нативных. Также в этой либе реализован простой интерфейс для работы с хранилищами данных: localStorage, sessionStorage, indexedDB. Итак: По сравнению с прошлой версией было выпилено много лишнего (теперь сжатая либа - это всего 11.3 килобайта). Теперь либа позиционируется как единый и очень навороченный API для функциональной работы с коллекциями данных, а не как блендер из кучи всего (как было раньше:) ). Реализован единый прозрачный API для работы с хранилищами данных (localStorage, sessionStorage, indexedDB). Реализован свой собственный JIT компилятор для итераторов, который позволяет увеличить до 10-ти раз производительность в сравнении с нативными методами во всех современных браузерах (применяемые оптимизации не делают работу за JIT VM JavaScript, а наоборот: дополняют те вещи, которые не могут быть оптимизированы нативным JIT). В древних браузерах этот эффект может быть ещё выше. Переработаны интерфейсы методов для более простой и удобной работы. Код переписан на ECMAScript6 (с транслятором) :) Парочка примеров:
// Перебор элементов в обратном порядке начиная с 5-го
$C(document.querySelectorAll('.foo')).forEach(function (el) {
...
}, {
reverse: true,
startIndex: 5
});
// Вернуть массив всех чётных элементов исходного массива
$C([1, 2, 3, 4]).get(function (el) { return el % 2 === 0; });
// Cоздать новый объект на основе исходного,
// {a: 2, b: 4}
$C({a: 4, b: 16}).map(Math.sqrt);
// Загрузить коллекцию foo из локального хранилища
$C().load('foo');
О найденых багах пишите на гитхаб, вопросы можно задавать тут:) ЗЫ: про отвратительную историю коммитов в курсе, буду исправляться :) |
Было бы интересно послушать про оптимизации)
|
Цитата:
Т.е. методы Collection принимают оч много параметров-ограничений, вроде количество элементов в ответе и т.д. Эта информация учитывается при генерации оптимизированный функции, т.е. если нет необходимости это учитывать то логика проверки не войдёт. В функциях callback учитывается количество принимаемых параметров, наличие this, наличие возвращаемого значения и т.д. - эта информация также помогает построить оптимизированную функцию. Анализируется тип данных, например для массиво-подобных объектов по умолчанию используется for, как и для массивов вместо for in. После нормальной реализации в браузерах for of добавлю дополнительную поддержку итераторов. Фильтры анализируются более глубоко, вплоть до возможного инлайнинга. Инлайнингу почти всегда подвергаются функции-строки (вроде :el > 2), а также лямбда-функции (в теле которых сразу идёт return), но только если пройдёт проверка внешних зависимостей. Поощряется разбивать фильтры на множество атомарных и затем использовать композицию, вроде:
.get('odd && unique || some') // и т.д. подробнее в доке
Т.к. составные фильтры перед выполнением строят дерево логики и оптимизируют его для максимального инлайнинга. После всех оптимизаций составляется специальный ключ, который определяет вид применённых оптимизаций и в дальнейшем этот ключ может использоваться для схожий операций, т.е. оптимизация делается один раз. В браузерах дополнительно оптимизированные функции подключаются как внешние скрипты, что дополнительно увеличивает скорость работы и улучшает отладку (для node планирую добавить кеширование во внешний файл). Также ключи оптимизации в браузере сохраняются в локальном хранилище и при обновлении странице будут подключены как внешний скрипт, т.е. уже не будет необходимости в повторной оптимизации, причём ключи привязываются к домену, т.е. при повторном заходе на сайт оптимизация будет ещё сильнее. На примере проекта над которым я сейчас работаю замена нативных методов на Collection без внесения дополнительных оптимизация увеличило скорость работы всего сайта в 12-16 раз во всех браузерах. ЗЫ: Вот пример сгенерированных ключей http://screencast.com/t/M4IqYUqRNF ЗЫЗЫ: Ещё один слой оптимизации - это оптимизация и сжатие либы в GCC Adv Mode :) Хз что он там колдует, но после сжатия всё реально шустрее работает :) |
Цитата:
не особо вникал в код пока, но я так понял ты вообще не используешь встроенные циклы типа for при переборе, или как ты перебираешь то что передали? К примеру я передал массив , как он будет обработан и каким циклов перебран? |
Цитата:
Цитата:
|
Цитата:
Цитата:
вопрос по продвинутом сжатию, к примеру есть код
(function (undef) {
var local_name = "cache_module_",
v_name = local_name + "cache_version";
function cache(params) {
this.storage = sessionStorage;
this.isUse = false;
if(params.isLocal) {
this.storage = localStorage;
}
}
cache.prototype.get = function (id) {
if(!this.isUse) return;
try{
var obj = this.storage.getItem(local_name + id)
return JSON.parse(obj);
} catch(ignore) {}
};
cache.prototype.set = function(key, data) {
if(!this.isUse) return;
setTimeout(function() {
this.storage.setItem(local_name + key, JSON.stringify(data));
}.bind(this), 0)
};
cache.prototype.removeItem = function(key) {
this.storage.removeItem(local_name + key);
};
cache.prototype.clear = function () {
var storage = this.storage, key;
for(var i = 0; i < storage.length; i++) {
key = storage.key(i);
if(~key.indexOf(local_name))
storage.removeItem(key);
}
}
cache.prototype.init = function(parent, initObj) {
var v;
if(arguments.length != 2 ||
initObj.cacheVersion === undef) return;
if(v = this.get(v_name) &&
v != initObj.cacheVersion) {
this.clear();
}
this.set(v_name, initObj.cacheVersion);
this.isUse = true;
};
Object.defineProperty(cache.prototype, "name", {
value: "cache",
writable: false
});
window.Cache_module = cache;
}());
// почему из него убираються прототипы и остаеться
(function(){function b(){}Object.defineProperty(b.prototype,"name",{value:"cache",writable:!1});window.a=b})();
это из за того что они явно не используеться в коде? |
Цитата:
Цитата:
|
Цитата:
Цитата:
|
Цитата:
|
Цитата:
|
Цитата:
(это официальная дока closure compiler) |
kobezzza, а почему не используешь объект для передачи параметров?
|
Цитата:
https://github.com/kobezzza/Collecti...%D 0%BE%D0%B2 Цитата:
{
filter,
id,
mult,
count,
from,
startIndex,
endIndex,
reverse,
inverseFilter,
notOwn,
live,
useForIn,
vars,
context
}
т.е. использую. Для внутренних вызовов юзается вызов с перечислением параметров, т.к. это работает быстрее, а для конечного использования конечно можно использовать объекты-параметры, если нужно. |
Цитата:
Plovr тоже ничё так :) |
kobezzza, не могу понять как сделать что класс не переименовывало, пробывал ставить @constructor но не помогает
(function () {
/**
@constructor - пробывал так
*/
function constr() {// сохранить имя constr
}
window.Constr = constr
}())
Цитата:
|
Цитата:
Во первых: http://javascript.ru/optimize/google...d-optimization Во вторых: @expose https://developers.google.com/closur...s-for-compiler Ну и в третьих просто глянул бы, как сделано у меня: https://github.com/kobezzza/Collecti.../core/core.jsn (this.Collection; и this.$C; - это для поддержки автокомплита в WebStorm) |
Цитата:
|
Цитата:
вот как у тебя, так и надо. (про строковые имена): global['Collection'] |
Цитата:
Либа жмётся и нужные методы внешние, а все "издержки" 100% закрываются gzip, т.е. нет никакой разницы. Если я жму либу вместе с проектом, то пред сжатием я дополнительно вырезаю @expose (replace(/@expose/gm, '') в скрипте сборки) и всё (или написать файл экстерн, но это даст хуже сжатие). А вот если я юзаю экспорт как ['...'], то при сжатии с проектом уже без файла экстерна не обойтись, и сжатие будет хуже. *** Если я пишу ['...'] то это означает что всегда-всегда это свойство должно быть с таким именем. Вот пример: https://github.com/kobezzza/Collecti...rs.storage.jsn Свойство Collection.Drivers.Storage должно быть доступно при использовании либы как "внешнего скрипта", но при сжатии вместе с проектом должно сжиматься, поэтому я юзаю @expose. А свойства 'indexedDB', 'localStorage' и т.д. должны быть такими всегда, поэтому я экспортирую их в кавычках. Например: global['Collection'] сделан так не спроста, т.к. Collection фигурирует в генерируемых скриптах в рантайме, т.е. всегда должны быть именно таким. Разумеется я читал: Цитата:
|
kobezzza,
![]() Генерирует, по-моему, такой код: goog.exportProperty( %OBJECT% , "%PROP_NAME%", %PROP_VAL%); Исходник:
/**
* Exports a property unobfuscated into the object's namespace.
* ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
* ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);
* @param {Object} object Object whose static property is being exported.
* @param {string} publicName Unobfuscated name to export.
* @param {*} symbol Object the name should point to.
*/
goog.exportProperty = function(object, publicName, symbol) {
object[publicName] = symbol;
};
Использование: /** @export */ MYOBJ.MYPROP = 'FOO' Цитата:
|
melky, я в курсе про это, только где аргументы против @expose ?:) Юзать его можно, юзать его удобно, но просто когда его юзаешь нужно думать. Иначе бы его просто не было:)
|
Цитата:
Вид хранилища у меня задаётся как Storage.lib = '...' поэтому нужно такое эскпортирование. |
Цитата:
Y.prototype.t = function(a) {
/* A CODE */
};
Y.prototype.FOO= Y.prototype.t;
@expose :
Y.prototype.FOO = function(a) {
/* A CODE */
};
разница - в использовании. при экспорте использование - это Y.t, при expose - Y.FOO Ну и исчо при компилировании некоторые методы выносятся в функции. У меня был метод без аругментов.
Obj.foo = function () { alert(this.bar) }
// Используется так :
Obj.foo();
Теперь он стал такой функцией :
function foo (a) {
alert(a.bar)
}
// Используется так :
foo(this);
Хотя как и экспорты, expose эту фишку убивает (проверил сейчас). Наверное, наш спор похож на "табы VS пробелы" и похожие, но у меня плохое предчувствие насчёт @expose, поэтому и не юзаю :) |
kobezzza, вся документация находится в Readme.md ?
|
Цитата:
|
Жаль, экстернов нет :( нипосжимать.
Попробую пощупать либу в след. проекте. Кстати, хотел вот сделать pull request на unrolling loops, но не смог разобраться в исходниках и коммитах :Р |
Цитата:
Кстати можно сделать свою сборку либы: node build А в файле build.jsn можно выкинуть не нужное. Цитата:
А в исходниках что не понятно? Либа лежит в папке lib, итерационные методы в папке mult, все методы имеют jsdoc и описание. Основные файлы имеют расширение .jsn (а .js - это файлы сгенериные транслятором). Развёртка циклов профита не даёт, т.к. современные JS VM в этом плане оч прокачались и сами всё делают, я пробывал, но потом выпилил за ненадобностью:) Но копать тут: https://github.com/kobezzza/Collecti...t/compiler.jsn |
Цитата:
|
Цитата:
Исходники в папке /lib/, сборка в папке /build/ (там также есть сжатая версия) |
в форме офтопа
kobezzza, не могу понять - либа тебе нужна по основной работе? Или тебе хватает энтузиазма делать её вместе с докой просто для себя, обучения и с призрачной надеждой что она станет популярной? Вы тут такой жути понаписали про оптимизацию что я могу только догадываться о количестве потраченных человеко-часов. Уже прежней версии нет, сайт сгинул, так ещё раз всё с нуля на гитхабе теперь написано...
Лично я в одном проекте уже третью версию использовал, сейчас буду применять четвёртую во втором. |
Цитата:
Про связь с прошлой версией: я не изменил ни концепцию ни структуру, а просто выкинул те вещи, которые не показали своей полезности на практике, т.е. с большой долей вероятности переход будет простым. Были конечно некоторые дизайнерские изменения в интерфейсах, но их не очень много. Основные ломающие изменения: 1) Был выкинут кластер шаблонизации, т.к. он эволюционировал в отдельный мой проект Snakeskin. 2) Был убран SQL-парсер, т.к. на практике применять его у меня как то не очень вышло. 3) Выкинуты методы: stat, groupStat, copy, move, т.к. их легко создать на основе get/map/reduce/ 4) Выкинуты некоторые вспомогательные методы, вроде resetCollection, вряд ли их кто использовал 5) push_ методы переименованы в add_. Т.е. основная цель версии 4 была уменьшить кодовую базу и добиться высокой скорости создания/выполнения. Теперь про сайт: по хорошему нужно создать сайт-описание на гитхабе, а домен collection-js.com привязать к нему, но у меня правда нет на это времени, поэтому я его прикрыл пока, т.к. там содержится устаревшая дока и написал полностью новую доку на гитхабе. Если честно не вижу особых проблем с этим, т.к. какая разница где лежит дока. Пиши вопросы: я всегда оперативно на них отвечаю. |
Выпустил обновление 4.0.3 с исправлениями некоторых найденых ошибок.
|
Цитата:
|
Цитата:
ЗЫ: чёто у мя сёня день релизов :) запил 4.1.0, где были улучшены интерфейсы методов indexOf, lastIndexOf, _drop. |
kobezzza, хотел спросить насчет реализации forEach, по сути этот метод только перебирает массивы и только по числовым индексам (без использования Object.keys или for in), почему он тогда медленнее подобной реализации (в хроме критически медленне) и что может не так быть в этой реализации?
function each(arr, callback, context) {
for(var i = 0, leng = arr.length; i < leng; i++) {
if(context)
callback.call(context, arr[i], i, arr);
else
callback(arr[i], i, arr);
}
}
сделал тест ради интереса http://jsperf.com/each-test-speed |
Цитата:
На самом деле нельзя сказать точно: медленнее или нет, т.к. в некоторых случаях JIT VM может эффективно скомпилить тот или иной код, а иногда нет. Лучше всех на данный момент с такими оптимизациями справляется ИЕ11. Но в большинстве случаев падение скорости в нативных методах связано с тем, что стандартные массивы в JS - это на самом деле не массивы, а просто сахар над хешом. Цитата:
Но на самом деле forEach не то место, где нужно смотреть, т.к. он быстрый сейчас везде. Большая разница в производительности на всяких выборках (всякие map, filter и т.д.). |
Цитата:
|
Цитата:
Посмотри тесты, что я тебе привёл по ссылке: https://github.com/kobezzza/Collecti...r/forEach.html сравнения forEach https://github.com/kobezzza/Collecti...filer/get.html get vs filter https://github.com/kobezzza/Collecti...WithCycle.html get vs filter внутри цикла Чтобы склонить репозитарий git clone [url]https://github.com/kobezzza/Collection[/url] |
сделал тест
Цитата:
|
Цитата:
http://screencast.com/t/kTWjV15n5 вот результат замеров в 3-х браузерах на моей машинке. Хотя повторюсь, что эти тесты все искусственные. |
| Часовой пояс GMT +3, время: 09:45. |