среда, 29 февраля 2012 г.

Встраивание поддержки стандартов в DOM-дерево

В предыдущих статьях я приводил код, позволяющий добиться частичной поддержки стандартных методов в IE6-8, используя минимум ресурсов. Но встраивание его в DOM-модель производилось пользователем вручную. В данной статье я намерен показать, как этого можно было бы избежать, сделав стандартизацию IE прозрачной.

В одном из предыдущих постов я описал функцию, которая добавляет метод addEventListener к любому DOM-объекту при помощи нестандартного IE`шного метода attachEvent (в последующих постах я привёл для неё ряд улучшений - 1, 2). И всё бы хорошо, но сама по себе эта функция, очевидно, проблемы не решает, ведь теперь её нужно вызывать для всех объектов DOM-дерева, с которыми работает сценарий.

Можно было бы, конечно, вставлять эти вызовы в основной код, корректно отрабатывающий во всех браузерах, кроме IE6-8, но это его замусорит. Я уже не раз писал, что в современных проектах код часто итак достаточно сложен, так что отвлекаться при его написании ещё и на нестандартные браузеры часто слишком накладно как с позиции трудоёмкости написания, так и с позиции трудоёмкости его последующего изменения при отладке и расширении.

Так что я решил пойти несколько другим путём.

Я написал основной код на стандартном JavaScript`е строго в рамках стандартов, отладил всё в стандартных браузерах - IE9, а так же в последних версиях Chrome, Safari, Opera и FireFox, и лишь потом взялся за IE6-8, создав для них отдельный файл сценария в проекте. Добавление этого сценария можно произвести очень удобно - IE умеет понимать комментарии специального вида, которые диктуют ему различное поведение в отличие от его различных версий. Я написал так:

<!--[if lt IE 9]><script type="text/javascript" src="/_scripts/_lib/patch.js"></script><![endif]-->
Очень удобно, что все остальные браузеры считают этот код простым комментарием и не вставляют его, а IE 8`й и более ранних версий этот код вставляет и выполняет. Этот "комментарий" я вставил до объявления всех остальных сценариев, добившись, таким образом, что бы у меня была возможность в этом сценарии именно нужным образом подготовить браузер к выполнению стандартного кода, следующего дальше.

Итак, теперь осталось решить, как ранее уже описанную функцию setAddEventListener вызывать для всех DOM-объектов, что бы в нужный момент у них можно было вызвать стандартный метод addEventListener.

Легко понять, что попытка решить эту задачу в общем слишком глобальна, ведь модель DOM по сути представляет собой в большей степени коллекцию интерфейсов, а не конструкторов, как многие ошибочно полагают. В действительности у нас нет, например, конструктора Node и конструктора HTMLElement в браузерной среде - у нас есть только фабрика document, имеющая методы, которые возвращают объекты, соответствующие этим интерфейсам. Так что бесполезно было бы пытаться добавлять функционал к прототипам этих интерфейсов - они ни на что не влияют, а зачастую и вообще не существуют в браузерном окружении. Если мы попытаемся добавить стандартный, но отсутствующий в IE6-8 метод к Node.prototype или HTMLElement.prototype, надеясь, что данный метод появится у всех HTMLElement`ов, мы неминуемо будем разочарованы их отсутствием, а то и вообще получим ошибку при обращении к несуществующему объекту. Так что добавлением стандартного функционала надо заниматься на уровне фабрики - т.е. объекта document.

Однако сказать это проще, чем сделать. Ведь способов получить DOM-объекты в JavaScript масса и учесть все возможные их комбинации не представляется возможным! Вот, допустим, мы переопределим стандартный и наиболее часто встречающийся метод document.getElementById примерно так:

