Шаблон дизайна JavaScript для вариантов со значениями по умолчанию? - PullRequest
62 голосов
/ 07 марта 2012
// 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;
  }
}

Вышеизложенное кажется ужасно многословным.Как лучше обрабатывать параметры аргументов с параметрами по умолчанию?

Ответы [ 9 ]

69 голосов
/ 07 марта 2012

Используется jQuery.extend , но его можно заменить на слияние объектов из выбранной вами библиотеки или Object.assign в 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

Редактировать: теперь также в underscore или 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

В Javascript ES6 можно использовать Object.присваивать :

function Module(options = {}){
    let defaults = {
        color: 'red',
    };
    let actual = Object.assign({}, defaults, options);
    console.info( actual.color );
}
27 голосов
/ 20 июля 2016

В ES6 / ES2015 есть несколько новых способов. Использование Object.assign:

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

Использование задания по деструктуризации:

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

Вы также можете использовать параметры функции разрушения:

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

Нет зависимостей!

20 голосов
/ 22 октября 2013

Чтобы получить параметры по умолчанию без дополнительных зависимостей, я использую следующий шаблон:

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

Хотя я немного многословен, мне легко читать, добавлять / удалять параметры и добавлять значения по умолчанию.Когда есть много вариантов, немного более компактная версия:

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.
};
11 голосов
/ 19 января 2014

И более компактная версия 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"} 
2 голосов
/ 20 февраля 2015

Если вам нужно сделать это во многих последовательных функциях, способ стандартизировать процесс и ускорить его:

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

Тогда вы можете просто определить свои функции просто так:

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

Таким образом вы определяете только один объект параметров внутри функции, который содержит значения по умолчанию.

Если вы хотите поставить дополнительную проверку на избегать наследования прототипа , первыйфункция может быть:

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

Последний случай не поддерживается для: IE <9, Chrome <5, Firefox <4, Safari <5 </p>

(вы можете проверить таблицу совместимости здесь )


Наконец ECMAScript 6 предоставит нам лучший способ сделать это: параметры по умолчанию .

Пройдет несколько месяцев, прежде чем это станет широко распространенным в браузерах.

0 голосов
/ 01 января 2019

Если у вас есть доступ к ES6 с предложением этапа 4 (например, с Babel), вы можете выполнить это с помощью распределения и деструктурирования.

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

Это также работает (но может создавать больше объектов):

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

(последний пример из Дэвид Уолш - ответ @ wprl также упоминает это)

0 голосов
/ 17 октября 2017

Хотя Object.assign - это довольно простой способ объединения опций со значениями по умолчанию, у него есть некоторые недостатки:

  1. , если вы хотите установить условные опции с помощью троичного оператора - он заменит значения по умолчанию даже для undefined значений:

    const options = {
      logging: isProduction ? 'none' : undefined
    };
    const defaults = {
      logging: 'verbose'
    }
    Object.assign({}, defaults, options); // {logging: undefined} !
    
  2. если вы укажете неправильное имя опции - вы не будете предупреждены:

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

Для этих случаев я создал крошечный пакет flat-options .
Он не перезаписывает значения по умолчанию для undefined значений:

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

и предупреждает о неправильных именах параметров:

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

Надеюсь, это поможет!

0 голосов
/ 05 марта 2013

Я думаю, вы ищете что-то вроде этого (извините за поздний ответ):

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

edit: извинения, выхватили это из какого-то старого кода ... Вы должны убедиться, что используетеМетод hasOwnProperty (), чтобы убедиться, что вы не выполняете итерации по всем функциям.prototype

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

0 голосов
/ 07 марта 2012

Теперь, когда я думаю об этом, я вроде как:

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