web-dev-qa-db-fra.com

Un modèle de conception javascript pour les options avec des valeurs par défaut?

// opt_options is optional
function foo(a, b, opt_options) {
  // opt_c, opt_d, and opt_e are read from 'opt_options', only c and d have defaults
  var opt_c = 'default_for_c';
  var opt_d = 'default_for_d';
  var opt_e; // e has no default

  if (opt_options) {
    opt_c = opt_options.c || opt_c;
    opt_d = opt_options.d || opt_d;
    opt_e = opt_options.e;
  }
}

Ce qui précède semble terriblement verbeux. Quelle est la meilleure façon de gérer les options d'argument avec les paramètres par défaut?

64
ripper234

Cela utilise jQuery.extend mais pourrait être échangé avec une fusion d'objets de votre bibliothèque de choix ou Object.assign dans ES6.

function Module(options){
    var defaults = {
        color: 'red',
    };
    var actual = $.extend({}, defaults, options || {});
    console.info( actual.color );
}

var a = new Module();
// Red
var b = new Module( { color: 'blue' } );
// Blue

Modifier : Maintenant aussi dans underscore ou lodash !

function Module(options){
    var actual = _.defaults(options || {}, {
         color: 'red',
    });
    console.info( actual.color );
}

var a = new Module();
// Red
var b = new Module( { color: 'blue' } );
// Blue

Dans Javascript ES6, vous pouvez utiliser Object.assign :

function Module(options = {}){
    let defaults = {
        color: 'red',
    };
    let actual = Object.assign({}, defaults, options);
    console.info( actual.color );
}
70
max

Il y a quelques nouvelles façons dans ES6/ES2015. En utilisant Object.assign:

options = Object.assign({}, defaults, options);

Utilisation de l'assignation de déstructuration:

const { a = 1, b = 2 } = options;

Vous pouvez également utiliser les paramètres de la fonction de déstructuration:

const ƒ = ({a = 1, b = 2, c = 3} = {}) => {
   console.log({ a, b, c });
};

Pas de dépendances!

31
wprl

Pour obtenir des options par défaut sans dépendances supplémentaires, j'utilise le modèle suivant:

var my_function = function (arg1, arg2, options) {
    options = options || {};
    options.opt_a = options.hasOwnProperty('opt_a') ? options.opt_a : 'default_opt_a';
    options.opt_b = options.hasOwnProperty('opt_b') ? options.opt_b : 'default_opt_b';
    options.opt_c = options.hasOwnProperty('opt_c') ? options.opt_c : 'default_opt_b';


    // perform operation using options.opt_a, options.opt_b, etc.
};

Bien qu'un peu bavard, je trouve cela facile à lire, ajouter/supprimer des options et ajouter des valeurs par défaut. Lorsqu'il y a BEAUCOUP d'options, une version légèrement plus compacte est:

var my_function = function (arg1, arg2, options) {
    var default_options = {
        opt_a: 'default_opt_a',
        opt_b: 'default_opt_b',
        opt_c: 'default_opt_c'};

    options = options || {};
    for (var opt in default_options)
        if (default_options.hasOwnProperty(opt) && !options.hasOwnProperty(opt))
            options[opt] = default_options[opt];

    // perform operation using options.opt_a, options.opt_b, etc.
};
20
Rick Deckard

Et la version jQuery plus compacte:

function func(opts) {
    opts = $.extend({
        a: 1,
        b: 2
    }, opts);

    console.log(opts);
}

func();            // Object {a: 1, b: 2} 
func({b: 'new'});  // Object {a: 1, b: "new"} 
11
imbolc

Si vous devez le faire dans de nombreuses fonctions consécutives, un moyen de standardiser le processus et de l'accélérer est:

