Cách tốt nhất để xác định xem một đối số không được gửi đến hàm JavaScript


236

Bây giờ tôi đã thấy 2 phương thức để xác định xem một đối số đã được truyền cho hàm JavaScript chưa. Tôi đang tự hỏi liệu một phương pháp tốt hơn phương pháp kia hay liệu phương pháp này có tệ không?

 function Test(argument1, argument2) {
      if (Test.arguments.length == 1) argument2 = 'blah';

      alert(argument2);
 }

 Test('test');

Hoặc là

 function Test(argument1, argument2) {
      argument2 = argument2 || 'blah';

      alert(argument2);
 }

 Test('test');

Theo như tôi có thể nói, cả hai đều có kết quả giống nhau, nhưng tôi chỉ sử dụng cái đầu tiên trước khi sản xuất.

Một lựa chọn khác như Tom đã đề cập :

function Test(argument1, argument2) {
    if(argument2 === null) {
        argument2 = 'blah';
    }

    alert(argument2);
}

Theo nhận xét của Juan, sẽ tốt hơn nếu thay đổi đề xuất của Tom thành:

function Test(argument1, argument2) {
    if(argument2 === undefined) {
        argument2 = 'blah';
    }

    alert(argument2);
}

Nó thực sự giống nhau. Nếu bạn luôn có một số lượng đối số tĩnh thì hãy sử dụng phương thức thứ hai, nếu không thì việc lặp lại bằng cách sử dụng mảng đối số có thể dễ dàng hơn.
Luca Matteis

7
Một đối số không được thông qua là không xác định. Thử nghiệm với sự bình đẳng nghiêm ngặt chống lại null sẽ thất bại. Bạn nên sử dụng bình đẳng nghiêm ngặt với không xác định.
Juan Mendes

19
Hãy nhớ rằng: argument2 || 'blah';sẽ dẫn đến 'blah' nếu argument2false(!), Không chỉ đơn giản nếu nó không được xác định. Nếu argument2là boolean và hàm được truyền falsecho nó, dòng đó sẽ trả về 'blah' mặc dù argument2được xác định đúng .
Sandy Gifford

9
@SandyGifford: Cùng một vấn đề nếu argument20, ''hoặc null.
rvighne

@rvighne Rất đúng. Sự giải thích độc đáo của Javascript về các đối tượng và đúc tất cả cùng một lúc là phần tốt nhất và tồi tệ nhất.
Sandy Gifford

Câu trả lời:


274

Có một số cách khác nhau để kiểm tra xem một đối số có được truyền cho hàm không. Ngoài hai câu hỏi bạn đã đề cập trong câu hỏi (bản gốc) của bạn - kiểm tra arguments.lengthhoặc sử dụng ||toán tử để cung cấp các giá trị mặc định - người ta cũng có thể kiểm tra rõ ràng các đối số undefinedthông qua argument2 === undefinedhoặc typeof argument2 === 'undefined'nếu một trong số đó là hoang tưởng (xem bình luận).

Sử dụng ||toán tử đã trở thành tiêu chuẩn thực hiện - tất cả những đứa trẻ mát mẻ làm điều đó - nhưng hãy cẩn thận: Giá trị mặc định sẽ được kích hoạt nếu đánh giá lại lập luận để false, mà có nghĩa là nó thực sự có thể undefined, null, false, 0, ''(hoặc bất cứ điều gì khác mà Boolean(...)lợi nhuận false).

Vì vậy, câu hỏi là khi nào nên sử dụng kiểm tra nào, vì tất cả chúng đều mang lại kết quả hơi khác nhau.

Kiểm tra arguments.lengththể hiện hành vi 'đúng nhất', nhưng có thể không khả thi nếu có nhiều hơn một đối số tùy chọn.

Thử nghiệm cho undefinedlần tiếp theo là 'tốt nhất' - chỉ 'thất bại' nếu hàm được gọi rõ ràng bằng một undefinedgiá trị, trong mọi trường hợp có thể được xử lý giống như cách bỏ qua đối số.

Việc sử dụng ||toán tử có thể kích hoạt việc sử dụng giá trị mặc định ngay cả khi một đối số hợp lệ được cung cấp. Mặt khác, hành vi của nó thực sự có thể được mong muốn.

Tóm lại: Chỉ sử dụng nó nếu bạn biết bạn đang làm gì!

Theo tôi, sử dụng ||cũng là cách để đi nếu có nhiều hơn một đối số tùy chọn và người ta không muốn truyền một đối tượng theo nghĩa đen như một cách giải quyết cho các tham số được đặt tên.

