Як ініціалізувати довжину масиву в JavaScript?


541

Більшість навчальних посібників, які я читав на масивах у JavaScript (включаючи w3schools та devguru ), передбачають, що ви можете ініціалізувати масив певної довжини, передавши ціле число до конструктора Array, використовуючи var test = new Array(4);синтаксис.

Після використання цього синтаксису в моїх js-файлах я запустив один із файлів через jsLint , і він вибухнув :

Помилка: Проблема в рядку 1 символу 22: Очікується ')' і замість цього побачив "4".
var test = новий масив (4);
Проблема в рядку 1 символу 23: очікувано ';' і замість цього побачив ")".
var test = новий масив (4);
Проблема в рядку 1 символу 23: Очікується, що ідентифікатор і замість цього побачив ")".

Прочитавши пояснення jsLint щодо його поведінки , схоже, що jsLint не дуже любить new Array()синтаксис, і натомість вважає []за краще при оголошенні масивів.

Тож у мене є кілька питань:

По-перше, чому? Чи ризикую я new Array()замість цього використовувати синтаксис? Чи є несумісність браузера, про яку я маю знати?

І по-друге, якщо я перейду до синтаксису квадратних дужок, чи є спосіб оголосити масив і встановити його довжину все на одному рядку, чи я повинен робити щось подібне:

var test = [];
test.length = 4;

стандартні JS також радять проти використанняnew Array()в цілому, але це нормальноіз зазначенням розміру. Я думаю, що все зводиться до послідовності коду через весь контекст.
Крегокс

Для тих, хто хоче заздалегідь виділити більш жорсткі структури масивів, є типізовані масиви . Зауважте, що переваги від продуктивності можуть відрізнятися
rovyko

Відповіді:


398
  1. Чому ви хочете ініціалізувати довжину? Теоретично в цьому немає потреби. Це навіть може призвести до заплутаної поведінки, тому що всі тести, які використовують те, lengthщоб з'ясувати, чи масив порожній чи ні, повідомлять, що масив не порожній.
    Деякі тести показують, що встановлення початкової довжини великих масивів може бути ефективнішим, якщо масив буде заповнений згодом, але посилення продуктивності (якщо є) відрізняється від браузера до браузера.

  2. jsLint не подобається, new Array()оскільки конструктор неоднозначний.

    new Array(4);

    створює порожній масив довжиною 4. Але

    new Array('4');

    створює масив, що містить значення '4' .

Щодо коментаря: У JS вам не потрібно ініціалізувати довжину масиву. Він росте динамічно. Ви можете просто зберегти довжину в якійсь змінній, наприклад

var data = [];
var length = 5; // user defined length

for(var i = 0; i < length; i++) {
    data.push(createSomeObject());
}

13
Кількість об'єктів у масиві визначено користувачем, тому я дозволив користувачеві вибрати номер, а потім ініціалізувати масив із такою кількістю слотів. Потім я запускаю forцикл, який повторюється по довжині масиву і заповнює його. Я думаю, були б інші способи зробити це в JavaScript, тому справжня відповідь на "Чому я хочу це робити?" це "Через старі звички, що склалися під час програмування іншими мовами." :)
Майкл Мартін-Смукер

4
@mlms Просто дозвольте циклу for повторювати визначене користувачем число, замість того, щоб встановлювати довжину масиву, а потім повторювати його. Це не має для вас більше сенсу?
Šime Vidas

150
<blockquote> Чому ви хочете ініціалізувати довжину? Теоретично в цьому немає потреби. І всі тести, які використовують довжину, щоб з’ясувати, чи масив порожній чи ні, не вдасться. </blockquote> Гм, можливо , продуктивність ? Швидше встановити попередньо існуючий елемент масиву, ніж додати його на льоту.
заголовок

44
"Час відмовитися від старих звичок програмування" коментар дійсно зайвий. Правильно структуровані мови завжди перевершать ecmascript mombo jambo
user151496

