Add variable to a functions scope / closure. funct

2019-01-20 18:28发布

问题:

With the following code I am able to take values out of an object and add them to the global namespace. This is defined in the expose function

function expose(key, obj){
  window[key] = obj[key];
}

I can have been using it to take functions I use a lot out of libraries, such as map from underscore

// before
map               //undefined
_.map             // have to use like this

expose('map', _)

// after
map // function

I would like modify expose so that it does the same job but only in a closure. How to solve this is the question I am trying to articulate.

map //undefined

(function(){
expose('map', {})

// map can be used here 
}())
// map is still undefined here.

I know this problem in this case can be solved with a var map = _.map I am presenting a simplified version to describe the point here. I would like to solve it as described above to I can write a function that exposes more than one variable at a time.

For an complex example have a look at this repo.

https://github.com/CrowdHailer/cuminjs

The tests for the expose function are found here

https://github.com/CrowdHailer/cuminjs/blob/master/spec/cumin_spec.js

Large example to express better what I want the end result to be

// file one - sets up some core utilities
var MyModule = (function(){
var foo = function(){
  // ..some functionality
};

return {
  foo: foo
  // plus several more functions
};
}());


// file two - sets up some other functionality.
// Makes use of lots of the functions set up in file 1
(function(parent){
  // var foo = MyModule.foo -trying to avoid as want to use several maybe names change

  expose([foo, etc], MyModule);

  var bar = function(){
    // .. other code would like to make use of foo
  };

  parent.extend({
    bar: bar
  });
}(MyModule))

回答1:

I know this problem in this case can be solved with a var map = _.map.

That's exactly the (only) way to go.

I want write a function that exposes more than one variable at a time.

You should not. Have a look at my answer on the dual question Is it possible to import variables in JavaScript (node.js)?

However, exporting variables (into a scope that you don't own) is even more complicated than importing (into your own scope). Basically, it is impossible because scopes (apart from global) are not objects that can be passed around, and be dynamically modified.

How to solve this

You need to modify the code of the function whose scope you want to change/extend. I can think of two ways:

  • use eval. export() will return a statement that will declare and initialise the variables:

    function export() {
        var lines = [];
        for (var p in moduletoexpose)
            lines.push("var "+p+" = require('moduletoexpose')."+p+";");
        return lines.join("\n");
    }
    
    (function() {
        eval(export(…));
        // use `map` here
    }())
    
  • Similarly, you could write a function that takes the closure, decompiles it, adds the variable declarations to the code, and uses the Function constructor to create a function from it. It might be used like

    exportTo(…, function() {
        // use `map` here
        // don't use any free variables but global ones
    })();
    

    Doesn't sound as good as the eval solution, but you might want to check out this answer for inspiration on how to write such an exportTo.