3
\$\begingroup\$

I recently asked to use eval() for a module function. Now I ended up doing a very weird function which does what I want. I like the idea I made it but I'm not so sure if this is state of the art, or more, good to build an application on.

index.html:

<script src="module.js"></script>

<script>

require('alert');

bam(); // 9
</script>

module.js:

(function () {

    var require = window.require = function (fnName) {

        var dependencies = fnName.split(' '); // load multiple dependencies etc. (not nessecairy now)

        // REAL CASE WOULD BE, TO LOAD A .JS FILE INTO A STRING VAR
        var code = 'var bam = function() { var x = 1; var y = 2; var z = (x+y)*3; alert(z); }';

        return (function () {
            window.eval(code);
        })();
    }
})();

Is this a good solution for a require function like in PHP if I only load and eval() files made by me?

\$\endgroup\$
  • \$\begingroup\$ Hello, and welcome to Code Review! What's the point of this? There are lots of libraries that handle this issue, eg. RequireJS. Aren't they a better fit for this? \$\endgroup\$ – Quentin Pradet May 7 '13 at 15:26
  • \$\begingroup\$ I know require.js and i was looking for as much informations as i could, about the mechanism in require. I end up with writing it based on eval, because it provides better latency on 3G network. In addition, i dont like unnessecairy function wrappers like require.js does it, and that for every module, every require call. I am now able to use my script like php's include, so my only question is, is that state of the art or should i avoid that approach? \$\endgroup\$ – David Fariña May 7 '13 at 22:17
1
\$\begingroup\$

The functionality you are looking for is found in RequireJS and follows a spec called Asynchronous Module Definition (AMD). It is a mechanism that allows dependency management in JS, most notably in browsers.

As to why you think your own code is better, think again:

it provides better latency on 3G network

It's because you think the modules are deployed "as is", as file-per-module basis. You are thinking of the "additional HTTP request per module" am I right?

Sure, they work when broken down into modules. But you don't actually deploy them that way. You use the optimier which minifies your modules, and combines related modules into a file. This results in:

  • Smaller files due to minification
  • Lesser files due to dependency combination
  • Lesser files to download since your dependencies are grouped into related modules.

Also, the modules are cached once loaded. Once another modules requires a module that is already loaded by another module earlier in the execution of your code, it uses the cached one rather than requesting it again.

I don't like unnecessary function wrappers... for every module, every require call.

A require/define call is designed to:

  • define the dependencies
  • define the module
  • the filename is the module name

It isn't that hard to code:

//MyModule.js
define([
  'what',
  'you',
  'need'
],function(what,you,need){

  //what you do

  return whatYouWantExposedFromThisModule;

});

I am now able to use my script like php's include.

Handling the modules

include is like appending one file to the other, which is synonymous to concatenating files. In JS, we don't want globals. What if one module of yours had a bam variable, but has bam in another and you required the two. How would you handle that? Also, JS execution does not halt. How would you expect to do a require in your implementation? Synchronous XHR? preloading? That takes time! You'd be halting the execution for each require call of your implementation just to load a script.

<script>

  require('alert');
  //halt here to load alert? 
  //you'd block the UI, freeze the browser, lose the user
  bam(); // 9

</script>

In the module definition in require, the code of the module lives in it's own scope. Nothing is spilled to the global scope. What is exposed by the module is only exposed to the function that requires it, which is also a closed scope. Also, modules are cached. Once a module is already loaded, future requires to that module are taken from the cache, not from the network. They are asynchronous, and does not block the UI thread and the requiring code only executes when the dependencies are loaded - nothing is halted while we wait for the dependencies to load.

Loading the modules

Suppose you will do AJAX with your eval approach - it's not cross-domain friendly, and cross-browser friendly (IE).

The concept of loading a module is based on script-loaders which can do cross-domain. It's similar to how JSONP works, but instead of an arbitrary callback, they are fed through define.

I suggest you read more about how script loaders and dependency loaders work. This is a broad topic and has it's own spec.

\$\endgroup\$
  • \$\begingroup\$ Nice explanation. I consider using only one function or one object to be in the global scope, while all functionalities of my application lies inside that function. Maybe i could improve that piece of code by attaching a parent object to the require function, which will be used to work as a container for the loaded scripts, instead setting eval on the window object? \$\endgroup\$ – David Fariña May 8 '13 at 6:04
  • 1
    \$\begingroup\$ @DavidFariña yup, that's pretty much how requirejs does it. I suggest you use requirejs instead. \$\endgroup\$ – Joseph May 8 '13 at 6:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.