document.getElementById = function(){
    var src = document.getElementById;
    return function() {
        var /** @type HTMLElement */ result = src.apply(this, arguments);
        if (result !== null)
            setAddEventListener(result);
        return result;
    }
}();
Вроде бы, всё замечательно, но это, ведь, не единственный способ получения элемента. Вот вернули мы этим нашим методом в стандартный сценарий element, а в этом стандартном сценарии производится вызов свойства parentNode или nextSibling, и на него уже производится попытка повесить обработчик события стандартным образом - с этим как? Ведь, это же свойства, а не функции, и в IE6-8 у нас нет методов определения их в качестве properties - ни ECMAScript5`овских Object.defineProperties c Object.defineProperty, ни даже FireFox`овских нестандартных (и кстати уже deprecated) __defineGetter__ с __defineSetter__`ом. Так что мы не можем обработать их вызовы. Можем, конечно, написать так:
setAddEventListener(result)(result.parentNode)(result.nextSibling);
, но если в стандартном коде свойство parentNode будет вызвано несколько раз - и этот объект прописывать, а остальные?.. В общем, реальных комбинаций может быть очень много - это, конечно, конечное множество, но полноценный качественный алгоритм, который переберёт все возможные варианты, неминуемо серьёзно затормозит работу браузера, так что это решение не приемлемо.

Так что, думаю, будет не рентабельно вершить мир во всём мире, создавая универсальное решение - гораздо продуктивнее отталкиваться от конкретики стандартного сценария, которому нужна поддержка ограниченного стандартного функционала и обеспечить её здесь и сейчас. В моём случае мне вполне хватало одноразового использования ссылки parentNode для возвращаемого методом document.getElementById элемента, так что я остановился на вполне достаточном для меня варианте:

document.getElementById = function(){
    var src = document.getElementById;
    return function() {
        var /** @type HTMLElement */ result = src.apply(this, arguments);
        if (result !== null)
            setAddEventListener(result)(result.parentNode);
        return result;
    }
}();
Кроме того, мне был нужен слушатель для элемента, возвращаемого конструктором Image, который по аналогии был реализован так же сходным образом, лишь с незначительными изменениями:
Image = function (){
    var src = Image;
    return function(){
        var /** @type HTMLImageElement */ result = new src;
        setAddEventListener(result);
        return result;
    }
}();

Улучшение метода setAddEventListener

Активное применение описанноего в одном из предыдущих постов метода setAddEventListener для придания браузерам IE6-8 стандартного W3C DOM`овского метода addEventListener позволило выявить у первого некоторые недостатки. О крупных недостатках я напишу отдельно, но о паре мелких улучшений давайте поговорим сейчас.

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

Вот как теперь можно объявив эту функцию, сразу же в том же выражении элегантно вызвать её и для объекта window и для объекта document:

var setAddEventListener = function f(that) {
    if (!('addEventListener' in that) && 'attachEvent' in that) {
        //реализация
        that.addEventListener = function(eventName, handler) {
            //реализация
            return handler;
        };
        that.removeEventListener = function(eventName, handler) {
            //реализация
            return handler;
        };
    }
    return f;
}(window)(document);

Усовершенствование шаблона enrichment

Стоян Стефанов (Stoyan Stefanov) описывает в своей книге "JavaScript Шаблоны" ("JavaScript Patterns") в гл.6 шаблон "Заимствование конструктора" (к сожалению, в русском издании английские названия шаблонов были опущены и по-этому каноническое название этого шаблона мне пока не удалось найти, в том чиле и на сайте http://www.jspatterns.com/ этот шаблон не упомянут).

Этот шаблон очень напоминает то, что я в одном из предыдущих своих постов назвал "Enrichment", единственное отличие заключается в том, что он предлагает его использование в контексте наследования (в том числе предлагает на его основе реализацию множественного наследования), а не в контексте придания дополнительной функциональности уже сформированным объектам. Читатель, конечно, может считать это одним и тем же шаблоном, однако специфика применения в данном случае, на мой взгляд, имеет важное значение.

Немного попрактиковавшись, я пришёл к выводу о том, что шаблон enrichment луше будет всё-таки описывать именно простой функцией, не пытаясь делать вид, что это конструктор, начиная её не с заглавной буквы, а с приставки "to", что бы не путать её с настящим конструктором. Конструктора же из неё не выйдет просто потому, что, как справедливо указывает Стоян Стефанов в разделе недостатков этого шаблона, она не может прицепить к передаваемому ей объекту ссылку "__proto__" на прототип, а по-этому не может встроить его в модель наследования.

Кроме того, что бы не плодить большого количества функций, которые имеют характер постоянных при многократном использовании данного шаблона, будет гораздо лучше, если мы будем создавать их один раз и потом просто присваивать. Т.е. вместо кода, который я приводил ранее:

function EventW3C() {
    if (!('preventDefault' in this))
        this.preventDefault = function() {
            this.returnValue = false;
        };
    if (!('stopPropagation' in this))
        this.stopPropagation = function(){
            this.cancelBubble = true;
        };
    //Прочие реализации стандартных свойств и методов
    return this;
}
, теперь я пишу так, что, с моей точки зрения, более корректно:
function toW3CEvent(that) {
    for (var p in toW3CEvent)
        if (!(p in that) && toW3CEvent.hasOwnProperty(p) && p !== 'prototype')
            that[p] = toW3CEvent[p];
    return that;
}
toW3CEvent.preventDefault = function() { this.returnValue = false; };
toW3CEvent.stopPropagation = function() { this.cancelBubble = true; };
//Прочие реализации стандартных свойств и методов
Так, теперь для придания объекту event в IE6-8 функциональности стандартного W3C DOM объекта Event, можно вызвать эту функцию следующим образом:
handler.call(that, toW3CEvent(event));

суббота, 21 января 2012 г.

__proto__ для стандартных конструкторов

Сегодня слушателями курса по JavaScript сподвигли меня на написание очередной заплатки для IE. В статье "__proto__ во всех браузерах" я уже приводил код для вставки ссылки __proto__ в любой объект. Теперь настала пора вспомнить о стандартных конструкторах JavaScript - у них соответствующие ссылки так же должны вести в правильные места:
if (!Object.hasOwnProperty('__proto__')) {
 Object.prototype.__proto__ = null;
 Object.__proto__ =
 Function.__proto__ =
 Date.__proto__ =
 Number.__proto__ =
 String.__proto__
 Boolean.__proto__ = Function.prototype;
}
Только не спрашивайте, зачем это может быть нужно в реальном проекте - не знаю. :)

