web-dev-qa-db-fra.com

Quelle est la meilleure méthode pour déterminer si un argument n'est pas envoyé à la fonction JavaScript

J'ai maintenant vu 2 méthodes pour déterminer si un argument a été passé à une fonction JavaScript. Je me demande si une méthode est meilleure que l'autre ou si l'une d'entre elles est mauvaise à utiliser?

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

Autant que je sache, ils aboutissent tous deux à la même chose, mais je n’avais utilisé que le premier en production.

Une autre option mentionnée par Tom :

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

    alert(argument2);
}

Selon le commentaire de Juan, il serait préférable de modifier la suggestion de Tom en:

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

    alert(argument2);
}
229
Darryl Hein

Il existe différentes manières de vérifier si un argument a été passé à une fonction. En plus des deux que vous avez mentionnés dans votre question (originale) - vérifier arguments.length ou utiliser l'opérateur || pour fournir des valeurs par défaut - vous pouvez également vérifier explicitement les arguments de undefined via argument2 === undefined ou typeof argument2 === 'undefined' s'il est paranoïaque (voir commentaires).

L'utilisation de l'opérateur || est devenue une pratique courante - tous les enfants cools le font - mais soyez prudent: la valeur par défaut sera déclenchée si l'argument a pour valeur false, ce qui signifie qu'il pourrait en réalité être undefined, null, false, 0, '' (ou tout autre élément pour lequel Boolean(...) renvoie false).

La question est donc de savoir quand utiliser quelle vérification, car elles donnent toutes des résultats légèrement différents.

Vérifier arguments.length présente le comportement "le plus correct", mais cela pourrait ne pas être possible s'il existe plusieurs arguments facultatifs.

Le test pour undefined est ensuite 'meilleur' ​​- il échoue uniquement si la fonction est explicitement appelée avec une valeur undefined qui, selon toute vraisemblance, devrait être traitée de la même manière que l'omission de l'argument.

L'utilisation de l'opérateur || peut déclencher l'utilisation de la valeur par défaut même si un argument valide est fourni. D'un autre côté, son comportement pourrait être réellement souhaité.

Pour résumer: tilisez-le uniquement si vous savez ce que vous faites!

À mon avis, utiliser || est également la méthode à utiliser s’il existe plusieurs arguments facultatifs et qu’un ne veut pas passer un littéral d’objet comme solution de contournement pour les paramètres nommés.

Un autre moyen intéressant de fournir des valeurs par défaut à l'aide de arguments.length est possible en passant par les étiquettes d'une instruction 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
}

Cela a l'inconvénient que l'intention du programmeur n'est pas (visuellement) évidente et utilise des "nombres magiques"; il est donc peut-être sujet aux erreurs.

267
Christoph

Si vous utilisez jQuery, une option intéressante (en particulier pour les situations compliquées) consiste à utiliser méthode extend de jQuery .

function foo(options) {

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

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

Si vous appelez la fonction alors comme ceci:

foo({timeout : 500});

La variable options serait alors:

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

C'est l'un des rares cas où je trouve le test:

if(! argument2) {  

}

fonctionne assez bien et porte l’implication correcte syntaxiquement.

(Avec la restriction simultanée que je ne permettrais pas une valeur nulle légitime pour argument2 qui a une autre signification; mais ce serait vraiment déroutant.)

MODIFIER:

C’est un très bon exemple de différence stylistique entre les langages faiblement typés et les langages fortement typés; et une option stylistique que javascript offre à la volée.

Ma préférence personnelle (sans faire de critique pour d’autres préférences) est le minimalisme. Moins le code doit en dire, tant que je suis cohérent et concis, moins une autre personne doit comprendre pour inférer correctement mon sens.

Une implication de cette préférence est que je ne veux pas - je ne trouve pas cela utile - empiler un tas de tests de dépendance de type. Au lieu de cela, j'essaie de faire en sorte que le code ait une signification réelle. et ne testez que ce que je vais vraiment devoir tester.

L'une des aggravations que je constate dans le code de certains autres peuples consiste à déterminer si, dans le contexte plus large, ils s'attendent ou non à se retrouver dans les cas pour lesquels ils effectuent des tests. Ou s’ils essaient de tester tout ce qui est possible, ils risquent de ne pas anticiper suffisamment le contexte. Ce qui signifie que je finis par avoir besoin de les retrouver de manière exhaustive dans les deux sens avant de pouvoir refacturer ou modifier quoi que ce soit en toute confiance. Je suppose qu'il est fort probable qu'ils aient mis en place ces différents tests car ils prévoyaient des circonstances dans lesquelles ils seraient nécessaires (et qui ne me sont généralement pas évidents).

(Je considère que ces personnes utilisent un langage dynamique comme inconvénient majeur. Trop souvent, les utilisateurs ne veulent pas abandonner tous les tests statiques et finissent par les imiter.)

C'est ce que j'ai constaté de la manière la plus flagrante lors de la comparaison d'un code ActionScript 3 complet avec un code JavaScript élégant. L'AS3 peut représenter 3 à 4 fois le volume des js, et la fiabilité que je soupçonne n'est pas meilleure, uniquement en raison du nombre (3-4X) de décisions de codage prises.

Comme tu le dis, Shog9, YMMV. :RÉ

13
dkretz

Il y a des différences significatives. Configurons quelques cas de test:

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

Avec la première méthode que vous décrivez, seul le deuxième test utilisera la valeur par défaut. La seconde méthode utilisera toutes les options sauf la première (car JS convertira undefined, null, 0 et "" en le booléen false. Et si vous devaient utiliser la méthode de Tom, seul le quatrième test utilisera la méthode par défaut!

La méthode que vous choisissez dépend vraiment de votre comportement souhaité. Si des valeurs autres que undefined sont autorisées pour argument2, vous souhaiterez probablement une certaine variation sur le premier; si une valeur non nulle, non nulle et non vide est souhaitée, la seconde méthode est idéale - en fait, elle est souvent utilisée pour éliminer rapidement une plage de valeurs aussi étendue.

7
Shog9
url = url === undefined ? location.href : url;
6
Lamy

Dans ES6 (ES2015), vous pouvez utiliser paramètres par défaut

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

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

Je suis désolé, je ne peux toujours pas commenter, alors pour répondre à la réponse de Tom ... En javascript (undefined! = Null) == false En fait, cette fonction ne fonctionnera pas avec "null", vous devrez utiliser "undefined"

4
Luca Matteis

Pourquoi ne pas utiliser l'opérateur !!? Cet opérateur, placé avant la variable, le transforme en un booléen (si j'ai bien compris), donc !!undefined et !!null (et même !!NaN, ce qui peut être très intéressant) retourne false.

Voici un exemple:

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

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

foo() //=> will log false
3
RallionRl

Il y a un moyen aussi difficile de trouver si un paramètre est passé à une fonction ou non . Regardez l'exemple ci-dessous:

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

Cela signifie que si la valeur de value n’est pas présente/transmise, définissez-la sur 0.

Assez cool hein!

2
student

Il peut être pratique d’approcher la détection des arguments en évoquant votre fonction avec un objet de propriétés facultatives:

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
1nfiniti

Parfois, vous pouvez également vérifier le type, surtout si vous utilisez la fonction comme getter et setter. Le code suivant est ES6 (ne fonctionnera pas dans EcmaScript 5 ou plus ancien):

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