Чи повинні негативні індекси в масивах JavaScript сприяти довжині масиву?


84

У javascript я визначаю такий масив

var arr = [1,2,3];

також я можу це зробити

arr[-1] = 4;

Тепер якщо я це зроблю

arr = undefined;

Я також втрачаю посилання на значення в arr [-1] .

ТАК для мене логічно здається, що arr [-1] також є частиною arr .

Але коли я роблю підписку (без встановлення arr на undefined)

arr.length;

Повертає 3, а не 4 ;

Отже, мій сенс полягає в тому, що якщо масиви можна використовувати з негативними індексами, ці негативні індекси також повинні бути частиною їх довжини **. Я не знаю, можливо, я помиляюся, або мені не вистачає якогось поняття про масиви.


Чому ви хочете використовувати негативний індекс? Я не бачу сенсу, якщо чогось не пропускаю.
elclanrs

11
Ви також можете писати, arr[1.5] = 1і це також не впливає на довжину. Мовна специфікація чітко визначає, що впливає на довжину. Можливо, вам це не сподобається, але з цим доведеться жити. Або це, або розробіть свою власну конкуруючу мову і переконайте людей перейти на неї.
Реймонд Чен,

1
@DigvijayYadav: Це може розглядатися як недолік або особливість, як і багато інших речей у JavaScript. Це тому, що масиви поводяться трохи як об’єкти, ви також можете використовувати рядок, var a = []; a['foo'] = 'baz'але це не означає, що ви повинні; це явно проти всіх конвенцій.
elclanrs

2
Хлопці, головне тут полягає в тому, що ARRAYS - ОБ’ЄКТИ. Різниці немає. Це причина такої поведінки, і вона цілком навмисна, навіть якщо вона вам не подобається. Просто почекайте, поки ви дізнаєтесь, що ВСІ числа представлені як плаваючі крапки, навіть цілі числа. JavaScript є цікавою мовою ...
Tony R

2
Детальну інформацію про алгоритм, який визначає налаштування елементів масиву, можна знайти в специфікації: es5.github.com/#x15.4.5.1.(особливо крок 4), а "індекс масиву" визначено тут: es5.github.com /#x15.4 .
Фелікс Клінг,

Відповіді:


109

ТАК для мене логічно здається, що arr [-1] також є частиною arr.

Так, але не так, як ти думаєш.

Ви можете призначити масиву довільні властивості (як і будь-який інший Об’єкт у JavaScript), що і робите, коли ви “індексуєте” масив -1і призначаєте значення. Оскільки це не член масиву, а лише довільна властивість, ви не повинні розраховувати lengthна розгляд цієї властивості.

Іншими словами, наступний код робить те саме:

var arr = [1, 2, 3];

​arr.cookies = 4;

alert(arr.length) // 3;

31
Гаразд, це означає, що негативні індекси насправді не діють як реальні індекси.
me_digvijay

4
Правильно, це просто довільні властивості масиву.
Ендрю Вітакер,

Але що, якщо я хочу використовувати позитивні індекси як властивості, не схожі на індекси, і я не хочу, щоб вони сприяли довжині масиву?
me_digvijay

7
Тоді не використовуйте масив, використовуйте звичайний ol 'об'єкт. Але в такому випадку ви втратите вбудоване lengthмайно.
Ендрю Вітакер,

2
@Digvijay: Консоль відображає лише властивості з позитивними цілими іменами, оскільки (я припускаю) вона просто робить звичайний forцикл від 0до .length. Зробити, console.dir(arr)щоб побачити повний об’єкт. Крім того, використання квадратних нотацій для доступу до масивів не є характеристикою масивів, це невід’ємна характеристика об’єктів ( var obj = {foo: 'bar'}; obj.foo or obj['foo']).
Фелікс Клінг,

25

lengthВластивість повертає число на одиницю більше , ніж найвищий призначений «індекс», де масив «індекси» є цілими числами більше або дорівнює нулю. Зверніть увагу, що JS дозволяє "розріджені" масиви:

var someArray = [];
someArray[10] = "whatever";
console.log(someArray.length); // "11"

Звичайно, якщо немає елементів, тоді lengthє 0. Зауважте також, що lengthне оновлюється, якщо ви використовуєте deleteдля видалення найвищого елемента.

Але масиви є об’єктами, тому ви можете призначати властивості з іншими довільними іменами властивостей, включаючи від’ємні числа або дроби:

