จำนวนตัวแปร JavaScript ของอาร์กิวเมนต์ที่ใช้ในการทำงาน


531

มีวิธีการอนุญาต vars "ไม่ จำกัด " สำหรับฟังก์ชั่นใน JavaScript หรือไม่?

ตัวอย่าง:

load(var1, var2, var3, var4, var5, etc...)
load(var1)


ซ้ำซ้อนกับ / เกี่ยวข้องเป็นไปได้ของstackoverflow.com/questions/4633125/…
ลูกา

1
@ ลุคไม่มันไม่ใช่ คำถามนั้นถามว่าจะเรียกใช้ฟังก์ชันอย่างไรโดยมีอาร์กิวเมนต์เป็นจำนวนเท่าใดก็ได้พร้อมอาร์กิวเมนต์ในอาร์เรย์ วิธีนี้จะถามวิธีจัดการกับการโทร
Scruffy

1
เพื่อการค้นหาที่ง่ายขึ้นฟังก์ชันดังกล่าวเรียกว่า 'ฟังก์ชัน Variadic'
gschenk

คำตอบ:


814

แน่นอนว่าใช้argumentsวัตถุ

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}

1
tnx เป็นวิธีที่ดีในการแยกวิเคราะห์ Strings จากโค้ดเนม Android ไปยัง javascript ใน Webview
Johan Hoeksma

4
วิธีนี้ใช้ได้ผลดีที่สุดสำหรับฉัน ขอบคุณ ข้อมูลเพิ่มเติมเกี่ยวกับข้อโต้แย้งคำหลักที่นี่
ใช้ 2

26
argumentsเป็นวัตถุพิเศษ "คล้ายอาร์เรย์" ซึ่งหมายความว่ามันมีความยาว แต่ไม่มีฟังก์ชันอาร์เรย์อื่น ๆ ดูdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/ …สำหรับข้อมูลเพิ่มเติมและคำตอบนี้: stackoverflow.com/a/13145228/1766230
ลุค

4
ที่น่าสนใจคือวิธีการ Array ใน Javascript ได้ถูกกำหนดในลักษณะที่พวกมันทำงานกับวัตถุใด ๆ ที่มีlengthคุณสมบัติ รวมถึงargumentsวัตถุด้วย รู้อย่างนี้แล้วและว่าวิธีการที่concatจะส่งคืนสำเนาของ 'อาเรย์' ก็เรียกว่าอยู่กับเราได้อย่างง่ายดายสามารถแปลงวัตถุอาร์เรย์จริงเช่นนี้arguments var args = [].concat.call(arguments)บางคนชอบที่จะใช้Array.prototype.concat.callแทน แต่ฉันชอบ[]มันสั้นและหวาน!
Stijn de Witt

2
@YasirJan ใช้[...arguments].join()
Willian D. Andrade

179

ในเบราว์เซอร์ล่าสุด (ส่วนใหญ่) คุณสามารถยอมรับจำนวนตัวแปรที่มีอาร์กิวเมนต์ด้วยไวยากรณ์นี้:

function my_log(...args) {
     // args is an Array
     console.log(args);
     // You can pass this array as parameters to another function
     console.log(...args);
}

นี่คือตัวอย่างเล็ก ๆ :

function foo(x, ...args) {
  console.log(x, args, ...args, arguments);
}

foo('a', 'b', 'c', z='d')

=>

a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
    0: "a"
    1: "b"
    2: "c"
    3: "d"
    length: 4

เอกสารและตัวอย่างเพิ่มเติมได้ที่นี่: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters


27
FYI จะถูกเรียกว่า "ไวยากรณ์พารามิเตอร์ส่วนที่เหลือ": developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
tanguy_k

4
+1 นี่คือทางออกที่หรูหราและสะอาดตา เหมาะอย่างยิ่งสำหรับการผ่านรายการพารามิเตอร์แบบยาวไปยังการเรียกใช้ฟังก์ชันอื่นและอาจเป็นไปได้ว่าพารามิเตอร์ตัวแปรเหล่านั้นอยู่ในตำแหน่งที่ต้องการ
haxpor

3
โปรดทราบว่าตามdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ......ไม่รองรับ IE
Roee Gavirel

2
นี่คือตารางที่แสดงการสนับสนุนเบราว์เซอร์ - caniuse.com/#feat=rest-parameters
Brian Burns

