जावास्क्रिप्ट "नया ऐरे (एन)" और "एरे.प्रोटोटाइप.मैप" अजीबता


209

मैंने इसे Firefox-3.5.7 / Firebug-1.5.3 और Firefox-3.6.16 / Firebug-1.6.2 में देखा है

जब मैं फायरबग फायर करता हूं:

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]

यहाँ क्या चल रहा है? यह एक बग है, या मैं गलतफहमी है कि कैसे उपयोग करना है new Array(3)?


मुझे वही परिणाम नहीं मिलते हैं जो आप सरणी के शाब्दिक अंकन से देखते हैं। मैं अब भी 0. के बजाय अपरिभाषित हो जाता हूं। मुझे केवल 0 परिणाम मिलता है अगर मैं कुछ सेट करता हूं var y = x.map(function(){return 0; });, और मुझे यह नया एरे () विधि और सरणी शाब्दिक दोनों के लिए मिलता है। मैंने फ़ायरफ़ॉक्स 4 और क्रोम में परीक्षण किया।
रसेलवेलि

क्रोम में भी
हलचल

जवाबों:


125

ऐसा प्रतीत होता है कि पहला उदाहरण

x = new Array(3);

अपरिभाषित बिंदुओं के साथ एक सरणी बनाता है।

और दूसरा 3 अपरिभाषित वस्तुओं के संकेत के साथ एक सरणी बनाता है, इस मामले में वे स्वयं को इंगित नहीं अपरिभाषित कर रहे हैं, केवल वे वस्तुओं की ओर इशारा करते हैं।

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

जैसा कि सरणी में ऑब्जेक्ट्स के संदर्भ में नक्शा चलता है, मेरा मानना ​​है कि पहला नक्शा फ़ंक्शन को चलाने में विफल रहता है जबकि दूसरा चलाने के लिए प्रबंधित करता है।


86
से MDC (जोर मेरा): " mapएक सरणी में प्रत्येक तत्व, क्रम में के लिए एक बार एक प्रदान की कॉलबैक फ़ंक्शन को कॉल करने, और परिणाम से एक नई सरणी निर्माण करती है। callbackकेवल सरणी जो मूल्यों सौंपा है की अनुक्रमणिका शुरू होता है , यह लागू नहीं है उन अनुक्रमणिकाओं के लिए जिन्हें हटा दिया गया है या जिन्हें कभी भी असाइन नहीं किया गया है। " इस मामले में, xमूल्यों को स्पष्ट रूप से निर्दिष्ट नहीं किया गया है, जबकि मूल्य yनिर्धारित किए गए थे, भले ही उन्हें सौंपा गया हो undefined
मार्टिअन

2
तो क्या यह जावास्क्रिप्ट विफल है कि यह जांचना असंभव है कि क्या यह अपरिभाषित सूचक है, या अपरिभाषित करने के लिए सूचक है? मेरा मतलब है (new Array(1))[0] === [undefined][0]
ट्रेवर नॉरिस

ठीक है, अपरिभाषित की एक सरणी संकेत की एक सरणी से अपरिभाषित वस्तुओं से अलग है। अपरिभाषितों की एक सरणी शून्य मानों की एक सरणी की तरह होगी, [अशक्त, अशक्त, अशक्त] जबकि अनिर्दिष्ट बिंदुओं की एक सरणी [343423, 343424, 343425] की तरह होगी जो अशक्त और अशक्त और अशक्त की ओर इशारा करती है। दूसरे समाधान में वास्तविक पाइंट्स हैं जो मेमोरी एड्रेस की ओर इशारा करते हैं जबकि पहले कहीं इंगित नहीं करते हैं। यदि जेएस की असफलता संभवत: ओ चर्चा का विषय है, लेकिन यहां नहीं;)
डेविड मोर्टेंसन

