14

I noticed a weird thing in javascript. Consider the below:

var fn = ''.toUpperCase.call
console.log(typeof fn); // "function"
fn(); // Uncaught TypeError: `fn` is not a function

The above was executed on my Chrome's Developer Console. Version is 43.0.2357.81 m.

The typeof operator clearly shows that fn is a function, but the error suggests otherwise.

I've noticed that Function.apply shows at least some meaningful error message.

So, when is a function, not a function?

John
  • 10,154
  • 9
  • 79
  • 143
Amit Joki
  • 53,955
  • 7
  • 67
  • 89
  • Please show *how* exactly you're calling `fn`. – deceze Jun 02 '15 at 12:00
  • 5
    I'm getting `undefined is not a function` when calling `fn`, *not* `fn is not a function`. – Darkhogg Jun 02 '15 at 12:02
  • @deceze this isn't a production code..I just happened to see a question, pondering on which, I came across this weird stuff. – Amit Joki Jun 02 '15 at 12:02
  • 2
    Sure. Still, please show a full example of what code produces what error. – deceze Jun 02 '15 at 12:03
  • 2
    Please clarify if consoele is a typo and edit if not – Burkhard Jun 02 '15 at 12:03
  • I guess you are calling `fn()` and `call` expects to be executed on a function, which your not doing, your calling on `undefined`. In the latest chrome, the error shows `Uncaught TypeError: undefined is not a function` – Xotic750 Jun 02 '15 at 12:04
  • @Burkhard it was a typo indeed. Fixed. Thanks – Amit Joki Jun 02 '15 at 12:04
  • 2
    Firebug has a more insightful error message: `TypeError: Function.prototype.call called on incompatible undefined`. Basically `call()` needs to be called on an object (`this`), which is lost, being called outside the original statement. – jd. Jun 02 '15 at 12:05
  • Saw a similar issue on [Remy Sharp's Twitter](https://twitter.com/rem/status/601481375854759937) feed a while back: http://jsconsole.com/?c%20%3D%20Function.prototype.call.bind()%3B%20console.log(c%20instanceof%20Function%2C%20typeof%20c)%3B%20c()%3B – CodingIntrigue Jun 02 '15 at 12:06
  • @deceze that's the full code I have. I've made it one. – Amit Joki Jun 02 '15 at 12:06
  • Interestingly, since the "bug" is simply that the `this` inside `fn()` is not a function (thus, `fn()` is actually trying to do `undefined()` and it should fail since undefined is not a function). This can be made to work by doing: `fn.call(function(){return 'hi'})` which sets `this` correctly inside `fn()` – slebetman Jun 02 '15 at 12:11
  • BTW `String.prototype.toUpperCase.call()` gives this error in Chrome: "Uncaught TypeError: String.prototype.toUpperCase called on null or undefined" – Matt Browne Jun 02 '15 at 12:11
  • https://nemisj.com/some-interesting-aspects-of-call-method-in-javascript/ – felipsmartins Jun 02 '15 at 12:12
  • @MattBrowne: Correct, the first argument of `.call()` is the `this` value you want it to operate on. So since an empty argument in js is `undefined` you're basically trying to do: `undefined.toUpperCase()` – slebetman Jun 02 '15 at 12:13
  • @MattBrowne that's reasonable since you've not passed the first argument, and it will be `undefined` by default – Amit Joki Jun 02 '15 at 12:13

3 Answers3

20

Context in Javascript is always established by the way you call a function.

var fn = ''.toUpperCase.call

This assigns the prototype implementation of the call function to fn. If you now call fn(), there's no context to the call. call would try to invoke the function object it was associated with. However, that context is established at call time. Since you're not giving it any context at call time, some internal component of call is throwing an error.

You'd have to do this:

fn.call(''.toUpperCase)

That's right, you call the call function, establishing a context, namely the toUpperCase string function. In this specific case this would lead to another error inside toUpperCase, since it is not bound to a specific context. You'd need to establish that context explicitly as well:

var fn = ''.toUpperCase.call
fn.call(''.toUpperCase.bind(''))

Also see How does the "this" keyword work?

Community
  • 1
  • 1
deceze
  • 471,072
  • 76
  • 664
  • 811
  • so do you think, [my answer](http://stackoverflow.com/questions/30594471/array-prototype-map-is-not-a-function-when-passing-a-function-generated-by-funct/30594762#30594762) is the answer to the linked question too? – Amit Joki Jun 02 '15 at 12:11
  • @Amit Yeah, you're basically saying the same thing there. – deceze Jun 02 '15 at 12:21
  • `Context in Javascript is always established by the way you call a function.` Unless it's an arrow function, or the function has been bound. – Juan Mendes Jul 03 '19 at 11:22
3

deceze's answer is correct, I just want to explain it from a different point of view.

Your fn is a reference to Function.prototype.call which needs to be called with a function as its this reference, in this case, the context for call is String.prototype.toUpperCase, which was inherited through ''.toUpperCase

On top of that, String.prototype.toUpperCase also has to be called with a specific context, the string to upper case.

Here's another way to code what you wanted that may help you understand what is going on.

    var str = 'aaa';
    var upper = ''.toUpperCase;
    var fn = upper.call;
    // Now we have to specify the context for both upper and fn
    console.log( fn.call(function() { return upper.call(str)}) ); // AAA

In your example, fn() is attempting to call call but it's not specifying the context, which typically defaults to the window object but in this case it just makes it undefined, which triggers a weird error in Chrome (as you discovered), but Firefox is clearer about the problem,

TypeError: Function.prototype.call called on incompatible undefined

Community
  • 1
  • 1
Juan Mendes
  • 80,964
  • 26
  • 138
  • 189
1

So, when is a function, not a function?

The function is a function, but you are not executing it correctly, as explained in comments and the answer by @deceze

I noticed a weird thing in javascript.

You seem confused by the message that you are getting in the environment in which you tested, the message is not strictly correct.

Uncaught TypeError: fn is not a function

The current stable version of chromium (40.0.2214.91 on my linux install, chrome fails to run on my hardware without making settings changes), chrome being the environment on which you tested, gives a seemingly more correct error message that makes more sense.

Uncaught TypeError: undefined is not a function

So, were you asking/wanting to ask a serious question or were you just poking a little fun at a mistake in a version of chrome?

Xotic750
  • 20,394
  • 8
  • 50
  • 71
  • 2
    `40.0.2214.91` is not the current stable version of Chrome; [`43.0.2357.81` is](http://googlechromereleases.blogspot.com/2015/06/stable-update-for-chrome-os.html). The package source you're getting Chrome from is not up to date. Also, I'd just like to make the distinguishing point that (even if the OP knew the answer to this question before asking), wanting to create a repository of information about this very confusing error is far cry from "poking fun". – apsillers Jun 02 '15 at 12:38
  • 1
    I've lots of other work to do rather than poking a little fun at chrome. And FYI, I came across this stuff while answering [here](http://stackoverflow.com/questions/30594471/array-prototype-map-is-not-a-function-when-passing-a-function-generated-by-funct/30594762#30594762) and not everyone uses the chrome version you use(and I don't update chrome manually, it happens automatically) and thanks for your answer. Thanks for the comment above mine too. – Amit Joki Jun 02 '15 at 12:38
  • On Fedora 19 it is 40.0.2214.91, which updates automatically using YUM from a package repository, I don't intend to build it myself (and I just remembered this is chromium rather than chrome). I understand that different OS's have different stable versions, and there are bound to be differences. Ok, you did not include that information in your question, so you were really trying to check that you have answered another question correctly? Or I still don't really understand what this question is or what kind of answer you are expecting? – Xotic750 Jun 02 '15 at 12:45