ฉันพบรหัสต่อไปนี้ในรายชื่อผู้รับจดหมาย es-discuss:
Array.apply(null, { length: 5 }).map(Number.call, Number);
สิ่งนี้ผลิต
[0, 1, 2, 3, 4]
เหตุใดจึงเป็นผลลัพธ์ของรหัส เกิดอะไรขึ้นที่นี่
ฉันพบรหัสต่อไปนี้ในรายชื่อผู้รับจดหมาย es-discuss:
Array.apply(null, { length: 5 }).map(Number.call, Number);
สิ่งนี้ผลิต
[0, 1, 2, 3, 4]
เหตุใดจึงเป็นผลลัพธ์ของรหัส เกิดอะไรขึ้นที่นี่
คำตอบ:
การทำความเข้าใจกับ "แฮ็ค" นี้ต้องมีความเข้าใจหลายสิ่ง:
Array(5).map(...)Function.prototype.applyการขัดแย้งจับArrayจัดการกับข้อโต้แย้งหลาย ๆNumberฟังก์ชั่นจัดการกับข้อโต้แย้งFunction.prototype.callไม่พวกเขาค่อนข้างหัวข้อขั้นสูงในจาวาสคริปต์ดังนั้นนี้จะยาวกว่าค่อนข้าง เราจะเริ่มจากด้านบน หัวเข็มขัดขึ้น!
Array(5).map?อาร์เรย์คืออะไรจริงเหรอ? วัตถุปกติที่มีคีย์จำนวนเต็มซึ่งจับคู่กับค่า มันมีคุณสมบัติพิเศษอื่น ๆ เช่นlengthตัวแปรเวทมนต์แต่ที่แกนกลางมันเป็นkey => valueแผนที่ปกติเหมือนกับวัตถุอื่น ๆ มาเล่นกับอาร์เรย์กันหน่อยสิ
var arr = ['a', 'b', 'c'];
arr.hasOwnProperty(0); //true
arr[0]; //'a'
Object.keys(arr); //['0', '1', '2']
arr.length; //3, implies arr[3] === undefined
//we expand the array by 1 item
arr.length = 4;
arr[3]; //undefined
arr.hasOwnProperty(3); //false
Object.keys(arr); //['0', '1', '2']
เราได้รับความแตกต่างโดยธรรมชาติระหว่างจำนวนของรายการในอาร์เรย์arr.lengthและหมายเลขของแมปอาร์เรย์มีซึ่งอาจจะแตกต่างกว่าkey=>valuearr.length
ขยายอาร์เรย์ผ่านarr.length ไม่ได้สร้างใหม่ ๆkey=>valueแมปดังนั้นก็ไม่ได้ว่าอาร์เรย์มีค่าไม่ได้กำหนดมันไม่ได้มีคีย์เหล่านี้ และจะเกิดอะไรขึ้นเมื่อคุณพยายามเข้าถึงทรัพย์สินที่ไม่มีอยู่จริง? undefinedคุณจะได้รับ
ตอนนี้เราสามารถยกหัวของเราขึ้นเล็กน้อยและดูว่าทำไมฟังก์ชั่นอย่างarr.mapอย่าเดินข้ามคุณสมบัติเหล่านี้ หากarr[3]ไม่ได้กำหนดเพียงอย่างเดียวและมีคีย์อยู่ฟังก์ชันอาร์เรย์ทั้งหมดเหล่านี้จะไปแทนที่ค่าอื่น ๆ :
//just to remind you
arr; //['a', 'b', 'c', undefined];
arr.length; //4
arr[4] = 'e';
arr; //['a', 'b', 'c', undefined, 'e'];
arr.length; //5
Object.keys(arr); //['0', '1', '2', '4']
arr.map(function (item) { return item.toUpperCase() });
//["A", "B", "C", undefined, "E"]
ฉันจงใจใช้การเรียกใช้เมธอดเพื่อพิสูจน์จุดที่คีย์ไม่เคยอยู่: การโทรundefined.toUpperCaseจะทำให้เกิดข้อผิดพลาด แต่ไม่ได้เกิดขึ้น เพื่อพิสูจน์ว่า :
arr[5] = undefined;
arr; //["a", "b", "c", undefined, "e", undefined]
arr.hasOwnProperty(5); //true
arr.map(function (item) { return item.toUpperCase() });
//TypeError: Cannot call method 'toUpperCase' of undefined
และตอนนี้เรามาถึงจุดของฉัน: Array(N)สิ่งต่าง ๆ ได้อย่างไร ส่วนที่ 15.4.2.2อธิบายกระบวนการ มีจัมโบ้จัมโบ้ขนาดใหญ่ที่เราไม่สนใจ แต่ถ้าคุณจัดการอ่านระหว่างบรรทัด (หรือคุณสามารถเชื่อใจฉันในอันนี้ แต่ไม่ทำ) มันจะเดือดลงไปที่นี่:
function Array(len) {
var ret = [];
ret.length = len;
return ret;
}
(ทำงานภายใต้สมมติฐาน (ซึ่งถูกตรวจสอบในสเป็คจริง) นั่นlenคือ uint32 ที่ถูกต้องและไม่ใช่แค่จำนวนค่าใด ๆ )
ดังนั้นตอนนี้คุณสามารถเห็นว่าทำไมการทำArray(5).map(...)ไม่ได้ผลเราไม่ได้กำหนดlenรายการในอาร์เรย์เราไม่สร้างการkey => valueแมปเราเพียงแค่เปลี่ยนlengthคุณสมบัติ
ตอนนี้เรามีวิธีการที่ให้ลองดูสิ่งที่มีมนต์ขลังที่สอง:
Function.prototype.applyทำงานอย่างไรapplyโดยทั่วไปแล้วอะไรคือการใช้อาเรย์และคลี่มันเป็นอาร์กิวเมนต์ของการเรียกใช้ฟังก์ชัน นั่นหมายความว่าสิ่งต่อไปนี้เหมือนกันมาก:
function foo (a, b, c) {
return a + b + c;
}
foo(0, 1, 2); //3
foo.apply(null, [0, 1, 2]); //3
ตอนนี้เราสามารถทำให้กระบวนการดูapplyง่ายขึ้นโดยการบันทึกargumentsตัวแปรพิเศษ:
function log () {
console.log(arguments);
}
log.apply(null, ['mary', 'had', 'a', 'little', 'lamb']);
//["mary", "had", "a", "little", "lamb"]
//arguments is a pseudo-array itself, so we can use it as well
(function () {
log.apply(null, arguments);
})('mary', 'had', 'a', 'little', 'lamb');
//["mary", "had", "a", "little", "lamb"]
//a NodeList, like the one returned from DOM methods, is also a pseudo-array
log.apply(null, document.getElementsByTagName('script'));
//[script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script]
//carefully look at the following two
log.apply(null, Array(5));
//[undefined, undefined, undefined, undefined, undefined]
//note that the above are not undefined keys - but the value undefined itself!
log.apply(null, {length : 5});
//[undefined, undefined, undefined, undefined, undefined]
มันง่ายที่จะพิสูจน์การอ้างสิทธิ์ของฉันในตัวอย่างที่สองถึงครั้งสุดท้าย:
function ahaExclamationMark () {
console.log(arguments.length);
console.log(arguments.hasOwnProperty(0));
}
ahaExclamationMark.apply(null, Array(2)); //2, true
(ใช่เล่นสำนวนเจตนา) การkey => valueแมปอาจไม่มีอยู่ในอาเรย์ที่เราส่งไปถึงapplyแต่มันมีอยู่ในargumentsตัวแปรอย่างแน่นอน มันเป็นเหตุผลเดียวกันกับตัวอย่างล่าสุดที่ใช้งานได้: ปุ่มไม่มีอยู่บนวัตถุที่เราผ่าน แต่มีอยู่ในargumentsนั้น
ทำไมถึงเป็นอย่างนั้น? ลองดูหัวข้อ 15.3.4.3ที่Function.prototype.applyกำหนดไว้ สิ่งที่เราไม่สนใจเป็นส่วนใหญ่ แต่นี่คือส่วนที่น่าสนใจ:
- ให้ len เป็นผลลัพธ์ของการเรียกเมธอดภายใน [[Get]] ของ argArray ด้วยอาร์กิวเมนต์ "length"
ซึ่งโดยทั่วไปหมายถึง: argArray.length. จากนั้นสเป็คจะทำการforวนซ้ำอย่างง่าย ๆ ของlengthไอเท็มทำให้มีlistค่าที่สอดคล้องกัน ( listเป็นของขึ้นภายในภายใน ในแง่ของรหัสที่หลวมมาก ๆ :
Function.prototype.apply = function (thisArg, argArray) {
var len = argArray.length,
argList = [];
for (var i = 0; i < len; i += 1) {
argList[i] = argArray[i];
}
//yeah...
superMagicalFunctionInvocation(this, thisArg, argList);
};
ดังนั้นสิ่งที่เราต้องเลียนแบบargArrayในกรณีนี้คือวัตถุที่มีlengthคุณสมบัติ และตอนนี้เราสามารถเห็นได้ว่าทำไมค่าจึงไม่ได้ถูกกำหนด แต่ไม่มีคีย์บนarguments: เราสร้างการkey=>valueแมป
ว้าดังนั้นสิ่งนี้อาจไม่สั้นกว่าส่วนก่อนหน้า แต่จะมีเค้กเมื่อเราเสร็จสิ้นดังนั้นจงอดทน! อย่างไรก็ตามหลังจากส่วนต่อไปนี้ (ซึ่งจะสั้นฉันสัญญา) เราสามารถเริ่มต้นการแสดงออก ในกรณีที่คุณลืมคำถามคือวิธีการทำงานต่อไปนี้:
Array.apply(null, { length: 5 }).map(Number.call, Number);
Arrayจัดการกับอาร์กิวเมนต์หลายข้อดังนั้น! เราเห็นสิ่งที่เกิดขึ้นเมื่อคุณผ่านการlengthโต้แย้งไปArrayแต่ในการแสดงออกเราผ่านหลายสิ่งเป็นข้อโต้แย้ง (อาร์เรย์ 5 undefinedเพื่อให้แน่นอน) ส่วนที่ 15.4.2.1บอกเราว่าต้องทำอะไร ย่อหน้าสุดท้ายคือสิ่งที่สำคัญสำหรับเราและมันมีคำพูดที่แปลกจริงๆแต่มันก็เดือดร้อนลงไปที่:
function Array () {
var ret = [];
ret.length = arguments.length;
for (var i = 0; i < arguments.length; i += 1) {
ret[i] = arguments[i];
}
return ret;
}
Array(0, 1, 2); //[0, 1, 2]
Array.apply(null, [0, 1, 2]); //[0, 1, 2]
Array.apply(null, Array(2)); //[undefined, undefined]
Array.apply(null, {length:2}); //[undefined, undefined]
Tada! เราได้รับอาร์เรย์ของค่าที่ไม่ได้กำหนดจำนวนมากและเราส่งคืนอาร์เรย์ของค่าที่ไม่ได้กำหนดเหล่านี้
สุดท้ายเราสามารถถอดรหัสต่อไปนี้:
Array.apply(null, { length: 5 })
เราเห็นว่ามันส่งคืนอาร์เรย์ที่มีค่าที่ไม่ได้กำหนด 5 ค่าโดยมีปุ่มอยู่ทั้งหมด
ตอนนี้ไปยังส่วนที่สองของการแสดงออก:
[undefined, undefined, undefined, undefined, undefined].map(Number.call, Number)
นี่จะเป็นส่วนที่ง่ายและไม่ซับซ้อนเนื่องจากไม่ได้พึ่งพาแฮ็กที่คลุมเครือมากนัก
Numberป้อนข้อมูลการทำNumber(something)( ส่วนที่ 15.7.1 ) จะแปลงsomethingเป็นตัวเลขและนั่นคือทั้งหมด มันเป็นวิธีที่ซับซ้อนเล็กน้อยโดยเฉพาะอย่างยิ่งในกรณีของสตริง แต่การดำเนินการถูกกำหนดไว้ในส่วน 9.3ในกรณีที่คุณสนใจ
Function.prototype.callcallเป็นapplyของพี่ชายที่กำหนดไว้ในมาตรา 15.3.4.4 แทนที่จะใช้อาเรย์ของอาร์กิวเมนต์มันแค่รับอาร์กิวเมนต์ที่ได้รับและส่งต่อไป
สิ่งต่าง ๆ ที่น่าสนใจเมื่อคุณcallโยงโซ่เข้าด้วยกันมากกว่าหนึ่งข้อเหวี่ยงสิ่งแปลกประหลาดถึง 11:
function log () {
console.log(this, arguments);
}
log.call.call(log, {a:4}, {a:5});
//{a:4}, [{a:5}]
//^---^ ^-----^
// this arguments
มันค่อนข้างจะมีค่าจนกว่าคุณจะเข้าใจว่าเกิดอะไรขึ้น log.callเป็นเพียงฟังก์ชั่นเทียบเท่ากับcallวิธีการฟังก์ชั่นอื่น ๆ ของและมีcallวิธีการในตัวเองเช่นกัน:
log.call === log.call.call; //true
log.call === Function.call; //true
แล้วจะcallทำอย่างไร? มันยอมรับthisArgและกลุ่มของข้อโต้แย้งและเรียกใช้ฟังก์ชั่นหลักของมัน เราสามารถกำหนดมันผ่านapply (อีกครั้งรหัสหลวมมากไม่ทำงาน):
Function.prototype.call = function (thisArg) {
var args = arguments.slice(1); //I wish that'd work
return this.apply(thisArg, args);
};
มาติดตามกันว่ามันจะลงอย่างไร:
log.call.call(log, {a:4}, {a:5});
this = log.call
thisArg = log
args = [{a:4}, {a:5}]
log.call.apply(log, [{a:4}, {a:5}])
log.call({a:4}, {a:5})
this = log
thisArg = {a:4}
args = [{a:5}]
log.apply({a:4}, [{a:5}])
.mapของมันทั้งหมดมันยังไม่จบ. มาดูกันว่าจะเกิดอะไรขึ้นเมื่อคุณจัดหาฟังก์ชันให้กับวิธีส่วนใหญ่:
function log () {
console.log(this, arguments);
}
var arr = ['a', 'b', 'c'];
arr.forEach(log);
//window, ['a', 0, ['a', 'b', 'c']]
//window, ['b', 1, ['a', 'b', 'c']]
//window, ['c', 2, ['a', 'b', 'c']]
//^----^ ^-----------------------^
// this arguments
หากเราไม่ได้ให้thisข้อโต้แย้งกับwindowเรา จดบันทึกลำดับการขัดแย้งที่มีให้ในการเรียกกลับของเราและเราจะทำให้มันแปลกไปจนถึง 11 อีกครั้ง:
arr.forEach(log.call, log);
//'a', [0, ['a', 'b', 'c']]
//'b', [1, ['a', 'b', 'c']]
//'b', [2, ['a', 'b', 'c']]
// ^ ^
โอ้โหโอ้โหกลับมาอีกหน่อย เกิดอะไรขึ้นที่นี่? เราสามารถดูได้ในหัวข้อ 15.4.4.18ซึ่งforEachมีการนิยามสิ่งต่าง ๆ ดังต่อไปนี้:
var callback = log.call,
thisArg = log;
for (var i = 0; i < arr.length; i += 1) {
callback.call(thisArg, arr[i], i, arr);
}
ดังนั้นเราได้รับสิ่งนี้:
log.call.call(log, arr[i], i, arr);
//After one `.call`, it cascades to:
log.call(arr[i], i, arr);
//Further cascading to:
log(i, arr);
ตอนนี้เราสามารถเห็นวิธีการ.map(Number.call, Number)ทำงาน:
Number.call.call(Number, arr[i], i, arr);
Number.call(arr[i], i, arr);
Number(i, arr);
ซึ่งส่งกลับการแปลงของiดัชนีปัจจุบันเป็นตัวเลข
การแสดงออก
Array.apply(null, { length: 5 }).map(Number.call, Number);
ใช้งานได้สองส่วน:
var arr = Array.apply(null, { length: 5 }); //1
arr.map(Number.call, Number); //2
ส่วนแรกสร้างอาร์เรย์ของ 5 รายการที่ไม่ได้กำหนด ครั้งที่สองจะผ่านอาร์เรย์นั้นและใช้ดัชนีของมันทำให้เกิดอาร์เรย์ของดัชนีองค์ประกอบ:
[0, 1, 2, 3, 4]
ahaExclamationMark.apply(null, Array(2)); //2, trueโปรดช่วยฉันในการทำความเข้าใจต่อไปนี้ ทำไมมันกลับมา2และtrueตามลำดับ? คุณไม่ผ่านการโต้แย้งเพียงครั้งเดียวนั่นคือArray(2)ที่นี่หรือ
applyเท่านั้น แต่อาร์กิวเมนต์นั้นคือ "splatted" เป็นสองอาร์กิวเมนต์ที่ส่งผ่านไปยังฟังก์ชัน คุณสามารถดูได้ง่ายขึ้นในapplyตัวอย่างแรก ครั้งแรกconsole.logแสดงให้เห็นว่าแน่นอนเราได้รับสองข้อโต้แย้ง (รายการอาร์เรย์สองรายการ) และรายการที่สองconsole.logแสดงให้เห็นว่าอาร์เรย์มีการkey=>valueแมปในช่องที่ 1 (ตามที่อธิบายไว้ในส่วนที่ 1 ของคำตอบ)
log.apply(null, document.getElementsByTagName('script'));ไม่จำเป็นต้องใช้ในการทำงานและไม่ทำงานในบางเบราว์เซอร์และ[].slice.call(NodeList)การเปลี่ยน NodeList ให้เป็นอาร์เรย์จะไม่ทำงานเช่นกัน
thisเดียว: เริ่มต้นเฉพาะWindowในโหมดที่ไม่เข้มงวด
คำเตือน : นี่เป็นคำอธิบายอย่างเป็นทางการของรหัสข้างต้น - นี่คือวิธีที่ฉันรู้วิธีที่จะอธิบายมัน สำหรับคำตอบที่ง่ายขึ้น - ตรวจสอบคำตอบที่ยอดเยี่ยมของ Zirak ด้านบน นี่เป็นข้อมูลจำเพาะเชิงลึกในใบหน้าของคุณและ "aha" น้อยลง
มีหลายสิ่งเกิดขึ้นที่นี่ เรามาทำลายมันกันสักหน่อย
var arr = Array.apply(null, { length: 5 }); // Create an array of 5 `undefined` values
arr.map(Number.call, Number); // Calculate and return a number based on the index passed
ในบรรทัดแรก, ตัวสร้างอาร์เรย์จะเรียกว่าเป็นฟังก์ชั่นFunction.prototype.applyที่มี
thisค่าnullซึ่งไม่สำคัญสำหรับตัวสร้างอาร์เรย์ ( thisเป็นเช่นเดียวthisกับในบริบทตาม 15.3.4.3.2.a.new Arrayถูกเรียกว่ากำลังถูกส่งผ่านวัตถุที่มีlengthคุณสมบัติ - ซึ่งทำให้วัตถุนั้นเป็นอาร์เรย์ที่เหมือนกับทุกสิ่งที่มันเป็น.applyเพราะข้อต่อไปนี้ใน.apply:
.applyจะผ่านข้อโต้แย้งจาก 0 ถึง.lengthตั้งแต่การโทร[[Get]]บน{ length: 5 }ด้วยค่าที่ 0-4 อัตราผลตอบแทนundefinedคอนสตรัคอาร์เรย์เรียกว่ามีห้าข้อโต้แย้งที่มีค่าundefined(ได้รับทรัพย์สินที่ไม่ได้ประกาศของวัตถุ)var arr = Array.apply(null, { length: 5 });สร้างรายการห้าค่าที่ไม่ได้กำหนดบันทึก : เห็นความแตกต่างระหว่างที่นี่Array.apply(0,{length: 5})และArray(5)เป็นครั้งแรกที่สร้างห้าครั้งประเภทค่าดั้งเดิมundefinedและหลังการสร้างอาร์เรย์ที่ว่างของความยาว 5. เฉพาะเพราะ.mapพฤติกรรม (8.b)[[HasProperty]และโดยเฉพาะ
ดังนั้นรหัสข้างต้นในสเปคที่เข้ากันได้เป็นเช่นเดียวกับ:
var arr = [undefined, undefined, undefined, undefined, undefined];
arr.map(Number.call, Number); // Calculate and return a number based on the index passed
ตอนนี้ออกไปส่วนที่สอง
Array.prototype.mapเรียกฟังก์ชั่นการโทรกลับ (ในกรณีนี้Number.call) ในแต่ละองค์ประกอบของอาร์เรย์และใช้thisค่าที่ระบุ(ในกรณีนี้การตั้งthisค่าเป็น `จำนวน)Number.call) คือดัชนีและตัวแรกคือค่านี้Numberจะถูกเรียกด้วยthisas undefined(ค่าอาร์เรย์) และดัชนีเป็นพารามิเตอร์ ดังนั้นโดยพื้นฐานแล้วจะเหมือนกับการจับคู่แต่ละรายการundefinedกับดัชนีอาร์เรย์Numberทำการแปลงชนิดในกรณีนี้จากตัวเลขเป็นหมายเลขไม่เปลี่ยนดัชนี)ดังนั้นรหัสด้านบนจะใช้ค่าห้าค่าที่ไม่ได้กำหนดและแม็พแต่ละค่ากับดัชนีในอาเรย์
นี่คือสาเหตุที่เราได้ผลลัพธ์จากรหัสของเรา
Array.apply(null,[2])เป็นเหมือนArray(2)ที่สร้างอาร์เรย์ว่างที่มีความยาว 2 และไม่ใช่อาร์เรย์ที่มีค่าดั้งเดิมundefinedสองครั้ง ดูการแก้ไขล่าสุดของฉันในบันทึกย่อหลังจากส่วนแรกแจ้งให้เราทราบว่าชัดเจนเพียงพอหรือไม่หากไม่ฉันจะชี้แจงให้ชัดเจน
{length: 2}ปลอมอาร์เรย์ด้วยสององค์ประกอบที่ตัวArrayสร้างจะแทรกลงในอาร์เรย์ที่สร้างขึ้นใหม่ เนื่องจากไม่มีอาเรย์จริงเข้าถึงองค์ประกอบที่ไม่ปรากฏundefinedซึ่งจะถูกแทรกเข้าไป Nice trick :)
อย่างที่คุณพูดตอนแรก:
var arr = Array.apply(null, { length: 5 });
สร้างอาร์เรย์ 5 undefinedค่า
ส่วนที่สองคือการเรียกใช้mapฟังก์ชันของอาร์เรย์ซึ่งใช้เวลา 2 ข้อโต้แย้งและส่งกลับอาร์เรย์ใหม่ที่มีขนาดเท่ากัน
อาร์กิวเมนต์แรกที่mapรับเป็นจริงฟังก์ชั่นที่จะใช้กับแต่ละองค์ประกอบในอาเรย์นั้นคาดว่าจะเป็นฟังก์ชั่นที่ใช้ 3 ข้อโต้แย้งและส่งกลับค่า ตัวอย่างเช่น:
function foo(a,b,c){
...
return ...
}
ถ้าเราผ่านฟังก์ชั่น foo เป็นอาร์กิวเมนต์แรกมันจะถูกเรียกสำหรับแต่ละองค์ประกอบด้วย
อาร์กิวเมนต์ที่สองที่mapรับจะถูกส่งผ่านไปยังฟังก์ชันที่คุณส่งผ่านเป็นอาร์กิวเมนต์แรก แต่มันจะไม่เป็น A, B, C หรือในกรณีที่มันจะเป็นfoothis
สองตัวอย่าง:
function bar(a,b,c){
return this
}
var arr2 = [3,4,5]
var newArr2 = arr2.map(bar, 9);
//newArr2 is equal to [9,9,9]
function baz(a,b,c){
return b
}
var newArr3 = arr2.map(baz,9);
//newArr3 is equal to [0,1,2]
และอีกอันหนึ่งเพื่อให้ชัดเจนขึ้น:
function qux(a,b,c){
return a
}
var newArr4 = arr2.map(qux,9);
//newArr4 is equal to [3,4,5]
แล้วสิ่งที่เกี่ยวกับ Number.call?
Number.call เป็นฟังก์ชั่นที่รับอาร์กิวเมนต์ 2 ตัวและพยายามแยกอาร์กิวเมนต์ที่สองเป็นตัวเลข (ฉันไม่แน่ใจว่ามันทำอะไรกับอาร์กิวเมนต์แรก)
เนื่องจากอาร์กิวเมนต์ที่สองที่mapผ่านไปนั้นคือดัชนีค่าที่จะถูกวางในอาร์เรย์ใหม่ที่ดัชนีนั้นจะเท่ากับดัชนี เช่นเดียวกับฟังก์ชั่นbazในตัวอย่างด้านบน Number.callจะพยายามแยกวิเคราะห์ดัชนีโดยจะคืนค่าเดิมตามธรรมชาติ
อาร์กิวเมนต์ที่สองที่คุณส่งผ่านไปยังmapฟังก์ชันในรหัสของคุณไม่มีผลกับผลลัพธ์ โปรดแก้ไขให้ฉันถ้าฉันผิดโปรด
Number.callไม่มีฟังก์ชั่นพิเศษที่แยกวิเคราะห์ข้อโต้แย้งกับตัวเลข === Function.prototype.callมันเป็นเพียง เพียงอาร์กิวเมนต์ที่สองฟังก์ชั่นที่ได้รับการส่งผ่านทางthis-value จะcallเป็นที่เกี่ยวข้อง - .map(eval.call, Number), .map(String.call, Number)และ.map(Function.prototype.call, Number)เทียบเท่าทุก
อาร์เรย์เป็นเพียงวัตถุที่ประกอบด้วยฟิลด์ 'ความยาว' และวิธีการบางอย่าง (เช่นการพุช) ดังนั้น arr in var arr = { length: 5}จึงเหมือนกับ Array ที่ฟิลด์ 0..4 มีค่าเริ่มต้นซึ่งไม่ได้กำหนด (เช่นarr[0] === undefinedให้ผลเป็นจริง)
ส่วนที่สองแผนที่ตามชื่อหมายถึงแผนที่จากอาร์เรย์หนึ่งไปยังใหม่ มันทำได้โดยการเข้าไปในอาเรย์ดั้งเดิมและเรียกใช้ฟังก์ชั่นการแมปในแต่ละรายการ
สิ่งที่เหลืออยู่คือการโน้มน้าวใจคุณว่าผลลัพธ์ของการทำแผนที่ฟังก์ชั่นคือดัชนี เคล็ดลับคือการใช้วิธีการที่เรียกว่า 'call' (*) ซึ่งเรียกใช้ฟังก์ชันโดยมีข้อยกเว้นเล็กน้อยที่ param แรกถูกตั้งค่าให้เป็นบริบท 'this' และที่สองกลายเป็น param แรก (เป็นต้น) เมื่อมีการเรียกใช้การแม็พฟังก์ชันพารามิเตอร์ที่สองคือดัชนี
สุดท้าย แต่ไม่ท้ายสุดวิธีที่เรียกใช้คือ Number "Class" และอย่างที่เรารู้ใน JS คือ "Class" เป็นเพียงฟังก์ชั่นและอันนี้ (Number) คาดหวังว่าพารามิเตอร์แรกจะเป็นค่า
(*) พบในต้นแบบของ Function (และ Number เป็นฟังก์ชัน)
Mashal
[undefined, undefined, undefined, …]และnew Array(n)หรือ{length: n}- อันหลังเบาบางนั่นคือพวกเขาไม่มีองค์ประกอบ สิ่งนี้มีความเกี่ยวข้องมากmapและนั่นเป็นสาเหตุที่ทำให้Array.applyมีการใช้เลขคี่
Array.apply(null, Array(30)).map(Number.call, Number)อ่านง่ายกว่าเพราะหลีกเลี่ยงการแสร้งว่าวัตถุธรรมดาเป็น Array