Как лучше определить, не передан ли аргумент функции JavaScript

Теперь я увидел 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');

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

Другой вариант, упомянутый Tom:

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

    alert(argument2);
}

Согласно комментарию Хуана, было бы лучше изменить предложение Tom:

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

    alert(argument2);
}

Ответы

Ответ 1

Существует несколько способов проверить, был ли аргумент передан функции. В дополнение к двум, указанным в вашем (оригинальном) вопросе - проверка arguments.length или использование оператора || для предоставления значений по умолчанию - также можно явно проверить аргументы для undefined через argument2 === undefined или typeof argument2 === 'undefined', если одна параноидальная (см. комментарии).

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

Итак, вопрос заключается в том, когда использовать эту проверку, поскольку все они дают несколько разные результаты.

Проверка arguments.length показывает "наиболее правильное" поведение, но это может оказаться невозможным, если имеется более одного необязательного аргумента.

Тест для undefined следующий "лучший" - он "не работает", если функция явно вызвана значением undefined, которое, вероятно, должно рассматриваться так же, как и без аргумента.

Использование оператора || может вызвать использование значения по умолчанию, даже если предоставлен допустимый аргумент. С другой стороны, его поведение действительно может быть желательным.

Подводя итог: используйте его, только если вы знаете, что делаете!

По-моему, использование || - это также путь, если есть более одного необязательного аргумента, и один не хочет передавать литерал объекта в качестве обходного пути для именованных параметров.

Еще один хороший способ предоставить значения по умолчанию с помощью arguments.length возможен, пропустив метки оператора switch:

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
}

Это имеет недостаток, что намерение программиста не является (визуально) очевидным и использует "магические числа"; поэтому он может быть подвержен ошибкам.

Ответ 2

Если вы используете jQuery, один из вариантов, который хорош (особенно для сложных ситуаций), заключается в использовании метода jQuery extend.

function foo(options) {

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

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

Если вы вызываете функцию, то вот так:

foo({timeout : 500});

Затем переменная options:

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

Ответ 3

Это один из немногих случаев, когда я нахожу тест:

if(! argument2) {  

}

работает довольно хорошо и синтаксически выполняет правильную импликацию.

(При одновременном ограничении, чтобы я не допустил допустимое значение null для argument2, которое имеет какое-то другое значение, но это будет действительно запутанным.)

EDIT:

Это действительно хороший пример стилистической разницы между свободно типизированными и строго типизированными языками; и стилистический вариант, который javascript предоставляет в пиках.

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

Одним из последствий этого предпочтения является то, что я не хочу - не считаю его полезным - накапливать кучу тестов зависимости типа. Вместо этого я пытаюсь сделать код таким, каким он выглядит; и проверяйте только то, что мне действительно нужно проверить.

Одно из усугублений, которое я нахожу в коде некоторых других народов, необходимо выяснить, ожидают ли они в более широком контексте, чтобы они действительно столкнулись с теми случаями, которые они испытывают. Или, если они пытаются проверить все возможное, на случай, если они не предвидят контекст достаточно полно. Это означает, что мне в конечном итоге нужно тщательно отслеживать их в обоих направлениях, прежде чем я могу уверенно реорганизовать или изменить что-либо. Я полагаю, что у них есть хорошие шансы, что они могли бы провести эти различные тесты, потому что они предвидели обстоятельства, в которых они были бы необходимы (и которые обычно не кажутся мне).

(Я считаю, что серьезный недостаток в том, как эти люди используют динамические языки. Слишком часто люди не хотят отказываться от всех статических тестов и в конечном итоге притворяются.)

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

Как вы говорите, Shog9, YMMV.: D

Ответ 4

Существуют значительные различия. Пусть заданы некоторые тестовые примеры:

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. И если бы вы использовали метод Тома, то только четвертый тест будет использовать значение по умолчанию!

Какой метод вы выбираете, действительно зависит от вашего предполагаемого поведения. Если для argument2 допустимы значения, отличные от undefined, то вы, вероятно, захотите получить некоторые изменения в первом; если требуется ненулевое, ненулевое, непустое значение, то второй метод идеален - действительно, он часто используется для быстрого устранения такого широкого диапазона значений из соображений.

Ответ 5

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

Ответ 6

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

Ответ 7

Удобно подойти к обнаружению аргументов, вызвав вашу функцию объектом дополнительных свойств:

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

Ответ 8

Почему бы не использовать оператор !!? Этот оператор, помещенный перед переменной, превратит его в логическое (если я хорошо понял), поэтому !!undefined и !!null (и даже !!NaN, что может быть довольно интересно) вернут false.

Вот пример:

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

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

foo() //=> will log false

Ответ 9

Несколько раз вы также можете проверить тип, особенно если вы используете функцию как getter и setter. Следующий код - 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;
            }
        }
    }
}

Ответ 10

В ES6 (ES2015) вы можете использовать Параметры по умолчанию

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

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