Có thể gửi một số lượng đối số khác nhau cho hàm JavaScript không?


169

Có thể gửi một số lượng đối số khác nhau cho một hàm JavaScript, từ một mảng không?

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'

Gần đây tôi đã viết rất nhiều Python và đó là một mô hình tuyệt vời để có thể chấp nhận varargs và gửi chúng. ví dụ

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'

JavaScript có thể gửi một mảng được coi mảng đối số không?


4
Lưu ý rằng không nên sử dụng for - invòng lặp với argumentsđối tượng - một 'bình thường' cho vòng lặp lặp lại trên thuộc lengthtính nên được sử dụng thay thế
Yi Jiang

2
nó không bao giờ là một vấn đề, bạn có thể giải thích tại sao lại như vậy không? đối tượng hầu như luôn luôn đủ nhỏ để có sự cải thiện hiệu suất không đáng kể khi sử dụng phiên bản agrument [0..length-1].
Lửa Crow


1
Tôi nghĩ rằng các đối số không phải là một mảng thực tế, mà là một đối tượng đặc biệt, vì vậy cú pháp "trong" có thể không hoạt động, tùy thuộc vào việc triển khai JS.
O'Rooney

Câu trả lời:


206

Cập nhật : Kể từ ES6, bạn chỉ cần sử dụng cú pháp lây lan khi gọi hàm:

func(...arr);

Vì ES6 cũng vậy, nếu bạn muốn coi các đối số của mình là một mảng, bạn cũng có thể sử dụng cú pháp lây lan trong danh sách tham số, ví dụ:

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

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

Và bạn có thể kết hợp nó với các tham số bình thường, ví dụ nếu bạn muốn nhận riêng hai đối số đầu tiên và phần còn lại dưới dạng một mảng:

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

Và có thể hữu ích cho bạn, rằng bạn có thể biết có bao nhiêu đối số mà hàm mong đợi:

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

Nhưng dù sao bạn cũng có thể vượt qua một số lượng đối số tùy ý ...

Cú pháp lây lan ngắn hơn và "ngọt ngào" hơn applyvà nếu bạn không cần đặt thisgiá trị trong lệnh gọi hàm, đây là cách để đi.

Đây là một ví dụ áp dụng , đó là cách trước đây để làm điều đó:

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);

Ngày nay tôi chỉ khuyên bạn chỉ nên sử dụng applynếu bạn cần truyền một số lượng đối số tùy ý từ một mảng đặt thisgiá trị. applytakes là thisgiá trị như các đối số đầu tiên, sẽ được sử dụng trong lệnh gọi hàm, nếu chúng ta sử dụng nullmã không nghiêm ngặt, thistừ khóa sẽ đề cập đến đối tượng Toàn cầu (cửa sổ) bên trong func, trong chế độ nghiêm ngặt, khi sử dụng rõ ràng 'sử dụng nghiêm ngặt 'hoặc trong các mô-đun ES,null sẽ được sử dụng.

Cũng lưu ý rằng argumentsđối tượng không thực sự là một mảng, bạn có thể chuyển đổi nó bằng cách:

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

Và trong ES6:

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

Nhưng argumentsngày nay bạn hiếm khi sử dụng đối tượng nhờ cú pháp lây lan.


1
Cảm ơn, áp dụng thực hiện các mẹo, gọi trong trường hợp này không hoạt động. đó có phải là do viết nhầm?
Lửa quạ

Làm thế nào để bạn làm điều đó nếu chức năng thực sự là một thành viên của một lớp? Thê nay đung không? theObject.member.apply (theObject, argument);
Baxissimo

4
Array.prototype.slice.call(arguments);ngăn chặn tối ưu hóa trình duyệt JS như V8. Chi tiết tại đây
Dheeraj Bhaskar

