Пользователь
0,0
рейтинг
29 мая 2011 в 17:31

Разработка → JavaScript F.A.Q: Часть 1

image

Несколько дней назад мы с TheShock создали топик в котором собирали ваши вопросы, касательно JavaScript (архитектура, фрэймворки, проблемы). Настало время ответить на них. Мы получили очень много вопросов, как в комментариях так и по email. Эта первая часть ответов — те вопросы, которые достались мне.

1. Прототипное наследование. Как все работает?

У меня есть проблема с пониманием прототипной модели, привык к «классической» классовой :) но вот решил изучить JS. Просьба, если возможно, напишите статью где в виде паттернов объясните возможные варианты построения «классов», разных уровней видимости методов и переменных. Понимаю что подобные статьи можно найти в огромной кол-ве, понимаю что в JS как-бы «не нужны» уровни видимости.

Ответ получался очень длинным, поэтому я создал отдельный топик: Основы и заблуждения насчет JavaScript

2. Какая модель создания объектов наиболее удобная?

Если с new, то как защищаетесь от ошибок:
1. Функции-конструкторы пишу всегда с большой буквы;
2. Проверяют валидность создания через this instanceof Имя_функции (избегаю this instanceof arguments.callee по соображения производительности)
3. Аналогично второму, но сверяюсь с window, т.к. не хочу хардкодить имя и не пишу скрипты для out-of-browser сред.

Лучше, привычнее и идеологические создавать объекты через new. Конструкторы стоит называть с заглавной буквы.
Я предпочитаю основываться на соглашениях и не проверяю this внутри конструктора — вызвал конструктор без new и поэтому утекло в глобалы — значит «сам дурак». И ни в коем случае не поощряю ошибку с new — некоторые проверяют если this это глобал значит пользователь вызвал конструктор без new и создают внутри конструктора объект и возвращают его — это поощрение ошибки и идеологически не верный подход.
var Obj = function () {
    "use strict";
    this.pew = 100;
};

// Правильно
new Obj.pew++;

// пользователь словит ошибку
Obj(); // TypeError: this is undefined

new не приемлем для factory методов, и коротких конструкторов — jQuery

Резюмирую кодом:
// Хорошо: меньше кода, не поощряется ошибка, use strict
var Obj = function () {
    "use strict";
    this.pew = 100;
};

// Не очень хорошо: лишний и совершенно ненужный код
var Obj = function () {
    if (!(this instanceof Obj)) {
        return new Obj();
    }
    this.pew = 100;
};

3. Как определить какая кнопка мыши нажата на JS?


Событие mousedown mouseup вызывают все кнопки мыши, click — только левая. В обработчике события необходимо проверить код кнопки event.button, чтобы узнать какая была нажата:
(0 — Левая, 1 — Средняя, 2 — Правая). В ИЕ8- все не так, см. код:
var button = document.getElementById('button'),
              //  0       1         2
    buttonMap = ['Left', 'Middle', 'Right'],
    handler = function (event) {
        event = event || window.event;
        alert(buttonMap[event.button] + ' id: ' + event.button);
    };

if (button.addEventListener) {
     button.addEventListener('mousedown',  handler, false);     
} else {
     // IE         0      1       2        3      4
     buttonMap = ['???', 'Left', 'Right', '???', 'Middle'];
     button.attachEvent('onmousedown',  handler);
}

jQuery чинит этот недостаток ИЕ, в нем стоит проверять event.which вместо магии с event.button
$('button').mousedown(function (event) {
    alert(['Left', 'Middle', 'Right'][event.which]);
});

Пример: jsfiddle.net/azproduction/W2XgH
Подробнее: www.quirksmode.org/js/events_properties.html Параграф «Which mouse button has been clicked?»
jQuery event.which: api.jquery.com/event.which

4. Можно ли перехватывать события нажатия клавиш клавиатуры?

Можно ли перехватывать события нажатия клавиш клавиатуры (стрелка вниз, вверх) в javascript, так чтобы браузер после этого не выполнял прокрутку окна? Есть ли особенности среди браузеров в таком поведении, если оно вообще возможно? Например есть таблица, которая не влазит на экран целиком, при этом перемещение по строкам реализовано с помощью стрелок клавиатуры. Нужно, чтобы браузер не пролистывал при этом страницу.

Для этого необходимо отменить так называемое действие по умолчанию(Default Action): стрелка вниз и колесо мыши скроллит окно, правая кнопка мыши поднимает контекстное меню, по клику на sumbit выполняется form.submit(), при клике на input он получит фокус, при клике на анкор браузер перейдет по ссылке и т.д.

Используя jQuery это можно сделать так:
// ИЕ и сафари не отслеживает стрелки на keypress, а Опера глючит на keyup
$(window).bind($.browser.opera ? 'keypress' : 'keyup', function (event) {
    event.preventDefault();
    // или
    return false;
});

