เป็นไปได้ไหมที่จะส่งอาร์กิวเมนต์จำนวนตัวแปรไปยังฟังก์ชัน JavaScript


171

เป็นไปได้ไหมที่จะส่งอาร์กิวเมนต์จำนวนตัวแปรไปยังฟังก์ชัน JavaScript จากอาร์เรย์

var arr = ['a','b','c']

var func = function()
{
    // debug 
    alert(arguments.length);
    //
    for(arg in arguments)
        alert(arg);
}

func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(arr); // prints 1, then 'Array'

ฉันเพิ่งเขียน Python จำนวนมากและมันเป็นรูปแบบที่ยอดเยี่ยมที่สามารถรับ varargs และส่งได้ เช่น

def func(*args):
   print len(args)
   for i in args:
       print i

func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(*arr) // prints 4 which is what I want, then 'a','b','c','d'

เป็นไปได้ไหมที่จาวาสคริปต์ในการส่งอาเรย์จะถือว่าเป็นอาเรย์อาร์กิวเมนต์?


4
โปรดทราบว่าไม่ควรใช้การfor - inวนซ้ำกับargumentsวัตถุ - lengthควรใช้'การวนซ้ำ' แบบวนซ้ำทั่วคุณสมบัติแทน
Jiang Jiang

2
ไม่เคยมีปัญหาคุณสามารถอธิบายว่าทำไมถึงเป็นเช่นนั้น? วัตถุอาร์กิวเมนต์มีขนาดเล็กพอที่จะปรับปรุงประสิทธิภาพการทำงานเล็กน้อยสำหรับการใช้เวอร์ชัน [0..length-1]
ไฟอีกา

เป็นไปได้ที่จะซ้ำซ้อนของจำนวนตัวแปร JavaScript ในการทำงาน
Anderson Green

1
ฉันคิดว่าข้อโต้แย้งไม่ใช่อาร์เรย์จริง แต่เป็นวัตถุพิเศษดังนั้นไวยากรณ์ "ใน" อาจไม่ทำงานขึ้นอยู่กับการนำ JS ไปใช้
O'Rooney

คำตอบ:


209

อัปเดต : ตั้งแต่ ES6 คุณสามารถใช้ไวยากรณ์การกระจายเมื่อเรียกใช้ฟังก์ชัน:

func(...arr);

ตั้งแต่ ES6 เช่นกันหากคุณคาดว่าจะถือว่าอาร์กิวเมนต์ของคุณเป็นอาร์เรย์คุณสามารถใช้ไวยากรณ์การแพร่กระจายในรายการพารามิเตอร์ได้เช่น:

function func(...args) {
  args.forEach(arg => console.log(arg))
}

const values = ['a', 'b', 'c']
func(...values)
func(1, 2, 3)

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

function func(first, second, ...theRest) {
  //...
}

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

var test = function (one, two, three) {}; 
test.length == 3;

แต่อย่างไรก็ตามคุณสามารถผ่านจำนวนอาร์กิวเมนต์โดยพลการ ...

ไวยากรณ์การแพร่กระจายสั้นกว่าและ "หวาน" มากกว่าapplyและถ้าคุณไม่จำเป็นต้องตั้งthisค่าในการเรียกใช้ฟังก์ชันนี่เป็นวิธีที่จะดำเนินการ

นี่คือตัวอย่างการใช้งานซึ่งเป็นวิธีแรกที่จะทำ:

var arr = ['a','b','c'];

function func() {
  console.log(this); // 'test'
  console.log(arguments.length); // 3

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

};

func.apply('test', arr);

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

โปรดทราบด้วยว่าargumentsวัตถุนั้นไม่ใช่ Array จริงๆคุณสามารถแปลงได้โดย:

var argsArray = Array.prototype.slice.call(arguments);

และใน ES6:

const argsArray = [...arguments] // or Array.from(arguments)

แต่คุณไม่ค่อยได้ใช้argumentsวัตถุโดยตรงในปัจจุบันด้วยไวยากรณ์สเปรด


1
ขอขอบคุณใช้เคล็ดลับการโทรในกรณีนี้ไม่ทำงาน ถูกเขียนโดยไม่ได้ตั้งใจ?
ไฟอีกา