вторник, 22 ноября 2011 г.

Задержка выхода второй версии библиотеки Drag&Drop

Извиняюсь за задержку с выходом второй версии библиотеки - знаю, что обещал, знаю, что моя вина. Но поймите и Вы меня - дело в том, что когда я уже вносил в неё последние штришки, готовясь сегодня-завтра опубликовать проект, случайно услышал о HTML5 Drag&Drop`е. В итоге я встал перед дилеммой - добивать эту версию со своим собственным интерфейсом, или переделывать её для того, что бы она поддерживала стандартный, но пока ещё мало где (по утверждению W3Schools, пока только в Chrom`е и Safari) реализованный интерфейс HTML5 Drag&Drop. После некоторых колебаний я решил, что теперь, после появления этой спеки, библиотека с собственным интерфейсом для Drag&Drop мало кого заинтересует, так что я решил выбрать последнее.

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

Так же для пользователей первой версии библиотеки, возможно, будет нужна обратная совместимость - поддержка интерфейса первой версии, благо реализовать её не сложно, а так же с помощью Google Closure Compiler`а выкинуть её в случае необходимости не представляется сложным, просто подложив другие экстерны.

К сожалению, работа отнимает довольно много времени. Похоже, успею выпустить библиотечку не раньше февраля. За то она будет HTML5-совместима! :)

четверг, 10 ноября 2011 г.

Коллекции в JavaScript - допиливаем setAddEventListener

Иногда Java-программистам в JavaScript`е не хватает коллекций. Давайте разберёмся - как можно в JavaScript без написания громоздких тяжеловесных решений обойти ситуацию, для которой нам могла бы понадобиться коллекция?

Итак, в Java есть три типа коллекций - List, Set и Map.

Эмулируем List

Строго говоря, как таковая эмуляция List`а в JavaScript нам в общем-то и не нужна - по сути ей и является массив (Array). Собственно, List`ы и в Java-то нам были нужны лишь исключительно по той причине, что массивы в Java имеют чёткий заранее определённый размер, который не может быть изменён, а по задаче мы не всегда можем сказать, массив какого размера нам понадобится. Т.к. в JavaScript такой проблемы нет - нет и необходимости создавать этот тип коллекций специально.

Эмулируем Set

Множество (Set) уникальных значений в JavaScript можно составить на основании того же массива (Array) с добавлением метода, который проверял бы уникальность добавляемого значения:

/** Определяет индекс элемента в массиве.
 * @param {Object} value
 * @return {number} индекс переданного объекта, если тот содержится в массиве.
 * Если он в нём отсутствует, возарвщается -1. */
Array.prototype.indexOf = function(value) 
{
 var /** @type {number} */ length = this.length;
 for (var /** @type {number} */ i = 0; i < length; i++)
  if (this[i] == value)
   return i;
 return -1;
};
/** Определяет, содержит лимассив переданный объект.
 * @param {Object} value
 * @return {boolean} */
Array.prototype.contains = function(value) 
{
 return this.indexOf(value) === -1;
};
Теперь достаточно просто перед добавлением элемента проверять его методом contains - для простых зазачь этого вполне хватит.

