Як найкраще визначити, чи не надсилається аргумент функції JavaScript


236

Зараз я бачив 2 способи визначення того, чи передано аргумент функції JavaScript. Мені цікаво, чи один метод кращий за інший, чи один із них просто поганий?

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

      alert(argument2);
 }

 Test('test');

Або

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

      alert(argument2);
 }

 Test('test');

Наскільки я можу сказати, вони обоє призводять до одного і того ж, але я використовував лише перший раніше у виробництві.

Ще один варіант, як згадував Том :

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

    alert(argument2);
}

Відповідно до коментаря Хуана, було б краще змінити пропозицію Тома на:

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

    alert(argument2);
}

Це дійсно те саме. Якщо у вас завжди є статична кількість аргументів, тоді перейдіть за другим методом, інакше, мабуть, простіше пройти ітерацію за допомогою масиву аргументів.
Лука Маттейс

7
Аргумент, який не був прийнятий, вважається невизначеним. Тестування з суворою рівністю проти нуля не вдасться. Вам слід використовувати сувору рівність з невизначеною.
Хуан Мендес

19
Пам'ятайте: argument2 || 'blah';в результаті буде "blah", якщо argument2це false(!), А не просто, якщо це не визначено. Якщо argument2булева функція, і функція передана falseдля неї, цей рядок поверне 'blah', незважаючи на argument2те, що він правильно визначений .
Сенді Гіффорд

9
@SandyGifford: Та ж проблема , якщо argument2є 0, ''або null.
rvighne

@rvighne Дуже правда. Унікальна інтерпретація об'єктів і кастингу Javascript - це все найкраще та найгірше.
Сенді Гіффорд

Відповіді:


274

Існує кілька різних способів перевірити, чи був переданий аргумент функції. На додаток до двох згаданих вами у своєму (оригінальному) запитанні - перевірка arguments.lengthабо використання ||оператора для надання значень за замовчуванням - можна також явно перевірити аргументи для undefinedчерез argument2 === undefinedабо, typeof argument2 === 'undefined'якщо параноїдний (див. Коментарі).

Використання ||оператора стало стандартною практикою - все круті діти роблять це - але будьте обережні: Значення за замовчуванням буде спрацьовувати , якщо аргумент має значення false, що означає , що вона може бути на самому ділі undefined, null, false, 0, ''(або що - небудь ще , для якого Boolean(...)повертається false).

Тож питання в тому, коли використовувати яку перевірку, оскільки всі вони дають трохи різні результати.

Перевірка arguments.lengthдемонструє «найбільш правильну» поведінку, але це може бути неможливо здійснити, якщо є більше одного необов’язкового аргументу.

Наступний тест на undefined"кращий" - він "не вдається", лише якщо функція явно викликається undefinedзначенням, яке, швидше за все, слід трактувати так само, як і пропускання аргументу.

Використання ||оператора може викликати використання значення за замовчуванням, навіть якщо надано дійсний аргумент. З іншого боку, її поведінка може бути насправді бажаною.

Підводячи підсумок: використовуйте його лише якщо ви знаєте, що робите!

На мою думку, використання ||також є шляхом, якщо існує більше одного необов’язкового аргументу і не хочеться передавати об'єкт буквально як вирішення для названих параметрів.

Ще один приємний спосіб надання значень за замовчуванням за допомогою arguments.lengthможливого, пропустивши мітки оператора перемикача:

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
}

Це має і мінус, що намір програміста не є (візуально) очевидним і використовує "магічні числа"; тому, можливо, схильні до помилок.


42
Ви дійсно повинні перевірити typeof argument2 === "undefined", якщо хтось визначить "undefined".
JW.

133
Я додам повідомлення - але які хворі сволочі роблять такі речі?
Крістоф

12
undefined - змінна в глобальному просторі. Шукати цю змінну в глобальному масштабі повільніше, ніж змінну в локальній області. Але найшвидше - це використовувати typeof x === "undefined"
якийсь

5
Цікаво. З іншого боку, порівняння === undefined може бути швидше порівняння рядків. Мої тести, схоже, вказують на те, що ти маєш рацію: x === неозначено потрібно ~ 1,5 рази час typeof x === 'undefined'
Крістоф