1
ช่างเป็นคำตอบที่ดีมาก!
Ashish

113

อีกทางเลือกหนึ่งคือการส่งผ่านข้อโต้แย้งของคุณในวัตถุบริบท

function load(context)
{
    // do whatever with context.name, context.address, etc
}

และใช้มันแบบนี้

load({name:'Ken',address:'secret',unused:true})

นี่เป็นข้อดีที่คุณสามารถเพิ่มอาร์กิวเมนต์ที่มีชื่อมากเท่าที่คุณต้องการและฟังก์ชั่นสามารถใช้พวกเขา (หรือไม่) ตามที่เห็นสมควร


4
สิ่งนี้จะดีกว่าเนื่องจากจะลบการเชื่อมต่อเพื่อการโต้แย้ง อินเทอร์เฟซที่เชื่อมโยงกันอย่างหลวม ๆ เป็นแนวปฏิบัติที่ดี ...
Jonas Schubert Erlandsson

9
แน่นอนว่าดีกว่าในบางกรณี แต่สมมุติว่าแต่ละข้อโต้แย้งไม่เกี่ยวข้องกันจริง ๆ หรือควรจะมีความหมายที่เท่าเทียมกัน (เช่นองค์ประกอบอาร์เรย์) ถ้าอย่างนั้นวิธีของ OP นั้นดีที่สุด
rvighne

1
นี่เป็นสิ่งที่ดีเพราะถ้าคุณต้องการคุณสามารถสร้างcontextอาร์กิวเมนต์ด้วยรหัสและส่งผ่านมันก่อนที่มันจะถูกใช้
Nate CK

46

ฉันเห็นด้วยกับคำตอบของเคนว่าเป็นคนที่มีพลวัตมากที่สุดและฉันชอบที่จะก้าวไปอีกขั้น ถ้ามันเป็นฟังก์ชั่นที่คุณโทรหาหลายครั้งด้วยข้อโต้แย้งที่แตกต่างกัน - ฉันใช้การออกแบบของ Ken แต่ก็เพิ่มค่าเริ่มต้น:

function load(context) {

    var defaults = {
        parameter1: defaultValue1,
        parameter2: defaultValue2,
        ...
    };

    var context = extend(defaults, context);

    // do stuff
}

วิธีนี้หากคุณมีพารามิเตอร์จำนวนมาก แต่ไม่จำเป็นต้องตั้งค่าด้วยการเรียกใช้ฟังก์ชันแต่ละครั้งคุณสามารถระบุค่าเริ่มต้นที่ไม่ใช่ค่าเริ่มต้นได้ สำหรับวิธีการขยายคุณสามารถใช้วิธีการขยายของ jQuery ($.extend() ) ประดิษฐ์ของคุณเองหรือใช้วิธีการต่อไปนี้:

function extend() {
    for (var i = 1; i < arguments.length; i++)
        for (var key in arguments[i])
            if (arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}

สิ่งนี้จะรวมวัตถุบริบทด้วยค่าเริ่มต้นและเติมค่าที่ไม่ได้กำหนดไว้ในวัตถุของคุณด้วยค่าเริ่มต้น


3
+1 เคล็ดลับดี บันทึกแผ่นบอยเลอร์จำนวนมากเพื่อกำหนดพารามิเตอร์ทุกค่าเริ่มต้นหรืออย่างอื่น
Neil

1
_.defaults()เมธอดUnderscoreเป็นทางเลือกที่ดีมากในการผสานอาร์กิวเมนต์ที่ระบุและอาร์กิวเมนต์เริ่มต้น
mbeasley


18

มันจะดีกว่าที่จะใช้ไวยากรณ์ส่วนที่เหลือเป็น Ramast ชี้ให้เห็น

function (a, b, ...args) {}

ฉันแค่ต้องการเพิ่มคุณสมบัติที่ดีของอาร์กิวเมนต์ ... args

  1. มันเป็นอาร์เรย์และไม่ใช่วัตถุเช่นการขัดแย้ง สิ่งนี้ช่วยให้คุณสามารถใช้ฟังก์ชั่นเช่นแผนที่หรือเรียงลำดับโดยตรง
  2. ไม่รวมพารามิเตอร์ทั้งหมด แต่มีเพียงพารามิเตอร์เดียวเท่านั้นที่ผ่านมา เช่นฟังก์ชัน (a, b, ... args) ในกรณีนี้ args มีอาร์กิวเมนต์ 3 ถึง arguments.length

15

ดังกล่าวแล้วคุณสามารถใช้ argumentsวัตถุเพื่อดึงหมายเลขตัวแปรของพารามิเตอร์ฟังก์ชั่น

หากคุณต้องการที่จะเรียกฟังก์ชั่นอื่น ๆ applyที่มีการขัดแย้งกันในการใช้งาน คุณสามารถเพิ่มหรือลบข้อโต้แย้งโดยแปลงargumentsเป็นอาเรย์ ตัวอย่างเช่นฟังก์ชั่นนี้จะแทรกข้อความบางส่วนก่อนที่จะเข้าสู่คอนโซล:

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}

