Jonathan Gray
Jonathan Gray

Reputation: 2609

Really wierd JavaScript behaviour while setting local variables

I've been trying to refine an answer for a question here on SO but I've come across a wierd issue and I am hoping someone could help me understand the cause. What I am attempting to do is disable the use of AJAX operations from within a "protected" function by overriding them with locally-scoped variables. This needs to work with libraries such as jQuery, so I tried implementing logic to recreate the $ variable but for some reason I can't override the global scope with a local version like I can with the others, and meanwhile is producing some very unexpected (and interesting) behaviour.

This is the code that I'm using:

var testFunction = (function(){
    // Global to local/lexical:
    var XMLHttpRequest = undefined;
    var eval = undefined;
    var setTimeout = undefined;
    var setInterval = undefined;
    var Function = undefined;
    var window = undefined;
    // Can't set directly to var $?
    var $new = (function($old){ if($old) {
        var newjq = function(s, c) {
            // Reroute main function
            return $old(s, c);
            };
        var jQueryBlacklist = {
            // Initialize blacklist
            "ajax": true,
            "post": true,
            "get": true,
            "getJSON": true,
            "getScript": true
            };
        for(i in $old) // Reconstruct Object
         if($old.hasOwnProperty(i)
         && !jQueryBlacklist[i])
          newjq[i] = $old[i];
        return newjq;
        } }($));
    // Line below completely breaks script?:
    var $ = $new;
    if(!$new) alert("$new is undefined");
    // Real testFunction() below:
    return function() {
        // AJAX-forbidden code
        if(!$) alert("$ is undefined");
        else alert("jQuery is working");
        // alert($.ajax);
        // $.ajax should be undefined
        }
    }());
testFunction();
// alert($.ajax);
// should be defined

You can click here to see the fiddle.

I'm simply interested in any feedback about this, as to me it seems like a bug. I would love to know the reason(s) for this behaviour. Thanks in advance!

Upvotes: 1

Views: 106

Answers (1)

Ruan Mendes
Ruan Mendes

Reputation: 92274

Your var $ is being hoisted (the declaration is moved to the top of function), shadowing the global $ to be undefined (when it gets passed into the self executing function) until its actual assignment in var $ = new$.

The easiest solution is to pass jquery into your module.

var testFunction = (function ($) {
    // Global to local/lexical:
    var XMLHttpRequest = undefined;
    var eval = undefined;
    var setTimeout = undefined;
    var setInterval = undefined;
    var Function = undefined;
    var window = undefined;
    // Can't set directly to var $?
    var $new = (function ($old) {
        if ($old) {
            var newjq = function (s, c) {
                // Reroute main function
                return $old(s, c);
            };
            var jQueryBlacklist = {
                // Initialize blacklist
                "ajax": true,
                    "post": true,
                    "get": true,
                    "getJSON": true,
                    "getScript": true
            };
            for (i in $old) // Reconstruct Object
            if ($old.hasOwnProperty(i) && !jQueryBlacklist[i]) newjq[i] = $old[i];
            return newjq;
        }
    }($));
    // Line below only affects the object reference passed in to this module
    // Doesn't affect jQuery outside of this function:
    $ = $new;
    if (!$new) alert("$new is undefined");
    // Real testFunction() below:
    return function () {
        // AJAX-forbidden code
        if (!$) alert("$ is undefined");
        else alert("jQuery is working");
        // alert($.ajax);
        // $.ajax should be undefined
    }
}(jQuery));
testFunction();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Upvotes: 2

Related Questions