web-dev-qa-db-fra.com

Comment utiliser au mieux les paramètres en JavaScript?

J'utilise Javascript avec jQuery. Je voudrais mettre en place des params. En C #, cela ressemblerait à quelque chose comme ça:

/*
 * odp      the object to test
 * error    a string that will be filled with the error message if odp is illegal. Undefined otherwise.
 *
 * Returns  true if odp is legal.
 */
bool isLegal(odp, out error);

Quelle est la meilleure façon de faire quelque chose comme ça dans JS? Objets?

function isLegal(odp, errorObj)
{
    // ...
    errorObj.val = "ODP failed test foo";
    return false;
}

Firebug me dit que l'approche ci-dessus fonctionnerait, mais existe-t-il un meilleur moyen?

29
Nick Heiner

L'approche de rappel mentionnée par @Felix Kling est probablement la meilleure idée, mais j'ai également constaté qu'il était parfois facile de tirer parti de la syntaxe littérale d'un objet Javascript et de simplement demander à votre fonction de renvoyer un objet en cas d'erreur:

function mightFail(param) {
  // ...
  return didThisFail ? { error: true, msg: "Did not work" } : realResult;
}

puis quand vous appelez la fonction:

var result = mightFail("something");
if (result.error) alert("It failed: " + result.msg);

Pas fantaisiste et à peine à l'épreuve des balles, mais c'est certainement OK pour des situations simples.

39
Pointy

Je pense que c'est à peu près la seule façon (mais je ne suis pas un programmeur JavaScript hardcore;)).

Ce que vous pourriez également envisager est d'utiliser une fonction de rappel:

function onError(data) {
    // do stuff
}


function isLegal(odp, cb) {
    //...
    if(error) cb(error);
    return false;
}

isLegal(value, onError);
14
Felix Kling

Oui, comme vous l'avez dit vous-même, les objets sont le meilleur et le seul moyen de transmettre des données par référence en JavaScript. Je garderais votre fonction isLegal en tant que telle et l'appellerais simplement comme ceci:

var error = {};
isLegal("something", error);
alert(error.val);
14
casablanca

jS peut transmettre les paramètres de sortie d'une autre manière. mais je crois que les meilleurs pour votre situation ont déjà été mentionnés.

Les tableaux sont également passés par référence, pas par valeur. Ainsi, tout comme vous pouvez passer un objet à une fonction, puis définir une propriété de l'objet dans la fonction, puis retourner et accéder à la propriété de cet objet, vous pouvez également transmettre un tableau à une fonction, définir certaines valeurs du tableau. à l'intérieur de la fonction, et retourner et accéder à ces valeurs en dehors du tableau.

ainsi, dans chaque situation, vous pouvez vous demander: "est-ce qu'un tableau ou un objet est mieux?"

3

Les réponses que j'ai vues jusqu'à présent n'implémentent pas les paramètres en JavaScript, car ils sont utilisés en C # (le mot clé out). Il s’agit simplement d’une solution de contournement qui renvoie un objet en cas d’erreur. 

Mais que faites-vous si vous avez vraiment besoin de paramètres?

Parce que Javascript ne le supporte pas directement, vous devez construire quelque chose qui soit proche des paramètres de sortie de C #. Jetez un coup d'oeil à cette approche, j'émule la fonction DateTime.TryParse de C # en JavaScript. Le paramètre out est result, et comme JavaScript ne fournit pas de mot clé out, j'utilise .value dans la fonction pour transmettre la valeur en dehors de la fonction (comme inspiré par suggestion MDN ):

// create a function similar to C#'s DateTime.TryParse
var DateTime = [];
DateTime.TryParse = function(str, result) {
  result.value = new Date(str); // out value
  return (result.value != "Invalid Date");
};

// now invoke it
var result = [];
if (DateTime.TryParse("05.01.2018", result)) {
  alert(result.value);
} else {
  alert("no date");
};

Exécutez l'extrait et vous verrez qu'il fonctionne. Notez que result doit être initialisé sous la forme d'un tableau vide [], avant vous appelez la fonction. Cela est nécessaire car dans la fonction, vous "injectez" la propriété .value.