10
@codehead: у відповідь на цю цитату: "Швидше встановити попередньо існуючий елемент масиву, ніж додати його на льоту".: Зауважте, що new Array(10)не створюється масив з 10 невизначених елементів. Він просто створює порожній масив довжиною 10. Дивіться цю відповідь за противну істину: stackoverflow.com/questions/18947892/…
Міліметричний

598
  • Array(5) дає вам масив довжиною 5, але не має значень, отже, ви не можете перебирати його.

  • Array.apply(null, Array(5)).map(function () {}) дає вам масив довжиною 5 та невизначений як значення, тепер його можна переглядати.

  • Array.apply(null, Array(5)).map(function (x, i) { return i; }) дає масив довжиною 5 та значеннями 0,1,2,3,4.

  • Array(5).forEach(alert)нічого не робить, Array.apply(null, Array(5)).forEach(alert)дає 5 попереджень

  • ES6дає нам, Array.fromтак що тепер ви можете також використовуватиArray.from(Array(5)).forEach(alert)

  • Якщо ви хочете ініціалізуватися з певним значенням, це добре знати ...
    Array.from('abcde'), Array.from('x'.repeat(5))
    абоArray.from({length: 5}, (v, i) => i) // gives [0, 1, 2, 3, 4]


11
Це те, що я шукав. Я хотів застосувати карту через логічну послідовність; це має робити. Дякую!
jedd.ahyoung

3
чому Array.apply () надає йому невизначене значення?
wdanxna

5
@wdanxna, коли Arrayйому надано декілька аргументів, він повторюється над argumentsоб'єктом і явно застосовує кожне значення до нового масиву. Коли ви зателефонували Array.applyз масивом або об'єктом з властивістю довжини, Arrayвін буде використовувати довжину, щоб явно встановити кожне значення нового масиву. Ось чому Array(5)дає масив з 5 елісій, тоді як Array.apply(null, Array(5))дає масив з 5 невизначених. Для отримання додаткової інформації дивіться цю відповідь.
KylePlusPlus

2
Чи є різниця між масивом (5) та новим масивом (5)?
Петро Хуртак

2
Array.apply(null, Array(5)) Також можна записати в вигляді Array.apply(null, {length: 5}). Різниці дійсно не так вже й багато, але останнє однозначно зрозуміло, що намір полягає у створенні масиву length 5, а не масиву, що містить5
AlexMorley-Finch

271

З ES2015.fill() тепер можна просто:

// `n` is the size you want to initialize your array
// `0` is what the array will be filled with (can be any other value)
Array(n).fill(0)

Що набагато більш стисло, ніж Array.apply(0, new Array(n)).map(i => value)

Можна опускати 0в .fill()і запустити без аргументів, які будуть заповнювати масив з undefined. ( Однак у Typescript це не вдасться )


1
@AralRoca Ви завжди можете використовувати Polyfill, наданий на сторінці MDN, або розглянути можливість використання Modernizr .
АП.

4
Так, спробуйте, перш ніж коментувати
AP.

1
@GarretWilson та @Christian Це в специфікації . When Array is called as a function rather than as a constructor, it also creates and initializes a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments
АП.

1
@AP., Нуль є зайвим. РобитиArray(n).fill()
Pacerier

2
@Pacerier Я не думаю, що 0 був зайвим. Відповідно до визначення типу es6, це функція підпису: fill (значення: T, start ?: число, кінець ?: число): this; Отже, значення заливки не здається необов’язковим. Компіляція машинопису не вдасться, оскільки виклик функції написаний вище.
Micha Schwab

148
[...Array(6)].map(x => 0);
// [0, 0, 0, 0, 0, 0]

АБО

Array(6).fill(0);
// [0, 0, 0, 0, 0, 0]

Примітка: ви не можете зациклювати порожні слоти, тобто Array(4).forEach(() => …)


АБО

( безпечний друкарський текст )

Array(6).fill(null).map((_, i) => i);
// [0, 1, 2, 3, 4, 5]

АБО

Класичний метод за допомогою функції (працює в будь-якому браузері)

function NewArray(size) {
    var x = [];
    for (var i = 0; i < size; ++i) {
        x[i] = i;
        return x;
    }
}