4
@TrevNorris, आप आसानी से परीक्षण कर सकते हैं कि hasOwnPropertyजब तक कि hasOwnPropertyस्वयं बग न हो: (new Array(1)).hasOwnProperty(0) === falseऔर [undefined].hasOwnProperty(0) === true। वास्तव में, आप ठीक उसी के साथ कर सकते हैं in: 0 in [undefined] === trueऔर 0 in new Array(0) === false
स्क्वीड 314

3
जावास्क्रिप्ट में "अपरिभाषित संकेत" के बारे में बात करना समस्या को भ्रमित करता है। आप जिस शब्द की तलाश कर रहे हैं, वह "elisions" हैx = new Array(3);के बराबर है x = [,,,];, नहीं x = [undefined, undefined, undefined]
मैट कांटोर

118

मेरा एक काम था कि मैं केवल सरणी की लंबाई जानता था और वस्तुओं को बदलने की जरूरत थी। मैं कुछ इस तरह करना चाहता था:

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

इस तरह एक सरणी बनाने के लिए जल्दी से:

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

लेकिन यह काम नहीं किया क्योंकि: (ऊपर कुछ जवाब जोनाथन लोनोस्की का जवाब देखें)

Array.prototype.ill () का उपयोग करके किसी भी मूल्य (यहां तक ​​कि अपरिभाषित के साथ) सरणी आइटम को भरने के लिए समाधान हो सकता है

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

अपडेट करें

एक और उपाय हो सकता है:

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

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


28
ध्यान देने योग्य बात आपको undefinedइस .fill()विधि में बताने की आवश्यकता नहीं है , कोड को थोड़ा सरल करनाlet arr = new Array(10).fill().map((val,idx) => idx);
Yann Eves

इसी तरह आप उपयोग कर सकते हैंArray.from(Array(10))

84

ES6 के साथ, आप कर सकते हैं [...Array(10)].map((a, b) => a), त्वरित और आसान!


9
पूर्व ES6 आप उपयोग कर सकते हैं new Array(10).fill()। समान परिणाम[...Array(10)]
2

बड़े सरणियों के साथ प्रसार सिंटैक्स समस्याएं बनाता है ताकि बचने के लिए बेहतर है

या[...Array(10).keys()]
चुंगज़ुवाला

25

ES6 समाधान:

[...Array(10)]

टाइपस्क्रिप्ट (2.3) पर काम नहीं करता है, हालांकि


6
Array(10).fill("").map( ...क्या मेरे लिए टाइपस्क्रिप्ट 2.9 के साथ काम किया है
ibex

19

सरणियाँ अलग हैं। अंतर यह है कि new Array(3)तीन की लंबाई के [undefined, undefined, undefined]साथ एक सरणी बनाता है , लेकिन कोई गुण नहीं है, जबकि एक सरणी बनाता है जिसमें तीन और तीन गुणों की लंबाई होती है जिसे "0", "1" और "2" कहा जाता है, प्रत्येक का मान होता है undefined। आप inऑपरेटर का उपयोग करके अंतर देख सकते हैं :

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

यह थोड़ा भ्रमित करने वाले तथ्य से उपजा है कि यदि आप जावास्क्रिप्ट में किसी भी मूल वस्तु की गैर-मौजूद संपत्ति का मूल्य प्राप्त करने का प्रयास करते हैं, तो यह वापस आता है undefined(त्रुटि को फेंकने के बजाय, जैसा कि तब होता है जब आप किसी गैर-मौजूद चर का संदर्भ देने का प्रयास करते हैं ), जो वही है जो आपको मिलता है यदि संपत्ति पहले से निर्धारित की गई है undefined


17

इसके लिए MDC पृष्ठ से map:

[...] callbackकेवल उसी सरणी के अनुक्रमित के लिए आह्वान किया जाता है जिसने मूल्य निर्दिष्ट किया है; [...]

[undefined]वास्तव में इंडेक्स पर सेटर लागू करता है (इसलिए) mapयह पुनरावृति करेगा, जबकि new Array(1)बस इंडेक्स (एस) को डिफ़ॉल्ट मान के साथ आरंभ करता undefinedहैmap परिभाषित करता है यह स्केप कर सके।

मेरा मानना ​​है कि यह सभी पुनरावृत्ति विधियों के लिए समान है ।


8

ईसीएमएस्क्रिप्ट 6 वें संस्करण के विनिर्देश में।

new Array(3)केवल संपत्ति को परिभाषित करें lengthऔर जैसे सूचकांक गुणों को परिभाषित न करें {length: 3}। देख https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-len चरण 9 ।

[undefined, undefined, undefined]सूचकांक गुण और लंबाई की संपत्ति को परिभाषित करेगा {0: undefined, 1: undefined, 2: undefined, length: 3}। देख https://www.ecma-international.org/ecma-262/6.0/index.html#sec-runtime-semantics-arrayaccumulation ElementList चरण 5 ।

तरीकों map, every, some, forEach, slice, reduce, reduceRight, filterसरणी के द्वारा सूचकांक संपत्ति की जाँच करेगा HasPropertyआंतरिक विधि, इसलिएnew Array(3).map(v => 1) नहीं होगा कॉलबैक आह्वान।

अधिक विस्तार के लिए, देखें https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.map देखें

कैसे ठीक करना है?

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);