Vous pouvez maintenant utiliser le modèle ci-dessus pour écrire une fonction comme celle de votre question (cela vous indique également comment émuler le nouveau paramètre ignorer connu sous le nom out _ dans C #: en passant [] comme indiqué ci-dessous):

// create a function similar to C#'s DateTime.TryParse
var DateTime = [];
DateTime.TryParse = function(str, result) {
  result.value = new Date(str); // out value
  return (result.value != "Invalid Date");
};

// returns false, if odb is no date, otherwise true
function isLegal(odp, errorObj) {
  if (DateTime.TryParse(odp, [])) { // discard result here by passing []
    // all OK: leave errorObj.value undefined and return true
    return true;
  } else {
    errorObj.value = "ODP failed test foo"; // return error
    return false;
  }
}

// now test the function
var odp = "xxx01.12.2018xx"; // invalid date
var errorObj = [];
if (!isLegal(odp, errorObj)) alert(errorObj.value); else alert("OK!");

Remarque: Au lieu d'utiliser un paramètre de suppression comme indiqué ci-dessus, vous pouvez également utiliser, dans JavaScript, une vérification de undefined, c'est-à-dire à l'intérieur de la vérification de fonction pour 

if (result === undefined) { 
   // do the check without passing back a value, i.e. just return true or false 
};

Ensuite, il est possible d'omettre result comme paramètre complètement si ce n'est pas nécessaire, afin que vous puissiez l'invoquer comme ceci:

if (DateTime.TryParse(odp)) { 
    // ... same code as in the snippet above ...
};
2
Matt

J'utilise une méthode de rappel (similaire à l'approche de Felix Kling ) pour simuler le comportement des paramètres de sortie. Ma réponse diffère de celle de Kling en ce que la fonction de rappel agit comme une fermeture de capture de référence plutôt que comme un gestionnaire.

Cette approche souffre de la syntaxe de la fonction anonyme anonyme de JavaScript, mais reproduit fidèlement la sémantique des paramètres d'autres langues.

function isLegal(odp, out_error) {
    //...
    out_error("ODP failed test foo"); // Assign to out parameter.
    return false;
}

var error;
var success = isLegal(null, function (e) { error = e; });

// Invariant: error === "ODP failed test foo".
2
Gooseberry

Ce qui suit est l'approche que j'utilise. Et ceci est la réponse à cette question. Cependant, le code n'a pas été testé.

function mineCoords( an_x1, an_y1 ) {
  this.x1 = an_x1;
  this.y1 = an_y1;
}

function mineTest( an_in_param1, an_in_param2 ) {

  // local variables
  var lo1 = an_in_param1;
  var lo2 = an_in_param2;

  // process here lo1 and lo2 and 
  // store result in lo1, lo2

  // set result object
  var lo_result = new mineCoords( lo1, lo2 );
  return lo_result;
}

var lo_test = mineTest( 16.7, 22.4 );
alert( 'x1 = ' + lo_test.x1.toString() + ', y1 = ' + lo_test.y1.toString() );
1
Dominik Lebar

Je ne publierai aucune code, mais ce qui n’a pas été fait ici dans ces réponses, c’est de mettre la rime à la raison. Je travaille dans l’arène JS native et le problème qui se pose est que certains native API calls doivent être transformés car nous ne pouvons pas écrire les paramètres sans hacks honteux et laids.

Ceci est ma solution:

    // Functions that return parameter data should be modified to return
    // an array whose zeroeth member is the return value, all other values
    // are their respective 1-based parameter index.

Cela ne signifie pas définir et renvoyer chaque paramètre. Seulement le paramètres qui reçoivent la sortie.

La raison de cette approche est donc: Multiple return values peut être nécessaire pour un nombre quelconque de procédures. Cela crée une situation où les objets avec des valeurs nommées (qui ne seront finalement pas synchronisés avec le contexte lexical de toutes les opérations), constamment doivent être mémorisés afin de travailler correctement avec la (les) procédure (s).

En utilisant la méthode prescrite, il suffit de connaître what you called et where you should be looking plutôt que de savoir ce que vous recherchez.

Il existe également l’avantage que les algorithmes "robust and stupid" peuvent être écrits pour englober les appels de procédure souhaités afin de rendre cette opération "plus transparente".