var a = NewArray(10);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Створення вкладених масивів

При створенні 2D масиву fillінтуїтивно слід створювати нові екземпляри. Але те, що насправді станеться, той самий масив буде зберігатися як посилання.

var a = Array(3).fill([6]);
// [  [6], [6], [6]  ]

a[0].push(9);
// [  [6, 9], [6, 9], [6, 9]  ]

Рішення

var a = [...Array(3)].map(x => []);

a[0].push(4, 2);
// [  [4, 2], [], []  ]

Отже, масив 3x2 буде виглядати приблизно так:

[...Array(3)].map(x => Array(2).fill(0));
// [  [0, 0], [0, 0], [0, 0]  ]

N-мірний масив

function NArray(...dimensions) {
    var index = 0;
    function NArrayRec(dims) {
        var first = dims[0], next = dims.slice().splice(1); 
        if(dims.length > 1) 
            return Array(dims[0]).fill(null).map((x, i) => NArrayRec(next ));
        return Array(dims[0]).fill(null).map((x, i) => (index++));
    }
    return NArrayRec(dimensions);
}

var arr = NArray(3, 2, 4);
// [   [  [ 0,  1,  2,  3 ] , [  4,  5,  6,  7]  ],
//     [  [ 8,  9,  10, 11] , [ 12, 13, 14, 15]  ],
//     [  [ 16, 17, 18, 19] , [ 20, 21, 22, 23]  ]   ]

Ініціалізуйте шахову дошку

var Chessboard = [...Array(8)].map((x, j) => {
    return Array(8).fill(null).map((y, i) => {
        return `${String.fromCharCode(65 + i)}${8 - j}`;
    });
});

// [ [A8, B8, C8, D8, E8, F8, G8, H8],
//   [A7, B7, C7, D7, E7, F7, G7, H7],
//   [A6, B6, C6, D6, E6, F6, G6, H6],
//   [A5, B5, C5, D5, E5, F5, G5, H5],
//   [A4, B4, C4, D4, E4, F4, G4, H4],
//   [A3, B3, C3, D3, E3, F3, G3, H3],
//   [A2, B2, C2, D2, E2, F2, G2, H2],
//   [A1, B1, C1, D1, E1, F1, G1, H1] ]

1
a[0].push(9); // [ [6, 9], [6], [6] ] повинно бути, a[0].push(9); // [ [6, 9], [6, 9], [6, 9] ] тому що кожен вкладений масив зберігається як довідковий, тому мутування одного масиву впливає на решту. Ви, можливо, просто помилково ввели його, хоча я думаю :)
Alex_Zhong

@Alex_Zhong tru, спасибі, втратив це в редакції
Влад

68

Найкоротший:

[...Array(1000)]

