在 JavaScript 中更新/创建数组时运行自定义方法

问题描述 投票:0回答:3

我知道如何向对象添加自定义属性。

假设我有一个 foo 方法,

const foo = () => { console.log('custom method'); }

我可以将

foo
方法添加到 Array 原型并使用数组变量调用它

Array.prototype.foo = foo;

然后如果我创建一个名为的数组,

bar = [1, 2, 3];

然后跑

bar.foo()

它将执行我自定义的 foo 方法。但是我不知道如何在每次创建数组和每次更新数组时运行

foo
方法。

我想在 JavaScript 中创建/更新数组期间运行自定义方法并存储一些数据。我该怎么做?

假设我有一个自定义方法,

const customMethod = () => {
   ...doing some stuff    
}

我希望这个自定义方法在每次创建数组时运行,并在该数组中存储一些数据。就像我想找到数组的最大值并将其存储在带有键

maximum
的数组中,因为我们现在正在存储
length
。这样在创建/更新数组后我就不必计算最大值。我可以调用
myArray.maximum
并获得最大值。

这种方法做了类似的事情。但是它需要添加事件侦听器和一个新的推送方法,以便它可以在每次使用该自定义推送方法将某些内容推送到数组中时触发一个事件。但是在那种情况下,如果我使用常规的

Array.prototype.push
方法或使用像这样的扩展运算符创建新数组,我的自定义函数将不会更新,
newArr = [...oldArray, value]
.

更新: 经过搜索并借助评论中的链接,我发现如果不扩展数组对象或从头开始创建自定义数组类型(这是不可接受的)就无法修改数组对象。

我尝试扩展现有的 Array 类型并创建 MyCustomArray。但它根本不能用作数组。

class MyCustomArray extends Array {
   constructor(...args) {
      super(...args);
      console.log('custom array');
   }
}

知道如何扩展数组以创建我的 CustomArray 类型并向其添加侦听器,这样每次我创建/更新 CustomArray 时它都会计算最大值并将其设置为数组最大值属性(代码更改最少) ?

所以我的 CustomArray 将像普通数组一样拥有所有方法和实例变量。但它会做一件额外的事情,每次我更新/创建 CustomArray 时,它都会计算该数组中的最大值并将其设置为属性。

javascript arrays prototype
3个回答
2
投票

我不认为你可以像那样“劫持”所有数组。即使你猴子修补了原生的

Array
函数,用文字符号创建的数组,即
[]
也不会受到影响:

console.log(new Array(1,2,3));
console.log([4,5,6]);

Array = function () {
  throw new Error;
};

try {
  console.log(new Array(1,2,3));
} catch (e) {
  console.log('Cannot create array with new Array');
}

console.log([4,5,6]);

正如您可能认为的那样,您需要将数组传递给其他东西。您只需要选择适合您的解决方案。我个人会尝试使用代理:

const spy =
  (arr, fn) =>
    new Proxy(arr, {
      get(target, prop) {
        if (prop === "max") {
          return Math.max(...target);
        }
        if (prop === "min") {
          return Math.min(...target);
        }
        if (typeof target[prop] === "function") {
          fn();
        }
        return target[prop];
      }
    });


const arr = spy([1,2,3], () => console.log('spied'));
arr.push(500);
arr.push(-10);

console.log(arr.min, arr.max, JSON.stringify(arr), Array.isArray(arr));

我喜欢代理的一点是它的设计不会妨碍它。在上面的例子中,

arr
仍然是一个数组。你可以做你以前做的所有正常的事情。例如
Array.isArray
仍然返回
true
JSON.stringify
仍然产生预期的结果。

但是请注意,在上面的实现中,如果您从

arr
生成另一个数组,它不会被自动代理:

const arr = spy([1,2,3], fn);
arr.slice(1); // [2,3] (non-proxied array!)

0
投票

好吧,它有点蛮力,但是,Array 有点 special 因为如果您 provide 扩展类的构造函数,它创建新数组的方法将使用该构造函数,例如,

myCustomArray.filter
会产生
myCustomArray
对象结果,而不是
Array
对象结果。 (如果你不提供构造函数,这不起作用)。

如果我想随时访问该自定义类型中数组的特定自定义功能,我可能会在创建时将值设为空,并扩展更改现有对象的方法(如

pop
)以将其设为空,然后有一个方法可以提供非空缓存值,或者计算、缓存并返回当前值。

我没有仔细计算,但我认为只有少数数组方法会更改现有数组(而不是创建一个新数组),因此应该不需要太多努力。


0
投票

你的想法是对的:

class MyCustomArray extends Array {
   constructor(...args) {
      super(...args);
      console.log('custom array');
   }
}

您只需要更改要扩展课程的

Array
课程,它应该是
Array<T>
而不是像这样的
Array

class MyCustomArray extends Array<T> {
   constructor(...items) {
      super(...items);
      console.log('custom array');
   }
}

在这里

<T>
T 代表数组中包含的对象的类型,例如,如果它将是一个仅包含数字的数组,那么您将放置
<number>
.

现在您可以向这个数组添加任何功能并使用这个语法创建这个数组的实例:

const array = new MyCustomArray(item1, item2...)

希望这有帮助

© www.soinside.com 2019 - 2024. All rights reserved.