4
@Cristoph: прочитавши ваш коментар, я поцікавився. Я зміг довести, що порівняння рядків, безумовно, не є (лише) порівнянням покажчиків, оскільки порівняння гігантської рядки займає більше часу, ніж невелика. Однак порівняння двох гігантських рядків дійсно повільне, лише якщо вони були побудовані конкатенацією, але дуже швидко, якщо другий був створений як завдання з першого. Тож, ймовірно, працює тест на вказівник, а за ним буквене письмове тестування. Отже, так, я був повний лайну :) дякую, що мене просвітили ...
Хуан Мендес

17

Якщо ви використовуєте jQuery, одним із приємних варіантів (особливо для складних ситуацій) є використання методу розширення jQuery .

function foo(options) {

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

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

Якщо ви викликаєте функцію, то наступним чином:

foo({timeout : 500});

Змінна параметрів тоді буде:

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

15

Це один з небагатьох випадків, коли я знаходжу тест:

if(! argument2) {  

}

працює досить добре і синтаксично несе правильне значення.

(З одночасним обмеженням, що я не допустив би законного нульового значення, argument2яке має якесь інше значення; але це було б дійсно заплутано.)

Редагувати:

Це дійсно хороший приклад стилістичної різниці між слабко набраними та сильно набраними мовами; і стилістичний варіант, який надає javas в лопатах.

Мої особисті переваги (без критики, що стосується інших уподобань) - мінімалізм. Чим менше код повинен сказати, чим я послідовніший і лаконічніший, тим менше хтось інший повинен зрозуміти, щоб правильно зробити моє значення.

Одним із наслідків цього уподобання є те, що я не хочу - не вважаю корисним - збирати купу тестів на залежність від типу. Натомість я намагаюся зробити так, щоб код мав на увазі те, на що він схожий; і тестувати лише те, на що я дійсно знадобиться для перевірки.

Одне із загострень, які я знаходжу в коді інших народів, потребує з'ясування того, чи очікують вони в більш широкому контексті насправді натрапити на справи, на які вони тестуються. Або якщо вони намагаються перевірити на все можливе, на шанс, що вони не передбачать контекст достатньо. Це означає, що мені потрібно вичерпно відстежувати їх в обох напрямках, перш ніж я зможу впевнено переробити або змінити що-небудь. Я вважаю, що є хороший шанс, що вони могли б поставити ті різні тести на місце, тому що вони передбачили обставини, коли вони будуть потрібні (і які, як правило, мені не здаються).

(Я вважаю, що це серйозний мінус у тому, як ці люди використовують динамічні мови. Занадто часто люди не хочуть відмовлятися від усіх статичних тестів і, в кінцевому підсумку, підробляють це.)

Я це найяскравіше бачив у порівнянні всебічного коду ActionScript 3 з елегантним кодом JavaScript. AS3 може бути в 3 або 4 рази більшим за обсягом js, і надійність, на яку я підозрюю, принаймні не краща, саме через кількість (3-4X) рішень кодування, які були прийняті.

Як ви кажете, Shog9, YMMV. : D


1
if (! argument2) argument2 = 'default' еквівалентний argument2 = argument2 || 'за замовчуванням' - я вважаю другу версію візуально приємнішою ...
Крістоф

5
І я вважаю це більш багатослівним і відволікаючим; але це особисті переваги, я впевнений.
dkretz

4
@le dorfier: він також виключає використання порожніх рядків, 0 та булевих помилок.
Shog9

@le dorfier: окрім естетики, є одна ключова відмінність: остання ефективно створює другий шлях виконання, який може спокусити недбайливих обслуговуючого персоналу додати поведінку поза простим призначенням значення за замовчуванням. YMMV, звичайно.
Shog9

3
що робити, якщо параметр2 булевий === хибний; або функція {return false;}?
фреско

7

Є суттєві відмінності. Давайте встановимо кілька тестових випадків:

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

У першому описаному вами методі лише другий тест використовуватиме значення за замовчуванням. Другий метод буде по замовчуванням все , крім першого (як JS буде конвертувати undefined, null, 0і ""в логічне значенняfalse . І якби ви використовували метод Тома, тільки четвертий тест буде використовувати за замовчуванням!

Який спосіб ви вибрали насправді, залежить від вашої наміченої поведінки. Якщо undefinedможливі інші значення, ніж допустимі argument2, ви, мабуть, хочете певного варіанту на першому; якщо бажано ненульове, ненуле, не порожнє значення, то другий метод ідеальний - дійсно, його часто використовують для швидкого усунення такого широкого діапазону значень з розгляду.


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

Голі кістки відповідають. Деякі пояснення не зашкодили б.
Пол Руні

Це потрійний оператор, який стисло каже: Якщо URL не визначений (відсутній), встановіть змінною URL-адресу розташування.href (поточна веб-сторінка), інакше встановіть змінну URL-адреси на визначений URL.
rmooney

5

У ES6 (ES2015) ви можете використовувати параметри за замовчуванням

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

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


Ця відповідь дійсно цікава і може бути корисною для оп. Хоча це насправді не відповідає на питання
Ulysse BN

Як я бачу - він хоче визначити аргумент, якщо він не був визначений, тому я розмістив корисну інформацію
Andriy2

Моя думка, ви не відповідаєте на те, як найкраще визначити, чи не надсилається аргумент функції JavaScript . Але на це питання можна відповісти, використовуючи аргументи за замовчуванням: наприклад, називаючи вам аргументи "default value"і перевіряючи, чи дійсно значення "default value".
Ulysse BN

4

Вибачте, я все ще не можу коментувати, тому щоб відповісти на відповідь Тома ... У javascript (undefined! = Null) == false false Насправді, що функція не буде працювати з "null", ви повинні використовувати "undefined"


1
І Том отримав два відгуки за неправильну відповідь - завжди приємно знати, наскільки добре працюють ці системи громад;)
Крістоф

3

Чому б не скористатися !!оператором? Цей оператор, поставлений перед змінною, перетворить її на булеву (якщо я добре зрозумів), так !!undefinedі !!null(і навіть !!NaN, що може бути досить цікаво) повернеться false.

Ось приклад:

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

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

foo() //=> will log false

Не працює з булевим істинним, нульовим та порожнім рядком. Наприклад, foo (0); увійде помилково, але foo (1) ввійде true
rosell.dk

2

Можна зручно підходити до виявлення аргументів, викликаючи вашу функцію Об'єктом необов'язкових властивостей:

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

Існує також складний спосіб встановити, передається параметр функції чи ні . Погляньте на приклад нижче:

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

Це необхідне означає, що якщо значення value немає / передане - встановіть його на 0.

Досить класно так!


1
Це насправді означає "якщо valueеквівалентно false, встановіть його на 0." Це тонка, але дуже важлива відмінність.
Чарлз Вуд

@CharlesWood Значення не передано / теперішнє означає лише помилкове
Mohammed Zameer

1
Звичайно, якщо це відповідає вимогам вашої функції. Але якщо, наприклад, ваш параметр булевий, то trueі falseє допустимими значеннями, і ви можете мати третю поведінку, коли параметр взагалі не передається (особливо якщо функція має більше одного параметра).
Чарльз Вуд

1
І я повинен визнати, що це величезний аргумент у галузі інформатики, і він може виявитися просто думкою: D
Чарльз Вуд,

@CharlesWood шкода, що запізнився на вечірку. Я пропоную вам додати їх у саму відповідь із варіантом редагування
Мохаммед Замер

1

Інколи ви також можете перевірити тип, особливо якщо ви використовуєте функцію як геттер і сетер. Наступний код - ES6 (не запускається в EcmaScript 5 або новіших версіях):

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

Тому що масиви успадковують від Object.prototype. Поміркуйте ⇑ зробити світ кращим.


-1

fnCalledFunction (Param1, Param2, window.YourO optionalParameter)

Якщо вищезгадана функція викликається з багатьох місць, і ви впевнені, що перші 2 параметри передаються з кожного місця, але не впевнені в 3-му параметрі, тоді ви можете використовувати вікно.

window.param3 буде обробляти, якщо це не визначено з методу виклику.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.