10
Запропонований, але один застереження, ви абсолютно не повинні починати рядок у JS з [без використання, ;оскільки це спробує знешкодити рядок вище. Тож безпечним способом передбачуваного використання цього рядка в будь-якій частині коду було б:;[...Array(1000)]//.whateverOpsNeeded()
AP.

не зовсім. спробуйте інструменти для розробників. [...Array(5)].map((item, index) => ({ index })) спробуйте це також:[...Array(5)]; console.log('hello');
Майкл Маммоліті

2
Спробуйте використовувати його у багаторядковому js-файлі. Якщо ваш перший рядок const a = 'a'і наступний [...Array(5)].//irrelevent. Як ви думаєте, що вирішить перший рядок? Це призвело const a = 'a'[...Array(5)]б до:Uncaught SyntaxError: Unexpected token ...
AP.

7
Це правда, але в цьому випадку я б сказав, що проблема десь в іншому місці, крапки з комою та підшивка є важливими в наші дні.
Майкл Маммоліті

8
@AP саме цей приклад - це саме та причина, що багато стильових посібників наказують закінчувати кожну заяву крапкою з комою. const a = 'a'в цьому випадку буде помилка Lint. У всякому разі, це дійсно не має нічого спільного з цією відповіддю.
грауп

41

ES6 представляє, Array.fromщо дозволяє створювати Arrayбудь-які "масиви" або ітерабельні об'єкти:

Array.from({length: 10}, (x, i) => i);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

У цьому випадку {length: 10}представлено мінімальне визначення об'єкта "схожий на масив" : порожній об'єкт із визначеним просто lengthвластивістю.

Array.from дозволяє другий аргумент для відображення отриманого масиву.


2
@dain Я думаю , що [... Array (10)] краще stackoverflow.com/a/47929322/4137472
Олександр Shutau

1
Чому краще? Використання конструктора Array перешкоджає, і деякі лінтери також можуть поскаржитися
Крістіан Трайна

26

Це ініціалізує властивість довжини до 4:

var x = [,,,,];

3
Це розумно. Він не має гнучкості конструктора, тому що ви не можете використовувати змінну для встановлення довжини, але він відповідає на моє запитання, як я спочатку його фразував, тому +1.
Майкл Мартін-Смукер

12
Уявіть, що для 300 предметів продуктивність справді має значення!
Marco Luglio

9
При попередньому розміщенні масиву з таким невеликим розміром, мабуть, не надто велике підвищення продуктивності. Різниця в продуктивності буде краще сприйнята при створенні великих масивів. І в таких випадках ініціалізація комами була б трохи непосильною.
Marco Luglio

6
Я думаю , що це буде давати різні результати в різних браузерах з - за остаточною коми, іноді 4, іноді 5 см stackoverflow.com/questions/7246618 / ...
jperelli

1
Як щодо var size = 42; var myArray = eval("["+",".repeat(size)+"]");? (Не так вже й серйозно;)
xoxox

15

Я здивований, що не було запропоновано функціональне рішення, яке дозволяє встановлювати довжину в одному рядку. На основі UnderscoreJS:

var test = _.map(_.range(4), function () { return undefined; });
console.log(test.length);

З причин, зазначених вище, я б уникав цього робити, якщо б не хотів ініціалізувати масив до певного значення. Цікаво відзначити, що є й інші бібліотеки, які реалізують діапазон, включаючи Lo-dash і Lazy, які можуть мати різні характеристики продуктивності.


Чорна магія підкреслення тут справді не потрібна. Залежності схожі на цукор, спочатку він здається солодким, але перш ніж це знати, у вас діабет.
Ромен Вінсент

9

Будь ласка, люди поки не відмовляються від ваших старих звичок. Існує велика різниця в швидкості між розподілом пам'яті, а потім роботою з записами в цьому масиві (як і раніше), і розподілом його багато разів, коли масив зростає (що неминуче те, що система робить під капотом з іншими запропонованими методами) .

Нічого з цього не має значення, поки ви не захочете зробити щось круте з більшими масивами. Тоді це робить.

Бачачи, що в JS на даний момент все ще не існує можливості встановити початкову ємність масиву, я використовую наступне ...

var newArrayWithSize = function(size) {
  this.standard = this.standard||[];
  for (var add = size-this.standard.length; add>0; add--) {
   this.standard.push(undefined);// or whatever
  }
  return this.standard.slice(0,size);
}

Задіяні компроміси:

  • Цей метод займає стільки ж часу, скільки й інші для першого виклику функції, але дуже мало часу для пізніших викликів (якщо не вимагати більшого масиву).
  • standardМасив має постійно Залишає стільки простору , як найбільший масив ви просили.

Але якщо це відповідає тому, що ви робите, це може бути виграшним. Неформальні терміни ставить

for (var n=10000;n>0;n--) {var b = newArrayWithSize(10000);b[0]=0;}

з досить швидкою швидкістю (приблизно 50 мс для 10000, враховуючи, що при n = 1000000 знадобилося близько 5 секунд), і

for (var n=10000;n>0;n--) {
  var b = [];for (var add=10000;add>0;add--) {
    b.push(undefined);
  }
}

приблизно за хвилину (приблизно 90 сек за 10000 на тій же хромованій консолі, або приблизно в 2000 разів повільніше). Це буде не лише розподіл, а й 10000 натискань для циклу тощо.


якщо кожен раз, коли ви доходите до кінця масиву, ви дублюєте його, складність вставки n значень все одно O (n), тому різниці в складності немає (але це повільніше).
Томер Волберг

Добре зловив @TomerWolberg, я змінив своє формулювання. Справа в тому, чи має метод push більш високу складність, ніж ініціалізація / копія окремого члена за фрагментом. AFAIK, що вони обоє постійний час, принаймні вони можуть бути, тому я використав слово "швидкість".
wils

8

(це, мабуть, було краще як коментар, але вийшло занадто довго)

Отже, прочитавши це, мені було цікаво, чи попереднє виділення насправді було швидшим, бо теоретично це повинно бути. Однак цей блог дав кілька порад, що радили проти нього http://www.html5rocks.com/en/tutorials/speed/v8/ .

Так що я все ще не впевнений, я перевіряю це. І як виявляється, це здається насправді повільніше.

var time = Date.now();
var temp = [];
for(var i=0;i<100000;i++){
    temp[i]=i;
}
console.log(Date.now()-time);


var time = Date.now();
var temp2 = new Array(100000);
for(var i=0;i<100000;i++){
    temp2[i] = i;
}
console.log(Date.now()-time); 

Після декількох випадкових запусків цей код дає наступне:

$ node main.js 
9
16
$ node main.js 
8
14
$ node main.js 
7
20
$ node main.js 
9
14
$ node main.js 
9
19

3
Цікаво, що здалося несподіваним, тому я зробив тест на JSPerf . Результати Chrome відповідають вашим тестам Node, але Firefox та IE трохи швидші, коли ви попередньо виділяєте простір для масиву.
Майкл Мартін-Смукер

1
@ MichaelMartin-Smucker Зачекайте ... це означає, що V8 насправді не повністю оптимізований та ідеальний?!?!? : P
Zeb McCorkle

1
@ MichaelMartin-Smucker - я щойно запустив ваш jsperf у Chrome версії 42.0.2311.90 (якщо бути конкретнішим - Тестування в Chrome 42.0.2311.90 32-розрядної версії 64-бітного Windows Server 2008 R2 / 7), а масив динамічного розміру був на 69% повільніше .
Єллен

1
Дітто. (Я написав оригінальний тест на вузол) У chrome 42.0.2311.90 на Windows динамічний розмір був на 81% повільніше :). Але наші оригінальні тести були вже рік тому. Час тримається на ковзанні, шпильці ....
j03m