Тут есть важный момент. Необходимо выполнить preventDefault() до того, как выполнится defaultAction. Например при клике на input мы не хотим передавать ему фокус, тогда надо повесить обработчик на событие в цепочке до выполнения defaultAction — mousedown.
$('input').bind('mousedown', function (event) {
    event.preventDefault();
    // или
    return false;
});

Сама цепочка событий такая:
1. mousedown
2. focus (перед фокусом сработает blur на другом объекте)
3. mouseup
4. click
Если мы повесим событие на focus и ниже, то ничего не получится т.к. defaultAction отработает после mousedown.

5. Как решить проблему остановки gif-анимации при нажатии ESC, если эта клавиша забиндована?


Смотри ответ выше. Некоторые браузеры при нажатии на Esc останавливают gif анимацию, прекращают загрузку страницы — это их действие по умолчанию.
Необходимо отменить действие по умолчанию event.preventDefault():
$(document).bind($.browser.webkit ? 'keydown' : 'keypress', function (event) { 
    // Нажали Esc
    if ((event.which || event.keyCode) === 27) {
        event.preventDefault();
        // или
        return false;
    }
});

6. А что такое оператор (), с помощью которого замыкание создали?


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

Если сделать вот так:
function () {
  // source
}()

В этом случае мы получаем SyntaxError из-за отсутствия имени функции (функция-декларация всегда должна иметь имя).

Если добавить имя:
function foo() {
  // source
}()

Во втором случае имя задано (foo), и по идее, декларация функции должна пройти нормально. Однако, мы всё равно имеем ошибку синтаксиса, но уже, касаемо оператора группировки без выражения внутри. Обратите внимание, в данном случае — это именно оператор группировки, который следует за декларацией функции, а не скобки вызова функции!

Существуют и другие способы предотвратить ParseError — поставить функцию в стостяние выражения т.е. показать парсеру, что это именно Function Expression:
От TheShock:
!function () {
  // source
}();

+function () {
  // source
}();

[function() {
  // source
}()];

var a = function () {
  // source
}();

Она используется в т.ч. в jQuery. Позволяет выделить весь код в один блок с локальной областью видимости. Это ускоряет доступ к внутренним переменным, позволяет на захламлять глобальное пространство имён и, главное, лучше сжимается минификаторами. habrahabr.ru/blogs/jquery/118564


По материалам dsCode Тонкости ECMA-262-3. Часть 5. Функции. — Вопрос «о скобках»
Ещё почитать: kangax.github.com/nfe

7. Пересылка кода в XHR


На действия пользователя в ajax-системе сервер присылает ответ «alert('Бум!!!');». На клиенте полученный ответ прогоняется через eval() и выполняется. Как называется такая передача данных? Это не JSON, не XML, не HTML.

Названия этому нет. На самом деле это очень плохой подход, такой же плохой как хранить код PHP в БД и каждый раз eval'ить его. Кроме условной не секурности такая архитектура несет за собой сильную связанность, и в будущем что-то переделать будет сложно. Получается месиво: данные + кода + представление, в такой модели чтобы что-то переделать нам придется распутать этот клубок, внести изменения, учитывая многочисленные связи, запутать обратно. Я уже не говорю о том, чтобы оторвать кусок функционала из такого месива…
Для упрощения поддержки кода необходимо максимально отделить части системы и уменьшить количество связей (зависимостей). Для получения слабой связанности(когда кусок приложения можно максимально безболезненно оторвать или заменить) вводят события и, например, архитектуру приложения MVC.

Почитать:
И опять про MVC
Применение Event-driven модели в веб-приложении
Написание сложных интерфейсов с Backbone.js

8. Как корректно организовать очередь выполнения команд с задержкой без подвисания всего скрипта?


JavaScript имеет один поток, в котором выполняется сам код, перерисовывается DOM дерево, работают таймеры. Каждый раз когда вы выполняете цепочку операций (циклы, тяжелые функции) блокируется взаимодействие пользователя с интерфейсом (если цепочка не тяжелая, то пользователь не замечает изменений). Чтобы предотвратить блокировку UI Threed ввели Web Workers — треды в JavaScript.
Если использование воркеров невозможно, то необходимо провести оптимизацию циклов и тяжелых функций. Как пишет Nicholas C. Zakas в своей книге OReilly High Performance JavaScript: пользователь не заметит лагов если UI Threed будет блокироваться на 100 мсек и меньше. Т.е. нам можно вычислять 100 мсек, потом стоит разблокировать UI Threed, чтобы пользователь не заметил лагов.