2
1. Rõ ràng, argumentsđược lên kế hoạch để không được ủng hộ của nhà điều hành lây lan . Tôi không có nguồn cho việc này, nhưng tôi đã nghe thấy nó ở đâu đó. 2. MDN tuyên bố rằng việc sử dụng slicetrên argumentsđối tượng sẽ ngăn chặn tối ưu hóa trong các động cơ như V8. Họ đề nghị sử dụng "hàm tạo mảng bị coi thường" Array.from(arguments), hoặc [...arguments]. Bạn có quan tâm để cập nhật câu trả lời cho ES2015 không?
Braden hay nhất

NB: Tôi đã đi trước và viết câu trả lời của riêng tôi .
Braden hay nhất

75

Bạn thực sự có thể chuyển nhiều giá trị như bạn muốn cho bất kỳ chức năng javascript nào. Các tham số được đặt tên rõ ràng sẽ nhận được một vài giá trị đầu tiên, nhưng TẤT CẢ các tham số sẽ được lưu trữ trong mảng đối số.

Để vượt qua mảng đối số ở dạng "giải nén", bạn có thể sử dụng áp dụng, như vậy (cf Javascript chức năng ):

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

Các toán tử splatlây lan là một phần của ES6, phiên bản tiếp theo của Javascript. Cho đến nay chỉ có Firefox hỗ trợ họ. Mã này hoạt động trong 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');

Lưu ý cú pháp awkard cho lây lan. Cú pháp thông thường của sprintf('The %s %s fox jumps over the %s dog.', ...arr);chưa được hỗ trợ . Bạn có thể tìm thấy một bảng tương thích ES6 ở đây .

Cũng lưu ý việc sử dụng for...of, một bổ sung ES6 khác. Sử dụng for...incho mảng là một ý tưởng tồi .


Cú pháp thông thường để truyền bá hiện được hỗ trợ trong Firefox và Chrome
người dùng

8

Các applychức năng nhận hai đối số; đối tượng thissẽ được liên kết đến và các đối số được biểu diễn bằng một mảng.

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

8

Có hai phương pháp kể từ ES2015.

các argumentsđối tượng

Đối tượng này được xây dựng thành các hàm và nó tham chiếu một cách thích hợp các đối số của hàm. Về mặt kỹ thuật, đây không phải là một mảng, do đó, các hoạt động mảng điển hình sẽ không hoạt động trên đó. Phương pháp được đề xuất là sử dụng Array.fromhoặc toán tử trải để tạo một mảng từ nó.

Tôi đã thấy các câu trả lời khác đề cập đến bằng cách sử dụng slice. Đừng làm vậy. Nó ngăn chặn tối ưu hóa (nguồn: MDN ).

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

Tuy nhiên, tôi sẽ cho rằng đó argumentslà vấn đề vì nó che giấu những gì một chức năng chấp nhận làm đầu vào. Một argumentshàm thường được viết như thế này:

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

Đôi khi, tiêu đề hàm được viết như sau để ghi lại các đối số theo kiểu C:

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

Nhưng đó là hiếm.

Đối với lý do tại sao nó có vấn đề, lấy C, ví dụ. C tương thích ngược với một phương ngữ tiền ANSI cổ của ngôn ngữ được gọi là K & R C. K & R C cho phép các nguyên mẫu hàm có một danh sách đối số trống.

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

ANSI C cung cấp một phương tiện cho varargs( ...) và voidđể chỉ định một danh sách đối số trống.

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

Nhiều người vô tình khai báo các hàm với một unspecifieddanh sách đối số ( int myfunction();), khi họ mong đợi hàm sẽ lấy các đối số bằng không. Đây là một lỗi kỹ thuật vì chức năng sẽ chấp nhận đối số. Bất kỳ số lượng của họ.

Một varargshàm thích hợp trong C có dạng:

int myFunction(int nargs, ...);

Và JavaScript thực sự có một cái gì đó tương tự như thế này.

Thông số lan truyền / phần còn lại

Tôi đã cho bạn thấy toán tử lây lan, thực sự.

...name

Nó khá linh hoạt và cũng có thể được sử dụng trong danh sách đối số của hàm ("tham số phần còn lại") để chỉ định các biến đổi theo kiểu tài liệu độc đáo:

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

Hoặc như một biểu thức lambda:

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