Эмуляция Map

С коллекциями типа map будет немного сложнее. В принципе, для наиболее частого случая, когда в качестве ключей нас устроят строки, решение тривиально - это Object. Т.е. создаём объект - он и есть наша карта. Его свойства - это ключи, а их значения - это значения полей коллекции.

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

Пример эмуляции Map

Например, в одной из недавних статей, где я рассматривал шаблон "Заплатка", я писал о методе создать на основе механизма навешивания слушателей в IE6-8 (attachEvent) стандартный механизм навешивания слушателей (addEventListener). Решение, напомню, было таким:

function setAddEventListener()
{
    if ('attachEvent' in this && !('addEventListener' in this))
        this.addEventListener = function(eventName, handler, isCapturing) {
            if (isCapturing)
                throw new Error("We are in IE, so we haven`t way to set event listener on capturing phase of any event to any of HTML-elements");
            attachEvent("on" + eventName, handler);
        }
}
setAddEventListener.call(document);//Для document`а, например, вызываем так
Однако, если подходить серьёзно, то с этим решением в реальном проекте будет некоторое количество проблем. Первая из них - неполная совместимость IE`шного объекта Event и стандартного. Путь для решения этой проблемы я уже демонстрировал в статье про pattern "Enrichment". Напомню, что там получилось:
/** Конструктор, служащий для придания не совместимым с W3C DOM level 3 объектам
 * Event стандартного поведения.
 * @constructor
 * @extends Event */
function EventW3C() {
    /** Отменяет поведение браузера для данного события по-умолчанию. */
    this.preventDefault = function() {
        this.returnValue = false;
    };
    // Выставление других стандартных свойств

    return this;
}
function setAddEventListener()
{
    if ('attachEvent' in this && !('addEventListener' in this))
        this.addEventListener = function(eventName, handler, isCapturing) {
            if (isCapturing)
                throw new Error("We are in IE, so we haven`t way to set event listener on capturing phase of any event to any of HTML-elements");
            attachEvent("on" + eventName, function() {
                handler(EventW3C.call(event) //Здесь используется pattern "Enrichment"
            });
        }
}
Вторая проблема - ссылка this. В обработчик IE6-8 по ней не передаётся элемент, с которым связано событие. Решение простое - запомнить ссылку на элемент в отдельной переменной в замыкании и передать её в качестве ссылки this обработчику при помощи метода call объекта Function:
function setAddEventListener()
{
    if ('attachEvent' in this && !('addEventListener' in this))
        this.addEventListener = function(eventName, handler, isCapturing) {
            if (isCapturing)
                throw new Error("We are in IE, so we haven`t way to set event listener on capturing phase of any event to any of HTML-elements");

                //Запоминаем элемент
                var /** @type Element */ that = this;

                this.attachEvent(
                    'on' + eventName, function() {
                        handler.call(that, EventW3C.call(event));//Правильно вызываем обработчик
                    }
                );
        }
}
При этом приёме, правда, как утверждает Д.Флэнаган, есть проблема с утечкой памяти в ранних версиях IE, но это сейчас out of scope. И, наконец, третья проблема - всплывает, когда мы попытаемся рассмотреть применение механизма удаления обработчика IE6-8 detachEvent для эмуляции стандартного removeEventListener. Дело в том, что т.к. мы используем не сам handler, а функцию, которая его вызывает, то и detach`ить нам нужно её, а не изначальный handler. Соответственно, для удаления нам где-то нужно её найти, так? Но в стандартный метод removeEventListener будет передаваться лишь сам handler. Значит, нам нужно по handler`у найти настраивающую его функцию. Тут-то нам и понадобится коллекция типа Map.