Вот оригинальный код, оптимизированный под все процессоры, из его книги:
function timedProcessArray(items, process, callback) {
    var todo = items.concat();   //create a clone of the original
    setTimeout(function () {
        var start = +new Date();
        do {
            process(todo.shift());
        } while (todo.length > 0 && (+new Date() - start < 50));
        if (todo.length > 0){
            setTimeout(arguments.callee, 25);
        } else {
            callback(items);
        }
    }, 25);
}

function saveDocument(id) {
    var tasks = [openDocument, writeText, closeDocument, updateUI];
    timedProcessArray(tasks, [id], function(){
        alert("Save completed!");
    });
}

Функция timedProcessArray блокирует UI Threed на 25 мсек, выполняя цепочку действий, затем освобождает UI Threed на 25 мсек и так далее

Почитать:
Nicholas C. Zakas — OReilly High Performance JavaScript
Коммикс Web Workers
Вычисляем при помощи Web Workers
WXHR: старый добрый XHR со вкусом

9. Можно ли как-то узнать, что юзер закончил ресайзить окно?


Такого события нет, но можно узнать не ресайзил ли пользователь окно сколько-то времени, что примерно соответствует onresizeend

Набросок кода:
var time = 0,
    timerId,
    TIME_ADMISSION = 100; // 0.1s

function onresizeend () {
    console.log('onresizeend');
};

function resizeWatcher () {
    if (+new Date - time >= TIME_ADMISSION) {
        onresizeend();
        if (timerId) {
            window.clearInterval(timerId);
            timerId = null;
        }
    }
};

$(window).resize(function () {
    if (!timerId) {
        timerId = window.setInterval(resizeWatcher, 25);
    }
    time = +new Date;
});

Живой пример: jsfiddle.net/azproduction/2Yt6T

10. Как при помощи window.open() открыть именно новое окно, а не вкладку?


Это поведение зависит от конкретного браузера. Опера всегда открывает вкладку (но она представляется в виде окна), Сафари всегда открывают окно (поведение Сафари можно обойти). Хром, ФФ и ИЕ управляемы.

Если передать дополнительные параметры — позицию окна, то откроется новое окно:
window.open('http://www.google.com', '_blank', 'toolbar=0,location=0,menubar=0');

Если ничего не передать, то откроется таб:
window.open('http://www.google.com');

Чаще требуется отрыть новый таб, тут может быть проблема с сафари: по умолчанию (зависит от настроек) сафари при любом вызове функции window.open открывает новое окно. Но при клике на ссылку с нажатыми Ctrl+Shift/Meta+Shift всегда открывает новый таб (независимо от настроек). Для открытия нового таба будем эмулировать событие «click» с нажатыми Ctrl+Shift/Meta+Shift:
function safariOpenWindowInNewTab(href) {
    var event = document.createEvent('MouseEvents'),
        mac = (navigator.userAgent.indexOf('Macintosh') >= 0);

    // выполняем Ctrl+Shift+LeftClick/Meta+Shift+LeftClick (фокус)
    // создаем собственное событие
    event.initMouseEvent(
        /* type */          "click",
        /* canBubble */     true,
        /* cancelable */    true,
        /* view */          window,
        /* detail */        0,
        /* screenX, screenY, clientX, clientY */ 0, 0, 0, 0,
        /* ctrlKey */       !mac,
        /* altKey */        false,
        /* shiftKey */      true,
        /* metaKey */       mac,
        /* button */        0,
        /* relatedTarget */ null
    );

    // создаем ссылку в памяти и кликеам этм событием по ссылке - откроется новый таб
    $('<a/>', {'href': href, 'target': '_blank'})[0].dispatchEvent(event);
}

11. Как эффективно сделать глубокое копирование (deep copy)?


Если oldObject не будет меняться, то эффективнее будет склонировать через прототип (будет ну очень быстро):
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

var newObject = object(oldObject);

Если вам нужно честное клонирование, то быстрее будет рекурсивно пройтись по дереву объекта + сделать некоторые оптимизации (это пока самый быстрый алгоритм честного клонирования):
var cloner = {
    _clone: function _clone(obj) {
        if (obj instanceof Array) {
            var out = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                var value = obj[i];
                out[i] = (value !== null && typeof value === "object") ? _clone(value) : value;
            }
        } else {
            var out = {};
            for (var key in obj) {
                if (obj.hasOwnProperty(key)) {
                    var value = obj[key];
                    out[key] = (value !== null && typeof value === "object") ? _clone(value) : value;
                }
            }
        }
        return out;
    },

    clone: function(it) {
        return this._clone({
        it: it
        }).it;
    }
};

var newObject = cloner.clone(oldObject);


Для jQuery можно использовать следующее:
// Мелкое копирование
var newObject = jQuery.extend({}, oldObject);

// Глубокое копирование
var newObject = jQuery.extend(true, {}, oldObject);


