JavaScript “yeni Dizi (n)” ve “Array.prototype.map” tuhaflığı


209

Bunu Firefox-3.5.7 / Firebug-1.5.3 ve Firefox-3.6.16 / Firebug-1.6.2'de gözlemledim

Firebug'u ateşlediğimde:

var x = new Array(3)
console.log(x) 
// [undefined, undefined, undefined]

var y = [undefined, undefined, undefined]
console.log(y) 
// [undefined, undefined, undefined]

console.log( x.constructor == y.constructor) // true

console.log( 
  x.map(function() { return 0; })
)
// [undefined, undefined, undefined]

console.log(
  y.map(function() { return 0; })
)
// [0, 0, 0]

Burada neler oluyor? Bu bir hata mı, yoksa nasıl kullanılacağını yanlış anlıyor muyum new Array(3)?


Dizi değişmez gösteriminden gördüğünüz sonuçları almıyorum. Ben hala 0 yerine undefined olsun. Ben sadece gibi bir şey ayarladıysanız 0 sonucu var y = x.map(function(){return 0; });almak ve hem yeni Array () yöntemi hem de dizi değişmezi için olsun. Firefox 4 ve Chrome'da test ettim.
RussellUresti

Ayrıca Chrome'da baskın, bu bir dilde tanımlanmış olabilir, ancak bu bir anlam ifade etmiyor, bu yüzden gerçekten ummuyorum
Hashbrown

Yanıtlar:


125

Görünüşe göre ilk örnek

x = new Array(3);

Tanımlanmamış işaretçiler içeren bir dizi oluşturur.

İkincisi, 3 tanımsız nesneye işaretçiler içeren bir dizi oluşturur, bu durumda kendilerinin işaretçileri tanımlanmamış DEĞİLDİR, sadece işaret ettikleri nesnelerdir.

y = [undefined, undefined, undefined]
// The following is not equivalent to the above, it's the same as new Array(3)
y = [,,,];

Harita dizideki nesneler bağlamında çalıştırılırken, ikinci haritanın işlevi çalıştırmayı başaramadığını, ikinci haritanın çalışmayı başaramadığına inanıyorum.


86
Kaynaktan MDC (vurgu benim): " mapamacıyla bir dizi içinde her bir elemanı, bir kez bir mesafede geri arama işlevini çağırır ve sonuçlarından yeni bir dizi oluşturur. callbackYalnızca değerlerine sahiptir dizi indeksleri için çağrılır , aynı çağrılır silinmiş veya hiç değer atanmamış dizinler için. " Bu durumda, xdeğerlerine açıkça değerler yatanmazken, değer olsa bile değerleri atanmıştır undefined.
Martijn

2
Öyleyse bir JavaScript'in tanımsız bir işaretçi mi yoksa tanımlanmamış bir işaretçi mi olduğunu kontrol etmek imkansız mı? Yani (new Array(1))[0] === [undefined][0].
Trevor Norris

Tanımlanmamış bir dizi, bir işaretçi dizisinden tanımsız nesnelere farklıdır. Undefine dizisi bir null değer dizisi, [null, null, null] gibi olurken, undefined'a işaret eden bir dizi dizisi [343423, 343424, 343425] null ve null ve null değerlerini gösterir. İkinci çözümde bellek adreslerini gösteren gerçek işaretçiler bulunurken ilk çözüm hiçbir yere işaret etmiyor. JS'nin başarısız olması muhtemelen bir tartışma konusudur, ama burada değil;)
David Mårtensson

4
@TrevNorris, kendisi bir hata hasOwnPropertyolmadığı sürece bunu kolayca test edebilirsiniz hasOwnProperty: (new Array(1)).hasOwnProperty(0) === falseve [undefined].hasOwnProperty(0) === true. Aslında, aynısını şu şekilde yapabilirsiniz in: 0 in [undefined] === trueve 0 in new Array(0) === false.
Mayıs 15:46