बहुत अच्छी व्याख्या।
धीरज राव

7

मुझे लगता है कि इसे समझाने का सबसे अच्छा तरीका यह है कि क्रोम इसे कैसे हैंडल करता है।

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

तो वास्तव में क्या हो रहा है कि नया ऐरे () एक खाली सरणी लौटा रहा है जिसकी लंबाई 3 है, लेकिन कोई मूल्य नहीं है। इसलिए, जब आप तकनीकी रूप से खाली सरणी x.mapपर चलते हैं , तो सेट होने के लिए कुछ भी नहीं होता है।

फ़ायरफ़ॉक्स सिर्फ 'उन खाली स्लॉट्स में' भरता है undefined भले ही इसका कोई मूल्य नहीं है।

मुझे नहीं लगता कि यह स्पष्ट रूप से एक बग है, जो कि चल रहा है उसका प्रतिनिधित्व करने का सिर्फ एक खराब तरीका है। मुझे लगता है कि क्रोम "अधिक सही" है क्योंकि यह दर्शाता है कि वास्तव में सरणी में कुछ भी नहीं है।


4

बस इसी में भाग गया। यह सुनिश्चित करने के लिए उपयोग करने में सक्षम होने के लिए सुविधाजनक होगा Array(n).map

Array(3) लगभग उपज {length: 3}

[undefined, undefined, undefined]क्रमांकित गुण बनाता है
{0: undefined, 1: undefined, 2: undefined, length: 3}:।

मानचित्र () कार्यान्वयन केवल परिभाषित गुणों पर कार्य करता है।


3

बग नहीं। एरे कंस्ट्रक्टर को काम करने के लिए परिभाषित किया गया है।

MDC से:

जब आप ऐरे कंस्ट्रक्टर के साथ एक एकल संख्यात्मक पैरामीटर निर्दिष्ट करते हैं, तो आप सरणी की प्रारंभिक लंबाई निर्दिष्ट करते हैं। निम्नलिखित कोड पाँच तत्वों की एक सरणी बनाता है:

var billingMethod = new Array(5);

ऐरे कंस्ट्रक्टर का व्यवहार इस बात पर निर्भर करता है कि सिंगल पैरामीटर एक संख्या है।

.map()विधि केवल सरणी कि स्पष्ट रूप से सौंपा मूल्यों पड़ा है की यात्रा तत्वों में शामिल हैं। यहां तक ​​कि स्पष्ट असाइनमेंट के undefinedकारण मान को पुनरावृत्ति में शामिल करने के लिए योग्य माना जाएगा। यह अजीब लगता है, लेकिन यह अनिवार्य रूप से undefinedएक वस्तु पर एक स्पष्ट संपत्ति और एक लापता संपत्ति के बीच का अंतर है:

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