Итак, есть соответствие двух функций и по ключу - одной функции мы должны найти значение - другую функцию, что бы её удалить из обработчиков данного события. Т.к. Map мы эмулируем на основе Object`а, у которого в качестве имён полей, т.е. ключей, традиционно выступают строки, то нам, очевидно, нужен метод, который преобразует объект в уникальную для него строку. В общем случае эта задача не является тривиальной, но в нашем конкретном - какая удача! - для функций, т.е. для объектов Function, этот метод уже есть! И называется он - ни за что не догадаетесь! - toString()! :)))) Этот метод по сути возвращает представление данной функции в виде многострочного текста её определения. Очевидно, что текст определения функции не меняется никогда и он соответствует критерию уникальности для каждой функции, а для одной и той же функции всегда будет возвращать одинаковый результат.

Так что вот какое компактное решение получаем в данном случае:

/** Конструктор, служащий для придания не совместимым с W3C DOM level 3 объектам Event стандартного поведения.
 * @constructor
 * @extends Event */
function EventW3C() {
    /** Отменяет поведение браузера для данного события по-умолчанию. */
    if (!('preventDefault' in this))
        this.preventDefault = function() {
            this.returnValue = false;
        };

    if (!('stopPropagation' in this))
        this.stopPropagation = function(){
            this.cancelBubble = true;
        };

    // Выставление других стандартных свойств

    return this;
}

(/** IE patch for addEventListener and removeEventListener methods. */
function setAddEventListener() {
    if (!('addEventListener' in this))
        if ('attachEvent' in this) {
            var /** Объект-карта всех слушателей данного объекта
                 * @type Object<Object<Function>> */
                fnMap = {};
            this.addEventListener = function(eventName, handler) {
                if (!(eventName in fnMap)) //Если для данного события ещё нет коллекции обработчиков...
                    /**@type Object<Function> */ fnMap[eventName] = {}; //...создаём её
                var /** @type Element */ that = this; //Запоминаем элемент
                this.attachEvent('on' + eventName,
                    fnMap[eventName][handler.toString()] = function() {
                        handler.call(that, EventW3C.call(event));//Правильно вызываем обработчик
                    }
                );
            };
            this.removeEventListener = function(eventName, handler) {
                if (eventName in fnMap && handler.toString() in fnMap[eventName]) {
                    this.detachEvent(
                        'on' + eventName,
                        fnMap[eventName][handler.toString()]
                    );
                    delete fnMap[eventName][handler.toString()];
                }
            };
        }
})(); // Вызываем для window сразу
setAddEventListener.call(document);//Для document`а, например, вызываем так

среда, 9 ноября 2011 г.

Браузерные войны – 2 или нас ждёт JSON-Hibernate?

Прошло примерно полтора года после (первой) публикации мной статьи «Браузерные войны» с анализом рынка развития приложений. Тогда я описал ситуацию такой, какой увидел её на момент начала 2010 года. Теперь, в середине 2011-го, она во многом качественно изменилась. Что же произошло?

Internet Explorer 9 и стандарты W3C

Явно не по доброй воле, хотя маркетологи компании в этом и не признаются, Microsoft всё-таки сделала шаг в сторону реализации стандартов от W3C и ECMA, приведя свой Internet Explorer (IE) в 9-й версии в некоторое соответствие со стандартами. Конечно, о полной победе говорить пока рано. Например, тест ACID3 IE9 проходит лишь на 95%, но, безусловно, это просто несравнимо с 20% от IE8. А учитывая, что даже Mozilla Firefox проходит его лишь на 97%, думаю, можно открывать шампанское!

Правда есть один неприятный момент - IE9 доступен только пользователям Windows Vista и Windows 7. А вот огромной армии пользователей Windows XP, не признающих альтернативных браузеров (а таких "консерваторов" немало), так и придётся сидеть в лучшем случае под IE8.

