«Объекты JavaScript» и «Массивы JavaScript»: разница между страницами

Материал из support.qbpro.ru
(Различия между страницами)
imported>Supportadmin
 
imported>Supportadmin
(Новая страница: «==Массивы== ===push=== '''array.push( elem1, elem2, ... )''' Эти элементы будут добавлены в конец массива. Он д…»)
 
Строка 1: Строка 1:
==Объекты.==
==Массивы==
первоисточник понимания http://karaboz.ru/?p=5
===push===


'''array.push( elem1, elem2, ... )'''


'''var <имя_объекта> = new <Тип_объекта> ('Привет мир');'''
Эти элементы будут добавлены в конец массива. Он добавляет элементы, начиная с текущей длины length и возвращает новую, увеличенную длину массива.


Это типичный пример. Ключевые слова:
Пример: добавление двух элементов
:'''var''' - создает локальную переменную, не принадлежащую объекту window
// array.length = 2
:'''new''' - создает новую область видимости, отличную от области видимости window.
var array = [ "one", "two" ]
// добавить элементы "three", "four"
var pushed = array.push("three", "four")
// теперь array = [ "one", "two", "three", "four" ]
// array.length = 4
// pushed = 4
===pop===
'''arrayObj.pop()'''


Пример создания объектов разных типов:
Этот метод извлекает последний элемент массива и возвращает его. При этом возвращенный элемент удаляется из массива, а длина массива уменьшается на единицу. Если массив пустой, то метод pop() возвращает значение undefined, при этом массив так и остается пустым.


{|border="1" cellspacing="0"
Этот метод изменяет исходный массив.
|  <center>'''Явное создание'''</center>
| <center>'''Неявное создание'''</center>
|-
| colspan="2"| <center>'''Число'''</center>
|-
| var num = new Number(12345.6789);
| var num = 12345.6789;
|-
| colspan="2"| <center>  '''Булево значение'''</center>
|-
|var c = new Boolean(true);
|var bul = true;
|-
| colspan="2"| <center>  '''Функция'''</center>
|-
|var fun = new Function('x', 'var p = x');
|var fun = function(x){var p = x}
|-
| colspan="2"| <center>  '''Массив'''</center>
|-
|var arr = new Array('a', 'b', 'c');
|var arr = ['a', 'b', 'c'];
|-
| colspan="2"| <center>  '''Объект'''</center>
|-
|var obj = new Object();
|var obj = {};
|}


<span style="color:red">'''Ограничения'''</span>
myFish = ["angel", "clown", "mandarin", "surgeon"];
Следующий код показывает равенство двух переменных (объектов класса String):
  popped = myFish.pop();
var str1 = 'karaboz';
  // теперь popped = "surgeon"
  var str2 = new String('karaboz');
// myFish = ["angel", "clown", "mandarin"]
  alert(str1 == str2); // выводит true


при попытке определить новый пользовательский метод для str1 мы получим ошибку:
str1.tell = function(){ alert(this);}
str1.tell(); // выводит ошибку 'str1.tell is not a function'


При этом, для str2 все сработает, как мы и ожидаем:
===shift===
str2.tell = function(){ alert(this);}
str2.tell(); // выводит 'karaboz'


Это ограничение, наложенное JavaScript на переменные (объекты), созданные через строковые, числовые и булевы литералы, тем не менее,  не распространяется на объекты, созданные через литералы функции, массива или объекта. Т.е. переменным (объектам), содержащим в себе функцию, или массив, или объект можно напрямую присваивать пользовательские свойства и методы:
'''var elem = arrayObj.shift()'''
var s = 'futurico'; // создаем новое свойство s объекта window (window.s)
var f = function(){ // создаем новый метод f объекта window (window.f)
alert(this == window); // выводит true
alert(this.s); // выводит 'futurico'
}
f(); // вызываем метод f объекта window (window.f())


f.s = 'karaboz'; // создаем новое свойство s объекта window.f (window.f.s)
Удаляет элемент с индексом 0 и сдвигает остальные элементы на один вниз. Возвращает удаленный элемент
f.m = function(){ // создаем новый метод m объекта window.f (window.f.m)
alert(this == f); // выводит true
alert(this.s); // выводит 'karaboz'
}
f.m(); // вызываем метод m объекта window.f (window.f.m())
Здесь мы наглядно убеждаемся, что функция f, созданная как метод глобального объекта window, сама оказывается объектом, у которого могут быть свои собственные свойства и методы!