ทางออกที่ดีในการแปลงอาร์กิวเมนต์เป็นอาร์เรย์ มันเป็นประโยชน์สำหรับฉันในวันนี้
Dmytro Medvid

8

แม้ว่าโดยทั่วไปฉันยอมรับว่าวิธีการตั้งชื่ออาร์กิวเมนต์นั้นมีประโยชน์และยืดหยุ่น (เว้นแต่คุณสนใจเรื่องคำสั่งซึ่งในกรณีที่การโต้แย้งง่ายที่สุด) ฉันมีข้อกังวลเกี่ยวกับค่าใช้จ่ายของวิธีการ mbeasley (ใช้ค่าเริ่มต้นและขยาย) นี่เป็นค่าใช้จ่ายจำนวนมากสำหรับการดึงค่าเริ่มต้น ก่อนอื่นค่าเริ่มต้นจะถูกกำหนดไว้ภายในฟังก์ชั่นดังนั้นจึงมีการ repopulated ในทุกการโทร ประการที่สองคุณสามารถอ่านค่าที่ตั้งชื่อและตั้งค่าเริ่มต้นพร้อมกันได้อย่างง่ายดายโดยใช้ || ไม่จำเป็นต้องสร้างและรวมวัตถุใหม่อีกตัวเพื่อรับข้อมูลนี้

function load(context) {
   var parameter1 = context.parameter1 || defaultValue1,
       parameter2 = context.parameter2 || defaultValue2;

   // do stuff
}

นี่คือจำนวนรหัสประมาณเท่ากัน (อาจมากกว่าเล็กน้อย) แต่ควรเป็นเศษส่วนของต้นทุนรันไทม์


ตกลงแม้ว่าอันตรายขึ้นอยู่กับประเภทของค่าหรือค่าเริ่มต้นเอง มิฉะนั้น(parameter1=context.parameter1)===undefined && (parameter1=defaultValue1)หรือสำหรับปริมาณรหัสน้อยฟังก์ชั่นผู้ช่วยขนาดเล็กเช่น: function def(providedValue, default) {return providedValue !== undefined ? providedValue : defaultValue;} var parameter1 = def(context.parameter1, defaultValue1)ให้รูปแบบทางเลือก อย่างไรก็ตามจุดของฉันยังคงอยู่: การสร้างวัตถุพิเศษสำหรับทุกการเรียกใช้ฟังก์ชั่นและการใช้ลูปที่มีราคาแพงเพื่อตั้งค่าเริ่มต้นสองเท่าคือค่าใช้จ่ายจำนวนบ้า
mcurland

7

ในขณะที่ @roufamatic ได้แสดงการใช้คำหลักอาร์กิวเมนต์และ @Ken แสดงตัวอย่างที่ยอดเยี่ยมของวัตถุสำหรับการใช้งานฉันรู้สึกว่าไม่ได้พูดถึงสิ่งที่เกิดขึ้นในตัวอย่างนี้อย่างแท้จริงและอาจทำให้ผู้อ่านในอนาคตสับสน / method มีวัตถุประสงค์เพื่อใช้จำนวนตัวแปรของอาร์กิวเมนต์ / พารามิเตอร์

function varyArg () {
    return arguments[0] + arguments[1];
}

เมื่อนักพัฒนาซอฟต์แวร์คนอื่นดูรหัสของคุณมันง่ายมากที่จะสมมติว่าฟังก์ชั่นนี้ไม่ใช้พารามิเตอร์ โดยเฉพาะอย่างยิ่งหากนักพัฒนานั้นไม่ได้เป็นส่วนตัวกับข้อโต้แย้งคำหลักด้วยเหตุนี้จึงเป็นความคิดที่ดีที่จะทำตามแนวทางของสไตล์และสอดคล้องกัน ฉันจะใช้ Google เป็นตัวอย่างทั้งหมด