Просто монополия Microsoft`а имеет одну особенность - эта компания конкурирует сама с собой. Точнее, "Microsoft сегодняшняя" жестоко конкурирует с "Microsoft вчерашней", поскольку главный конкурент новой версии Windows - это старая версия Windows, которая уже куплена громадным количеством пользователей. При этом многие из них резонно сомневаются в том, нужно ли им платить за переход на новую, если их и так всё устраивает? И Windows XP оказался для них очень мощной преградой хотя бы потому, что обладает преимуществами в быстродействии. Это в свою очередь делает его более предпочтительным для многих пользователей с относительно-слабыми машинами. При этом все приложения на данной системе прекрасно выполняются. Никаких проблем нет, если не считать проблемой трудности Microsoft по продвижению Windows 7 на рынок. Microsoft до сих пор не может закрыть поддержку этой системы. А под давлением общественного мнения (думаю, что под ним понимаются не столько простые пользователи, сколько крупные частные корпорации, государственные компании и, главное, военные организации самих США) вынуждена была несколько раз несмотря на свои попытки остановить её, объявить о продлении поддержки (последний раз была обещана поддержка до 2014 года). До сих пор в магазинах компьютерной техники можно купить нетбуки с предустановленной Windows XP.

Так что выпуск IE9, который будет работать только под Vista и Windows 7 - это, по-видимому, шаг в направлении, призванном заставить пользователей перейти на одну из последних версий Windows, приплатив Microsoft`у ещё немного денег - другого объяснения я не вижу. Конечно, Microsoft в этом не признаётся, утверждая, что Windows XP морально устарел и не поддерживает тех высоких технологий, которые были использованы в супер-совершенном браузере IE9. Однако Google Chrome, Mozilla FireFox, Apple Safari и Opera прекрасно поддерживают стандарты W3C и ECMA, которые поддерживает IE9, при этом отлично работают под Windows XP...
Могу лишь предположить, что маркетологи Microsoft почему-то думают, что Web-разработчики на радостях от того, что Microsoft поддержала стандарты, сейчас навояют кучи сайтов под них (которые будут смотреться только в IE9). Потом же огромная масса пользователей, уныло наблюдая, как эти сайты у них глючат, от безысходности поковыляет в сторону магазинов покупать Windows 7, чтобы не потерять для себя "ускользающую красоту" интернета. На мой же взгляд, даже если это произойдёт, таким пользователям будет значительно проще и, главное, дешевле поставить себе какой-либо из бесплатных браузеров, поддерживающих стандарты (Chrome, FireFox, Opera или Safari), чем обновлять ОС.
Но этого, очевидно, не будет. Я уже писал и повторюсь снова: рынок разработки сайтов в высшей степени конкурентен, и заказчики тут определяют значительно больше, чем разработчики. А если коммерческий успех им всё-таки нужен, то при заказе сайтов у дизайн-студий или одиночек-freelancer`ов они будут ориентироваться на статистику использования браузеров пользователями и в соответствии с ней будут определять приоритеты.

Кстати, о статистике. Сбор адекватной статистики в последнее время становится всё более и более сложной задачей. Если раньше основной массой пользователей интернета были представители среднего класса, то сейчас в него активно вливаются и люди достаточно бедные. Интернет действительно постепенно расслаивается на сегменты, соответствующие различным слоям общества. Точнее, не интернет расслаивается, а разные слои общества проникают в интернет. Если даже 2-3 года назад приходилось слышать высказывания медийщиков (например, гл. редактор Russia.Ru), что Internet – это media для среднего класса, и тогда это походило на правду, то теперь мне всё ближе утверждение А.Лебедева о том, что социальные сети – это «Internet для бедных» (такое мнение было недавно высказано им в программе «Бизнес-секреты с Олегом Тиньковым» 21.02.2011).
По-этому если раньше можно было приблизительно ориентироваться на общую статистику использования браузеров в зоне runet`а, то сейчас всё чаще приходится слышать о том, что бы проводить исследования чуть ли не под каждый проект, что бы понять, какими браузерами в основном пользуется target group, на которую этот проект нацелен. И если в первом случае можно найти десяток сайтов, которые дадут более или менее реальную картину, то во втором – это платное исследование, которое часто получится не рентабельным. По хорошему, нужно было бы более или менее рассортировать интернет-пользователей на группы, примерно соответствующие уровню доходов, и определить, какие их доли используют какие браузеры. Тогда для каждой группы можно было бы описать статистику используемых ими браузеров, что бы каждый, кто делает сайт именно для этой группы, мог ориентироваться на неё. Но сайтов с такой стстистикой я, к сожалению, не знаю...

Кстати, несколько лет назад в нашей стране был осуществлён мощный проект - все почтовые отделения России были подключены к интернету. Это не только города, но и сёла и даже многие деревни. Компьютеры для этого использовались, понятно, слабенькие и оснащались IE6 - расходы и так, думаю, были фантастическими. Так что теперь на почте каждый житель России может получить доступ к Internet. Но это будет Internet через IE6, и обновлять они это всё, похоже, в ближайшее время не собираются...