'''Это ограничение, наложенное JavaScript на переменные (объекты), созданные через строковые, числовые и булевы литералы, тем не менее, не распространяется на объекты, созданные через литералы функции, массива или объекта. Т.е. переменным (объектам), содержащим в себе функцию, или массив, или объект можно напрямую присваивать пользовательские свойства и методы''':
var arr = ["мой","маленький", "массив"]
  var s = 'futurico'; // создаем новое свойство s объекта window (window.s)
  var my = arr.shift() // => "мой"
var f = function(){ // создаем новый метод f объекта window (window.f)
  alert(arr[0]) // => "маленький"
alert(this == window); // выводит true
  // теперь arr = ["маленький", "массив"]
  alert(this.s); // выводит 'futurico'
  }
f(); // вызываем метод f объекта window (window.f())


f.s = 'karaboz'; // создаем новое свойство s объекта window.f (window.f.s)
===unshift===
f.m = function(){ // создаем новый метод m объекта window.f (window.f.m)
alert(this == f); // выводит true
alert(this.s); // выводит 'karaboz'
}
f.m(); // вызываем метод m объекта window.f (window.f.m())
Здесь мы наглядно убеждаемся, что функция f, созданная как метод глобального объекта window, сама оказывается объектом, у которого могут быть свои собственные свойства и методы!


<span style="color:darkgreen">'''Вывод'''</span>
'''arrayObj.unshift( [elem1[, elem2[, ...[, elemN]]]] )'''
Для того, что бы объект вел себя предсказуемо (это исключает долгую логическую отладку) объекты <span style="color:darkgreen">'''''НАДО СОЗДАВАТЬ ТОЛЬКО ЯВНЫМ СПОСОБОМ'''''</span>


оригинал http://habrahabr.ru/post/17613/
Аргументы


elem1, elem2, ..., elemN


JavaScript предоставляет разработчикам возможность создавать объекты и работать с ними. Для этого существуют следующие приёмы:
Добавляет в начало массива элементы и возвращает получившуюся длину.Добавленные элементы сохранят порядок следования. Данный метод изменяет исходный массив.
Оператор new
Литеральная нотация
Конструкторы объектов
Ассоциативные массивы


var arr = ["a", "b"]
unshifted = arr.unshift(-2, -1);
alert(arr ); // [ -2, -1, "a", "b"]
alert("New length: " + unshifted); // 4


===Организация очереди===


==Оператор new==
<nowiki>/*
Это, наверное, самый легкий способ создания объекта. Вы просто создаете имя объекта и приравниваете его к новому объекту Javascript.


//Создаем наш объект
Queue.js
var MyObject = new Object();
//Переменные
MyObject.id = 5; //Число
MyObject.name = "Sample"; //Строка
//Функции
MyObject.getName = function()
{
    return this.name;
}


Минус данного способа заключается в том, что вы можете работать только с одним вновь созданным объектом.
A function to represent a queue
//Используем наш объект
alert(MyObject.getName());


==Литеральная нотация==
Created by Stephen Morley - http://code.stephenmorley.org/ - and released under
Литеральная нотация является несколько непривычным способом определения новых объектов, но достаточно легким для понимания. Литеральная нотация работает с версии Javascript 1.3.
the terms of the CC0 1.0 Universal legal code:
//Создаем наш объект с использованием литеральной нотации
MyObject = {
    id : 1,
    name : "Sample",
    boolval : true,
    getName : function()
    {
        return this.name;
    }
}
Как видите, это довольно просто.


http://creativecommons.org/publicdomain/zero/1.0/legalcode
Объект = {
идентификатор : значение,
...
}


И пример использования:
*/


  alert(MyObject.getName());
