tag - Qual a melhor forma de determinar se um argumento não é enviado para a função JavaScript



arguments (8)

Eu vi agora 2 métodos para determinar se um argumento foi passado para uma função JavaScript. Eu estou querendo saber se um método é melhor que o outro ou se é apenas ruim de usar?

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

      alert(argument2);
 }

 Test('test');

Ou

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

      alert(argument2);
 }

 Test('test');

Tanto quanto eu posso dizer, ambos resultam na mesma coisa, mas eu só usei o primeiro antes na produção.

Outra opção mencionada por Tom :

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

    alert(argument2);
}

De acordo com o comentário de Juan, seria melhor mudar a sugestão de Tom para:

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

    alert(argument2);
}

Algumas vezes você também pode querer verificar o tipo, especialmente se estiver usando a função como getter e setter. O código a seguir é o ES6 (não será executado no EcmaScript 5 ou anterior):

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

Este é um dos poucos casos em que encontro o teste:

if(! argument2) {  

}

funciona muito bem e carrega a implicação correta sintaticamente.

(Com a restrição simultânea de que eu não permitiria um valor nulo legítimo para o argument2 que tivesse algum outro significado; mas isso seria realmente confuso.)

EDITAR:

Este é realmente um bom exemplo de uma diferença estilística entre linguagens fracamente tipadas e fortemente tipadas; e uma opção estilística que o javascript oferece em espadas.

Minha preferência pessoal (sem críticas para outras preferências) é o minimalismo. Quanto menos o código tem a dizer, contanto que eu seja consistente e conciso, menos alguém tem que compreender para inferir corretamente o meu significado.

Uma implicação dessa preferência é que eu não quero - não acho útil - acumular um monte de testes de dependência de tipos. Em vez disso, tento fazer com que o código signifique o que parece que significa; e teste apenas o que eu realmente preciso testar.

Um dos agravos que encontro no código de outras pessoas é a necessidade de descobrir se eles esperam ou não, no contexto mais amplo, encontrar os casos para os quais estão testando. Ou se eles estão tentando testar tudo o que é possível, na chance de não anteciparem o contexto completamente. O que significa que eu acabo precisando rastreá-los exaustivamente em ambas as direções antes de poder refatorar ou modificar qualquer coisa com confiança. Eu acho que há uma boa chance de eles terem colocado esses vários testes no lugar porque eles previram as circunstâncias em que seriam necessários (e que geralmente não são aparentes para mim).

(Eu considero uma séria desvantagem na forma como essas pessoas usam linguagens dinâmicas. Muitas vezes as pessoas não querem desistir de todos os testes estáticos e acabam fingindo.)

Eu vi isso com muita clareza na comparação de código abrangente do ActionScript 3 com código javascript elegante. O AS3 pode ser 3 ou 4 vezes a maior parte do js, ​​e a confiabilidade que eu suspeito é pelo menos não melhor, apenas por causa do número (3-4X) das decisões de codificação que foram tomadas.

Como você diz, Shog9, YMMV. : D


Existem diferenças significativas. Vamos configurar alguns casos de teste:

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

Com o primeiro método descrito, somente o segundo teste usará o valor padrão. O segundo método será o padrão, exceto o primeiro (como JS converterá undefined , null , 0 e "" no booleano false . E se você usar o método de Tom, apenas o quarto teste usará o padrão!

Qual método você escolhe realmente depende do seu comportamento pretendido. Se valores diferentes de undefined forem permitidos para o argument2 , você provavelmente desejará alguma variação no primeiro; se um valor diferente de zero, não nulo, não vazio for desejado, o segundo método é ideal - na verdade, é frequentemente usado para eliminar rapidamente uma ampla variedade de valores de consideração.


Existem várias maneiras diferentes de verificar se um argumento foi passado para uma função. Além dos dois que você mencionou na sua pergunta (original) - verificando arguments.length ou usando o || operador para fornecer valores padrão - também é possível verificar explicitamente os argumentos para undefined via argument2 === undefined ou typeof argument2 === 'undefined' se um for paranóico (ver comentários).

Usando o || O operador se tornou uma prática padrão - todos os garotos legais fazem isso - mas tenha cuidado: O valor padrão será acionado se o argumento for false , o que significa que pode ser undefined , null , false , 0 , '' (ou qualquer outra coisa para o qual Boolean(...) retorna false ).

Portanto, a questão é quando usar o cheque, pois todos produzem resultados ligeiramente diferentes.

Marcar arguments.length exibe o comportamento 'mais correto', mas pode não ser viável se houver mais de um argumento opcional.

O teste para undefined é o próximo 'melhor' - ele só 'falha' se a função for explicitamente chamada com um valor undefined , o que provavelmente deve ser tratado da mesma maneira que omitir o argumento.

O uso do || operador pode acionar o uso do valor padrão, mesmo que um argumento válido seja fornecido. Por outro lado, seu comportamento pode realmente ser desejado.

Para resumir: use-o somente se você souber o que está fazendo!

Na minha opinião, usando || também é o caminho a percorrer se houver mais de um argumento opcional e não se quiser passar um literal de objeto como uma solução alternativa para parâmetros nomeados.

Outra maneira interessante de fornecer valores padrão usando arguments.length é possível ao passar pelos rótulos de uma instrução 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
}

Isto tem a desvantagem de que a intenção do programador não é (visualmente) óbvia e usa 'números mágicos'; é, portanto, propenso a erros.


No ES6 (ES2015) você pode usar os parâmetros padrão

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

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


Pode ser conveniente abordar a detecção de argumentos evocando sua função com um objeto de propriedades opcionais:

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

Se você estiver usando o jQuery, uma opção que seja boa (especialmente para situações complicadas) é usar o método extend do jQuery .

function foo(options) {

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

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

Se você chamar a função então, assim:

foo({timeout : 500});

A variável de opções seria então:

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

fnCalledFunction (Param1, Param2, window.YourOptionalParameter)

Se a função acima é chamada de muitos lugares e você tem certeza que primeiro dois parâmetros são passados ​​de todos os lugares, mas não tenho certeza sobre o 3º parâmetro, então você pode usar a janela.

window.param3 será manipulado se não for definido pelo método do chamador.





arguments