Roland
Roland

Reputation: 9701

Typeof evaluates differently depending on parentheses

I have the following code example :

Application.Polyfills.prototype.prettifyCode = function(enable, js_lib, css_lib) {
    return Modernizr.load([{
        test : enable,
        yep : [ css_lib, js_lib ],
        complete: function() {
            return window.prettyPrint && prettyPrint();
        }
    }]);
};

If I do a console.log(typeof this.prettifyCode), in which this refers to the Application.Polyfills, I get function, but if I do console.log(typeof this.prettifyCode()) I get undefined. Can someone tell me why do I get that and how can I fix it so I get the same result in both cases, because most of the times the functions require arguments so I need to use parentheses ?

To be more specific, I have a method :

Application.Polyfills.prototype.callPolyfills = function(array) {
    for (var i = array.length - 1; i >= 0; i--) {
        console.log(typeof array[i]);
        (typeof array[i] === "function") ? array[i].apply(this, [ this ]) : console.log('Index [' + i + ']: Invalid [ Function Required ]') ;
    };
};

The above method is used to call all my functions that are placed inside an array, something like the following :

this.callPolyfills([
        self.polyfillize([
            {
                test : [Modernizr.localstorage, Modernizr.sessionstorage],
                polyfill : [self.polyfills_url.storage_polyfill_url]
            },
            {
                test : [Modernizr.json],
                polyfill : [self.polyfills_url.json_polyfill_url]
            }
        ]),
        self.prettifyCode(self.prettify, self.libraries_url.google_code_prettyfier.css, self.libraries_url.google_code_prettyfier.js),
        self.consoleAvoidError()
    ]);

Making abstraction of all the variables in there that are unknown, I would like to see if what I call in that array is actually a function, as I'm already trying to check now in the callPolyfills method. But it fails because it returns undefined each time even though it's a function.

Upvotes: 0

Views: 147

Answers (3)

dfsq
dfsq

Reputation: 193261

Maybe instead of calling explicitly your polyfills inside of callPolyfills, you could change your approach? Like this:

this.callPolyfills([
    [self.polyfillize, [
        {
            test : [Modernizr.localstorage, Modernizr.sessionstorage],
            polyfill : [self.polyfills_url.storage_polyfill_url]
        },
        {
            test : [Modernizr.json],
            polyfill : [self.polyfills_url.json_polyfill_url]
        }
    ]],
    [self.prettifyCodeself.prettify, self.libraries_url.google_code_prettyfier.css, self.libraries_url.google_code_prettyfier.js],
    self.consoleAvoidError
]);

So then you would check like this:

Application.Polyfills.prototype.callPolyfills = function(array) {
    for (var i = array.length - 1; i >= 0; i--) {
        if (typeof array[i] == "function") {
            // call with no arguments
            array[i].apply(this);
        }
        else if (typeof array[i].shift() == "function") {
            // call with arguments
            array[i].apply(this, array[i]);
        }
        else {
            // Invalid [ Function Required ]') ;
        }
    };
};

Upvotes: 1

Matt
Matt

Reputation: 75317

Adding this as a new answer as the two answers aren't realling related at all


You're function callPolyfills is spot on; it's what you're passing to callPolyfills that is the problem (you're passing an array of the results of the functions you wan't to call, not the functions themselves).

function foo() {
    return "hi";
};

var bar = foo; // stores a reference to the function foo in `bar`, but doesn't call foo.
var baz = foo(); // calls foo and stores the result of the function ("hi") in baz.

What you're doing (self, this's, and confusing arguments aside is):

callPolyfills([
    foo(),
    foo(),
    foo()
]);

... i.e. saying "hi" to callPolyfills numerous times, without actually passing it what you want.

What you should be passing is;

callPolyfills([
    foo,
    foo,
    foo
]);

This stops you being able to specify arguments to the functions though; in this circumstance the easiest way to do this would be to wrap those functions in anonymous functions.

this.callPolyfills([
  function () { 
    self.polyfillize([{
      test: [Modernizr.localstorage, Modernizr.sessionstorage],
      polyfill: [self.polyfills_url.storage_polyfill_url]
    }, {
      test: [Modernizr.json],
      polyfill: [self.polyfills_url.json_polyfill_url]
    }]);
  }, function () {
    self.prettifyCode(self.prettify, self.libraries_url.google_code_prettyfier.css, self.libraries_url.google_code_prettyfier.js);
  },
  self.consoleAvoidError
]);

... but then I'm not sure what you're gaining really.

Upvotes: 1

Matt
Matt

Reputation: 75317

typeof this.prettifyCode() is checking the type of the value returned by this.prettifyCode(); which is undefined.

typeof this.prettifyCode however, is checking what the type of the prettifyCode member on this is; which is a function

You shouldn't want these to be the same. The only way they could be the same is if this.prettifyCode returned a function, but the fact that both typeof this.prettifyCode() and typeof this.prettifyCode would be function is meaningless.

Upvotes: 4

Related Questions