Một cách hay khác để cung cấp các giá trị mặc định arguments.lengthcó thể sử dụng bằng cách rơi vào nhãn của câu lệnh chuyển đổi:

function test(requiredArg, optionalArg1, optionalArg2, optionalArg3) {
    switch(arguments.length) {
        case 1: optionalArg1 = 'default1';
        case 2: optionalArg2 = 'default2';
        case 3: optionalArg3 = 'default3';
        case 4: break;
        default: throw new Error('illegal argument count')
    }
    // do stuff
}

Điều này có nhược điểm là ý định của lập trình viên không rõ ràng (trực quan) và sử dụng 'số ma thuật'; do đó nó có thể dễ bị lỗi.


42
Bạn thực sự nên kiểm tra typeof argument2 === "không xác định", trong trường hợp ai đó định nghĩa "không xác định".
JW.

133
Tôi sẽ thêm một thông báo - nhưng những kẻ khốn bệnh hoạn nào làm những việc như vậy?
Christoph

12
không xác định là một biến trong không gian toàn cầu. Việc tìm kiếm biến đó trong phạm vi toàn cầu chậm hơn so với biến trong phạm vi cục bộ. Nhưng nhanh nhất là sử dụng typeof x === "không xác định"
một số

5
Hấp dẫn. Mặt khác, so sánh === không xác định có thể nhanh hơn so với so sánh chuỗi. Các thử nghiệm của tôi dường như chỉ ra rằng bạn đúng, mặc dù: x === không xác định cần thiết ~ 1,5 lần thời gian của typeof x === 'không xác định'
Christoph

4
@Cristoph: sau khi đọc bình luận của bạn, tôi hỏi xung quanh. Tôi đã có thể chứng minh rằng so sánh chuỗi chắc chắn không (chỉ) bằng so sánh con trỏ vì so sánh một chuỗi khổng lồ mất nhiều thời gian hơn một chuỗi nhỏ. Tuy nhiên, việc so sánh hai chuỗi khổng lồ chỉ thực sự chậm nếu chúng được xây dựng với sự kết hợp, nhưng thực sự nhanh nếu chuỗi thứ hai được tạo như một nhiệm vụ từ đầu tiên. Vì vậy, nó có thể hoạt động bằng một bài kiểm tra con trỏ và sau đó là kiểm tra bằng chữ cái Vì vậy, vâng, tôi đầy rác rưởi :) cảm ơn vì đã khai sáng cho tôi ...
Juan Mendes

17

Nếu bạn đang sử dụng jQuery, một tùy chọn tốt (đặc biệt đối với các tình huống phức tạp) là sử dụng phương thức mở rộng của jQuery .

function foo(options) {

    default_options = {
        timeout : 1000,
        callback : function(){},
        some_number : 50,
        some_text : "hello world"
    };

    options = $.extend({}, default_options, options);
}

Nếu bạn gọi hàm thì như thế này:

foo({timeout : 500});

Biến tùy chọn sau đó sẽ là:

{
    timeout : 500,
    callback : function(){},
    some_number : 50,
    some_text : "hello world"
};

15

Đây là một trong số ít trường hợp tôi tìm thấy bài kiểm tra:

if(! argument2) {  

}

hoạt động khá độc đáo và mang hàm ý chính xác về mặt cú pháp.

(Với hạn chế đồng thời rằng tôi sẽ không cho phép giá trị null hợp pháp argument2có ý nghĩa khác; nhưng điều đó thực sự khó hiểu.)

BIÊN TẬP:

Đây là một ví dụ thực sự tốt về sự khác biệt về phong cách giữa các ngôn ngữ được đánh máy lỏng lẻo và đánh máy mạnh; và một tùy chọn phong cách mà javascript dành cho spades.

Sở thích cá nhân của tôi (không có ý chỉ trích dành cho các sở thích khác) là sự tối giản. Mã càng ít phải nói, miễn là tôi nhất quán và súc tích, thì càng ít người khác phải hiểu để suy luận chính xác ý nghĩa của tôi.

Một ý nghĩa của sở thích đó là tôi không muốn - không thấy nó hữu ích - chồng chất một loạt các bài kiểm tra phụ thuộc loại. Thay vào đó, tôi cố gắng làm cho mã có nghĩa là nó trông như thế nào; và chỉ kiểm tra những gì tôi thực sự sẽ cần kiểm tra.

