«Массивы JavaScript» и «JavaScript на новый лад»: разница между страницами

Материал из support.qbpro.ru
(Различия между страницами)
imported>Supportadmin
 
imported>Supportadmin
 
Строка 1: Строка 1:
==Массивы==
За последние пару лет, а сейчас январь 2018, в JS появилось столько всего нового и это в стольких местах описано. Пора собрать в кучу весь поток сознания.
оригинал:http://javascript.ru/basic/array
==Объявления переменных let и const ==
-----
[https://learn.javascript.ru/let-const Теория]
Javascript поддерживает два вида структуры "массив":
[https://jsperf.com/let-vs-var-performance/93 Тесты]


Ассоциативный массив (хеш), где данные хранятся по произвольному ключу. (Об этом читайте в разделе [[Javascript объекты в примерах|Объекты]].)
Вывод: никакого существенного изменения производительности не несут. Вопрос в обратной совместимости кода.
 
Числовой массив Array, где данные хранятся по номерам. Он описан в этой статье.
 
Javascript - очень гибкий язык, поэтому технически в Array можно хранить произвольные ключи, как в Object. Но лучше использовать типы по назначению.
 
Для хранения данных по номеру предназначен тип Array.
 
var arr = new Array()
arr.test = 5
arr[1] = "blabla"
 
В типе Array есть специальные методы, ориентированные именно на работу с числовыми ключами.
 
== Создание и изменение ==
 
Есть два эквивалентных способа создания массива:
var a = new Array()
var a = []
Или, сразу со значениями
var a = new Array("a", 1, true)
var a = ["a", 1, true]
 
Эти способы работают одинаково, кроме объявления вида new Array(10), когда у конструктора есть единственный аргумент-число.
 
Такое объявление создаст пустой массив (все элементы undefined) длиной 10. По возможности, не используйте new Array.
 
Отсчет элементов начинается с нуля:
 
alert(a[0])  // => "a"
 
Массив хранит данные по численным ключам, но внутри он использует точно такой же хэш (ту же структуру данных), как и обычный объект, поэтому можно сделать так:
 
var a = []
a[1] = 1
a[999999] = 2
 
и массив a будет занимать память, нужную для хранения этих двух соответствий, а не займет длинный непрерывный кусок памяти, как это произошло бы в языке С.
 
 
== Авто-длина length ==
 
 
У каждого массива есть свойство length, которое автоматом меняется при каждом обновлении массива. Длина массива - это не количество элементов, а максимальный целый ключ + 1:
 
alert(a.length) // всего 2 элемента, но выведет 1000000
Добавлять новый элемент можно эквивалентными вызовами
a[a.length] = "new element"
a.push("new element")
 
 
== Перебор элементов ==
 
 
Перебор элементов обычно (когда индексы непрерывные) осуществляется простым циклом:
 
var arr = [ "array", "elements", "here" ]
for(var i=0; i<arr.length; i++) {
  ... сделать что-то с arr[i] ...
}
 
Если индексы - с разрывами, то перебор осуществляется так же, как в объектах:
 
var arr = []
arr[1] = 123
arr[9999] = 456
for(var i in arr) {
    if (!arr.hasOwnProperty(i)) continue;
  ... сделать что-то с arr[i] ...
}
 
===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
 
Добавляет в начало массива элементы и возвращает получившуюся длину.Добавленные элементы сохранят порядок следования. Данный метод изменяет исходный массив.
 
<nowiki>
var arr = ["a", "b"]
unshifted = arr.unshift(-2, -1);
alert(arr ); // [ -2, -1, "a", "b"]
alert("New length: " + unshifted); // 4</nowiki>
 
=== slice ===
 
 
slice(begin[, end])
Возвращает подмассив с индексами begin…end.
 
 
=== splice ===
 
 
splice(index, deleteCount[, element1,…, elementN])
Удалить deleteCount элементов, начиная с index, и вставить на их место element1…elementN
 
Есть и еще много методов:
 
join
reverse
...
О них можно почитать на английском, например, в http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array
 
==Организация очереди==
 
<nowiki>/*
 
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);
 
  }
 
}</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 () {
 
    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");
}</nowiki>
 
В этом примере будет сгенерировано 20 объектов сообщений, у которых есть метод delivery, реализующий всю работу по доставке этого сообщения. Они все будут сразу поставлены в очередь, но одновременно отправляться смогут только 5 сообщений.
 
После добавления задания в очередь генерируется событие schedule, а до и после запуска задачи — before и after соответственно. В событии after в качестве параметра передается promise-объект задачи. Через него можно так же отслеживать окончание её выполнения в другом модуле.
 
<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>
 
Контекстом во всех событиях выступает объект, который ставился в очередь.
 
== Очередь + стек ==
 
 
В массиве есть всё необходимое, чтобы работать с ним как с очередью или со стеком, или и с тем и другим одновременно.
 
Методы push и pop добавляют или вынимают значение с конца массива
var arr = [3,5,7]
arr.push(9)
var last = arr.pop()    //= 9
var last = arr.pop()  // = 7
alert(arr.length)  // = 2
Методы shift/unshift делают то же самое, с начала массива.
var arr = [4,6,8]
arr.unshift(2) // arr = [2,4,6,8]
arr.unshift(0) // arr = [0,2,4,6,8]
var last = arr.shift() // last = 0, arr = [2,4,6,8]
arr.shift()  // arr = [4,6,8]
 
shift/unshift обычно приводят к перенумерации всего массива. shift сдвигает все элементы на единицу влево, а unshift - вправо. Поэтому на больших массивах эти методы работают медленнее, чем push/pop.

Версия от 13:18, 5 января 2018

За последние пару лет, а сейчас январь 2018, в JS появилось столько всего нового и это в стольких местах описано. Пора собрать в кучу весь поток сознания.

Объявления переменных let и const

Теория Тесты

Вывод: никакого существенного изменения производительности не несут. Вопрос в обратной совместимости кода.