5

So, I want to do something like this:

    var a = 'a';

    var dummy = function() {

        // Print out var 'a', from the scope above
        console.log('Dummy a: ' + a);

        // Print out 'b', from the 'compelled' scope
        console.log('Dummy b: ' + b);
    }

    (function() {

        var b = 'otherscope';

        // I know apply won't work, I also don't want to merge scopes
        dummy.apply(this);

        // I want something like this:
        dummy.compel(this, [], {b: 'injected!'});

    })();

But that won't work.

I don't actually want a function to be able to reach 2 scopes, I DO want to be able to set the 'b' variable used inside the dummy function from the outside.

Jelle De Loecker
  • 20,999
  • 27
  • 100
  • 142
  • Whats the application for doing this? There might be other techniques people could suggest –  Mar 21 '13 at 19:29
  • 1
    Maybe this is a (complex) workaround: http://stackoverflow.com/questions/10060857/is-it-possible-to-achieve-dynamic-scoping-in-javascript-without-resorting-to-eva – bfavaretto Mar 21 '13 at 19:30
  • I mainly want to see if it's possible. Well, I actually want to use it in a node.js MVC. I don't like that every action has set arguments, this would have been nicer. @bfavaretto has shown me a nice workaround, though I fear using eval() will be too much of a performance hit :) – Jelle De Loecker Mar 21 '13 at 19:38

4 Answers4

6

You can make b a parameter for the function, or a global variable.

var a = 'a';
var dummy = function(b) {
   ...
}

or

var a = 'a';
var b;
var dummy = function() {
   ...
}

The first allows you to choose when the dummy function has access to the variable, the second allows it to be accessed everywhere.

Ben McCormick
  • 25,260
  • 12
  • 52
  • 71
  • 3
    As for the *why*: JavaScript uses lexical scoping, so the scope is attached to the function object the moment the function object is created. It's not possible to inject something into the scope when the function is called. – bfavaretto Mar 21 '13 at 19:25
4

So, I found a little faster way to do such a thing:

var C = function(ctx, funcBody){
        var newBody = [];

        for(var k in ctx){
            var i =  "var "+k + " = ctx['"+k+"'];";
            newBody.push(i);
        }
        var res = "return function(t){ " +funcBody+ " }";
        newBody.push(res);
        var F = new Function("ctx", newBody.join('\n'));
        return F(ctx);
}
var newFunction = C({"foo":10, "bar":100}, "return foo+bar*t")
newFunction(50);
Vasiliy Stavenko
  • 1,174
  • 1
  • 12
  • 29
2

Use this:

Function.prototype.applyVars = function(scope, params, scope_variables) {
  if (scope_variables) {
    var variable, defVars = [];
    for (variable in scope_variables) {
      if (scope_variables.hasOwnProperty(variable)) {
        defVars.push(variable + '=scope_variables["' + variable + '"]');
      }
    }
    eval('var ' + defVars.join(',') + ';');
    return eval('(' + this + ').apply(scope, params);');
  }
  return this.apply(scope, params);
}

// Example

function foo(p1) {
  document.write('Variable [p1]: ', p1);
  document.write('<br />');
  document.write('Variable [x]: ', x);
  document.write('<br />');
  document.write('Variable [y]: ', y);
}

foo.applyVars(this, ['param X'], { x: "1'2\"3", y: false });

Or this:

function callWithVars(fn, scope, params, scope_variables) {
  if (scope_variables) {
    var variable, defVars = [];
    for (variable in scope_variables) {
      if (scope_variables.hasOwnProperty(variable)) {
        defVars.push(variable + '=scope_variables["' + variable + '"]');
      }
    }
    eval('var ' + defVars.join(',') + ';');
    return eval('(' + fn + ').apply(scope, params);');
  }
  return fn.apply(scope, params);
}

// Example

function foo(p1) {
  document.write('Variable [p1]: ', p1);
  document.write('<br />');
  document.write('Variable [x]: ', x);
  document.write('<br />');
  document.write('Variable [y]: ', y);
}

callWithVars(foo, this, ['param X'], { x: "1'2\"3", y: false });
Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
  • 2
    this will not work if `foo()` is inside a closure, and `foo()` invoke another function inside that closure. – Huan Oct 08 '16 at 18:47
0

the @vasiliy's solution extended to inject any function

function injectScope(ctx, func) {
  var newBody = [];

  for (var k in ctx) {
    var i = "  var " + k + " = ctx['" + k + "'];";
    newBody.push(i);
  }

  let res = (func + '').replace(/^(.*?)\)\s*{(.*)$/gms, `return(\n$1){\n${newBody.join('\n')}\n$2)`);
  // console.log(res)
  let F = new Function("ctx", res);
  return F(ctx);
}

function sum(t) {
  return foo + bar * t
}

var injectedSum = injectScope({ "foo": 10, "bar": 100 }, sum)
// console.log(injectedSum+'')  
console.log(injectedSum(50));
bittnkr
  • 99
  • 10