Một trong những tình tiết tăng nặng mà tôi tìm thấy trong mã của một số người khác là cần phải tìm hiểu xem họ có mong đợi hay không, trong bối cảnh lớn hơn, có thực sự gặp phải các trường hợp mà họ đang thử nghiệm hay không. Hoặc nếu họ đang cố gắng kiểm tra mọi thứ có thể, thì có khả năng họ không lường trước được bối cảnh hoàn toàn đủ. Điều đó có nghĩa là tôi cuối cùng cần phải theo dõi chúng một cách triệt để theo cả hai hướng trước khi tôi có thể tự tin tái cấu trúc hoặc sửa đổi bất cứ điều gì. Tôi cho rằng có một cơ hội tốt mà họ có thể đã thực hiện các thử nghiệm khác nhau đó bởi vì họ thấy trước các trường hợp cần thiết (và điều này thường không rõ ràng đối với tôi).

(Tôi cho rằng một nhược điểm nghiêm trọng trong cách những người này sử dụng ngôn ngữ động. Thông thường mọi người không muốn từ bỏ tất cả các bài kiểm tra tĩnh, và cuối cùng giả mạo nó.)

Tôi đã thấy điều này rõ ràng nhất khi so sánh mã ActionScript 3 toàn diện với mã javascript thanh lịch. AS3 có thể gấp 3 hoặc 4 lần số lượng js và độ tin cậy mà tôi nghi ngờ ít nhất là không tốt hơn, chỉ vì số lượng (3-4X) của các quyết định mã hóa đã được đưa ra.

Như bạn nói, Shog9, YMMV. : D


1
if (! argument2) argument2 = 'default' tương đương với argument2 = argument2 || 'mặc định' - Tôi thấy phiên bản thứ hai trực quan dễ chịu hơn ...
Christoph

5
Và tôi thấy nó dài dòng và mất tập trung hơn; Nhưng đó là sở thích cá nhân, tôi chắc chắn.
dkretz

4
@le dorfier: nó cũng loại trừ việc sử dụng các chuỗi rỗng, 0 và boolean false.
Shog9

@le dorfier: ngoài tính thẩm mỹ, có một điểm khác biệt chính: cái sau có hiệu quả tạo ra một con đường thực thi thứ hai, có thể cám dỗ những người duy trì bất cẩn để thêm hành vi vượt quá sự gán đơn giản của một giá trị mặc định. YMMV, tất nhiên.
Shog9

3
nếu tham số2 là boolean === false; hoặc một hàm {return false;}?
fresko

7

Có sự khác biệt đáng kể. Hãy thiết lập một số trường hợp thử nghiệm:

var unused; // value will be undefined
Test("test1", "some value");
Test("test2");
Test("test3", unused);
Test("test4", null);
Test("test5", 0);
Test("test6", "");

Với phương pháp đầu tiên bạn mô tả, chỉ có thử nghiệm thứ hai sẽ sử dụng giá trị mặc định. Phương pháp thứ hai sẽ mặc định tất cả nhưng người đầu tiên (như JS sẽ chuyển đổi undefined, null, 0, và ""vào boolean false. Và nếu bạn đã sử dụng phương pháp của Tom, chỉ có thử nghiệm thứ tư sẽ sử dụng mặc định!

Phương pháp bạn chọn thực sự phụ thuộc vào hành vi dự định của bạn. Nếu các giá trị khác ngoài mức undefinedcho phép argument2, thì có thể bạn sẽ muốn một số biến thể ở lần đầu tiên; nếu muốn một giá trị khác không, không rỗng, không rỗng, thì phương thức thứ hai là lý tưởng - thực sự, nó thường được sử dụng để nhanh chóng loại bỏ một loạt các giá trị như vậy khỏi xem xét.


7
url = url === undefined ? location.href : url;

Xương trần trả lời. Một số lời giải thích sẽ không đau.
Paul Rooney

Đó là một toán tử ternary nói chính xác: Nếu url không được xác định (thiếu), hãy đặt biến url là location.href (trang web hiện tại), nếu không thì đặt biến url là url được xác định.
rmooney

5

Trong ES6 (ES2015), bạn có thể sử dụng các tham số mặc định

function Test(arg1 = 'Hello', arg2 = 'World!'){
  alert(arg1 + ' ' +arg2);
}

Test('Hello', 'World!'); // Hello World!
Test('Hello'); // Hello World!
Test(); // Hello World!


Câu trả lời này thực sự thú vị và có thể hữu ích cho op. Mặc dù nó không thực sự trả lời câu hỏi
Ulysse BN

Như tôi thấy - anh ấy muốn xác định arg nếu nó không được xác định, vì vậy tôi đã đăng một số thông tin hữu ích
Andriy2

Quan điểm của tôi là bạn không trả lời Cách tốt nhất để xác định xem một đối số không được gửi đến hàm JavaScript . Nhưng câu hỏi có thể được trả lời bằng các đối số mặc định: ví dụ: đặt tên cho các đối số của bạn "default value"và kiểm tra xem giá trị có thực sự không "default value".
Ulysse BN

4

Tôi xin lỗi, tôi vẫn chưa thể bình luận, vì vậy để trả lời câu trả lời của Tom ... Trong javascript (không xác định! = Null) == false Trên thực tế chức năng đó không hoạt động với "null", bạn nên sử dụng "không xác định"


1
Và Tom đã nhận được hai câu trả lời cho một câu trả lời sai - thật tuyệt khi biết các hệ thống cộng đồng này hoạt động tốt như thế nào;)
Christoph

