我有一个带有属性的对象。这些属性可能存在或可能不存在。
为了访问属性,我的工作方式如下:
myData.properties.find(p => p.name == 'target').value = 'Foo';
现在可能会发生myData
根本没有名称为target
的属性。
因此,我想扩展Array.prototype.find()
函数以创建缺少的元素。
Array.prototype.findOrCreate()
应该稍后可以使用:
myData.properties.findOrCreate(p => p.name == 'target', { name: 'target', value: '' });
但是,我不知道如何使用该功能。
我不是在这里寻找解决方案(我什至要求这里没有人提供解决方案),而是在实施该解决方案时所必需的理论。
在PHP中,我将研究函数定义并将其扩展。但是,我找不到Array.prototype.find()的函数定义。
答案 0 :(得分:1)
不需要扩展数组原型(因为这可能导致冲突和前向兼容性问题)。相反,您可以创建一个单独的函数,以数组作为输入来执行此操作。如果.find()
返回?
,则可以将.find()
与条件(undefined
)运算符一起使用来默认值:
const findOrCreate = (arr, f, def) => {
const res = arr.find(f);
return res === undefined ? def : res;
}
const data = [{name: 'foo', value: 1}, {name: 'bar', value: 1}];
const obj = findOrCreate(data, p => p.name === 'xyz', { name: 'target', value: '' });
console.log(obj);
或者,如果您支持ES2020,则可以像这样使用nullish coalescing operator(??
):
const findOrCreate = (arr, f, def) => arr.find(f) ?? def;
const data = [{name: 'foo', value: 1}, {name: 'bar', value: 1}];
const obj = findOrCreate(data, p => p.name === 'xyz', { name: 'target', value: '' });
console.log(obj); // {name: "target", value: ""}
答案 1 :(得分:0)
您可以仅向Array.prototype
添加新功能。 find()
是一个简单的循环,数组没有其他索引魔术,它必须遍历数组,或者偶然发现一个合适的元素。
var a=[{name:"something",value:1},{name:"somethingelse",value:2}];
Array.prototype.findOrCreate=function(name){
for(var elem of this)
if(elem.name === name)
return elem;
var newelem={name:name};
this.push(newelem);
return newelem;
}
console.log("original",JSON.stringify(a));
a.findOrCreate("something").value=3;
console.log("modified",JSON.stringify(a));
a.findOrCreate("somethingbrandnew").value=4;
console.log("extended",JSON.stringify(a));
请注意,您宁愿使用Map
来存储键值对(甚至是简单的对象)。
答案 2 :(得分:0)
在绝对可以扩展Vanilla JS对象的地方,这种做法通常被认为是不好的,因为它会导致conflicts和compatibility上的问题。
在这种情况下,由于要在数组中查找名称为target
的项,如果没有,请创建一个新项,我将拥有更多的原子函数。这些动作对我来说显然是分开的,将它们放在一种方法中也不是一种好习惯-因为原子函数很容易组合。
在您的情况下,代码很简单:
let target = myData.properties.find(p => p.name == 'target') || {name: "target"};
target.value = "Foo";
如果您还想将此新对象添加到properties
数组中:
let target = myData.properties.find(p => p.name == 'target');
if (!target) {
target = {name: "target"};
myData.properties.push(target);
}
target.value = "Foo";
但是,如果您确实希望扩展本机数组:
Array.prototype.findOrCreate = function findOrCreate(predicate, item) {
let target = this.find(predicate);
if (!target) {
this.push(item);
return item;
}
return target;
}
myData.properties.findOrCreate(p => p.name == 'target', {name: "target"}).value = "Foo";
但是,作为函数可能会更安全:
function findOrCreate (array, predicate, item) {
let target = array.find(predicate);
if (!target) {
array.push(item);
return item;
}
return target;
}
findOrCreate(myData.properties, p => p.name == 'target', {name: "target"}).value = "Foo";
因为您不会发生冲突(您也可以避免使用Symbol进行冲突,但是每次您要访问该方法时,都必须在代码中将其扔掉。)
您可能拥有更多的原子功能:
find(myData.properties, item => item.name == "target")
.or(create({name : "target"}))
.value = "Foo";
该方法的实现可能类似于:
function find(array, predicate) {
const item = array.find(predicate);
return {
or(action) {
return item || action(array);
}
}
}
const create = (item) => (array) => (array.push(item), item);
但是,如果您真的更喜欢OOP方法,我强烈建议您在自己的课程中扩展Array
:
class MyArray extends Array {
constructor(...items) {
super(...items);
}
findOrCreate(predicate, item) {
let target = this.find(predicate);
if (!target) {
this.push(item);
return item;
}
return target;
}
}
这时,您可以使用数组的所有方法,也可以使用您的方法。而且,您也可以将常规数组转换为自己的数组-如果您不能直接创建它们,例如,您是从外部API获得的>
myData.properties = MyArray.from(myData.properties);
如果您是从JSON字符串中获取的,则还可以在parsing期间使用自定义reviver
来使用您的类而不是本机数组。