12

I'm trying to figure out a one-liner code using map.

Here is a simple set up.

function Cat(name) {
    this.name = name;
    // FYI: using __proto__ is discouraged. thanks @KarelG
    this.__proto__.mew = function () {
        console.log(this.name + " mews");
    };
}

var cats = [
    new Cat('MB'),
    new Cat('503')
];

Then, I can use map() to call mew method in cats.

cats.map(function (cat) {
    cat.mew();
});
// MB mews
// 503 mews

call() on prototype also works.

cats.map(function (cat) {
    Cat.prototype.mew.call(cat);
});
// MB mews
// 503 mews

And here is my final one-liner, but it emits error and I could't understand why:

cats.map(Cat.prototype.mew.call);

// Uncaught TypeError: undefined is not a function
// at Array.map (<anonymous>)

Checking typeof Cat.prototype.mew.call says it's a function and map()'s parameter should be a function.

Could anyone explain why it doesn't work? What did I miss and where to correct?

2
  • 1
    yes it should be like cats.map((Cat) => Cat.mew.call()); or like cats.map(function(Cat) { Cat.mew.call()}); Commented Dec 22, 2017 at 6:33
  • 2
    just fyi, adding functions to the internal [[Prototype]] using __proto__ is discouraged (mainly due of the nature of prototypes which may impact the performance). Even if there is a function in favor of that __proto__ (the function is Object.setPrototypeOf()), I should not use it. Commented Dec 22, 2017 at 6:57

1 Answer 1

6

Could anyone explain why it doesn't work? What did I miss and where to correct?

Error message says

VM1100:15 Uncaught TypeError: undefined is not a function

This means that it is looking to call the mew method on a context which is undefined.

You need to bind the context as

cats.map( Cat.prototype.mew.call.bind( new Cat().mew ) );

Demo

function Cat(name) {
    this.name = name;
    this.__proto__.mew = function () {
        console.log(this.name + " mews");
    };
}

var cats = [
    new Cat('MB'),
    new Cat('503')
];
cats.map( Cat.prototype.mew.call.bind( new Cat().mew ) );

Or as @PatrickRoberts has suggested, you can use

cats.map(Function.call.bind(Cat.prototype.mew))

to avoid needlessly creating an instance of Cat

In cats.map(function (cat) { Cat.prototype.mew.call(cat); }); expression, why Cat.prototype.mew.call has a correct context?

As per spec

If a thisArg parameter is provided, it will be used as the this value for each invocation of callbackfn. If it is not provided, undefined is used instead.

So, unless you pass a context like

cats.map( Cat.prototype.mew.call,  new Cat().mew );

It will be undefined.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, your code works. In cats.map(function (cat) { Cat.prototype.mew.call(cat); }); expression, why Cat.prototype.mew.call has a correct context?
cats.map(Function.call.bind(Cat.prototype.mew)) would avoid needlessly constructing an instance of Cat
@Kita in map (as per spec), unless the context is passed explicitly, it is taken as undefined.

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.