Обновление 2018-07-21: В течение долгого времени мне было стыдно за этот ответ, поэтому я думаю, что пришло время немного его подправить. Просто небольшой комментарий, уточнение и форматирование, чтобы помочь ускорить чтение излишне длинных и запутанных частей этого ответа.
КРАТКАЯ ВЕРСИЯ
Фактический ответ на вопрос
Как уже говорили другие, вы можете использовать delete
.
obj // {"foo": "bar"}
delete obj["foo"]
obj // {}
obj["foo"] // undefined
Эквивалент массива
Не delete
из массива. Вместо этого используйте Array.prototype.splice
.
arr // [1,2,3,4,5]
arr.splice(3,1); // 4
arr // [1,2,3,5]
ДЛИТЕЛЬНАЯ ВЕРСИЯ
JavaScript - это язык ООП, поэтому все является объектом, включая массивы . Таким образом, я считаю необходимым указать конкретную оговорку.
В массивах, в отличие от простых старых объектов, использование delete
оставляет мусор в виде null
, создавая «дыру» в массиве.
var array = [1, 2, 3, 4];
delete array[2];
/* Expected result --> [1, 2, 4]
* Actual result --> [1, 2, null, 4]
*/
Как видите, delete
не всегда работает так, как можно было ожидать. Значение перезаписывается, но память не перераспределяется. То есть array[4]
не перемещено в array[3]
. В отличие от Array.prototype.unshift
, который вставляет элемент в начало массива и сдвигает все вверх (array[0]
становится array[1]
и т. Д.)
Честно говоря, кроме установки null
, а не undefined
- что на самом деле странно - такое поведение не должно быть удивительным, поскольку delete
является унарным оператором, как typeof
, который жестко вписывается в язык и не должен заботиться о типе объекта, на котором он используется, тогда как Array
является подклассом Object
с методами специально предназначен для работы с массивами. Так что для delete
нет веской причины готовить специальный случай для повторного смещения массива, поскольку это просто замедлит работу с ненужной работой. Оглядываясь назад, мои ожидания были нереальными.
Конечно, это удивило меня . Потому что я написал это, чтобы оправдать свой крестовый поход против «нулевого мусора»:
Игнорирование опасностей и проблем, присущих null
, и пространства, потраченного впустую, это может быть проблематично, если массив должен быть точным.
Что является ужасным оправданием для избавления от null
s - null
, опасно только при неправильном использовании и не имеет ничего общего с «точностью». Реальная причина, по которой вы не должны delete
из массива, состоит в том, что оставление заполненных мусором и грязных структур данных вокруг является небрежным и подверженным ошибкам.
Ниже приведен надуманный сценарий, который становится довольно многословным, поэтому вы можете перейти к разделу Решение , если хотите. Единственная причина, по которой я покидаю этот раздел, заключается в том, что я думаю, что некоторые люди, вероятно, считают это смешным, и я не хочу быть «тем парнем», который публикует «смешной» ответ, а затем удаляет из него все «смешные» ,
... Это глупо, я знаю.
Придуманный и многословный сценарий PDP-11
Например, допустим, вы создаете веб-приложение, которое использует JSON-сериализацию для хранения массива, используемого для «вкладок» в строке (в данном случае localStorage
). Скажем также, что код использует числовые индексы элементов массива для «заголовка» их при рисовании на экране. Почему вы делаете это, а не просто сохраняете «заголовок»? Потому что ... причины .
Хорошо, давайте просто скажем, что вы пытаетесь сэкономить память по запросу этого одного пользователя, который запускает мини-компьютер PDP-11 из UNIX 1960-х годов и написал свой собственный JavaScript на основе Elinks совместимый с линейным принтером браузер, потому что X11 исключен .
За исключением все более и более глупого сценария с крайним регистром, использование delete
в указанном массиве приведет к null
загрязнению массива и, возможно, позже к ошибкам в приложении. И если вы проверите для null
, он будет пропускать цифры, в результате чего вкладки будут отображаться как [1] [2] [4] [5] ...
.
if (array[index] == null)
continue;
else
title = (index + 1).toString();
/* 0 -> "1"
* 1 -> "2"
* 2 -> (nothing)
* 3 -> "4"
*/
Да, это определенно не то, что вы хотели.
Теперь, вы могли бы сохранить второй итератор, например j
, для приращения только при считывании допустимых значений из массива. Но это точно не решит проблему null
, и вам все равно придется порадовать этого пользователя troll PDP-11. Увы, его компьютер просто не имеет достаточно памяти для хранения последнего целого числа (не спрашивайте, как ему удается обрабатывать массив переменной ширины ...) .
Итак, он отправляет вам письмо в гневе:
Hey, your webapp broke my browser! I checked my localStorage database after your stupid code made my browser segfault, and this is what I found:
>"tabs:['Hello World', 'foo bar baz', null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, ... ]"
After clearing my precious data, it segfaulted again, and I did a backtrace, and what do I find? WHAT DO I FIND!? YOU USE TOO MANY VARIABLES!
>var i = index;
>var j = 1;
Grr, I am angry now.
-Troll Davidson
Примерно сейчас ты в своем уме. Этот парень без конца жаловался на ваше приложение, и вы хотите сказать ему, чтобы он заткнулся и взял лучший компьютер.
Решение: Array.prototype.splice
К счастью, массивы do имеют специальный метод удаления индексов и перераспределения памяти: Array.prototype.splice()
. Вы могли бы написать что-то вроде этого:
Array.prototype.remove = function(index){
this.splice(index,1);
}
...
array = [1, 2, 3, 4];
array.remove(2);
// Result -> [1, 2, 4]
И именно так вы порадовали мистера PDP-11. Ура! (я бы все равно отчитал его ...)
Array.prototype.splice против Array.prototype.slice
Мне кажется, важно указать на разницу между этими двумя функциями с одинаковыми именами, поскольку они обе очень полезны.
Array.prototype.splice (начало, n)
.splice()
мутирует массив и возвращает удаленные индексы. Массив нарезается, начиная с индекса, элементы start
и n
нарезаются. Если n не указано, весь массив после start
выделяется (n = array.length - start
).
let a = [5,4,3,2,1];
let chunk = a.splice(2,2);
// a [5,4,3,2,1]
// start 0 1 2 - -
// n - - 1 2 -
chunk; // [3,2]
a; // [5,4,1]
Array.prototype.slice (начало, конец)
.slice()
является неразрушающим и возвращает новый массив, содержащий указанные индексы, от start
до end
. Если end
не указан, поведение аналогично .splice()
(end = array.length
). Поведение немного сложнее, поскольку по какой-то причине end
индексирует от 1 вместо 0. Я не знаю, почему это происходит, но так оно и есть. Также, если end <= start
, результатом будет пустой массив.
let a = [5,4,3,2,1];
let chunks = [
a.slice(2,0),
a.slice(2,2),
a.slice(2,3),
a.slice(2,5) ];
// a [5,4,3,2,1]
// start 0 1 2 - -
// end, for... - - - - -
// chunks[0] 0 - - - - -
// chunks[1] 1 2 - - -
// chunks[2] 1 2 3 - -
// chunks[3] 1 2 3 4 5
chunks; // [ [], [], [3], [3,2,1] ]
a; // [5,4,3,2,1]
На самом деле это не то, что происходит, но так проще думать. Согласно MDN, вот что на самом деле происходит:
// a [5,4,3,2,1]
// start 0 1 2 - - -
// end, for... - - - - - -
// chunks[0] 0 - - - - -
// chunks[1] 0 1 2 - - -
// chunks[2] 0 1(2)3 - -
// chunks[3] 0 1(2 3 4)5
Индекс, указанный end
, просто исключается из среза. Индексы в скобках указывают на то, что вырезано. В любом случае, поведение не является интуитивно понятным, и оно неизбежно вызовет значительную долю ошибок «один на один», поэтому может оказаться полезным создать функцию-обертку, чтобы более точно имитировать поведение .splice()
:
function ez_slice(array, start = 0, n = null){
if(!Array.isArray(array) || !is_number(start))
return null;
if(is_number(n))
return array.slice(start, start + n);
if(n === null)
return array.slice(start);
return null;
}
ez_slice([5,4,3,2,1], 2, 1) // [3]
ez_slice([5,4,3,2,1], 2) // [3,2,1]
/* Fun fact: isNaN is unreliable.
* [NaN, [], {}, 0, 1, Infinity, undefined, null, "Hi"].filter(isNaN)
* [NaN, {}, undefined, "Hi"]
*
* What we want is...
*
* [NaN, [], {}, 0, 1, Infinity, undefined, null, "Hi"].filter(is_nan)
* [NaN]
*/
function is_nan(num){
return typeof num === "number"
&& num !== num;
}
function is_number(num){
return !is_nan(num)
&& typeof num === "number"
&& isFinite(num);
}
Обратите внимание, что функция-обертка разработана очень строго к типам и возвращает null
, если что-то отключено. Это включает в себя строку типа "3"
. Программист должен быть усердным в своих типах. Это должно поощрять хорошую практику программирования.
Обновление относительно is_array()
Это относится к этому (теперь удаленному) фрагменту:
function is_array(array){
return array !== null
&& typeof array === "object"
&& typeof array.length !== "undefined"
&& array.__proto__ === Array.prototype;
}
Итак, как выясняется, на самом деле есть встроенный способ определить, является ли массив действительно массивом, и это Array.isArray()
, введенное в ECMAScript 5 (декабрь 2009 г.). Я нашел это, когда смотрел, нет ли вопроса о том, как сказать массивы от объектов, чтобы узнать, есть ли лучшее решение, чем у меня, или добавить мое, если его нет. Так что, если вы используете версию JavaScript, более раннюю, чем ECMA 5, есть ваш polyfill. Однако я настоятельно рекомендую не использовать мою функцию is_array()
, поскольку продолжать поддерживать старые версии JavaScript означает продолжать поддерживать старые браузеры, которые их реализуют, то есть поощрять использование небезопасного программного обеспечения и подвергать пользователей риску вредоносного ПО. Поэтому, пожалуйста, используйте Array.isArray()
. Используйте let
и const
. Используйте новые функции, которые добавляются в язык. Не использовать префиксы поставщиков. Удалите , что IE polyfill дерьмо с вашего сайта. Удалите и эту хрень XHTML <!CDATA[[...
- мы перешли на HTML5 еще в 2014 году. Чем раньше все откажутся от поддержки этих старых / эзотерических браузеров, тем скорее производители браузеров фактически последуют веб-стандарту и примут новую технологию раньше мы сможем перейти к более безопасной сети.