ฉันพบรหัสต่อไปนี้ในรายชื่อผู้รับจดหมาย 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=>value
arr.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.call
call
เป็น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
จะถูกเรียกด้วยthis
as 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 หรือในกรณีที่มันจะเป็นfoo
this
สองตัวอย่าง:
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