В общем, радость, конечно, не полная, но это всё-таки радость. По большому счёту, поддержка стандартов будет, ибо рано или поздно, на IE9 перейдут все пользователи IE. А требование обратной совместимости не позволит Microsoft`у отказаться от поддержки стандартов на том уровне, на котором это реализовано в IE9. Это, кстати, и подтверждает IE10 Tech Preview (уровень поддержки стабилен - ACID3 проходится на 95%, хотя отдельные нововведения, в основном из категории HTML5, постепенно вводятся - их просто ещё не успели внести в тест ACID).

Так же среди изменений за эти полтора года следует отметить резкий рост популярности коммуникаторов и планшетных компьютеров, прежде всего, на базе IPhone OS и Google Android. Ваш покорный слуга уже использует вместо телефона Samsung Galaxy Tab и находит это весьма удобным. Эти устройства так же здорово влияют на рынок и, что весьма приятно, их браузеры изначально ориентированы на поддержку стандартов, т.е. проблем при совместимой разработке под них сайтов особых нет.

Перспективы

И вот, когда я в последнее время думаю о перспективах развития RIA (Rich Internet Applications), SaaS и Облаков, меня всё чаще посещает мысль о том, что в ближайшем будущем клиентская часть Web-приложения могла бы полностью взять на себя выполнение бизнес-логики.
Конечно, у такого перехода будет масса проблем, прежде всего - безопасность и производительность. Ведь у JavaScript даже в последней реализации ECMAScript v.5 существует масса проблем с этим, главным образом потому, что этот язык по-прежнему остаётся интерпретируемым, а значит его алгоритмы легко вскрыть и внедриться в их работу. Однако в последнее время наблюдается настойчивая попытка разработчиков разнообразного инструментария решить все эти проблемы и, надо думать, им это со временем как-нибудь удастся. Ведь за этим стоят не только open-source`ники, в отношении которых скепсис ещё был бы хоть как-то уместен (даже несмотря на вполне достойные продукты типа Lunux`а, PostgreSQL`я, Eclipse`а и JBoss`а), но и такие гиганты индустрии, как Google и Yahoo!.

Почему мне так кажется? Для такой ситуации я вижу несколько предпосылок:

  1. С одной стороны, мы имеем устойчивую тенденцию перехода к модели сервисов, которая, напомню, состоит в том, что персональный компьютер представляет собой лишь терминал, связывающий пользователя с сервером, и все сервисы пользователь через него получает от сервера.
  2. При этом мощности компьютеров падать явно не собираются. Даже специально разработанные для Google Chrome OS ноутбуки (chromebook`и) достаточно мощны (4ГБ ОЗУ, 1,5ГГц`овые AMD-процессоры), не говоря уже о других нетбуках и ноутбуках.