คุณจะทำอย่างไรถ้าฟังก์ชั่นเป็นสมาชิกของชั้นเรียนจริง? ถูกต้องหรือไม่ theObject.member.apply (theObject, อาร์กิวเมนต์);
Baxissimo

4
Array.prototype.slice.call(arguments);ป้องกันการเพิ่มประสิทธิภาพเบราว์เซอร์ JS เช่น V8 รายละเอียดที่นี่
Dheeraj Bhaskar

2
1.เห็นได้ชัดว่าargumentsมีการวางแผนที่จะเลิกในความโปรดปรานของผู้ประกอบการแพร่กระจาย ฉันไม่มีที่มาสำหรับเรื่องนี้ แต่ฉันได้ยินที่ใดที่หนึ่ง 2. MDN ระบุว่าการใช้sliceบนargumentsวัตถุนั้นป้องกันการปรับให้เหมาะสมในเอ็นจิ้นอย่าง V8 พวกเขาขอแนะนำให้ใช้ "ตัวสร้างอาร์เรย์ดูหมิ่น" หรือArray.from(arguments) [...arguments]คุณสนใจที่จะอัพเดทคำตอบสำหรับ ES2015 หรือไม่?
Braden ที่ดีที่สุด

หมายเหตุ: ฉันไปข้างหน้าและเขียนคำตอบของตัวเอง
Braden ที่ดีที่สุด

75

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

ในการส่งอาเรย์อาร์กิวเมนต์ในรูปแบบ "unpacked" คุณสามารถใช้นำไปใช้เช่น (cf Functional Javascript ):

var otherFunc = function() {
   alert(arguments.length); // Outputs: 10
}

var myFunc = function() {
  alert(arguments.length); // Outputs: 10
  otherFunc.apply(this, arguments);
}
myFunc(1,2,3,4,5,6,7,8,9,10);

23

ตัวดำเนินการsplatและspreadเป็นส่วนหนึ่งของ ES6 ซึ่งเป็น Javascript เวอร์ชันถัดไปที่วางแผนไว้ จนถึง Firefox เท่านั้นที่สนับสนุนพวกเขา รหัสนี้ใช้งานได้ใน FF16 +:

var arr = ['quick', 'brown', 'lazy'];

var sprintf = function(str, ...args)
{
    for (arg of args) {
        str = str.replace(/%s/, arg);
    }
    return str;
}

sprintf.apply(null, ['The %s %s fox jumps over the %s dog.', ...arr]);
sprintf('The %s %s fox jumps over the %s dog.', 'slow', 'red', 'sleeping');

หมายเหตุไวยากรณ์ awkard สำหรับการแพร่กระจาย ไวยากรณ์ปกติsprintf('The %s %s fox jumps over the %s dog.', ...arr);จะยังไม่สนับสนุน คุณสามารถค้นหาตารางการทำงานร่วมกัน ES6 ที่นี่

หมายเหตุการใช้งานfor...ofนอกจากนี้ ES6 อื่น ใช้for...inสำหรับอาร์เรย์เป็นความคิดที่ดี


ขณะนี้รองรับไวยากรณ์ปกติสำหรับสเปรดใน Firefox และ Chrome
ผู้ใช้

8

applyฟังก์ชั่นใช้เวลาสองข้อโต้แย้ง; วัตถุthisจะถูกรวมเข้ากับและอาร์กิวเมนต์ที่ถูกแทนด้วยอาร์เรย์

some_func = function (a, b) { return b }
some_func.apply(obj, ["arguments", "are", "here"])
// "are"

8

มีสองวิธี ณ ES2015

argumentsวัตถุ

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

ผมเคยเห็นคำตอบอื่น ๆ sliceพูดถึงการใช้ อย่าทำอย่างนั้น จะป้องกันการเพิ่มประสิทธิภาพ (ที่มา: MDN )

Array.from(arguments)
[...arguments]

อย่างไรก็ตามฉันจะเถียงว่าargumentsเป็นปัญหาเพราะมันซ่อนสิ่งที่ฟังก์ชั่นยอมรับว่าเป็นอินพุต argumentsฟังก์ชั่นมักจะเขียนเช่นนี้

function mean(){
    let args = [...arguments];
    return args.reduce((a,b)=>a+b) / args.length;
}