3
JavaScript'te "tanımsız işaretçiler" hakkında konuşmak sorunu karıştırır. Aradığınız terim "seçimler" dir . x = new Array(3);eşittir x = [,,,];, değil x = [undefined, undefined, undefined].
Matt Kantor

118

Ben sadece dizi uzunluğunu biliyordu ve öğeleri dönüştürmek için gereken bir görev vardı. Ben böyle bir şey yapmak istedim:

let arr = new Array(10).map((val,idx) => idx);

Hızlı bir şekilde böyle bir dizi oluşturmak için:

[0,1,2,3,4,5,6,7,8,9]

Ama işe yaramadı çünkü: (yukarıdaki Jonathan Lonowski'nin cevabına bakınız)

Çözüm, Array.prototype.fill () kullanarak dizi öğelerini herhangi bir değerle (tanımsız olsa bile) doldurmak olabilir

let arr = new Array(10).fill(undefined).map((val,idx) => idx);

Güncelleme

Başka bir çözüm şunlar olabilir:

let arr = Array.apply(null, Array(10)).map((val, idx) => idx);

console.log(Array.apply(null, Array(10)).map((val, idx) => idx));


28
sizi kayda değer durumuna gerekmez undefinediçinde .fill()çok hafif kodu basitleştirilmesi, yönteminlet arr = new Array(10).fill().map((val,idx) => idx);
Yann Havvalar

Benzer şekilde kullanabilirsinizArray.from(Array(10))

84

ES6 ile [...Array(10)].map((a, b) => a)hızlı ve kolay bir şekilde yapabilirsiniz!


9
Önceden ES6 kullanabilirsiniz new Array(10).fill(). [...Array(10)]
İle

Büyük dizilerle yayılım sözdizimi sorunlar yaratır, bu nedenle kaçınmak daha iyidir

veya[...Array(10).keys()]
Chungzuwalla

25

ES6 çözümü:

[...Array(10)]

Yine de daktilo (2.3) üzerinde çalışmıyor


6
Array(10).fill("").map( ...benim için ne işe yaradığını daktilo yazıyor 2.9
ibex

19

Diziler farklı. Aradaki fark, new Array(3)üç uzunluğa sahip, ancak özellik [undefined, undefined, undefined]içermeyen bir dizi oluştururken, her biri bir değere sahip "0", "1" ve "2" adı verilen üç ve üç uzunluğa sahip bir dizi oluşturur undefined. Farkı inişleci kullanarak görebilirsiniz :

"0" in new Array(3); // false
"0" in [undefined, undefined, undefined]; // true

Bu, JavaScript'te herhangi bir yerel nesnenin var olmayan bir özelliğinin değerini almaya undefinedçalıştığınızda, var olmayan bir değişkene başvurmaya çalıştığınızda olduğu gibi (bir hata atmak yerine) döndürdüğünün biraz kafa karıştırıcı gerçeğinden kaynaklanmaktadır. ), özellik daha önce açık bir şekilde ayarlanmışsa elde ettiğinizle aynıdır undefined.


17

MDC sayfasından map:

[...] callbackyalnızca atanmış değere sahip dizinin dizinleri için çağrılır; [...]

[undefined]Aslında indekste ki (ler) üzerinde ayarlayıcı uygular mapoysa yineleme olacak new Array(1)sadece varsayılan değeri ile index (es) başlatır undefinedböylece mapatlama buna.

Bunun tüm yineleme yöntemleri için aynı olduğuna inanıyorum .


8

ECMAScript 6. baskı spesifikasyonunda.

new Array(3)yalnızca özelliği tanımlayın lengthve gibi dizin özelliklerini tanımlamayın {length: 3}. bkz. https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-len 9. Adım.

[undefined, undefined, undefined]dizin özelliklerini ve length özelliğini gibi tanımlar {0: undefined, 1: undefined, 2: undefined, length: 3}. bkz. https://www.ecma-international.org/ecma-262/6.0/index.html#sec-runtime-semantics-arrayaccumulation ElementList Adım 5.

yöntemler map, every, some, forEach, slice, reduce, reduceRight, filtertarafından indeksi özelliğini kontrol edecek Array HasPropertyiç yöntemine, bu yüzden new Array(3).map(v => 1)olmaz callback'inde çağırmak.

daha fazla ayrıntı için bkz. https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.map

Nasıl düzeltilir?

let a = new Array(3);
a.join('.').split('.').map(v => 1);

let a = new Array(3);
a.fill(1);

let a = new Array(3);
a.fill(undefined).map(v => 1);

let a = new Array(3);
[...a].map(v => 1);

Çok güzel bir açıklama.
Dheeraj Rao

7

Bunu açıklamanın en iyi yolunun Chrome'un bunu nasıl ele aldığına bakmak olduğunu düşünüyorum.

>>> x = new Array(3)
[]
>>> x.length
3

Yani gerçekte olan şey, yeni Array () öğesinin uzunluğu 3 olan, ancak değeri olmayan boş bir dizi döndürmesi. Çalıştırdığınızda nedenle, x.mapbir üzerinde teknik olarak boş dizide, ayarlanacak bir şey yok.

Firefox undefined, değerleri olmasa bile bu boş yuvaları 'doldurur' .

Bunun açıkça bir hata olduğunu düşünmüyorum, sadece neler olduğunu temsil etmenin zayıf bir yolu. Diyelim ki Chrome'un dizisi aslında hiçbir şey olmadığını gösteriyor çünkü "daha doğru".


4

Sadece bununla karşılaştı. Kullanabilmek uygun olurdu Array(n).map.

Array(3) kabaca verir {length: 3}

[undefined, undefined, undefined]numaralandırılmış özelliklerini oluşturur:
{0: undefined, 1: undefined, 2: undefined, length: 3}.

Map () uygulaması yalnızca tanımlanan özelliklere göre çalışır.


3

Hata değil. Array yapıcısının çalışması için bu şekilde tanımlanır.

MDC'den:

Dizi yapıcısıyla tek bir sayısal parametre belirttiğinizde, dizinin başlangıç ​​uzunluğunu belirtirsiniz. Aşağıdaki kod beş öğeden oluşan bir dizi oluşturur:

var billingMethod = new Array(5);

Array yapıcısının davranışı, tek parametrenin bir sayı olup olmadığına bağlıdır.

.map()Yöntem yalnızca ile belirtilmiştir değerleri olan dizinin yineleme elemanları içerir. Açık bir ataması bile, undefinedbir değerin yinelemeye dahil edilmeye uygun olarak kabul edilmesine neden olacaktır. Bu tuhaf görünüyor, ancak aslında undefinedbir nesnedeki açık bir özellik ile eksik bir özellik arasındaki fark :

var x = { }, y = { z: undefined };
if (x.z === y.z) // true

Nesnenin x"z" adında bir özelliği yyok ve nesne var. Ancak, her iki durumda da özelliğin "değeri" olduğu görülmektedir undefined. Bir dizide durum benzerdir: öğesinin değeri, lengthsıfırdan tüm öğelere örtük olarak bir değer ataması gerçekleştirir length - 1. Bu .map()nedenle işlev, Array yapıcısı ve sayısal bir bağımsız değişken ile yeni oluşturulan bir dizide çağrıldığında hiçbir şey yapmaz (geri arama çağrılmaz).


Kırılması tanımlandı mı? Her zaman undefinedsonsuza dek olacak üç öğeden oluşan bir dizi üretmek için tasarlanmıştır ?
Yörüngedeki Hafiflik Yarışları

Evet, bu "sonsuza dek" kısmı hariç doğrudur. Daha sonra öğelere değerler atayabilirsiniz.
Sivri

3
Bu yüzden x = []yerine kullanmalısınx = new Array()
Rocket Hazmat

3

Bunu bir diziyi değerlerle kolayca doldurmak için yapıyorsanız, tarayıcı destek nedenleriyle dolgu kullanamazsınız ve gerçekten bir for-loop yapmak istemiyorsanız, bunu da x = new Array(3).join(".").split(".").map(...boş bir dizi verebilirsiniz. Teller.

Oldukça çirkin söylemeliyim, ama en azından sorun ve niyet açıkça anlaşılıyor.


1

Soru neden olduğundan, bunun JS'nin nasıl tasarlandığıyla ilgisi var.

Bu davranışı açıklamak için düşünebileceğim 2 ana neden var:

  • Performans: Verilen x = 10000ve new Array(x)yapıcı diziyi undefineddeğerlerle doldurmak için 0 ile 10000 arasında döngüden kaçınmak akıllıca olacaktır .

  • Dolaylı olarak "tanımsız": Ver a = [undefined, undefined]ve b = new Array(2), a[1]ve b[1]de dönüp olacak undefinedama a[8]ve b[8]aynı zamanda dönecektir undefinedonlar menzil dışında olsanız bile.

Sonuç olarak, gösterim empty x 3, açıkça bildirilmedikleri undefinediçin undefinedzaten uzun bir değer listesi ayarlamayı ve görüntülemeyi önlemek için bir kısayoldur .

Not: Verilen dizi a = [0]ve a[9] = 9, console.log(a)geri döner (10) [0, empty x 8, 9], açıkça belirtilen iki değer arasındaki farkı döndürerek boşluğu otomatik olarak doldurur.


1

Diğer cevaplarda ayrıntılı olarak açıklanan nedenlerden dolayı Array(n).mapişe yaramıyor. Ancak, ES2015'te Array.frombir harita işlevi kabul eder:

let array1 = Array.from(Array(5), (_, i) => i + 1)
console.log('array1', JSON.stringify(array1)) // 1,2,3,4,5

let array2 = Array.from({length: 5}, (_, i) => (i + 1) * 2)
console.log('array2', JSON.stringify(array2)) // 2,4,6,8,10


0

İşte geçici bir çözüm olarak basit bir yardımcı program yöntemi:

Basit harita

function mapFor(toExclusive, callback) {
    callback = callback || function(){};
    var arr = [];
    for (var i = 0; i < toExclusive; i++) {
        arr.push(callback(i));
    }
    return arr;
};

var arr = mapFor(3, function(i){ return i; });
console.log(arr); // [0, 1, 2]
arr = mapFor(3);
console.log(arr); // [undefined, undefined, undefined]

Komple Örnek

İsteğe bağlı bir başlangıç ​​dizini belirtmeye de izin veren daha eksiksiz bir örnek (akıl sağlığı denetimleriyle):

function mapFor() {
var from, toExclusive, callback;
if (arguments.length == 3) {
    from = arguments[0];
    toExclusive = arguments[1];
    callback = arguments[2];
} else if (arguments.length == 2) {
    if (typeof arguments[1] === 'function') {
        from = 0;
        toExclusive = arguments[0];
        callback = arguments[1];
    } else {
        from = arguments[0];
        toExclusive = arguments[1];
    }
} else if (arguments.length == 1) {
    from = 0;
    toExclusive = arguments[0];
}

callback = callback || function () {};

var arr = [];
for (; from < toExclusive; from++) {
    arr.push(callback(from));
}
return arr;
}

var arr = mapFor(1, 3, function (i) { return i; });
console.log(arr); // [1, 2]
arr = mapFor(1, 3);
console.log(arr); // [undefined, undefined]
arr = mapFor(3);
console.log(arr); // [undefined, undefined, undefined]

Geri saymak

Geri aramaya iletilen dizini değiştirmek geriye doğru saymaya izin verir:

var count = 3;
var arr = arrayUtil.mapFor(count, function (i) {
    return count - 1 - i;
});
// arr = [2, 1, 0]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.