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

StackOverflow https://stackoverflow.com/questions/411352

  •  03-07-2019
  •  | 
  •  

Question

J'ai maintenant vu deux 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 donnent le même résultat, mais je n’avais utilisé que le premier auparavant dans la 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);
}
Était-ce utile?

La solution

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 pour undefined via argument2 === undefined ou type of argument2 === 'undefined' s'il en existe un paranoïaque (voir les commentaires).

L'utilisation de l'opérateur || est devenue une pratique courante - tous les enfants cools le font - mais attention: la valeur par défaut sera déclenchée si l'argument a pour valeur false , ce qui signifie qu'il pourrait en fait s'agir de indéfini , 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.

La vérification de 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 "optimal" - il échoue uniquement si la fonction est explicitement appelée avec une valeur undefined , qui doit probablement être traitée. de la même manière qu'en omettant 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. En revanche, son comportement pourrait être réellement souhaité.

Pour résumer: Ne l'utilisez que si vous savez ce que vous faites!

À mon avis, l'utilisation de || est également la meilleure solution s'il existe plusieurs arguments facultatifs et qu'aucun ne veut transmettre un littéral d'objet en guise d'une solution de contournement pour les paramètres nommés.

Un autre moyen intéressant de fournir des valeurs par défaut en utilisant 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
}

L’inconvénient est que l’intention du programmeur n’est pas évidente (visuellement) et utilise des «nombres magiques»; il est donc probablement sujet aux erreurs.

Autres conseils

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

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, procédez comme suit:

foo({timeout : 500});

La variable options serait alors:

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

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

if(! argument2) {  

}

fonctionne assez bien et porte correctement l’implication syntaxique.

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

EDIT:

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 aucune critique destinée à 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 des implications de cette préférence est que je ne veux pas - je ne trouve pas ça 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 utilisateurs est la nécessité de 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 bien, s'ils tentent 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 avec 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 qu’un inconvénient sérieux de la façon dont ces personnes utilisent les langages dynamiques. Trop souvent, les gens 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 en comparant un code ActionScript 3 complet à un code JavaScript élégant. L’AS3 peut représenter 3 à 4 fois l’essentiel du js, et la fiabilité dont je soupçonne qu’elle n’est en rien meilleure, uniquement en raison du nombre (3-4X) de décisions de codage prises.

Comme tu le dis, Shog9, YMMV. : D

Il existe des différences significatives. Mettons en place quelques cas de tests:

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 deuxième méthode utilisera toutes les options sauf la première (JS convertira indéfini , null , 0 et "" dans le false booléen. Et si vous utilisiez la méthode de Tom, seul le quatrième test utilisera la valeur 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.

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

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

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

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

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

.

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

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

Voici un exemple:

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

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

foo() //=> will log false

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

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

Il existe un manière délicate également de savoir si un paramètre est transmis à une fonction ou non . Regardez l'exemple ci-dessous:

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

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

Assez cool hein!

fnCalledFunction (Param1, Param2, window.YourOptionalParameter)

Si la fonction ci-dessus est appelée de nombreux endroits et que vous êtes sûr que les 2 premiers paramètres sont passés de partout où vous ne le savez pas, vous pouvez utiliser window.

window.param3 gérera s'il n'est pas défini à partir de la méthode de l'appelant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top