3

Tại sao không sử dụng !!toán tử? Toán tử này, được đặt trước biến, biến nó thành boolean (nếu tôi đã hiểu rõ), !!undefined!!null(và thậm chí !!NaN, có thể khá thú vị) sẽ trở lại false.

Đây là một ví dụ:

function foo(bar){
    console.log(!!bar);
}

foo("hey") //=> will log true

foo() //=> will log false

Không hoạt động với chuỗi boolean true, zero và chuỗi rỗng. Ví dụ: foo (0); sẽ đăng nhập sai, nhưng foo (1) sẽ đăng nhập đúng
rosell.dk

2

Có thể thuận tiện để tiếp cận phát hiện đối số bằng cách gợi lên chức năng của bạn với Đối tượng thuộc tính tùy chọn:

function foo(options) {
    var config = { // defaults
        list: 'string value',
        of: [a, b, c],
        optional: {x: y},
        objects: function(param){
           // do stuff here
        }
    }; 
    if(options !== undefined){
        for (i in config) {
            if (config.hasOwnProperty(i)){
                if (options[i] !== undefined) { config[i] = options[i]; }
            }
        }
    }
}

2

Cũng có một cách khó để tìm, cho dù một tham số có được truyền cho hàm hay không . Hãy xem ví dụ dưới đây:

this.setCurrent = function(value) {
  this.current = value || 0;
};

Điều này cần thiết có nghĩa là nếu giá trị của valuekhông có / được thông qua - đặt nó thành 0.

Khá tuyệt nhỉ!


1
Điều này thực sự có nghĩa là "nếu valuetương đương với false, đặt nó thành 0." Đây là một sự khác biệt tinh tế nhưng rất quan trọng.
Charles Wood

@CharlesWood Giá trị không được thông qua / hiện tại chỉ là sai
Mohammed Zameer

1
Chắc chắn, nếu điều đó phù hợp với yêu cầu cho chức năng của bạn. Nhưng nếu, ví dụ, nếu tham số của bạn là boolean, truevà sau đó falselà các giá trị hợp lệ và bạn có thể muốn có hành vi thứ ba khi tham số không được thông qua (đặc biệt là nếu hàm có nhiều hơn một tham số).
Charles Wood

1
Và tôi nên thừa nhận rằng đây là một cuộc tranh luận lớn trong khoa học máy tính và có thể cuối cùng chỉ là vấn đề quan điểm: D
Charles Wood

@CharlesWood xin lỗi vì đến bữa tiệc muộn. Tôi đề nghị bạn thêm những câu này vào câu trả lời bằng tùy chọn chỉnh sửa
Mohammed Zameer 17/2/18

1

Đôi khi bạn cũng có thể muốn kiểm tra loại, đặc biệt nếu bạn đang sử dụng chức năng như getter và setter. Mã sau đây là ES6 (sẽ không chạy trong EcmaScript 5 trở lên):

class PrivateTest {
    constructor(aNumber) {
        let _aNumber = aNumber;

        //Privileged setter/getter with access to private _number:
        this.aNumber = function(value) {
            if (value !== undefined && (typeof value === typeof _aNumber)) {
                _aNumber = value;
            }
            else {
                return _aNumber;
            }
        }
    }
}

0
function example(arg) {
  var argumentID = '0'; //1,2,3,4...whatever
  if (argumentID in arguments === false) {
    console.log(`the argument with id ${argumentID} was not passed to the function`);
  }
}

Bởi vì mảng kế thừa từ Object.prototype. Xem xét để làm cho thế giới tốt hơn.


-1

fnCalledFunction (Param1, Param2, window.YourOptionalParameter)

Nếu hàm trên được gọi từ nhiều nơi và bạn chắc chắn 2 tham số đầu tiên được truyền từ mọi nơi nhưng không chắc chắn về tham số thứ 3 thì bạn có thể sử dụng cửa sổ.

window.param3 sẽ xử lý nếu nó không được xác định từ phương thức người gọ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.