/* Creates a new queue. A queue is a first-in-first-out (FIFO) data structure -
  * items are added to the end of the queue and removed from the front.
*/
function Queue(){


==Конструкторы объектов==
  // initialise the queue and offset
  var queue  = [];
  var offset = 0;


Конструкторы объектов — это мощное средство для создания объектов, которые можно использовать неоднократно. Конструктор объекта — это, по сути, обычная функция Javascript, которой так же можно передавать различные параметры.
  /* Returns the length of the queue.
  */
  this.getLength = function(){


<nowiki>function MyObject(id, name)
    // return the length of the queue
{
    return (queue.length - offset);


}</nowiki>
  }


Только что мы написали конструтор. С помощью него мы и будем создавать наш объект.
  /* Returns true if the queue is empty, and false otherwise.
  */
  this.isEmpty = function(){


<nowiki>var MyFirstObjectInstance = new MyObject(5,"Sample");
    // return whether the queue is empty
var MySecondObjectInstace = new MyObject(12,"Othe Sample");</nowiki>
    return (queue.length == 0);


Таким образом мы создали различные экземпляры объекта. Теперь мы можем работать отдельно с каждым экземпляром объекта MyObject, не боясь того, что, изменяя свойства одного экземпляра, мы затронем свойства другого экземпляра.
  }


Как и в ООП, у MyObject могут быть методы и различные свойства. Свойствам можно присвоить значения по умолчанию, либо значения, переданные пользователем в конструкторе объекта.
  /* Enqueues the specified item. The parameter is:
  *
  * item - the item to enqueue
  */
  this.enqueue = function(item){


    // enqueue the item
    queue.push(item);


<nowiki>function MyObject(id, name)
  }
{
    //Значения переданные пользователем
    this._id = id;
    this._name = name;
    //Значение по умолчанию
    this.defaultvalue = "MyDefaultValue";
}</nowiki>


  /* Dequeues an item and returns it. If the queue is empty then undefined is
  * returned.
  */
  this.dequeue = function(){


Аналогичным образом мы можем создавать и функции.
    // if the queue is empty, return undefined
    if (queue.length == 0) return undefined;


    // store the item at the front of the queue
    var item = queue[offset];


<nowiki>function MyObject(id,name)
     // increment the offset and remove the free space if necessary
{
     if (++ offset * 2 >= queue.length){
    this._id = id;
      queue  = queue.slice(offset);
    this._name = name;
      offset = 0;
    this.defaultvalue = "MyDefaultValue";
   
     //Получение текущего значения
     this.getDefaultValue = function()
    {
        return this.defaultvalue;
    }
   
    //Установка нового значения
    this.setDefaultValue = function(newvalue)
    {
        this.defaultvalue = newvalue;
    }
   
    //Произвольная функция
    this.sum = function(a, b)
    {
        return (a+b);
     }
     }
}</nowiki>


==Ассоциативные массивы==
    // return the dequeued item
    return item;


  }


Подобный метод будет полезен упорядочивания большого числа однотипных объектов.
  /* Returns the item at the front of the queue (without dequeuing it). If the
  * queue is empty then undefined is returned.
  */
  this.peek = function(){


    // return the item at the front of the queue
    return (queue.length > 0 ? queue[offset] : undefined);


var MyObject = new Number();
  }
MyObject["id"] = 5;
MyObject["name"] = "SampleName";


}</nowiki>


===Очередь с одновременным выполнением нескольких заданий===