ऑब्जेक्ट में x"z" नामक गुण नहीं है, और ऑब्जेक्ट yकरता है। हालांकि, दोनों मामलों में यह प्रतीत होता है कि संपत्ति का "मूल्य" है undefined। किसी सरणी में, स्थिति समान होती है: lengthशून्य से सभी तत्वों के लिए एक मूल्य असाइनमेंट निहितार्थ करता है length - 1.map()समारोह इसलिए कुछ भी (कॉलबैक फोन नहीं होगा) जब एक सरणी नव सरणी निर्माता और एक संख्यात्मक तर्क के साथ निर्माण किया पर कहा कि काम नहीं चलेगा।


इसे तोड़ा जाना परिभाषित किया गया है? यह तीन तत्वों की एक सरणी का निर्माण करने के लिए डिज़ाइन किया गया है जो हमेशा के undefinedलिए रहेगा ?
ऑर्बिट

हां, यह सही है, "हमेशा के लिए" भाग को छोड़कर। आप बाद में तत्वों को मान निर्दिष्ट कर सकते हैं।
पॉइन्टी

3
यही कारण है कि आप का उपयोग करना चाहिए है x = []के बजायx = new Array()
Hazmat रॉकेट

3

यदि आप इसे आसानी से मानों के साथ एक सरणी भरने के लिए कर रहे हैं, तो ब्राउज़र समर्थन कारणों के लिए भरण का उपयोग नहीं कर सकते हैं और वास्तव में फॉर-लूप नहीं करना चाहते हैं, तो आप भी कर सकते हैंx = new Array(3).join(".").split(".").map(... जो आपको खाली का एक सरणी देगा। तार।

काफी बदसूरत मैं कहना है, लेकिन कम से कम समस्या और इरादा काफी स्पष्ट रूप से संवाद कर रहे हैं।


1

चूंकि सवाल यह है कि, जेएस को कैसे डिजाइन किया गया था, इसके साथ यह करना है।

इस व्यवहार की व्याख्या करने के लिए मैं 2 मुख्य कारण सोच सकता हूं:

  • प्रदर्शन: दिए गए x = 10000और new Array(x)यह undefinedमान के साथ सरणी को भरने के लिए निर्माणकर्ता के लिए 0 से 10000 तक लूपिंग से बचने के लिए बुद्धिमान है ।

  • परोक्ष "अपरिभाषित": को a = [undefined, undefined]और b = new Array(2), a[1]और b[1]दोनों वापस आ जाएगी undefined, लेकिन a[8]और b[8]भी वापस आ जाएगी undefined, भले ही वे सीमा से बाहर कर रहे हैं।

अंत में, संकेतन empty x 3सेटिंग की एक लंबी सूची को प्रदर्शित करने और प्रदर्शित करने से बचने का एक शॉर्टकट है undefinedजो undefinedवैसे भी हैं क्योंकि उन्हें स्पष्ट रूप से घोषित नहीं किया गया है।

ध्यान दें: सरणी को देखते हुए a = [0]और a[9] = 9, स्पष्ट रूप से घोषित किए गए दो मूल्यों के बीच के अंतर को वापस करके अंतर को स्वचालित रूप से भरकर console.log(a)वापस आ जाएगा (10) [0, empty x 8, 9]


1

अन्य उत्तरों में अच्छी तरह से समझाया गया कारणों से, Array(n).mapकाम नहीं करता है। हालांकि, ES2015 में Array.fromएक नक्शा फ़ंक्शन स्वीकार करता है:

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

वर्कअराउंड के रूप में एक सरल उपयोगिता विधि यहां दी गई है:

सरल मानचित्र

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]

पूरा उदाहरण

यहाँ एक और अधिक पूर्ण उदाहरण है (स्वच्छता जांच के साथ) जो एक वैकल्पिक आरंभिक सूचकांक को निर्दिष्ट करने की अनुमति देता है:

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]

उल्टी गिनती

कॉलबैक में दिए गए इंडेक्स को जोड़कर पीछे की ओर गिनने की अनुमति देता है:

var count = 3;
var arr = arrayUtil.mapFor(count, function (i) {
    return count - 1 - i;
});
// arr = [2, 1, 0]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.