Il serait sage d'utiliser object, function ou array (qui sont tous des objets) comme paramètre "write-back-output", mais je pense que si un travail supplémentaire devait être effectué, il devrait l'être par le on écrit la boîte à outils pour faciliter les choses, ou élargir la fonctionnalité.

C’est une réponse unique pour chaque occasion, qui garde APIs l'apparence qu'il faut au premier abord, plutôt que de sembler être et d'avoir toutes les ressemblances d'un tissu encombré de bandes de code spaghetti qui ne peuvent pas déterminer s'il définition ou data.

Félicitations et bonne chance.

J'utilise le webkitgtk3 et l'interface avec certains procs de la bibliothèque C native. Cet échantillon de code éprouvé peut donc au moins servir d'illustration.

// ssize_t read(int filedes, void *buf, size_t nbyte)
SeedValue libc_native_io_read (SeedContext ctx, SeedObject function, SeedObject this_object, gsize argument_count, const SeedValue arguments[], SeedException *exception) {


    // NOTE: caller is completely responsible for buffering!

                    /* C CODING LOOK AND FEEL */


    if (argument_count != 3) {
        seed_make_exception (ctx, exception, xXx_native_params_invalid,
            "read expects 3 arguments: filedes, buffer, nbyte: see `man 3 read' for details",
            argument_count
        );  return seed_make_undefined (ctx);
    }

    gint filedes = seed_value_to_int(ctx, arguments[0], exception);
    void *buf = seed_value_to_string(ctx, arguments[1], exception);
    size_t nbyte = seed_value_to_ulong(ctx, arguments[2], exception);

    SeedValue result[3];

    result[0] = seed_value_from_long(ctx, read(filedes, buf, nbyte), exception);
    result[2] = seed_value_from_binary_string(ctx, buf, nbyte, exception);

    g_free(buf);
    return  seed_make_array(ctx, result, 3, exception);

}
1
user735796

L’approche habituelle pour le cas d’utilisation spécifique que vous avez décrit en Javascript, et en fait pour la plupart des langages de haut niveau, consiste à vous fier à Errors (ou exceptions) pour vous avertir lorsque quelque chose d’extraordinaire s’est produit. Il n'y a aucun moyen de passer un type de valeur (chaînes, nombres, etc.) par référence en Javascript.

Je voudrais juste faire ça. Si vous devez réellement intégrer des données personnalisées à la fonction appelante, vous pouvez sous-classer Erreur.

var MyError = function (message, some_other_param)
{
    this.message = message;
    this.some_other_param = some_other_param;
}
//I don't think you even need to do this, but it makes it Nice and official
MyError.prototype = Error; 
...
if (something_is_wrong)
    throw new MyError('It failed', /* here's a number I made up */ 150); 

Catching exceptions est une douleur, je sais, mais encore une fois est de garder une trace des références.

Si vous avez vraiment besoin de quelque chose qui approche le comportement des variables out, les objets sont passés par référence par défaut et peuvent facilement capturer des données à partir d'autres portées ...

function use_out (outvar)
{
    outvar.message = 'This is my failure';
    return false;
}

var container = { message : '' };
var result = use_out(container );
console.log(container.message); ///gives the string above
console.log(result); //false

Je pense que cela répond en partie à votre question, mais je pense que toute votre approche est cassée depuis le début. Javascript prend en charge de nombreuses manières beaucoup plus élégantes et puissantes d'obtenir plusieurs valeurs d'une fonction. Faites des lectures sur les générateurs, les fermetures, et même les rappels peuvent être agréables dans certaines situations - recherchez le style de continuation.

Mon but avec tout ce discours est d’encourager tous ceux qui liront ceci à adapter leur style de programmation aux limitations et aux capacités de la langue qu’ils utilisent, plutôt que d’essayer d’imposer de force ce qu’ils ont appris des autres langues.

(Au fait, certaines personnes déconseillent fortement les fermetures, car elles sont à l'origine de mal effets secondaires}, mais je ne les écouterais pas. Ce sont des puristes. Les effets secondaires sont presque inévitables dans de nombreuses applications sans beaucoup revenir en arrière et contourner les obstacles fastidieux, si vous en avez besoin, les garder tous ensemble dans un cadre purement lexical plutôt que dispersés dans un paysage infernal de pointeurs et de références obscurs me semble beaucoup mieux)

0
Chuck Bajax