Skip to content

Instantly share code, notes, and snippets.

@MikeRatcliffe
Last active August 29, 2015 14:05
Show Gist options
  • Save MikeRatcliffe/73b588618e0fc7ad770e to your computer and use it in GitHub Desktop.
Save MikeRatcliffe/73b588618e0fc7ad770e to your computer and use it in GitHub Desktop.
jQuery proxies

###Problem

jQuery and other libraries often use a poor man's bind:

// jQuery's proxy method.
let outer = {
  fn: {
    proxy: function(fn) {
      return function() {
        return fn.apply(this, arguments);
      };
    }
  }
}

// Function that we are to proxy
function unwrapped() {
  alert("Howdy y'all");
}

// Bind the function
let proxied = outer.fn.proxy(unwrapped);
let dblProxied = outer.fn.proxy(proxied);

dblProxied() // displays the popup
dblProxied.toString() // function() { return fn.apply(this, arguments);}

We can get the original function like this: globalDO.makeDebuggeeValue(dblProxied).environment.getValue("fn").environment.getValue("fn")

The problem with this approach is that when libraries are minified variable names change and between library versions their index can also change. On top of this there is the problem that there can be multiple functions stored as variables within scope so simply iterating over variables and checking for class === "Function" rarely works.

###Solution? What we really need is a way to get the original function that will be executed when dblProxied is called. Something like this: globalDO.makeDebuggeeValue(dblProxied).getOriginator()

This solution would have the advantage of working with a lot of libraries.

###Scratchpad

var code = "// jQuery's proxy method.\n" +
           "var outer = {\n" +
           "  fn: {\n" +
           "    proxy: function(fn) {\n" +
           "      return function() {\n" +
           "        return fn.apply(this, arguments);\n" +
           "      };\n" +
           "    }\n" +
           "  }\n" +
           "};\n" +
           "\n" +
           "// Function that we are to proxy\n" +
           "function unwrapped() {\n" +
           "  alert(\"Howdy y'all\");\n" +
           "}\n" +
           "\n" +
           "// Bind the function\n" +
           "var proxied = outer.fn.proxy(unwrapped);\n" +
           "var dblProxied = outer.fn.proxy(proxied);\n";

var sandbox = new Cu.Sandbox("http://www.example.com");
sandbox.alert = alert;
Cu.evalInSandbox(code, sandbox);
var dblProxied = sandbox.dblProxied;

Cu.import("resource://gre/modules/jsdebugger.jsm");
addDebuggerToGlobal(this);
var dbg = new Debugger;
var globalDO = dbg.addDebuggee(sandbox);

var fn = globalDO.makeDebuggeeValue(dblProxied).environment.getVariable("fn")
                                               .environment.getVariable("fn");
var rv = globalDO.evalInGlobalWithBindings("fn.toString()", {fn: fn});
alert(rv.return);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment