0

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))
Peter Saxton
  • 4,466
  • 5
  • 33
  • 51
  • just curious are you trying to do something similar to RequireJS (http://requirejs.org/)? – source.rar Jun 07 '14 at 13:37
  • I have not got much experience with requirejs at all. However a quick read over seams that it could be a similar thing. Does require allow you to require items into a closure? – Peter Saxton Jun 07 '14 at 13:41
  • Well its more of a module loader. But yes it allows you to define modules (as closures) and then "require" them as dependencies elsewhere in your code. – source.rar Jun 07 '14 at 13:45
  • Is creating a custom object, and assigning external library methods as methods of that object acceptable? (for example use my.map(thing) instead of map(thing) inside your closure) If not I don't see a way of doing it without using eval. – James Jun 07 '14 at 13:59

1 Answers1

0

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.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • This looks like it has what I am looking for. I had heard that eval was something to avoid. I will try and write a better example of what I am trying to achieve with breaking up closures to create large pieces of functionality with pollute global namespace only once. – Peter Saxton Jun 07 '14 at 14:07
  • Yes, `eval` is to avoid, but it's the only way to achieve what you want. You might of course want to avoid `export()`ing at all as I suggested (see the link about importing variables). – Bergi Jun 07 '14 at 14:10