具有默认值的选项的javascript设计模式?
Posted
技术标签:
【中文标题】具有默认值的选项的javascript设计模式?【英文标题】:A javascript design pattern for options with default values? 【发布时间】:2012-03-25 00:54:58 【问题描述】:// 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;
上面的内容看起来非常冗长。使用默认参数处理参数选项的更好方法是什么?
【问题讨论】:
jQuery 和 underscore 都带有一个可以很好地处理这个问题的 extend 方法。 @blockhead - 所以,使用extend
并且不要为选项保留局部变量?
实际上,一段时间后我得出结论,当涉及到 javascript 命名参数时,冗长是一件好事。在函数开始时将每个参数复制到变量是记录预期选项的好方法,并且还可以让您修改它们而不会与原始对象搞砸。
@missingno - 我倾向于同意,但我不喜欢每个变量出现两次。在下面查看我的解决方案。
【参考方案1】:
将 ES2018 spread syntax 用于对象属性:
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);
没有依赖关系!
【讨论】:
这种模式很棒,为我节省了很多房地产。虽然应该注意的是,分配是浅的,而不是深的 小心!如果您的options
输入具有映射到undefined
的已知键(例如a
),您将不会从defaults
中获取该属性的值【参考方案2】:
有一种新的 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
【讨论】:
【参考方案3】:使用 ES6 扩展运算符而不使用外部库
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
【讨论】:
【参考方案4】:这是一个简单而干净的方法,希望对某人有所帮助:
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
【讨论】:
【参考方案5】:这使用 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 );
【讨论】:
命名有点令人困惑......你不是真的想要defaults.color
,你想要实际的选项。
我做了一个小的样式编辑,将'color'
更改为color
- 我认为这是更惯用的 js,不是吗?
编辑了命名,使其更加明显。
迟到了,但是 object.assign 的问题是它会从“options”复制整个对象。所以如果你有一个深层嵌套结构,它不会合并这些选项,它会覆盖它们。
@LarryBud 说得好。不过,深层嵌套似乎有点超出了原始问题的范围。【参考方案6】:
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;
【讨论】:
【参考方案7】:如果您有权访问ES6 with a stage 4 proposal(例如使用 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 的回答也提到了这一点)
【讨论】:
【参考方案8】:虽然Object.assign 是将选项与默认值合并的直接方法,但它有一些缺点:
如果您想使用三元运算符设置条件选项 - 即使undefined
值也会覆盖默认值:
const options =
logging: isProduction ? 'none' : undefined
;
const defaults =
logging: 'verbose'
Object.assign(, defaults, options); // logging: undefined !
如果您提供不正确的选项名称 - 您不会收到警告:
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."
希望这会有所帮助!
【讨论】:
【参考方案9】:如果您需要在许多连续的功能中执行此操作,那么标准化流程并加快流程的方法是:
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);
这样你只在函数内部定义一个选项对象,它包含默认值。
如果要对avoid prototype inheritance进行额外检查,第一个函数可以是:
function setOpts (standard, user)
if (typeof user === 'object')
Object.keys(user).forEach(function (key)
standard[key] = user[key];
);
不支持后一种情况:IE
(可以查看兼容性表here)
最后,ECMAScript 6 将为我们带来最好的方法:default parameters。
这需要几个月的时间才能在浏览器中得到广泛支持。
【讨论】:
"这需要几个月的时间才能在浏览器中得到广泛支持。"。我喜欢你的乐观。 :)【参考方案10】:还有更紧凑的 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"
【讨论】:
缺陷在于你的代码现在依赖 jQuery 来运行,如果这是使用 jQuery 的唯一原因。通过包含 jQuery,您在客户端添加了许多不必要的代码。【参考方案11】:为了获得没有额外依赖的默认选项,我使用以下模式:
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.
;
【讨论】:
另外,我想指出,截至 10 月 22 日,ripper234 接受的答案无法正常工作。在代码示例中 opt_c 和 opt_d 始终具有 default_for_c 和 default_for_d,即使选项对象中存在其他值。将参数的顺序更改为逻辑 OR 可以解决此问题(a || b 与 b || a)。【参考方案12】:我认为您正在寻找这样的东西(抱歉回复晚了):
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
【讨论】:
这里有一个错字:您正在检查选项是否是字符串"undefined"
,而不是真正的未定义(您可能遗漏了typeof
,但options[i] !== undefined
也很好,如果您'我愿意假设某些笨蛋没有重新定义undefined
)。
有关此伪代码的工作版本,请参阅the answer by Rick Deckard。【参考方案13】:
现在回想起来,我有点喜欢这样:
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
【讨论】:
继续我们的讨论......我以前也这样想,因为我是一个顽固的代码重复仇恨者,但我改变了主意。我意识到我经常会忘记接受哪些选项(并且必须搜索功能代码才能找到)。然后我开始添加 cmets 以列出命名参数,然后我继续复制所有变量,将它们全部视为相同。虽然输入的时间有点长,但它不会受到注释腐烂的影响,而且更灵活(我可以重命名函数内的变量,我可以轻松地添加和删除默认值等等......)。跨度> @missingno - 我不确定我是否遵循。在这个答案中,我确实将变量复制到变量中,这不会受到代码腐烂的影响。我只是不重复它们两次。 @Sukima 另一个问题是,如果 opt_options.c 设置为 false,它会被改回 true,这不是有意的。在这种情况下,明确检查 undefined 可能会更好。 您可能想使用 undefined,因为 null 值必须在 javascript 中显式声明为 null。以上是关于具有默认值的选项的javascript设计模式?的主要内容,如果未能解决你的问题,请参考以下文章
Javascript将具有多个值的选定选项从一个列表移动到另一个列表