我想创建一个对象,有条件地添加成员。
简单的方法是:
var a = {};
if (someCondition)
a.b = 5;
现在,我想写一个更习惯的代码。我在努力:
a = {
b: (someCondition? 5 : undefined)
};
但是现在,b是a的一个元素,它的值是未定义的。这不是我们想要的结果。
有没有方便的解决办法?
更新
我寻求一个解决方案,可以处理一般情况与几个成员。
a = {
b: (conditionB? 5 : undefined),
c: (conditionC? 5 : undefined),
d: (conditionD? 5 : undefined),
e: (conditionE? 5 : undefined),
f: (conditionF? 5 : undefined),
g: (conditionG? 5 : undefined),
};
在纯Javascript中,我想不出比第一个代码片段更习惯的东西了。
但是,如果使用jQuery库不是不可能的,那么$.extend()应该可以满足您的需求,因为正如文档所述:
未定义的属性不会被复制。
因此,你可以这样写:
var a = $.extend({}, {
b: conditionB ? 5 : undefined,
c: conditionC ? 5 : undefined,
// and so on...
});
并获得您期望的结果(如果条件b为假,则b将不存在于a中)。
我认为你第一个有条件地增加成员的方法是完全没问题的。我不同意不让元素b (a)的值为undefined。使用带有in操作符的for循环来添加一个未定义的检查非常简单。但无论如何,你可以很容易地编写一个函数来过滤掉未定义的成员。
var filterUndefined = function(obj) {
var ret = {};
for (var key in obj) {
var value = obj[key];
if (obj.hasOwnProperty(key) && value !== undefined) {
ret[key] = value;
}
}
return ret;
};
var a = filterUndefined({
b: (conditionB? 5 : undefined),
c: (conditionC? 5 : undefined),
d: (conditionD? 5 : undefined),
e: (conditionE? 5 : undefined),
f: (conditionF? 5 : undefined),
g: (conditionG? 5 : undefined),
});
还可以使用delete操作符就地编辑对象。
在EcmaScript2015中,你可以使用Object.assign:
Object.assign(a, conditionB ? { b: 1 } : null,
conditionC ? { c: 2 } : null,
conditionD ? { d: 3 } : null);
var a,条件b,条件c,条件;
条件c = true;
A = {};
对象。赋值(a, conditionB ?{b: 1}: null,
conditionC吗?{c: 2}: null,
conditionD吗?{d: 3}: null);
console.log(一个);
一些评论:
对象。Assign会原地修改第一个参数,但它也会返回更新后的对象:所以你可以在一个更大的表达式中使用这个方法来进一步操作对象。
而不是null,你可以传递undefined或{},有相同的结果。您甚至可以提供0,因为原始值被包装了,而Number没有自己的可枚举属性。
更简洁
进一步考虑第二点,你可以把它缩短如下(正如@Jamie指出的那样),因为假值没有自己的可枚举属性(false, 0, NaN, null, undefined,”,除了document.all):
Object.assign(a, conditionB && { b: 1 },
conditionC && { c: 2 },
conditionD && { d: 3 });
条件a,条件b,条件c,条件d;
conditionC = "this is true ";
条件= NaN;/ / falsy
a = {};
物体。assign(a,条件b && {b: 1},
条件&& {c: 2},
条件&& {d: 3});
console.log (a);
使用增强对象属性并只设置为真值的属性,例如:
[isConditionTrue() && 'propertyName']: 'propertyValue'
因此,如果条件不满足,它不会创建首选属性,因此您可以丢弃它。
见:http://es6-features.org/ ComputedPropertyNames
更新:
最好遵循Axel Rauschmayer在他的博客文章中关于在对象字面量和数组中有条件地添加条目的方法(http://2ality.com/2017/04/conditional-literal-entries.html):)
const arr = [
...(isConditionTrue() ? [{
key: 'value'
}] : [])
];
const obj = {
...(isConditionTrue() ? {key: 'value'} : {})
};
对我帮助很大。
使用lodash库,您可以使用_.omitBy
var a = _.omitBy({
b: conditionB ? 4 : undefined,
c: conditionC ? 5 : undefined,
}, _.IsUndefined)
当您有可选的请求时,这会非常方便
var a = _.omitBy({
b: req.body.optionalA, //if undefined, will be removed
c: req.body.optionalB,
}, _.IsUndefined)
包装成一个对象
像这样的东西比较干净
const obj = {
X: 'dataX',
Y: 'dataY',
//...
}
const list = {
A: true && 'dataA',
B: false && 'dataB',
C: 'A' != 'B' && 'dataC',
D: 2000 < 100 && 'dataD',
// E: conditionE && 'dataE',
// F: conditionF && 'dataF',
//...
}
Object.keys(list).map(prop => list[prop] ? obj[prop] = list[prop] : null)
包装成数组
或者如果你想使用Jamie Hill的方法并且有一个很长的条件列表,那么你必须写。语法多次。为了更简洁,可以将它们包装到一个数组中,然后使用reduce()将它们作为单个对象返回。
const obj = {
X: 'dataX',
Y: 'dataY',
//...
...[
true && { A: 'dataA'},
false && { B: 'dataB'},
'A' != 'B' && { C: 'dataC'},
2000 < 100 && { D: 'dataD'},
// conditionE && { E: 'dataE'},
// conditionF && { F: 'dataF'},
//...
].reduce(( v1, v2 ) => ({ ...v1, ...v2 }))
}
或者使用map()函数
const obj = {
X: 'dataX',
Y: 'dataY',
//...
}
const array = [
true && { A: 'dataA'},
false && { B: 'dataB'},
'A' != 'B' && { C: 'dataC'},
2000 < 100 && { D: 'dataD'},
// conditionE && { E: 'dataE'},
// conditionF && { F: 'dataF'},
//...
].map(val => Object.assign(obj, val))
用let定义一个变量,然后赋值一个新属性
let msg = {
to: "hito@email.com",
from: "hifrom@email.com",
subject: "Contact form",
};
if (file_uploaded_in_form) { // the condition goes here
msg.attachments = [ // here 'attachments' is the new property added to msg Javascript object
{
content: "attachment",
filename: "filename",
type: "mime_type",
disposition: "attachment",
},
];
}
现在味精变成了
{
to: "hito@email.com",
from: "hifrom@email.com",
subject: "Contact form",
attachments: [
{
content: "attachment",
filename: "filename",
type: "mime_type",
disposition: "attachment",
},
]
}
在我看来,这是一个非常简单易行的解决方案。
性能测试
经典的方法
const a = {};
if (someCondition)
a.b = 5;
VS
展开算子法
const a2 = {
...(someCondition && {b: 5})
}
结果:
经典的方法要快得多,所以要考虑到语法糖化更慢。
testClassicConditionFulfilled ();// ~ 234.9ms
testClassicConditionNotFulfilled ();/ / ~ 493 1ms。
testSpreadOperatorConditionFulfilled ();/ / ~紧密4ms。
testSpreadOperatorConditionNotFulfilled ();/ / ~ 2239。卫生组织
function testSpreadOperatorConditionFulfilled() {
const value = 5;
console.time('testSpreadOperatorConditionFulfilled');
for (let i = 0; i < 200000000; i++) {
let a = {
...(value && {b: value})
};
}
console.timeEnd('testSpreadOperatorConditionFulfilled');
}
function testSpreadOperatorConditionNotFulfilled() {
const value = undefined;
console.time('testSpreadOperatorConditionNotFulfilled');
for (let i = 0; i < 200000000; i++) {
let a = {
...(value && {b: value})
};
}
console.timeEnd('testSpreadOperatorConditionNotFulfilled');
}
function testClassicConditionFulfilled() {
const value = 5;
console.time('testClassicConditionFulfilled');
for (let i = 0; i < 200000000; i++) {
let a = {};
if (value)
a.b = value;
}
console.timeEnd('testClassicConditionFulfilled');
}
function testClassicConditionNotFulfilled() {
const value = undefined;
console.time('testClassicConditionNotFulfilled');
for (let i = 0; i < 200000000; i++) {
let a = {};
if (value)
a.b = value;
}
console.timeEnd('testClassicConditionNotFulfilled');
}
testClassicConditionFulfilled(); // ~ 234.9ms
testClassicConditionNotFulfilled(); // ~493.1ms
testSpreadOperatorConditionFulfilled(); // ~2649.4ms
testSpreadOperatorConditionNotFulfilled(); // ~2278.0ms
为了完整起见,如果您想添加额外的描述符,可以使用Object.defineProperty()。注意,我特意添加了enumerable: true,否则该属性不会出现在console.log()中。这种方法的优点是,如果你想添加多个新属性,你也可以使用Object.defineProperties()(然而,这样每个属性都依赖于相同的条件…)
const select = document.getElementById("condition");
const output = document.getElementById("output");
let a = {};
let b = {};
select.onchange = (e) => {
const condition = e.target.value === "true";
condition
? Object.defineProperty(a, "b", {
value: 5,
enumerable: true,
})
: (a = {});
condition
? Object.defineProperties(b, {
c: {
value: 5,
enumerable: true,
},
d: {
value: 6,
enumerable: true,
},
e: {
value: 7,
enumerable: true,
},
})
: (b = {});
outputSingle.innerText = JSON.stringify(a);
outputMultiple.innerText = JSON.stringify(b);
};
Condition:
<select id="condition">
<option value="false">false</option>
<option value="true">true</option>
</select>
<br/>
<br/>
Single Property: <pre id="outputSingle">{}</pre><br/>
Multiple Properties: <pre id="outputMultiple">{}</pre>
我用另一个选项做了一个小的基准测试。我喜欢从一些物体上去除“累赘”。通常是错误的值。
以下是本尼的结果:
清洁
const clean = o => {
for (const prop in o) if (!o) delete o[prop];
}
clean({ value });
传播
let a = {
...(value && {b: value})
};
if
let a = {};
if (value) {
a.b = value;
}
结果
clean : 84 918 483 ops/s, ±1.16% | 51.58% slower
spread : 20 188 291 ops/s, ±0.92% | slowest, 88.49% slower
if : 175 368 197 ops/s, ±0.50% | fastest
简单的es6解决方案
带有(&)的单一条件
const didIPassExam = true
Const study = {
星期一:“写作”,
周二:“阅读”,
/*有条件检查,如果为真,则增加周三学习*/
...(didIPassExam &&{星期三:'睡得开心'})
}
console.log(研究)
带有(?):)
Const分数= 110
//const得分= 10
Const storage = {
答:10
b: 20,
...(得分> 100 ?{c: 30}: {d:40})
}
console.log(存储)
解释
假设你有一个这样的存储对象
const storage = {
a : 10,
b : 20,
}
你想在此基础上有条件地增加一个道具
const score = 90
如果分数大于100,您现在希望将道具c:30添加到存储区。
如果score小于100,则需要将d:40添加到存储中。你可以这样做
const score = 110
const storage = {
a:10,
b:20,
...(score > 100 ? {c: 30} : {d:40})
}
上面的代码给出存储为
{
a: 10,
b: 20,
c: 30
}
如果分数= 90
然后你得到存储
{
a: 10,
b: 20,
d: 40
}
Codepen例子