someArray[-1] = "A property";
someArray[3.1415] = "Vaguely Pi";
someArray["test"] = "Whatever";

Зверніть увагу, що за лаштунками JS перетворює імена властивостей у рядки, навіть коли ви вводите число типу -1. (Позитивні цілі індекси також стають рядками, з цього приводу.)

Методи масивів, наприклад .pop(), .slice()тощо, працюють лише з цілими "індексами" з нулем чи вищим, а не з іншими властивостями, тому lengthє узгодженим на цьому етапі.


Дякую, ваша відповідь дуже допомогла. Також я спробував метод сплайсингу з негативним індексом, і виявив, що якщо я використовую -1, він переходить до останнього елемента масиву, а -2 переходить до другого останнього елемента тощо.
me_digvijay

+1. Довжина не говорить вам, скільки елементів чи властивостей. Це просто найвищий показник + 1. наприклад, заданий var a = new Array(9), aмає довжину 9 і взагалі не має елементів.
RobG

1
@DigvijayYadav - Так, Array.slice()метод повинен це зробити. String.slice()Метод робить те ж саме.
nnnnnn

7

Зверніть увагу, що коли ви використовуєте індекс позиції (або 0), значення розміщуються в масиві:

var array = [];

array[0] = "Foo";
array[1] = "Bar";

// Result: ["Foo", "Bar"]
// Length: 2

Це не так, коли ви додаєте значення без індексу (не 0-9 +):

var array = [];

array[0]  = "Foo";
array[1]  = "Bar";
array[-1] = "Fizzbuzz"; // Not a proper array index - kill it

// Result: ["Foo", "Bar"]
// Length: 2

Значення розміщуються в масиві лише тоді, коли ви граєте за правилами. Коли ви цього не робите, їх не приймають. Однак вони приймаються на самому об'єкті Array, як це буває практично з будь-чим у JavaScript. Незважаючи на те ["Foo", "Bar"], що це єдині значення в нашому масиві, ми все одно можемо отримати доступ до "Fizzbuzz":

array[-1]; // "Fizzbuzz"

Але зауважте ще раз, що це не є частиною значень масиву, оскільки його "індекс" не дійсний. Натомість він був доданий до масиву як просто ще один член. Ми могли отримати доступ до інших членів масиву таким же чином:

array["pop"]; // function pop() { [native code] }

Тут зауважте, що ми отримуємо доступ до popметоду в масиві, який повідомляє нам, що він містить власний код. Ми не отримуємо доступ до жодного зі значень масиву за допомогою клавіші "pop", а скоріше до елемента самого об'єкту масиву. Ми можемо ще більше підтвердити це, проїжджаючи по громадських членах об'єкта:

for (var prop in array) 
    console.log(prop, array[prop]);

Що випльовує наступне:

 0 Foo
 1 Bar
-1 Fizzbuzz

Так знову ж , це на на об'єкті , але не в в масиві .

Чудове запитання! Змусив мене зробити подвійний знімок напевно.


1
+1 Навіть у специфікації зазначено, що властивості з позитивним числовим ім’ям властивості називаються елементами масиву (а будь-яке інше властивість - ні): es5.github.com/#x15.4 .
Фелікс Клінг,

Результат не такий: ["Foo", "Bar"]але ["Foo", "Bar", -1: "Fizzbuzz"]перевірте тут скрипку . Як і ви писали, це стає властивістю з ключем -1, але це точно видно у вихідних даних. В іншому випадку ваша відповідь буде приємною та повною. Якщо ви зміните, я знову прийму голос, мій голос був жорстким, але не можу його зняти; час закінчився.
в’янення

5

Якщо ви дійсно хочете, щоб у довжину були включені негативні індекси та інші індекси, створіть функціонал самостійно:

function getExtendedArray() {
    var a = [];

    Object.defineProperty(a, 'totalLength', { 
        get : function() { return Object.keys(a).length; }
    });

    return a;
}

Тоді, наприклад:

var myArray = getExtendedArray();
console.log(myArray.totalLength); // 0
myArray.push("asdf");
myArray[-2] = "some string";
myArray[1.7777] = "other string";
console.log(myArray.totalLength); // 3

І якщо ви хочете, щоб довжина включала лише пронумеровані індекси / властивості, ви можете додати оператор if для ключа - if(Number(key) != NaN) length++;Якщо ви хочете відфільтрувати плаваючі, додайте && key % 1 == 0до умови
Боннєв

