¿Un patrón de diseño javascript para opciones con valores predeterminados?


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

Lo anterior parece terriblemente detallado. ¿Cuál es una mejor manera de manejar las opciones de argumento con parámetros predeterminados?

Author: ripper234, 2012-03-07

8 answers

Utiliza jQuery.extend pero podría intercambiarse con una fusión de objetos de su biblioteca de elección u Objeto.asignar en 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

Editar: Ahora también en underscore o 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

En Javascript ES6 puede usar el objeto .asignar:

function Module(options = {}){
    let defaults = {
        color : 'red',
    };
    let actual = Object.assign({}, defaults, options);
    console.info( actual.color );
}
 58
Author: max,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-20 14:54:10

Para obtener opciones predeterminadas sin dependencias adicionales, utilizo el siguiente patrón:

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

Aunque un poco detallado, me resulta fácil de leer, agregar/eliminar opciones y agregar valores predeterminados. Cuando hay MUCHAS opciones, una versión un poco más compacta es:

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
Author: Rick Deckard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-22 17:59:27

Hay un par de nuevas formas en ES6/ES2015. Usando Object.assign:

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

Usando la asignación de desestructuración:

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

También puede usar parámetros de función de desestructuración:

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

¡Sin dependencias!

 13
Author: wprl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-18 22:53:31

Y la versión más compacta de jQuery:

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
Author: imbolc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-19 05:14:08

Si necesita hacer esto en muchas funciones consecutivas, una forma de estandarizar el proceso y acelerarlo es:

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

Entonces puedes definir tus funciones simplemente así:

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

De esta manera, solo se define un objeto de opciones dentro de la función, que contiene los valores predeterminados.

Si desea poner una verificación adicional a evitar la herencia de prototipos , la primera función puede ser:

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

Este último caso no es compatible con: IE

(puede consultar la tabla de compatibilidad aquí)


Finally ECMAScript 6 nos traerá la mejor manera de hacer esto: los parámetros por defecto.

Sin embargo, tomará unos meses antes de que esto sea ampliamente compatible con todos los navegadores.

 2
Author: Pensierinmusica,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-25 15:57:35

Aunque Objeto.asignar es una forma bastante directa de combinar opciones con valores predeterminados tiene algunas desventajas:

  1. Si desea establecer opciones condicionales con el operador ternario, sobrescribirá los valores predeterminados incluso para undefined valores:

    const options = {
      logging: isProduction ? 'none' : undefined
    };
    const defaults = {
      logging: 'verbose'
    }
    Object.assign({}, defaults, options); // {logging: undefined} !
    
  2. Si proporciona un nombre de opción incorrecto, no se le advertirá:

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

Para cubrir estos casos he creado el paquete tiny flat-options.
No sobrescribe los valores predeterminados para undefined valores:

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

Y advierte de nombres de opciones incorrectos:

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

Espero que esto ayude!

 0
Author: vitalets,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-17 11:08:00

Creo que estás buscando algo como esto (lo siento por la respuesta tardía):

function foo(a, b, options) { 
    this.defaults = {
        x: 48, 
        y: 72,
        z: 35
    };
    for (var i in this.defaults) {
        if (options[i] != "undefined") { this.defaults[i] = options[i]; }
    }
    // more code...
}

Editar: disculpas, tomé esto de un código antiguo... Deberías asegurarte de usar el método hasOwnProperty () para asegurarte de no iterar sobre todo en la función.prototipo

Https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/hasOwnProperty

 -1
Author: 1nfiniti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-04 21:26:16

Ahora que lo pienso, me gusta esto:

function foo(a, b, opt_options) {
  // Force opt_options to be an object
  opt_options = 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' || opt_options.c;
  var opt_d = 'default_for_d' || opt_options.d;
  var opt_e = opt_options.e; // e has no default
}
 -5
Author: ripper234,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-07 13:50:58