Думаю, последнее обстоятельство связано с тем, что ситуация на рынке производства компьютеров (во многом "благодаря" застою в области web, организованного Microsoft`ом в течение последних 10 лет), уже прошла ту самую "точку невозврата". То есть технологии переразвиты по сравнению с потребностями, а экономия на масштабе для производителя стала перевешивать даже отсутствие реального спроса на вычислительные мощности. Таким образом, мы имеем ситуацию, когда человеку нужен маломощненький компьютер, но он не может его найти на рынке, поскольку произвести такой оказывается чуть-ли не дороже, чем значительно более мощный, запущенный в массовое производство. Так что даже если мощный компьютер будет немного дороже, оказывается проще купить его, чем искать дешевле тот, который нужен, т.е. которого будет достаточно для задач пользователя. А пользователь просто купит, чтобы было "с запасом". Такие явления на рынке называют "экономикой изобилия", в которой спрос уже является не столько источником, сколько следствием предложения, являясь скорее предметом роскоши, нежели необходимости.

Хотя возможно, я ошибаюсь, отводя Microsoft главную роль в этом процессе. Ведь всем известно, что исторически гонка мощностей персоналок по крайней мере у домашних пользователей обеспечивалась, прежде всего, развитием компьютерных игр, всегда столь жадных до вычислительных ресурсов в погоне за реалистичностью, красотой и скоростью отображения виртуальной реальности. Возможно, главную роль в этом процессе сыграли именно они, а, может быть, и что-то третье - тут уже, что называется, можно спорить, но следствие налицо: какими бы ни были причины, мы видим, что не смотря на развитие SaaS и "Облаков", рынок производства более мощных персональных компьютеров набрал слишком большую инерцию, что бы останавливаться и поворачивать вспять. А корпорации-производители железа будут землю рыть, чтобы искать возможности продолжать развитие этого рынка и дальше, ведь ресурсов у них для этого много.

Не исключаю даже, что они давили на Google с его проектом Google Chrome OS, и эта корпорация была вынуждена идти на уступки, что фактически теперь ставит под вопрос судьбу проекта – по крайней мере, по первым отзывам пользователей можно судить, что энтузиазм у людей поугас. Дело-то нешуточное, миллиарды долларов на кону! Было бы наивным полагать, что такой проект, который фактически угрожает привести к значительному сокращению производства компьютеров, так просто дадут осуществить даже такой компании, как они...

И по развитию рынка мобильных телефонов, смартфонов и планшетников мы видим в целом отсутствие минималистических тенденций - те самые игрушки здесь тоже играют если не ведущую, то заметно-важную роль. Несмотря на то, что лидирующие платформы Android и IPhone OS резко теряют преимущества при отсутствии возможности постоянного доступа в Internet (т.е. по сути в них сделано всё, чтобы просто-таки маниакально зависеть от сервисов интернета), мы наблюдаем лишь рост, а отнюдь не падение мощностей всё новых и новых устройств, выбрасываемых на рынок и, главное, пользующихся спросом!

Т.е. идея того, что рынок аппаратных мощностей клиентов сожмётся, в то время как мощности перекочуют на рынок серверов посредством облаков и SaaS - на поверку оказывается не верна. Подъём идёт по обоим этим направлениям. Почему? Я вижу лишь одно объяснение - привычка пользователей работать с шикарными интерфейсами. Эта идея в архитектуре enterprise-систем известна, как подход REST. За неё-то, скорее всего, и постараются ухватиться производители железа!

Вот мне и кажется, что наиболее логичным выходом из ситуации будут являться максимизация подхода RIA, т.е. развитие всё более сложных Web-клиентских приложений, которые так же, как и игрушки, начнут съедать большие вычислительные мощности персональных компьютеров, вбирая в себя как можно больше бизнес-логики приложений. Это, может быть, и не будет особенно разумным выходом, но будет отвечать интересам мощных игроков IT-индустрии, у которых в руках сосредоточено достаточно ресурсов, чтобы эту тему продавливать.

В случае успеха мы с Вами будем наблюдать, как уровень логики на стороне сервера сожмётся до минимума, подвергнется стандартизации и зафиксируется в некоторой спецификации API тех сервисов, которые будет предоставлять для логики клиентской и речь пойдёт о переходе к новой двухзвенной архитектуре. Почему двухуровневой? smile;) Потому, что уровень данных, т.е. БД, никуда, конечно же, не денется. Т.е. у нас снова будет клиент, содержащий всю логику и сервер, содержащий данные.

Даже с учётом наличия в последних браузерах гораздо более мощных средств для хранения данных на стороне клиента - таких, как userData в IE и SharedObject подключаемого Flash-модуля (про несчастные cookies умолчу) - у нас всё равно остаётся вопрос обмена данными с сервером для синхронизации различных web-клиентов между собой. Ведь мы говорим о распределённых приложениях. Конечно, теоретически, механизм синхронизации данных различных web-клиентов может происходить практически без участия сервера, с использованием механизма пиринговых сетей, но разработка механизмов такой синхронизации представляется на сегодняшний день крайне фантастичной и серьёзно об этом говорить, конечно же, слишком рано в силу огромного количества проблем с ними. Думаю, это вопрос даже не ближайшего десятилетия, так что оставим его фантастам. :)

Таким образом, Базы на стороне сервера даже в достаточно длительной перспективе не отомрут, в то время как на серверную логику Web-приложений я бы стратегическую ставку делать не стал. С другой стороны, если ставить вопрос таким образом, то на стороне клиента (т.е. в JavaScript`е) придётся писать большое количество кода для правильного доступа к данным на сервере, управлении транзакциями и прочего, что представляется крайне трудным для этого языка. Поэтому логику работы с данными нужно будет сильно адаптировать для эффективной работы из Web-клиента, максимально её упрощая.

JavaScript-приложения сейчас получают данные в форматах JSON, JSONP и JSONPP, так что, надо думать, интерфейс такой БД должен будет предоставлять данные в одном из этих форматов, либо будет разработан другой, более удобный формат на их основе. Он будет отражать структуру данных, т.е. прототипную реализацию ООП, коя реализована в JavaScript. Т.е. по сути сервер для приложения должен быть ORM Tools`ой на манер Hibernate, только выдавать объекты должен в JSON-подобном формате.

Впрочем, это станет реальным, если победит подход HTML5/SVG/JavaScript, который пока доминирует для RIA. Пока ему не хватает только грамотной поддержки 3D - VRML бесславно сошёл с арены, теперь идёт борьба между X3D от ISO, O3D от Google`а и Universal 3D от ECMA при поддержке ряда корпораций, в числе которых Adobe и HP. Если же победят технологии плагинов типа Silverlite`а или Adobe Flash, то технологически картина будет несколько иной, хотя суть от этого, мне кажется, не сильно изменится - в любом случае производители железа будут гнать нас в русло RIA, иначе им самим несдобровать...