วิธีการใช้อาร์กิวเมนต์และพารามิเตอร์ ES6
เผยแพร่แล้ว: 2022-03-10ECMAScript 6 (หรือ ECMAScript 2015) เป็นเวอร์ชันล่าสุดของมาตรฐาน ECMAScript และมีการปรับปรุงการจัดการพารามิเตอร์ใน JavaScript อย่างน่าทึ่ง ตอนนี้เราสามารถใช้พารามิเตอร์การพัก ค่าดีฟอลต์ และการทำลายโครงสร้าง ท่ามกลางคุณสมบัติใหม่อื่นๆ
ในบทช่วยสอนนี้ เราจะสำรวจอาร์กิวเมนต์และพารามิเตอร์โดยละเอียดและดูว่า ECMAScript 6 อัปเกรดได้อย่างไร
อาร์กิวเมนต์กับพารามิเตอร์
อาร์กิวเมนต์และพารามิเตอร์มักถูกอ้างถึงแทนกันได้ อย่างไรก็ตาม สำหรับจุดประสงค์ของบทช่วยสอนนี้ เราจะสร้างความแตกต่าง ในมาตรฐานส่วนใหญ่ พารามิเตอร์ (หรือพารามิเตอร์ที่เป็นทางการ) คือสิ่งที่กำหนดในการประกาศฟังก์ชัน และอาร์กิวเมนต์ (หรือพารามิเตอร์จริง) คือสิ่งที่ส่งผ่านไปยังฟังก์ชัน พิจารณาฟังก์ชั่นนี้:
function foo(param1, param2) { // do something } foo(10, 20);
ในฟังก์ชันนี้ param1
และ param2
เป็นพารามิเตอร์ของฟังก์ชัน และค่าที่ส่งไปยังฟังก์ชัน ( 10
และ 20
) เป็นอาร์กิวเมนต์
ตัวดำเนินการสเปรด (…)
ใน ECMAScript 5 เมธอด apply()
เป็นเครื่องมือที่สะดวกสำหรับการส่งอาร์เรย์เป็นอาร์กิวเมนต์ไปยังฟังก์ชัน ตัวอย่างเช่น โดยทั่วไปจะใช้กับ Math.max()
เพื่อค้นหาค่าสูงสุดในอาร์เรย์ พิจารณาส่วนย่อยของรหัสนี้:
var myArray = [5, 10, 50]; Math.max(myArray); // Error: NaN Math.max.apply(Math, myArray); // 50
Math.max()
ไม่รองรับอาร์เรย์ รับเฉพาะตัวเลขเท่านั้น เมื่ออาร์เรย์ถูกส่งไปยัง Math.max()
อาร์เรย์จะเกิดข้อผิดพลาด แต่เมื่อใช้วิธี apply()
อาร์เรย์จะถูกส่งเป็นตัวเลขเดี่ยว ดังนั้น Math.max()
จึงสามารถจัดการได้
โชคดีที่ด้วยการแนะนำตัวดำเนินการสเปรดใน ECMAScript 6 เราไม่จำเป็นต้องใช้เมธอด apply()
อีกต่อไป ด้วยตัวดำเนินการกระจาย เราสามารถขยายนิพจน์ออกเป็นหลายอาร์กิวเมนต์ได้อย่างง่ายดาย:
var myArray = [5, 10, 50]; Math.max(...myArray); // 50
ที่นี่ ตัวดำเนินการสเปรดขยาย myArray
เพื่อสร้างค่าแต่ละค่าสำหรับฟังก์ชัน ในขณะที่จำลองตัวดำเนินการสเปรดโดยใช้ apply()
ใน ECMAScript 5 นั้นเป็นไปได้ ไวยากรณ์นั้นสับสนและขาดความยืดหยุ่นของตัวดำเนินการสเปรด ตัวดำเนินการสเปรดไม่เพียงแต่จะใช้งานง่ายขึ้นเท่านั้น แต่ยังมีฟีเจอร์อีกมากมาย ตัวอย่างเช่น สามารถใช้หลายครั้งและสามารถผสมกับอาร์กิวเมนต์อื่นในการเรียก function
:
function myFunction() { for(var i in arguments){ console.log(arguments[i]); } } var params = [10, 15]; myFunction(5, ...params, 20, ...[25]); // 5 10 15 20 25
ข้อดีอีกประการของตัวดำเนินการสเปรดคือสามารถใช้กับคอนสตรัคเตอร์ได้อย่างง่ายดาย:
new Date(...[2016, 5, 6]); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)
แน่นอน เราสามารถเขียนโค้ดก่อนหน้าใน ECMAScript 5 ได้ แต่เราจำเป็นต้องใช้รูปแบบที่ซับซ้อนเพื่อหลีกเลี่ยงข้อผิดพลาดประเภท:
new Date.apply(null, [2016, 4, 24]); // TypeError: Date.apply is not a constructor new (Function.prototype.bind.apply(Date, [null].concat([2016, 5, 6]))); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)
การสนับสนุนเบราว์เซอร์ตัวดำเนินการกระจายในการเรียกใช้ฟังก์ชัน
เบราว์เซอร์เดสก์ท็อป:
โครเมียม | Firefox | Internet Explorer | Microsoft Edge | โอเปร่า | ซาฟารี |
---|---|---|---|---|---|
46 | 27 | – | ได้รับการสนับสนุน | – | 7.1 |
เบราว์เซอร์มือถือ:
Chrome สำหรับ Android | Firefox Mobile | Safari Mobile | Opera Mobile | IE มือถือ |
---|---|---|---|---|
46 | 27 | 8 | – | – |
พารามิเตอร์ส่วนที่เหลือ
พารามิเตอร์ rest มีไวยากรณ์เดียวกันกับตัวดำเนินการ spread แต่แทนที่จะขยายอาร์เรย์เป็นพารามิเตอร์ พารามิเตอร์จะรวบรวมพารามิเตอร์และเปลี่ยนเป็นอาร์เรย์
function myFunction(...options) { return options; } myFunction('a', 'b', 'c'); // ["a", "b", "c"]
หากไม่มีอาร์กิวเมนต์ พารามิเตอร์ rest จะถูกตั้งค่าเป็นอาร์เรย์ว่าง:
function myFunction(...options) { return options; } myFunction(); // []
พารามิเตอร์ rest มีประโยชน์อย่างยิ่งเมื่อสร้างฟังก์ชัน Variadic (ฟังก์ชันที่ยอมรับจำนวนตัวแปรของอาร์กิวเมนต์) ประโยชน์ของการเป็นอาร์เรย์ พารามิเตอร์ส่วนที่เหลือสามารถแทนที่วัตถุ arguments
ได้อย่างง่ายดาย (ซึ่งเราจะอธิบายในบทช่วยสอนนี้ในภายหลัง) พิจารณาฟังก์ชันนี้ ซึ่งเขียนด้วย ECMAScript 5:
function checkSubstrings(string) { for (var i = 1; i < arguments.length; i++) { if (string.indexOf(arguments[i]) === -1) { return false; } } return true; } checkSubstrings('this is a string', 'is', 'this'); // true
ฟังก์ชันนี้จะตรวจสอบว่าสตริงมีสตริงย่อยจำนวนหนึ่งหรือไม่ ปัญหาแรกของฟังก์ชันนี้คือ เราต้องมองเข้าไปในเนื้อความของ function
เพื่อดูว่าต้องใช้หลายอาร์กิวเมนต์ ปัญหาที่สองคือการวนซ้ำต้องเริ่มจาก 1
แทนที่จะเป็น 0
เพราะ arguments[0]
ชี้ไปที่อาร์กิวเมนต์แรก หากภายหลังเราตัดสินใจเพิ่มพารามิเตอร์อื่นก่อนหรือหลังสตริง เราอาจลืมอัปเดตลูป ด้วยพารามิเตอร์ที่เหลือ เราหลีกเลี่ยงปัญหาเหล่านี้ได้อย่างง่ายดาย:
function checkSubstrings(string, ...keys) { for (var key of keys) { if (string.indexOf(key) === -1) { return false; } } return true; } checkSubstrings('this is a string', 'is', 'this'); // true
เอาต์พุตของฟังก์ชันนี้เหมือนกับฟังก์ชันก่อนหน้า ที่นี่อีกครั้ง string
พารามิเตอร์เต็มไปด้วยอาร์กิวเมนต์ที่ส่งผ่านก่อน แต่อาร์กิวเมนต์ที่เหลือจะใส่ในอาร์เรย์และกำหนดให้กับ keys
ตัวแปร
การใช้พารามิเตอร์ rest แทนอ็อบเจ็กต์ arguments
ช่วยปรับปรุงความสามารถในการอ่านโค้ดและหลีกเลี่ยงปัญหาการปรับให้เหมาะสมใน JavaScript อย่างไรก็ตาม พารามิเตอร์ที่เหลือไม่มีข้อจำกัด ตัวอย่างเช่น ต้องเป็นอาร์กิวเมนต์สุดท้าย มิฉะนั้น จะเกิดข้อผิดพลาดทางไวยากรณ์:
function logArguments(a, ...params, b) { console.log(a, params, b); } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter
ข้อจำกัดอีกประการหนึ่งคืออนุญาตให้ใช้พารามิเตอร์ส่วนที่เหลือเพียงตัวเดียวในการประกาศ function
:
function logArguments(...param1, ...param2) { } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter
รองรับเบราว์เซอร์พารามิเตอร์ส่วนที่เหลือ
เบราว์เซอร์เดสก์ท็อป:
โครเมียม | Firefox | Internet Explorer | Microsoft Edge | โอเปร่า | ซาฟารี |
---|---|---|---|---|---|
47 | 15 | – | ได้รับการสนับสนุน | 34 | – |
เบราว์เซอร์มือถือ:
Chrome สำหรับ Android | Firefox Mobile | Safari Mobile | Opera Mobile | IE มือถือ |
---|---|---|---|---|
47 | 15 | – | – | – |
พารามิเตอร์เริ่มต้น
พารามิเตอร์เริ่มต้นใน ECMAScript 5
JavaScript ไม่สนับสนุนพารามิเตอร์เริ่มต้นใน ECMAScript 5 แต่มีวิธีแก้ปัญหาที่ง่าย การใช้ตัวดำเนินการ OR
แบบลอจิคัล ( ||
) ภายในฟังก์ชัน เราสามารถจำลองพารามิเตอร์เริ่มต้นใน ECMAScript 5 ได้อย่างง่ายดาย พิจารณาฟังก์ชันนี้:
function foo(param1, param2) { param1 = param1 || 10; param2 = param2 || 10; console.log(param1, param2); } foo(5, 5); // 5 5 foo(5); // 5 10 foo(); // 10 10
ฟังก์ชันนี้คาดหวังสองอาร์กิวเมนต์ แต่เมื่อถูกเรียกโดยไม่มีอาร์กิวเมนต์ จะใช้ค่าเริ่มต้น ภายในฟังก์ชัน อาร์กิวเมนต์ที่หายไปจะถูกตั้งค่าโดยอัตโนมัติเป็นไม่ได้กำหนด ดังนั้นเราจึงสามารถตรวจจับอาร์กิวเมนต์เหล่านี้และประกาศค่าเริ่มต้นสำหรับอาร์กิวเมนต์เหล่านี้ได้ ในการตรวจจับอาร์กิวเมนต์ที่ขาดหายไปและตั้งค่าเริ่มต้น เราใช้ตัวดำเนินการ OR
แบบลอจิคัล ( ||
) โอเปอเรเตอร์นี้ตรวจสอบอาร์กิวเมนต์แรก: ถ้าเป็นจริง โอเปอเรเตอร์จะส่งกลับ ถ้าไม่ใช่ ตัวดำเนินการจะส่งกลับอาร์กิวเมนต์ที่สอง
วิธีนี้มักใช้ในฟังก์ชัน แต่มีข้อบกพร่อง การส่งค่า 0
หรือ null
จะทำให้ค่าดีฟอลต์ทำงานได้เช่นกัน เนื่องจากค่าเหล่านี้ถือเป็นค่าเท็จ ดังนั้น หากเราต้องการส่งค่า 0
หรือ null
ไปยังฟังก์ชันนี้จริง ๆ เราจะต้องมีวิธีอื่นในการตรวจสอบว่าอาร์กิวเมนต์หายไปหรือไม่:
function foo(param1, param2) { if(param1 === undefined){ param1 = 10; } if(param2 === undefined){ param2 = 10; } console.log(param1, param2); } foo(0, null); // 0, null foo(); // 10, 10
ภายในฟังก์ชันนี้ ประเภทของอาร์กิวเมนต์ที่ส่งผ่านจะถูกตรวจสอบเพื่อให้แน่ใจว่าไม่ได้กำหนดไว้ก่อนที่จะกำหนดค่าเริ่มต้น วิธีนี้ต้องใช้โค้ดเพิ่มอีกเล็กน้อย แต่เป็นทางเลือกที่ปลอดภัยกว่า และทำให้เราสามารถส่งค่า 0
และ null
ไปยังฟังก์ชันได้
พารามิเตอร์เริ่มต้นใน ECMAScript 6
ด้วย ECMAScript 6 เราไม่จำเป็นต้องตรวจสอบค่าที่ไม่ได้กำหนดเพื่อจำลองพารามิเตอร์เริ่มต้นอีกต่อไป ตอนนี้เราสามารถใส่ค่าดีฟอลต์ลงในการประกาศ function
ได้:
function foo(a = 10, b = 10) { console.log(a, b); } foo(5); // 5 10 foo(0, null); // 0 null
อย่างที่คุณเห็น การละเว้นอาร์กิวเมนต์จะทริกเกอร์ค่าเริ่มต้น แต่จะไม่ส่งผ่าน 0
หรือ null
เรายังสามารถใช้ฟังก์ชันเพื่อดึงค่าสำหรับพารามิเตอร์เริ่มต้นได้:
function getParam() { alert("getParam was called"); return 3; } function multiply(param1, param2 = getParam()) { return param1 * param2; } multiply(2, 5); // 10 multiply(2); // 6 (also displays an alert dialog)
โปรดทราบว่าฟังก์ชัน getParam
จะถูกเรียกใช้ก็ต่อเมื่อละเว้นอาร์กิวเมนต์ที่สอง ดังนั้น เมื่อเราเรียกใช้ฟังก์ชัน multiply()
ด้วยสองพารามิเตอร์ การแจ้งเตือนจะไม่ปรากฏ
คุณสมบัติที่น่าสนใจอีกประการของพารามิเตอร์เริ่มต้นคือเราสามารถอ้างถึงพารามิเตอร์และตัวแปรอื่น ๆ ในการประกาศ function
:
function myFunction(a=10, b=a) { console.log('a = ' + a + '; b = ' + b); } myFunction(); // a=10; b=10 myFunction(22); // a=22; b=22 myFunction(2, 4); // a=2; b=4
คุณยังสามารถดำเนินการในการประกาศ function
:
function myFunction(a, b = ++a, c = a*b) { console.log(c); } myFunction(5); // 36
โปรดทราบว่า JavaScript ประเมินพารามิเตอร์เริ่มต้น ณ เวลาโทร ไม่เหมือนกับภาษาอื่นๆ บางภาษา:
function add(value, array = []) { array.push(value); return array; } add(5); // [5] add(6); // [6], not [5, 6]
การสนับสนุนเบราว์เซอร์พารามิเตอร์เริ่มต้น
เบราว์เซอร์เดสก์ท็อป:
คุณสมบัติ | โครเมียม | Firefox | Internet Explorer | Microsoft Edge | โอเปร่า | ซาฟารี |
---|---|---|---|---|---|---|
การสนับสนุนขั้นพื้นฐาน | 49 | 15 | – | 14 | – | – |
พารามิเตอร์ที่ไม่มีค่าเริ่มต้นหลังจากพารามิเตอร์เริ่มต้น | 49 | 26 | – | 14 | – | – |
เบราว์เซอร์มือถือ:
คุณสมบัติ | Chrome สำหรับ Android | Firefox Mobile | Safari Mobile | Opera Mobile | IE มือถือ |
---|---|---|---|---|---|
การสนับสนุนขั้นพื้นฐาน | 49 | 15 | – | – | – |
พารามิเตอร์ที่ไม่มีค่าเริ่มต้นหลังจากพารามิเตอร์เริ่มต้น | 46 | 26 | – | – | – |
การทำลายล้าง
การทำลายโครงสร้างเป็นคุณลักษณะใหม่ใน ECMAScript 6 ที่ช่วยให้เราสามารถดึงค่าจากอาร์เรย์และอ็อบเจ็กต์ และกำหนดให้กับตัวแปรโดยใช้ไวยากรณ์ที่คล้ายกับอ็อบเจกต์และอาร์เรย์ ไวยากรณ์มีความชัดเจนและเข้าใจง่าย และมีประโยชน์อย่างยิ่งเมื่อส่งผ่านอาร์กิวเมนต์ไปยังฟังก์ชัน
ใน ECMAScript 5 มักใช้อ็อบเจ็กต์การกำหนดค่าเพื่อจัดการกับพารามิเตอร์ทางเลือกจำนวนมาก โดยเฉพาะอย่างยิ่งเมื่อลำดับของคุณสมบัติไม่สำคัญ พิจารณาฟังก์ชั่นนี้:
function initiateTransfer(options) { var protocol = options.protocol, port = options.port, delay = options.delay, retries = options.retries, timeout = options.timeout, log = options.log; // code to initiate transfer } options = { protocol: 'http', port: 800, delay: 150, retries: 10, timeout: 500, log: true }; initiateTransfer(options);
รูปแบบนี้มักใช้โดยนักพัฒนา JavaScript และทำงานได้ดี แต่เราต้องดูภายในเนื้อหาของ function
เพื่อดูว่าคาดหวังพารามิเตอร์ใดบ้าง ด้วยพารามิเตอร์ที่ทำลายล้าง เราสามารถระบุพารามิเตอร์ในการประกาศ function
ได้อย่างชัดเจน:
function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer }; var options = { protocol: 'http', port: 800, delay: 150, retries: 10, timeout: 500, log: true } initiateTransfer(options);
ในฟังก์ชันนี้ เราได้ใช้รูปแบบการทำลายวัตถุ แทนที่จะเป็นวัตถุการกำหนดค่า ทำให้ฟังก์ชันของเราไม่เพียงแค่กระชับขึ้นเท่านั้น แต่ยังอ่านง่ายขึ้นอีกด้วย
นอกจากนี้เรายังสามารถรวมพารามิเตอร์ที่ทำลายล้างกับพารามิเตอร์ปกติได้:
function initiateTransfer(param1, {protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer('some value', options);
โปรดทราบว่าข้อผิดพลาดประเภทจะถูกส่งออกไปถ้าพารามิเตอร์ถูกละเว้นในการเรียก function
:
function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer(); // TypeError: Cannot match against 'undefined' or 'null'
นี่เป็นพฤติกรรมที่ต้องการเมื่อเราต้องการพารามิเตอร์ แต่ถ้าเราต้องการให้พารามิเตอร์เหล่านี้เป็นทางเลือกล่ะ เพื่อป้องกันข้อผิดพลาดนี้เมื่อพารามิเตอร์หายไป เราจำเป็นต้องกำหนดค่าเริ่มต้นให้กับพารามิเตอร์ที่ทำลาย:
function initiateTransfer({protocol, port, delay, retries, timeout, log} = {}) { // code to initiate transfer } initiateTransfer(); // no error
ในฟังก์ชันนี้ ออบเจ็กต์ว่างจะถูกจัดเตรียมเป็นค่าเริ่มต้นสำหรับพารามิเตอร์ที่ทำลายโครงสร้าง ตอนนี้ หากฟังก์ชันนี้ถูกเรียกใช้โดยไม่มีพารามิเตอร์ใดๆ จะไม่มีข้อผิดพลาดเกิดขึ้น
นอกจากนี้เรายังสามารถกำหนดค่าเริ่มต้นให้กับแต่ละพารามิเตอร์ที่ทำลาย:
function initiateTransfer({ protocol = 'http', port = 800, delay = 150, retries = 10, timeout = 500, log = true }) { // code to initiate transfer }
ในตัวอย่างนี้ ทุกคุณสมบัติมีพารามิเตอร์เริ่มต้น ทำให้ไม่จำเป็นต้องตรวจสอบพารามิเตอร์ที่ไม่ได้กำหนดด้วยตนเองและกำหนดค่าเริ่มต้นภายในเนื้อหาของ function
การทำลายล้างการสนับสนุนเบราว์เซอร์
เบราว์เซอร์เดสก์ท็อป:
คุณสมบัติ | โครเมียม | Firefox | Internet Explorer | Microsoft Edge | โอเปร่า | ซาฟารี |
---|---|---|---|---|---|---|
การสนับสนุนขั้นพื้นฐาน | 49 | 2.0 | – | 14 | – | 7.1 |
พารามิเตอร์ที่ถูกทำลายด้วยการกำหนดค่าเริ่มต้น | 49 | 47 | – | 14 | – | – |
เบราว์เซอร์มือถือ:
คุณสมบัติ | Chrome สำหรับ Android | Firefox Mobile | Safari Mobile | Opera Mobile | IE มือถือ |
---|---|---|---|---|---|
การสนับสนุนขั้นพื้นฐาน | 49 | 1 | 8 | – | – |
พารามิเตอร์ที่ไม่มีค่าเริ่มต้นหลังจากพารามิเตอร์เริ่มต้น | 49 | 47 | – | – | – |
ผ่านข้อโต้แย้ง
มีสองวิธีในการส่งผ่านอาร์กิวเมนต์ไปยังฟังก์ชัน: โดยการอ้างอิงหรือตามค่า การแก้ไขอาร์กิวเมนต์ที่ส่งผ่านโดยการอ้างอิงจะมีผลทั่วโลก แต่การแก้ไขอาร์กิวเมนต์ที่ส่งผ่านโดยค่าจะสะท้อนให้เห็นในฟังก์ชันเท่านั้น
ในบางภาษา เช่น Visual Basic และ PowerShell เรามีตัวเลือกในการระบุว่าจะส่งอาร์กิวเมนต์โดยการอ้างอิงหรือตามค่า แต่นั่นไม่ใช่กรณีของ JavaScript
ผ่านอาร์กิวเมนต์ตามค่า
ในทางเทคนิค JavaScript สามารถส่งผ่านค่าเท่านั้น เมื่อเราส่งอาร์กิวเมนต์ไปยังฟังก์ชันตามค่า สำเนาของค่านั้นจะถูกสร้างขึ้นภายในขอบเขตของ function
ดังนั้น การเปลี่ยนแปลงใดๆ ของค่าจะสะท้อนให้เห็นภายใน function
เท่านั้น พิจารณาตัวอย่างนี้:
var a = 5; function increment(a) { a = ++a; console.log(a); } increment(a); // 6 console.log(a); // 5
ในที่นี้ การปรับเปลี่ยนอาร์กิวเมนต์ภายในฟังก์ชันจะไม่มีผลกับค่าดั้งเดิม ดังนั้น เมื่อตัวแปรถูกบันทึกจากภายนอกฟังก์ชัน ค่าที่พิมพ์ยังคงเป็น 5
ผ่านอาร์กิวเมนต์โดยการอ้างอิง
ใน JavaScript ทุกอย่างถูกส่งผ่านด้วยค่า แต่เมื่อเราส่งผ่านตัวแปรที่อ้างถึงวัตถุ (รวมถึงอาร์เรย์) "ค่า" คือการอ้างอิงถึงวัตถุ และการเปลี่ยนแปลงคุณสมบัติของวัตถุที่อ้างอิงโดยตัวแปรจะทำให้เปลี่ยน วัตถุพื้นฐาน
พิจารณาฟังก์ชั่นนี้:
function foo(param){ param.bar = 'new value'; } obj = { bar : 'value' } console.log(obj.bar); // value foo(obj); console.log(obj.bar); // new value
ดังที่คุณเห็น คุณสมบัติของวัตถุถูกแก้ไขภายในฟังก์ชัน แต่ค่าที่แก้ไขจะมองเห็นได้ภายนอกฟังก์ชัน
เมื่อเราส่งค่าที่ไม่ใช่ค่าดั้งเดิม เช่น อาร์เรย์หรืออ็อบเจ็กต์ เบื้องหลังจะมีการสร้างตัวแปรซึ่งชี้ไปยังตำแหน่งของออบเจกต์ดั้งเดิมในหน่วยความจำ จากนั้นตัวแปรนี้จะถูกส่งต่อไปยังฟังก์ชัน และการแก้ไขจะส่งผลต่อออบเจกต์ดั้งเดิม
การตรวจสอบประเภทและพารามิเตอร์ที่ขาดหายไปหรือเพิ่มเติม
ในภาษาที่มีการพิมพ์ที่ชัดเจน เราต้องระบุประเภทของพารามิเตอร์ในการประกาศ function
แต่ JavaScript ขาดคุณสมบัตินี้ ใน JavaScript ไม่สำคัญว่าข้อมูลประเภทใดหรือกี่อาร์กิวเมนต์ที่เราส่งผ่านไปยังฟังก์ชัน
สมมติว่าเรามีฟังก์ชันที่ยอมรับอาร์กิวเมนต์เดียวเท่านั้น เมื่อเราเรียกใช้ฟังก์ชันนั้น เราไม่ได้จำกัดให้ส่งผ่านอาร์กิวเมนต์เพียงตัวเดียวไปยังฟังก์ชัน เรามีอิสระที่จะส่งข้อโต้แย้งหนึ่ง สองข้อขึ้นไป! เราอาจเลือกที่จะไม่ผ่านเลยก็ได้ และจะไม่มีข้อผิดพลาดเกิดขึ้น
จำนวนอาร์กิวเมนต์และพารามิเตอร์อาจแตกต่างกันในสองวิธี:
- อาร์กิวเมนต์น้อยกว่าพารามิเตอร์
พารามิเตอร์ที่ขาดหายไปจะเท่ากับundefined
- อาร์กิวเมนต์มากกว่าพารามิเตอร์
พารามิเตอร์พิเศษจะถูกละเว้น แต่สามารถดึงข้อมูลได้ผ่านอาร์กิวเมนต์ตัวแปรพิเศษคล้ายอาร์เรย์ (จะกล่าวถึงต่อไป)
อาร์กิวเมนต์บังคับ
หากไม่มีอาร์กิวเมนต์ในการเรียก function
อาร์กิวเมนต์จะถูกตั้งค่าเป็น undefined
เราสามารถใช้ประโยชน์จากพฤติกรรมนี้และแสดงข้อผิดพลาดหากละเว้นอาร์กิวเมนต์:
function foo(mandatory, optional) { if (mandatory === undefined) { throw new Error('Missing parameter: mandatory'); } }
ใน ECMAScript 6 เราสามารถดำเนินการเพิ่มเติมและใช้พารามิเตอร์เริ่มต้นเพื่อตั้งค่าอาร์กิวเมนต์บังคับ:
function throwError() { throw new Error('Missing parameter'); } function foo(param1 = throwError(), param2 = throwError()) { // do something } foo(10, 20); // ok foo(10); // Error: missing parameter
วัตถุอาร์กิวเมนต์
เพิ่มการรองรับพารามิเตอร์ส่วนที่เหลือใน ECMAScript 4 ด้วยความตั้งใจที่จะแทนที่อ็อบเจ็กต์ arguments
แต่ ECMAScript 4 ไม่เคยบรรลุผล ด้วยการเปิดตัว ECMAScript 6 ตอนนี้ JavaScript รองรับพารามิเตอร์ส่วนที่เหลืออย่างเป็นทางการ นอกจากนี้ยังขัดขวางแผนการที่จะยกเลิกการสนับสนุนสำหรับวัตถุ arguments
วัตถุ arguments
เป็นวัตถุคล้ายอาร์เรย์ที่มีอยู่ในฟังก์ชันทั้งหมด อนุญาตให้เรียกค่าของ argument
ที่ส่งไปยังฟังก์ชันโดยใช้ตัวเลข แทนที่จะเป็นชื่อ วัตถุช่วยให้เราสามารถส่งผ่านอาร์กิวเมนต์จำนวนเท่าใดก็ได้ไปยังฟังก์ชัน พิจารณาส่วนย่อยของรหัสต่อไปนี้:
function checkParams(param1) { console.log(param1); // 2 console.log(arguments[0], arguments[1]); // 2 3 console.log(param1 + arguments[0]); // 2 + 2 } checkParams(2, 3);
ฟังก์ชันนี้คาดว่าจะได้รับอาร์กิวเมนต์เดียวเท่านั้น เมื่อเราเรียกมันด้วยสองอาร์กิวเมนต์ อาร์กิวเมนต์แรกสามารถเข้าถึงได้ในฟังก์ชันโดยชื่อพารามิเตอร์ param1
หรืออาร์กิวเมนต์ object arguments[0]
แต่อาร์กิวเมนต์ที่สองสามารถเข้าถึงได้เป็น arguments[1]
เท่านั้น นอกจากนี้ โปรดทราบว่าวัตถุ arguments
สามารถใช้ร่วมกับอาร์กิวเมนต์ที่มีชื่อได้
ออบเจ็กต์ arguments
มีรายการสำหรับอาร์กิวเมนต์แต่ละรายการที่ส่งไปยังฟังก์ชัน และดัชนีของรายการแรกเริ่มต้นที่ 0
หากเราต้องการเข้าถึงอาร์กิวเมนต์เพิ่มเติมในตัวอย่างข้างต้น เราจะเขียน arguments[2]
arguments[3]
และอื่นๆ
เราสามารถข้ามการตั้งค่าที่มีชื่อพารามิเตอร์ไปพร้อมกันและใช้วัตถุ arguments
:
function checkParams() { console.log(arguments[1], arguments[0], arguments[2]); } checkParams(2, 4, 6); // 4 2 6
อันที่จริง พารามิเตอร์ที่ระบุชื่อนั้นสะดวก ไม่จำเป็น ในทำนองเดียวกัน พารามิเตอร์ส่วนที่เหลือสามารถใช้เพื่อสะท้อนอาร์กิวเมนต์ที่ส่งผ่านได้:
function checkParams(...params) { console.log(params[1], params[0], params[2]); // 4 2 6 console.log(arguments[1], arguments[0], arguments[2]); // 4 2 6 } checkParams(2, 4, 6);
วัตถุ arguments
เป็นวัตถุคล้ายอาร์เรย์ แต่ไม่มีวิธีการอาร์เรย์เช่น slice()
และ foreach()
เพื่อที่จะใช้วิธีการอาร์เรย์บนวัตถุ arguments
จะต้องแปลงวัตถุเป็นอาร์เรย์จริงก่อน:
function sort() { var a = Array.prototype.slice.call(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]
ในฟังก์ชันนี้ Array.prototype.slice.call()
ถูกใช้เป็นวิธีที่รวดเร็วในการแปลงอ็อบเจ็กต์ arguments
เป็นอาร์เรย์ ถัดไป sort()
วิธีการเรียงลำดับรายการของอาร์เรย์และส่งกลับ
ECMAScript 6 มีวิธีการที่ตรงไปตรงมายิ่งขึ้น Array.from()
การเพิ่มใหม่ใน ECMAScript 6 สร้างอาร์เรย์ใหม่จากอ็อบเจ็กต์ที่เหมือนอาร์เรย์:
function sort() { var a = Array.from(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]
คุณสมบัติความยาว
แม้ว่าในทางเทคนิคแล้วอ็อบเจ็กต์อาร์กิวเมนต์จะไม่ใช่อาร์เรย์ แต่ก็มีคุณสมบัติด้าน length
ที่สามารถใช้ตรวจสอบจำนวนอาร์กิวเมนต์ที่ส่งผ่านไปยังฟังก์ชันได้:
function countArguments() { console.log(arguments.length); } countArguments(); // 0 countArguments(10, null, "string"); // 3
ด้วยการใช้คุณสมบัติ length
เราสามารถควบคุมจำนวนอาร์กิวเมนต์ที่ส่งผ่านไปยังฟังก์ชันได้ดีขึ้น ตัวอย่างเช่น ถ้าฟังก์ชันต้องการสองอาร์กิวเมนต์ในการทำงาน เราสามารถใช้คุณสมบัติ length
เพื่อตรวจสอบจำนวนอาร์กิวเมนต์ที่ส่งผ่าน และส่งข้อผิดพลาดหากน้อยกว่าที่คาดไว้:
function foo(param1, param2) { if (arguments.length < 2) { throw new Error("This function expects at least two arguments"); } else if (arguments.length === 2) { // do something } }
พารามิเตอร์ส่วนที่เหลือเป็นอาร์เรย์ ดังนั้นจึงมีคุณสมบัติด้าน length
ใน ECMAScript 6 โค้ดก่อนหน้าสามารถเขียนใหม่ด้วยพารามิเตอร์ส่วนที่เหลือ:
function foo(...params) { if (params.length < 2) { throw new Error("This function expects at least two arguments"); } else if (params.length === 2) { // do something } }
คุณสมบัติของผู้โทรและผู้โทร
คุณสมบัติ callee
หมายถึงฟังก์ชันที่กำลังทำงานอยู่ และ caller
หมายถึงฟังก์ชันที่เรียกใช้ฟังก์ชันที่กำลังดำเนินการอยู่ ในโหมดเข้มงวด ECMAScript 5 คุณสมบัติเหล่านี้เลิกใช้แล้ว และการพยายามเข้าถึงจะทำให้เกิด TypeError
คุณสมบัติ arguments.callee
มีประโยชน์ในฟังก์ชันแบบเรียกซ้ำ (ฟังก์ชันแบบเรียกซ้ำเป็นฟังก์ชันปกติที่อ้างอิงถึงตัวเองโดยใช้ชื่อ) โดยเฉพาะอย่างยิ่งเมื่อไม่มีชื่อฟังก์ชัน (ฟังก์ชันที่ไม่ระบุตัวตน) เนื่องจากฟังก์ชันที่ไม่ระบุชื่อไม่มีชื่อ วิธีเดียวที่จะอ้างอิงถึงได้คือโดย arguments.callee
var result = (function(n) { if (n <= 1) { return 1; } else { return n * arguments.callee(n - 1); } })(4); // 24
อาร์กิวเมนต์วัตถุในโหมดเข้มงวดและไม่เข้มงวด
ในโหมดไม่เคร่งครัดของ ECMAScript 5 อ็อบเจ็กต์ arguments
มีคุณลักษณะที่ผิดปกติ: ทำให้ค่าซิงค์กับค่าของพารามิเตอร์ที่มีชื่อที่สอดคล้องกัน
พิจารณาส่วนย่อยของรหัสต่อไปนี้:
function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // true return param } foo(200); // 500
ภายในฟังก์ชันนี้ ค่าใหม่ถูกกำหนดให้กับ arguments[0]
เนื่องจากค่าของ arguments
จะซิงค์กับค่าของพารามิเตอร์ที่มีชื่อเสมอ การเปลี่ยนแปลงเป็น arguments[0]
จะเปลี่ยนค่าของ param
ด้วย อันที่จริง มันเหมือนกับชื่อที่แตกต่างกันสองชื่อสำหรับตัวแปรเดียวกัน ในโหมดเข้มงวด ECMAScript 5 พฤติกรรมที่สับสนของวัตถุ arguments
นี้ถูกลบออก:
"use strict"; function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // false return param } foo(200); // 200
คราวนี้ การเปลี่ยน arguments[0]
จะไม่มีผลกับ param
และผลลัพธ์เป็นไปตามที่คาดไว้ ผลลัพธ์ของฟังก์ชันนี้ใน ECMAScript 6 จะเหมือนกับในโหมดเข้มงวดของ ECMAScript 5 แต่โปรดทราบว่าเมื่อใช้ค่าเริ่มต้นในการประกาศ function
วัตถุ arguments
จะไม่ได้รับผลกระทบ:
function foo(param1, param2 = 10, param3 = 20) { console.log(param1 === arguments[0]); // true console.log(param2 === arguments[1]); // true console.log(param3 === arguments[2]); // false console.log(arguments[2]); // undefined console.log(param3); // 20 } foo('string1', 'string2');
ในฟังก์ชันนี้ แม้ว่า param3
จะมีค่าเริ่มต้น แต่ก็ไม่เท่ากับ arguments[2]
เนื่องจากมีเพียงสองอาร์กิวเมนต์เท่านั้นที่ส่งผ่านไปยังฟังก์ชัน กล่าวอีกนัยหนึ่ง การตั้งค่าเริ่มต้นไม่มีผลกับวัตถุ arguments
บทสรุป
ECMAScript 6 ได้นำการปรับปรุงขนาดเล็กและใหญ่หลายร้อยรายการมาสู่ JavaScript นักพัฒนาใช้ฟีเจอร์ ECMAScript 6 มากขึ้นเรื่อยๆ และในไม่ช้าฟีเจอร์เหล่านี้จะหลีกเลี่ยงไม่ได้ ในบทช่วยสอนนี้ เราได้เรียนรู้ว่า ECMAScript 6 ได้อัปเกรดการจัดการพารามิเตอร์ใน JavaScript ได้อย่างไร แต่เราเพิ่งจะขีดข่วนพื้นผิวของ ECMAScript 6 ฟีเจอร์ใหม่และน่าสนใจอื่นๆ ของภาษานั้นคุ้มค่าที่จะลองดู
ลิงค์
- ตารางความเข้ากันได้ของ ECMAScript 6, Juriy Zaytsev
- “ข้อกำหนดภาษา ECMAScript 2015” ECMA International