增强Vanialla JS功能

时间:2019-11-21 10:56:08

标签: javascript arrays

我有一个带有属性的对象。这些属性可能存在或可能不存在。

为了访问属性,我的工作方式如下:

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()的函数定义。

3 个答案:

答案 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对象的地方,这种做法通常被认为是不好的,因为它会导致conflictscompatibility上的问题。

在这种情况下,由于要在数组中查找名称为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来使用您的类而不是本机数组。

相关问题