1
Захоплююче ... з результатів на jsperf схоже, що попередньо виділений отримав величезний приріст у Chrome 38. Майбутнє!
Майкл Мартін-Смукер

8
var arr=[];
arr[5]=0;
alert("length="+arr.length); // gives 6

2
Так, однак, що console.log (arr) дає [5: 0]це рідкісний масив, і, ймовірно, не те, що потрібно.
dreftymac

7

Ось ще одне рішення

var arr = Array.apply( null, { length: 4 } );
arr;  // [undefined, undefined, undefined, undefined] (in Chrome)
arr.length; // 4

Перший аргумент apply()- це зв'язування цього об'єкта, про який ми тут не піклуємось, тому ми його встановили null.

Array.apply(..)викликає Array(..)функцію і поширює { length: 3 }значення об'єкта як його аргументи.


Я не розумію, чому ця відповідь отримала зворотний зв'язок. Це насправді гарний злом. Це означає, що ви не можете використовувати new Array(n)для ініціалізації такого масиву new Array(n).map(function(notUsed,index){...}), але при такому підході, як згадував @zangw, ви можете це зробити. +1 від мене.
Віктор.Палівода

3

Конструктор масиву має неоднозначний синтаксис , і JSLint все-таки шкодить вашим відчуттям.

Також ваш приклад код порушений, друге varтвердження підніме a SyntaxError. Ви встановлюєте властивість lengthмасиву test, тому немає потреби в іншому var.