บางครั้งส่วนหัวของฟังก์ชั่นจะถูกเขียนเหมือนดังต่อไปนี้เพื่อบันทึกอาร์กิวเมนต์ในรูปแบบ C-like

function mean(/* ... */){ ... }

แต่นั่นหายาก

สำหรับสาเหตุที่เป็นปัญหาให้ยกตัวอย่างเช่น C C ย้อนหลังเข้ากันได้กับภาษา pre-ANSI โบราณของภาษาที่รู้จักกันในชื่อ K&R C. K&R C ช่วยให้ต้นแบบฟังก์ชั่นมีรายการอาร์กิวเมนต์ที่ว่างเปล่า

int myFunction();
/* This function accepts unspecified parameters */

ANSI C จัดให้มีสิ่งอำนวยความสะดวกสำหรับvarargs( ...) และvoidเพื่อระบุรายการอาร์กิวเมนต์ว่าง

int myFunction(void);
/* This function accepts no parameters */

หลายคนประกาศฟังก์ชั่นโดยไม่ตั้งใจด้วยunspecifiedรายการอาร์กิวเมนต์ ( int myfunction();) เมื่อพวกเขาคาดหวังว่าฟังก์ชั่นจะใช้อาร์กิวเมนต์เป็นศูนย์ นี่เป็นข้อผิดพลาดทางเทคนิคเพราะฟังก์ชันจะยอมรับอาร์กิวเมนต์ ไม่ จำกัด จำนวน

varargsฟังก์ชั่นที่เหมาะสมใน C ใช้แบบฟอร์ม:

int myFunction(int nargs, ...);

และจาวาสคริปต์ก็มีบางสิ่งที่คล้ายกันกับเรื่องนี้

กระจายตัวดำเนินการ / พารามิเตอร์ส่วนที่เหลือ

ฉันได้แสดงโอเปอเรเตอร์การแพร่กระจายให้คุณแล้ว

...name

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

function mean(...args){
    return args.reduce((a,b)=>a+b) / args.length;
}

หรือเป็นการแสดงออกของแลมบ์ดา:

((...args)=>args.reduce((a,b)=>a+b) / args.length)(1,2,3,4,5); // 3

ฉันชอบผู้ดำเนินการแพร่กระจายมาก มันสะอาดและจัดทำเอกสารด้วยตนเอง


2
ขอบคุณสำหรับคำตอบที่ได้รับการปรับปรุงโพสต์นี้มีมาก่อน ES 2015 ดังนั้นฉันดีใจที่คุณได้กรอกข้อมูลในตัวเลือกล่าสุด
Fire Crow

5

มันเรียกว่าsplatผู้ประกอบการ คุณสามารถทำได้ใน JavaScript โดยใช้apply:

var arr = ['a','b','c','d'];
var func = function() {
    // debug 
    console.log(arguments.length);
    console.log(arguments);
}
func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(arr); // prints 1, then 'Array'
func.apply(null, arr); 


2

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

var CalculateSum = function(){
    calculateSumService.apply( null, arguments );
}

var calculateSumService = function(){
    var sum = 0;

    if( arguments.length === 1){
        var args = arguments[0];

        for(var i = 0;i<args.length; i++){
           sum += args[i]; 
        }
    }else{
        for(var i = 0;i<arguments.length; i++){
           sum += arguments[i]; 
        }        
    }

     alert(sum);

}


//Sample method call

// CalculateSum(10,20,30);         

// CalculateSum([10,20,30,40,50]);

// CalculateSum(10,20);

1
หมายเหตุบางประการ: 1. varประกาศfunction scopeตัวแปร ใหม่letและconstคำหลัก (ES 2015) ประกาศblock scopeตัวแปร ก่อน ES 2015 แม้ว่าตัวแปรควรได้รับการยกขึ้นไปด้านบนของฟังก์ชั่นเป็นเรื่องของการปฏิบัติ 2. แบ่งการทำงานของคุณเมื่อได้รับค่าจำนวนเต็ม 1 เพราะได้รับการยอมรับที่ผิดพลาดของคุณของอาร์เรย์เป็นอาร์กิวเมนต์เป็นนักการ (คำถามที่ถามเกี่ยวกับ varargs; CalculateSum(6) -> 0ไม่ยอมรับอาร์เรย์เป็นอาร์กิวเมนต์): (ดำเนินการในการโพสต์ถัดไป)
Braden ที่ดีที่สุด