Почитать:
Бенчмарк методов честного клонирования
Очень длинное обсуждение сабжа на stackoverflow

12. Как сделать аналог деструктора/финализатора? И вообщем, как управлять временем жизни объектов?


В JavaScript объект будет удален когда исчезнет последняя ссылка на него:

var a = {z: 'z'};

var b = a;
var c = a;

delete a.z;
delete a; // Мы всего лишь удаляем убиваем ссылку "а"
console.log(b, c); // Объект по факту существует: Object {} Object {}, но он пустой

Т.е. Используя «деструктор», полностью удалить объект нельзя — можно только вычистить содержимое.

13. Можно ли сделать обработку бинарных данных? Если да, то как?


В JavaScript все числа представляются для использования в строчном виде и нет встроенных средств для работы с бинарными данными. Есть библиотека JavaScript BinaryParser для работы с двоичными числами: кодирование, декодирование (её код — ад!)

В ECMAScript 6+ (strawman) есть черновик StructType (это struct знакомый нам из С++ и других). Он нужен для упрощения работы с двоичными файлами. Вот как это может выглядеть в будущем:
const Point2D = new StructType({ x: uint32, y: uint32 });
const Color = new StructType({ r: uint8, g: uint8, b: uint8 });
const Pixel = new StructType({ point: Point2D, color: Color });

const Triangle = new ArrayType(Pixel, 3);

let t = new Triangle([{ point: { x:  0, y: 0 }, color: { r: 255, g: 255, b: 255 } },
                      { point: { x:  5, y: 5 }, color: { r: 128, g: 0,   b: 0   } },
                      { point: { x: 10, y: 0 }, color: { r: 0,   g: 0,   b: 128 } }]);


donnerjack13589 ArtemS Для чтения из буферов возможно использовать JavaScript typed arrays, но получить число в двоичной форме не получится.

XMLHttpRequest Level 2 позволяет отправлять и получить бинарные файлы:

14. Как из одной функции изменить переменные контекста другой функции?


1. Можно предать ссылку на объект контекста primer в smth
2. Передать функцию, порождённую в контексте primer в функцию smth
var primer = function (){
    var a, b, c, d, e = {};

    smth(function () {
        a = 1;
        b = 2;
        c = 3;
        d = 4;
    }, e);

    alert([a, b, c, d, e.pewpew]);
},
smth = function (callback, e) {
    callback();
    e.pewpew = "pewpew";
};

primer();


3. Раньше (FireFox 3.6-) можно было достучаться к контексту через __parent__, но в 4 версии выпилили эту возможность.

15. Касательно статьи «Пять способов вызвать функцию». Какой из этих N способов (в заголовке 5, в статье 4, несколько в комментариях) когда применять лучше и почему?


Не буду рассматривать глобальный вызов/вызов метода и конструктор, их область применения и так понятна.
Остановлюсь отдельно на call и apply. Делают они одно и тоже — вызывают функцию с явным контекстом this.
1. Сall и apply для оверрайда конструктора:
// Вспомогательная функция
function extend(newObj, oldObj) {function F() {}F.prototype = oldObj.prototype;newObj.prototype = new F();return newObj}

var Obj = function () {
   this.obj_var = 100;
};
Obj.prototype.obj_proto_var = 101;

var NewObj = function () {
   Obj.call(this); // Вызываем конструктор Obj и получаем Own property obj_var
   this.new_obj_var = 102;
};
extend(NewObj, Obj)

NewObj.prototype.new_obj_proto_var = 103; 
new NewObj(); // {new_obj_proto_var: 103, new_obj_var: 102, obj_proto_var: 101, obj_var: 100}

2. Преобразование arguments NodeList и прочих массивоподобных объектов к массиву, превращение живого списка (getElementsByTagName) к массиву
// document.getElementsByTagName("div") возвращает не массив (хотя похож), поэтому мы не можем выполнять методы массива  
document.getElementsByTagName("div").forEach(function (elem) {
    // ...
}); // TypeError: document.getElementsByTagName("div").forEach is not a function

// Приводим к массиву: мы подсовываем фукнции slice this, который поход на массив
Array.prototype.slice.call(document.getElementsByTagName("div")).forEach(function (elem) {
    // OK
});

// Аналогично можно сделать со строками
Array.prototype.slice.call('pewpew') // ["p", "e", "w", "p", "e", "w"]
// В ИЕ8- будет будет массив из undefined

3. Трюки с Function.call.apply для создания оберток:
Нам нужно написать обёртку foo(), которая вызывает bar() в указанном контексте с произвольным числом аргументов
От hyborg
В традиционном виде, это выглядело бы так:
function bar() {}
// foo(context, arg1, arg2, ...)
function foo() {
    var context = arguments[0]; 
    var args = Array.prototype.slice.call(arguments, 1); //делаем массив аргументов для bar
    bar.apply(context, args);
}