function setOpts (standard, user) {
  if (typeof user === 'object' {
    for (var key in user) {
      standard[key] = user[key];
    }
  }
}

Ensuite, vous pouvez simplement définir vos fonctions comme ceci:

var example = function (options) {
  var opts = {
    a: 1,
    b: 2,
    c:3
  };
  setOpts(opts, options);
}

De cette façon, vous ne définissez qu'un seul objet d'options à l'intérieur de la fonction, qui contient les valeurs par défaut.

Si vous voulez mettre une vérification supplémentaire à éviter l'héritage du prototype , la première fonction peut être:

function setOpts (standard, user) {
  if (typeof user === 'object') {
    Object.keys(user).forEach(function (key) {
      standard[key] = user[key];
    });
  }
}

Ce dernier cas n'est pas pris en charge pour: IE <9, Chrome <5, Firefox <4, Safari <5

(vous pouvez consulter le tableau de compatibilité ici )


Enfin ECMAScript 6 nous apportera le meilleur moyen de le faire: paramètres par défaut .

Il faudra cependant quelques mois avant que cela ne soit largement pris en charge par les navigateurs.

2
Pensierinmusica

Si vous avez accès à ES6 avec une proposition de l'étape 4 (comme avec Babel), vous pouvez le faire avec une affectation de diffusion et de déstructuration.

const defaultPrintOptions = {
  fontName: "times",
  fontStyle: "normal",
  fontSize: 10,
  align: "left"
};

// Setting the null default isn't necessary but
// makes it clear that the parameter is optional.
// Could use {} but would create a new object
// each time the function is called.
function print(text, options = null) {
  let {
    fontName,
    fontStyle,
    fontSize,
    align
  } = {
    ...defaultPrintOptions,
    ...options
  };

  console.log(text, fontName, fontStyle, fontSize, align);
}

print("All defaults:");
print("Override some:", {
  fontStyle: "italic",
  align: "center"
});
print("Override all:", {
  fontName: "courier",
  fontStyle: "italic",
  fontSize: 24,
  align: "right"
});

Cela fonctionne également (mais peut créer plus d'objets):

function myFunction({ 
  text = "", 
  line = 0, 
  truncate = 100 
} = {}) {
  console.log(text, line, truncate);
}

(dernier exemple de David Walsh - La réponse de @ wprl le mentionne également)

0
TrueWill

Bien que Object.assign soit un moyen assez simple de fusionner les options avec les valeurs par défaut, il présente certains inconvénients:

  1. si vous voulez définir des options conditionnelles avec l'opérateur ternaire - il écrasera les valeurs par défaut même pour les valeurs undefined:

    const options = {
      logging: isProduction ? 'none' : undefined
    };
    const defaults = {
      logging: 'verbose'
    }
    Object.assign({}, defaults, options); // {logging: undefined} !
    
  2. si vous fournissez un nom d'option incorrect - vous ne serez pas averti:

    const options = {
      loging: 'none' // typo
    };
    const defaults = {
      logging: 'verbose'
    }
    Object.assign({}, defaults, options); // {logging: 'verbose', loging: 'none'} !
    

Pour couvrir ces cas, j'ai créé un petit paquet flat-options .
Il n'écrase pas les valeurs par défaut pour les valeurs undefined:

const options = {
  logging: isProduction ? 'none' : undefined
};
const defaults = {
  logging: 'verbose'
}
flatOptions(options, defaults); // {logging: 'verbose'}

et met en garde contre les noms d'options incorrects:

const options = {
  loging: 'none' // typo
};
const defaults = {
  logging: 'verbose'
}
flatOptions(options, defaults); // throws "Unknown option: loging."

J'espère que cela t'aides!

0
vitalets
      var mergeOptions = function mergeOptions(userOptions) {

        // Default options
        var options = {
            height: "100px",
            width: "100px" ,
            color: "blue"
        }

        if (userOptions) {
            Object.keys(userOptions).forEach(function (key) {
                options[key] = userOptions[key]
            })
        }

        return options;
    }
0
Puyi Severino