Що стосується ваших варіантів, array.lengthце єдиний «чистий». Питання полягає в тому, навіщо вам в першу чергу встановлювати розмір? Спробуйте перефактурувати свій код, щоб позбутися цієї залежності.


Вуп, добре око на цю секунду var test. Це було якесь неохайне копіювання та вклеювання з мого боку.
Майкл Мартін-Смукер

@IvoWetzel, ви хочете встановити розмір для покращення продуктивності. JS-масиви - це динамічні масиви . Якщо ви не встановите розмір (а точніше, ємність), JS виділить масив розміру за замовчуванням. Якщо потім додати більше елементів, ніж вміститься, йому доведеться наростити масив, а це означає, що він внутрішньо виділить новий масив і потім скопіює всі елементи.
Домі

3

Якщо припустити, що довжина масиву є постійною. У Javascript це те, що ми робимо:

const intialArray = new Array(specify the value);


2

Як було пояснено вище, використовувати new Array(size)дещо небезпечно. Замість того, щоб використовувати його безпосередньо, помістіть його всередину "функції творця масиву". Ви можете легко переконатися, що ця функція відсутня помилок, і ви уникнете небезпеки зателефонувати new Array(size)безпосередньо. Також ви можете надати йому необов'язкове початкове значення за замовчуванням. Ця createArrayфункція робить саме так:

function createArray(size, defaultVal) {
    var arr = new Array(size);
    if (arguments.length == 2) {
        // optional default value
        for (int i = 0; i < size; ++i) {
            arr[i] = defaultVal;
        }
    }
    return arr;
}

1

У більшості відповідей рекомендується fillмасив, оскільки в іншому випадку "ви не можете перебрати його" , але це неправда. Ви можете повторити порожній масив, тільки не forEach. У той час як петлі, для циклів і петлі працюють добре.

const count = Array(5);

Не працює.

console.log('---for each loop:---');
count.forEach((empty, index) => {
    console.log(`counting ${index}`);
});

Ці роботи:

console.log('---for of loop:---');
for (let [index, empty] of count.entries()) {
  console.log(`counting for of loop ${index}`);
}

console.log('---for i loop:---');
for (let i = 0, il = count.length; i < il; ++i) {
  console.log(`counting for i loop ${i}`);
}

console.log('---while loop:---');
let index = 0;
while (index < count.length) { 
  console.log(`counting while loop ${index}`); 
  index++; 
}

Перевірте цю загадку з наведеними вище прикладами.

Також куточки *ngForдобре справляються з порожнім масивом:

<li *ngFor="let empty of count; let i = index" [ngClass]="
  <span>Counting with *ngFor {{i}}</span>
</li>

-1

Ви можете встановити довжину масиву, використовуючи array.length = youValue

Так було б

var myArray = [];
myArray.length = yourValue;

-3

Причина, яку ви не повинні використовувати new Array, демонструється цим кодом:

var Array = function () {};

var x = new Array(4);

alert(x.length);  // undefined...

Якийсь інший код міг би зіпсуватись зі змінною Array. Я знаю, що далеко не зрозуміло, що хтось писатиме такий код, але все ж ...

Крім того, як сказав Фелікс Кінг, інтерфейс трохи непослідовний, і це може призвести до появи дуже важких помилок.

Якщо ви хочете масив довжиною = x, заповнений невизначеним (як це new Array(x)було б), ви можете зробити це:

var x = 4;
var myArray = [];
myArray[x - 1] = undefined;

alert(myArray.length); // 4

48
Згідно з цим міркуванням, ви не повинні використовувати, alertі undefinedтому, що якийсь інший код може зіпсуватись з ними. Другий приклад менш читабельний, ніж new Array(4)і не дає того ж результату: jsfiddle.net/73fKd
Олексій Лебедєв

25
Ця відповідь химерна
Марко Демайо

6
Ви не можете уникнути людей, які возиться з глобальними об’єктами в JavaScript; просто не робіть цього чи використовуйте рамки, де це роблять люди.
Річард Коннамахер

У хромованому відтворенні на даний момент: j = новий масив (4); j. довжина; // результати в 4
Parris
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.