Вместо этого салата использовать трюк с call.apply:
function foo() { 
    Function.call.apply(bar, arguments);
}

Работает это так: aplly вызывает Function.call на объекте bar c переданными в foo параметрами. То есть получаем следующее для самого первого примера с context и arg1, arg2:
bar.call(context, arg1, arg2)

4. Эмуляция bind

16. Как передать scope выполнения одной функции в другую?


Никак. Раньше (FireFox 3.6-) можно было достучаться к контексту через __parent__, но в 4 версии выпилили эту возможность.

17. Как корректно получить глобальный объект без его прямого указания, без eval и при 'use strict'?


Никак, если опустить одно из условий, либо выполнить только в глобальном scope, то можно:
// 1: eval - on
(function(){
    "use strict";
    var globalObject = (0, eval)("this"); // Магия :)
    return globalObject;
}());

// 2: указание имени - on
(function(global){
    // ...
}(window));

// 3: "use strict" - off
(function(){
    return this;
}());

// 4: Если выполнить вот этот код в глобалах, то мы получим ссылку на глобальную переменную, но в остальных случаях он не будет работать.
// Это самый оптимальный вариант
"use strict";
(function(global){
    // global
})(this);

18. Возможно ли в javascript после перехвата события перезапустить его позднее?


event не несет никакой нагрузки это просто дескриптор события. Но можно явно передать ссылку на обработчик события, вот так:
$('#smth').click(function onSmthClick(event){
    if (smth) {
        // Прописать обработчик
        event.handlerFunction = onSmthClick;
        event.handlerContext = this;
        // передать левому объекту
        // теперь otherObjectSetSomeEvent может использовать event.handlerFunction и вызывать обработчик
        otherObjectSetSomeEvent(event);
    } else {
        // делаем что-то другое
    }
});

Но это не удачное решение, т.к. придется ещё много чего лишнего накрутить. И логика получается очень запутанная.
Лучше переделать логику и разделить общий обработчик на 2 части:
$('#smth').click(function handler1(event) {
    if (smth) {
        // передать левому объекту
        leftObjectSetSomeEvent(event, function handler2(e) {
            // делаем что-нибудь с event или e
        });
    } else {
        // делаем что-то другое
    }
});

function leftObjectSetSomeEvent(event, callback) {
    callback(event);
    // делаем что-нибудь с event
}

19. А вы не думали написать свой справиочник по js? Подскажите где углубленно изучить JavaScript? Книги, туториалы?


Существует много сайтов и много книг, часть из них переведены на русский.

20. Как на JS перехватить все клики на странице по любым элементам? То есть сделать единый обработчик для кликов


Необходимо повесить обработчик события click на самый нижний объект в DOM дереве, все клики по элементам будут «всплывать» (если по дороге не тормознут гаишникизапретят всплытие) до него.
// jQuery
$(window).bind('click', function (e) {
    console.log('Clicked on ', e.target);
});

// Можно также ограничить какой-то областью, используя jQuery delegate
$('#pewpew').delegate('*', 'click', function (e) {
    console.log('Clicked on ', e.target);
});

// Можно ограничить и цели
$('#pewpew').delegate('.pewpew', 'click', function (e) {
    console.log('Clicked on element with .pewpew class name');
});

21. Как выполнить XHR без jQuery?


Не кросс-браузерная функция:
function xhr(m,u,c,x){with(new XMLHttpRequest)onreadystatechange=function(x){readyState^4||c(x.target)},open(m,u),send(с)}

Кросс-браузерная, немного длиннее:
function xhr(m,u,c,x){with(new(this.XMLHttpRequest||ActiveXObject)("Microsoft.XMLHTTP"))onreadystatechange=function(x){readyState^4||c(x)},open(m,u),send(с)}

Использование:
xhr('get', '//ya.ru/favicon.ico', function(xhr){console.dir(xhr)});

22. Работа с файловой системой


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

Про клиентскую работу с файловой системой, в том числе аплоад файлов на хабре есть куча статей:
HTML5 File API: множественная загрузка файлов на сервер
Загрузка файлов с помощью html5 File API, с преферансом и танцовщицами
FileSystem API&File API: разбираемся и используем
Самый короткий аплоадер картинок!

Про работу с файлами на сервере Node.js есть хорошая статья Чтение и запись файлов в Node.js

По XUL можно почитать тут в статье MDC File_I/O

23. Reflow, repaint и методы их минимизации