1
3. alertอย่าใช้ฟังก์ชั่นการแก้ปัญหาเช่น ที่จริงแล้วอย่าใช้alert/prompt/confirmช่วงเวลา ใช้console.logแทนเมื่อทำการดีบั๊ก 4. ฟังก์ชั่นควรจะreturnมีค่าไม่ใช่alertไอเอ็นจี 5. หลีกเลี่ยงPascalCaseเว้นแต่จะอ้างถึง "ประเภท" ของข้อมูล นั่นคือชั้นเรียน ใช้camelCaseหรือunder_scoreแทน 4. ฉันไม่ชอบวิธีที่คุณประกาศฟังก์ชั่นของคุณ var f = function(){ ... }หมายความว่าคุณต้องการให้มันเปลี่ยนแปลงในบางจุดซึ่งอาจไม่ใช่ความตั้งใจของคุณ ใช้function name(){ ... }ไวยากรณ์ทั่วไปแทน
Braden ที่ดีที่สุด

เพื่อแสดงให้เห็นถึงจุดวิจารณ์ทั้งหมดเหล่านี้นี่เป็นฟังก์ชั่นที่ปรับปรุงใหม่ในคำตอบของคุณ
Braden ที่ดีที่สุด

2
(function(window) {
  var proxied = window.alert;
  window.alert = function() {
    return proxied.apply(window, arguments);
  };
}(this));

alert(13, 37);

1

สำหรับผู้ที่ถูกเปลี่ยนเส้นทางที่นี่จากการผ่านจำนวนตัวแปรของการขัดแย้งจากฟังก์ชั่นหนึ่งไปยังอีกฟังก์ชั่น (ซึ่งไม่ควรทำเครื่องหมายว่าซ้ำซ้อนของคำถามนี้):

หากคุณพยายามส่งจำนวนอาร์กิวเมนต์ที่หลากหลายจากฟังก์ชันหนึ่งไปยังอีกฟังก์ชันหนึ่งเนื่องจาก JavaScript 1.8.5 คุณสามารถเรียกapply()ใช้ฟังก์ชันที่สองและส่งผ่านargumentsพารามิเตอร์ได้ดังนี้

var caller = function()
{
    callee.apply( null, arguments );
}

var callee = function()
{
    alert( arguments.length );
}

caller( "Hello", "World!", 88 ); // Shows "3".

หมายเหตุ:อาร์กิวเมนต์แรกคือthisพารามิเตอร์ที่จะใช้ การส่งผ่านnullจะเรียกใช้ฟังก์ชันจากบริบทส่วนกลางเช่นเป็นฟังก์ชันสากลแทนวิธีการของวัตถุบางอย่าง

ตามเอกสารนี้ข้อมูลจำเพาะของ ECMAScript 5 ได้นิยามapply()วิธีการที่จะใช้ "วัตถุที่มีลักษณะเหมือนอาร์เรย์ทั่วไป" แทนการใช้อาเรย์อย่างเคร่งครัด ดังนั้นคุณสามารถส่งargumentsรายการไปยังฟังก์ชันที่สองได้โดยตรง

ทดสอบใน Chrome 28.0, Safari 6.0.5 และ IE 10 ลองใช้ด้วยJSFiddleนี้


0

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

เช่น:

var arr = ["Hi","How","are","you"];

function applyTest(){
    var arg = arguments.length;
    console.log(arg);
}

applyTest.apply(null,arr);

0

คุณต้องการให้ฟังก์ชั่นของคุณตอบสนองต่ออาเรย์อาร์กิวเมนต์หรืออาร์กิวเมนต์ตัวแปรหรือไม่? หากหลังลอง:

var func = function(...rest) {
  alert(rest.length);

  // In JS, don't use for..in with arrays
  // use for..of that consumes array's pre-defined iterator
  // or a more functional approach
  rest.forEach((v) => console.log(v));
};

แต่ถ้าคุณต้องการจัดการกับอาเรย์อาร์กิวเมนต์

var fn = function(arr) {
  alert(arr.length);

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