Pull to refresh

JavaScript F.A.Q: Часть 1

Reading time 15 min
Views 73K
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, и прочие).

Если что-то не понятно — задавайте вопросы.
Tags:
Hubs:
+222
Comments 50
Comments Comments 50

Articles