具有默认值选项的 JavaScript 设计模式?

问题描述 投票:0回答:15
// 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;
  }
}

上面的内容看起来非常冗长。使用默认参数处理参数选项的更好方法是什么?

javascript design-patterns optional-parameters
15个回答
91
投票

这使用 jQuery.extend,但可以与您选择的库中的对象合并或 ES6 中的 Object.assign 互换。

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.assign:

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

69
投票

对对象属性使用 ES2018 扩展语法

const defaults = { a: 1, b: 2 };

const ƒ = (given = {}) => {
  const options = { ...defaults, ...given };
  console.log(options);
};

使用 ES6/ES2015 功能,可以使用更多选项。

使用解构赋值:

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

您还可以使用解构函数参数:

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

使用

Object.assign

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

无依赖性!


20
投票

要获取默认选项而不需要额外的依赖项,我使用以下模式:

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
投票

还有更紧凑的 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"} 

4
投票

使用 ES6 Spread Operator 而不使用外部库

function Example(opts) {
   let defaults = { foo: 1, bar: 2 }
   opts = { ...defaults, ...(opts || {}) }
   console.log(opts);
}

Example({ bar: 3, baz: 4 })

// { foo: 1, bar: 3, baz: 4 }

2
投票

如果您需要在许多连续的函数中执行此操作,标准化该过程并加快速度的方法是:

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

(您可以查看兼容性表这里


最后ECMAScript 6将为我们带来最好的方法:默认参数

需要几个月的时间才能得到跨浏览器的广泛支持。


2
投票

如果您可以通过第 4 阶段提案访问 ES6(例如 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); }

(来自

David Walsh 的后一个示例 - @wprl 的回答也提到了这一点)


2
投票
这是一个简单干净的方法,希望对某人有所帮助:

function example(url, {title = false, check = false, wait = false} = {}){ console.log('url: ', URL); console.log('title: ', title); console.log('check: ', check); console.log('wait: ', wait); } example('https://example.com', {wait: 20})

这是上面代码的输出:

url: https://example.com title: false check: false wait: 20
    

1
投票
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; }
    

1
投票
看过所有解决方案后,我更喜欢以下方式:

function example(firstArgument, optional = {}) { const { a = 123, // default value b, } = optional; console.log(firstArgument, a, b); }
并且这种方式支持多种语法调用:

example(); // undefined 123 undefined example("test"); // test 123 undefined example("test", { a: 456 }); // test 456 undefined example("test", { b: 456 }); // test 123 456 example("test", { a: 456, b: 123 }); // test 456 123
    

0
投票
虽然

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
投票
有一个新的 JavaScript 语法可以轻松设置默认值、逻辑赋值运算符:

// Super crazy this: staticConfig.defaultPropsForFoo = staticConfig.defaultPropsForFoo || { myDefault: 'props' } // turns into: staticConfig.defaultPropsForFoo ||= { myDefault: 'props' }
如果您喜欢更严格的布尔语义,也可以使用 nullish 运算符:

staticConfig.defaultPropsForFoo ??= { myDefault: 'props' }
(可以说我们应该始终使用

??=

版本,但它也很新)

此外,我一直使用默认参数,但此语法适用于任何解构赋值:

const { defaultPropsForFoo = { myDefault: 'props' }, ...restConfig } = staticConfig
    

0
投票
这是一个非常古老的问题,答案没有按照现代解构进行更新。如果使用 Typescript 或 Babel:

function test({ a = 2, b = 2 } = {}) { console.log(a + b) } test() // 4 test({ a: 3 }) // 5
使用 

=

 提供属性的默认参数,最后一个 
=
 确保在没有传入参数的情况下使用默认属性值设置空对象。


-1
投票
我想你正在寻找这样的东西(抱歉回复晚了):

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

编辑:抱歉,从一些旧代码中获取了这一点...您应该确保使用 hasOwnProperty() 方法来确保您不会迭代 function.prototype 上的所有内容

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


-6
投票
现在想起来,我有点喜欢这样:

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 }
    
© www.soinside.com 2019 - 2024. All rights reserved.