1. Если браузер поддерживает функцию requestAnimationFrame, то стоит использовать её вместо setInterval/setTimeout
Браузеры могут оптимизировать анимации идущие одновременно, уменьшив число reflow и repaint до одного, что в свою очередь приведет к повышению точности анимации. Например анимации на JavaScript синхронизированные с CSS transitions или SVG SMIL. Плюс ко всему если выполняется анимация в табе, который невидим, браузеры не будут продолжать перерисовку, что приведет к меньшему использованию CPU, GPU, памяти и как следствие снизит расход батареи в мобильных устройствах.
2. Избегайте большого количества float элементов (уменьшится reflow)
4. Как можно реже модифицируйте DOM дерево — пишите в память, а потом 1 раз вставляйте в DOM (уменьшится reflow)
5. Изменяйте свойства объекта пачкой (уменьшится reflow, redraw) (это не справедливо для современных браузеров)
// Вместо
element.style.left="150px;";
//...
element.style.color="green";

// Измените все сразу
element.setAttribute('style', 'color:green;left:150px');

6. Проводите анимации только с абсолютно позиционированными объектами (уменьшится reflow)
7. Перед изменением группы элементов спрячьте их style.display = "none" (уменьшится reflow) (это не справедливо для современных браузеров)

Не по теме, но тоже про оптимизацию:
8. Используйте делегацию событий для уменьшения их количества
9. Кэшируйте ссылки на DOM элементы (вызов селектора самая дорогая операция)
10. Используйте быстрые функции селекторов querySelectorAll() firstElementChild
11. Помните, что document.getElementsByTagName возвращает «живую» коллецию элементов (если элемент добавиться в DOM дерево, то коллекция получит его автоматически)

Во многих современных браузерах эти методы не будут давать столь видимое преимущество (разработчики браузеров все оптимизируют за нас).

Почитать:
Nicholas C. Zakas — OReilly High Performance JavaScript
Продвинутые анимации с requestAnimationFrame

24. Стоит ли использовать childProcesses в node.js для каждого запроса в высоко-нагруженных проектах?


Для каждого запроса ни в коем случае не стоит использовать childProcesses потому, что мы получаем слишком много накладных расходов (это как PHP с апачем): выделение лишней памяти, время форка, время инициализации (jid компиляция), нагрузка на ЦП и т.п. Node.js очень хорошо распределяет нагрузку и загружает одно ядро процессора в своем «evented processing loop» — основной поток приложения. Идеальная загрузка для Node.js — по 1му форку на ядро, лучше всего форкать, используя Cluster. Кластер будет выступать в роли балансира (мастеров), а форки — слэйвы. Оправдано использование childProcesses для тяжелых запросов.
Можно ещё почитать тут: stackoverflow.com/questions/3491811/node-js-and-cpu-intensive-requests

25. Использование runInNewContext в node.js


Что такое runInNewContext?node-js.ru/12-control-context-using-runinnewcontext
Единственное применение данной технологии я вижу для запуска чужого, потенциально опасного кода (так делает хостинг Node.js — nodester). Если же критической в этом необходимости нет, то я категорически против этого — это совершенно не нужная обертка, которую можно не использовать если выбрать правильную архитектуру приложения и использовать договоренности при разработке. Чем плохо: создание/удаление контектса — выделение памяти как следствие частый GC (который блокирует все приложение). Думаю будут проблемы с поддержкой такого кода.

Заключение


На все вопросы, которых нет в этой статья ответит TheShock За мной ещё статья про архитектуру тяжелых интерфейсов (gmail, и прочие).

Если что-то не понятно — задавайте вопросы.
Mikhail Davydov @azproduction
карма
449,5
рейтинг 0,0

