最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to use map() to call method common to list of objects? - Stack Overflow

programmeradmin0浏览0评论

TL;DR: Basically, I'm trying to use Array.prototype.map() to call a method mon to a list of context objects.

Say I have some code like this:

a.doSomething();
b.doSomething();
c.doSomething();
d.doSomething();
e.doSomething();
...

This is overly verbose. I want to be able to shorten this code up to something like this

[a, b, c, d, e].map(doSomething);

because I don't want to have to use a for loop.

How would I be able to do this with the native map function in JS, without any external libraries?

Thank you in advance! If this post is too long, I will happily edit it. Just scream at me.

Related questions:

How to use map() to call class methods on a list of objects

How to call same method for a list of objects?

I didn't use the info in the related questions because Python and JS use different paradigms and I don't have assurance they'll work the same. If I'm wrong here, please don't hesitate to burn me at the stake.

Extra info, disregard if you'd like:

I've already RTFM, and the wording and examples suggest I can't do the above with map. The MDN docs describe map as "calling a provided function on every element in an array", while the MSDN docs say it "calls a function on each element of an array, and returns an array." I'm not trying to call a method on an array, I'm trying to call a method from several context objects. If this is not possible, is there a native polyfill?

I'm using this in a game, so speed is my main concern. If there is a more optimal way to code this, please let me know!

TL;DR: Basically, I'm trying to use Array.prototype.map() to call a method mon to a list of context objects.

Say I have some code like this:

a.doSomething();
b.doSomething();
c.doSomething();
d.doSomething();
e.doSomething();
...

This is overly verbose. I want to be able to shorten this code up to something like this

[a, b, c, d, e].map(doSomething);

because I don't want to have to use a for loop.

How would I be able to do this with the native map function in JS, without any external libraries?

Thank you in advance! If this post is too long, I will happily edit it. Just scream at me.

Related questions:

How to use map() to call class methods on a list of objects

How to call same method for a list of objects?

I didn't use the info in the related questions because Python and JS use different paradigms and I don't have assurance they'll work the same. If I'm wrong here, please don't hesitate to burn me at the stake.

Extra info, disregard if you'd like:

I've already RTFM, and the wording and examples suggest I can't do the above with map. The MDN docs describe map as "calling a provided function on every element in an array", while the MSDN docs say it "calls a function on each element of an array, and returns an array." I'm not trying to call a method on an array, I'm trying to call a method from several context objects. If this is not possible, is there a native polyfill?

I'm using this in a game, so speed is my main concern. If there is a more optimal way to code this, please let me know!

Share Improve this question edited May 23, 2017 at 12:29 CommunityBot 11 silver badge asked Jul 26, 2015 at 2:57 Dylan LaCoursiereDylan LaCoursiere 4791 gold badge8 silver badges18 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 3

The closest I can think of is

[a, b, c, d, e].forEach(function(item) {item.doSomething()});

using [].map would be pointless for your purpose

edit, you could also do something like this

Array.prototype.fnName = function(methodName) {
    var args = Array.prototype.slice.call(arguments, 1);
    this.forEach(function(item) { 
        item[methodName].apply(item, args); 
    });
};

I've used fnName, you could call it fnMap or something else that makes sense to you

you'd use it like

[a,b,c].fnName('doSomething', param1, param2 ...)

Replied without thinking. Array#map is supported in most browsers today and was apart of ES5. See the MDN link: https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

[1,2,3,4,5].map(function(item){ console.log(item)}

will print:

1
2
3
4
5

and return undefined. If you return something like:

[1,2,3,4,5].map(function(item){return 2*item})

it will return:

[2,4,6,8,10]

But it's not neccessary to return anything. You can just as easily do a forEach.

items.forEach(function(item){items.method()}

It's all O(n) time, because to apply anything to n items in the array, it takes at least n time. For loop is as good as it gets.

WRONG ES6 supports #map but it's not available on all browsers/Node.js without different settings being set.

Underscorejs has this functionality, if you do want an external library. See their map documentation: http://underscorejs/#map

I use it in server code, no problem. Should probably work for your game. If page load time is an issue, you can always extend the prototype of Array yourself.

Array.map() calls given function for every item in array. That’s where anonymous functions can be very appropriate:

[a, b, c].map(function (item) { item.doSomething(); });

Seeing this post is pretty old, I wanted to give some elements that might plement the other answers.

Let's take a simple class A which has a method double.

class A {
  constructor(n) { this.n = n; }
  double() { return 2 * this.n; }
}

We will have an array of instances to work on.

const data = [ new A(3), new A(12), new A(4), new A(10) ];

Function wrapper

The Array#map() method takes in a function callback that it will execute on each of the elements of that array.

Essentially it es down to wrapping the method call on the object. To do so we can use a standard function like:

data.map(function(o) { return o.double() });

Usually however you would e across an arrow function instead:

data.map(o => o.double());

which alleviates the need for parentheses, curly braces and the return statement.


Destructuring

Now we could try to improve this. We can actually destructure objects in the function's header. Below, we are retrieving double directly from the object and calling it:

data.map(({ double }) => double());

However, that won't work...

TypeError: this is undefined

What happens is, we are not calling double on the current object. This is because of function binding, when independently calling double as a function we are leaving the value of this unset.

We could bind the function to the object,

data.map((o, _, __, fn=o.double) => fn.bind(o)())

but... this would defeat the purpose of going after a shorter version.


Class member arrow functions!

However, there is a way around this by using class member arrow functions.

class A {
  constructor(n) { this.n = n; }
  double = () => { return 2 * this.n; }
}

This way we bypass the need for a bind when calling the method.

Then we can successfully do:

data.map(({ double } => double());

Keep in mind, this is not supported by all browsers. And since you will have to fiddle with your class declarations in order to use this syntax sugar. I wouldn't really remend it. Class member arrow functions are quite nice for event handlers though...


Demo:

class A {
  constructor(n) { this.n = n; }
  double() { return 2 * this.n; }
  triple = () => { return 3 * this.n; }
}

const data = [new A(3), new A(12), new A(4), new A(10)];

console.log(data.map(o => o.double()));

// console.log(data.map(({ double }) => double())); // won't work!

console.log(data.map((o, _,__, fn=o.double) => fn.bind(o)())); // ugly

console.log(data.map(({triple}) => triple()));

发布评论

评论列表(0)

  1. 暂无评论