เรามาพูดถึงฟังก์ชั่นเดียวกันอย่างชัดเจนว่ามีพารามิเตอร์ตัวแปร:

function varyArg (var_args) {
    return arguments[0] + arguments[1];
}

พารามิเตอร์วัตถุ VS var_args

อาจมีบางครั้งที่จำเป็นต้องใช้วัตถุเนื่องจากเป็นวิธีปฏิบัติที่ดีที่สุดที่ได้รับอนุมัติและพิจารณาแล้วของแผนที่ข้อมูล อาเรย์เชื่อมโยงจะถูกหน้างอและท้อแท้

sidenote:คีย์เวิร์ดอาร์กิวเมนต์ส่งคืนอ็อบเจ็กต์ที่ใช้ตัวเลขเป็นคีย์ มรดกต้นแบบยังเป็นตระกูลของวัตถุด้วย ดูจุดสิ้นสุดของคำตอบสำหรับการใช้งานอาร์เรย์ที่เหมาะสมใน JS

ในกรณีนี้เราสามารถระบุได้อย่างชัดเจนเช่นกัน หมายเหตุ: ข้อตกลงการตั้งชื่อนี้ไม่ได้จัดทำโดย Google แต่เป็นตัวอย่างของการประกาศอย่างชัดเจนถึงประเภทของพารามิเตอร์ นี่เป็นสิ่งสำคัญหากคุณต้องการสร้างรูปแบบการพิมพ์ที่เข้มงวดยิ่งขึ้นในรหัสของคุณ

function varyArg (args_obj) {
    return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});

เลือกแบบไหน

ขึ้นอยู่กับความต้องการของฟังก์ชันและโปรแกรมของคุณ หากเช่นคุณเพียงแค่ต้องการส่งคืนค่าฐานในกระบวนการวนซ้ำในทุกอาร์กิวเมนต์ที่ส่งผ่านดังนั้นส่วนใหญ่จะยึดติดกับคีย์เวิร์ดอาร์กิวเมนต์ ถ้าคุณต้องการนิยามอาร์กิวเมนต์และการแม็พข้อมูลของคุณดังนั้นเมธอด object คือวิธีที่จะไป ลองดูตัวอย่างสองตัวอย่างจากนั้นก็เสร็จแล้ว!

การใช้อาร์กิวเมนต์

function sumOfAll (var_args) {
    return arguments.reduce(function(a, b) {
        return a + b;
    }, 0);
}
sumOfAll(1,2,3); // returns 6

การใช้วัตถุ

function myObjArgs(args_obj) {
    // MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
    if (typeof args_obj !== "object") {
        return "Arguments passed must be in object form!";
    }

    return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old

การเข้าถึงอาร์เรย์แทนวัตถุ ("... args" พารามิเตอร์ที่เหลือ)

ตามที่กล่าวถึงด้านบนของคำตอบอาร์กิวเมนต์คำหลักส่งคืนวัตถุจริง ด้วยเหตุนี้วิธีการใด ๆ ที่คุณต้องการใช้สำหรับอาร์เรย์จะต้องถูกเรียก ตัวอย่างของสิ่งนี้:

Array.prototype.map.call(arguments, function (val, idx, arr) {});

เพื่อหลีกเลี่ยงปัญหานี้ให้ใช้พารามิเตอร์ส่วนที่เหลือ:

function varyArgArr (...var_args) {
    return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5

5

ใช้argumentsวัตถุเมื่ออยู่ภายในฟังก์ชั่นเพื่อให้สามารถเข้าถึงอาร์กิวเมนต์ทั้งหมดที่ส่งผ่าน


3

ระวังให้ดีว่าการส่ง Object ด้วยคุณสมบัติที่มีชื่อตามที่ Ken แนะนำจะเป็นการเพิ่มค่าใช้จ่ายในการจัดสรรและปล่อยวัตถุชั่วคราวไปยังการโทรทุกครั้ง การส่งผ่านข้อโต้แย้งปกติตามค่าหรือการอ้างอิงโดยทั่วไปจะมีประสิทธิภาพมากที่สุด สำหรับหลาย ๆ แอปพลิเคชั่น แต่ประสิทธิภาพไม่สำคัญ แต่สำหรับบางแอพพลิเคชั่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.