4

Масив - це лише особливий вид Об’єкта в JS. Існує спеціальний синтаксис, який є хорошим, оскільки він дозволяє нам ефективно з ними працювати. Уявіть, як було б нудно писати літерал масиву таким чином:

const array = {0: 'foo', 1: 'bar', 2: 'baz'} //etc...

Але вони все-таки є об'єктами . Отже, якщо ви це зробите:

const myArray = [42, 'foo', 'bar', 'baz'];

myArray[-1] = 'I am not here';
myArray['whatever'] = 'Hello World';

Ці нецілі властивості будуть приєднані до об'єкта (яким є масив), але вони не доступні за допомогою деяких власних методів, таких як Array.length.

Ви можете припустити, що власний метод 'length' - це геттер, який підраховує всі властивості (а індекси - це властивості, тоді як значення - це фактичні значення ), конвертовані в додатне ціле число або нуль. Щось на зразок цієї псевдореалізації:

Завдяки специфікаціям , власний lengthметод - як getter ( exampleArray.length) - перевірить усі ключі `` невід'ємне ціле число менше 2 32 '', отримає найбільший і поверне його числове значення + 1.

Як сеттер ( exampleArray.length = 42) він створить або кілька порожніх слотів для `` відсутніх '' індексів, якщо задана довжина перевищує фактичну кількість цілочисельних невід'ємних ключів, або видалить усі цілісні негативні цілі (та їх значення), які не більші за задана довжина.

Псевдореалізація:

const myArray = {
  '-1': 'I am not here', // I have to use apostrophes to avoid syntax error
  0: 42,
  1: 'foo',
  2: 'bar',
  3: 'baz',
  whatever: 'Hello World',

  get myLength() {
    let highestIndex = -1;
    for (let key in this) {
      let toInt = parseInt(key, 10);
      if (!Number.isNaN(toInt) && toInt > -1) {
        // If you're not an integer, that's not your business! Back off!
        if (toInt > highestIndex) highestIndex = toInt;
      }
    }
    return highestIndex + 1;
  },
  set myLength(newLength) {
    /* This setter would either:
      1) create some empty slots for 'missing' indices if the given length is greater than the actual number of non-negative-integer keys
      2) delete all non-negative-integer keys (and their values) which are not greater then the given length.
    */
  }
}

console.log(myArray.myLength); // 4

myArray[9] = 'foobar';

console.log(myArray.myLength); // 10


3

Масиви в JavaScript насправді є об’єктами. Вони просто прототипуються з конструктора Array.

Індекси масивів насправді є ключами в хеш-капі, і всі ключі перетворюються на рядки. Ви можете створити будь-який ключ (тобто "-1"), але методи в Array призначені для дії як масив. Отже length, не є розмір об’єкта, він, швидше за все, лише більший, ніж найбільший цілочисельний індекс. Аналогічним чином, друк arrбуде відображати лише значення із цілими клавішами> = 0.

Більше інформації тут.


точно. або ми можемо почати дивуватися, чому array ['foo'] не є дійсним індексом - javascript дозволяє це робити, але це не означає, що визначення того, що масив (та його елементи) змінюється з однієї мови на іншу. працюючи за призначенням.
два повітряні кулі

Насправді "масиви" в JS не існують. Але "асоціативні масиви", також відомі як "об'єкти", є основою JS. Отже, це не був величезний стрибок, щоб зробити об’єкт Array, який імітує звичайний масив.
Tony R

1
Існує одна особлива різниця між масивами та звичайними об’єктами: властивість саморегулювання довжини.
RobG

1

Згідно з MDN:

Значенням властивості length є ціле число з додатним знаком і значенням менше 2 до 32 степеня (232)

У Javascript ви можете встановити властивість для будь-якого створеного вами об'єкта.

var array = new Array();
array = [1,2,3];
array["boom"] = "pow";

Таким же чином, коли ви встановлюєте негативний індекс, він зберігає його як властивість масиву, а не як частину індексу.

array[-1] = "property does not add to array length";

Ось чому довжина цього не відображає, але цикл for..in це показує.


-1

Коли ви використовуєте непозитивні або нечислові індекси, масив поводиться як асоціативний масив, який має пари ключ-значення.

Для перебору масиву, який ви можете використовувати

for(var index in myArray) {
  document.write( index + " : " + myArray[index] + "<br />");
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.