Tôi rất thích các toán tử lây lan. Nó là sạch sẽ và tự tài liệu.


2
cảm ơn bạn đã trả lời cập nhật, bài đăng này có trước ES 2015 vì vậy tôi rất vui vì bạn đã điền vào các tùy chọn gần đây hơn
Fire Crow

5

Nó được gọi là splattoán tử. Bạn có thể làm điều đó bằng JavaScript bằng cách sử dụng 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); 

4

Với ES6, bạn có thể sử dụng các tham số nghỉ ngơi cho varagrs. Cái này lấy danh sách đối số và chuyển nó thành một mảng.

function logArgs(...args) {
    console.log(args.length)
    for(let arg of args) {
        console.log(arg)
    }
}

2

Đây là một chương trình mẫu để tính tổng các số nguyên cho các đối số và mảng biến đổi trên các số nguyên. Hi vọng điêu nay co ich.

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
Một số lưu ý: 1. varkhai báo một function scopebiến. Các từ khóa mới letconsttừ khóa (ES 2015) block scope. Tuy nhiên, trước ES 2015, các biến số vẫn phải được đưa lên đầu hàm như một vấn đề thực tế. 2. hàm của bạn bị hỏng khi được cung cấp 1 giá trị nguyên do sự chấp nhận sai của bạn về một mảng làm đối số (câu hỏi hỏi về varargs; không chấp nhận mảng làm đối số) : CalculateSum(6) -> 0. (tiếp theo trong bài tiếp theo)
Braden Best

1
3. Không sử dụng các chức năng gỡ lỗi như alert. Trên thực tế, không sử dụng alert/prompt/confirm, thời gian. Sử dụng console.logthay thế khi gỡ lỗi. 4. Hàm nên được returning một giá trị, không alerting nó. 5. tránh PascalCasetrừ khi nó đề cập đến một "loại" dữ liệu. Tức là một lớp học. Sử dụng camelCasehoặc under_scorethay thế. 4. Tôi không thích cách bạn khai báo các chức năng của mình. var f = function(){ ... }ngụ ý rằng bạn muốn nó thay đổi tại một số điểm, đó có thể không phải là ý định của bạn. Sử dụng function name(){ ... }cú pháp thông thường thay thế.
Braden hay nhất

Để chứng minh tất cả các điểm phê bình này, đây là phiên bản cải tiến của chức năng trong câu trả lời của bạn.
Braden hay nhất

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

alert(13, 37);

1

Đối với những người được chuyển hướng ở đây từ việc chuyển số lượng đối số biến đổi từ hàm này sang hàm khác ( không nên đánh dấu là trùng lặp của câu hỏi này):

Nếu bạn đang cố gắng chuyển một số lượng đối số khác nhau từ hàm này sang hàm khác, vì JavaScript 1.8.5, bạn chỉ cần gọi apply()hàm thứ hai và truyền argumentstham số:

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

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

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

Lưu ý: Đối số đầu tiên là thistham số cần sử dụng. Passing nullsẽ gọi hàm từ bối cảnh toàn cầu, tức là dưới dạng hàm toàn cầu thay vì phương thức của một số đối tượng.

Dựa theo tài liệu này , đặc tả ECMAScript 5 đã định nghĩa lại apply()phương thức để lấy bất kỳ "đối tượng giống như mảng chung" nào, thay vì nghiêm ngặt là một mảng. Vì vậy, bạn có thể trực tiếp chuyển argumentsdanh sách vào chức năng thứ hai.

Đã thử nghiệm trong Chrome 28.0, Safari 6.0.5 và IE 10. Hãy dùng thử với JSFiddle này .


0

Có bạn có thể vượt qua biến không. của các đối số cho một chức năng. Bạn có thể dùngapply để đạt được điều này.

Ví dụ:

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

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

applyTest.apply(null,arr);

0

Bạn có muốn hàm của bạn phản ứng với một đối số mảng hoặc đối số biến không? Nếu sau này, hãy thử:

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));
};

Nhưng nếu bạn muốn xử lý một đối số mảng

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

  for(var i of arr) {
    console.log(i);
  }
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.