4

I know how to add a custom property to an object.

Lets say I have a foo method,

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

I can add the foo method to the Array prototype and call it using array variables by

Array.prototype.foo = foo;

And then if I create an array called,

bar = [1, 2, 3];

and run

bar.foo()

It will execute my custom foo method. But I don't know how to run the foo method every time an array is created and every time an array is updated.

I want to run a custom method and store some data during array creation/updating in JavaScript. How do I do that?

Lets say I have a custom method,

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

I want this custom method to run every time an array is created and store some data in that array. Like I want to find the maximum of the array and store it inside the array with key maximum as we are storing length now. So that after creating/updating the array I don't have to calculate the maximum. I can just call myArray.maximum and will get that maximum value.

This approach does something similar. But it requires to add event listener and a new push method so that it can fire an event everytime something pushed into the array using that custom push method. But in that case my custom function will not be updated if I use regular Array.prototype.push method or create a new array using spread operator like this, newArr = [...oldArray, value].

Update: After searching and with the help of the links in comment I found out that it is not possible to modify the Array object without extending it or creating a custom array type from scratch(which is not acceptable).

I have tried to extend the existing Array type and create MyCustomArray. But it doesn't work as an array at all.

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

Any idea how can I extend the Array to create my CustomArray type and add a listener to it, so that every time I create/update a CustomArray it calculates the max and set it as an array max attribute (With minimum code change)?

So my CustomArray will have all the methods and instance variables like a normal array. But it will do one extra thing, every time I update/create a CustomArray it will calculate the max in that array and set it as property.

12
  • Does this answer your question? adding custom functions into Array.prototype Commented Nov 3, 2020 at 16:30
  • Not really. I was looking for some way to customize the array object during each update and during the creation of the array. Commented Nov 3, 2020 at 16:33
  • "I know how to add a custom property to an object" - can you show what you mean by that? Maybe some code for how you get a custom method to run every time a non-array object is created or data is stored in it? Commented Nov 3, 2020 at 16:39
  • Like I can do that to add the foo method to Array prototype and call it using array variables. Array.prototype.foo = foo; . And then if I create an array called bar and run bar.foo() it will execute my custom foo method. But I don't know how to run the foo method everytime an array is created and everytime an array is updated. Commented Nov 3, 2020 at 16:42
  • 2
    @sabbir.alam You can't do that, it is not possible to hijack "every array". You can explicitly wrap some specific arrays though, so that you control how they are updated, and during that update also do whatever else you want. Commented Nov 3, 2020 at 16:51

3 Answers 3

2

I don't think you can "hijack" all arrays just like that. Even if you monkey patch the native Array function, arrays created with the literal notation i.e. [] won't be affected by that:

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]);

As you probably figured you will need to pass your arrays to something else. You just need to pick the solution that works for you. Personally I'd try with a proxy:

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));

What I like about a proxy is that it can be designed so that it doesn't get in the way. In the example above arr is still an array. You can do all the normal stuff you used to do. For example Array.isArray still returns true and JSON.stringify still produces the expected result.

However be aware that in the implementation above, if you produce another array from arr it wouldn't be automatically proxied:

const arr = spy([1,2,3], fn);
arr.slice(1); // [2,3] (non-proxied array!)
Sign up to request clarification or add additional context in comments.

7 Comments

You mean proxy is for individual array instances, right?
@sabbir.alam Yes. But I'm sure we could write a proxy that return another proxy.
Now, this is going straight above my head. Some pointer for understanding proxy would be nice where I can see this pattern of returning a new proxy.
An offtopic question, Why everyone is double quoting hijack like this "hijack"? :)
@sabbir.alam I was just trying to say that you can't automatically intercept all the calls just like that for free. (I didn't mean this in a bad way though :)
|
0

Well, its a bit brute force, but, Array is kind of special in that if you provide a constructor to an extension class, its methods that create new arrays will use that constructor, so, for example, myCustomArray.filter would produce a myCustomArray object result rather than an Array object result. (If you DON'T provide a constructor, this doesn't work).

If I wanted to have ready access to a particular custom feature of an Array in that custom type, I'd probably null the value on creation, and extend methods that change the existing object (like pop) to null it as well, and then have a method which would either provide the non-null cached value, or calculate, cache and return the current value.

I didn't go through and count, but I think there are only a handful of array methods which change the existing arrays (as opposed to creating a new one), so it should not require much effort.

Comments

0

You have the right idea here:

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

You only need to change the Array class from which you are extending your class, It should be Array<T> instead of just Array like this:

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

Here <T> the T represents the type of objects contained in the array, for example if it will be an array of just numbers then you would put <number>.

Now you can add any functionality to this array and create instances of this array using this sintax:

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

Hope this helps

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.