<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8487749935709371325</id><updated>2012-01-26T15:54:32.151-08:00</updated><category term='Тонкости JavaScript'/><category term='Расширение JavaScript'/><category term='Тенденции'/><category term='Drag and Drop'/><category term='JavaScript библиотеки'/><category term='Дополнения JavaScript'/><category term='JavaScript'/><category term='Java patterns'/><category term='Effective JavaScript'/><category term='JavaScript patterns'/><title type='text'>Advanced Programming Features</title><subtitle type='html'>Статьи о программировании для профессионалов</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>32</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-5355345667534523214</id><published>2012-01-21T07:12:00.000-08:00</published><updated>2012-01-21T07:12:33.741-08:00</updated><title type='text'>__proto__ для стандартных конструкторов</title><content type='html'>Сегодня слушателями курса по JavaScript сподвигли меня на написание очередной заплатки для IE. В статье &lt;a href="http://se-la-vy.blogspot.com/2011/05/proto.html"&gt;"__proto__ во всех браузерах"&lt;/a&gt; я уже приводил код для вставки ссылки __proto__ в любой объект. Теперь настала пора вспомнить о стандартных конструкторах JavaScript - у них соответствующие ссылки так же должны вести в правильные места:&lt;pre&gt;&lt;code class="javascript"&gt;if (!Object.hasOwnProperty('__proto__')) {&lt;br /&gt; Object.prototype.__proto__ = null;&lt;br /&gt; Object.__proto__ =&lt;br /&gt; Function.__proto__ =&lt;br /&gt; Date.__proto__ =&lt;br /&gt; Number.__proto__ =&lt;br /&gt; String.__proto__&lt;br /&gt; Boolean.__proto__ = Function.prototype;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Только не спрашивайте, зачем это может быть нужно в реальном проекте - не знаю. :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-5355345667534523214?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/5355345667534523214/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=5355345667534523214' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/5355345667534523214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/5355345667534523214'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2012/01/proto.html' title='__proto__ для стандартных конструкторов'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-6269725749201859901</id><published>2011-11-22T06:58:00.001-08:00</published><updated>2011-11-22T09:07:53.586-08:00</updated><title type='text'>Задержка выхода второй версии библиотеки Drag&amp;Drop</title><content type='html'>Извиняюсь за задержку с выходом второй версии библиотеки - знаю, что обещал, знаю, что моя вина. Но поймите и Вы меня - дело в том, что когда я уже вносил в неё последние штришки, готовясь сегодня-завтра опубликовать проект, случайно услышал о &lt;a href="http://dev.w3.org/html5/spec/dnd.html"&gt;HTML5 Drag&amp;Drop`е&lt;/a&gt;. В итоге я встал перед дилеммой - добивать эту версию со своим собственным интерфейсом, или переделывать её для того, что бы она поддерживала стандартный, но пока ещё мало где (по &lt;a href="http://www.w3schools.com/html5/att_global_draggable.asp"&gt;утверждению W3Schools&lt;/a&gt;, пока только в Chrom`е и Safari) реализованный интерфейс HTML5 Drag&amp;Drop. После некоторых колебаний я решил, что теперь, после появления этой спеки, библиотека с собственным интерфейсом для Drag&amp;Drop мало кого заинтересует, так что я решил выбрать последнее.&lt;p&gt;Не всё было так просто, как хотелось бы - пока не могу придумать, как реализовать в некоторых браузерах некоторые из особенностей этого интерфейса, так что возможно, поддержка будет частичной, т.е. придётся выполнять некоторые специальные действия. Хочется избавить программистов от этого и я буду стараться, но не факт, что получится.&lt;/p&gt;&lt;p&gt;Так же для пользователей первой версии библиотеки, возможно, будет нужна обратная совместимость - поддержка интерфейса первой версии, благо реализовать её не сложно, а так же с помощью Google Closure Compiler`а выкинуть её в случае необходимости не представляется сложным, просто подложив другие экстерны.&lt;/p&gt;&lt;p&gt;К сожалению, работа отнимает довольно много времени. Похоже, успею выпустить библиотечку не раньше февраля. За то она будет HTML5-совместима! :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-6269725749201859901?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/6269725749201859901/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=6269725749201859901' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/6269725749201859901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/6269725749201859901'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/11/drag.html' title='Задержка выхода второй версии библиотеки Drag&amp;Drop'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-4246705481739152502</id><published>2011-11-10T01:51:00.000-08:00</published><updated>2012-01-25T01:50:29.581-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='Effective JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Тонкости JavaScript'/><title type='text'>Коллекции в JavaScript - допиливаем setAddEventListener</title><content type='html'>&lt;p&gt;Иногда Java-программистам в JavaScript`е не хватает коллекций. Давайте разберёмся - как можно в JavaScript без написания громоздких тяжеловесных решений обойти ситуацию, для которой нам могла бы понадобиться коллекция?&lt;/p&gt;&lt;p&gt;Итак, в Java есть три типа коллекций - List, Set и Map.&lt;/p&gt;&lt;a name="list"&gt;&lt;/a&gt;&lt;h4&gt;Эмулируем List&lt;/h4&gt;&lt;p&gt;Строго говоря, как таковая эмуляция List`а в JavaScript нам в общем-то и не нужна - по сути ей и является массив (Array). Собственно, List`ы и в Java-то нам были нужны лишь исключительно по той причине, что массивы в Java имеют чёткий заранее определённый размер, который не может быть изменён, а по задаче мы не всегда можем сказать, массив какого размера нам понадобится. Т.к. в JavaScript такой проблемы нет - нет и необходимости создавать этот тип коллекций специально.&lt;/p&gt;&lt;a name="set"&gt;&lt;/a&gt;&lt;h4&gt;Эмулируем Set&lt;/h4&gt;&lt;p&gt;Множество (Set) уникальных значений в JavaScript можно составить на основании того же массива (Array) с добавлением метода, который проверял бы уникальность добавляемого значения:&lt;pre&gt;&lt;code class="javascript"&gt;/** Определяет индекс элемента в массиве.&lt;br /&gt; * @param {Object} value&lt;br /&gt; * @return {number} индекс переданного объекта, если тот содержится в массиве.&lt;br /&gt; * Если он в нём отсутствует, возарвщается -1. */&lt;br /&gt;Array.prototype.indexOf = function(value) &lt;br /&gt;{&lt;br /&gt; var /** @type {number} */ length = this.length;&lt;br /&gt; for (var /** @type {number} */ i = 0; i &lt; length; i++)&lt;br /&gt;  if (this[i] == value)&lt;br /&gt;   return i;&lt;br /&gt; return -1;&lt;br /&gt;};&lt;br /&gt;/** Определяет, содержит лимассив переданный объект.&lt;br /&gt; * @param {Object} value&lt;br /&gt; * @return {boolean} */&lt;br /&gt;Array.prototype.contains = function(value) &lt;br /&gt;{&lt;br /&gt; return this.indexOf(value) === -1;&lt;br /&gt;};&lt;/code&gt;&lt;/pre&gt;Теперь достаточно просто перед добавлением элемента проверять его методом &lt;code&gt;contains&lt;/code&gt; - для простых зазачь этого вполне хватит.&lt;/p&gt;&lt;a name="map"&gt;&lt;/a&gt;&lt;h4&gt;Эмуляция Map&lt;/h4&gt;&lt;p&gt;С коллекциями типа map будет немного сложнее. В принципе, для наиболее частого случая, когда в качестве ключей нас устроят строки, решение тривиально - это &lt;code&gt;Object&lt;/code&gt;. Т.е. создаём объект - он и есть наша карта. Его свойства - это ключи, а их значения - это значения полей коллекции.&lt;/p&gt;&lt;p&gt;Но вот что делать, если ключами по логике задачи не должны быть строки, а ими должны быть другие объекты? Для того, что бы действовать в этой ситуации, нужен эффективный механизм преобразования объектов в строку. В качестве такого метода в общем случае может выступать какое-либо специальное поле-идентификатор, либо метод, возвращающий уникальное строковое значение для каждого объекта.&lt;/p&gt;&lt;a name="mapExample"&gt;&lt;/a&gt;&lt;h4&gt;Пример эмуляции Map&lt;/h4&gt;&lt;p&gt;Например, в &lt;a href="http://se-la-vy.blogspot.com/2011/09/patch-javascript.html"&gt;одной из недавних статей, где я рассматривал шаблон "Заплатка"&lt;/a&gt;, я писал о методе создать на основе механизма навешивания слушателей в IE6-8 (&lt;code&gt;attachEvent&lt;/code&gt;) стандартный механизм навешивания слушателей (&lt;code&gt;addEventListener&lt;/code&gt;). Решение, напомню, было таким:&lt;pre&gt;&lt;code class="javascript"&gt;function setAddEventListener()&lt;br /&gt;{&lt;br /&gt;    if ('attachEvent' in this &amp;&amp; !('addEventListener' in this))&lt;br /&gt;        this.addEventListener = function(eventName, handler, isCapturing) {&lt;br /&gt;            if (isCapturing)&lt;br /&gt;                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");&lt;br /&gt;            attachEvent("on" + eventName, handler);&lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt;setAddEventListener.call(document);//Для document`а, например, вызываем так&lt;/code&gt;&lt;/pre&gt;Однако, если подходить серьёзно, то с этим решением в реальном проекте будет некоторое количество проблем. Первая из них - неполная совместимость IE`шного объекта &lt;code&gt;Event&lt;/code&gt; и стандартного. Путь для решения этой проблемы я уже демонстрировал в &lt;a href="http://se-la-vy.blogspot.com/2011/10/enrichment-pseudo-constructor.html"&gt;статье про pattern "Enrichment"&lt;/a&gt;. Напомню, что там получилось:&lt;pre&gt;&lt;code ckass="javascript"&gt;/** Конструктор, служащий для придания не совместимым с W3C DOM level 3 объектам&lt;br /&gt; * Event стандартного поведения.&lt;br /&gt; * @constructor&lt;br /&gt; * @extends Event */&lt;br /&gt;function EventW3C() {&lt;br /&gt;    /** Отменяет поведение браузера для данного события по-умолчанию. */&lt;br /&gt;    this.preventDefault = function() {&lt;br /&gt;        this.returnValue = false;&lt;br /&gt;    };&lt;br /&gt;    // Выставление других стандартных свойств&lt;br /&gt;&lt;br /&gt;    return this;&lt;br /&gt;}&lt;br /&gt;function setAddEventListener()&lt;br /&gt;{&lt;br /&gt;    if ('attachEvent' in this &amp;&amp; !('addEventListener' in this))&lt;br /&gt;        this.addEventListener = function(eventName, handler, isCapturing) {&lt;br /&gt;            if (isCapturing)&lt;br /&gt;                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");&lt;br /&gt;            attachEvent("on" + eventName, function() {&lt;br /&gt;                handler(EventW3C.call(event) //Здесь используется pattern "Enrichment"&lt;br /&gt;            });&lt;br /&gt;        }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Вторая проблема - ссылка &lt;code&gt;this&lt;/code&gt;. В обработчик IE6-8 по ней не передаётся элемент, с которым связано событие. Решение простое - запомнить ссылку на элемент в отдельной переменной в замыкании и передать её в качестве ссылки &lt;code&gt;this&lt;/code&gt; обработчику при помощи метода &lt;code&gt;call&lt;/code&gt; объекта &lt;code&gt;Function&lt;/code&gt;:&lt;pre&gt;&lt;code ckass="javascript"&gt;function setAddEventListener()&lt;br /&gt;{&lt;br /&gt;    if ('attachEvent' in this &amp;&amp; !('addEventListener' in this))&lt;br /&gt;        this.addEventListener = function(eventName, handler, isCapturing) {&lt;br /&gt;            if (isCapturing)&lt;br /&gt;                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");&lt;br /&gt;&lt;br /&gt;                //Запоминаем элемент&lt;br /&gt;                var /** @type Element */ that = this;&lt;br /&gt;&lt;br /&gt;                this.attachEvent(&lt;br /&gt;                    'on' + eventName, function() {&lt;br /&gt;                        handler.call(that, EventW3C.call(event));//Правильно вызываем обработчик&lt;br /&gt;                    }&lt;br /&gt;                );&lt;br /&gt;        }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;При этом приёме, правда, как утверждает Д.Флэнаган, есть проблема с утечкой памяти в ранних версиях IE, но это сейчас out of scope. И, наконец, третья проблема - всплывает, когда мы попытаемся рассмотреть применение механизма удаления обработчика IE6-8 &lt;code&gt;detachEvent&lt;/code&gt; для эмуляции стандартного &lt;code&gt;removeEventListener&lt;/code&gt;. Дело в том, что т.к. мы используем не сам handler, а функцию, которая его вызывает, то и detach`ить нам нужно её, а не изначальный handler. Соответственно, для удаления нам где-то нужно её найти, так? Но в стандартный метод &lt;code&gt;removeEventListener&lt;/code&gt; будет передаваться лишь сам handler. Значит, нам нужно по handler`у найти настраивающую его функцию. Тут-то нам и понадобится коллекция типа Map.&lt;/p&gt;&lt;p&gt;Итак, есть соответствие двух функций и по ключу - одной функции мы должны найти значение - другую функцию, что бы её удалить из обработчиков данного события. Т.к. Map мы эмулируем на основе Object`а, у которого в качестве имён полей, т.е. ключей, традиционно выступают строки, то нам, очевидно, нужен метод, который преобразует объект в уникальную для него строку. В общем случае эта задача не является тривиальной, но в нашем конкретном - какая удача! - для функций, т.е. для объектов Function, этот метод уже есть! И называется он - ни за что не догадаетесь! - &lt;code&gt;toString()&lt;/code&gt;! :)))) Этот метод по сути возвращает представление данной функции в виде многострочного текста её определения. Очевидно, что текст определения функции не меняется никогда и он соответствует критерию уникальности для каждой функции, а для одной и той же функции всегда будет возвращать одинаковый результат.&lt;/p&gt;&lt;p&gt;Так что вот какое компактное решение получаем в данном случае:&lt;pre&gt;&lt;code class="javascript"&gt;/** Конструктор, служащий для придания не совместимым с W3C DOM level 3 объектам Event стандартного поведения.&lt;br /&gt; * @constructor&lt;br /&gt; * @extends Event */&lt;br /&gt;function EventW3C() {&lt;br /&gt;    /** Отменяет поведение браузера для данного события по-умолчанию. */&lt;br /&gt;    if (!('preventDefault' in this))&lt;br /&gt;        this.preventDefault = function() {&lt;br /&gt;            this.returnValue = false;&lt;br /&gt;        };&lt;br /&gt;&lt;br /&gt;    if (!('stopPropagation' in this))&lt;br /&gt;        this.stopPropagation = function(){&lt;br /&gt;            this.cancelBubble = true;&lt;br /&gt;        };&lt;br /&gt;&lt;br /&gt;    // Выставление других стандартных свойств&lt;br /&gt;&lt;br /&gt;    return this;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;(/** IE patch for addEventListener and removeEventListener methods. */&lt;br /&gt;function setAddEventListener() {&lt;br /&gt;    if (!('addEventListener' in this))&lt;br /&gt;        if ('attachEvent' in this) {&lt;br /&gt;            var /** Объект-карта всех слушателей данного объекта&lt;br /&gt;                 * @type Object&amp;lt;Object&amp;lt;Function&amp;gt;&amp;gt; */&lt;br /&gt;                fnMap = {};&lt;br /&gt;            this.addEventListener = function(eventName, handler) {&lt;br /&gt;                if (!(eventName in fnMap)) //Если для данного события ещё нет коллекции обработчиков...&lt;br /&gt;                    /**@type Object&amp;lt;Function&amp;gt; */ fnMap[eventName] = {}; //...создаём её&lt;br /&gt;                var /** @type Element */ that = this; //Запоминаем элемент&lt;br /&gt;                this.attachEvent('on' + eventName,&lt;br /&gt;                    fnMap[eventName][handler.toString()] = function() {&lt;br /&gt;                        handler.call(that, EventW3C.call(event));//Правильно вызываем обработчик&lt;br /&gt;                    }&lt;br /&gt;                );&lt;br /&gt;            };&lt;br /&gt;            this.removeEventListener = function(eventName, handler) {&lt;br /&gt;                if (eventName in fnMap &amp;&amp; handler.toString() in fnMap[eventName]) {&lt;br /&gt;                    this.detachEvent(&lt;br /&gt;                        'on' + eventName,&lt;br /&gt;                        fnMap[eventName][handler.toString()]&lt;br /&gt;                    );&lt;br /&gt;                    delete fnMap[eventName][handler.toString()];&lt;br /&gt;                }&lt;br /&gt;            };&lt;br /&gt;        }&lt;br /&gt;})(); // Вызываем для window сразу&lt;br /&gt;setAddEventListener.call(document);//Для document`а, например, вызываем так&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-4246705481739152502?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/4246705481739152502/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=4246705481739152502' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4246705481739152502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4246705481739152502'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/11/javascript-setaddeventlistener.html' title='Коллекции в JavaScript - допиливаем setAddEventListener'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-7202490698821356642</id><published>2011-11-09T07:24:00.000-08:00</published><updated>2011-11-10T03:46:43.364-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Тенденции'/><title type='text'>Браузерные войны – 2 или нас ждёт JSON-Hibernate?</title><content type='html'>&lt;p&gt;Прошло примерно полтора года после (первой) публикации мной &lt;a href="http://se-la-vy.blogspot.com/2011/11/blog-post.html"&gt;статьи «Браузерные войны»&lt;/a&gt; с анализом рынка развития приложений. Тогда я описал ситуацию такой, какой увидел её на момент начала 2010 года. Теперь, в середине 2011-го, она во многом качественно изменилась. Что же произошло?&lt;/p&gt;&lt;a name="IE9AndW3CStandarts"&gt;&lt;/a&gt;&lt;h4&gt;Internet Explorer 9 и стандарты W3C&lt;/h4&gt;&lt;p&gt;Явно не по доброй воле, хотя маркетологи компании в этом и не признаются, Microsoft всё-таки сделала шаг в сторону реализации стандартов от &lt;a href="http://www.w3c.org/"&gt;W3C&lt;/a&gt; и &lt;a href="http://www.ecma-international.org/"&gt;ECMA&lt;/a&gt;, приведя свой Internet Explorer (IE) в 9-й версии в некоторое соответствие со стандартами. Конечно, о полной победе говорить пока рано. Например, тест ACID3 IE9 проходит лишь на 95%, но, безусловно, это просто несравнимо с 20% от IE8. А учитывая, что даже Mozilla Firefox проходит его лишь на 97%, думаю, можно открывать шампанское!&lt;/p&gt;&lt;p&gt;Правда есть один неприятный момент - IE9 доступен только пользователям Windows Vista и Windows 7. А вот огромной армии пользователей Windows XP, не признающих альтернативных браузеров (а таких "консерваторов" немало), так и придётся сидеть в лучшем случае под IE8.&lt;/p&gt;&lt;p&gt;Просто монополия Microsoft`а имеет одну особенность - эта компания конкурирует сама с собой. Точнее, "Microsoft сегодняшняя" жестоко конкурирует с "Microsoft вчерашней", поскольку главный конкурент новой версии Windows - это старая версия Windows, которая уже куплена громадным количеством пользователей. При этом многие из них резонно сомневаются в том, нужно ли им платить за переход на новую, если их и так всё устраивает? И Windows XP оказался для них очень мощной преградой хотя бы потому, что обладает преимуществами в быстродействии. Это в свою очередь делает его более предпочтительным для многих пользователей с относительно-слабыми машинами. При этом все приложения на данной системе прекрасно выполняются. Никаких проблем нет, если не считать проблемой трудности Microsoft по продвижению Windows 7 на рынок. Microsoft до сих пор не может закрыть поддержку этой системы. А под давлением общественного мнения (думаю, что под ним понимаются не столько простые пользователи, сколько крупные частные корпорации, государственные компании и, главное, военные организации самих США) вынуждена была несколько раз несмотря на свои попытки остановить её, объявить о продлении поддержки (последний раз была обещана поддержка до 2014 года). До сих пор в магазинах компьютерной техники можно купить нетбуки с предустановленной Windows XP.&lt;/p&gt;&lt;p&gt;Так что выпуск IE9, который будет работать только под Vista и Windows 7 - это, по-видимому, шаг в направлении, призванном заставить пользователей перейти на одну из последних версий Windows, приплатив Microsoft`у ещё немного денег - другого объяснения я не вижу. Конечно, Microsoft в этом не признаётся, &lt;a href="http://habrahabr.ru/blogs/ie/103927/"&gt;утверждая, что Windows XP морально устарел и не поддерживает тех высоких технологий, которые были использованы в супер-совершенном браузере IE9&lt;/a&gt;. Однако Google Chrome, Mozilla FireFox, Apple Safari и Opera прекрасно поддерживают стандарты W3C и ECMA, которые поддерживает IE9, при этом отлично работают под Windows XP...&lt;br/&gt;Могу лишь предположить, что маркетологи Microsoft почему-то думают, что Web-разработчики на радостях от того, что Microsoft поддержала стандарты, сейчас навояют кучи сайтов под них (которые будут смотреться только в IE9). Потом же огромная масса пользователей, уныло наблюдая, как эти сайты у них глючат, от безысходности поковыляет в сторону магазинов покупать Windows 7, чтобы не потерять для себя "ускользающую красоту" интернета. На мой же взгляд, даже если это произойдёт, таким пользователям будет значительно проще и, главное, дешевле поставить себе какой-либо из бесплатных браузеров, поддерживающих стандарты (Chrome, FireFox, Opera или Safari), чем обновлять ОС.&lt;br/&gt;Но этого, очевидно, не будет. Я уже писал и повторюсь снова: рынок разработки сайтов в высшей степени конкурентен, и заказчики тут определяют значительно больше, чем разработчики. А если коммерческий успех им всё-таки нужен, то при заказе сайтов у дизайн-студий или одиночек-freelancer`ов они будут ориентироваться на статистику использования браузеров пользователями и в соответствии с ней будут определять приоритеты.&lt;/p&gt;&lt;p&gt;Кстати, о статистике. Сбор адекватной статистики в последнее время становится всё более и более сложной задачей. Если раньше основной массой пользователей интернета были представители среднего класса, то сейчас в него активно вливаются и люди достаточно бедные. Интернет действительно постепенно расслаивается на сегменты, соответствующие различным слоям общества. Точнее, не интернет расслаивается, а разные слои общества проникают в интернет. Если даже 2-3 года назад приходилось слышать высказывания медийщиков (например, гл. редактор Russia.Ru), что Internet – это media для среднего класса, и тогда это походило на правду, то теперь мне всё ближе утверждение А.Лебедева о том, что социальные сети – это «Internet для бедных» (такое мнение было недавно высказано им в &lt;a href="http://www.youtube.com/watch?v=MKoNHZbERMQ"&gt;программе «Бизнес-секреты с Олегом Тиньковым» 21.02.2011&lt;/a&gt;).&lt;br/&gt;По-этому если раньше можно было приблизительно ориентироваться на общую статистику использования браузеров в зоне runet`а, то сейчас всё чаще приходится слышать о том, что бы проводить исследования чуть ли не под каждый проект, что бы понять, какими браузерами в основном пользуется target group, на которую этот проект нацелен. И если в первом случае можно найти десяток сайтов, которые дадут более или менее реальную картину, то во втором – это платное исследование, которое часто получится не рентабельным. По хорошему, нужно было бы более или менее рассортировать интернет-пользователей на группы, примерно соответствующие уровню доходов, и определить, какие их доли используют какие браузеры. Тогда для каждой группы можно было бы описать статистику используемых ими браузеров, что бы каждый, кто делает сайт именно для этой группы, мог ориентироваться на неё. Но сайтов с такой стстистикой я, к сожалению, не знаю...&lt;/p&gt;&lt;p&gt;Кстати, несколько лет назад в нашей стране был осуществлён мощный проект - все почтовые отделения России были подключены к интернету. Это не только города, но и сёла и даже многие деревни. Компьютеры для этого использовались, понятно, слабенькие и оснащались IE6 - расходы и так, думаю, были фантастическими. Так что теперь на почте каждый житель России может получить доступ к Internet. Но это будет Internet через IE6, и обновлять они это всё, похоже, в ближайшее время не собираются...&lt;/p&gt;&lt;p&gt;В общем, радость, конечно, не полная, но это всё-таки радость. По большому счёту, поддержка стандартов будет, ибо рано или поздно, на IE9 перейдут все пользователи IE. А требование обратной совместимости не позволит Microsoft`у отказаться от поддержки стандартов на том уровне, на котором это реализовано в IE9. Это, кстати, и подтверждает IE10 Tech Preview (уровень поддержки стабилен - ACID3 проходится на 95%, хотя отдельные нововведения, в основном из категории HTML5, постепенно вводятся - их просто ещё не успели внести в тест ACID).&lt;/p&gt;&lt;p&gt;Так же среди изменений за эти полтора года следует отметить резкий рост популярности коммуникаторов и планшетных компьютеров, прежде всего, на базе IPhone OS и Google Android. Ваш покорный слуга уже использует вместо телефона Samsung Galaxy Tab и находит это весьма удобным. Эти устройства так же здорово влияют на рынок и, что весьма приятно, их браузеры изначально ориентированы на поддержку стандартов, т.е. проблем при совместимой разработке под них сайтов особых нет.&lt;/p&gt;&lt;a name="perspectives"&gt;&lt;/a&gt;&lt;h4&gt;Перспективы&lt;/h4&gt;&lt;p&gt;И вот, когда я в последнее время думаю о перспективах развития RIA (Rich Internet Applications), SaaS и Облаков, меня всё чаще посещает мысль о том, что в ближайшем будущем клиентская часть Web-приложения могла бы полностью взять на себя выполнение бизнес-логики.&lt;br/&gt;Конечно, у такого перехода будет масса проблем, прежде всего - безопасность и производительность. Ведь у JavaScript даже в последней реализации ECMAScript v.5 существует масса проблем с этим, главным образом потому, что этот язык по-прежнему остаётся интерпретируемым, а значит его алгоритмы легко вскрыть и внедриться в их работу. Однако в последнее время наблюдается настойчивая попытка разработчиков разнообразного инструментария решить все эти проблемы и, надо думать, им это со временем как-нибудь удастся. Ведь за этим стоят не только open-source`ники, в отношении которых скепсис ещё был бы хоть как-то уместен (даже несмотря на вполне достойные продукты типа Lunux`а, PostgreSQL`я, Eclipse`а и JBoss`а), но и такие гиганты индустрии, как Google и Yahoo!.&lt;/p&gt;&lt;p&gt;Почему мне так кажется? Для такой ситуации я вижу несколько предпосылок:&lt;ol&gt;&lt;li&gt;С одной стороны, мы имеем устойчивую тенденцию перехода к модели сервисов, которая, напомню, состоит в том, что персональный компьютер представляет собой лишь терминал, связывающий пользователя с сервером, и все сервисы пользователь через него получает от сервера.&lt;/li&gt;&lt;li&gt;При этом мощности компьютеров падать явно не собираются. Даже специально разработанные для Google Chrome OS ноутбуки (chromebook`и) достаточно мощны (4ГБ ОЗУ, 1,5ГГц`овые AMD-процессоры), не говоря уже о других нетбуках и ноутбуках.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Думаю, последнее обстоятельство связано с тем, что ситуация на рынке производства компьютеров (во многом "благодаря" застою в области web, организованного Microsoft`ом в течение последних 10 лет), уже прошла ту самую "точку невозврата". То есть технологии переразвиты по сравнению с потребностями, а экономия на масштабе для производителя стала перевешивать даже отсутствие реального спроса на вычислительные мощности. Таким образом, мы имеем ситуацию, когда человеку нужен маломощненький компьютер, но он не может его найти на рынке, поскольку произвести такой оказывается чуть-ли не дороже, чем значительно более мощный, запущенный в массовое производство. Так что даже если мощный компьютер будет немного дороже, оказывается проще купить его, чем искать дешевле тот, который нужен, т.е. которого будет достаточно для задач пользователя. А пользователь просто купит, чтобы было "с запасом". Такие явления на рынке называют "экономикой изобилия", в которой спрос уже является не столько источником, сколько следствием предложения, являясь скорее предметом роскоши, нежели необходимости.&lt;/p&gt;&lt;p&gt;Хотя возможно, я ошибаюсь, отводя Microsoft главную роль в этом процессе. Ведь всем известно, что исторически гонка мощностей персоналок по крайней мере у домашних пользователей обеспечивалась, прежде всего, развитием компьютерных игр, всегда столь жадных до вычислительных ресурсов в погоне за реалистичностью, красотой и скоростью отображения виртуальной реальности. Возможно, главную роль в этом процессе сыграли именно они, а, может быть, и что-то третье - тут уже, что называется, можно спорить, но следствие налицо: какими бы ни были причины, мы видим, что не смотря на развитие SaaS и "Облаков", рынок производства более мощных персональных компьютеров набрал слишком большую инерцию, что бы останавливаться и поворачивать вспять. А корпорации-производители железа будут землю рыть, чтобы искать возможности продолжать развитие этого рынка и дальше, ведь ресурсов у них для этого много.&lt;/p&gt;&lt;p&gt;Не исключаю даже, что они давили на Google с его проектом Google Chrome OS, и эта корпорация была вынуждена идти на уступки, что фактически теперь ставит под вопрос судьбу проекта – по крайней мере, по первым отзывам пользователей можно судить, что энтузиазм у людей поугас. Дело-то нешуточное, миллиарды долларов на кону! Было бы наивным полагать, что такой проект, который фактически угрожает привести к значительному сокращению производства компьютеров, так просто дадут осуществить даже такой компании, как они...&lt;/p&gt;&lt;p&gt;И по развитию рынка мобильных телефонов, смартфонов и планшетников мы видим в целом отсутствие минималистических тенденций - те самые игрушки здесь тоже играют если не ведущую, то заметно-важную роль. Несмотря на то, что лидирующие платформы Android и IPhone OS резко теряют преимущества при отсутствии возможности постоянного доступа в Internet (т.е. по сути в них сделано всё, чтобы просто-таки маниакально зависеть от сервисов интернета), мы наблюдаем лишь рост, а отнюдь не падение мощностей всё новых и новых устройств, выбрасываемых на рынок и, главное, пользующихся спросом!&lt;/p&gt;&lt;p&gt;Т.е. идея того, что рынок аппаратных мощностей клиентов сожмётся, в то время как мощности перекочуют на рынок серверов посредством облаков и SaaS - на поверку оказывается не верна. Подъём идёт по обоим этим направлениям. Почему? Я вижу лишь одно объяснение - привычка пользователей работать с шикарными интерфейсами. Эта идея в архитектуре enterprise-систем известна, как подход REST. За неё-то, скорее всего, и постараются ухватиться производители железа!&lt;/p&gt;&lt;p&gt;Вот мне и кажется, что наиболее логичным выходом из ситуации будут являться максимизация подхода RIA, т.е. развитие всё более сложных Web-клиентских приложений, которые так же, как и игрушки, начнут съедать большие вычислительные мощности персональных компьютеров, вбирая в себя как можно больше бизнес-логики приложений. Это, может быть, и не будет особенно разумным выходом, но будет отвечать интересам мощных игроков IT-индустрии, у которых в руках сосредоточено достаточно ресурсов, чтобы эту тему продавливать.&lt;/p&gt;&lt;p&gt;В случае успеха мы с Вами будем наблюдать, как уровень логики на стороне сервера сожмётся до минимума, подвергнется стандартизации и зафиксируется в некоторой спецификации API тех сервисов, которые будет предоставлять для логики клиентской и речь пойдёт о переходе к новой двухзвенной архитектуре. Почему двухуровневой? smile;) Потому, что уровень данных, т.е. БД, никуда, конечно же, не денется. Т.е. у нас снова будет клиент, содержащий всю логику и сервер, содержащий данные.&lt;/p&gt;&lt;p&gt;Даже с учётом наличия в последних браузерах гораздо более мощных средств для хранения данных на стороне клиента - таких, как userData в IE и SharedObject подключаемого Flash-модуля (про несчастные cookies умолчу) - у нас всё равно остаётся вопрос обмена данными с сервером для синхронизации различных web-клиентов между собой. Ведь мы говорим о распределённых приложениях.Конечно, теоретически, механизм синхронизации данных различных web-клиентов может происходить практически без участия сервера, с использованием механизма пиринговых сетей, но разработка механизмов такой синхронизации представляется на сегодняшний день крайне фантастичной и серьёзно об этом говорить, конечно же, слишком рано в силу огромного количества проблем с ними. Думаю, это вопрос даже не ближайшего десятилетия, так что оставим его фантастам. :)&lt;/p&gt;&lt;p&gt;Таким образом, Базы на стороне сервера даже в достаточно длительной перспективе не отомрут, в то время как на серверную логику Web-приложений я бы стратегическую ставку делать не стал. С другой стороны, если ставить вопрос таким образом, то на стороне клиента (т.е. в JavaScript`е) придётся писать большое количество кода для правильного доступа к данным на сервере, управлении транзакциями и прочего, что представляется крайне трудным для этого языка. Поэтому логику работы с данными нужно будет сильно адаптировать для эффективной работы из Web-клиента, максимально её упрощая.&lt;/p&gt;&lt;p&gt;JavaScript-приложения сейчас получают данные в форматах JSON, JSONP и JSONPP, так что, надо думать, интерфейс такой БД должен будет предоставлять данные в одном из этих форматов, либо будет разработан другой, более удобный формат на их основе. Он будет отражать структуру данных, т.е. прототипную реализацию ООП, коя реализована в JavaScript. Т.е. по сути сервер для приложения должен быть ORM Tools`ой на манер Hibernate, только выдавать объекты должен в JSON-подобном формате.&lt;/p&gt;&lt;p&gt;Впрочем, это станет реальным, если победит подход HTML5/SVG/JavaScript, который пока доминирует для RIA. Пока ему не хватает только грамотной поддержки 3D - VRML бесславно сошёл с арены, теперь идёт борьба между X3D от ISO, O3D от Google`а и Universal 3D от ECMA при поддержке ряда корпораций, в числе которых Adobe и HP. Если же победят технологии плагинов типа Silverlite`а или Adobe Flash, то технологически картина будет несколько иной, хотя суть от этого, мне кажется, не сильно изменится - в любом случае производители железа будут гнать нас в русло RIA, иначе им самим несдобровать...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-7202490698821356642?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/7202490698821356642/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=7202490698821356642' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/7202490698821356642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/7202490698821356642'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/11/2-json-hibernate.html' title='Браузерные войны – 2 или нас ждёт JSON-Hibernate?'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-4936246914613346522</id><published>2011-11-09T04:18:00.000-08:00</published><updated>2011-11-10T03:47:00.540-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Тенденции'/><title type='text'>Браузерные войны</title><content type='html'>&lt;p&gt;&lt;i&gt;Примерно полтора года назад мной была написана статья, первоначально озаглавленная &lt;a href="http://se-la-vy.livejournal.com/53173.html"&gt;"Мечты web-разработчика" ( http://se-la-vy.livejournal.com/53173.html )&lt;/a&gt;. Немного позже я опубликовал её на сайте AV-School.ru - учебного проекта "Лаборатории Касперского", в которой тогда работал (там материал был скромно озаглавлен &lt;a href="http://av-school.ru/article/a-152.html"&gt;"Попытка анализа Браузерных войн" - http://av-school.ru/article/a-152.html )&lt;/a&gt;. В этой статье рассматривалась история "Браузерных Войн" и последовавших за ними событий с позиции разработки Web-клиентов (или Web-интерфейсов, что, на мой взгляд, является уже несколько устаревшим названием) в попытке объяснить логику происходивших процессов прежде всего влиянием в целях отстаивания своих интересов на рынке компании Microsoft.&lt;br/&gt;Сейчас я наблюдаю, что ситуация на рынке (главным образом в связи с выходом IE9, а так же с взрывным ростом продаж планшетных компьютеров и коммуникаторов от Apple и на базе Android) частично переломилась и проблема, на которой я заострял внимание, во многом разрешена, хотя выход из этой ситуации создал принципиально-новую конфигурацию рынка c интересными тенденциями, которую я не мог до конца предвидеть полтора года назад.&lt;br/&gt;В связи с этим настало время написать продолжение этой статьи, сделав в ней анализ того, что произошло за эти полтора года и представить картину такой, какой я вижу её в данный момент.Эта статья будет написана и опубликована в ближайшие дни и, учитывая мой переход на работу в Luxoft Training, я решил опубликовать её в этом блоге. Для тех же, кто не читал первой статьи и интересуется развитием рынка RIA (Rich Internet Applications), будет полезно для начала ознакомиться с первоначальной статьёй, каковую я и представляю на Ваш суд... &lt;/i&gt;&lt;br/&gt;&lt;br/&gt;&lt;/p&gt;&lt;blockquote style="text-align: right"&gt;&lt;i&gt;Существует 2 вида программистов:&lt;br/&gt;те, которые ненавидят Windows и программируют под Unix,&lt;br/&gt;и те, которые ненавидят Windows и программируют под Windows&lt;/i&gt;&lt;br/&gt;В.Пелевин&lt;/blockquote&gt;&lt;p&gt;Сегодня я хотел бы рассказать об известной для разработчиков, но малоизвестной для рядовых пользователей (а так же - начинающих программистов) теме - теме отношения существенной части разработчиков к политике, проводимой в настоящее время Microsoft. Периодически, из желания персонифицировать отношение к как бы эмоционально безликой корпорации, разработчики переносят своё отношение персонально на Билла Гейтса.&lt;/p&gt;&lt;p&gt;Когда я в светских разговорах упоминаю о своей позиции, далёкие от IT люди обычно склонны подозревать, что я завидую чёрной завистью его успеху, либо просто считают это проявлением какого-то бунтарского духа противоречия. Здесь я постараюсь просто и доходчиво описать, что за этим отношением стоит по крайней мере у меня, ну и, я уверен, ещё у достаточно солидного количества работников IT-сферы.&lt;/p&gt;&lt;a name="trends"&gt;&lt;h4&gt;Тенденции&lt;/h4&gt;&lt;/a&gt;&lt;p&gt;Примерно 15-20 лет назад наиболее продвинутые, думающие вперёд игроки IT-рынка стали укрепляться в подозрениях о том, что офисные приложения с Web-интерфейсом имеют огромный потенциал для вытеснения с рынка локальных приложений. Это касается доступа к документам, лёгкости их передачи и коллективного их редактирования. Кроме того, логика на стороне сервера разгружает клиентскую машину и она может быть менее мощной, а мощность, сконцентрированная таким образом на сервере даёт экономический выигрышь, т.н. "экономию от масштаба", что было бы весьма ценно для рынка.&lt;/p&gt;&lt;p&gt;Например, практически все приложения, входящие в пакет Microsoft Office, могли бы быть заменены сервисами Internet`а. И так со многим другим - более 70% программ, которыми активно пользуются люди локально, почти ничего не потеряют, за то немало приобретут, будучи перемещёнными в интернет и реализованными как интернет-сервисы.&lt;/p&gt;&lt;p&gt;А лет 10 назад это стало очевидно практически всем серьёзным участникам рынка.&lt;/p&gt;&lt;p&gt;Аналитики иногда даже говорят всвязи с этой тенденцией о том, что на новом витке спирали развития мы возвращаемся к подобию модели, господствовавшей в IT в 60-е годы - тогда у нас были огромные мощные компьютеры, называемые мейнфреймами и слебенькие терминальчики, практически ни на что не способные сами и вся задача которых - соединить пользователя с сервисами, предоставляемыми мейнфреймом. Теперь же роль тех мейнфреймов берут на себя сервера интернета и локальные машины теперь могут за счёт них терять свои собственные мощности, которые им уже не нужны - процессоры, оперативную память, винчестера и т.д., ведь для того, что бы пользоваться интернет-сервисами, достаточно довольно слабой (и, соответственно, более дешёвой) машины.&lt;/p&gt;&lt;p&gt;Конечно, речь в обозримом будущем не идёт о переносе вообще всех программ в браузер. Например, практически невозможно представить реализацию графических пакетов (таких как Adobe Photoshop), программ работы с 3D-графикой (3D MAX, не говоря уже о Maya), Архитектурных (ArchiCAD) в Web`е. Да что там такие высоты! Есть даже большие сомнения в возможности эффективного переноса разработческого инструментария (Visual Studio, IntelliJ IDEA, Eclipse, Enterprise Architect) в Web.&lt;/p&gt;&lt;p&gt;Но такого рода программы использует относительно небольшое число профессиональных работников в каких-то достаточно узких сферах и их общая доля среди пользователей компьютеров небольшая. Подавляющее же большинство пользуются практически исключительно сервисами, которые могут быть перенесены с локальных машин в интернет и от этого только выиграют. И они ничего не потеряют, если их посадят за на порядки менее мощные (и более дешёвые) компьютеры, чем те, за которыми они сейчас сидят, у которых операционная система будет состоять из одного лишь браузера, который будет предоставлять доступ к аналогичным тем, которые они использовали ранее, только уже интернет-сервисам.&lt;/p&gt;&lt;a name="TheBrowserWars"&gt;&lt;/a&gt;&lt;h4&gt;Браузерные войны&lt;/h4&gt;&lt;p&gt;Так бы ситуация и развивалась, постепенно эволюционно придя к этому, но почему-то Microsoft вдруг начала демонстрировать очень странное поведение, сильно повлиявшее на эту тенденцию.&lt;/p&gt;&lt;p&gt;Примерно в середине-конце 90-х она, обладая монополией на рынке операционных систем (ОС) под клиентские машины (в отличие от рынка ОС для серверов, где рынок ОС намного разнообразнее), вдруг начинает предпринимать действия, которые быстро получили в работах аналитиков рынка название "браузерной войны". Заключались они в том, что они внезапно включились в активную конкурентную борьбу с господствовавшим в середине 90-х интернет-браузером Netscape Navigator (NN). Свой Internet Explorer (IE) у них, конечно, уже был - 3 версия, но никто из участников рынка не рассматривал его всерьёз. Это была фактически маловажная заплатка - должны же они были в состав операционной системы включить хоть какой-никакой браузер! Не было смысла его делать хорошим - просто хоть что-то сделать, что бы как-то худо-бедно отображало текст - и довольно. По сравнению с Netscape Navigator на тот момент он был как Paintbrush по сравнению с Adobe Photoshop, как велосипед по сравнению с автомобилем. Не удивительно, что в то время практически все, кто активно пользовался интернетом, использовали именно NN - в то время это было общей нормой, фактически - стандартом де-факто.&lt;/p&gt;&lt;p&gt;Но уже в IE 4-й версии Microsoft реализовала массу нововведений, традиционно присутствовавших в NN, реализовала стандарты, использующиеся на тот момент в этой отрасли и в придачу к ним реализовала массу дополнительных возможностей, отсутствовавших в стандартах и спорящих с расширениями NN. IE из примитивной игрушки для любителей вдруг превратился в передовой браузер! Полноценный конкурент NN, он его, конечно, не превосходил, но и существенно ему не уступал. Практически все web-разработчики признают, что 4-й и 3-й IE - это просто небо и земля!&lt;/p&gt;&lt;p&gt;Дизайн-студии тут же разделились на два лагеря сторонников NN против сторонников IE и наполнили интернет сайтами, которые на первой странице писали, что "этот сайт рекомендуется просматривать в Netscape Navigator" или, наоборот - "Эта страница лучше смотрится в Internet Explorer" - и рядышком помещалась ссылочка для скачки и последующей бесплатной установки.&lt;/p&gt;&lt;p&gt;И всё бы было хорошо, если бы конкуренция между браузерами была честной и, соответственно, по законам рынка это приводило бы к ускорению развития этой отрасли, но Microsoft использовала в этой конкурентной борьбе один грязный приём, живо поставивший их конкурента на колени. Воспользовавшись своим господством на рынке операционных систем, она прибегла к тому, что в маркетинге называется "пакетной продажей". Она просто включала Internet Explorer в состав операционной системы при её выходе, сделав вид, что он бесплатен. Многие забывают, что бесплатен он только при покупке отнюдь не бесплатной лицензионной Windows, в цену которой на самом деле и входит реальная цена всех имеющихся в её составе программ и уж какая часть цены на Windows является ценой на Internet Explorer - можно только предполагать, однако это не имеет особого смысла, поскольку мы всё равно не можем купить Windows без Internet Explorer`а...&lt;/p&gt;&lt;p&gt;Никто не предлагает вам купить лицензионный Windows без IE и потом, при первой попытке выйти в интернет, скачать какой-нибудь браузер - NN, IE, Opera, Safari или какой-либо другой. Нет, наоборот, если пользователю кто-то со стороны специально не расскажет про то, что есть другие браузеры, он даже не будет знать, что есть что-то другое для хождения в интернете, кроме Internet Explorer`а. По-этому как только к этому прибавляется то качество, что этот самый IE не лучше, но и, в общем-то, не хуже других браузеров - выбор подавляющего большинства очевиден - зачем мне другой браузер, если этот у меня уже есть?&lt;/p&gt;&lt;p&gt;Netscape`у, конечно, ничего больше не оставалось, кроме как объявить в ответ на это свой браузер бесплатным и искать иные способы получения прибыли. Руководству Netscape быстро стало ясно, что при прочих равных Microsoft их вытеснит. Они признали, что это не честная борьба и противопоставить MS`у они могли только на порядок более высокое качество своего продукта. &lt;/p&gt;&lt;a name="antitrust"&gt;&lt;/a&gt;&lt;h4&gt;Антимонопольный комитет&lt;/h4&gt;&lt;p&gt;По-хорошему здесь-то и должен был бы вмешаться антимонопольный комитет и попытался запретить такую пакетную продажу, но по факту у них ничего не вышло. MS выплатил штраф, совершил какие-то формальные действия, СМИ и пресса посудачили, повозмущались и всё постепенно затихло, в итоге они как-то договорились и ситуация на рынке не изменилась.&lt;/p&gt;&lt;p&gt;Да и что может на самом деле антимонопольный комитет, если даже правительства независимых стран ничего не могут сделать против MS? В качестве примера можно привести поведение MS на рынке ЕС. В начале 2000-х годов правительство ЕС озаботилось аналогичной пакетной продажей на своей территории - MS Windows продавался в комплекте с MediaPlayer`ом и тем самым выдавливал с рынка компании, разрабатывавшие свои проигрыватели музыки. Решением суда Microsoft обязали продавать версию Windows без MediaPlayer`а на территории стран ЕС.&lt;/p&gt;&lt;p&gt;И MS даже выполнила это решение. Она действительно стала продавать Microsoft Windows без Media Player`а на территории стран ЕС! Казалось бы, производители проигрывателей музыки должны были плясать от счастья, но они быстро окончательно разорились и плясать стало некому, кроме хитрой Microsoft, ведь на прилавках европейских магазинов с программным обеспечением рядом продавались две коробки. На одной было написано, что это - Windows без MediaPlayer`а, а на второй - что это Windows с Media Player`ом (ведь речь шла о предоставлении возможности покупателям не покупать MediaPlayer`а вместе с Windows!). &lt;b&gt;&lt;i&gt;И обе эти коробки продавались по одинаковой цене!&lt;/i&gt;&lt;/b&gt; :)&lt;/p&gt;&lt;p&gt;Вот сами представьте себя европейцем, которому нужна Windows - вы приходите в магазин ПО, и видите Windows с MediaPlayer`ом и без него по одинаковой цене - что вы купите? :)&lt;/p&gt;&lt;p&gt;Вопрос о том, почему антимонопольный комитет так неэффективно сработал в этом случае - вопрос на самом деле интересный. Лично мне сложно это объяснить, не прибегая к пресловутой "теории заговора". Единственное объяснение, которое приходит на ум - в работу антимонопольного комитета по этому делу вмешались люди из правительства США. Ведь Microsoft - одни из крупнейших налогоплательщиков и так как его бизнес носит во многом и международный характер, то и налоги эта компания платит из доходов, получаемых в том числе на рынках Европы, Азии и Южной Америки. Следовательно, монополия на этом рынке обеспечивает правительству весьма немалый доход в виде налогов. Стремление же создать справедливый и честный рынок браузеров запросто могло привести к потере этой монополии и появлению сильных игроков на рынке, например, Европы, с которых налоги будет получать уже Европейский Союз и они будут идти мимо кошелька США. А если они ещё и захватят рынок США, так и вообще - деньги США начнут утекать из этой страны и оседать в карманах европейских корпораций и правительств.&lt;/p&gt;&lt;p&gt;К тому же так удобно всегда иметь под рукой справедливый повод придраться к такой крупной корпорации, что бы время от времени заставлять её идти на некоторые уступки, если вдруг её действия не понравятся политикам из Вашингтона... :)&lt;/p&gt;&lt;a name="theEndOfBrowserWars"&gt;&lt;/a&gt;&lt;h4&gt;Окончание браузерных войн&lt;/h4&gt;&lt;p&gt;Поняв всё это, руководство Netscape в целях резкого повышения качества своего браузера принимает решение, ставшее для них роковым - решение переписать свой браузер полностью, с нуля. Теперь во многих статьях и книгах по управлению IT проектами это решение приводится как иллюстрация того, как не надо делать - переписывание заняло 2 года, в течении которых был выпущен Internet Explorer 5 (и даже, если не изменяет память, 5.5), последняя же версия Netscape 4.01 в течении этого времени безнадёжно устарела, после чего все пользователи перешли на Internet Explorer, а за ними и разработчики (многие - нехотя) окончательно переориентировались на него.&lt;/p&gt;&lt;p&gt;Microsoft тогда даже учудила такой проект, как Internet Explorer для Macintosh`а, где традиционным браузером был Safari (более того - сама компания Apple в течении 5 лет ставила IE для Macintosh`а как браузер по умолчанию). Аргументация была примерно такая: раз многие дизайн-студии разрабатывают и тестируют свои сайты в IE, то, что бы не напрягать пользователей Apple Macintosh, им тоже нужно дать возможность смотреть сайты такими, какими они разрабатывались и тестировались (правда, и здесь не обошлось без "у-у-упс!" со стороны MS - далеко не все технологии, поддерживаемые в IE для Windows были реализованы в тех же версиях для Mac).&lt;/p&gt;&lt;p&gt;Выход Netscape Navigator`а 6-го состоялся (5-ю версию они пропустили), но фурора не вызвал - качественно он не превосходил Internet Explorer, да и его интерфейсных идей никто толком не понял. Для подавляющего большинства пользователей интернета этот выход прошёл незамеченным, даже бывшие пользователи NN уже окончательно забыли про этот браузер, не говоря уже о новых пользователях компьютеров и, всеми забытая и брошенная, компания была вынуждена бесславно уйти с рынка, передав все новёхонькие исходные коды своего некогда лидировавшего браузера разработчикам программ с открытым исходным кодом.&lt;/p&gt;&lt;a name="openSourceAge"&gt;&lt;/a&gt;&lt;h4&gt;Эра Open Source&lt;/h4&gt;&lt;p&gt;Разработчики программ с открытым исходным кодом обрадовались такому повороту дел и принялись дорабатывать и раскручивать этот браузер. Тогда проект получил название Mozilla и быстро стал стандартом де-факто среди пользорвателей Linux, но у него была версия и для Windows-платформы. Через некоторое время, отчасти из маркетинговых соображений, а отчасти и по технологическим причинам проект создания браузера был выделен из остальных появившихся приложений Mozilla (таких, как, например, почтовый клиент Thinderbird, система контроля версий BugFox и некоторых других) и получил название Mozilla FireFox (FF) и полностью сменил имидж - иконка динозавра была заменена на иконку лисы и т.д.&lt;/p&gt;&lt;p&gt;С точки зрения поддержки технологий создания страниц и сайтов, разработчики Open Source были в первую очередь ориентированы на стандарты. Происходило это по банальным причинам - у них был дефицит аналитиков. Ведь программный продукт - это не просто программа, он требует не только программистов, но и как минимум тестировщиков, аналитиков и менеджеров. И если более или менее сносное тестирование могут обеспечить сами разработчики-энтузиасты, то найти адекватное количество энтузиастов-аналитиков по ряду причин не получается. А кто же будет тогда писать Техническое Задание на разработку этим людям?&lt;/p&gt;&lt;p&gt;По-этому, как говорится, "не было бы счастья, да несчастье помогло" - по факту в качестве детальных инструкций на разработку в open source`ных проектах выступают спецификации стандартов, что очень удобно. :) Это коммерческие компании могут не сильно торопиться за стандартами - у них есть своё видение, свои маркетинговые и прочие соображения, которые могут накладывать отпечаток на скорость реализации того или иного стандарта. А у open source`а такого нет - но есть необходимость разработчикам-энтузиастам со всего мира иметь чёткие инструкции, понимание в деталях что и как нужно делать, что бы эффективно кооперироваться. Вот и получается, что FireFox быстро стал лидером в аспекте поддержки стандартов и заслужил по-этому безмерную любовь разработчиков сайтов и Front-end`ов (интерфейсов) корпоративных приложений, ведь в нём ничего правильно (т.е. в соответствии со стандартами) написанное, не глючило и исправно работало. :)&lt;/p&gt;&lt;a name="fairСonsequence"&gt;&lt;/a&gt;&lt;h4&gt;Последствия поражения&lt;/h4&gt;&lt;p&gt;Но для массового Internet-пользователя ситуация уже была безнадёжно запущена. Напрасно разработчики FireFox с помпой праздновали переход через 10% барьер (т.е. момент времени, когда по оценкам ряда популярных интернет-порталов, следивших, за какими браузерами сидят их пользователи, пользователей FF стало более 10%), напрасно считали, сколько миллионов раз был скачен дистрибутив FF 3-ей версии за первый день с момента выпуска - всем реальным игрокам рынка разработки сайтов было и до сих пор остаётся очевидно, что максимум, чего теперь можно добиться - это процентов 15, не больше. 15%, на которых бизнес не сделаешь.&lt;/p&gt;&lt;a name="MSDanceOnNetscapesGrave"&gt;&lt;/a&gt;&lt;h4&gt;Пляска Microsoft на могиле Netscape&lt;/h4&gt;&lt;p&gt;Впрочем, на рынке IT было много и оптимистов-идеалистов. Были люди, которые утверждали, что победа IE в "браузерной войне" рынку в целом - на руку, мол, кто сумеет сделать лучший браузер, чем имеющая для этого все условия Microsoft? У кого ещё достаточно ресурсов для того, что бы разрабатывать новые технологические новинки и подходы в области web?&lt;/p&gt;&lt;p&gt;Подзуживал их к этому и сам Билл Гейтс, ведь он был плодовит - книжечки писал, интервью многочисленные давал. И так любил порассуждать в них о светлом будущем интернета! (&lt;i&gt;Прям так и вспоминаю Михаила Саакашвилли, который как раз накануне войны с Южной Осетией по государственному телеканалу рассказывал, как он уважает осетин, как любит их культуру... Простите.&lt;/i&gt;)&lt;/p&gt;&lt;p&gt;Вот только действия MS по факту говорили скорее об обратном. Развитие IE, шедшее до поры - до времени семимильными шагами, вдруг по непонятной причине забуксовало. Постепенно застопорилась поддержка новых выходящих стандартов. Перспективные технологии HTA и HTC и вовсе были кинуты на полпути, явно не доведены до логического конца. Фактически, с версии 5.5 не создано ничего нового.&lt;/p&gt;&lt;p&gt;Организация World Wide Web Consorcium (W3C) продолжала выпускать новые и новые стандарты - CSS 3 (в IE и то частично реализована версия 2), XHTML 2.0 (поддерживается, да и то с "косяками", XHTML 1.0), XSLT 2.0 (в IE реализована лишь поддержка 1.0), XPath 2.0 (для xml-документов реализована поддержка 1.0, для html - вообще не реализована), DOM Level 3 (В IE даже DOM Level 1 реализован не полностью, плюс реализованы отдельные, точечные технологии, аналоги которых присутствуют в DOM Level 2), на фоне этого ряд мелких несоответствий со стандартом ECMA Script (такие, как разрешение использования ряда зарезервированных слов в качестве идентифткаторов и утечки памяти при использовании замыканий в работе с DOM-объектами) является сугубой мелочью. Ну и я уж не говорю про поддержку стандарта E4X или тега Canvas, давно реализованных во всех маломальски-популярных браузерах, кроме IE.&lt;/p&gt;&lt;p&gt;Кроме того, у Internet Explorer`а не реализовано достаточно удобной и незаметной системы обновления версий, в связи с чем в куче государственных компаний, а так же интернет-кафе, в почтовых отделениях и т.д., а вместе с этим - и у многих неопытных домашних пользователей и даже в конторах с консервативными системными администраторами до сих пор стоят старые версии Internet Explorer, не давая разработчикам сайтов пользоваться даже теми технологиями, которые предоставляет последняя версия IE, потому что при этом они вынуждены всегда оглядываться на "отстающих" - пользователей IE 7 и 6 (слава богу, хоть 5.5 уже ушёл в лету).&lt;/p&gt;&lt;a name="mystery"&gt;&lt;/a&gt;&lt;h4&gt;Загадка&lt;/h4&gt;&lt;p&gt;Почему так? Они ценой больших затрат "разбили в пух и прах проклятых конкурентов" и теперь... Практически ничего не делают!&lt;/p&gt;&lt;p&gt;Всё это сначала погрузило оптимистов в недоумение, кто-то из них пытался найти какие-то мутные оправдательные доводы - мол, это всё сложно (при том, что open source`ники без копейки денег прекрасно справляются!), что на Microsoft огромная ответственность (а open source`ники, под решениями которых работает весь Linux-мир, значит, безответственные?). В общем, наводят туману.&lt;/p&gt;&lt;p&gt;Скептики говорят о банальном рыночном законе спроса и предложения - мол, нет конкуренции - нет и развития. Но зачем же тогда вообще Microsoft`у было ввязываться в это противостояние с Netscape? Почему бы было не оставить этот рынок в покое, как они оставили, скажем, рынок интернет-общалок - ведь ICQ точно так же можно было выдворить с рынка общалок своим Messenger`ом, как Netscape - c рынка браузеров для интернета. Да и PaintBrush довести до профессионального уровня и потеснить Adobe Photoshop на рынке программ для обработки графики. Вообще, используя "пакетную продажу", можно выдавливать с рынка коробочных софтверных продуктов кого угодно.&lt;/p&gt;&lt;p&gt;Так почему - именно браузеры?&lt;br/&gt;Ведь денег на браузерном рынке не так уж много - Netscape, скажем, была не очень-то богатой конторой, бравшей денег лишь с корпоративных клиентов-пользователей своего браузера. Да и было в общем-то очевидно, что подними они плату слишком высоко - компаниям проще было бы проплатить создание open-source`ного браузера и использовать его.&lt;/p&gt;&lt;p&gt;Та же Opera пыталась брать денег с пользователей, но в итоге пришла к бесплатной схеме (правда, только на компьютерах - Opera mobile до сих пор остаётся платной) и получает деньги за продажу по сути приватной информации о том, куда через него ходят пользователи с тем, что бы маркетологи Internet-пространства могли анализировать эту информацию и с опорой на неё выстраивать свою маркетинговую стратегию о продвижении того или иного интернет-сервиса.&lt;/p&gt;&lt;a name="solution"&gt;&lt;/a&gt;&lt;h4&gt;Разгадка&lt;/h4&gt;&lt;p&gt;Для того, что бы понять действия MS, давайте порассуждаем о её бизнесе и о его потенциальной судьбе. Дело в том, что большую часть доходов Microsoft получает с продажи лицензионных копий своей операционной системы Windows.&lt;/p&gt;&lt;p&gt;При разработке этой операционной системы главный ориентир был - простота интерфейса и обратная совместимость. Рынок операционных систем для персональных компьютеров был наиболее чувствителен именно к этому. Они писали эту систему для домохозяек и их главной целью было создание системы, для работы с которой было бы достаточно как можно меньшего уровня технической компетенции пользователя. И они добились этой цели - на наиболее распространённой в мире платформе процессоров Intel (и совместимых с ними) у них фактически нет конкурентов. Чего стоит хотя бы заявление из стана их формальных врагов - от одного из замов президента компании Red Hut (одной из лидирующих компаний, специализирующихся на выпуске коммерческих Linux-дистрибутивов) в начале 2000-х годов о том, что в качестве операционной системы для персональных компьютеров они рекомендуют ставить Windows (это, конечно, было до появления Ubuntu - версии Linux`а, ориентированного на персональные компьютеры).&lt;/p&gt;&lt;p&gt;Их монополия настолько безальтернативна, что я даже встречал версию, высказываемую отнюдь не последними людьми нашего IT-бизнеса о том, что они (Microsoft) не просто не опасаются, а даже тайно поддерживают такие проекты, как Ubuntu Linux, т.е. проекты, которые пытаются дать альтернативу персональным пользователям - лишь для того, что бы сделать вид перед антимонопольным комитетом и клиентами, что конкуренция на этом рынке - есть! :)&lt;/p&gt;&lt;p&gt;С другой стороны, с точки зрения критериев качества серверных операционных систем Windows, мягко говоря, не блещет. Дело в том, что системные администраторы и разработчики сайтов, а так же программисты распределённых интернет-приложений - это совсем не домохозяйки. Они имеют высокий уровень технической компетенции и далеко не так чувствительны к простоте интерфейса (когда он излишне упрощён - это их часто даже раздражает), за то имеют повышенные требования к надёжности, качеству внутреннего API и быстродействию, с которыми у Windows, стремившейся к простоте интерфейса в ущерб всему остальному, дела, соответственно, обстоят далеко не так хорошо, как у тех, кто изначально ориентировался на серверный рынок. И тут явное лидерство принадлежит системам на базе Unix и его многочисленных модификаций (бесплатный - Linux, а так же Sun Solaris и ряд других). На этом рынке, насколько я помню, пару лет назад, их доля Windows Server оценивалась довольно скромно - примерно в 20% рынка. Думаю, что с тех пор она подросла и сегодня может быть 30-35%, не больше.&lt;/p&gt;&lt;p&gt;Что, конечно, далеко от практически 100%`оного доминирования на персоналках.По-этому переезд сервисов на серверную платформу с персоналок и упрощение операционной системы до уровня браузера означает для Microsoft, что их 99% рынка размениваются на 30-35% рынка серверов, на которых впредь всё и будет развиваться в то время, как рынок персональных операционных систем сжимается раза как минимум в 3. В итоге MS становится весьма заурядной конторкой - конечно, первой величины, но уже не качественно превосходящей конкурентов. И стоит ли удивляться, что Microsoft не желает себе такого падения доходов?&lt;/p&gt;&lt;p&gt;Понимая неминуемость такого финала, признавая, что прогресс неумолим, Microsoft лезет из кожи вон, что бы не потерять контроля за подавляющим большинством пользователей. Они предпринимают колоссальные усилия, пытаясь влезть на рынок распределённых интернет-приложений и корпоративных информационных систем, выпустив инструментальную платформу ".NET" с явным желанием вытеснить с рынка господствовавшую на рынке сайтов технологию PHP и господствовавшую на рынке создания крупных распределённых корпоративных приложений платформу Java EE.&lt;/p&gt;&lt;p&gt;Но для того, что бы максимизировать свою долю на рынке корпоративных и интернет-серверов, кроме денег, нужно ещё и время, а его нет. По популярности у Java и .NET`а пока примерный паритет (последняя оценка, которую я слышал, 40% Java EE, 50% - .NET и 10% все остальные платформы, при чём следует особо учитывать, что крупный бизнес охотнее работает с Java EE - решениями и только средний предпочитает .NET), а о вытеснении технологией ASP.NET технологии PHP на рынке разработки сайтов вообще говорить рано, даже не все провайдеры в принципе предоставляют услугу ASP-хостинга, при том, что без хостинга PHP никто даже не рискует выйти на этот рынок.&lt;/p&gt;&lt;p&gt;При этом рынок уже явно перегрет, широкие массы уже готовы пересаживаться на интернет-сервисы, будь они качественными, а доля MS от этого рынка даже не достигла половины. Понятно, что с нынешними финансовыми ресурсами Microsoft для них эта задача в принципе выполнимая, но нужно время. Как его выиграть?&lt;/p&gt;&lt;p&gt;Вот где собака-то и зарыта! Вот для чего им и нужен был контроль за браузерами. Вот для чего они в своё время последовательно выдавили с рынка Netscape - теперь они получили возможность притормаживать развитие интернет-сервисов при помощи своей монополии на браузерном рынке, как бы между прочим ставя много мелких палок в колёса развитию Web.&lt;/p&gt;&lt;a name="theEndOfBrowserWars"&gt;&lt;/a&gt;&lt;h4&gt;Что имеем сейчас&lt;/h4&gt;&lt;p&gt;Конечно, когда я говорю о том, что развитие IE полностью остановилось, я слегка преувеличиваю - кое-что они всё-таки меняют. Например, в IE 7 появились долгожданные закладки, которые давным-давно были у Opera и FireFox`а, худо-бедно исправляются старые ошибки, но это происходит исключительно медленно. Когда набирается критическая масса нововведений и та или иная фишка, описанная в стандарте или реализованная в одном из браузеров, начинает создавать отток большого количества пользователей, Microsoft "прозревает" и всё-таки реализует её в следующей версии своего браузера, при чём, как правило, максимально по-своему, что бы создать как можно больше неудобств разработчикам сайтов и web-интерфейсов корпоративных приложений, которые пытаются сделать свои продукты такими, что бы они хорошо смотрелись во всех браузерах.&lt;/p&gt;&lt;p&gt;Показателен пример с перспективными технологиями HTA и HTC, о которых я уже упоминал. Их разработку они, было, затеяли, но они быстро зафиксировались на достигнутом явно недостаточном для нормальной работы уровне (хорошо ещё, что они вообще их не выкинули - видимо, боялись потерять обратную совместимость решений, уже на них написанных) и дальнейшего их развития не происходит. В то время, как аналогичный по направленности XUL от разработчиков FireFox уже на порядки мощнее. Казалось бы - неужели Microsoft слабее, чем какие-то несчастные OpenSource`ники? У них меньше денег? Глупые программисты? Нет, им просто не нужна конкуренция на этом рынке, потому что им не нужно развитие этого рынка, этого направления.&lt;/p&gt;&lt;p&gt;В итоге создание развитых интернет-сервисов, требующих хитрых web-интерфейсов, весьма осложнено. Фактически, совместимой со всеми браузерами разработкой таких сложных браузерных приложений, как GMail, сервисы Google Docs, Google Map, могут заниматься только такие богатые конторы, как Google или как минимум Yandex. Не может и речи идти о том, что бы средний провейдер нанял команду из 5-7 грамотных разработчиков и реализовал у себя сервис с интерфейсом такого уровня сложности - тем не менее, я возьмусь утверждать, что это было бы возможно для браузеров, хорошо реализующих стандарты.&lt;/p&gt;&lt;p&gt;Просто пока их общая доля среди интернет-пользователей составляет не более 15%, ни один инвестор, хоть как-то думающий о возврате своих денег, не профинансирует такие проекты - нет, он будет настаивать на том, что бы какой бы хитрый интерфейс ни был у вашего приложения, он бы корректно работал в первую очередь как раз Internet Explorer`е.&lt;/p&gt;&lt;p&gt;Заказчик скорее согласится, что бы web-приложение не работало во всех остальных браузерах, чем откажется от требования корректной работы в IE. Но это не сильно облегчит жизнь разработчику. Ведь то, что реализовано в IE, делалось с желанием предоставить сервис, усложнив на сколько это возможно, с ним работу, а то, что записано в стандартах, делалось с желанием предоставить сервис, упростив работу насколько это возможно.&lt;/p&gt;&lt;p&gt;В итоге издержки разработки именно под IE съедают очень существенные ресурсы. И небольшие дизайн-студии вынуждены карабкаться на этот рынок буквально "через терни к звёздам" - весьма колючие терни Internet Explorer`а. И естественно, они в большинстве случаев могут выходить лишь с предельно-простыми c точки зрения поведения web-интерфейсами. Они так и делают - в итоге сервисы интернета развиваются в основном благодаря программированию на стороне сервера. Дисгармония просто ужасает - на сервере часто выполняются даже такие простейшие операции, как сортировка таблиц на странице или добавление новых полей формы, когда в нормальном браузере это сделать - раз плюнуть, совершенно не трогая сервера. Но это - только в нормальном браузере, а с Internet Explorer`ом придётся пободаться. Сделать можно, но придётся делать через известно что...&lt;/p&gt;&lt;p&gt;Разработчики коллекционируют решения большинства проблем совместимости стандартных браузеров и IE, при написании библиотек для языка сценариев JavaScript, которых развелось огромное количество - JQuery, Prototype, MooTools, Ext и многие другие, но это не решение проблем - сами эти библиотеки становятся всё более и более тяжеловесными и всё чаще из-за этого подвергаются критике со стороны разработчиков, стремящихся оптимизировать страницы, делать их легковесными.&lt;/p&gt;&lt;p&gt;Больше всего жаль несчастных разработчиков Opera - они с самого начала приняли, казалось бы, выигрышную стратегию - в случае конфликтов реализации IE и спецификаций W3C, реализовывать всё так, как в IE, а не так, как в стандарте. Они, очевидно, решили, что Microsoft с W3C просто разошлись во мнениях о том, в какую сторону развивать web-интерфейсы. Вот только ребята не поняли, что Microsoft не потому всё это делает, что просто видит собственный путь развития Web-технологий, отличный от того, как это видит W3C, а потому, что пытается по мере сил просто затормозить это развитие - и всё. В итоге Oper`цы оказались между двух огней - для Microsoft они - конкуренты, а для приверженцев стандартов они - жалкие трусливые отступники, как шакал Табаки в "Маугли" примкнувшие к грозному Шерхану-Microsoft`у... &lt;/p&gt;&lt;a name="newTrends"&gt;&lt;/a&gt;&lt;h4&gt;Намечающаяся тенденция&lt;/h4&gt;&lt;p&gt;К счастью, Google, прочно взявший курс на перевод сервисов в Web, на глазах всё-таки пересиливает MS даже со всеми её возможностями торможения web-разработок.&lt;/p&gt;&lt;p&gt;У этой компании, в отличие от многочисленных небольших дизайн-студий, достаточно денег для того, что бы преодолевать все капканы, которые ставит разработчикам Web-интерфейсов Microsoft - руководство этой компании когда надо и денег заплатит, наняв нужное количество разработчиков, а когда надо - и не побрезгует использовать решения open source и даже поможет этим разработчикам инфраструктурой, создав удобную среду.&lt;/p&gt;&lt;p&gt;Да и правительство США здесь вряд ли вступится за Microsoft используя не рыночные механизмы регулирования, ведь Google - такой же налогоплательщик и точно так же зарабатывает деньги во всём мире, а налоги платит - практически исключительно в США. Но при этом Google развивает отрасль, а Microsoft - стопорит, портя имидж экономической политике правительства США - а тут подворачивается такой прекрасный шанс стать "белыми и пушистыми" в глазах представителей IT-рынка во всём мире! :) Так что для правительства США переход с отстаивания антирыночными методами интересов Microsoft на мировой арене на отстаивание интересов Google - идеальный расклад! :)&lt;/p&gt;&lt;p&gt;У них есть уже и браузер, страмительно набирающий пользователей, агрессивно продвигаемый на всех порталах, владельцем которых является Google (все уже заметили, например, на YouTUBE внизу ссылочку, рекомендующую поставить Chrome, поскольку в нём эта страница смотрится лучше? smile:) ) и содержащий прекрасную реализацию всех стандартов, имеющихся на сегодняшний день. Есть инструментарий Google Web Toolkit (GWT), пока достаточно тяжеловесный, но позволяющий надёжно использовать java-технологии для создания web-интерфейсов, который сам берёт на себя заботу о межбраузерной совместимости получающихся интернет-приложений.&lt;/p&gt;&lt;p&gt;Но это всё - полумеры. А вот настоящий финал, развязка этой борьбы уже на пороге - в середине 2010 года появятся в продаже netbook`и с операционной системой Google Chrome OS, которая реализует как раз ту идею, которая уже давно просится - операционная система, которая будет представлять собой практически голый браузер. Надеюсь, что это будет началом конца если не Microsoft вообще, то по крайней мере их политики по торможению развития web-интерфейсов! :)&lt;/p&gt;&lt;p&gt;Вот простой и понятный ролик о том, что такое Google Chrome OS:&lt;/p&gt;&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/0QRO3gKj3qw" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;p&gt;&lt;i&gt;&lt;a href="http://se-la-vy.blogspot.com/2011/11/2-json-hibernate.html"&gt;Продолжение - "Браузерные войны – 2 или нас ждёт JSON-Hibernate?"&lt;/a&gt;.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;P.S. Есть ещё одна проблема со стандартизацией Web-интерфейсов, препятствующая их развитию, на которую в своё время &lt;a href="http://habrahabr.ru/blogs/webdev/31478"&gt;указал Джоел Спольски&lt;/a&gt; - отсутствие эталонной реализации для стандартов визуального отображения документов. Дело в том, что документы W3C не достаточно хорошо описывают внешние эффекты поведения тех или иных элементов в браузере и часто разобраться, как в данном конкретном случае должен отображать документ браузер в соответствии со стандартом - весьма не простое дело. В силу чего необходима эталонная реализация браузера, про которую было бы чётко сказано стандартизирующей организацией, что все элементы этот браузер отображает правильно. Роль такой эталонной реализации выполняет, например, Tomcat для Java EE Web и Glassfish для Java EE EJB.&lt;/p&gt;&lt;p&gt;Боюсь загадывать, но лично мне кажется наиболее приемлемым решение признать эталонной реализацию браузера FireFox - тогда Google сможет дополнять свой браузер по мере своих возможностей, а пересмотревшая свою политику Microsoft будет пытаться догнать и перегнать его и в случае спорных моментов, протестировав свою страницу в FireFox`е, каждому разработчику станет ясно - чей это косяк. :)&lt;/p&gt;&lt;p&gt;P.P.S. Под конец можете послушать песенку разработчика о наболевшем:&lt;/p&gt;&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/vTTzwJsHpU8" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;p&gt;ППКС! :))) &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-4936246914613346522?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/4936246914613346522/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=4936246914613346522' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4936246914613346522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4936246914613346522'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/11/blog-post.html' title='Браузерные войны'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/0QRO3gKj3qw/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-8958090765449641918</id><published>2011-11-02T04:56:00.000-07:00</published><updated>2011-11-23T11:02:44.213-08:00</updated><title type='text'>Уровни экспертизы</title><content type='html'>Когда-то давно услышал чьё-то мнение о том, что японцы - самая художественная нация. Возник законный вопрос - почему? Ведь на вскидку почти любой образованный гражданин России, США и Европы не сможет припомнить ни одного знаменитого японского художника, хотя легко назовёт несколько великих российских и европейских художников. Выяснил, что критерий тут другой - лингвистический. Дело в том, что в японском языке присутствует самое большое количество самостоятельных слов, выражающих различные оттенки цветов. Можно долго строить предположения, с чем это связано, но пока можно заключить лишь, что на каком-то этапе развития японской культуры для них было очень важно различать большое количество оттенков цветов и для их отражения в их языке появились эти слова.&lt;p&gt;Я - тренер, так что предмет моей работы - это знание. И я думаю, что уже назрела необходимость расщеплять это понятие и разобраться с тем, что мы называем "знаю", а что - нет. Т.е. разобраться с тем, что знание по сути собой представляет и каким оно в силу этого бывает, так как отсутствие возможности объясниться по существу не даёт развиваться, более глубоко разбираться в теме. Не могу сказать, что долго копался в работах по гносеологизму, которые должны были бы, видимо, в этом вопросе активно копаться, так что сильно рискую изобрести велосипед, но всё-таки напишу как я вижу вопрос, не претендуя на особую оригинальность. Надеюсь, это окажется полезным и кому-то другому.&lt;/p&gt;&lt;p&gt;Часто приходится слышать размышления о "практическом" и "теоретическом" характере того или иного знания. На мой взгляд, это разделение имеет более фундаментальный характер, чем то, о чём собираюсь написать я. Не следует забывать, что все программисты - это инженеры, а не учёные. Критерии знания учёных-теоретиков отличны от наших, поскольку от инженера, в конечном итоге, всегда требуется тот или иной результат его работы. Так что это разделение мне видится условным, поскольку теоретическое знание - это скорее прерогатива учёных. Конечно, теоретическое знание необходимо и инженеру, но чаще всего оказывается, что не во всей своей полноте, а в сильно-упрощённом и сокращённом виде - часто приходится слышать о том, что та или иная теория, выдвинутая учёными, нуждается в упрощении (обычно - по &lt;a href="http://ru.wikipedia.org/wiki/%C7%E0%EA%EE%ED_%CF%E0%F0%E5%F2%EE"&gt;закону Парето, более известному, как "Принцип 20/80"&lt;/a&gt;) для того, что бы превратиться в удобный для работы инструмент инженера.&lt;/p&gt;&lt;p&gt;Перво-наперво определим предмет обсуждения. Я бы хотел разобраться в том, что можно называть "глубиной знания" какой-то темы. Глубина бывает разной. Что бы мерить глубину, нужны какие-то объективные критерии в виде того, что позволяет знание той или иной глубины, имеющееся у человека в отношении какой-либо темы. Т.е. в качестве критериев удобнее всего взять возможности, которые открывает знание определённой глубины. Такие критерии и становятся уровнями, вносящими разметку в глубину знания.&lt;/p&gt;&lt;p&gt;Итак, что значит - знать какой либо инструмент? По своему опыту я склонен делить знание на 3 уровня:&lt;br/&gt;1. &lt;b&gt;Знакомство&lt;/b&gt; или &lt;b&gt;игровое знание&lt;/b&gt;. Знание, полученное в ходе игры с технологией. Знания на этом уровне достаточно для того, что бы делать интересные и в общем-то, как правило, никому, кроме автора, ненужные штуки на ней. Программисты так обычно и говорят - "поиграться" с какой-то технологией (например - "Я поигрался со Spring`ом"). Это означает, что на ней просто сделано что-то, что в принципе получилось и даже работает, но ничего полезного никому не приносит - не для этого делалось, а просто так.Кстати, знание именно на этом уровне обычно формируется в результате прохождения того или иного тренинга. В программировании людей, до конца прошедших этот уровень, обычно называют Junior Developer`ами или просто Junior`ами.&lt;/p&gt;&lt;p&gt;2. &lt;b&gt;Навык&lt;/b&gt; или &lt;b&gt;рабочее знание&lt;/b&gt;. Знание, полученное в ходе активного применения технологии в работе над каким-либо боевым проектом. Знания на этом уровне достаточно уже для того, что бы не просто делать ЧТО-ТО с помощью этой технологии, но и для того, что бы выполнять РЕАЛЬНЫЕ ЗАДАЧИ БИЗНЕСА, более или менее укладываясь в заявленные сроки. Т.е. необходим опыт применения этой технологии, достаточный для оценки рисков по срокам работ при получении задачи работником от своего менеджера проекта. Если такого опыта нет - просто ставить задачу бесполезно - человек не может оценить всех рисков и его согласие - это лишь согласие заняться этой задачей, а не принятие на себя ответственности за её решение в заявленные сроки. По-этому обычная обязанность Team-Lead`а (руководителя группы разработчиков) - брать на себя управление рисками при постановке задач Junior`ам.Обычно разработчиков, имеющих знание на этом уровне, называют либо просто разработчиками, либо Старшими разработчиками (Developer или Senior Developer).&lt;/p&gt;&lt;p&gt;У меня ушло очень много времени для того, что бы понять, в чём конкретно заключается разница между Senior`ом и простым Developer`ом. По сути во многих IT-организациях эти различия очень условны и на практике обычно отражают простую "выслугу лет" - например, 5 лет работал Developer`ом - после этого тебя нарекают Senior Developer`ом. Это, конечно, плохая практика с точки зрения удовлетворения карьерных амбиций - когда молодой сотрудник стремится построить свою карьеру, все его усилия сосредотачиваются на эффективном, быстром достижении каких-то целей, но когда он понимает, что цель "стать Senior`ом", фактически, является пассивно-достижимой (сама придёт к нему через 5 лет, и никак этот процесс ускорить нельзя) - это способно погрузить его в уныние.&lt;/p&gt;&lt;p&gt;Иногда говорят, что старшие разработчики должны дополнительно тянуть лямку обучения младших, однако на практике это требует уже следующего уровня (об этом - чуть ниже) и по-этому по факту выражается в пассивном обучении: такой старший разработчик говорит младшему примерно следующее - "Смотри на меня и делай, как я!" - и пишет при нём код, слегка проясняя какие-то моменты.Наиболее чёткий критерий я услышал от одного из крупных менеджеров Ланит`а. Он выразил мысль, что Senior - это тот, кому можно поставить задачу и не контролировать её выполнение, будучи полностью уверенным, что он уложится в срок или сам вовремя подаст сигнал о том, что не успевает или у него возникли какие-то иные трудности. А простому Developer`у нужен некоторый контроль, хотя, конечно, и не такой сильный, как за Junior`ом.&lt;/p&gt;&lt;p&gt;Кстати, именно по-этому Junior`ом так сложно устроиться - степень необходимого контроля за ним, выражающаяся во времени, которое тратит на него руководитель и консультирующие его по техническим вопросам коллеги, фактически часто перевешивает эффект от его работы, так что, вытягивая его на уровень Developer`а, организация часто больше тратит, чем приобретает и фактически такой сотрудник начинает приносить прибыль только после этого. Так что наём Junior`а - это лишь перспективное вложение денежных средств для компании.&lt;/p&gt;&lt;p&gt;3. &lt;b&gt;Владение темой&lt;/b&gt; или &lt;b&gt;тренерское знание&lt;/b&gt;. Уровень знания, достаточный для обучения других людей данной технологии посредством ведения тренингов, консультаций и написания обучающих материалов - книг, курсов, статей и т.д.&lt;/p&gt;&lt;p&gt;Основное различие этого уровня знания от предыдущего - навыка - состоит в таком понимании темы, которое характеризуется его &lt;strong&gt;&lt;em&gt;слабой связанностью&lt;/em&gt;&lt;/strong&gt; с психологическими особенностями самого тренера, в то время, как навык - это знание, &lt;strong&gt;&lt;em&gt;сильно связанное&lt;/em&gt;&lt;/strong&gt; с особенностями их носителя. Спросить у человека, обладающего лишь навыком, "Как ты это делаешь?" - примерно то же самое, что спросить любого человека - "Как ты дышишь?". Он, чаще всего, не сможет сказать ничего вразумительного или в лучшем случае скажет что-то, что сможет понять лишь человек, который очень хорошо его знает или очень на него похож психологически и похожим с ним образом мыслит.&lt;/p&gt;&lt;p&gt;Наверное, самый интересный момент в этой карте состоит в переходе со второго на третий уровень - подавляющее большинство менеджеров не понимают, как можно уметь использовать технологию, но не уметь ей обучать, т.е. передать это своё умение другому?&lt;/p&gt;&lt;p&gt;Действительно, многим из нас часто кажется, что если, как утверждают философы, "практика - критерий истины", то способность использовать знание - есть критерий его наличия. И, соответственно, если ты знаешь - то "чего такой жадный?", ведь, "От тебя не убудет?" - обучи другого, "Жалко, что ли?".&lt;/p&gt;&lt;p&gt;Ответ на это выглядит парадоксальным в сложившейся системе знаний и звучит просто: потому что обучаемый - другой и ему необходимо другое знание - которого у обучающего просто нет, так что тому, у кого есть лишь навык, часто совершенно нечего передавать.&lt;/p&gt;&lt;p&gt;Нужно понимать, что когда тренер даёт знания обучающимся, обучающиеся усваивают их, применяя всё тот же закон Парето - усваивают те 20% материала, которые им (о чём они судят с высоты своего опыта), могут понадобиться, а всё остальное - "вода" или, как часто говорят, "информационный шум". Но не понятно часто другое - каждый слушатель усваивает &lt;strong&gt;&lt;em&gt;свои 20%&lt;/em&gt;&lt;/strong&gt; материала. Именно эти 20% уже после того, как "обкатываются" в реальном проекте, становятся его навыком. Т.е. понятие "информационный шум" - относительно. Так же из этого вытекает и то, что тренер должен знать массу таких вещей, которых практически никогда не будет применять на практике.&lt;/p&gt;&lt;p&gt;Действительно, проведите над собой эксперимент. Найдите сейчас в своём поле зрения, например, дерево и задайте себе вопрос - какое оно? Скажите, что первым придёт в голову. Потом ещё раз - какое оно кроме того, что Вы уже сказали? И так раз 5-7, запоминая порядок, в котором Вы назвали эти признаки. Что получилось? У меня вот - Круглое в обхвате, большое, высокое, раскидистое, старое, унылое, шершавое, коричневое, красивое. Каких из этих признаков вы не назвали и что назвали такого, чего нет у меня? Попытайтесь проанализировать тот порядок, в котором вы назвали эти признаки. Это - не случайная последовательность признаков - нет! Это - картина ваших приоритетов, т.е. тех качеств, которые Вы видите в деревьях в первую очередь, поскольку именно в такой последовательности они для Вас важны. То, что вы назвали в числе последних - для вас практически не важно, а вот то, что называли первым - бросилось в глаза, значит, действительно важно. Если вы через несколько дней будете вспоминать, какое это было дерево, то вспомните, вероятно, только те его качества, которые назвали первыми. Этот порядок - это и есть проявление Вашей особенности восприятия мира вокруг на примере такого объекта, как дерево.&lt;/p&gt;&lt;p&gt;Так же происходит и с знанием. Программисты знают о том, что в подавляющем большинстве языков программирования присутствует избыточность - одни и те же вещи часто можно делать различными способами и выбор из этих способов в каждой ситуации часто диктуется субъективными причинами разработчиков. Можно критиковать этот выбор с различных точек знания - с точки зрения надёжности, производительности, гибкости (изменения поведения или масштабируемости), удобства сопровождения или сложности задачи разбора кода, но часто эти критерии выбора оказываются на практике субъективными и выбор делается интуитивно. У человека, обладающего лишь навыком, часто диапазон для такого выбора сужен, но если он знает в каждом случае лишь один способ решения проблемы, или несколько, но не все, то он уже способен справляться с практически любой задачей.&lt;/p&gt;&lt;p&gt;Но если дать ему группу желающих научиться работать с этой технологией и попросить её обучить - получится, что то, как привык действовать он, не подходит другим людям, т.к. они мыслят по-другому. У них другие приоритеты и они, применяя принцип Парето, из общего объёма знаний отсеяли бы ДРУГИЕ 20%, нежели те, которые отсеял он, восприняв то, что знает он, как "воду", "информационный шум".&lt;/p&gt;&lt;p&gt;Поэтому существует 2 подхода к решению данного вопроса.&lt;/p&gt;&lt;p&gt;Первый - тренер даёт полный объём материала, не ориентируясь ни на кого. Слушатель при этом применяет принцип Парето к тому, что слышит и пытается сформировать задел для того, что бы сформировать навык. Это обычно очень сложная задача и люди от такого рода обучения зевают, часто просят о перерывах и под конец сильно устают.&lt;/p&gt;&lt;p&gt;Второй - тренер применяет принцип Парето для группы сам, давая им только то, что с его точки зрения, им необходимо знать, когда всё остальное по этой теме - вода. От этого сильнее устаёт тренер (это, правда, зависит от того, насколько он психологически похож на среднего члена группы), но группе материал даётся легко и комфортно.&lt;/p&gt;&lt;p&gt;В реальности, конечно, не продуктивно впадать в обе эти крайности, а необходимо действовать по ситуации - пытаясь "прощупать" группу, сужать подаваемый материал до того уровня, который устроит всех слушателей. И здесь главный вопрос всегда в том, насколько группа "пёстрая", насколько из непохожих людей она состоит. Если слишком пёстрая - приходится ударяться в крайность первого метода и давать всё-всё, если же группа достаточно гомогенна, то тренер может себе позволить взять на себя частичное применение Принципа Парето и прицельно давать лишь то, что, как он считает, будет нужно слушателям.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-8958090765449641918?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/8958090765449641918/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=8958090765449641918' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/8958090765449641918'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/8958090765449641918'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/11/blog-post_02.html' title='Уровни экспертизы'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-5714434990129770879</id><published>2011-10-20T12:52:00.000-07:00</published><updated>2011-11-10T03:47:22.790-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Тонкости JavaScript'/><title type='text'>Вершина иерархии в JavaScript</title><content type='html'>Как-то раз меня спросили о том, что является вершиной ООП-иерархии в JavaScript. От меня явно ждали простого односложного ответа и я, поколебавшись, ответил, что такой вершиной является &lt;code&gt;Object&lt;/code&gt;. Мой ответ сочли неправильным - по мнению спрашивающего вершиной иерархии в JavaScript безоговорочно является &lt;code&gt;Function&lt;/code&gt;. Я попытался возразить, но мне не удалось убедить его. Думаю что он до сих пор в этом уверен. Это происходило на собеседовании на одну интересующую меня тогда вакансию и очень жаль, что они предоставили мне именно такого интервьюера - впрочем, Бог ему судья.&lt;p&gt;Тем не менее, вопрос о том, кто же является вершиной иерархии в JavaScript, действительно интересен. Взглянем на одну из самых лучших, по моему мнению, картинок, когда-либо нарисованных на тему реализации ООП в JavaScript:&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.mollypages.org/misc/jsobj.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="760" width="611" src="http://www.mollypages.org/misc/jsobj.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;(Всем, кто серьёзно программирует на JavaScript, советую выучить наизусть эту картинку. Хорошей практикой является распечатать её и повесить над рабочим местом, что бы по-чаще в неё глядеть)&lt;/p&gt;&lt;p&gt;Здесь видно, что в JavaScript существует не одно, а два дерева наследования - дерево объектов и дерево конструкторов. Т.е. хоть функции (каждая из которых технически является конструктором, несмотря на то, что не каждая используется в качестве такового) и являются объектами, но их специфика отражается на том, как проходит дерево их наследования.&lt;/p&gt;&lt;p&gt;Вообще, к любой реализации prototype-based ООП довольно трудно однозначно применить громкое для него слово "наследование". Что такое наследование в JavaScript изначально? По факту многие авторы популярных библиотек и framework`ов трактуют ээтот термин в применении к своему инструментарию по-своему и могут спорить друг с другом до изнеможения, доказывая если не истинность, то по крайней мере техническую целесообразность своей точки зрения. Так что, строго говоря, заранее чётко не определив данный термин, нельзя спрашивать и надеяться услышать правильный ответ о том, что является в JavaScript вершиной иерархии.&lt;/p&gt;&lt;p&gt;Не стану навязывать кому-то свою позицию, просто скажу, что солидарен в этом вопросе с создателями этого языка. В этом языке есть вполне чёткая конструкция - &lt;code&gt;instanceof&lt;/code&gt;, которая как раз и возвращает &lt;code&gt;true&lt;/code&gt; или &lt;code&gt;false&lt;/code&gt; в ответ на переданные ей операнды. Конечно, многие критикуют оператор &lt;code&gt;instanceof&lt;/code&gt;, призывая его не использовать, но по существу он является единственным объективным критерием того, что мы вправе называть наследованием в JavaScript - всё остальное, по сути, лишь попытки сделать вид, что это наследование.&lt;/p&gt;&lt;p&gt;Оператор &lt;code&gt;instanceof&lt;/code&gt; пробегает по дереву ссылок &lt;code&gt;__proto__&lt;/code&gt; левого операнда и сравнивает каждый из них со свойством &lt;code&gt;prototype&lt;/code&gt; правого операнда. Т.е. делает в сущности следующее (для наглядности представим, что это не оператор, а функция, которой оба операнда передаются в качестве первого и второго параметров):&lt;pre&gt;&lt;code class="javascript"&gt;function instanceOf(a, b)&lt;br /&gt;{&lt;br /&gt;    while ( (a = a.__proto__) !== null)&lt;br /&gt;        if (a === b.prototype)&lt;br /&gt;            return true;&lt;br /&gt;&lt;br /&gt;    return false;&lt;br /&gt;};&lt;/code&gt;&lt;/pre&gt;Как видим на картинке, "сколько верёвочке ни виться, а кончику - быть!" - в нормальной ситуации ссылка &lt;code&gt;__proto__&lt;/code&gt; всегда приведёт в &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Так что давайте посмотрим на картинку в контексте вопроса о том, "кто в доме хозяин" - &lt;code&gt;Object&lt;/code&gt; или &lt;code&gt;Function&lt;/code&gt;?. Итак, имеем два лагеря объектов - функции и остальные объекты.&lt;/p&gt;&lt;p&gt;Для объектов картина однозначна - для них вершиной иерархии является &lt;code&gt;Object&lt;/code&gt;, поскольку алгоритм прохода по ссылкам &lt;code&gt;__proto__&lt;/code&gt; посетит прототип ээтого конструктора последним перед тем, как упереться в &lt;code&gt;null&lt;/code&gt;. Тут всё ясно. Язык чётко даёт нам понять, что это - так:&lt;pre&gt;&lt;code class="javascript"&gt;&lt;a href="javascript:alert( (new Object()) instanceof Object);"&gt;alert( (new Object()) instanceof Object);&lt;/a&gt; // true&lt;br /&gt;&lt;a href="javascript:alert((new Object()) instanceof Function);"&gt;alert((new Object()) instanceof Function);&lt;/a&gt; // false&lt;/code&gt;&lt;/pre&gt;Что же касается функций, то все они являются дочерними по отношению к конструктору &lt;code&gt;Function&lt;/code&gt;, ссылаясь на прототип этой функции своей ссылкой &lt;code&gt;__proto__&lt;/code&gt;. И, что, видимо, и сбило с толку тогда моего интервьюера, для функции &lt;code&gt;Object&lt;/code&gt; язык (а точнее - спецификация ECMAScript) не делает в этом плане исключения - ссылка &lt;code&gt;Object.__proto__&lt;/code&gt; так же ссылается на &lt;code&gt;Function.prototype&lt;/code&gt;, т.е. вроде бы как бы &lt;code&gt;Object&lt;/code&gt; является наследником &lt;code&gt;Function&lt;/code&gt;`а, да? ;) Но вот только штука вся в том, что сам по себе &lt;code&gt;Function&lt;/code&gt;, как и любой другой конструктор, ссылается ссылкой &lt;code&gt;__proto__&lt;/code&gt; на свой &lt;code&gt;prototype&lt;/code&gt;(в сущности для конструктора &lt;code&gt;Function&lt;/code&gt; эти ссылки эквивалентны), а тот, в свою очередь, ссылается на &lt;code&gt;Object.prototype&lt;/code&gt;, что прекрасно видно на приведённой картинке. Так что в итоге вершиной иерархии всех объектов, в том числе и функций, является всё-таки &lt;code&gt;Object&lt;/code&gt;:&lt;pre&gt;&lt;code class="javascript"&gt;&lt;a href="javascript:alert(Object.__proto__ === Function.prototype);"&gt;alert(Object.__proto__ === Function.prototype);&lt;/a&gt; // true&lt;br /&gt;&lt;a href="javascript:alert(Object instanceof Function);"&gt;alert(Object instanceof Function);&lt;/a&gt; // true&lt;br /&gt;&lt;a href="javascript:alert(Function instanceof Object);"&gt;alert(Function instanceof Object);&lt;/a&gt; // true&lt;/code&gt;&lt;/pre&gt;Путаница возникает из-за того, что &lt;strong&gt;&lt;em&gt;многие программисты, привыкшие к наследованию в class based OOP языках программирования воспринимают конструкторы в JavaScript как классы и считают, что конструкторы участвуют в наследовании, хотя на самом деле в наследовании участвуют не конструкторы, а прототипы конструкторов!&lt;/em&gt;&lt;/strong&gt;. И синтаксис оператора &lt;code&gt;instanceof&lt;/code&gt; как бы потворствует этому заблуждению. Тем не менее, в JavaScript нет и не может быть никакого объекта, для которого бы выражение " &lt;code&gt;instanceof Object&lt;/code&gt;" вернуло бы false, в то время, как для аналогичного выражения с конструктором &lt;code&gt;Function&lt;/code&gt; это утверждение несправедливо - для всех объектов, не являющихся функциями, это выражение вернёт &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Короче говоря, &lt;code&gt;Object&lt;/code&gt;, как конструктор, является наследником &lt;code&gt;Function&lt;/code&gt;, но сам &lt;code&gt;Function&lt;/code&gt; - не только конструктор, но и объект, и, как объект, является наследником &lt;code&gt;Object&lt;/code&gt;`а. Ипостась же объекта для любого конструктора всегда выше его ипостаси конструктора, т.е. любая функция - в первую очередь всё-таки объект, и уже во-вторую очередь - функция, так что объектная иерархия наследования имеет приоритет перед конструкторской.&lt;/em&gt;&lt;/strong&gt;. Тем не менее, если рассматривать статические свойства (свойства функций), то конструктор &lt;code&gt;Object&lt;/code&gt; в качестве таковых, как и все остальные конструкторы, наследует свойства прототипа &lt;code&gt;Function&lt;/code&gt;, но и тот в свою очередь наследует свойства прототипа &lt;code&gt;Object&lt;/code&gt;`а. Если не понятно - всматривайтесь в картинку до тех пор, пока не поймёте.&lt;/p&gt;&lt;p&gt;Понимать это действительно важно для программирования на JavaScript, хотя, как показала практика, и может стоить не устройства на хорошую работу - но тут уж не угадаешь :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-5714434990129770879?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/5714434990129770879/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=5714434990129770879' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/5714434990129770879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/5714434990129770879'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/10/javascript.html' title='Вершина иерархии в JavaScript'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-2541919285760271886</id><published>2011-10-08T15:49:00.000-07:00</published><updated>2011-11-09T08:33:46.568-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='Effective JavaScript'/><title type='text'>Обогащение (enrichment) или псевдо-конструктор (pseudo-constructor) как JavaScript-pattern</title><content type='html'>Вторым открытым при разработке библеотеки Drag and Drop приёмом, который я бы назвал шаблоном, является обогащение (enrichment). Ещё я иногда про себя называю его псевдо-конструктор (pseudo-constructor), что считаю менее удачным, но так же подходящим для него названием.&lt;p&gt;Он служит для ситуаций, когда нужно придать некоторую дополнительную функциональность группе объектов одинакового вида.&lt;/p&gt;&lt;p&gt;Я столкнулся с этой проблемой при работе с тем же самым объектом Event для IE. Дело в том, что продемонстрированный в позапрошлой статье приём "Заплатка" (Patch) был несколько неполным. Напомню приведённый код:&lt;pre&gt;&lt;code class="javascript"&gt;(function setAddEventListener(){&lt;br /&gt;    if ('attachEvent' in this &amp;&amp; !('addEventListener' in this))&lt;br /&gt;        addEventListener = function(eventName, handler, isCapturing) {&lt;br /&gt;            if (isCapturing)&lt;br /&gt;                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");&lt;br /&gt;            attachEvent("on" + eventName, handler);&lt;br /&gt;    }&lt;br /&gt;})(); //IE patch for window.addEventListener&lt;/code&gt;&lt;/pre&gt;В этом коде, к сожалению, не было учтено, что для придания стандартного поведения функции &lt;code&gt;addEventListener&lt;/code&gt; нужно, что бы она вызывала слушателя, передавая ему объект Event, при чём это должен быть объект Event стандартного вида.&lt;/p&gt;&lt;p&gt;Но в случае, если выполнение сценария происходит в IE, у нас есть только IE`шный &lt;code&gt;Event&lt;/code&gt;, который отличается от стандартного достаточно сильно. Так что не достаточно просто подставить при вызове его - нужно обязательно придать ему нужную функциональность.&lt;/p&gt;&lt;p&gt;Имитация функциональности стандартного объекта &lt;code&gt;Event&lt;/code&gt; на почве IE`шной реализации сама по себе является сложной задачей и у меня нет уверенности, что она действительно представляет для читателей практический интерес, так что я просто проиллюстрирую принцип, показав скелет решения, а уже "наполнение мясом", каждый может делать для себя сам - по крайней мере для меня эта задача пока не актуальна. Так что упростим задачу - возьмём лишь один аспект нестандартного поведения и его сэмулируем. Например, отмена поведения браузера по-умолчанию для данного события. В стандартном W3C`шном объекте &lt;code&gt;Event&lt;/code&gt; это действие производится вызовом метода &lt;code&gt;preventDefault&lt;/code&gt;, а в IE - установкой значения &lt;code&gt;false&lt;/code&gt; в свойство &lt;code&gt;returnValue&lt;/code&gt; этого кода.&lt;/p&gt;&lt;p&gt;Итак, имеем нестандартный Event, который нужно сделать стандартным Event`ом. Для выбранного нами аспекта это означает, что ему нужно приделать такую функцию:&lt;pre&gt;&lt;code class="javascript"&gt;/** Отменяет поведение браузера для данного события по-умолчанию. */&lt;br /&gt;event.preventDefault = function(){&lt;br /&gt;    this.returnValue = false;&lt;br /&gt;};&lt;/code&gt;&lt;/pre&gt;Это нельзя сделать раз и навсегда, поскольку сам объект постоянно (при наступления каждого нового события) меняется. Тут мы, кстати, видимо, что в основу этой модели уже заложена однопоточность выполнения JavaScript-сценариев, которую сейчас так активно критикует JavaScript-сообщество. Так же однопоточность заложена в основу на этот раз стандартного конструктора &lt;code&gt;RegExp&lt;/code&gt;. Первое, что приходит на ум, это просто приписать этому объекту несколько новых свойств "не отходя от кассы" - т.е. там, где это нам понадобится. Однако это будет иная логика, которая тем самым замусорит код - будет удобнее собрать все эти операции в одном, отдельном месте.&lt;/p&gt;&lt;p&gt;Следующая мысль, уже более зрелая - создать отдельный конструктор, который наследовался бы от нестандартного Event`а и расширял бы его функционал стандартными свойствами и методами:&lt;pre&gt;&lt;code ckass="javascript"&gt;/** Конструктор, служащий для придания не совместимым с W3C DOM level 3 объектам&lt;br /&gt; * Event стандартного поведения.&lt;br /&gt; * @constructor&lt;br /&gt; * @extends Event */&lt;br /&gt;function EventW3C() {&lt;br /&gt;    /** Отменяет поведение браузера для данного события по-умолчанию. */&lt;br /&gt;    this.preventDefault = function() {&lt;br /&gt;        this.returnValue = false;&lt;br /&gt;    };&lt;br /&gt;};&lt;/code&gt;&lt;/pre&gt;Вызов же этого конструктора в нужном месте предполагается примерно такой:&lt;pre&gt;&lt;code class="javascript"&gt;EventW3C.prototype = event; //Сначала присваиваем правильный прототип конструктору&lt;br /&gt;var w3cEvent = new EventW3C(); //Затем создаём экземпляр, который будет ссылаться ссылкой __proto__ куда надо.&lt;/code&gt;&lt;/pre&gt;После этого получим объект, поведение которого соответствует стандартному и который уже можно спокойно передавать функции, не подозревающей, что она выполняется в нестандартном (IE6-8) браузере.&lt;/p&gt;&lt;p&gt;Однако нетрудно увидеть, что этот приём излишне-расточителен. Фактически, для каждого события он создаёт не один, а два объекта в памяти, так что сборщику мусора будет в два раза больше работы. Кроме того, само по себе создание объектов - не такая уж быстрая операция и хотелось бы по возможности её избегать, если это не сильно оправданно ситуацией. Не говоря уже о том, что механизм вызова из двух выражений выглядит излишне-громоздким.&lt;/p&gt;&lt;p&gt;Гораздо более экономным является такой вариант - можно вызвать эту функцию не при помощи оператора &lt;code&gt;new&lt;/code&gt;, а при помощи метода &lt;code&gt;call&lt;/code&gt;:&lt;pre&gt;&lt;code class="javascript"&gt;EventW3C.call(event);&lt;/code&gt;&lt;/pre&gt; и объект сам, без создания наследника, получит нужное свойство. Именно из-за того, что по форме это очень напоминает использование конструктора, я про себя иногда и называю этот шаблон псевдо-конструктором (pseudo-constructor).&lt;/p&gt;&lt;p&gt;Как нетрудно видеть, этот шаблон по сути является использованием реального конструктора в качестве обогащающей объект новыми свойствами функции. При этом сам по себе конструктор продолжает выполнять функцию конструктора, так что, наверное, можно придумать элегантный случай, в котором эта функция будет использована и как конструктор и как псевдо-конструктор. Единственное, что для большего удобства использования можно добавить необязательное выражение внутрь этого конструктора, а именно в его конец:&lt;pre&gt;&lt;code class="javascript"&gt;function EventW3C() {&lt;br /&gt;    /** Отменяет поведение браузера для данного события по-умолчанию. */&lt;br /&gt;    this.preventDefault = function() {&lt;br /&gt;        this.returnValue = false;&lt;br /&gt;    };&lt;br /&gt;    return this;&lt;br /&gt;};&lt;/code&gt;&lt;/pre&gt;Для использования этой функции в качестве реального конструктора это ровным счётом ничего не изменит, а вот для использования её в качестве псевдо-конструктора это может сделать вызов более удобным - тогда само выражение вызова будет возвращать результат, прямо как у реального конструктора:&lt;pre&gt;&lt;code class="javascript"&gt;var w3cEvent = EventW3C.call(event);&lt;/code&gt;&lt;/pre&gt;На последок приведу полный вариант реализации pattern`а "Заплатка" ("Patch") для эмуляции addEventListener`а, который использует приведённый pattern Обогащение (Enrichment) или псевдо-конструктор (Pseudo-Constructor):&lt;pre&gt;&lt;code class="javascript"&gt;//IE patch for window.addEventListener&lt;br /&gt;function setAddEventListener()&lt;br /&gt;{&lt;br /&gt;    if ('attachEvent' in this &amp;&amp; !('addEventListener' in this))&lt;br /&gt;        addEventListener = function(eventName, handler) {&lt;br /&gt;            if (isCapturing)&lt;br /&gt;                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");&lt;br /&gt;            attachEvent("on" + eventName, function() {&lt;br /&gt;                handler(EventW3C.call(event) //Здесь используется pattern "Enrichment"&lt;br /&gt;            });&lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt;setAddEventListener.call(document);// Применяем заплатку для объекта document&lt;/code&gt;&lt;/pre&gt;Конечно, полученные применением этого шаблона объекты не являются в строгом смысле потомками этого конструктора, о чём не применит сообщить операция &lt;code&gt;instanceof&lt;/code&gt;, но для практического применения это чаще всего не нужно, в то время как с полученными в результате этого объектами фактически можно обращаться как с наследниками. Так что для с одной стороны - экономии ресурсов, а с другой - удобства написания красивого и лаконичного кода без засорения логики, этот шаблон, на мой взгляд, прекрасно подходит.&lt;/p&gt;&lt;p&gt;P.S. Думается, что данный шаблон имеет так же большой потенциал применения для добавления свойств объектам DOM, если нужно придать им нужную функциональность. По крайней мере сейчас я прорабатываю эту идею как раз в контексте своей библиотеки - она поможет пользователям навешивать несколько обработчиков событий в ходе Drag&amp;amp;Drop-перемещения.&lt;/p&gt;&lt;p&gt;P.P.S. Пришло в голову, когда уже собирался опубликовать сообщение - а может, лучше будет назвать этот приём - Псевдо-наследование ("Pseudo-Inheritance")? Пишите в комментах Ваши версии :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-2541919285760271886?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/2541919285760271886/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=2541919285760271886' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/2541919285760271886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/2541919285760271886'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/10/enrichment-pseudo-constructor.html' title='Обогащение (enrichment) или псевдо-конструктор (pseudo-constructor) как JavaScript-pattern'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-8327799942782261591</id><published>2011-10-07T08:48:00.000-07:00</published><updated>2011-10-08T15:59:50.942-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='Тонкости JavaScript'/><title type='text'>Самонастраивающаяся функция</title><content type='html'>В ходе написания второй версии библиотеки для Drag&amp;Drop`а (уже скоро выложу и опубликую, ждите - осталось недолго :) ) была найдена парочка интересных JavaScript`овых решений, которые тоже вполне могут претендовать на звание Pattern`ов. Рискуя тем, что, возможно, изобретаю велосипед, всё-таки распишу, как я их использую.&lt;p&gt;Сегодня поговорим об одном из них, который я решил назвать "самонастраивающейся функцией". Он служит в тех ситуациях, когда заранее предсказать, какая понадобится функция затруднительно, и гораздо легче это сделать на этапе первого вызова. Т.е. для обеспечения правильной работы функции ей нужно принять первый вызов и только по нему она сможет понять, как ей работать.&lt;/p&gt;&lt;p&gt;Конечно, можно сделать в этой функции большой оператор ветвления &lt;code&gt;if&lt;/code&gt; с тем, что бы в случае одних параметров выполнять одну логику, а в случае других - другую. Но эта операция проверки будет отнимать дополнительное время, так что это приемлемо лишь для не очень критичной к скорости работы логики.&lt;/p&gt;&lt;p&gt;Именно с такой задачей я имел дело при написании второй версии своей библиотечки. Мне нужна была функция, которой передавался бы объект &lt;code&gt;pos&lt;/code&gt;, имеющий два свойства - 'x' и 'y' и от неё требовалось, что бы она проставила в них значения x и y-координат курсора мыши. Я назвал её &lt;code&gt;refreshMousePos&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Саму эту функцию должен вызывать обработчик того или иного события, который в W3C-совместимых браузерах получает ссылку на объект &lt;code&gt;Event&lt;/code&gt;, соответствующий данному событию. Для вычисления координат курсора мыши этот объект нужен, так что в этом случае ссылку на него нужно передать в &lt;code&gt;refreshMousePos&lt;/code&gt; вторым аргументом. В случае же W3C-несовместимого браузера (в основном, IE6-8), обработчику не передаётся этот объект, по-этому он и не может быть передан в функцию &lt;code&gt;refreshMousePos&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Эта функция вызывается очень-очень часто по ходу работы библиотеки, ведь каждый раз при перемещении курсора мыши в процессе перемещения нужно знать его координаты. Так что эта функция должна быть настолько быстро-выполнимой, насколько это возможно и каждый раз выполняющаяся проверка в выражении &lt;code&gt;if&lt;/code&gt; здорово бы замедлила работу библиотеки, особенно если речь идёт о слабых компьютерах.&lt;/p&gt;&lt;p&gt;Но заранее определить точно, будет или не будет передан объект &lt;code&gt;Event&lt;/code&gt; в функцию, затруднительно - гораздо удобнее это сделать в процессе работы, по факту проверив содержимое переданной переменной. При чём, это достаточно сделать один раз и сразу будет понятно, как функция должна выполняться впоследствии.&lt;/p&gt;&lt;p&gt;Сам IE меняет в соответствии с обрабатываемым событием объект event, который всегда находится у него в глобальном контексте. И интерфейс этого объекта несколько иной, что проявляется в логике вычисления координат курсора мыши.&lt;/p&gt;&lt;p&gt;Так что я сделал разделение логики следующим образом:&lt;pre&gt;&lt;code class="javascript"&gt;/** Обновить координаты мыши.&lt;br /&gt; * @param {Position} pos координаты курсора мыши&lt;br /&gt; * @param {Event} [evt] объект события */&lt;br /&gt;function refreshMousePos(pos, evt) {&lt;br /&gt;    return (refreshMousePos = typeof evt !== 'undefined' ?&lt;br /&gt;        function(pos, evt) { //W3C realization&lt;br /&gt;            var body = document.body;&lt;br /&gt;            pos.x = evt.clientX + body.scrollLeft - body.clientLeft;&lt;br /&gt;            pos.y = evt.clientY + body.scrollTop - body.clientTop;&lt;br /&gt;            return pos;&lt;br /&gt;        } :&lt;br /&gt;        function(pos) { //IE realization&lt;br /&gt;            pos.x = event.pageX;&lt;br /&gt;            pos.y = event.pageY;&lt;br /&gt;            return pos;&lt;br /&gt;        }&lt;br /&gt;    )(pos, evt); //вызываем тот вариант функции, которую присвоили и возвращаем результат выполнения&lt;br /&gt;};&lt;/code&gt;&lt;/pre&gt;Как видим, здесь переменной, которая содержит главную функцию, в зависимости от содержания ссылки &lt;code&gt;evt&lt;/code&gt; присваивается различное значение-функция, при этом выполняющаяся в данный момент функция автоматически затирается. Т.е. функция, будучи вызванной, как бы более тонко настраивает себя под конкретную среду в которой оказалась для более производительной работы в ней.&lt;/p&gt;&lt;p&gt;P.S. Спросите, зачем я возвращаю объект &lt;code&gt;pos&lt;/code&gt;, ведь его поля итак уже изменены и значит, необходимый внешний эффект достигнут? Отвечу - для того, что бы можно было после вызова функции сразу же в той же конструкции обратиться к объекту &lt;code&gt;pos&lt;/code&gt;:&lt;pre&gt;&lt;code class="javascript"&gt;alert(&lt;br /&gt;    refreshMousePos(pos, evt).x&lt;br /&gt;);&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-8327799942782261591?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/8327799942782261591/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=8327799942782261591' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/8327799942782261591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/8327799942782261591'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/10/blog-post.html' title='Самонастраивающаяся функция'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-6604391429713992448</id><published>2011-09-16T00:46:00.000-07:00</published><updated>2011-12-02T07:29:02.120-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='Тонкости JavaScript'/><title type='text'>"Заплатка" ("Patch") как JavaScript шаблон проектирования</title><content type='html'>Недавно пришла в голову мысль, что "Заплатка" вполне заслуживает звания JavaScript-шаблона проектирования. По-англицки можно было бы назвать "Patch". Суть его не собственно в хаке, а скорее в том, что бы отделять код, написанный в соответствии со стандартом, от кода, который приспосабливает браузер, не поддерживающий стандарты, корректно работать со стандартным кодом при помощи того или иного хака.&lt;p&gt;Сам я использую заплатки уже достаточно давно. Разумеется, основные заплатки ставятся на &lt;a href="http://www.luxoft-training.ru/blog/advanced-programming-features/74.html"&gt;IE6-8, с которыми, по-видимому, ещё предстоит много возни, пока они, наконец, отомрут&lt;/a&gt;. Однако так же не помешает установить их и для старых версий иных браузеров.&lt;/p&gt;&lt;p&gt;Итак, часто JavaScript-разработчики вставляют код, направленный на совместимость, непосредственно внутрь логики основного сценария. Например, если нам нужно установить слушателя события загрузки страницы, мы могли бы написать:&lt;pre&gt;&lt;code class="javascript"&gt;function onLoadListener() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; //some code...&lt;br /&gt;}&lt;br /&gt;if ('addEventListener' in window)&lt;br /&gt;&amp;nbsp; &amp;nbsp; addEventListener("load", onLoadListener, false);&lt;br /&gt;else if ('attachEvent' in window)&lt;br /&gt;&amp;nbsp; &amp;nbsp; attachEvent("onload", onLoadListener)&lt;/code&gt;&lt;/pre&gt;В принципе, ничего нет плохого в том, что бы так делать, однако проблема заключается в том, что при написании сложных сценариев ваша голова итак забита сложностью решаемой задачи и Вам не захочется отвлекаться ещё и на все эти глупые браузеросовместимости - вам бы хоть как-то её решить. И потом, при отладке и чтении кода вам всё время будет попадаться на глаза этот фрагмент и мозолить глаза. С точки зрения алгоритма он не несёт никакого смысла, а на экране занимает место, которое могли бы занять более полезные смысловые фрагменты кода - видя их одновременно, без прокрутки, Вам могла бы придти в голову ценная мысль, которая не пришла бы, если бы вам пришлось вращать колесо мыши, что бы всё увидеть.&lt;/p&gt;&lt;p&gt;Тем не менее, сократить эту конструкцию до стандартного кода&lt;pre&gt;&lt;code class="javascript"&gt;addEventListener("load", function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; //some code...&lt;br /&gt;}, false);&lt;/code&gt;&lt;/pre&gt;мы не можем, поскольку стандарты поддерживают не все браузеры, в которых нам хотелось бы, что бы наш сценарий успешно выполнялся. Для остальных же браузеров у нас есть многочисленные хаки. Так как же быть?&lt;/p&gt;&lt;p&gt;Как только я в своих проектах сталкиваюсь с несовместимостью работы того или иного браузера со стандартными методами, первая мысль у меня возникает о том, что бы вынести обеспечение совместимости в отдельный от основного код, что бы он его не захламлял. У меня уже накопилось достаточно много таких заплаток, латающих различные дыры в поддержке стандартов различным браузерами - главным образом IE. Обычно я помещаю их в отдельный файл проекта, называя его "commons.js" (есть и исключения - например в случае, если это нужно только для тестирования, я считаю не зазорным вставлять такой код просто в начало тестового файла).&lt;/p&gt;&lt;p&gt;В данном случае полезной была бы следующая заплатка:&lt;pre&gt;&lt;code class="javascript"&gt;(function setAddEventListener()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp; &amp;nbsp; if ('attachEvent' in this &amp;&amp; !('addEventListener' in this))&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; addEventListener = function(eventName, handlerб isCapturing) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (isCapturing)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 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");&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; attachEvent("on" + eventName, handler);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;})(); //IE patch for window.addEventListener&lt;/code&gt;&lt;/pre&gt;Здесь элегантно используется то обстоятельство, что ссылка &lt;code&gt;this&lt;/code&gt; при вызове в глобальном контексте в функции указывает на объект &lt;code&gt;window&lt;/code&gt;. По-этому сразу же после объявления функция просто вызывается.&lt;/p&gt;&lt;p&gt;Здесь не могу не отметить, что заплатка получилась лишь частичная - третий параметр метода &lt;code&gt;addEventListener&lt;/code&gt; невозможно эмулировать при помощи метода &lt;code&gt;attachEvent&lt;/code&gt;, доступного в IE. Так что в этом случае приходится вызывать исключение, что бы при отладке или тестировании это обязательно всплыло в виде корректного сообщения.&lt;/p&gt;&lt;p&gt;Если необходима реализация парного метода &lt;code&gt;removeEventListener&lt;/code&gt;, её легко написать по аналогии, реализовав через метод &lt;code&gt;detachEvent&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Теперь другие элементы, которым так же нужно "привить" правильное поведение, могли бы обрабатываться следующим образом:&lt;pre&gt;&lt;code class="javascript"&gt;setAddEventListener.call(document.getElementById('id1'));&lt;/code&gt;&lt;/pre&gt;В статье "JavaScript: Реализация pattern`а Singleton" я уже приводил ещё одну заплатку - для безпроблемного объявления объекта &lt;code&gt;XMLHttpRequest&lt;/code&gt; (который я &lt;a href="http://en.wikipedia.org/wiki/XMLHttpRequest#Support_in_Internet_Explorer_versions_5.2C_5.5_and_6"&gt;нашёл в англоязычной Wikipedia&lt;/a&gt;):&lt;pre&gt;&lt;code class="javascript"&gt;// Provide the XMLHttpRequest class for IE 5.x-6.x:&lt;br /&gt;if (typeof XMLHttpRequest == "undefined")&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @constructor */&lt;br /&gt;&amp;nbsp; &amp;nbsp; XMLHttpRequest = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch(e) {}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch(e) {}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try { return new ActiveXObject("Msxml2.XMLHTTP") } catch(e) {}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try { return new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new TypeError( "This browser does not support XMLHttpRequest." )&lt;br /&gt;&amp;nbsp; &amp;nbsp; };&lt;/code&gt;&lt;/pre&gt;Напоследок приведу ещё одну заплатку. Она служит для безпроблемной реализации стандартного метода &lt;code&gt;window.getComputedStyle&lt;/code&gt;, отсутствующего в IE вплоть до 8 версии включительно. Кто не знает, это очень удобный метод, позволяющий получать ссылки на объект, содержащий информацию о "вычислимых стилях" для HTML-элемента. Вычислимым называется стиль, специально не установленный разработчиком, однако получившийся в результате вывода браузером этого элемента на странице. IE вместо него имеет свойство &lt;code&gt;computedStyle&lt;/code&gt;, доступное у каждого элемента. К сожалению, оно не может представить вычислимый стиль для псевдоклассов, по-этому заплатку можно вставить так же, как и для &lt;code&gt;addActionListener&lt;/code&gt;`а - лишь частичную.&lt;/p&gt;&lt;p&gt;Таким образом, вот как можно решить данную проблему несовместимости IE со стандартом:&lt;pre&gt;&lt;code class="javascript"&gt;//IE patch for window.getComputedStyle&lt;br /&gt;if (!('getComputedStyle' in window))&lt;br /&gt;&amp;nbsp; &amp;nbsp; getComputedStyle = function(element, pseudoclass) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (pseudoclass === null || typeof pseudoclass === 'undefined')&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return element.currentStyle;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new Error("We are in IE, so we haven`t way to get pseudoclass styles of element");&lt;br /&gt;&amp;nbsp; &amp;nbsp; };&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-6604391429713992448?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/6604391429713992448/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=6604391429713992448' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/6604391429713992448'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/6604391429713992448'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/09/patch-javascript.html' title='&quot;Заплатка&quot; (&quot;Patch&quot;) как JavaScript шаблон проектирования'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-2724024271146258983</id><published>2011-07-21T06:24:00.000-07:00</published><updated>2011-07-21T06:24:39.070-07:00</updated><title type='text'>Я скачал ЕЁ!!!</title><content type='html'>Самая лучшая книга по JavaScript всех времён и народов! Лучше неё ничего нет!&lt;br /&gt;O`Reilly - Девид Фленаган, «JavaScript. Definitive Guide, 6th edition»(2011) - туда вошли все новшества от HTML5 и ECMAScript 5! :))))&lt;br /&gt;&lt;br /&gt;Вот &lt;a href="http://uploading.com/files/3735d769/Oreilly.j"&gt;тут&lt;/a&gt; она есть - http://uploading.com/files/3735d769/Oreilly.j&lt;br /&gt;&lt;br /&gt;Фленаган пишет очень подробно, для профессионалов.&lt;br /&gt;&lt;br /&gt;Искал в электронном виде уже месяца два, кучу времени убил – нигде не было, хотя куча вирья всякого пыталась под её видом пролезть на манер файлов «JavaScript_Definitive_Guide,6th_edition.pdf.exe» с иконкой pdf`а... И вот, наконец, она – вот она!!!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-2724024271146258983?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/2724024271146258983/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=2724024271146258983' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/2724024271146258983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/2724024271146258983'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/07/blog-post.html' title='Я скачал ЕЁ!!!'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-1937934684507249239</id><published>2011-05-31T08:13:00.000-07:00</published><updated>2011-11-10T03:48:13.469-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='Расширение JavaScript'/><title type='text'>__proto__ во всех браузерах</title><content type='html'>Ссылка "&lt;code&gt;__proto__&lt;/code&gt;", как известно, указывает на прототип объекта в браузерах FireFox, Google Chrome и Safari. Обычно ссылку на прототип можно получить комбинацией ссылок constructor и prototype, но когда используется классическое для JavaScript наследование:&lt;pre&gt;&lt;code type="javascript"&gt;function A(){/*...*/};&lt;br /&gt;function B(){/*...*/};&lt;br /&gt;B.prototype = new A();&lt;br /&gt;var b = new B();&lt;/code&gt;&lt;/pre&gt;, то в этом случае вызов&lt;pre&gt;&lt;code type="javascript"&gt;b.constructor.prototype&lt;/code&gt;&lt;/pre&gt;укажет на прототип конструктора A, а не на созданный объект конструктора A, который передан прототипу:&lt;pre&gt;&lt;code type="javascript"&gt;alert(b.constructor.prototype === A.prototype);//'true'&lt;/code&gt;&lt;/pre&gt;. Как же в этом случае можно обратиться именно к прототипу объекта b (естественно, не зная, каков его конструктор)? Можно переопределить ссылку constructor у объекта, созданного по конструктору A, указав ей на B:&lt;pre&gt;&lt;code type="javascript"&gt;B.prototype.constructor = B;&lt;/code&gt;&lt;/pre&gt;, но тогда мы потеряем возможность добраться до прототипа конструктора A, что может так же понадобиться.&lt;br /&gt;Так что остаётся только ссылка "&lt;code&gt;__proto__&lt;/code&gt;" - больше никак. Но этой ссылки нет в некоторых браузерах - например, в IE и в Opera`е.&lt;br /&gt;Но если чего-то нету, то можно это недостающее создать самим. Для того, что бы всё заработало везде, надо вставить конструкцию&lt;pre&gt;&lt;code type="javascript"&gt;if (!this.hasOwnProperty('__proto__'))&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @type A */ this.__proto__ = arguments.callee.prototype;&lt;/code&gt;&lt;/pre&gt;внутрь нашего конструктора-наследничка. И тогда всё получится:&lt;pre&gt;&lt;code type="javascript"&gt;function A(){/*...*/};&lt;br /&gt;function B() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; /*...*/&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (!this.hasOwnProperty('__proto__'))&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type A */ this.__proto__ = arguments.callee.prototype;&lt;br /&gt;};&lt;br /&gt;B.prototype = new A();&lt;br /&gt;var b = new B();&lt;br /&gt;alert(b.__proto__ === B.prptotype);//'true'&lt;br /&gt;alert(b.constructor.prototype === A.prototype);//'true'&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-1937934684507249239?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/1937934684507249239/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=1937934684507249239' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/1937934684507249239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/1937934684507249239'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/05/proto.html' title='__proto__ во всех браузерах'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-2386054526753290584</id><published>2011-05-31T06:56:00.000-07:00</published><updated>2011-11-10T03:48:36.818-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Effective JavaScript'/><title type='text'>Навигация в Google Cache</title><content type='html'>Интернет - это целый мир. Сайты в нём как люди в реальном мире - рождаются, живут и умирают. Иногда то, что они умирают, может кому-то не понравиться. Тогда на помощь тем, кто "хотел, но не успел" сохранить те или иные материалы у себя, приходит Google Cache.&lt;br /&gt;При поиске тех или иных страниц в интернете Вы наверняка замечали под каждой ссылкой с результатами поиска ссылочку "Сохранённая копия". Видели? Это и есть Google Cache. Если страницы уже и след простыл, она может всё ещё оказаться в этом хранилище.&lt;br /&gt;&lt;br /&gt;Точно не знаю, сколько живут страницы в Кэше`e Google, прежде чем окончательно проститься с Internet`ом, но если хотелось их сохранить, то искать их можно там. Но вот незадача - отдельную страницу найти-то можно, но вот если пропал целый сайт и хочется погулять по этому сайту, покликать на ссылочки почитать много страниц в правильном порядке, то Вас ждёт неприятный сюрприз - все внутренние ссылки будут битые. Т.е. страницы-то в Google Cach`е есть, но вот навигация по ним очень неудобная - нужно каждую из них искать с помощью поисковика Google`а и переходить по ссылке "Сохранённая копия".&lt;br /&gt;&lt;br /&gt;Буквально сегодня я столкнулся с тем, что великолепный сайт, посвящённый реальной медицине (а не этой дурацкой официальной, которая не лечит, а наоборот - медленно убивает людей) - http://www.revici.ru/ - исчез и теперь при наборе его адреса попадаешь на какую-то дурацкую рекламу фильмов.&lt;br /&gt;На этом сайте было большое количество интересных материалов, соединённых ссылками. И при его сохранении из этого Кэша передо мной встала проблема перемещения по этим ссылкам.&lt;br /&gt;Но, т.к. я программист, я быстро нашёл удобное решение. Дело в том, что страница из кэша google подгружается, если перед её адресом в адресной строке браузера вставить строку: "http://webcache.googleusercontent.com/search?q=cache:"&lt;br /&gt;Так что я быстренько написал следующий коротенький скриптик:&lt;pre&gt;&lt;code class="javascript"&gt;javascript:&lt;br /&gt;var l=document.getElementsByTagName('a'),&lt;br /&gt;&amp;nbsp; &amp;nbsp; e=l.length;&lt;br /&gt;for(var i=0;i&amp;lt;e;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; l[i].href=&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'http://webcache.googleusercontent.com/search?q=cache:'+&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; l[i++].href.replace(/http:\/\//gi,'');&lt;br /&gt;void(0);//Писать всё нужно в одну строчку, здесь я разбил для удобства восприятия&lt;/code&gt;&lt;/pre&gt;Вбил его в панель закладок Google Chrome`а и стал нажимать на эту кнопку каждый раз, как перейду на какую-то страницу с висящими ссылками. Всё заработало! Теперь спокойно могу гулять по ссылкам и сохранять страничку за страничкой :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-2386054526753290584?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/2386054526753290584/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=2386054526753290584' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/2386054526753290584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/2386054526753290584'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/05/google-cache.html' title='Навигация в Google Cache'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-5782567460106098653</id><published>2011-05-05T01:25:00.000-07:00</published><updated>2011-05-06T01:08:24.527-07:00</updated><title type='text'>Список используемых программ, которые нужно купить</title><content type='html'>Приветствую!&lt;br /&gt;&lt;br /&gt;Как и многие другие, грешен: пиратствую. Не во всём, конечно - Windows давно лицензионный (а Вы попробуйте сейчас купить ноутбук без лицензионной Wind`ы!), от MS Office`а в виду его большой дороговизны, отказался, перейдя на Open Office (ломка уже прошла))). Но другой хороший софт стоит дорого, а бесплатного на некоторые специфические задачи найти не удаётся.&lt;br /&gt;Однако зарабатываю достаточно много и уже просто неприлично этим делом продолжать заниматься. И совесть всё больше по этому поводу свербит...&lt;br /&gt;&lt;br /&gt;Решил опубликовать план по приобретению программ - вдруг кому-то пригодится:&lt;ol&gt;&lt;li&gt;ABBYY FineRider - уже купил. Стоила в р-не полутора-двух тысяч, вроде. Распознавание отсканированных текстов на ура! Вобщем-то по-моему вообще монополист - по крайней мере для русского языка то уж точно. Нужна мне для сканирования некоторых редких старых книг, которые мне попались и теперь их нужно отсканировать и разместить в интернете.&lt;/li&gt;&lt;li&gt;ABBYY Lingvo - нужна, http://translate.ru значительно менее удобен в повседневном использовании, да и обладает менее богатыми библиотеками, которые не всегда выдают нужный результат.&lt;/li&gt;&lt;li&gt;Kaspersky Cristal - до конца года обеспечила контора-производитель, откуда я ушёл месяц назад. Продлять не буду, скорее куплю KIS - Kaspersky Internet Security.&lt;/li&gt;&lt;li&gt;Sparx Systems Enterprise Architect 8.0 Professional. Хоть и не поддерживает JavaScript, для которой я его в основном использую, но прога незаменимая, подсел на неё, ещё когда работал в Лаборатории Касперского и теперь сложные объёмные решения без неё получается сильно дольше планировать и обдумывать, чем с ней - все аналоги либо намного дороже, либо намного хуже, либо и то и другое.&lt;/li&gt;&lt;li&gt;SPKet IDE - плагин к Eclipse, платный для коммерческого использования. Очень удобная штука для JavaScript-программирования - лучшая, что я видел.&lt;/li&gt;&lt;li&gt;Altova XMLSpy. Непревзойдённый XML-редактор, как без рук без него - все аналоги, которые видел, намного хуже.&lt;/li&gt;&lt;li&gt;Saxon. Часто не хватает возможностей XSLT 1.0 для текущих задач, а бесплатных удобных библиотек XSLT 2.0-преобразований найти не удалось. Saxon - практически единственная полноценная библиотека для XSLT 2.0 преобразований.&lt;/li&gt;&lt;li&gt;IntelliJ IDEA Corporate. Лучше среды для Java-разработки придумать, по-моему, просто невозможно.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-5782567460106098653?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/5782567460106098653/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=5782567460106098653' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/5782567460106098653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/5782567460106098653'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/05/blog-post.html' title='Список используемых программ, которые нужно купить'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-791636990278309657</id><published>2011-01-12T06:13:00.000-08:00</published><updated>2011-01-12T06:14:04.558-08:00</updated><title type='text'>JBoss 6 - final?</title><content type='html'>Ребята из Red Hut, конечно, жгут!.. Выпустили перед самым новым годом (28 декабря) &lt;a href="http://www.jboss.org/"&gt;final-версию JBoss`а 6&lt;/a&gt;. Подарок типа на новый год людям! А Tomcat 7, который входит в него как сервлет-контейнер, &lt;a href="http://tomcat.apache.org/download-70.cgi"&gt;всё ещё beta!&lt;/a&gt;!! Нормальный "подарочек", а?.. :))))&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-791636990278309657?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/791636990278309657/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=791636990278309657' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/791636990278309657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/791636990278309657'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2011/01/jboss-6-final.html' title='JBoss 6 - final?'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-1869656568440706231</id><published>2009-12-16T02:04:00.000-08:00</published><updated>2010-05-12T07:45:07.018-07:00</updated><title type='text'>Планы на будущее</title><content type='html'>Давно не писал, потому что был несколько нагружен работой и не успевал, а халтуру писать тоже не хочется.&lt;br /&gt;То, о чём я пишу, но ещё не дописал и что, соответственно, в этом блоге будет:&lt;ol&gt;&lt;li&gt;&amp;quot;Дополняем &amp;quot;include в JavaScript`е&amp;quot;&amp;quot;. Переработанная  &lt;a href="http://www.artlebedev.ru/tools/technogrette/js/include/"&gt;статья Володи Колесникова &amp;quot;Инклюд в яваскрипте&amp;quot;&lt;/a&gt; на &lt;a href="http://www.artlebedev.ru/tools/technogrette/"&gt;&amp;quot;Техногрете&amp;quot;&lt;/a&gt; - сайте для web-разработчиков от студии Артемия Лебедева.&lt;/li&gt;&lt;li&gt;&amp;quot;Хитрость с finally-блоком&amp;quot; - отличный приём кодинга на JS, который, в частности, позволит немного упростить &amp;quot;Базовую модель&amp;quot; приведённую в статьях цикла &amp;quot;Особенности инкапсуляции на основе замыканий&amp;quot;.&lt;/li&gt;&lt;li&gt;&amp;quot;Cookies API&amp;quot; - удобный API для работы с Cookies.&lt;/li&gt;&lt;li&gt;&amp;quot;URL advanced parsing&amp;quot; - переработанный в соответстви с серией статей &amp;quot;JavaScript: Особенности инкапсуляции на основе замыканий&amp;quot; вариант реализации парсинга, предложенный Kottenator`ом в &lt;a href="(http://habrahabr.ru/blogs/javascript/65407/"&gt;статье &amp;quot;Парсим URL&amp;quot;&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&amp;quot;Targeter pattern&amp;quot; - придуманный мной приём для динамического создания и подцепки статических блоков контента на странице. Я не раз использовал его в своих проектах и нахожу весьма ценным.&lt;/li&gt;&lt;li&gt;&amp;quot;SPKet IDE - советы&amp;quot;. Переработанный вариант &lt;a href="http://www.artlebedev.ru/tools/technogrette/soft/eclipse-spket/"&gt;статьи и видео-лекции С.Чикуенка &amp;quot;Eclipse: редактирование JavaScript в Spket IDE&amp;quot;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&amp;quot;JSDoc - советы&amp;quot;. Дополнения и критика &lt;a href="http://vingrad.ru/blogs/Alix/files/2008/09/jsdoc-discussion.pdf"&gt;статьи А.Старшинова - &amp;quot;Документирование JS-кода&amp;quot;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&amp;quot;&lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt; и &lt;a href="http://java.sun.com/javase/6/docs/technotes/guides/scripting/index.html"&gt;scripting API&lt;/a&gt; в Java SE 6 - способы применения в Java-программах (на примере &lt;a href="http://code.google.com/p/jsdoc-toolkit/"&gt;JSDoc Toolkit&lt;/a&gt;)&amp;quot;&lt;/li&gt;&lt;li&gt;&amp;quot;Универсализированная Обработка событий&amp;quot; - рабор того, что можно сделать с несовместимостью браузеров в области продвинутых моделей обработки событий.&lt;/li&gt;&lt;li&gt;&amp;quot;Универсализированный метод создания DOM-узлов&amp;quot;. Представлю свои библиотеки для создания узлов DOM-дерева. Хочу сосредоточиться на нахождении компромиса между максимальной производительностью и удобством работы с API.&lt;/li&gt;&lt;li&gt;&amp;quot;Работа с замыканиями&amp;quot;. Распишу, что представляют собой замыкания и как правильно их использовать.&lt;/li&gt;&lt;li&gt;&amp;quot;Примитивы и объекты в JavaScript&amp;quot;. Вопреки распространённому мнению, примитивы в JS есть, но они присутствуют в этом языке неявно и преобразуются на лету при попытке обратиться к ним как к объектам. Это может создать путанницу и привести к задержкам в работе кода (что явно и происходит, по моим наблюдениям, в коде у многих). В статье постараюсь подробно изложить отличия одних от других и показать верный путь для тюнинга JS-скриптов в этом отношении.&lt;/li&gt;&lt;li&gt;&amp;quot;Работа с XPath на JavaScript&amp;quot;. Описание &lt;a href="https://developer.mozilla.org/en/XPath"&gt;стандартного механизма поддержки XPath в браузерах&lt;/a&gt; и наилучшей с моей т.з.&lt;a href="http://coderepos.org/share/wiki/JavaScript-XPath"&gt;библиотеки, которая заменяет его в браузерах, не поддерживающих данный стандарт&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&amp;quot;Работа с XSLT на JavaScript&amp;quot;. Описание работы с&lt;a href="http://goog-ajaxslt.sourceforge.net/"&gt; библиотекой AJAXSLT&lt;/a&gt; от Google.&lt;/li&gt;&lt;li&gt;&amp;quot;Технологии &lt;a href="http://ru.wikipedia.org/wiki/XUL"&gt;XUL&lt;/a&gt; и &lt;a href="http://ru.wikipedia.org/wiki/HTML_Application"&gt;HTA&lt;/a&gt;/&lt;a href="http://msdn.microsoft.com/en-us/library/ms531079%28VS.85%29.aspx"&gt;HTC&lt;/a&gt;&amp;quot;. Описание работы с данными технологиями, позволяющими создавать полноценные приложения при помощи Web-технологий.&lt;/li&gt;&lt;li&gt;&amp;quot;ServerSide JavaScript&amp;quot;. Когда-то давным-давно, впечатлившись успехом JavaScript, фирма Netscape создала и &lt;a href="http://en.wikipedia.org/wiki/Server-side_JavaScript"&gt;серверный вариант этого языка&lt;/a&gt;, но он по ряду причин не получил должного распространения, упустив рынок приложений на стороне сервера таким языкам, как Perl и PHP. Сейчас, на основе &lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt; и &lt;a href="http://java.sun.com/javase/6/docs/technotes/guides/scripting/index.html"&gt;Scripting API Java 6&lt;/a&gt; можно использовать Java-сервера для программирования серверной логики на JS. В статье я постараюсь продемонстрировать, как это можно реализовать.&lt;/li&gt;&lt;li&gt;&amp;quot;Технология компонентов&amp;quot;. Дальнейшее развитие идеи паттерна Targeter, к которому добавляется аналогичный GUI-библиотекам Java (Swing) механизм обработки событий.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-1869656568440706231?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/1869656568440706231/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=1869656568440706231' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/1869656568440706231'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/1869656568440706231'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/12/blog-post.html' title='Планы на будущее'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-4452871557681906818</id><published>2009-06-01T12:11:00.000-07:00</published><updated>2009-06-02T14:13:02.409-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Drag and Drop'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript библиотеки'/><title type='text'>JavaScript: Drag and Drop - библиотека</title><content type='html'>В данной статье я представлю свою небольшую JavaScript-библиотечку для работы с Drag&amp;Drop (&lt;a href="http://forum.vingrad.ru/act-Attach/type/post/id-1884889.html"&gt;Скачать&lt;/a&gt;).&lt;br /&gt;&lt;h4&gt;Введение&lt;/h4&gt;Для начала немного теории. Drag&amp;Drop предполагает, что вы, как разработчик, даёте возможность пользователю перемещать те или иные графические объекты (будем по старинке называть их &lt;i&gt;слоями&lt;/i&gt;) по странице, нажав на них левой кнопкой мыши и, не отжимая её, передвигая курсор мыши до пункта назначения.&lt;br /&gt;В более сложном варианте нам необходимо вносить те или иные ограничения на этот процесс, по-этому API библиотеки должно позволять гибко настраивать перенос в соответствии с задачей. Сразу обратим внимание на три фазы перемещения:&lt;ul&gt;&lt;li&gt;Нажатие левой кнопки мыши на элементе&lt;/li&gt;&lt;li&gt;Перетаскивание элемента с помощью перемещения курсора при нажатой левой кнопке мыши&lt;/li&gt;&lt;li&gt;Отжатие левой кнопки мыши после перемещения&lt;/li&gt;&lt;/ul&gt;На уровне кода данная библиотека представляет собой один объект &lt;code&gt;DnD&lt;/code&gt; с двумя методами.&lt;br /&gt;&lt;h4&gt;Подключение библиотеки&lt;/h4&gt;Архив содержит библиотеку DragAndDrop в 2-х вариантах.&lt;ul&gt;&lt;li&gt;Оптимизированный код - в файлах "./lib/common.js" и "./build/dnd.js".&lt;/li&gt;&lt;li&gt;Комплексный оптимизированный код - в файле "./dnd.js"&lt;/li&gt;&lt;/ul&gt;Библиотека "common.js" необходима для ряда операций сравнения, по-этому её обязательно нужно подгружать до подгрузки библиотеки "dnd.js".&lt;br /&gt;&lt;br /&gt;Эта библиотека довольно простая и может оказаться полезной вам и для других ваших сценариев - если вы будете её использовать ещё для чего-то (как это делаю я), то имеет смысл её загрузить один раз и больше не загружать - тогда удобно отделить её от других файлов библиотек и загружать отдельно - один раз, что бы потом грузить другие библиотеки, которые её используют (исходный код данной библиотеки лежит там же, в директории "./lib"). Для тестирования (файлы в директории "./test/") выбран именно этот способ, для такой сборки в Ant предназначен target по-умолчанию "build".&lt;br /&gt;&lt;br /&gt;Если же вы не собираетесь использовать библиотеку "commons.js" где-либо ещё, то имеет смысл для сокращения количества загружаемых файлов, объединить её с библиотекой dnd в один оптимизированный файл. Такой объединённый файл и лежит в корне архива (он собирается с помощью действия "build-complex" в Ant).&lt;br /&gt;&lt;h4&gt;Что ещё есть в архиве&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;"./doc/" - документация&lt;/li&gt;&lt;li&gt;"./test/" - демонстрационные тесты&lt;/li&gt;&lt;li&gt;"build.xml" - сценарий сборки Ant&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;Первый пример&lt;/h4&gt;Если у Вас на странице есть некоторый объект, который Вам хотелось бы сделать передвигаемым по ней, то Вам достаточно зарегистрировать его в объекте DnD:&lt;pre&gt;&lt;code class="javascript"&gt;var /** @type {HTMLElement} */ layer = document.createElement('div');&lt;br /&gt;layer.appendChild(document.createElement('img')).setAttribute('src', 'pic.gif');&lt;br /&gt;document.body.appendChild(layer);&lt;br /&gt;DnD.register(layer);&lt;/code&gt;&lt;/pre&gt;В результате выполнения этого кода картинка "pic.gif" появится на экране и станет возможным её передвижение по окну браузера.&lt;br /&gt;&lt;br /&gt;Второй метод объекта &lt;code&gt;DnD&lt;/code&gt; служит обратной задаче - отмены регистрации. Например, если после пердыдущего участка кода вставить следующий:&lt;pre&gt;&lt;code class="javascript"&gt;setTimeout('DnD.unregister(layer)', 6000);&lt;/code&gt;&lt;/pre&gt;, то передвижение слоя будет доступно только в течении 6 секунд - затем он зафиксируется и больше не будет доступен для перемещений.&lt;br /&gt;&lt;br /&gt;В этом примере мы видим работу двух методов объекта DnD - это методы &lt;code&gt;register&lt;/code&gt; и &lt;code&gt;unregister&lt;/code&gt;. Как нетрудно догадаться из их названий, один регистрирует объект как DragAndDrop, второй - отменяет эту регистрацию, возвращая его в нормальное состояние (но не отменяя его перемещения, которое уже совершено, а так же текущего перемещения - если метод &lt;code&gt;unregister&lt;/code&gt; был вызван в момент перемещения. Если быть более точным, метод &lt;code&gt;unregister&lt;/code&gt; блокирует у слоя возможность начать его новое перемещение пользователем). Оба эти метода возвращают ссылки на объект &lt;code&gt;DnD&lt;/code&gt;, так что их вызовы можно выстраивать в цепочки любой длинны:&lt;pre&gt;&lt;code class="javascript"&gt;DnD.register(layer1).register(layer2).unregister(layer3).register(layer4);//...&lt;/code&gt;&lt;/pre&gt;Перемещать можно только слои, у которых свойство 'position' установлено в значение 'absolute' или 'relative'. По-этому при регистрации слоя происходит проверка на то, какое значение выставлено у него в этом css-свойстве. Если оно не выставлено в одно из этих двух - библиотека сама выставляет для него значение 'absolute'.&lt;br /&gt;&lt;h4&gt;Установка ограничений&lt;/h4&gt;Однако, читатель наверняка согласится с тем, что в реальных задачах редко нужны просто свободно перемещаемые по окну браузера мышкой элементы. Как правило, нужно как-либо ограничить возможности производить перемещения - например, оставить перемещения лишь по заданным траекториям или ограничить те места в браузере, где перемещения можно было бы завершить.&lt;br /&gt;&lt;br /&gt;Для этого предусмотрен простой API, регламентирующий Drag&amp;Drop-перемещения для элемента страницы. В основе его лежит событийная модель - для элемента, регистрируемого в объекте &lt;code&gt;DnD&lt;/code&gt;, можно определить обработчиков трёх событий. Для каждого из событий в объекте &lt;code&gt;DnD&lt;/code&gt; определено поведение по-умолчанию, но если обработчик данного события вернёт булево значение &lt;code&gt;false&lt;/code&gt;, то это поведение не будет выполнено. Значение &lt;code&gt;true&lt;/code&gt; можно и не возвращать - всё, что угодно, кроме значения &lt;code&gt;false&lt;/code&gt; будет командой выполнить действие по-умолчанию (обратите внимание на то, что значение &lt;code&gt;false&lt;/code&gt; должно быть иименно &lt;i&gt;примитивным&lt;/i&gt;, объект &lt;code&gt;new Boolean(false)&lt;/code&gt; будет интерпретирован как &lt;code&gt;true&lt;/code&gt;, так же будет интерпретированы и значения &lt;code&gt;null&lt;/code&gt; и &lt;code&gt;undefined&lt;/code&gt; - только примитив &lt;code&gt;false&lt;/code&gt; заблокирует поведение для объекта по-умолчанию).&lt;table border="1"&gt;&lt;caption&gt;Таблица №1: Обработчики событий Drag&amp;amp;Drop&lt;/caption&gt;&lt;tr&gt;&lt;th&gt;Событие&lt;/th&gt;&lt;th&gt;Сигнатура обработчика&lt;/ht&gt;&lt;th&gt;Описание параметров&lt;/ht&gt;&lt;th&gt;Поведение по-умолчанию&lt;/ht&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Нажатие пользователем левой кнопки мыши в момент нахождения курсора над слоем - инициализация перемещения элемента.&lt;/td&gt;&lt;td&gt;function mouseDown(/*Object*/ offset)&lt;/td&gt;&lt;td&gt;В качестве парамметра принимает объект &lt;code&gt;offset&lt;/code&gt;, содержащий информацию о начале перетаскивания - поля 'x' и 'y' этого объекта содержат разницу между позицией курсора мыши и левым верхним краем элемента, а поле 'startPos' содержит объект с полями 'x' и 'y', содержащими координаты левого верхнего угла элемента.&lt;/td&gt;&lt;td&gt;Перемещение стартует. Форма курсора мыши по всему документу выставляется в значение 'move'.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Изменение координат курсора мыши при нажатой левой кнопке мыши.&lt;/td&gt;&lt;td&gt;mouseMove(/*number*/ x, /*number*/ y)&lt;/td&gt;&lt;td&gt;/*number*/ x - координата по горизонтали, /*number*/ y - координата по вертикали левого верхнего края курсора мыши.&lt;/td&gt;&lt;td&gt;Передвижение элемента, по вертикали и горизонтали соответствующее перемещению курсора.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Отпускание левой кнопки мыши после перетаскивания элемента.&lt;/td&gt;&lt;td&gt;mouseUp(/*number*/ x, /*number*/ y)&lt;/td&gt;&lt;td&gt;/*number*/ x - координата по горизонтали, /*number*/ y - координата по вертикали левого верхнего края курсора мыши.&lt;/td&gt;&lt;td&gt;Фиксация слоя в текущей позиции в окне браузера и изменение формы курсора мыши на всём документе на значение 'auto'. В случае отмены данного действия производится возврат слоя к координатам, с которых началось его передвижение.&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;Можно переопределить любой из обработчиков, все вместе или любые их комбинации - каждый из них будет вызываться только в том случае, если будет присутствовать.&lt;br /&gt;&lt;h4&gt;Подробности&lt;/h4&gt;Начинается перетаскивание для нас с того, что вызывается метод &lt;code&gt;mouseDown&lt;/code&gt; и ему передаётся параметр &lt;code&gt;offset&lt;/code&gt; - смещение. Дело в том, что координатой элемента у нас считается координата его верхнего левого угла, а мышка может быть нажата, находясь не только в левой верхней точке, но и в любой другой точке элемента, по-этому для того, что бы корректно перемещать объект в зависимости от будущих координат мыши, нам нужно знать, на сколько пикселов правее и ниже "взялся мышкой" за элемент пользователь, когда начал его перетаскивание, что бы потом корректировать координаты так, что бы курсор мышки при перетаскивании был именно над этой самой точкой, а не над левым верхним углом, как это было бы, если бы мы не учитывали смещения. Именно эту информацию и несёт объект &lt;code&gt;offset&lt;/code&gt;. Так же в нём содержится информация о стартовом положении элемента в момент до начала перетаскивания, по-этому может быть удобно запомнить этот объект и использовать эту информацию в дальнейшем - при обработке других событий.&lt;br /&gt;&lt;br /&gt;Все три метода могут возвращать значения типа &lt;code&gt;boolean&lt;/code&gt; (обратите внимание - с маленькой буквы, т.е. речь идёт о примитиве, а не об обёртке &lt;code&gt;Boolean&lt;/code&gt;). Любое другое значение, а так же отсутствие возвращаемого значения интерпретируется как возвращение значения &lt;code&gt;true&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;В общем случае возвращение &lt;code&gt;true&lt;/code&gt; означает разрешение на проделывание действий по-умолчанию, &lt;code&gt;false&lt;/code&gt; - отказ от этих действий. Действия по-умолчанию для событий приведены в таблице №1.&lt;br /&gt;&lt;h4&gt;Пример: Перетаскивание только по горизонтали&lt;/h4&gt;Допустим, перед нами стоит задача создать интерфейс, управляющий какой-либо количественной величиной, скажем, уровнем громкости в динамике или задержкой по времени при автодозвоне. Тогда нам нужно создать некоторый бегунок, который бы перемещался только по горизонтали, по нужной нам шкале, а мы бы отслеживали это перемещение и, в соответствии с ним, меняли бы ту величину, за изменение которой был бы ответственнен наш компонент.&lt;br /&gt;&lt;br /&gt;На уровне API это означает, что нам нужно заблокировать поведение по-умолчанию при возникновении события mouseMove и заменить его на перемещение по горизонтали, не трогая вертикальной координаты объекта.&lt;br /&gt;&lt;br /&gt;Делается это примерно так:&lt;pre&gt;&lt;code class="javascript"&gt;(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var /** @type {number} */ offsetX;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; layer.mouseDown = function(offset) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; offsetX = offset.x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; layer.mouseMove = function(x) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.style.left = (x - offsetX) + 'px';&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return false;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; layer.mouseUp = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; offsetX = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;})();&lt;/code&gt;&lt;/pre&gt;Вставьте этот код перед регистрацией слоя в объекте &lt;code&gt;DnD&lt;/code&gt; в первом примере (в начале статьи) - и он придаст слою нужную функциональность. Послоностью пример можно увидеть, посмотре исходные коды тестов (директория "./test/") - там этот пример соответствует примеру №3. Запустите файл "./test/test.html" и проверьте его работу в своём браузере.&lt;br /&gt;&lt;h4&gt;Пример: перетаскивание в заданную область&lt;/h4&gt;Допустим, у нас есть интернет-магазин и мы хотим сделать возможность перемещения товаров в корзину. Тогда мы имеем задачу обеспечить перетаскивание элемента в ограниченную область экрана (корзину) и нас не устроит, если пользователь переместит слой, соответствуюдий товару, куда-либо ещё.&lt;br /&gt;&lt;br /&gt;Значит, нам нужно перегрузить обработчик события окончания передвижения элемента с тем, что бы проверять, находится ли курсор над областью, в которую разрешено перемещение и, в случае, если нет - возвращать &lt;code&gt;false&lt;/code&gt; с тем, что бы вернуть элемент назад, отменив результат перемещения.&lt;br /&gt;&lt;br /&gt;Кроме того, было бы полезно в процессе перемещения сразу показывать пользователю, что в данный момент элемент находится не над приемлемой для помещения в неё зоной. Традиционно это делается путём присвоения курсору такого значения, которое выглядит как знак запрета. К сожалению, такое значение для CSS-свойства 'cursor' на сегодняшний момент не стандартизовано, однако, несмотря на это, поддерживается последними версиями всех достаточно популярных браузеров, за исключением, разве что, Opera`ы (будем надеяться, что это временно). Это значение - 'no-drop'.&lt;br /&gt;&lt;br /&gt;Допустим, что область карзины представлена объектом SquareArea, которому в конструкторе передаются её координаты и размеры, и у неё есть один метод - &lt;code&gt;isInside(/*number*/ x, /*number*/ y)&lt;/code&gt;, который возвращает результат типа &lt;code&gt;boolean&lt;/code&gt;, говорящий, соответственно, входит или не входит точка с переданными ему координатами в его область. Тогда наш код для этой ситуации может быть примерно таким:&lt;pre&gt;&lt;code class="javascript"&gt;(function() {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var /** @type {Object} */ offset,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type{SquareArea} */ toArea = new SquareArea(450, 450, 100, 100);&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; layer.mouseDown = function(_offset) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; offset = _offset;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; layer.mouseMove = function(x, y) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.style.cursor =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; document.body.style.cursor = toArea.isInside(x, y) ? 'move' : 'no-drop';&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; layer.mouseUp = function(x, y) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; offset = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.style.cursor = 'move';&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return toArea.isInside(x, y);&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;})();&lt;/code&gt;&lt;/pre&gt;Наверное, нужно особо остановиться на том, почему в обработчике &lt;code&gt;mouseMove&lt;/code&gt; значение курсора присваивается сразу же двум стилям элементов - и самого элемента и документа. Дело в том, что при перемещении, резко дёрнув мышь, можно на долю секунды поймать момент, когда элемент ещё не успел перерисоваться и подтянуться к курсору мыши - и тогда, если документу не присвоен тот же курсор, что и элементу, курсор изменит форму, а потом вернёт старую форму обратно. Это может смотреться довольно раздражающе - именно для того, что бы избежать этого эффекта, рекомендуется в процессе DragAndDrop-передвижения выставлять необходимую форму курсора не только над элементом, но и на всём документе.&lt;br /&gt;&lt;br /&gt;Вставьте этот код перед регистрацией слоя в объекте &lt;code&gt;DnD&lt;/code&gt; в первом примере (в начале статьи) - и он придаст слою нужную функциональность. Полоностью пример можно увидеть, посмотре исходные коды тестов (директория "./test/") - там этот пример соответствует примеру №4. Запустите файл "./test/test.html" и проверьте его работу в своём браузере.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-4452871557681906818?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/4452871557681906818/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=4452871557681906818' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4452871557681906818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4452871557681906818'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/06/javascript-drug-and-drop.html' title='JavaScript: Drag and Drop - библиотека'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-5594252472055019692</id><published>2009-04-28T04:08:00.000-07:00</published><updated>2010-01-18T07:23:46.907-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript patterns'/><title type='text'>JavaScript: Реализация pattern`а Singleton</title><content type='html'>По моему мнению гибкость языка JavaScript оставляет далеко позади практически все известные мне языки. Одним из тонких моментов является работа оператора &lt;code&gt;new&lt;/code&gt;. Мы привыкли, что он создаёт объект с помощью конструктора, передавая ему аргументы. Но на самом деле результат может быть и иным: если в функции, используемой в качестве конструктора, вернуть какой-либо другой объект, то он-то и станет результатом всего выражения с оператором &lt;code&gt;new&lt;/code&gt;. Посмотрим на пример:&lt;pre&gt;&lt;code class="javascript"&gt;function X(){}&lt;br /&gt;function Y(){ return new X; }&lt;br /&gt;var y = new Y; &lt;br /&gt;alert(y instanceof Y); // false&lt;br /&gt;alert(y instanceof X); // true&lt;/code&gt;&lt;/pre&gt;Этот приём, например, можно очень удобно использовать для того, что бы заставить IE работать, как это должен делать любой нормальный браузер, с объектом XMLHttpRequest без всяких заморочек с его ActiveX`овскими конструкторами:&lt;pre&gt;&lt;code class="javascript"&gt;// Provide the XMLHttpRequest class for IE 5.x-6.x:&lt;br /&gt;if (typeof XMLHttpRequest == "undefined")&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @constructor */&lt;br /&gt;&amp;nbsp; &amp;nbsp; XMLHttpRequest = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch(e) {}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch(e) {}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try { return new ActiveXObject("Msxml2.XMLHTTP") } catch(e) {}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try { return new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new TypeError( "This browser does not support XMLHttpRequest." )&lt;br /&gt;&amp;nbsp; &amp;nbsp; };&lt;/code&gt;&lt;/pre&gt;- после этого можно работать с этим конструктором практически так же, как во всех остальных браузерах - FireFox`е, Safari, Opera`е и Chrom`е. Этот приём &lt;a href="http://en.wikipedia.org/wiki/XMLHttpRequest#History_and_support"&gt;описан в Wikipedia&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Обычно для реализации Singleton`а применяются специальные конструкции на основе методов и прочего. Но в JavaScript представляется намного более простым использование этой возможности так, что бы внешний интерфейс создания экземпляра вообще не отличался от возврата уже созданного предыдущими вызовами экземпляра.&lt;br /&gt;Так может быть реализован и pattern "Singleton" на JavaScript:&lt;pre&gt;&lt;code class="javascript"&gt;/**&lt;br /&gt;&amp;nbsp;* @constructor&lt;br /&gt;&amp;nbsp;* @singleton&lt;br /&gt;&amp;nbsp;*/&lt;br /&gt;function SingletonClass() {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (arguments.callee.instance)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return arguments.callee.instance;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; return arguments.callee.instance = this;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Update №1 (18.01.2010)&lt;/span&gt;&lt;br /&gt;Мне пришла в голову мысль о том, что не очень хорошо делать так, как я предложил в примере выше. Дело в том, что поле instance открыто для редактирования и внесения изменений и этот механизм из-за этого можно сломать. Конечно, вряд ли кто-то станет намеренно так поступать, но лучше всё-таки не оставлять такой возможности.&lt;br /&gt;Так что сейчас я оформляю Singleton`ы несколько подругому:&lt;pre&gt;&lt;code class="javascript"&gt;/**&lt;br /&gt;&amp;nbsp;* @constructor&lt;br /&gt;&amp;nbsp;* @singleton&lt;br /&gt;&amp;nbsp;*/&lt;br /&gt;var SingletonConstructor;&lt;br /&gt;(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var /** @type {SingletonConstructor} */ instance;&lt;br /&gt;&amp;nbsp; &amp;nbsp; SingletonConstructor = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (typeof instance !== 'undefined')&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return instance;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return instance = this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; };&lt;br /&gt;})();&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-5594252472055019692?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/5594252472055019692/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=5594252472055019692' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/5594252472055019692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/5594252472055019692'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/04/javascript-pattern-singleton.html' title='JavaScript: Реализация pattern`а Singleton'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-438319918663823687</id><published>2009-04-23T01:10:00.000-07:00</published><updated>2010-01-05T07:05:09.756-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Расширение JavaScript'/><title type='text'>JavaScript: Выяснение имени класса (конструктора)</title><content type='html'>В статье "&lt;a href="http://se-la-vy.blogspot.com/2009/01/javascript-argumentscallee.html"&gt;JavaScript: используйте arguments.callee вместо названия функции&lt;/a&gt;" я уже обращал ваше внимание на то, что в JavaScript в общем случае нельзя полагаться на то, что имена функций будут соответствовать тем функциям, которым вы их присвоили. Вещь это настолько важная, что не грех будет кратко повторить суть рассуждений.&lt;br /&gt;&lt;br /&gt;В JavaScript функция является одним из типов данных, специфика которого заключается в том, что к нему можно обращаться, вызывая его как функцию и как конструктор. Но если это тип данных, то, что бы с ним работать, нам нужно получить ссылку на него, которая может содержатся в какой-то переменной. Это и происходит при объявлении функции, по сути имя функции - это переменная, которой присваивается значение - функция. В этом смысле следующие две строчки эквивалентны:&lt;pre&gt;&lt;code class="javascript"&gt;function x(){}&lt;br /&gt;var x = function(){}&lt;/code&gt;&lt;/pre&gt;Между этими строками есть &lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-function-function.html"&gt;небольшое отличие&lt;/a&gt;, но в данном случае оно не существенно. Так вот, само название ПЕРЕМЕННАЯ говорит нам о том, что она может менять своё значение, по-этому опираться на то, что эта переменная будет всегда ссылаться на один раз присвоенную ей функцию, нельзя. Следующий пример это демонстрирует:&lt;pre&gt;&lt;code class="javascript"&gt;function x(){};&lt;br /&gt;//...&lt;br /&gt;x = 5;&lt;br /&gt;//...&lt;br /&gt;x(); //Error!&lt;/code&gt;&lt;/pre&gt;Но что же делать, если про функцию всё-таки нужно узнать её имя? Например, у нас может быть написан следующий код:&lt;pre&gt;&lt;code class="javascript"&gt;function x1(){}&lt;br /&gt;var x2;&lt;br /&gt;if (x1) x2 = x1;&lt;br /&gt;x1 = 5;&lt;/code&gt;&lt;/pre&gt;В результате ссылка на функцию есть, но как её найти, имея лишь саму функцию - не понятно.&lt;br /&gt;В некотором частном случае, а именно если объявление функции и манипуляции с ней происходили в глобальном контексте (глобальной области видимости переменных), выяснение имени возможно.&lt;br /&gt;&lt;br /&gt;Здесь следует сказать пару слов о глобальном контексте. В JavaScript определён объект &lt;code&gt;Global&lt;/code&gt;, доступный по ссылке &lt;code&gt;this&lt;/code&gt;. Это означает, что если мы объявляем переменную не внутри какой-либо функции, а просто в сценарии, то она автоматически становится свойством объекта &lt;code&gt;Global&lt;/code&gt; и к ней можно обращаться не только по имени, но и как к свойству этого объекта:&lt;pre&gt;&lt;code class="javascript"&gt;var x = 5;&lt;br /&gt;alert(this.x); // '5'&lt;/code&gt;&lt;/pre&gt;В частном, но наиболее часто встречающемся случае клиентского JavaScript (client-side JavaScript), т.е. расширения стандарта ECMAScript в браузерах моделью DOM, нам доступна ссылка &lt;code&gt;window&lt;/code&gt;, укзывающая на объект &lt;code&gt;Window&lt;/code&gt;, который является расширением объекта &lt;code&gt;Global&lt;/code&gt; для браузерной среды, и в нём, соответственно, так же можно, обращаться к его свойствам как к переменным, однако в общем случае использование ссылки &lt;code&gt;window&lt;/code&gt; нельзя назвать корректным, по крайней мере лично мне хотелоось бы писать решения, которые работали бы, кроме клиентского JavaScript, ещё и, скажем, в Java-программах, использующих Scripting API, поддержка которого есть в  JDK6.&lt;br /&gt;&lt;br /&gt;Со ссылкой же на объект &lt;code&gt;Global&lt;/code&gt; есть некоторая сложность, связанная с тем, что она по сути и доступна будет лишь в глобальном контексте и в функциях, которые не являются свойствами различных объектов, поскольку в других местах ссылка &lt;code&gt;this&lt;/code&gt; будет ссылаться на другие объекты. Что бы решить эту проблему, предлагаю ввести специальную переменную &lt;code&gt;global&lt;/code&gt;, которая будет ссылаться на глобальный контекст:&lt;pre&gt;&lt;code class="javascript"&gt;var /** @type {Global} */ global = this;&lt;/code&gt;&lt;/pre&gt;Теперь нам будет проще написать нужную функцию, но куда её лучше всего разместить? Мне представляется логичным помещение её в &lt;code&gt;Function.prototype&lt;/code&gt;, поскольку тогда она станет доступна у всех функций (т.к. любая функция представляет собой экземпляр объекта Function и ссылается по-этому своей ссылкой &lt;code&gt;__proto__&lt;/code&gt; на объект &lt;code&gt;Function.prototype&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;Итак, какой код получаем в результате:&lt;pre&gt;&lt;code class="javascript"&gt;var /** @type {Global} */ global = this;&lt;br /&gt;&lt;br /&gt;//...&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Функция, возвращающая имя функции this, если оно присутствует в глобальном&lt;br /&gt; * контексте.&lt;br /&gt; * &lt;br /&gt; * @return {string} имя функии, доступное по ссылке this или null, если имя не&lt;br /&gt; * найдено.&lt;br /&gt; */&lt;br /&gt;Function.prototype.getName = function() {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; for (var /** @type {string} */ i in global)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (global[i] === this)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return i;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; return null;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//Тестируем:&lt;br /&gt;&lt;br /&gt;//Объект узнаёт имя своего конструктора:&lt;br /&gt;function x1(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.showMyName = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; alert('My constructor name is ' + this.constructor.getName());&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;y = new x1;&lt;br /&gt;y.showMyName(); // 'My constructor name is x1'&lt;br /&gt;&lt;br /&gt;var x2;&lt;br /&gt;if (x1) x2 = x1;&lt;br /&gt;x1 = 5;&lt;br /&gt;y.showMyName(); // 'My constructor name is x2'&lt;br /&gt;&lt;br /&gt;// Функция узнаёт своё имя:&lt;br /&gt;function x3(){ alert('My name is ' + arguments.callee.getName()); }&lt;br /&gt;x3(); // 'My name is x3'&lt;br /&gt;&lt;br /&gt;// Узнаём у функции её имя:&lt;br /&gt;function x4(){}&lt;br /&gt;alert(x4.getName()); // 'x4'&lt;/code&gt;&lt;/pre&gt;Таким образом, функция, если ссылка на неё присутствует в глобальном контексте, всегда сможет узнать своё имя.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update&lt;/strong&gt;: К сожалению, в последней на сегодняшний день версии Internet Explorer`а - 8 (тестировал на версии 8.0.7600.16385) - всем переменным глобального пространства имён и, соответственно, функциям, автоматически присваивается модификатор {Don`t Enum}, из-за чего данный метод в этом браузере не работает... :(&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-438319918663823687?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/438319918663823687/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=438319918663823687' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/438319918663823687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/438319918663823687'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/04/javascript_23.html' title='JavaScript: Выяснение имени класса (конструктора)'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-4932623386106645377</id><published>2009-04-20T14:47:00.000-07:00</published><updated>2010-01-06T17:58:53.324-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript patterns'/><title type='text'>JavaScript: особенности инкапсуляции на основе замыканий. Часть 4: Наследование</title><content type='html'>Существует огромное множество различных реализаций наследования в JavaScript - от кустарных приёмов до реализаций в раскрученных библиотеках типа JQuery и Prototype. Но всех их объединяет один недостаток - они не позволяют использовать инкапсуляцию на основе замыканий (и никакую другую тоже). По крайней мере мне не попадалось ни одной реализации наследования в JS, которая позволяла бы скрывать данные.&lt;br /&gt;&lt;br /&gt;В предыдущих статьях данного цикла (&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-1.html"&gt;1&lt;/a&gt;, &lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-2.html"&gt;2&lt;/a&gt;, &lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-3.html"&gt;3&lt;/a&gt; части) мной была пошагово продемонстрирована разработка "базовой модели", которая позволяет реализовать структуру, предоставляющую возможность экономно создавать экземпляры классов сложных объектов, разруливая проблему совмещения инкапсуляции с ООП на основе прототипов. Конечно, само по себе решение этой проблемы довольно-таки важно, но истинную мощь "базовая модель" получит только тогда, когда будет дополнена механизмами наследования, поскольку одноуровневые объектные структуры весьма ограничены в рамках дизайна больших приложений (на которые и нацелена "базовая модель").&lt;br /&gt;&lt;br /&gt;Итак, давайте разберёмся, что реально означает наследование одного класса от другого на уровне работы кода?&lt;br /&gt;Если смотреть извне, то это означает, что любой код, нормально работающий с объектом унаследованного класса, должен продолжать нормально работать с объектом его потомка. Т.е. у объекта потомка  присутствуют все методы объекта родителя и они внешне (т.е. на уровне типов возвращаемых значений, принимаемых параметров и возбуждаемых исключений) ведут себя так же.&lt;br /&gt;Если же смотреть на наследование изнутри, то наследник:&lt;ol&gt;&lt;li&gt;Должен в конструкторе первым делом вызывать конструктор класса-предка.&lt;/li&gt;&lt;li&gt;Всегда иметь ссылку на экземпляр созданного в конструкторе класса-предка в переменной &lt;code&gt;super&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Некоторые public-методы могут быть переопределены и добавлены новые. То же самое в отношении public-полей.&lt;/li&gt;&lt;li&gt;Существует возможность открывать потомкам доступ к некоторым функциям и полям (модификатор &lt;code&gt;protected&lt;/code&gt; в Java).&lt;/li&gt;&lt;li&gt;Только те исключения (и их потомков), которые мог вызывать переопределённый метод, может вызывать метод переопределяющий. Либо последний исключений может не вызывать вовсе.&lt;/li&gt;&lt;/ol&gt;По поводу 2 пункта замечу, что слово &lt;code&gt;super&lt;/code&gt; в JavaScript является зарезервированным и поэтому его использовать не получится, довольствуясь лишь &lt;code&gt;_super&lt;/code&gt;, что вобщем-то даже имеет положительную сторону - будет сразу восприниматься в качестве особой переменной.&lt;br /&gt;Ну а по поводу 4 пункта, честно признаюсь, сколько ни думал о том, как это реализовать с применением замыканий, так ничего путного и не надумал. Возможно, я придумаю, как можно хитро вывернуться, всё-таки реализовав это, но пока давайте про этот пункт забудем и реализуем наследование без него, считая, что такого модификатора у нас нет.&lt;br /&gt;&lt;br /&gt;Принципиально важным, на мой взгляд, является возможность реализации наследования так, что бы не замыкать её только на объекты, созданные в соответствии с базовой моделью, а дать возможность использовать в отношении любых конструкторов пользовательских объектов.&lt;br /&gt;&lt;br /&gt;Итак, как нам нужно теперь модифицировать наш код, что бы стало возможным использование наследования? Для начала давайте посмотрим на схему:&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_e9ftZE9b4-4/Sd5O1AHgecI/AAAAAAAAAHg/roK2fDCxwx0/s640/%D0%A0%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F%20%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C.jpg" alt="Схема наследования для Расширенной модели" /&gt;&lt;br /&gt;Мы видим, что прототип ссылается неявной ссылкой (доступной в FireFox по имени &lt;code&gt;__proto__&lt;/code&gt; и недоступной в других браузерах - по-этому я называл её здесь именно так) на объект-прототип класса-предка, а на экземпляр ссылается только ссылка &lt;code&gt;_super&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Первое изменение коснётся механизма создания прототипа. Т.к. разработчики спецификации ECMAScript не оставили нам возможности напрямую контролировать ссылку &lt;code&gt;__proto__&lt;/code&gt;, мы вынуждены хитрить, что бы заставить её ссылаться на то, что нам будет нужно.&lt;br /&gt;&lt;br /&gt;В Базовой модели конструктор прототипа выглядел так:&lt;pre&gt;&lt;code class="javascript"&gt;A.prototype = new function() {/*здесь код конструктора прототипа*/}&lt;br /&gt;A.prototype.constructor = A;&lt;/code&gt;&lt;/pre&gt;Таким образом, у нас создавалась безымянная (анонимная) функция и тут же использовалась в качестве конструктора нового объекта-прототипа для класса &lt;code&gt;A&lt;/code&gt;. Затем, что бы не нарушать принятые в JavaScript договорённости относительно взаимных ссылок между конструкторами и прототипами, мы выставляли свойству &lt;code&gt;constructor&lt;/code&gt; прототипа ссылку на класс.&lt;br /&gt;&lt;br /&gt;Теперь же, учитывая, что мы не можем напрямую контролировать ссылку &lt;code&gt;__proto__&lt;/code&gt;, единственная возможность для нас прописать в неё то значение, которое нам нужно, состоит в том, что бы функция, создающая объект, который должен ссылаться посредством &lt;code&gt;__proto__&lt;/code&gt; туда, куда нам надо, должна до создания объекта ссылаться туда свойством &lt;code&gt;prototype&lt;/code&gt;:&lt;pre&gt;&lt;code class="javascript"&gt;function X() {}&lt;br /&gt;function Y() {this.z = 5;}&lt;br /&gt;var x1 = new X;&lt;br /&gt;X.prototype = new Y;&lt;br /&gt;var x2 = new X;&lt;br /&gt;alert(x1.z); //Выведет 'undefined'&lt;br /&gt;alert(x2.z); //Выведет '5';&lt;/code&gt;&lt;/pre&gt;Т.е. получается, что функция, создающая для нас прототип, теперь не сможет быть безымянной. А как не хочется засорять пространство имён ещё одной переменной... По-этому я предлагаю немного схитрить - присвоить прототипу сначала эту функцию, а потом, произведя все нужные изменения, присвоить ему уже её результат. Выглядеть это будет вот так:&lt;pre&gt;&lt;code class="javascript"&gt;A.prototype = function() {/*...*/}&lt;br /&gt;A.prototype.prototype = B.prototype; // B - конструктор, от которого мы наследуем A&lt;br /&gt;A.prototype = new A.prototype;&lt;br /&gt;A.prototype.constructor = A;&lt;/code&gt;&lt;/pre&gt;На выходе получим прототип, ссылающийся ссылкой &lt;code&gt;__proto__&lt;/code&gt; на прототип конструктора-предка.&lt;br /&gt;&lt;br /&gt;Теперь осталось разобраться со ссылкой &lt;code&gt;_super&lt;/code&gt;. Она будет обладать похожим поведением на ссылку &lt;code&gt;_this&lt;/code&gt; - так же будет переменной, имеющий двойника - переменную &lt;code&gt;__super&lt;/code&gt;, являющуюся полем объекта. Так же, как и ссылку &lt;code&gt;__this&lt;/code&gt;, её не будет смысла очищать в методе &lt;code&gt;privateState.get()&lt;/code&gt; и, соответственно, наполнять в &lt;code&gt;privateState.set()&lt;/code&gt;. Так же она будет константой для пользователя.&lt;br /&gt;Кроме того, она будет присваиваться вызову конструктора-предка вначале метода pcreatePivateState и в нём же из неё будут извлекаться все свойства и присваиваться ссылке &lt;code&gt;__this&lt;/code&gt; - если они, конечно, не будут переопределены в классе-потомке.&lt;br /&gt;&lt;br /&gt;Теперь соберём всё вместе и протестируем. Вот, какая получается расширенная модель:&lt;pre&gt;&lt;code class="javascript"&gt;function B() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var y = 15;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.getY = function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return y;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.setY = function(_y) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; y = _y;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * @author Vyacheslav Lapin&lt;br /&gt; * @version 0.01 09.04.2009 21:10:04&lt;br /&gt; *&lt;br /&gt; * @constructor&lt;br /&gt; * @extends B&lt;br /&gt; * &lt;br /&gt; *&lt;br /&gt; * @param {number} x + &lt;br /&gt; */&lt;br /&gt;function A(x) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (this.constructor !== arguments.callee)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return new arguments.callee(x);&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState = this.createPrivateState(x);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;A.prototype = function() {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var /** @type {A} */ _this,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type {B} */ _super,&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /**&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;* @static&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;* @type {Array&amp;lt;A&amp;gt;}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;*/&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;__callInstances = [];&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.createPrivateState = function(__x) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //Переменные для хранения private-полей объекта&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var /** @type {number} */ _x = __x,&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /**&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;* @constant&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;* @type {A}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;*/&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __this = this,&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /**&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;* @constant&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;* @type {B}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;*/&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __super = new B(); // Вызываем конструктор класса-предка&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.createPrivateState = null;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Отдельный функциональный блок для того, что бы не заводить в&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // closure ещё одно поле - переменную i&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //Здесь мы перечисляем свойства экземпляра класса-предка&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for (var /** @type {string} */ i in __super)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Проверяем, собственное это свойство экземпляра super-класса&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // или унаследованное? Ведь унаследованные свойства нам не нужны -&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // мы и так их унаследуем по цепочке прототипов, если они не будут&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // переопределены в нашем прототипе, а тогда - унаследуем&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // переопределённые, что нас вполне устроит.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (__super.hasOwnProperty(i)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Так же проверяем, есть ли такие же в прототипе&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;&amp; !this.constructor.prototype.hasOwnProperty(i)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // или в самом объекте?&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;&amp; !this.hasOwnProperty(i)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this[i] = __super[i];&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; } catch (/** @type {Error} */ e) { }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; })();&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; get: function() {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (_this) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __callInstances.push(_this);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this.privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = _x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this = __this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _super = __super;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _x =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; },&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; set: function() {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _x = x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _super = null;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (__callInstances.length&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;amp;&amp;amp; __callInstances[__callInstances.length - 1] !== __this)&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __callInstances.pop().privateState.get();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; /**&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @param {Arguments} args +&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;*/&lt;br /&gt;&amp;nbsp; &amp;nbsp; function methodCaller(args) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.privateState.get();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var result = args.callee.apply(_this, args);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return result;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var /** @type {number} */ x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; /**&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @return {number}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;*/&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.getX = function() {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (_this !== this) return methodCaller.call(this, arguments);&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; /**&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @param {number} _x&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @return {A} this&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;*/&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.setX = function(_x) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (_this !== this) return methodCaller.call(this, arguments);&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = _x;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; /**&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @override&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;*/&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.setY = function(_y) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (_this !== this) return methodCaller.call(this, arguments);&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; alert('Hello from A!');&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _super.setY(_y);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/** @type {string} */ A._className = "A";&lt;br /&gt;A.prototype.prototype = B.prototype;&lt;br /&gt;A.prototype = new A.prototype;&lt;br /&gt;A.prototype.constructor = A;&lt;br /&gt;&lt;br /&gt;// Импортируем в конструктор-потомок так же статические поля&lt;br /&gt;// конструктора-предка- свойства класса&lt;br /&gt;(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; for (var /** @type{string} */ i in B)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (!(i in A))&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; A[i] = B[i];&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; } catch (/** @type {Error} */ e) { }&lt;br /&gt;})();&lt;br /&gt;&lt;br /&gt;//test&lt;br /&gt;var a = new A(10);&lt;br /&gt;alert(a.getY()); //Выводит '15'&lt;br /&gt;alert(a.setY(20).getX()); // Выводит сначала 'Hello from A!', затем 10&lt;br /&gt;alert(a.getY()); //Выводит  '20'&lt;/code&gt;&lt;/pre&gt;Вот и всё. Можно теперь делать любые сложные деревья наследования в JavaScript в полном соответствии с принципами инкапсуляции.&lt;br /&gt;&lt;br /&gt;В дальнейших статьях цикла мы поговорим о том, как упростить разработку конструкторов по базовой и расширенной модели, поскольку у меня нет иллюзий о том, что вручную писать такой код не слишком-то удобно.&lt;br /&gt;&lt;br /&gt;P.S. Напоследок хочу предостеречь вас от попытки на радостях наследовать от системных конструкторов - к сожалению, с ними может не пройти выражение:&lt;pre&gt;&lt;code class="javascript"&gt;__this[i] = __super[i];&lt;/code&gt;&lt;/pre&gt;Дело в том, что они не являются в полном смысле JavaScript`овыми объектами и для них такой возможности часто не предусмотрено. Например, мне не удалось наследовать от &lt;code&gt;XMLHttpRequest&lt;/code&gt; - там это выражение вылетало с ошибкой даже при том, что я обромил его блоком &lt;code&gt;try/catch&lt;/code&gt;:&lt;pre&gt;&lt;code class="javascript"&gt;try {__this[i] = __super[i];} catch(/** @type {Error} */ e) {}&lt;/code&gt;&lt;/pre&gt;Всё равно, даже такой бронебойный код вываливался с ошибкой в браузере. По крайней мере в FF это дело у меня не прошло. Так что ещё раз повторюсь - расширенная модель предназначена для наследования от родных JavaScript-конструкторов объектов. Будьте осторожны!&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Другие части&lt;/em&gt;:&lt;br /&gt;&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-1.html"&gt;Часть 1: Основа Базовой модели&lt;/a&gt;&lt;br /&gt;&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-2.html"&gt;Часть 2:Механизм вызова методов&lt;/a&gt;&lt;br /&gt;&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-3.html"&gt;Часть 3: Внутренние вызовы&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-4932623386106645377?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/4932623386106645377/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=4932623386106645377' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4932623386106645377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4932623386106645377'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/04/javascript-4.html' title='JavaScript: особенности инкапсуляции на основе замыканий. Часть 4: Наследование'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_e9ftZE9b4-4/Sd5O1AHgecI/AAAAAAAAAHg/roK2fDCxwx0/s72-c/%D0%A0%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F%20%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-501368440575642280</id><published>2009-04-20T04:41:00.000-07:00</published><updated>2009-04-23T01:12:56.916-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Effective JavaScript'/><title type='text'>JavaScript: Контроль типов</title><content type='html'>JavaScript считается слабо-типизированным зыком не потому, что у него нету типов, а потому, что у переменных в этом языке не фиксируется, на объект или примитив  какого типа они будут смотреть. При этом интерпретатор JS преобразует типы в зависимости от контекста их использования.&lt;br /&gt;&lt;br /&gt;Тем не менее, не всегда это удобно. В частности, это не удобно, когда пишешь библиотеку для кого-то, кто потом может вызвать твои методы &lt;em&gt;не так&lt;/em&gt;. По-этому я иногда предпочитаю вносить дополнительный код в свои скрипты, что бы чётко определять, какими типами должны являться значения тех или иных переменных.&lt;br /&gt;&lt;br /&gt;Это решение направлено не только на &lt;span style="font-style:italic;"&gt;проверку&lt;/span&gt;, но ещё и на &lt;span style="font-style:italic;"&gt;преобразование&lt;/span&gt; типа к тому, которого я жду во избежание разных фокусов, которые могут подстерегать при автоматических контекстных преобразованиях.&lt;br /&gt;&lt;br /&gt;В интегрированной среде разработки Eclipse есть такое средство, как Snippets, прекрасно подходящее для решения задачи контроля типов в JavaScript. Snippet - это кусочек кода в общем виде, настраиваемый под конкретную ситуацию путём редактирования некоторых имён и значений, называемых переменными snippet`ов. Их можно создавать и группировать, что бы потом удобно было находить и использовать. Я у себя создал группу "Params check" и в ней расположил snippet`ы для всех примитивов.&lt;br /&gt;&lt;br /&gt;Перед тем, как начать, следует ввести константы-обозначения типов, с которыми будет проще осуществлять эти проверки. Эти константы будут иметь строковые типы и будут служить для сравнения результатов операции &lt;code&gt;typeof&lt;/code&gt;, применяемой к переменным:&lt;pre&gt;&lt;code class="javascript"&gt;var undefined,&lt;br /&gt;&amp;nbsp; &amp;nbsp; Types = {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Primitive types&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type {string} */ STRING_TYPE: 'string',&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type {string} */ OBJECT_TYPE: 'object',&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type {string} */ NUMBER_TYPE: 'number',&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type {string} */ BOOLEAN_TYPE: 'boolean',&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type {string} */ FUNCTION_TYPE: 'function',&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type {string} */ UNDEFINED_TYPE: 'undefined'&lt;br /&gt;&amp;nbsp; &amp;nbsp; };&lt;/code&gt;&lt;/pre&gt;Итак, начнём с самого простого типа - boolean. На данный момент я произвожу проверку на него следующим блоком кода:&lt;pre&gt;&lt;code class="javascript"&gt;//Param x as boolean check&lt;br /&gt;if (typeof x !== Types.BOOLEAN_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (x instanceof Boolean)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = x.valueOf();&lt;br /&gt;&amp;nbsp; &amp;nbsp; else {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (x instanceof String)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = x.valueOf();&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (typeof x === Types.STRING_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = x !== '' &amp;&amp; x !== 'false';&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = x ? true : false;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;/code&gt;&lt;/pre&gt;, изначальный же Snippet для этогой проверки должен содержать переменную snippet`а для имяни переменной, что бы внести его один раз и больше не маяться:&lt;pre&gt;&lt;code class="javascript"&gt;//Param ${varName} as boolean check&lt;br /&gt;if (typeof ${varName} !== Types.BOOLEAN_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (${varName} instanceof Boolean)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName} = ${varName}.valueOf();&lt;br /&gt;&amp;nbsp; &amp;nbsp; else {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (${varName} instanceof String)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName} = ${varName}.valueOf();&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (typeof ${varName} === Types.STRING_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName} = ${varName} !== '' &amp;&amp; ${varName} !== 'false';&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName} = ${varName} ? true : false;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;/code&gt;&lt;/pre&gt;Следующий пример - для наиболее частого в использовании типа - строк:&lt;pre&gt;&lt;code class="javascript"&gt;//Param s1 as string check&lt;br /&gt;if (s1 === null || typeof s1 === Types.UNDEFINED_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw new TypeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - s1 - is not specified by function call!'&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&lt;br /&gt;if (typeof s1 !== Types.STRING_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; s1 = s1 instanceof String ?&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; s1.valueOf()&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; : s1.toString();&lt;br /&gt;&lt;br /&gt;if (!/^[1-0a-f]*$/i.test())&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw SyntaxError('Required param - s1 - has invalid format.');&lt;/code&gt;&lt;/pre&gt;Обратите внимание, что здесь у нас уже две переменных для строки я счёл важным не только проверку её типа, но ещё и правильную проверку её формата с использованием регулярного выражения. В данном примере по шаблону проверяется, является ли строка представлением числа в 16-ричном формате.&lt;br /&gt;Теперь решение в общем виде - string Snippet:&lt;pre&gt;&lt;code class="javascript"&gt;//Param ${varName} as string check&lt;br /&gt;if (${varName} === null || typeof ${varName} === Types.UNDEFINED_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw new TypeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - ${varName} - is not specified by function call!'&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&lt;br /&gt;if (typeof ${varName} !== Types.STRING_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; ${varName} = ${varName} instanceof String ?&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName}.valueOf()&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; : ${varName}.toString();&lt;br /&gt;&lt;br /&gt;if (!/^${regexp}$$/.test())&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw SyntaxError('Required param - ${varName} - has invalid format.');&lt;/code&gt;&lt;/pre&gt;Что же касается числового типа - number, то здесь имеется небольшой подводный камешек. Дело в том, что целые и вещественные числа спецификация ECMAScript не различает. Однако, как правило, в сценарии по логике легко понять, какого именно числа мы ждём - целого или дробного, так что я сделал два разных блока проверки - для целых (integer) и вещественных (float) чисел:&lt;pre&gt;&lt;code class="javascript"&gt;//Param x as integer check&lt;br /&gt;if (x === null || typeof x === Types.UNDEFINED_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw new TypeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - x - is not specified by function call.'&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&lt;br /&gt;if (typeof x !== Types.NUMBER_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (x instanceof Number)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = x.valueOf();&lt;br /&gt;&amp;nbsp; &amp;nbsp; else {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (x instanceof String)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = x.valueOf();&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (typeof x === Types.STRING_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = parseInt(x);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new TypeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - x - has invalid type!'&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;if (x &lt; 1 || x &gt; 100)&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw new RangeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - x = '&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; + x&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; + ' - is out of correct range (1 - 100).'&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//Param y as float check&lt;br /&gt;if (y === null || typeof y === Types.UNDEFINED_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw new TypeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - y - is not specified by function call.'&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&lt;br /&gt;if (typeof y !== Types.NUMBER_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (y instanceof Number)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; y = y.valueOf();&lt;br /&gt;&amp;nbsp; &amp;nbsp; else {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (y instanceof String)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; y = y.valueOf();&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (typeof y === Types.STRING_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; y = parseFloat(y);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new TypeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - y - has invalid type.'&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;if (y &lt; 0.55 || y &gt; 0.99)&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw new RangeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - y = '&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; + y&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; + ' - is out of correct range (0.55 - 0.99).'&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;/code&gt;&lt;/pre&gt;Видим, что для чисел кроме типа проверяется тот интервал значений, в который они должны попадать. Если они не входят в него, возбуждается исключение. Snippet`ы:&lt;pre&gt;&lt;code class="javascript"&gt;//Param ${varName} as integer check&lt;br /&gt;if (${varName} === null || typeof ${varName} === Types.UNDEFINED_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw new TypeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - ${varName} - is not specified by function call.'&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&lt;br /&gt;if (typeof ${varName} !== Types.NUMBER_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (${varName} instanceof Number)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName} = ${varName}.valueOf();&lt;br /&gt;&amp;nbsp; &amp;nbsp; else {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (${varName} instanceof String)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName} = ${varName}.valueOf();&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (typeof ${varName} === Types.STRING_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName} = parseInt(${varName});&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new TypeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - ${varName} - has invalid type!'&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;if (${varName} &lt; ${min_value} || ${varName} &gt; ${max_value})&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw new RangeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - ${varName} = '&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; + ${varName}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; + ' - is out of correct range (${min_value} - ${max_value}).'&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//Param ${varName} as float check&lt;br /&gt;if (${varName} === null || typeof ${varName} === Types.UNDEFINED_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw new TypeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - ${varName} - is not specified by function call.'&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&lt;br /&gt;if (typeof ${varName} !== Types.NUMBER_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (${varName} instanceof Number)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName} = ${varName}.valueOf();&lt;br /&gt;&amp;nbsp; &amp;nbsp; else {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (${varName} instanceof String)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName} = ${varName}.valueOf();&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (typeof ${varName} === Types.STRING_TYPE)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ${varName} = parseFloat(${varName});&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new TypeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - ${varName} - has invalid type.'&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;if (${varName} &lt; ${min_value} || ${varName} &gt; ${max_value})&lt;br /&gt;&amp;nbsp; &amp;nbsp; throw new RangeError(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Required param - ${varName} = '&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; + ${varName}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; + ' - is out of correct range (${min_value} - ${max_value}).'&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;/code&gt;&lt;/pre&gt;Вот и всё. Остаётся лишь добавить, что чаще всего я использую такие проверки для написания внешних библиотек, что бы контролировать те значения, которые приходят в мою функцию. Эти проверки излишни, если тот код, который вы пишете, будет вызываться так же вами и, таким образом, легче установить контроль с вызывающей стороны. Однако бывают ситуации, когда параметры вызова так или иначе зависит не от вас и тогда такие проверки бывают оправданны.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-501368440575642280?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/501368440575642280/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=501368440575642280' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/501368440575642280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/501368440575642280'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/04/blog-post.html' title='JavaScript: Контроль типов'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-4354094718728699635</id><published>2009-04-19T11:20:00.000-07:00</published><updated>2011-09-19T09:15:31.602-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript patterns'/><title type='text'>JavaScript: особенности инкапсуляции на основе замыканий. Часть 3: Внутренние вызовы</title><content type='html'>Продолжаю серию публикаций, направленную на усовершенствование применения инкапсуляции на основе замыканий.В &lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-1.html"&gt;первой статье, "Базовая модель"&lt;/a&gt;, я поставил проблему несовместимости инкапсуляции на основе замыканий с реализацией ООП на основе прототипов, каковая наличествует в JavaScript и предложил т.н. "базовую модель" того, как можно было бы это обойти. Во &lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-2.html"&gt;второй статье цикла, "Механизм вызова методов"&lt;/a&gt;, я сосредоточил внимание на упрощении вызова методов в рамках базовой модели и исправлении недочёта базовой модели в отношении использования ключевого слова &lt;code&gt;this&lt;/code&gt; и в отношении вызовов объектом собственных методов. Данная статья посвящена окончанию разработки базовой модели. Осталась ещё не решённой проблема "внутренних вызовов", т.е. вызовов одним экземпляром объекта методов другого экземпляра этого же объекта.Итак, какая проблема возникает в базовой модели при внутреннем вызове? Давайте посмотрим по-внимательнее на методы &lt;code&gt;get&lt;/code&gt; и &lt;code&gt;set&lt;/code&gt;:&lt;pre&gt;&lt;code class="javascript"&gt;get: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; x = _x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; _this = __this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; _x = null;&lt;br /&gt;},&lt;br /&gt;set: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; _x = x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; x =&lt;br /&gt;&amp;nbsp; &amp;nbsp; _this = null;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;, а так же давайте снова взглянем на &lt;code&gt;methodCaller&lt;/code&gt;:&lt;pre&gt;&lt;code class="javascript"&gt;function methodCaller(args) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState.get();&lt;br /&gt;&amp;nbsp; &amp;nbsp; var result = args.callee.apply(_this, args);&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; return result;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Итак, что у нас получится, когда объект вызовет какой-нибудь метод другого объекта, являющегося экземпляром того же самого класса? Очевидно, у вызываемого объекта выполнится метод &lt;code&gt;privateState.get()&lt;/code&gt;, который затрёт значения переменных вызывающего объекта и они окажутся безвозвратно утеряны.Наилучшим решением представляется модификация методов &lt;code&gt;get&lt;/code&gt; и &lt;code&gt;set&lt;/code&gt; так, что бы первый проверял, наполнен ли прототип в данный момент переменными какого-либо объекта или пуст перед тем, как вносить изменения в его переменные и сохранял их куда-то, а второй, после очищения прототипа проверял, были ли сохранены данные предыдущего объекта и возвращал бы в таком случае эти значения на место.Тогда сначала надо придумать, как определить - наполнен ли объект или пуст. Здесь нам опять поможет специальная переменная &lt;code&gt;_this&lt;/code&gt;, в отношении которой мы в прошлой статье договорились, что она будет ссылаться на объект, переменными которого в данный момент наполнен прототип и очищаться в методе &lt;code&gt;set&lt;/code&gt;, где ей будет присваиваться значение &lt;code&gt;null&lt;/code&gt;:&lt;pre&gt;&lt;code class="javascript"&gt;get: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (_this) //Объект уже чем-то наполнен&lt;br /&gt;&amp;nbsp; &amp;nbsp; //...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Ну хорошо, выяснили - наполнен, что делать дальше? Очевидно, нужно куда-то сохранить значения переменных, что бы потом, в методе &lt;code&gt;set&lt;/code&gt;, присвоить их обратно. Но куда и как? Перечислять снова все переменные вручную - это уже окончательно перегрузит "базовую модель". Наверное, лучше всего просто вернуть переменные тому объекту, который их разместил, вызвав у его &lt;code&gt;privateState&lt;/code&gt; метод &lt;code&gt;set&lt;/code&gt;, с тем, что бы потом, в методе &lt;code&gt;set&lt;/code&gt; вернуть их назад методом &lt;code&gt;privateState.get&lt;/code&gt;. Примерно так:&lt;pre&gt;&lt;code class="javascript"&gt;var __instance = null;&lt;br /&gt;return {&lt;br /&gt;&amp;nbsp; &amp;nbsp; get: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (_this)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (__instance = _this).privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = _x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this = __this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _x = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; set: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _x = x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (__instance) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __instance.privateState.get();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __instance = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Вроде бы, всё нормально. Но... теоретически, возможна ситуация, когда этого будет не достаточно.Представьте ситуацию, когда &lt;code&gt;a1&lt;/code&gt; и &lt;code&gt;a2&lt;/code&gt; являются объектами, созданными конструктором &lt;code&gt;A&lt;/code&gt; и, соответственно, ссылаются на один и тот же прототип. Тогда давайте проанализируем, что же будет происходить:&lt;ol&gt;&lt;li&gt;Какой-то метод &lt;code&gt;a1&lt;/code&gt; вызывает другой или тот же самый метод &lt;code&gt;a2&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Тогда метод &lt;code&gt;a2.privateState.get()&lt;/code&gt; вызовет &lt;code&gt;a1.privateState.set()&lt;/code&gt;, очищая прототип от переменных &lt;code&gt;a1&lt;/code&gt; и наполняя его своими переменными.&lt;/li&gt;&lt;li&gt;После выполнения вызванного метода &lt;code&gt;a2&lt;/code&gt; вернёт всё обратно, вызвав в методе &lt;code&gt;a2.privateState.set()&lt;/code&gt; метод &lt;code&gt;a1.privateState.get()&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Метод объекта &lt;code&gt;a1&lt;/code&gt; продолжит выполнение, как ни в чём не бывало. В распоряжении прототипа в этот момент будут именно переменные &lt;code&gt;a1&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;Теперь давайте усложним ситуацию - введём дополнительно объект &lt;code&gt;a3&lt;/code&gt;:&lt;ol&gt;&lt;li&gt;Какой-то метод &lt;code&gt;a1&lt;/code&gt; вызывает другой или тот же самый метод &lt;code&gt;a2&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Тогда метод &lt;code&gt;a2.privateState.get()&lt;/code&gt; вызовет &lt;code&gt;a1.privateState.set()&lt;/code&gt;, очищая прототип от переменных &lt;code&gt;a1&lt;/code&gt; и наполняя его значениями своих полей.&lt;/li&gt;&lt;li&gt;Выполняющийся в данный момент метод объекта &lt;code&gt;a2&lt;/code&gt; вызывает какой-то из методов объекта &lt;code&gt;a3&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Вызывается метод &lt;code&gt;a3.privateState.get()&lt;/code&gt;, который вызывает метод &lt;code&gt;a2.privateState.set()&lt;/code&gt;, который в свою очередь очищает прототип от переменных объекта &lt;code&gt;a2&lt;/code&gt; и (что нам вовсе не нужно) вызывает &lt;code&gt;a1.privateState.get()&lt;/code&gt;, который прописывает в прототип значения переменных &lt;code&gt;a1&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;После выполнения &lt;code&gt;a2.privateState.set()&lt;/code&gt;, метод &lt;code&gt;a3.privateState.get()&lt;/code&gt; продолжит выполняться и затрёт в прототипе все переменные объекта &lt;code&gt;a1&lt;/code&gt;, присвоив им значения переменных &lt;code&gt;a3&lt;/code&gt;. Объект &lt;code&gt;a1&lt;/code&gt; потеряет свои поля.&lt;/li&gt;&lt;/ol&gt;Таким образом, более сложная схема взаимодействия объектов, созданных  одним конструктором, уже перестанет корректно работать.Решением данной проблемы представляется создание не одной переменной, а массива объектов и хранение его не в &lt;code&gt;privateState&lt;/code&gt;, а в прототипе, как закрытую статическую переменную. В этот массив можно будет помещать объекты данного класса, которые находятся на более высоких, чем текущий, уровнях "лестницы вызова":&lt;pre&gt;&lt;code class="javascript"&gt;var /** @static&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @type Array&amp;lt;Object&amp;gt; */&lt;br /&gt;&amp;nbsp; &amp;nbsp; __callInstances = [];&lt;/code&gt;&lt;/pre&gt;Тогда нам так же понадобится разделить функциональность метода &lt;code&gt;privateState.set()&lt;/code&gt; - что бы выкатывать состояние предыдущего объекта в случае вызова этого метода &lt;code&gt;methodCaller&lt;/code&gt;`ом и не выкатывать - в случае вызова &lt;code&gt;privateState.get&lt;/code&gt;`ом другого экземпляра того же класса. Определить это проще всего, договорившись добавлять объект в методе &lt;code&gt;privateState.get()&lt;/code&gt; в массив &lt;code&gt;__callInstances&lt;/code&gt; прежде, чем вызывать у него метод &lt;code&gt;privateState.set()&lt;/code&gt; - тогда по нахождению объекта &lt;code&gt;__this&lt;/code&gt; в конце массива мы сможем со всей определённостью понять - вызывают метод &lt;code&gt;privateState.set()&lt;/code&gt; из &lt;code&gt;privateState.get()&lt;/code&gt;`а другого экземпляра того же класса (и тогда выкатывать переменные последнего объекта в массиве &lt;code&gt;__callInstances&lt;/code&gt; не нужно) или он вызывается из &lt;code&gt;methodCaller&lt;/code&gt;`а прототипа (и тогда, соответственно, нужно):&lt;pre&gt;&lt;code class="javascript"&gt;get: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (_this) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __callInstances.push(_this);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this.privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; x = _x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; _this = __this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; _x = null;&lt;br /&gt;},&lt;br /&gt;set: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; _x = x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; x =&lt;br /&gt;&amp;nbsp; &amp;nbsp; _this = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (__callInstances.length&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;amp;&amp;amp; __callInstances[__callInstances.length - 1] !== __this)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __callInstances.pop().privateState.get();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Так же изменения немного коснутся и механизма вызова &lt;code&gt;methodCaller&lt;/code&gt;`а. Напомним, каким он у нас стал после выполнения действий из предыдущей статьи цикла:&lt;pre&gt;&lt;code class="javascript"&gt;if (_this) return methodCaller.call(this, arguments);&lt;/code&gt;&lt;/pre&gt;Теперь для того, что бы выполнять или не выполнять его, нам недостаточно будет знать о простом наличии в переменной &lt;code&gt;_this&lt;/code&gt; какого-то не &lt;code&gt;null&lt;/code&gt;`евого значения - ибо в случае вызова одним экземпляром метода другого экземпляра того же класса, у нас переменная &lt;code&gt;_this&lt;/code&gt; будет иметь значение, но оно будет не верным, поскольку будет ссылаться на вызывающий, а не на вызываемый объект. Так что теперь нам нужно будет в этом случае осуществлять проверку на идентичность значения этого свойства переменной &lt;code&gt;this&lt;/code&gt;, что бы понять, что переменная &lt;code&gt;_this&lt;/code&gt; заполнена правильным значением:&lt;pre&gt;&lt;code class="javascript"&gt;if (_this !== this) return methodCaller.call(this, arguments);&lt;/code&gt;&lt;/pre&gt;Соберём всё вместе и протестируем:&lt;pre&gt;&lt;code class="javascript"&gt;/** @author Vyacheslav Lapin&amp;lt;se-la-vy.blogspot.com&amp;gt;&lt;br /&gt;&amp;nbsp;* @version 0.01 08.04.2009 17:52:40&lt;br /&gt;&amp;nbsp;* @constructor&lt;br /&gt;&amp;nbsp;* @param {number} x */&lt;br /&gt;function A(x) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (this.constructor !== A)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return new A(x);&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState = this.createPrivateState(x);&lt;br /&gt;}&lt;br /&gt;A.prototype = new function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var /** @type A */ _this,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @static&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;* @type Array&amp;lt;A&amp;gt; */&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __callInstances = [];&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.createPrivateState = function(__x) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //Переменные для хранения private-полей объекта&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var /** @type number */ _x = __x,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @constant&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;* @type A */&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __this = this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.createPrivateState = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; get: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (_this) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __callInstances.push(_this);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this.privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = _x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this = __this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _x =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; set: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _x = x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (__callInstances.length&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;amp;&amp;amp; __callInstances[__callInstances.length - 1] !== __this)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __callInstances.pop().privateState.get();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @param {Arguments} args */&lt;br /&gt;&amp;nbsp; &amp;nbsp; function methodCaller(args) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.privateState.get();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var result = args.callee.apply(_this, args);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return result;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; //--------------------------------------------------------------------------&lt;br /&gt;&amp;nbsp; &amp;nbsp; var /** @type number */ x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; //...&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @return {number} */&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.getX = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (_this !== this) return methodCaller.call(this, arguments);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @param {number} _x&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @return {A} this */&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.setX = function(_x) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (_this !== this) return methodCaller.сall(this, arguments);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = _x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** Метод, вызывающий себя же, но от другого объекта, переданного ему в&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* первом аргументе и передающий второй свой аргумент в качестве первого. В&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* случае, если первый аргумент не передан, выводится результат метода getX.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @param {A} [a1=null]&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @param {A} [a2=null]&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @param {A} [a3=null]&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @return {number} */&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.callMethod = function(a1, a2, a3) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (_this !== this) return methodCaller.call(this, arguments);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (a1)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return this.getX() + a1.callMethod(a2, a3);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return this.getX();&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;A._className = 'A';&lt;br /&gt;A.prototype.constructor = A;&lt;br /&gt;//test&lt;br /&gt;var /** @type A */ a1 = new A(1),&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @type A */ a2 = new A(2),&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @type A */ a3 = A(3),&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @type A */ a4 = A(4); //Можно вызывать и так - мы это предусмотрели в конструкторе&lt;br /&gt;alert(a1.callMethod(a2, a3, a4));&lt;/code&gt;&lt;/pre&gt;Результат - "10" во всех браузерах, в которых я тестировал(IE, Opera, FF, Safari последних на данный момент стабильных версий).It works! :)))))На этом фрормирование первой версии "базовой модели" я считаю завершённым. Дальше в статьях данного цикла мы поговорим о том, как можно упростить разработку таких конструкторов с использованием инструментального средства Eclipse, а так же о том, как расширить "базовую модель", что бы применять её для наследования классов в JavaScript с сохранением преимуществ, которые даёт инкапсуляция на основе замыканий.А после этого мы поговорим о том, как расширить язык JavaScript, создав его диалект, направленный на более удобное использование базовой модели.&lt;em&gt;Другие части&lt;/em&gt;:&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-1.html"&gt;Часть 1: Основа Базовой модели&lt;/a&gt;&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-2.html"&gt;Часть 2:Механизм вызова методов&lt;/a&gt;&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-4.html"&gt;Часть 4: Наследование&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-4354094718728699635?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/4354094718728699635/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=4354094718728699635' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4354094718728699635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4354094718728699635'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/04/javascript-3.html' title='JavaScript: особенности инкапсуляции на основе замыканий. Часть 3: Внутренние вызовы'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-4800861638676827226</id><published>2009-04-19T09:28:00.000-07:00</published><updated>2011-09-22T08:19:59.388-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript patterns'/><title type='text'>JavaScript: особенности инкапсуляции на основе замыканий. Часть 2: Механизм вызова методов</title><content type='html'>В &lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-1.html/"&gt;предыдущей статье&lt;/a&gt; была обозначена проблема поиска совместимости инкапсуляции на основе замыканий с принципом делегирования методов объектов, созданных одним конструктором, объекту-прототипу. В качестве возможного решения была продемонстрирована модель создания специального генератора объектов (называемых "privateState") внутри прототипа, в котором находятся методы и набор закрытых полей. Каждый из порождаемых генератором объектов в этой модели содержит вложенный набор переменных (переменных объекта), соответствующих переменным прототипа, а так же двух методов:&lt;ul&gt;&lt;li&gt;"get" - присваивание значений переменных объекта - переменным прототипа,&lt;/li&gt;&lt;li&gt;"set" - присваивание значений переменных прототипа - переменным объекта.&lt;/li&gt;&lt;/ul&gt;У этой модели имеется ряд неудобств при оформлении кода и ограничений при работе с получающимися объектами, как то:&lt;ul&gt;&lt;li&gt;Необходимость в начале каждого метода вставлять вызов метода "get" и в конце - метода "set";&lt;/li&gt;&lt;li&gt;Неправильное поведение при вызове одним методом объекта других методов того же объекта;&lt;/li&gt;&lt;li&gt;Необходимость каждый раз вписывать имена переменных в методы "set" и "get";&lt;/li&gt;&lt;li&gt;Неправильное поведение при вызове одним объектом другого объекта, являющегося потомком того же класса.&lt;/li&gt;&lt;/ul&gt;Попыткам разрешить эти неудобства и снять ограничения посвящена данная и последующие статьи данного цикла. Тема данной статьи - разрешение первых двух из приведённых пунктов.Мне хотелось бы разработать максимально удобный паттерн использования инкапсуляции с ООП на основе прототипов, поэтому необходимость при написании каждого метода писать 2 строки кода - одну ("&lt;code&gt;this.privateState.get();&lt;/code&gt;") в начале метода, вторую ("&lt;code&gt;this.privateState.set();&lt;/code&gt;") - в конце, не может не резать мне глаз.Итак, что мы могли бы сделать, что бы сократить эти две строчки?Было бы здорово, если бы у нас была возможность поставить некоторый фильтр на вызовах всех методов прототипа, производимых через объект. Мы могли бы просто вписать перед ним вызов &lt;code&gt;get&lt;/code&gt;, а после вызова самого метода - вызов &lt;code&gt;set&lt;/code&gt; и - дело с концом. Но такой возможности у нас нет, поэтому нам придётся этот фильтр всё-таки вызывать из каждого метода, т.е. от одной строки - вызова.Назвать данный метод можно "&lt;code&gt;methodCaller&lt;/code&gt;", т.к. он будет ответственным за вызовы методов. Нетрудно будет его написать:&lt;pre&gt;&lt;code class="javascript"&gt;/** @returns {Object} результат выполнения метода в контексте полей&lt;br /&gt;&amp;nbsp;* данного объекта. */&lt;br /&gt;function methodCaller() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState.get();&lt;br /&gt;&amp;nbsp; &amp;nbsp; var /** @type Object */ result = //вызов вызвавшей данную функции&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; return result;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Как можно было бы передать этому методу имя и параметры вызова той функции, которая его вызвала, что бы он узнал, какой именно метод и с какими параметрами ему нужно будет вызвать? Можно было бы передать функцию - первым аргументом, а параметры - массивом во втором аргументе, но есть вариант по-проще - мы можем передавать ссылку "&lt;code&gt;arguments&lt;/code&gt;". Она содержит и параметры вызова нашей функции, являясь их псевдо-массивом и ссылку на неё саму (она содержится в поле "&lt;code&gt;callee&lt;/code&gt;").Необходимо помнить, что при вызове функции как метода объекта нам придётся пользоваться одним из методов объекта &lt;code&gt;Function&lt;/code&gt; - "&lt;code&gt;call&lt;/code&gt;" или "&lt;code&gt;apply&lt;/code&gt;", которые предоставляют контроль над ссылкой "&lt;code&gt;this&lt;/code&gt;" внутри неё, что бы подставлять методу правильную ссылку "&lt;code&gt;this&lt;/code&gt;", иначе она будет вести не туда, куда нужно, что может привести к неадекватной работе метода.Кроме того, следует учесть, что метод может вызывать те или иные исключения. Несмотря на это, для него в любом случае необходимо вызвать метод "&lt;code&gt;set&lt;/code&gt;". Для того, что бы этого добиться, поместим вызов этого метода в блок &lt;code&gt;finally&lt;/code&gt;, а вызов исконного - в блок &lt;code&gt;try&lt;/code&gt; перед ним. Тогда мы сможем элегантно избавиться от создания специальной переменной "&lt;code&gt;result&lt;/code&gt;", поскольку блок "&lt;code&gt;finally&lt;/code&gt;" выполняется даже после выполнения команды &lt;code&gt;return&lt;/code&gt;.Итак, результирующий метод:&lt;pre&gt;&lt;code class="javascript"&gt;/** @returns {Object} результат выполнения метода в контексте полей&lt;br /&gt;&amp;nbsp;* данного объекта. */&lt;br /&gt;function methodCaller(args) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState.get();&lt;br /&gt;&amp;nbsp; &amp;nbsp; try {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return args.callee.apply(this, args);&lt;br /&gt;&amp;nbsp; &amp;nbsp; } finally {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Теперь рассмотрим возможный механизм вызова данного метода:&lt;pre&gt;&lt;code class="javascript"&gt;this.getX = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (/* если вызов происходит извне */)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return methodCaller.call(this, arguments);&lt;br /&gt;&amp;nbsp; &amp;nbsp; return x;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Осталось только придумать, как же все наши методы могли бы узнать, извне они вызваны или изнутри - методом "methodCaller"? В принципе, у объекта &lt;code&gt;Function&lt;/code&gt; есть ссылка на функцию, вызвавшую данную - эта ссылка находится в свойстве &lt;code&gt;caller&lt;/code&gt; и доступна только внутри функции. Мы могли бы написать выражение так:&lt;pre&gt;&lt;code class="javascript"&gt;if (arguments.callee.caller !== methodCaller) // если вызов происходит извне&lt;/code&gt;&lt;/pre&gt;, однако, к сожалению, метод &lt;code&gt;caller&lt;/code&gt; не входит в стандарт ECMAScript и поэтому считается устаревшим. Почти все реализации JavaScript его до сих пор поддерживают, но у нас нет никаких гарантий того, что он будет поддерживаться дальше. Так что нужен какой-то другой механизм.Вспомним, что в первой статье данного цикла мы решили об-&lt;code&gt;null&lt;/code&gt;-ять ссылки прототипа при окончании выполнения им методов, что бы освобождать память. Таким образом, на момент вызова метода извне, когда метод &lt;code&gt;privateState.get&lt;/code&gt; ещё не вызван, у нас все закрытые поля ссылаются на &lt;code&gt;null&lt;/code&gt;. Т.е. мы могли бы узнать, произведён вызов метода снаружи или изнутри методом &lt;code&gt;methodCaller&lt;/code&gt;, просто проверив любое из полей объекта на равенство &lt;code&gt;null&lt;/code&gt;, но вот незадача - мы не можем точно знать, что поле объекта, которое выберем для этого, не окажется равным &lt;code&gt;null&lt;/code&gt; по логике работы самого класса и, соответственно, что мы не ошибёмся в нашем выводе о том, кто вызвал метод и не вызовем бесконечный цикл, в котором метод будет вызывать &lt;code&gt;methodCaller&lt;/code&gt;, а тот в свою очередь - опять тот же метод.Иными словами, нам нужна такая ссылка в каждом объекте, про которую у нас всегда будет известно, что она - не &lt;code&gt;null&lt;/code&gt;. Можно было бы сделать какой-нибудь простой служебный флаг типа &lt;code&gt;boolean&lt;/code&gt;, который был бы скрытым полем любого объекта и всегда был бы равен &lt;code&gt;true&lt;/code&gt;, что бы по его состоянию каждый метод мог определённо сказать - применён метод &lt;code&gt;privateState.get&lt;/code&gt; или не применён, но есть идея по-лучше: помните, я в начале статьи писал о необходимости всегда помнить о том правиле, что приватные методы нам придётся вызывать не как обычно, просто открывая за их именами круглые скобки и перечисляя параметры, а с использованием специальных методов объекта &lt;code&gt;function&lt;/code&gt; - &lt;code&gt;call&lt;/code&gt; или &lt;code&gt;apply&lt;/code&gt;, поскольку нам нужно явно указывать JavaScript-интерпретатору, что в этих методах ссылка &lt;code&gt;this&lt;/code&gt; должна вести на объект, иначе она будет вести не туда. Помните? Так вот, теперь у нас появляется возможность обойти это жёсткое правило, приняв определённое соглашение. Т.к. нам всё равно нужна некоторая флаговая переменная для того, что бы мы по ней смогли гарантированно отличить, выполнен уже для объекта метод &lt;code&gt;privateStage.get()&lt;/code&gt; или нет, мы можем этой переменной присвоить ссылку на сам этот объект. :)Т.е. введём специальную переменную в прототипе, назовём её &lt;code&gt;_this&lt;/code&gt;, и присвоим ей ссылку &lt;code&gt;this&lt;/code&gt;. Можно будет из любого закрытого метода использовать её вместо обычной ссылки &lt;code&gt;this&lt;/code&gt; и тогда станет возможно вызывать эти методы обычным образом - как функциии, без использования таких дополнительных методов объекта &lt;code&gt;function&lt;/code&gt;, как &lt;code&gt;call&lt;/code&gt; и &lt;code&gt;apply&lt;/code&gt;. ;)Тогда в начале private`ных методов мы можем писать следующую строчку:&lt;pre&gt;&lt;code class="javascript"&gt;if (_this !== this) return arguments.callee.apply(_this, arguments);&lt;/code&gt;&lt;/pre&gt;и это гарантирует нам правильную работу ссылки &lt;code&gt;this&lt;/code&gt; в них.Т.е. теперь механизм вызова методов будет выглядеть так:&lt;pre&gt;&lt;code class="javascript"&gt;function methodCaller(args) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState.get();&lt;br /&gt;&amp;nbsp; &amp;nbsp; try {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Обратите внимание - теперь мы можем использовать ссылку "_this",&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // которая доступна между вызовами методов объекта privateState -&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // "get()" и "set()".&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return args.callee.apply(_this, args);&lt;br /&gt;&amp;nbsp; &amp;nbsp; } finally {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Теперь рассмотрим возможный механизм вызова данного метода:&lt;pre&gt;&lt;code class="javascript"&gt;this.getX = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (!_this) // если вызов происходит извне&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return methodCaller.call(this, arguments);&lt;br /&gt;&amp;nbsp; &amp;nbsp; return x;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;И теперь обратим внимание, что мы решили и вторую проблему из списка проблем вначале статьи - "Неправильное поведение при вызове одним методом объекта других методов того же объекта", состоявшее в том, что метод "&lt;code&gt;get&lt;/code&gt;" объекта privateState вызывается несколько раз, а потом происходят серии вызовов методов "&lt;code&gt;set&lt;/code&gt;". Теперь, если наш метод "&lt;code&gt;getX&lt;/code&gt;" вызовет какой-либо другой метод своего объекта, то этот метод пройдёт проверку в начале, т.к. у него уже установлено значение переменной "&lt;code&gt;_this&lt;/code&gt;", ведь метод "&lt;code&gt;privateState.set()&lt;/code&gt;" у него ещё не вызывался - он вызовется только когда метод "&lt;code&gt;getX&lt;/code&gt;" завершит работу.Подведём предварительный итог, приведя весь получившийся код:&lt;pre&gt;&lt;code class="javascript"&gt;/** @constructor&lt;br /&gt; * @author Vyacheslav Lapin&amp;lt;se-la-vy.blogspot.com&amp;gt; */&lt;br /&gt;function A(x) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState = this.createPrivateState();&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.setX(x);&lt;br /&gt;}&lt;br /&gt;A.prototype = new function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var /** @type A */ _this,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type number */ x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** Конструктор объекта состояния private-полей объекта. Метод предназначен&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* для вызова один раз и только из конструктора объекта. Поэтому после&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* вызова он перекрывает объекту доступ к себе, посредством установки у&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* объекта одноимённого свойства, равного null&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @constructor&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @return {Object} - privateState-объект для конструктора A */&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.createPrivateState = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //Переменные для хранения private-полей объекта&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var /** @type number */ _x,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @constant&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;* @type A */&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __this = this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Закрываем возможность вызова этого метода объектом в дальнейшем&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.createPrivateState = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; get: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //Присвоение переменным прототипа значений private-полей объекта&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = _x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this = __this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Об-null-ение переменной объекта на время, пока её значение&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // находится в прототипе, воизбежание возможных проблем со&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // сборкой мусора&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _x = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //, а ссылку "__this" нет смысла об-null-ять - сборке мусора&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // никак не поможет, если мы на время выполнения метода запретим&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // объекту privateState ссылаться на объект, для которого он&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // создан.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; set: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Возвращение значения private-полю объекта после использования&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _x = x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Возвращать значение ссылки "__this" из прототипа так же не&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // имеет смысла - по сути она - константа и с ней не должно&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // ничего происходить.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //Об-null-ение переменных прототипа&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _this = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** Cлужебный метод для вызова методов объекта,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* которые используют его private-переменные. В методах, которые не&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* используют private-поля его вызывать не обязательно.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @param {Arguments} args */&lt;br /&gt;&amp;nbsp; &amp;nbsp; function methodCaller(args) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.privateState.get();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return args.callee.apply(_this, args);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; } finally {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.privateState.set();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @return {number} x */&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.getX = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (!_this) return methodCaller.call(this, arguments);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @param {number} newX Значение X для установки */&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.setX = function(newX) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (!_this) return methodCaller.call(this, arguments);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (typeof newX == 'number')&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = newX;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (typeof newX == 'object'&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;amp;&amp;amp; newX instanceof Number)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = newX.valueOf();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new Error(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'Param is not a number! Param type is ' + typeof newX&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** Функция для проверки корректности вызовова методов внутри объекта */&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.getX2 = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (!_this) return methodCaller.call(this, arguments);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return this.getX() * this.getX();&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;if (!A.hasOwnProperty('name'))&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @constant&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @type string&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;* @ignore */&lt;br /&gt;&amp;nbsp; &amp;nbsp; A.name = 'A';&lt;br /&gt;A.prototype.constructor = A;&lt;br /&gt;&lt;br /&gt;// Тестируем:&lt;br /&gt;var a1 = new A(5);&lt;br /&gt;alert('a1.getX() = ' + a1.getX()); // Выводит 'a1.getX() = 5'&lt;br /&gt;alert('a1.getX2() = ' + a1.getX2()); // Выводит 'a1.getX2() = 25'&lt;br /&gt;&lt;br /&gt;alert(a1.createPrivateState); // Выводит 'null' - метод не доступен&lt;br /&gt;&lt;br /&gt;var a2 = new A(40),&lt;br /&gt;&amp;nbsp; &amp;nbsp; a3 = new A(new Number(562)),&lt;br /&gt;&amp;nbsp; &amp;nbsp; a4 = new A(0);&lt;br /&gt;alert('a2.getX() = ' + a2.getX()); // Выводит 'a2.getX() = 40'&lt;br /&gt;alert('a2.getX2() = ' + a2.getX2()); // Выводит 'a2.getX2() = 1600'&lt;br /&gt;&lt;br /&gt;alert('a3.getX() = ' + a3.getX()); // Выводит 'a3.getX() = 562'&lt;br /&gt;alert('a3.getX2() = ' + a3.getX2()); // Выводит 'a3.getX2() = 315844'&lt;br /&gt;&lt;br /&gt;alert('a4.getX() = ' + a4.getX()); // Выводит 'a4.getX() = 0'&lt;br /&gt;alert('a4.getX2() = ' + a4.getX2()); // Выводит 'a4.getX2() = 0'&lt;br /&gt;&lt;br /&gt;alert('a1.getX() = ' + a1.getX()); // Выводит 'a1.getX() = 5'&lt;br /&gt;alert('a1.getX2() = ' + a1.getX2()); // Выводит 'a1.getX2() = 25'&lt;/code&gt;&lt;/pre&gt;&lt;em&gt;Другие части&lt;/em&gt;:&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-1.html"&gt;Часть 1: Основа Базовой модели&lt;/a&gt;&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-3.html"&gt;Часть 3: Внутренние вызовы&lt;/a&gt;&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-4.html"&gt;Часть 4: Наследование&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-4800861638676827226?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/4800861638676827226/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=4800861638676827226' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4800861638676827226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4800861638676827226'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/04/javascript-2.html' title='JavaScript: особенности инкапсуляции на основе замыканий. Часть 2: Механизм вызова методов'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-4307835950681505391</id><published>2009-04-19T08:08:00.000-07:00</published><updated>2011-09-22T08:22:37.993-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript patterns'/><title type='text'>JavaScript: особенности инкапсуляции на основе замыканий. Часть 1: Основа Базовой модели</title><content type='html'>Когда-то &lt;a href="http://www.crockford.com/"&gt;Дуглас Крокфорд&lt;/a&gt; впервые заметил, что на основе замыканий (closures) можно имитировать инкапсуляцию примерно так:&lt;pre&gt;&lt;code class="javascript"&gt;function A() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var /* number */ x = 5;&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.getX = function() { return x;}&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.setX = function(newX) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (typeof x == 'number')&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = newX;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new Error('Param is not a number!');&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var y = new A();&lt;br /&gt;alert(y.getX()); // Вернёт 5&lt;br /&gt;alert(y.x); // Вернёт 'undefined'&lt;/code&gt;&lt;/pre&gt;Вроде бы, выглядит логично, не правда ли? Вот только есть одна проблема, на которую в своё время &lt;a href="http://forum.vingrad.ru/index.php?showtopic=215139&amp;amp;view=findpost&amp;amp;p=1539135"&gt;обратил моё внимание&lt;/a&gt; участник &lt;a href="http://forum.vingrad.ru/"&gt;форума Vingrad&lt;/a&gt; с ником &lt;a href="http://forum.vingrad.ru/users/AKS"&gt;AKS&lt;/a&gt;: каждый объект содержит весь набор методов своего класса, и если объектов будет достаточно много, то этот метод приведёт к очень неэкономному расходованию памяти.&lt;br /&gt;&lt;br /&gt;JavaScript относится к языкам с ООП, основанным на прототипах (prototype-based OOP). В данном случае это значит, что у объектов с большим количеством методов, что бы сэкономить память, рекомендуется эти методы выносить в общий для них прототип. Это легко сделать, отказавшись от инкапсуляции и сделав эти поля открытыми, что бы методы прототипа каждый раз могли их считывать в зависимости от того, у какого объекта они вызваны. Но как добиться этого, не отказываясь от инкапсуляции для этих полей?&lt;br /&gt;&lt;br /&gt;Т.е. существует &lt;strong&gt;противоречие между инкапсуляцией на основе замыканий и принципами ООП на базе прототипов&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;Материалов на данную тему мне ещё нигде не попадалось, по-этому я решил сам заняться разработкой этой проблемы. Данный цикл статей посвящается описанию моих попыток разрешения этого противоречия.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Итак, наиболее удобным контейнером для методов класса является, конечно, его прототип (в нашем случае - &lt;code&gt;A.prototype&lt;/code&gt;). На него ссылаются все объекты данного класса и, если вызываемый метод не находится в них, то следует вызов метода из их прототипа. Так что задача состоит в том, что бы позволить методам прототипа работать с закрытыми полями объектов, из которых эти методы вызываются.&lt;br /&gt;&lt;br /&gt;Объект-прототип является чужим для того замыкания, в котором находятся поля. Он связан с ним неявной ссылкой (в FF она носит название "&lt;code&gt;__proto__&lt;/code&gt;"), но она не поможет ему считать закрытые переменные. Так что же делать?&lt;br /&gt;&lt;br /&gt;С другой стороны, объекту, который не содержит методов для работы со своими закрытыми полями, иметь эти поля и необязательно. Что он будет с ними делать, кроме как передавать и извлекать их из прототипа, если всё равно все методы для работы с ними находятся в прототипе? Ничего. Т.е. на самом деле они ему не нужны - ему нужен только персональный механизм, позволяющий загружать свой набор закрытых полей в прототип и затем сохранять их из прототипа.&lt;br /&gt;&lt;br /&gt;Поэтому решение может состоять в том, что бы создать для каждого объекта замыкание, которое будет иметь доступ к переменным в прототипе и обладать собственными (дублирующими их) свойствами и сможет осуществлять подмену. Что бы это замыкание имело доступ к переменным замыкания прототипа, оно должно быть внутренним по отношению к нему, а объекты класса могут просто иметь на него ссылку.&lt;br /&gt;&lt;br /&gt;Тогда какими должны быть требования к этому вспомогательному замыканию? Оно должно уметь делать две вещи:&lt;ul&gt;&lt;li&gt;Устанавливать в закрытые переменные прототипа значения закрытых переменных объекта;&lt;/li&gt;&lt;li&gt;Записывать в закрытые переменные объекта значения закрытых переменных прототипа (после того, как прототип произвёл с ними некоторые действия).&lt;/li&gt;&lt;/ul&gt;Т.к. одного действия нам не достаточно, это не может быть функция - это должен быть объект с двумя методами:&lt;ul&gt;&lt;li&gt;метод "get" выполняет взятие значений переменных из объекта, при этом самим переменным объекта неплохо было бы присвоить значение null, что бы не иметь дублирующихся ссылок;&lt;/li&gt;&lt;li&gt;метод "set" устанавливает значения обратно в объект, об-null-яя аналогичные переменные в прототипе.&lt;/li&gt;&lt;/ul&gt;Сам этот объект назовём "privateState", поскольку он несёт закрытую часть состояния объекта.&lt;br /&gt;&lt;br /&gt;Итак, что нам в итоге нужно:&lt;ul&gt;&lt;li&gt;метод для генерации внутреннего замыкания в прототипе объекта, который будет возвращать объект с двумя методами: get и set, которые, соответственно, будут устанавливать в прототип значения закрытых полей из этого объекта и в объект - из прототипа;&lt;/li&gt;&lt;li&gt;механизм вызова для методов прототипа, гарантирующий, что перед выполнением будут установлены значения закрытых полей.&lt;/li&gt;&lt;/ul&gt;Можно было бы, конечно, просто сделать внутри прототипа массив, который содержал бы privateState-объекты, а каждый объект просто содержал бы некоторый идентификатор, по которому прототип узнавал бы, с каким множеством значений ему нужно в данный момент работать. Но в таком случае мы не сможем рассчитывать на сборку мусора этих полей - т.к. ссылка содержится в прототипе, а в объекте - лишь идентификатор, то при уничтожении объекта у нас не будет возможности узнать о том, что следует удалить и принадлежащее ему множество закрытых полей (его privateState-объект) - и они останутся в памяти при том, что использоваться не будут. В итоге мы получим проблему с утечкой памяти. Поэтому единственная ссылка на privateState-объект должна быть у объекта, которому она принадлежит, прототип ссылаться на неё не должен.&lt;br /&gt;&lt;br /&gt;Итак, метод генерации privateState-объекта внутри прототипа может выглядеть примерно так:&lt;pre&gt;&lt;code class="javascript"&gt;// Переменная прототипа, в которую будем помещать значение private-поля&lt;br /&gt;// объекта каждый раз, когда будем выполнять какой-либо его метод.&lt;br /&gt;var x;&lt;br /&gt;&lt;br /&gt;// Функция, являющаяся конструктором provateState-объекта&lt;br /&gt;this.createPrivateState = function() {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; //"_x" - переменная для хранения private-поля объекта&lt;br /&gt;&amp;nbsp; &amp;nbsp; var _x;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; return {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; get: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //Присвоение переменной прототипа значения private-поля объекта&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = _x;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Об-null-ение переменной объекта на время, пока её значение&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // находится в прототипе, во избежание дублирующейся информации&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _x = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; },&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; set: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Возвращение значения private-полю объекта после использования&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _x = x;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //Об-null-ение переменной прототипа&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;При этом нам нужно так же продумать механизм вызова методов. В наиболее простейшем варианте мы могли бы сделать это так:&lt;pre&gt;&lt;code class="javascript"&gt;this.method1 = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState.get();&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; //здесь располагается код метода, работающего с private-полями...&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState.set();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Тогда в конструкторе объекта мы могли бы просто вызывать метод createPrivateState() прототипа:&lt;pre&gt;&lt;code class="javascript"&gt;function A() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.privateState = this.createPrivateState();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;A.prototype = new function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var x;&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.createPrivateState = function(){...} //Описан выше&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.method1 = function() {...} //Описан выше&lt;br /&gt;&amp;nbsp; &amp;nbsp; //Другие методы...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;A._className = "A"; //Записываем в специальное свойство класса его имя&lt;br /&gt;A.prototype.constructor = A; //Присваиваем прототипу ссылку на конструктор&lt;/code&gt;&lt;/pre&gt;Однако для промышленного применения этот метод не годится по ряду причин. Во-первых, мы не учли ситуации, когда один метод вызывает другой метод того же объекта (что делается довольно часто), во-вторых мы не учли, что в методе может происходить вызов метода другого объекта этого же класса. И, наконец, механизм вызова, состоящий из двух строчек вначале и в конце объявления метода выглядит излишне-громоздким. Тому, как наиболее элегантно справиться с этими и более мелкими проблемами, будут посвящены следующие статьи цикла "Особенности инкапсуляции на основе замыканий". :)&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Другие части&lt;/em&gt;:&lt;br /&gt;&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-2.html"&gt;Часть 2: Механизм вызова методов&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-3.html"&gt;Часть 3: Внутренние вызовы&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://se-la-vy.blogspot.com/2009/04/javascript-4.html"&gt;Часть 4: Наследование&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-4307835950681505391?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/4307835950681505391/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=4307835950681505391' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4307835950681505391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4307835950681505391'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/04/javascript-1.html' title='JavaScript: особенности инкапсуляции на основе замыканий. Часть 1: Основа Базовой модели'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-4554227828727257908</id><published>2009-04-19T08:00:00.000-07:00</published><updated>2010-01-18T08:33:46.957-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Дополнения JavaScript'/><title type='text'>JavaScript: Дополняем объект Date</title><content type='html'>На мой взгляд, объекту Date в JavaScript не хватает небольшого количества констант для более удобной работы с ним. Вот как можно все их создать для него:&lt;pre&gt;&lt;code class="javascript"&gt;// Adding consts for comfortable work with Date constructor.&lt;br /&gt;/** @type {number}*/ Date.MS_PER_YEAR = 365 * (&lt;br /&gt;&amp;nbsp; &amp;nbsp; /** @type {number} */ Date.MS_PER_DAY = 24 * (&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type {number} */ Date.MS_PER_HOUR = 60 * (&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type {number} */ Date.MS_PER_MINUTE = 60 * (&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /** @type {number} */ Date.MS_PER_SECOND = 1000&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;br /&gt;&amp;nbsp; &amp;nbsp; )&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;/** @type {number} */ Date.MS_PER_LEAP_YEAR =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Date.MS_PER_YEAR + Date.MS_PER_DAY;&lt;/code&gt;&lt;/pre&gt;LEAP YEAR - это високосный год.&lt;br /&gt;Т.к. с датой чаще всего бывает удобнее всего работать как с числом, возвращаемым методом &lt;code&gt;getTime()&lt;/code&gt; и передавать число в конструкторе, то эти константы часто помогают производить более сложные операции.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Update №1 (18.01.2010)&lt;/span&gt;:&lt;br /&gt;Учитывая, что дату и время в объекте Date можно менять, периодически возникает задача вернуть объект Date, на который ориентируется внутренняя структура какого-либо объекта и который в нём инкапсулирован (при помощи замыканий) и изменения в котором могут негативно отразиться на логике его работы. Для таких случаев в Java предназначен метод clone. Он возвращает точную копию объекта. Вот какой могла бы быть его реализация:&lt;br /&gt;&lt;pre&gt;&lt;code class="javascript"&gt;Date.prototype.clone = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return new Date(this.getTime());&lt;br /&gt;};&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-4554227828727257908?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/4554227828727257908/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=4554227828727257908' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4554227828727257908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4554227828727257908'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/04/javascript-date.html' title='JavaScript: Дополняем объект Date'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-3421434676519250781</id><published>2009-04-19T02:16:00.001-07:00</published><updated>2009-04-19T03:29:24.747-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Тонкости JavaScript'/><title type='text'>JavaScript: небольшое отличие function от function</title><content type='html'>Некоторое время назад я писал на &lt;a href="http://forum.vingrad.ru/"&gt;форуме&lt;/a&gt;, что не вижу разницы между следующими конструкциями:&lt;pre&gt;&lt;code class="javascript"&gt;function fnName() {&lt;br /&gt;   //...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;, и&lt;pre&gt;&lt;code class="javascript"&gt;var fnName = function () {...}&lt;/code&gt;&lt;/pre&gt;, но на самом деле разница есть. Состоит она в том, что в первом случае функция определяется на этапе синтаксического анализа, а во втором - в момент выполнения. Что бы её продемонстрировать, давайте посмотрим на следующий пример:&lt;pre&gt;&lt;code class="javascript"&gt;x(); // Выводит "1", хотя функция ещё вроде бы не определена...&lt;br /&gt;var x = 2;&lt;br /&gt;alert(typeof x); // Выводит "number"&lt;br /&gt;// Следующая строка выполнилась ещё до того, как выполнилась первая строка данного&lt;br /&gt;// примера, так что сейчас она ничего не меняет.&lt;br /&gt;function x(){alert(1);}&lt;br /&gt;alert(typeof x); // Опять выводит "number"&lt;br /&gt;var x = function(){alert(3);}&lt;br /&gt;x(); // Выводит "3". Функция переопределена.&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-3421434676519250781?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/3421434676519250781/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=3421434676519250781' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/3421434676519250781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/3421434676519250781'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/04/javascript-function-function.html' title='JavaScript: небольшое отличие function от function'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-2999100265261972237</id><published>2009-01-05T16:09:00.000-08:00</published><updated>2009-04-19T09:17:33.082-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>JavaScript: используйте arguments.callee вместо названия функции</title><content type='html'>Часто разработчики, когда им нужно изнутри функции сослаться на саму эту функцию, используют её имя, заявленное при её объявлении. Например, так:&lt;pre&gt;&lt;code class="javascript"&gt;var factorial = function(x){&lt;br /&gt;&amp;nbsp; &amp;nbsp; return x == 1 ? 1 : x + factorial(--x);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;В данном коде мы, фактически, обращаемся к внешней цепочке областей видимости и читаем из неё переменную "factorial" и, надеясь, что это - функция, вызываем её.&lt;br /&gt;&lt;br /&gt;Проблема заключается в том, что JavaScript - язык, в высшей степени чреватый конфликтами имён в случае, если над разработкой одного и того же функционала работает не один, а несколько человек. И в этой ситуации нельзя быть до конца уверенным, что переменной "factorial" в какой-то момент кто-нибудь не решит присвоить другую функцию или даже какое-нибудь значение типа числа, при этом функция может быть, скажем, записана в другую переменную и по этой причине разработчик, использующий эту функцию, может думать, что всё впорядке и функция должна работать:&lt;pre&gt;&lt;code class="javascript"&gt;var fac = factorial;&lt;br /&gt;factorial = 27;&lt;br /&gt;alert(fac(factorial));&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Но функция выдаст ошибку:&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;factorial is not a function&lt;br /&gt;factorial(26)test.js (line 2)&lt;/code&gt;&lt;/pre&gt;Просто нужно помнить, что функции в JavaScript - это такой особый тип данных, а не неизменяемая часть программы, как, например, в Java. Функцию можно присваивать любой переменной, свойству объекта или элементу массива, жонглируя ей как угодно. Эта возможность фактически означает, что &lt;span style="font-weight: bold;"&gt;у функций в JavaScript нет своих постоянных имён!&lt;/span&gt;  Поэтому для того, что бы не лишать программистов этой возможности, лучше всегда изнутри функции ссылаться на неё с помощью встроенного и поддерживаемого свойства псевдо-массива Arguments, называемого "callee". Это свойство всегда ссылается на функцию, которая выполняется в данный момент, поэтому вы оказываетесь застрахованы от недоразумений, продемонстрированных выше:&lt;pre&gt;&lt;code class="javascript"&gt;function factorial(x) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return x == 1 ? 1 : x + arguments.callee(--x);&lt;br /&gt;}&lt;br /&gt;var fac = factorial;&lt;br /&gt;factorial = 27;&lt;br /&gt;alert(fac(factorial)); // Возвращает "378".&lt;/code&gt;&lt;/pre&gt;Но в JavaScript всё-таки есть способ элегантно обойти данную ситуацию. Если вам не очень нравится прибегать к вызову свойства callee объекта Arguments, то можно воспользоваться конструкцией, называемой "литералом функции" с необязательным параметром, содержащим имя функции:&lt;pre&gt;&lt;code class="javascript"&gt;var factorial = function f(x) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return x == 1 ? 1 : x + f(--x);&lt;br /&gt;};&lt;br /&gt;alert(f); // Ошибка: "f is not defined"&lt;/code&gt;&lt;/pre&gt;В этом коде не создаётся внешней переменной "f", здесь функция, как и в предыдущем примере, присваивается только внешней переменной "factorial", а переменная "f" определяется только внутри области видимости функции и служит для ссылки на данную функцию, так же, как и "arguments.callee", так что можно без опаски использовать переменную "f" для своих нужд - на работу данной функции это никак не повлияет.&lt;br /&gt;&lt;br /&gt;Минус этого элегантного приёма состоит в том, что такие трюки, к сожалению, увеличивают требования к разработчикам, поддерживающим код. За JavaScript незаслуженно закрепилась репутация простого и чуть ли не детского языка программирования и поэтому когда разработчики сталкиваются с его нетривиальными приёмами, то скорее склонны раздражаться на того разработчика, который их использовал, чем менять давно и прочно сложившийся стереотип и глубже изучать этот интересный язык.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-2999100265261972237?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/2999100265261972237/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=2999100265261972237' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/2999100265261972237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/2999100265261972237'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2009/01/javascript-argumentscallee.html' title='JavaScript: используйте arguments.callee вместо названия функции'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-4856831707391109906</id><published>2008-05-14T04:39:00.000-07:00</published><updated>2009-04-23T17:38:32.364-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Программная обработка исключительных ситуаций в JavaScript</title><content type='html'>&lt;i&gt;Зачем JS-приграммисту может понадобиться обрабатывать ошибки в сценариях? В принципе, не существует задач, при решении которых без механизма исключений никак нельзя было бы обойтись. Однако часто их использование оказывается заметно удобнее бесконечных if`ов - поскольку в коде мы с их помощью изолируем себя от некоторых ситуаций, в которых программа должна вести себя по-другому (главным образом, это может быть связано с неадекватной работой пользователя, например, при вводе данных) - и сосредотачиваемся на описании алгоритма в случае нужных нам условий, а уже потом отдельно разбираться, как говорится, с косяками. Это как в жизни - мы часто отодвигаем проблемы на потом, просто помним, что они у нас есть, и когда будет время - мы их решим, в противовес if`овому подходу, когда мы всё время должны решать проблемы реакции на неадекватные зигзаги сценария не отходя от кассы. По себе могу сказать, что писать сложный код так намного удобней - и более того, по мере усложнения кода использование исключений становится всё более и более необходимым. А если забыть обработать какое-то исключение, заявленное при написании "чистого" кода - браузер об этом скорее всего напомнит при тестировании.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Для тех, кто не знаком с механизмом исключений в Java&lt;/h2&gt;&lt;br /&gt;&lt;i&gt;Механизм исключений в JavaScript очень похож на механизм исключений в Java, так что если вы знакомы с последним, можете спокойно пропустить этот раздел и перейти к чтению следующего - в этом рассматриваются азы, общие для этих двух механизмов.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Исключение - это некий объект, который характеризует состояние системы в момент, когда произошла какая-то исключительная ситуация, т.е. в скрипте либо возникла ошибка, в силу чего стало невозможным его дальнейшее выполнение, либо в скрипте программист явно указал возбудить исключение при некоторых условиях при помощи соответствующей конструкции &lt;code&gt;throw&lt;/code&gt;. По сравнению со старыми языками, где в этой ситуации интерпретатор или скомпилированный код выдавали ошибки, современные языки сделали шаг вперёд, позволив программам самим реагироваать на исключения в себе - это повысило надёжность ПО и увеличило качество кода при написании сложных программ.&lt;br /&gt;&lt;br /&gt;По сути, на уровне кода обработка исключений очень похожа на обработку событий - если сопоставить объекту &lt;code&gt;Error&lt;/code&gt; объект &lt;code&gt;event&lt;/code&gt;, то всё довольно быстро встанет на свои места - если код вызывает ошибку, происходит специфическое событие - исключение - его параметры записываются в специальный объект и он передаётся обработчику, который его обрабатывает.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Для тех, кто знаком с механизмом исключений в Java&lt;/h2&gt;&lt;br /&gt;&lt;i&gt;Здесь я кратко приведу различия между реализацией исключений в Java и JavaScript - пропустите этот раздел если с Java вы не знакомы.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Главное отличие от Java-реализации состоит в  отсутствии (в силу не классовой, а прототипной реализации ООП) возможности использовать множественный оператор &lt;code&gt;catch&lt;/code&gt;, основанный на наследовании и восходящем преобразовании. Здесь оператор &lt;code&gt;catch&lt;/code&gt; может быть только один для каждого &lt;code&gt;try&lt;/code&gt;`я. Этот приём можно менее изящно реализовать в обработчике на основе оператора &lt;code&gt;switch&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Блок &lt;code&gt;finally&lt;/code&gt; присутствует, но здесь он нужен ещё реже. Сам я, работая с JavaScript, ни разу не сталкивался с ситуацией, где бы он мне понадобился или был удобен - поэтому здесь я о нём тихонько умолчу, только намекнув, что он, в принципе, здесь есть...&lt;br /&gt;&lt;br /&gt;Ну и, конечно, нет великолепной возможности (отсутствующей даже в C#) Java, обязывающей указывать возможность вызова исключения функцией при её описании с помощью ключевого слова &lt;code&gt;throws&lt;/code&gt;, отсутствие какового сулит обернуться большой путаницей при использовании больших сторонних библиотек...&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Объект исключения&lt;/h2&gt;&lt;br /&gt;Обычно в качестве объекта исключения выступает объект &lt;code&gt;Error&lt;/code&gt;, хотя, строго говоря, конструкция &lt;code&gt;throw&lt;/code&gt; может передавать абсолютно любой объект (!), который может быть принят оператором &lt;code&gt;catch&lt;/code&gt; и соответственно обработан.&lt;br /&gt;&lt;br /&gt;К сожалению, и область обработки исключительных ситуаций задела браузерная война - боевые действия в этой области привели к тому, что объекты &lt;code&gt;Error&lt;/code&gt; в ECMAScript и в JScript практически не имеют ничего общего.&lt;br /&gt;&lt;br /&gt;Какую информацию о состоянии может в себе нести исключение? По большому счёту всего 2 поля - некое название, характеризующие тип исключения, и сообщение, уточняющее особенности конкретного исключения в данном случае - если пользователь возбуждает исключение сам, то он придумывает сообщение сам, если его генерирует система - то уже она решает, какое сообщение писать. Прибавим к этим 2-м свойствам 3-е - &lt;code&gt;prototype&lt;/code&gt;, для возможности изменять его характеристики ОО-средствами и 4-е стандартное &lt;code&gt;toString()&lt;/code&gt; - и получим объект исключения от ECMAScript, в котором название называется "&lt;code&gt;name&lt;/code&gt;" (в случае пользовательских объектов оно содержит строку "&lt;code&gt;Error&lt;/code&gt;", а в случае ошибки браузера - название из небольшого списка, представленного в следующей &lt;a href="http://wdh.suncloud.ru/js12.htm#table39" target="_blank"&gt;таблице&lt;/a&gt;), а строка сообщения - свойство "&lt;code&gt;message&lt;/code&gt;". Конструктор исключения от ECMAScript выглядит так:&lt;pre&gt;&lt;code class="javascript"&gt;new Error(message);&lt;/code&gt;&lt;/pre&gt;У Microsoft вместо имени у ошибки появляется составной номер (свойство "&lt;code&gt;number&lt;/code&gt;"), состоящий из кода источника ошибки (facility code) и номера самой ошибки. Что бы выделить номер источника, можно воспользоваться выражением &lt;pre&gt;&lt;code class="javascript"&gt;(e.number &gt;&gt; 16) &amp; 0x1FFF&lt;/code&gt;&lt;/pre&gt;, а что бы выделить код самой ошибки - выражением&lt;pre&gt;&lt;code class="javascript"&gt;e.number &amp; 0xFFFF&lt;/code&gt;&lt;/pre&gt;, где e - объект &lt;code&gt;Error&lt;/code&gt;), и "&lt;code&gt;description&lt;/code&gt;", полностью аналогичное свойству "&lt;code&gt;message&lt;/code&gt;" в ECMAScript (начиная с IE 5.5 значение свойства "&lt;b&gt;description&lt;/b&gt;" доступно и через свойство "&lt;code&gt;message&lt;/code&gt;", т.е. по существу, они - синонимы, ссылающиеся на одно и то же значение). Соответствие номеров описаниям для JScript можно найти в &lt;a href="http://wdh.suncloud.ru/jserrors.htm" target="_blank"&gt;этой таблице&lt;/a&gt;.&lt;br /&gt;Конструктор объекта исключения от Microsoft IE выглядит так:&lt;pre&gt;&lt;code class="javascript"&gt;new Error(errorNumber, message);&lt;/code&gt;&lt;/pre&gt;- этот формат не совместим с приведённым ранее форматом, соответственно, к сожалению, для различных браузеров придётся писать разные возбуждения исключений...&lt;br /&gt;&lt;br /&gt;Впрочем, популярный особенно в Linux-среде open-source`ный браузер Mozilla так же нередко расширяет стандарты (хотя существующие - поддерживает, в отличае от IE). В объекте &lt;code&gt;Error&lt;/code&gt; для этого браузера добавлено 2 интересных свойства для локализации исключения - "&lt;code&gt;fileName&lt;/code&gt;" и "&lt;code&gt;lineNumber&lt;/code&gt;", хранящие, соответственно, информацию о файле, где произошла ошибка (актуально, когда в проекте много *.js - файлов и непонятно в котором из них ошибка) и о номере строки в этом файле.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Пример использования&lt;/h2&gt;&lt;br /&gt;Теперь давайте перейдём к практике. Для отлова исключений используется блок &lt;code&gt;try..catch&lt;/code&gt;, выглядит это примерно так:&lt;pre&gt;&lt;code class="javascript"&gt;try {&lt;br /&gt;&amp;nbsp; &amp;nbsp; оператор1&lt;br /&gt;} catch (исключение) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; оператор2&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;, где: &lt;code&gt;оператор1&lt;/code&gt; - оператор или их группа, где может быть вызвано исключение, &lt;code&gt;оператор2&lt;/code&gt; - обработчик исключения, &lt;i&gt;исключение&lt;/i&gt; - обычно объект &lt;code&gt;Error&lt;/code&gt;, характеризующий исключение и передаваемый &lt;code&gt;оператору2&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Отметим, что &lt;code&gt;оператор1&lt;/code&gt; может включать вызовы функций - если в них возникнут исключения и там не окажется более глубоко вложенных блоков &lt;code&gt;try..catch&lt;/code&gt;, внутри которых оно будет вызвано, то их поймает именно этот &lt;code&gt;catch&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Теперь о самостоятельном вызове с помощью оператора &lt;code&gt;throw&lt;/code&gt;. Как и в других темах, существует 2 способа писать универсальный код:&lt;ul&gt;&lt;li&gt;Различать браузеры и писать разный код для разных браузеров, и&lt;/li&gt;&lt;li&gt;Писать более простой код, который относится к области пересечения браузеров.&lt;/li&gt;&lt;/ul&gt;Я нахожу более мудрым второй способ и по возможности стараюсь использовать его. В данном случае это означает, что лучше отказаться от объекта &lt;code&gt;Error&lt;/code&gt; из-за разного синтаксиса конструкторов и присваивать исключению простую строку:&lt;pre&gt;&lt;code class="javascript"&gt;function getMonthName(month) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; month--; // Переводим month в индекс массива (1=январь, 12=декабрь)&lt;br /&gt;&amp;nbsp; &amp;nbsp; var months=["январь","февраль","март","апрель","май","июнь","июль",&lt;br /&gt;"август","сентябрь","октябрь","ноябрь","декабрь"];&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (months[month] != null)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return months[month];&lt;br /&gt;&amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw "Неверный месяц";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;try {&lt;br /&gt;&amp;nbsp; &amp;nbsp; monthName = getMonthName(myMonth); // возможно исключение&lt;br /&gt;} catch (e) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; monthName="неизвестно";&lt;br /&gt;}&lt;br /&gt;document.write(monthName);&lt;/code&gt;&lt;/pre&gt;Ссылки по теме для более глубокого изучения:&lt;br /&gt;1) &lt;a href="http://wdh.suncloud.ru/js06.htm#ref367" target="_blank"&gt;Ю.С. Лукач, "Справочник Web-разработчика", раздел "Обработка исключений"&lt;/a&gt;;&lt;br /&gt;2) &lt;a href="http://citforum.ru/internet/javascript/try.shtml" target="_blank"&gt;Степанищев Евгений: Блоки try... catch... finally... в JScript 5. Статья на сайте CitForum.ru&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;P.S. Это одна из ранних моих статей, &lt;a href="http://forum.vingrad.ru/forum/topic-38795.html"&gt;опубликованных на форуме программистов "Vingrad"&lt;/a&gt;. Ознакомиться с комментариями Vingrad`цев можно &lt;a href="http://forum.vingrad.ru/index.php?showtopic=38795&amp;amp;view=findpost&amp;amp;p=294515"&gt;тут&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-4856831707391109906?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/4856831707391109906/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=4856831707391109906' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4856831707391109906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/4856831707391109906'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2008/05/blog-post.html' title='Программная обработка исключительных ситуаций в JavaScript'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-5376319902021478239</id><published>2008-04-21T00:02:00.000-07:00</published><updated>2009-04-23T07:19:37.822-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java patterns'/><title type='text'>Java: ParamsBean - альтернативный механизм вызова методов</title><content type='html'>&lt;div style="text-align: right;"&gt;&lt;span style="font-style: italic;"&gt;1. Каждый может принять решение, располагая достаточной информацией&lt;br /&gt;2. Хороший руководитель принимает решение и при её нехватке&lt;br /&gt;3. Идеальный - действует в абсолютном неведении&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Законы исходных данных Спенсера&lt;br /&gt;из "Законов Мёрфи"&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Считается, что ООП гораздо ближе к реальному миру, к модели его восприятия человеком, нежели предшествующие подходы. Действительно, мы можем создать объект "собака" и реализовать методы "голос", "сидеть", "лежать" и "фас" - и их вызов будет по смыслу отдалённо напоминать поведение человека по отношению к собаке на прогулке. Всё вроде бы логично, да не совсем - чем на самом деле отличается окружающий мир от программы, так это обилием различных факторов. Собака получает команду "фас" не в отрыве от всей остальной реальности, а в условиях окружения множеством различных второстепенных факторов окружающей действительности и в совокупности эти факторы имеют порой гораздо большее влияние на реакцию собаки на нашу команду, чем сама эта команда. Очевидно, с методом объекта Dog этого не будет - он обработает лишь те параметры, которые ему передадут, коими может стать, пожалуй, лишь громкость голоса, измеренная в децибелах. Он может так же учесть параметры внутреннего состояния, инкапсулированные в нём, но не данные окружающей его среды.&lt;br /&gt;&lt;br /&gt;Рассмотрим другой пример. Специалист в какой-то области архитектурно предстаёт перед нами как ресурс, способный справиться с каким-то классом задач (соответствующих данной предметной области). Когда возникает задача, специалист сначала выясняет более узкий тип задачи, затем входит в курс дела, погружаясь в специфику задачи, затем более узко - в специфику конкретной ситуации, в которой возникла проблема, затем ищет похожие случаи в своей практике и решает задачу либо на основе опыта (архитектурно - кэша уже выполненных задач или не выполненных, про которые сделаны выводы) либо, в отсутствии такового - пытается решить творчески.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Среда, в которой действуют объекты реальной действительности, обычно имеет фоновый характер и лишь косвенно влияет на процесс. Она состоит из множества разнообразных факторов. Соответственно, было бы логично передать её некоторым количеством переменных, редко изменяемых и имеющих значения по умолчанию для типичных ситуаций. Однако небольшой, ограниченный ряд свойств среды в контексте данного конкретного действия (работы метода) имеет первостепенную важность - очевидно, что эти-то свойства в меру возможностей мы и должны в первую очередь определять, передавая методу, остальные же, менее существенные можем считать равными значениям поумолчанию, если у нас нет достаточных ресурсов, что бы их уточнять.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.mindviewinc.com/Index.php"&gt;Брюс Эккель&lt;/a&gt; писал в "&lt;a href="http://www.mindview.net/Books/TIJ/"&gt;Философии Java&lt;/a&gt;" о том, что разработчиков удобно разделять на создателей и пользователей библиотек. Архитектурно задача программиста-пользователя - как можно точнее определить внешнюю среду (одним из ключевых факторов которой является само посылаемое объекту сообщение, т.е. вызываемый метод) объекта, которую и передать объекту, а задача программиста-разработчика библиотек - определить внутреннюю структуру объекта, что бы он как можно более адекватно работал в условиях, которые показывает ему программист-пользователь.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Возвращаясь к примеру со специалистом, можно говорить о том, что если сопоставить специалиста классу, то у него есть:&lt;ol&gt;&lt;li&gt;данные (энциклопедического характера и накопленные с опытом),&lt;/li&gt;&lt;li&gt;методы (типы задач, с которыми он способен справиться).&lt;/li&gt;&lt;/ol&gt;Методы описываются предметной областью и квалификацией, что программно соответствует интерфейсу класса (класс может реализовывать несколько таких интерфейсов, как человек может быть специалистом в разных областях). Соответственно, у этих методов есть параметры, количество и качество которых определяется с одной стороны - спецификой задачи, с другой - банально тем, можем ли мы их предоставить в данный конкретный момент или нет (качество решения проблемы, естественно, зависит как от эффективности организации алгоритма, так и от полноты данных, которых в реальной ситуации часто может не хватать - отсюда возможность обоюдного тюнинга программы - составителю библиотеки можно доводить до совершенства алгоритм реализации, а программисту-клиенту - пытаться передать более точные данные объекту, позволяя ему лучше "войти в курс дела").&lt;br /&gt;&lt;br /&gt;Т.е. для моделирования более реальных ситуаций, нам необходимо иметь возможность писать методы, которые имеют не обязательные параметры. Позволяют ли стандартные принятые подходы такое делать? Да, но очень не удобно, не эффективно и не гибко.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Прикладные аспекты описания сигнатур методов или чем нам может помочь подход ParamsBean?&lt;/h2&gt;&lt;br /&gt;Обычно когда мы планируем архитектуру приложения, мы закладываем в сигнатуры методов объектов в библиотеках какие-то параметры, которые играют для выполняемой задачи ключевую роль. Им необходимо их обработать и выдать приемлемый результат. Эти параметры имеют свои типы, имена и жёстко определённый порядок перечисления при вызове метода. Для того, что бы корректно работать с определённым таким образом методом, программист, который будет использовать нашу библиотеку, обязан считаться с этим.&lt;br /&gt;&lt;br /&gt;Порой возникают ситуации, когда один и тот же функционал удобнее сделать доступным с использованием разного количества параметров, некоторые удобнее задать по умолчанию и дать возможность пользователю библиотеки передать лишь те, которые важны для него. Для таких ситуаций принято пользоваться механизмом перегрузки - действительно, очень удобно написать метод с тем же именем, но с меньшим количеством параметров, из которого вызвать исходный, добавив к параметрам другие со значениями по умолчанию.&lt;br /&gt;&lt;br /&gt;Однако у этого метода есть ряд существенных недостатков, главным образом - он, очевидно, обладает очень низкой масштабируемостью. Он привносит элементы т.н. жесткого связывания обоих этих программистов по Эккелю (автора библиотек и клиента), а хорошо бы их разнести, что бы они не были вынуждены плотно общаться - особенно это может оказаться важно в больших проектах. Делать же перегруженные методы на все случаи жизни, очевидно, не разумно - количество возможных комбинаций растёт в геометрической прогрессии пропорционально количеству параметров.&lt;br /&gt;&lt;br /&gt;Как-то была у меня такая задача - надо было вытащить данные из базы и передать их в функцию, в которой очень-очень много параметров (около 30-ти). Реально нужны из них только 5, остальные - значения по умолчанию, но было известно, что этот метод понадобится ещё для реализации большого числа других функций и так же понадобится модифицировать некоторые из параметров вызова, какие - заранее сказать было сложно. Создавать конструктор с 30ю параметрами было не просто очень неудобно - было нужно иметь возможность задать некоторые параметры, а остальные что бы остались такими, какими должны быть по умолчанию.&lt;br /&gt;&lt;br /&gt;Создавать конструкторы для всех возможных комбинаций, естественно, я не стал.&lt;br /&gt;&lt;br /&gt;Я создал JavaBean (точнее именно POJO - обрезанный лишь getter`ами и settter`ами JavaBean), в котором инкапсулировались все параметры по умолчанию, и его через промежуточную функцию стал передавать той самой с огромным числом параметров.&lt;br /&gt;&lt;br /&gt;Вроде бы всё хорошо, но не очень красиво выглядело наполнение параметрами этого бина, к тому же хотелось все параметры всё-таки аккуратно вложить в вызов функции - так оно как-то логичней было бы.&lt;br /&gt;&lt;br /&gt;Есть множество аспектов языков, о которых не все знают, поскольку на них не принято акцентировать внимание. Так, недавно я открыл для себя, что оператор вызова метода - "." - не обязательно должен сразу следовать за тем объектом, за которым он вызывается, как это обычно принято делать, а может вызываться и на другой строке - главное, что бы метод был рядом, т.е., кто не знает, конструкция:&lt;pre&gt;&lt;code class="java"&gt;Obj1 theObj1 = new Obj1();&lt;br /&gt;&lt;br /&gt;theObj1&lt;br /&gt;&amp;nbsp; &amp;nbsp; .setProp1(1)&lt;br /&gt;&amp;nbsp; &amp;nbsp; .setProp2(2);&lt;/code&gt;&lt;/pre&gt;- вполне valid`ная. Такая конструкция навешивания изменений свойств была бы идеальна для моей задачи и очень лаконична - если бы я при вызове метода создавал объект и передавал ему те параметры, которые отличаются от параметров по умолчанию, все же остальные оставались бы со значениями по умолчанию.&lt;br /&gt;&lt;br /&gt;И вот я подумал - а почему принято setter`ы делать без возвращаемого значения, т.е. "&lt;code&gt;void&lt;/code&gt;"? А может быть, стоит заставить их выдавать ссылку на объект своего класса, в конце прописывая выражение "&lt;code&gt;return this;&lt;/code&gt;"? Тогда приведённая выше конструкция будет вполне реальной!&lt;br /&gt;&lt;br /&gt;Сделал и всё отлично получилось! :-))) Выполнил задачу и остался очень собой доволен.&lt;pre&gt;&lt;code class="java"&gt;class A implements Serializable {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; private int forumID = 1;&lt;br /&gt;&amp;nbsp; &amp;nbsp; public int getForumID(){ return forumID; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; public A setForumID(int forumID){ this.forumID = forumID; return this; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; private int memberID = 8;&lt;br /&gt;&amp;nbsp; &amp;nbsp; public int getMemberID(){ return memberID; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; public A setMemberID(int memberID){ this.memberID = memberID; return this; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; private String postBody = "";&lt;br /&gt;&amp;nbsp; &amp;nbsp; public String getPostBody(){ return postBody; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; public A setPostBody(String postBody){ this.postBody = postBody; return this; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; // и т.д.&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class B {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; public static void main(String[] args){&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ClassToGo.go( new A()); //Можем вызывать со всеми параметрами по-умолчанию&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Можем задавать все параметры&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ClassToGo.go(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; new A()&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .setForumID(5)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .setMemberID(2)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .setPostBody("ляляля")&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Можем выборочно&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ClassToGo.go(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; new A()&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .setPostBody("боди поста")&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Пока назвал я это &lt;span class="searchlite"&gt;ParamsBean&lt;/span&gt; - по-моему, очень удобная штука. :)&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Преимущества&lt;/h2&gt;&lt;br /&gt;Теперь я могу каждый раз при вызове менять значения только тех параметров, которые мне нужно изменить по сравнению с параметрами по умолчанию. Кроме того, я всегда знаю их имена и не путаю их, ведь я вызываю специальные методы, содержащие их имена. Так же теперь я жестко не привязан к порядку их следования при вызове метода - порядок отныне произволен. Могу теперь и пользоваться преимуществами модели JavaBeans для параметров метода, т.е. вводить ограничения на данные и преобразовывать их для хранения в другой формат, вообще, реализуя причудливую логику, инкапсулировать в бине часть функций по обработке данных - например, можно задать методу, которому нужна в качестве параметра длина, &lt;span class="searchlite"&gt;ParamsBean&lt;/span&gt;, в разных setter`ах которого можно задавать длину в метрах, милях, футах или дюймах, а он уж с методом сам как-нибудь разберётся - это уже вешается на составителя библиотеки, а не на клиента.&lt;br /&gt;&lt;br /&gt;Кроме того, в сеттерах-геттерах можно инкапсулировать логику прямого или косвенного влияния одних факторов на другие, таким образом, пользователь такого бина получает возможность задавать одним методом несколько факторов.&lt;br /&gt;&lt;br /&gt;Это возможность распараллелить работу программистов, выполняющих разные роли по Эккелю. Например, они договорились о каком-то методе, которому в качестве параметров передаётся объект "pBean", метод реализует какую-то функциональность. Потом понадобилось изменить метод, передавая ему дополнительные параметры, который в зависимости от них будет менять поведение - для того, что бы старый код работал, просто в этот pBean добавляется новое поле и оно передаётся методу - не надо перегружать метод. Зато программист-пользователь сможет гибче использовать метод и не заморачиваться с теми параметрами, которые ему не известны, при этом не должен будет клянчить каждый раз у разработчика библиотеки специальный перегруженный метод для его ситуации. Т.е. фактически данный подход - это как бы конструктор всех возможных индивидуальных перегрузок метода.&lt;br /&gt;&lt;br /&gt;По сути, это просто альтернативный привычному нам механизм вызова методов объектов, гораздо более гибкий и более масштабируемый, чем стандартный механизм с перечислимыми параметрами. Если раньше для таких приёмов применялся механизм перегрузки, то c таким подходом он нам уже не нужен - мы сами решаем, какие параметры передавать изменёнными, а какие - оставить со значениями по умолчанию :)&lt;br /&gt;&lt;br /&gt;Для использования такого подхода нужно, что бы параметры по умолчанию обеспечивали хоть какую-то функциональность и служили как бы "заглушками". В ситуации, когда ряд параметров методу быть передан &lt;i&gt;обязан&lt;/i&gt;, можно вызывать специальный &lt;code&gt;Exception&lt;/code&gt;, но лучше не использовать данный подход вовсе. Он был оправдан именно в задачах с передачей большого количества параметров.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;P.S. Это сокращённый вариант &lt;a href="http://forum.vingrad.ru/index.php?showtopic=43890&amp;amp;view=findpost&amp;amp;p=338337"&gt;моей статьи на Vingrad`е&lt;/a&gt;. Можно ознакомиться с коментариями Vingrad`цев &lt;a href="http://forum.vingrad.ru/index.php?showtopic=43890&amp;amp;view=findpost&amp;amp;p=338347"&gt;тут&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-5376319902021478239?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/5376319902021478239/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=5376319902021478239' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/5376319902021478239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/5376319902021478239'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2008/04/paramsbean.html' title='Java: ParamsBean - альтернативный механизм вызова методов'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-1722338375485800114</id><published>2008-03-16T06:36:00.000-07:00</published><updated>2008-03-17T01:51:02.425-07:00</updated><title type='text'>Отсрочка для разработчиков BEA</title><content type='html'>По информации из некоторого внушающего доверие источника, &lt;a href="http://www.oracle.com/"&gt;Oracle&lt;/a&gt; не собирается как-либо вмешиваться в разработку продуктов купленной им недавно компании &lt;a href="http://bea.com/"&gt;BEA Systems&lt;/a&gt; по крайней мере до середины лета.&lt;br /&gt;&lt;br /&gt;Напомню, что &lt;a href="http://top.rbc.ru/economics/16/01/2008/134102.shtml"&gt;данная покупка&lt;/a&gt;, состоявшаяся в начале этого года (и имевшая &lt;a href="http://www.ko.itc.ua/node/34018"&gt;интересную историю&lt;/a&gt;), поставила в первую очередь для контор, ориентирующихся на продукты BEA вопрос о том, будет ли сохранена в неприкосновенности линейка продуктов &lt;a href="http://bea.com/framework.jsp?CNT=index.htm&amp;amp;FP=/content/products/aqualogic/"&gt;BEA Aqualogic&lt;/a&gt;, т.е. решения &lt;a href="http://ru.wikipedia.org/wiki/%D0%A1%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%B0%D1%8F_%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D0%B0"&gt;SOA&lt;/a&gt; и &lt;a href="http://en.wikipedia.org/wiki/Business_Process_Management"&gt;BPM&lt;/a&gt; от BEA.&lt;br /&gt;&lt;br /&gt;Легко предположить, что курицу, несущую золотые яйца, а именно &lt;a href="http://bea.com/framework.jsp?CNT=index.htm&amp;amp;FP=/content/products/weblogic/server/"&gt;J2EE Application Server BEA WebLogic&lt;/a&gt;, Ларри вряд ли будет трогать, ибо его собственный &lt;a href="http://www.oracle.com/appserver/index.html"&gt;Oracle Application J2EE server&lt;/a&gt; имеет заметно меньший фрагмент рынка и особого смысла его развивать, имея теперь уже в арсенале одного из лидеров, на мой взгляд, нет. Так что в этой ситуации Oracle, с сервером БД которого и так преимущественно использовался BEA WebLogic, скорее всего, предпочтёт ограничиться облегчением своим клиентам перехода на него со своего детища и постепенно свести этот проект к поддержке, постепенно сокращая затраты на неё. С WebLogic`ом же Oracle превратится в гиганта J2EE-рынка, который, за счёт консолидации, будет иметь заметно б&lt;span style="font-weight: bold;"&gt;о&lt;/span&gt;льшие шансы в борьбе за пирог рынка корпоративного ПО против &lt;a href="http://www.ibm.com/us/"&gt;IBM&lt;/a&gt;, чем суммарно BEA и Oracle имели раньше.&lt;br /&gt;Дело в том, что один из основных козырей IBM как раз и заключался в том, что они предлагают всё - СУБД (&lt;a href="http://www-306.ibm.com/software/data/db2/9/"&gt;DB2&lt;/a&gt;), сервер приложений (&lt;a href="http://www-306.ibm.com/software/webservers/appserv/wasproductline/"&gt;WebSphere&lt;/a&gt;), инструментарий разработки и моделирования (продукты купленной некоторое время назад &lt;a href="http://www-306.ibm.com/software/rational/"&gt;Ratonal&lt;/a&gt;) и железо, т.е. работая с IBM, вы обеспечивались всем, что вам может понадобиться в этой области, в то время, как sales`ы BEA при всём желании не могли использовать гибкие маркетинговые схемы, например предложить скидку на покупку СУБД Oracle при покупке у них WebLogic`а в то время, как покупка первого действительно чаще всего означала покупку второго. Так что теперь преимущество IBM будет только в железе, софтверную же часть Oracle покроет полностью и получит свободу манёвра, позволяющую почти точно так же "обволакивать" клиентов со всех сторон.&lt;br /&gt;&lt;br /&gt;Однако, что касается решений SOA/BPM, то здесь всё выглядит соврем не так однозначно - рынок ещё только формируется и здесь позиции Aqualogic вполне сравнимы с &lt;a href="http://www-306.ibm.com/software/integration/wps/"&gt;позициями не только традиционного конкурента BEA - IBM&lt;/a&gt;, но и большого количества других производителей, в том числе, что критически важно - с позициями &lt;a href="http://www.oracle.com/technologies/soa/soa-suite.html"&gt;собственной линейки SOA/BPM от Oracle&lt;/a&gt;. Достаточно вспомнить хотя бы то, что BPM-решение от BEA было куплено и до этого являлось разработкой малоизвестной компании &lt;a href="http://fuego.com/"&gt;Fuego&lt;/a&gt; (однако, по утверждению сотрудников BEA, к 6-й версии они полностью с нуля переписали этот продукт). И со стороны Ларри было бы странно и тут отказываться от своих наработок в пользу решений купленной им BEA.&lt;br /&gt;В итоге из трёх принципиальных схем:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Оставить две линейки жить параллельной жизнью, как это, например, &lt;a href="http://www.quest.com/oracle/"&gt;сделала компания Quest Software со своими продуктами для Oracle&lt;/a&gt; (продукты &lt;a href="http://www.quest.com/toad-for-oracle/"&gt;TOAD for Oracle&lt;/a&gt; и &lt;a href="http://www.quest.com/sql-navigator/"&gt;SQLNavigator&lt;/a&gt; являются прямыми конкурентами, тем не менее имеют одного владельца и активно развиваются параллельно).&lt;/li&gt;&lt;li&gt;Отказаться от одной линейки в пользу другой.&lt;/li&gt;&lt;li&gt;Попытаться слить линейки и команды разработчиков и превратить в единый продукт.&lt;/li&gt;&lt;/ol&gt;выбор будет производиться скорее всего из 1-й и 3-ей, и последний пункт не очень приятен для тех, кто ориентировался на решения линейки Aqualogic. Могу сказать, что хотя в ней и хватало глюков и проблем (впрочем, не больше, чем у IBM`а и прочих), но она подавала большие надежды. Насколько я понял, пока чёткого решения на сей счёт ещё не принято.&lt;br /&gt;&lt;br /&gt;От себя могу сказать, что в общем-то не хочу класть все яйца в одну карзину и решил пока подстраховаться, овладев линейкой SOA/BPM от IBM.&lt;br /&gt;&lt;br /&gt;В общем, пока жить можно - живём и ждём середины лета...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-1722338375485800114?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/1722338375485800114/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=1722338375485800114' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/1722338375485800114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/1722338375485800114'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2008/03/bea.html' title='Отсрочка для разработчиков BEA'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-6341007915008710886</id><published>2008-01-16T20:26:00.000-08:00</published><updated>2008-01-16T21:08:00.970-08:00</updated><title type='text'>Sun to buy MySQL for $1 billion</title><content type='html'>Во дела!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://money.cnn.com/news/newsfeeds/articles/newstex/AFX-0013-22297085.htm"&gt;Читаем.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Видимо, Sun действительно решила плотно взяться за рынок небольших приложений (читай - сайтов), раз уж с софтом для крупного бизнеса не заладилось (его практически монополизировали IBM и BEA).&lt;br /&gt;&lt;br /&gt;Однако поражает цена. Интересно, какова прибыль акционеров MySQL от этой сделки?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-6341007915008710886?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/6341007915008710886/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=6341007915008710886' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/6341007915008710886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/6341007915008710886'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2008/01/sun-to-buy-mysql-for-1-billion.html' title='Sun to buy MySQL for $1 billion'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8487749935709371325.post-6754138631848927449</id><published>2007-11-16T03:34:00.000-08:00</published><updated>2007-11-16T03:37:58.413-08:00</updated><title type='text'>Намерение</title><content type='html'>Теперь я решил разделить блоги. Сюда я буду писать вещи, непосредственно относящиеся к моей профессиональной деятельности, а в ЖЖ продолжу писать общие вещи из других сфер своей жизни. В итоге здесь должно получиться что-то типа колонки статей в профессиональном журнале - буду к этому стремиться.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8487749935709371325-6754138631848927449?l=se-la-vy.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://se-la-vy.blogspot.com/feeds/6754138631848927449/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8487749935709371325&amp;postID=6754138631848927449' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/6754138631848927449'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8487749935709371325/posts/default/6754138631848927449'/><link rel='alternate' type='text/html' href='http://se-la-vy.blogspot.com/2007/11/blog-post.html' title='Намерение'/><author><name>C'est la vie</name><uri>http://www.blogger.com/profile/10891622296508327577</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_e9ftZE9b4-4/Sero61dkL4I/AAAAAAAAAIY/09vyAS_I6-U/S220/My+photo+-+Arhangelskoye+(96x95).gif'/></author><thr:total>0</thr:total></entry></feed>