Похожие публикации

Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (50)

  • +5
    Спасибо. Чем больше таких топиков — тем меньше тем на форумах и в гуглоответах вида «А как ...?».
  • 0
    Огромное Вам Спасибо с Большой Буквы!
  • +1
    Спасибо, действительно большая работа.
  • 0
    17. Как корректно получить глобальный объект без его прямого указания, без eval и при 'use strict'?

    перепроверил, все корректно работает.

    «use strict»;
    var global = (function( global) { return global; })( this );

    • 0
      Уточню, что именно такой код не корректно запускать в консоли ("use strict" не сработает).
      (function () {
          "use strict";
          var global = (function(global){ 
              return global;
          })(this);
          return global; // undefined
      }());

      Почему так происходит можно узнать в статье JavaScript Strict Mode § Почему я не могу включить Strict Mode в консоли моего браузера?
      • –1
        Есть одно но. В вопросе никто не говорил про консоль.
        • +1
          Все верно. Я на всякий случай написал ;)
          Если выполнить вот этот код в глобалах, то мы получим ссылку на глобальную переменную:
          "use strict";
          (function(global){
              // global
          })(this);

          Но такой трюк нельзя провернуть внутри другого scope: this будет undefined. Добавлю этот 4-й почти идеальный вариант.
        • 0
          Есть одно «но». В вопросе шла речь про универсальное решение.
      • 0
        К тому же, запуская из консоли, можно сделать вот так:

        (function ( global ) {

        }( this ));
        • 0
          Уточню. Если вы не хотите использовать параметры замыкания внутри кода, по каким либо причинам, т.к. разрабатывается решение, которое будет исполняться в другой среде, тогда достаточно сделать вот так:

          (function( global ) {
          «use strict»;
          function getGlobal() {
          return global; //потом, при запуске в другой среде, где не нужно внешнее замыкание, здесь нужно будет проставить this
          }

          })( this );
          • 0
            блин, торопился, вот верный код:

            (function( global ) {
            «use strict»;
            var global = (function getGlobal ( global ) { return global; })( global ); //тут в аргументе вместо global проставить this

            })( this );
    • 0
      shock@localhost:~> cat global.js
      'use strict';
      (function (glob) {
              console.log(glob == GLOBAL);
      })(this);
      
      shock@localhost:~> node global.js
      false
      
      • 0
        Если что GLOBAL в Node.js называется global (с маленькой буквы) ;-)
        $ node
        > 'use strict'; (function (glob){console.log(glob === global)}(this));
        true

        И в Node.js v0.4.7(8) нет Strict Mode, код в пруф:
        $ node
        > console.log(function(){'use strict'; return (this === global) }());
        true
        • 0
          Ну, у меня, конечно, 0.3 версия, но:
          • 0
            'use strict';
            (function (glob) {
            	console.log(global == GLOBAL); // true
            	console.log(glob   == global); // false
            	console.log(glob   == GLOBAL); // false
            })(this);
            
            

            Может, в новой версии это поменялось…
            • 0
              0.3.х не стабильная обнови до 0.4.х Мои приборы на v0.4.7 показывают false, true, false
      • 0
        Сейчас в Node.js cмысла от 'use strict' нет потому, что Strict Mode нет. И на самом деле в node.js GLOBAL называется global, если исправить код, то все получится ;-)
        • 0
          Какой-то полтергейст. Написал предыдущий комент, рефрешнул страницу пару раз — комента нет. Подумал, что нажал «предпросмотр» и забыл нажать «написать», всякое бывает. Написал ещё раз, но короче, но оказалось проблема не с памятью :)
          • 0
            Кажется, проблема с Хабракешем.
  • +1
    Большое спасибо. Почему нет функции избранного в избранном, я бы добавил :)?
  • 0
    > 13. Можно ли сделать обработку бинарных данных? Если да, то как?
    >…
    > Сейчас нет нормальной возможности работать с двоичными данными.

    А как же typed arrays?
    • 0
      Отчасти верно, но как используя его записать двоичное число (big-endian, little-endian, middle-endian)?!
      • 0
        Хм… ну это прерогатива ОС, с каким типом работает программа.
        Ну, а если требуется передача по сети, то у нас же массив байтов, пишем туда либо от 0 до lengh, либо от length до 0
        • 0
          Это понятно. Проблема не в типе записи двоичного числа, а в возможности преобразовать массив в двоичную строку — нет метода toBinaryString() или подобного.
  • 0
    > element.setAttribute('srtle', 'color:green;left:150px');
    должно быть style
  • 0
    Большое спасибо за фак =)
    Хочу задать вопрос — какие IDE порекомендуете? Может есть что-то на платформе клипсы?
    • +2
      Использовал Eclipse+Plugins, Aptana, Netbeans. Сейчас остановился на WebStorm.
      • –1
        Я в js дубовый нуб =) поэтому можно поинтересоваться, какие именно плагины? м?
        • 0
          В WebStorm ничего ставить не нужно, он хорош из коробки. Единственный плагин, который я добавил — idea-markdown (подсветка markdown синтаксиса, рандер HTML) очень упрощает жизнь при написании README.md для гитхаба.
          • 0
            Спасибо.
    • 0
      Самое лучше что встречал для JS это JetBrains WebStorm.
    • 0
      www.hobius.com/?post=4d978f6bb91c1b5564000001
      Читать с комментариями все.

      Я дополнял тесты для IDE.
      • 0
        Когда только переходил с NetBeans на WebStorm смущало, что для каждого проекта открывается свое окно (критерий Projects). Потом проникся дао этой фичи — так удобнее работать с ветками/версиями проекта, и меньше путаницы. Сейчас я не считаю, что это минус.
        • 0
          Если взять тот же Eclipse + Mercurial(Git) и работать, предположим, с проектом с веткой hotfix и product одновременно — то Profit виден на лицо.

          Так же очень удобно держать открытым проект разработки плагина + проект, где используется данныый плагин.
        • 0
          Зато большой минус, что Web\PHPShtorm плохо работает с полями объекта, который является полем другого объекта.
  • 0
    По пункту 11: Крокфордовский стиль далеко не самый быстрый — jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/10
  • 0
    Скажите, а возможно ли в JS имея ссылку на картинку отправить ее методом POST на сторонний сервер?

    Т.е. Отправить саму картинку, а не ссылку.
    По логике js ее должен считать, а потом отправить.
    • 0
      Если картинка лежит на вашем сервере или на кроссдоменном, который разрешил вам работать кроссдоменно (Same origin policy), то вы можете запросить картинку через обычный XHR GET и переправить её код (кодированный в Base64 или сырой) на ваш сервер или кроссдоменный (который тоже разрешил вам работать кроссдоменно) POST'ом.
      Как сделать кроссдоменную коммуникацию написано в статье MDC HTTP Access Control
      Если картинка лежит на другом домене(который не разрешил работать кроссдоменно), то трюк с canvas putImageData / getImageData не пройдет из-за Same origin policy.
      • 0
        Спасибо за ответ!
        А на сколько это кроссбраузерно?

        Вообще понятно, про access control при GET`e картинки. Но когда я ее отправляю POST`ом на другой сервер, зачем нужен access? Ведь это не чем не отличается от отправки формы постом на другой сервер, как здесь затрагивается политика безопасности, каким образом это может быть не безопасно?
        • 0
          Впринципе это приемлемо, что со вторым сервером у нас не будет договоренности. POST отправится(сервер получит данные), но статус отправки не будет успешным (status: 0). Придется ещё что-нибудь докрутить, чтобы узнать успешно отправился файл или нет. Вобщем проще сделать договоренность.
          А на сколько это кроссбраузерно?

          XDomainRequest Internet Explorer 8+
          XmlHttpRequest CORS Firefox 3.5+, Safari 4+, Chrome, не уверен, что поддерживает Opera

          Почитать
          cors xmlhttprequest
          Cross-domain Ajax with Cross-Origin Resource Sharing
  • 0
    У меня вопрос про функцию _cloner

    this._clone({ it: it }).it;

    Что дают эти операции?
    • 0
      Например, мы в оригинальную функцию клонирования засовываем строку cloner._clone("11111")
      На выходе получаем объект {1: "1", 2: "1", 3: "1", 4: "1'} (такой алгоритм), а функция clone всегда отправляет в _clone объект, поэтому на выходе мы получим тоже строку.
  • 0
    По поводу моего 23 вопроса о repaint и reflow.
    У меня есть некоторые сомнения на счет корректности 23.6:
    Проводите анимации только с абсолютно позиционированными объектами (уменьшится reflow)

    В качестве примера могу привести скролл в блоке «Что нового» на deti.mail.ru/
    В скрипте deti.mail.ru/scripts/jquery.vscroll.js анимация выполняется через jQuery изменением свойства style.top у абсолютно позиционированного элемента. При этом если посмотреть в Firefox'е область перерисовки выясняется, что страница почти полностью перерисовывается при скроллировании и нагрузка на процессор при этом весьма значительна.

    В качестве контрпримера могу привести слайдер в верхней части страницы showbiz.mail.ru/, где я использовал ручную анимацию через изменение атрибута scrollLeft, в результате чего перерисовывается только область непосредственно слайдера и нагрузка на процессор существенно ниже, нежели в случае анимации через style.left

    Возможно, это справедливо лишь для FF4.0.1 и IE9, т.к. в хроме судя по всему с этим лучше.
    Но всё же хотелось бы чуть более развернутого ответа.
  • +1
    Может будет уместно содержание?
  • 0
    А вот что это за шрифт на КДПВ?
    • 0
      Consolas, с подогнанным межбуквенным расстоянием.
  • 0
    На действия пользователя в ajax-системе сервер присылает ответ «alert('Бум!!!');». На клиенте полученный ответ прогоняется через eval() и выполняется. Как называется такая передача данных? Это не JSON, не XML, не HTML.


    Названия этому нет.

    Вообще встречал где-то для такого подхода аббревиатуру AJAJ — Asynchronous Javascript and JSON. Подходя формально, это не является AJAX'ом, как и не является «ксерокс» любым копиром. Но кого это волнует :)
    • 0
      А JSON — это подмножество JavaScript и вполне можно AJAJ расшифровать как Asynchronous Javascript and Javascript :)
  • +1
    Спасибо за статью!

         // IE         0      1       2        3      4
         buttonMap = ['???', 'Left', 'Right', '???', 'Middle'];
    


    Подозреваю, что это сделано для удобства получения бита состояний всех клавиш сразу:, т.е. при нажатых одновременно Left и Right мы получим 3, при нажатых Left и Middle — 6 и т.д. Нет под рукой ИЕ, что бы проверить. По крайней мере, мысль про битовые маски возникает первой.
    • 0
      Ещё в MSDN написано, что 0 — ни одна кнопка мыши не нажата

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.