Для обхода таких объектов можно использовать такой цикл:
Модуль использует Backbone.Events для оповещения о ходе выполнения задач и несколько полезных функций из Underscore. Экспорт объекта выполняется через [https://github.com/mistakster/app-skeleton/blob/master/app-skeleton.js мой фреймворк].


<nowiki>/**
* Queue for simultaneous task execution.
* Execution method MUST return the promise object.
*
* @param limit {Integer} number of simultaneous tasks
* @event schedule
* @event before
* @event after
*/
(function () {


for (MyElement in MyObject)
    var Task = function (obj, execMethod) {
{
        _.extend(this, {
    //Код обхода
            id: _.uniqueId("queueitem-"),
    //В MyElement - идентификатор записи
            obj: obj,
     //В MyObject[MyElement] - содержание записи
            execMethod: execMethod,
}
            active: false
        });
     };


    _.extend(Task.prototype, {
        run: function () {
            var func, value;


===Свойства объекта===
            this.active = true;
первоисточник для понимания  http://javascript.ru/tutorial/object/intro


Пусть существует такой объект:
            func = this.obj[this.execMethod];
            if (_.isFunction(func)) {
                value = func.call(this.obj);
            }
            // return promise object
            return value;
        }
    });


var stroka = new Object('Привет мир');
    function runTasks() {
        var activeTasks = _.filter(queue, function (task) {
            return task.active;
        });


        if (queue.length > 0 && activeTasks.length < limit) {
            // we can run another task
            var candidate = _.find(queue, function (task) {
                return !task.active;
            });


            if (candidate) {
                Q.trigger("before", candidate.obj);
                var taskDfd = candidate.run();
                Q.trigger("after", candidate.obj, taskDfd);
                if (taskDfd) {
                    taskDfd.always(function () {
                        var i, id = candidate.id;
                        for (i = 0; i < queue.length; i++) {
                            if (queue[i].id === id) {
                                queue.splice(i, 1);
                                break;
                            }
                        }
                        runTasks();
                    });
                }
                // check tasks one more time
                setTimeout(runTasks, 500);
            }
        }
    }


Добавим свойство объекту:
    var queue, limit;


stroka.property ="Новое свойство объекта"
    var Q = _.extend({
        init: function (opts) {
            queue = [];
            limit = opts.limit;
        },
        schedule: function (obj, execMethod) {
            var task = new Task(obj, execMethod);
            if (queue) {
                queue.push(task);
                Q.trigger("schedule", obj);
                runTasks();
            }
        }
    }, Backbone.Events);


Удаление метода
    App.namespace("App.Queue", Q);


delete
}());
Очередь конфигурируется с помощью одного обязательного параметра limit, который задает количество одновременно выполняемых задач.


===Методы объекта===
App.Queue.init({limit: 5});
 
Пусть существует такой объект:
 
var stroka = new String ('Привет мир');
 
Добавим этому объекту метод:
 
stroka.tell = function(){alert(this)};        //область видимости this указывает на объект, которому принадлежит данный метод
 
Вызовем этот метод:
 
stroka.tell();  //выведет  Привет мир
 
===Волшебная переменная arguments===
 
Переменная, доступная внутри функции и содержащая аргументы и ссылку на саму функцию.
Вы можете обращаться к аргументу по номеру, начиная от 0. При этом arguments содержит не объявленные, а реально переданные аргументы.
 
Следующий пример выведет реально переданные три аргумента, несмотря на то, что в функции их всего два.
 
 
function func(a,b) {
  alert(arguments[0])
  alert(arguments[1])
  alert(arguments[2])
}
func(1,2,3)
 
Кроме цифровых индексов, у arguments есть свойство length, такое же как у массива.
 
Благодаря этому можно вызывать функции с переменным числом параметров. В следующем примере функция возвращает сумму всех аргументов.
 
Пример: сумма аргументов
 
function sum() {
  var s = 0
  for(var i=0; i<arguments.length; i++) s += arguments[i]
  return s
}
Несмотря на доступ по индексу и наличие свойства length, arguments не является массивом, т.е не принадлежит типу Array.
 
Поэтому для arguments нельзя напрямую вызвать методы этого класса:
 
arguments.pop() // ошибка !
Можно, однако, вызвать методы Array через apply/call:
 
var args = Array.prototype.slice.call(arguments)
Ссылка на функцию arguments.callee
 
Кроме аргументов, arguments содержит ссылку на выполняющуюся функцию.
 
Ее можно использовать для задания и чтения статических свойств.
 
В следующем примере для этого используется статическое свойство called.
 
Пример: подсчет количества выполнений
 
function func() {
    arguments.callee.called++
}
func.called = 0;
func()
func()
alert(func.called) // 2
==Объекты в примерах==
оригинал:http://javascript.ru/tutorial/object/intro
-----
 
Объекты (они же - ассоциативные массивы, хэши) и работа с ними в Javascript - реализованы не так, как в большинстве языков. С этим связано много ошибок и непоняток.
 
В этой статье описаны базовые свойства объектов javascript, создание и изменение, перечисление свойств и т.п.
 
Объект в javascript представляет собой обычный ассоциативный массив или, иначе говоря, "хэш". Он хранит любые соответствия "ключ => значение" и имеет несколько стандартных методов.
 
Метод объекта в javascript - это просто функция, которая добавлена в ассоциативный массив. Далее - подробнее.
 
== Создание и работа со свойствами ==
=== Создание объекта ===
 
Следующие два варианта создания объекта эквивалентны:
 
var o = new Object()
var o = {}
 
=== Добавление свойств ===
 
Есть два синтаксиса добавления свойств в объект. Первый - точка, второй - квадратные скобки:
 
// эквивалентные записи
o.test = 5
o["test"] = 5
Квадратные скобки используются в основном, когда название свойства находится в переменной:
var name = 'test'
o[name] = 5
Здесь имя свойства "test" является ключом в ассоциативном массиве, по которому лежит значение 5.
 
 
=== Доступ к свойствам ===
 
Доступ к свойству осуществляется точно так же:
 
alert(o.test)
alert(o['test'])
Если у объекта нет такого свойства, то результат будет 'undefined'
 
var o = {}
alert(o.nosuchkey)  // => undefined
Никакой ошибки при обращении по несуществующему свойству не будет, просто вернется специальное значение undefined.
 
 
== Проверка глобальной переменной ==
 
В javascript нельзя проверить существование глобальной переменной простым if:
 
if (x) { ... }
Если x не определен, то конструкция if (x) вызовет ошибку javascript.
 
Распространенное решение - использовать typeof:
 
if (typeof x != 'undefined') { ... }  // или typeof(x)
Однако зная, что глобальная переменная в javascript - всего лишь свойство объекта window - мы можем записать проще:
 
if (window.x) { ... }  // правильный аналог if(x)
// или
if (window.x !== undefined) // аналог typeof x ..
Все свойства объектов - public, т.е при определении свойства никак нельзя ограничить доступ к свойству. В javascript есть специальные выверты для создания private свойств, связанные с замыканиями. Они рассмотрены вместе с наследованием объектов.
 
 
== Удаление свойств ==
Удаляет свойство оператор delete:
 
o.test = 5
delete o.test
o['bla'] = true
 
== Расширенное создание ==
Свойства можно указывать непосредственно при создании объекта, через список в фигурных скобках вида {..., ключ : значение, ...}:
 
var o = {
    test: 5,
    bla: true
}
 
== Методы объектов ==
 
=== Добавление метода ===
 
Как и в других языках, у объектов javascript есть методы.
 
Например, создадим объект rabbit с методом run
 
var rabbit = {}
rabbit.run = function(n) {
    alert("Пробежал "+n+" метров!")
}
Добавление метода в объект - просто присвоение функции function(n) { ... } свойству rabbit.run.
 
Теперь можно запускать
 
var rabbit = {}
rabbit.run = function(n) {
    alert("Пробежал "+n+" метров!")
}
rabbit.run(5)  // Пробежал 5 метров
rabbit.run(7)  // Пробежал 7 метров
 
Здесь не идет речь о классах, создании экземпляров и тому подобном. Просто - в любой объект в любое время можно добавить новый метод или удалить существующий.
 
Javascript - очень динамический язык, не правда ли?
 
 
=== Доступ к объекту из метода ===
 
 
Обычно хочется, чтобы метод не просто вызывался из объекта, но имел доступ к самому объекту, мог менять находящиеся в нем данные.
 
Для этого используется ключевое слово this:
 
for(var key in obj) {
… obj[key] …
}
В отличие от многих языков, this никак не привязано к объекту, а обозначает просто объект, вызвавший функцию.
Например,
 
function this1() {
    var vasya = { name:'Вася' }
    var petya = { name:'Петя' }
    sayName  = function() {
        alert("Я - "+ (this.name ? this.name : 'безымянный') )
  }
    vasya.sayName = sayName
    // один и тот же метод в двух объектах
    petya.sayName = vasya.sayName
    // тут - this будет petya
    petya.sayName()  // Я - Петя
  // тут - this будет vasya
    vasya.sayName()  // Я - Вася
  // а тут - вызывается метод глобального объекта window, у которого нет имени
  sayName() // Я - безымянный
}
 
Более подробно о том, как работает this можно почитать в этой [http://javascript.ru/tutorial/object/thiskeyword статье].
 
 
== Перебор свойств объекта ==
 
 
Для перебора всех свойств объекта используется специальный вид конструкции for, for..in:
 
 
for(var key in object) {
  // key - название свойства
  // object[key] - значение свойства
  ...
}
 
Например,
 
var o = {a:5, b:true}
for (var key in o) {
    alert(key+':'+o[key])
}


var message, i;
for (i = 0; i < 20; i += 1) {
    message = new Message("text " + i);
    App.Queue.schedule(message, "delivery");
}</nowiki>


Это уже выходит за рамки текущей статьи, но вообще - существует еще одна форма перебора свойств, которая более надежна, особенно если используется библиотека типа prototype.
В этом примере будет сгенерировано 20 объектов сообщений, у которых есть метод delivery, реализующий всю работу по доставке этого сообщения. Они все будут сразу поставлены в очередь, но одновременно отправляться смогут только 5 сообщений.


for(prop in object) {
После добавления задания в очередь генерируется событие schedule, а до и после запуска задачи — before и after соответственно. В событии after в качестве параметра передается promise-объект задачи. Через него можно так же отслеживать окончание её выполнения в другом модуле.
    if (!object.hasOwnProperty(prop)) continue
    //...
}
Эта форма отфильтровывает свойства, которые принадлежат не самому объекту, а его прототипу. Поэтому она работает, даже если в прототип Object добавлены новые свойства.


Более элегантный вариант записи:
<nowiki>App.Queue.on("after", function (messagePromise) {
    messagePromise.done(function () {
        console.log("message " + this.get("id") + " was delivered");
    });
    messagePromise.fail(function () {
        console.log("delivering message " + this.get("id") + " was failed");
    });
});</nowiki>


for(prop in object) if (object.hasOwnProperty(prop)) {
Контекстом во всех событиях выступает объект, который ставился в очередь.
    //...
}

Версия от 11:45, 4 августа 2013

Массивы

push

array.push( elem1, elem2, ... )

Эти элементы будут добавлены в конец массива. Он добавляет элементы, начиная с текущей длины length и возвращает новую, увеличенную длину массива.

Пример: добавление двух элементов
// array.length = 2
var array = [ "one", "two" ] 
// добавить элементы "three", "four"
var pushed = array.push("three", "four")
// теперь array = [ "one", "two", "three", "four" ]
// array.length = 4
// pushed = 4

pop

arrayObj.pop()

Этот метод извлекает последний элемент массива и возвращает его. При этом возвращенный элемент удаляется из массива, а длина массива уменьшается на единицу. Если массив пустой, то метод pop() возвращает значение undefined, при этом массив так и остается пустым.

Этот метод изменяет исходный массив.

myFish = ["angel", "clown", "mandarin", "surgeon"];
popped = myFish.pop();
// теперь popped = "surgeon"
// myFish = ["angel", "clown", "mandarin"]


shift

var elem = arrayObj.shift()

Удаляет элемент с индексом 0 и сдвигает остальные элементы на один вниз. Возвращает удаленный элемент

var arr = ["мой","маленький", "массив"]
var my = arr.shift() // => "мой"
alert(arr[0]) // => "маленький"
// теперь arr = ["маленький", "массив"]

unshift

arrayObj.unshift( [elem1[, elem2[, ...[, elemN]]]] )

Аргументы

elem1, elem2, ..., elemN

Добавляет в начало массива элементы и возвращает получившуюся длину.Добавленные элементы сохранят порядок следования. Данный метод изменяет исходный массив.

var arr = ["a", "b"]
unshifted = arr.unshift(-2, -1);
alert(arr ); // [ -2, -1, "a", "b"]
alert("New length: " + unshifted); // 4

Организация очереди

/*

Queue.js

A function to represent a queue

Created by Stephen Morley - http://code.stephenmorley.org/ - and released under
the terms of the CC0 1.0 Universal legal code:

http://creativecommons.org/publicdomain/zero/1.0/legalcode

*/

/* Creates a new queue. A queue is a first-in-first-out (FIFO) data structure -
 * items are added to the end of the queue and removed from the front.
 */
function Queue(){

  // initialise the queue and offset
  var queue  = [];
  var offset = 0;

  /* Returns the length of the queue.
   */
  this.getLength = function(){

    // return the length of the queue
    return (queue.length - offset);

  }

  /* Returns true if the queue is empty, and false otherwise.
   */
  this.isEmpty = function(){

    // return whether the queue is empty
    return (queue.length == 0);

  }

  /* Enqueues the specified item. The parameter is:
   *
   * item - the item to enqueue
   */
  this.enqueue = function(item){

    // enqueue the item
    queue.push(item);

  }

  /* Dequeues an item and returns it. If the queue is empty then undefined is
   * returned.
   */
  this.dequeue = function(){

    // if the queue is empty, return undefined
    if (queue.length == 0) return undefined;

    // store the item at the front of the queue
    var item = queue[offset];

    // increment the offset and remove the free space if necessary
    if (++ offset * 2 >= queue.length){
      queue  = queue.slice(offset);
      offset = 0;
    }

    // return the dequeued item
    return item;

  }

  /* Returns the item at the front of the queue (without dequeuing it). If the
   * queue is empty then undefined is returned.
   */
  this.peek = function(){

    // return the item at the front of the queue
    return (queue.length > 0 ? queue[offset] : undefined);

  }

}

Очередь с одновременным выполнением нескольких заданий

Модуль использует Backbone.Events для оповещения о ходе выполнения задач и несколько полезных функций из Underscore. Экспорт объекта выполняется через мой фреймворк.

/**
 * Queue for simultaneous task execution.
 * Execution method MUST return the promise object.
 *
 * @param limit {Integer} number of simultaneous tasks
 * @event schedule
 * @event before
 * @event after
 */
(function () {

    var Task = function (obj, execMethod) {
        _.extend(this, {
            id: _.uniqueId("queueitem-"),
            obj: obj,
            execMethod: execMethod,
            active: false
        });
    };

    _.extend(Task.prototype, {
        run: function () {
            var func, value;

            this.active = true;

            func = this.obj[this.execMethod];
            if (_.isFunction(func)) {
                value = func.call(this.obj);
            }
            // return promise object
            return value;
        }
    });

    function runTasks() {
        var activeTasks = _.filter(queue, function (task) {
            return task.active;
        });

        if (queue.length > 0 && activeTasks.length < limit) {
            // we can run another task
            var candidate = _.find(queue, function (task) {
                return !task.active;
            });

            if (candidate) {
                Q.trigger("before", candidate.obj);
                var taskDfd = candidate.run();
                Q.trigger("after", candidate.obj, taskDfd);
                if (taskDfd) {
                    taskDfd.always(function () {
                        var i, id = candidate.id;
                        for (i = 0; i < queue.length; i++) {
                            if (queue[i].id === id) {
                                queue.splice(i, 1);
                                break;
                            }
                        }
                        runTasks();
                    });
                }
                // check tasks one more time
                setTimeout(runTasks, 500);
            }
        }
    }

    var queue, limit;

    var Q = _.extend({
        init: function (opts) {
            queue = [];
            limit = opts.limit;
        },
        schedule: function (obj, execMethod) {
            var task = new Task(obj, execMethod);
            if (queue) {
                queue.push(task);
                Q.trigger("schedule", obj);
                runTasks();
            }
        }
    }, Backbone.Events);

    App.namespace("App.Queue", Q);

}());
Очередь конфигурируется с помощью одного обязательного параметра limit, который задает количество одновременно выполняемых задач.

App.Queue.init({limit: 5});

var message, i;
for (i = 0; i < 20; i += 1) {
    message = new Message("text " + i);
    App.Queue.schedule(message, "delivery");
}

В этом примере будет сгенерировано 20 объектов сообщений, у которых есть метод delivery, реализующий всю работу по доставке этого сообщения. Они все будут сразу поставлены в очередь, но одновременно отправляться смогут только 5 сообщений.

После добавления задания в очередь генерируется событие schedule, а до и после запуска задачи — before и after соответственно. В событии after в качестве параметра передается promise-объект задачи. Через него можно так же отслеживать окончание её выполнения в другом модуле.

App.Queue.on("after", function (messagePromise) {
    messagePromise.done(function () {
        console.log("message " + this.get("id") + " was delivered");
    });
    messagePromise.fail(function () {
        console.log("delivering message " + this.get("id") + " was failed");
    });
});

Контекстом во всех событиях выступает объект, который ставился в очередь.