You don't know JS - module loader - javascript

Im reading "You don't know JS" (1st edition) on github and i'm playing arround with the module loader example from chapter 5. But there are two things, that i don't understand. I found another question here for the same module loader (javascript module pattern from You don't know JS), but it does not answers my questions.
First the original code:
var MyModules = (function Manager() {
var modules = {};
function define(name, deps, impl) {
for (var i=0; i<deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply( impl, deps );
}
function get(name) {
return modules[name];
}
return {
define: define,
get: get
};
})();
MyModules.define( "bar", [], function(){
function hello(who) {
return "Let me introduce: " + who;
}
return {
hello: hello
};
} ); // module["bar"] = hello(who) & deps = []
MyModules.define( "foo", ["bar"], function(bar){
var hungry = "hippo";
function awesome() {
console.log( bar.hello( hungry ).toUpperCase() );
}
return {
awesome: awesome
};
} ); // module["foo"] = awesome() & deps = [hello(who)]
var bar = MyModules.get( "bar" );
var foo = MyModules.get( "foo" );
console.log(
bar.hello( "hippo" )
); // Let me introduce: hippo
foo.awesome(); // LET ME INTRODUCE: HIPPO
Now the questions:
Why is impl applied to itself (plus the deps object)? I used w3schools/apply() for looking at how apply() works. There is in the 1st example just an object applied to a function, and not the function itself plus an object.
modules[name] = impl.apply( impl, deps );
Why should i give bar as a function argument in the definition of foo, since it is in the global scope (also in strict mode)?
MyModules.define( "foo", ["bar"], function(bar){
If i delete impl an bar it works like the original code.
"use strict";
...
modules[name] = impl.apply( deps );
...
MyModules.define( "foo", ["bar"], function(){
...
Are these two things unnecessary or is it a coincidence, that the code works?

Why is impl applied to itself (plus the deps object)? I used w3schools/apply() for looking at how apply() works. There is in the 1st example just an object applied to a function, and not the function itself plus an object.
Check out Function#apply() on MDN. Usually MDN is has more information, so it's worth using it for reference.
When you call apply() the first argument is the this value for the executed function. In a lot of cases, it doesn't matter what that value is - you can also pass null if you don't care about it.
const thisValue = {foo: 3};
function usingThis() {
return this.foo + 2;
}
console.log(usingThis.apply(thisValue)); // 3 + 2 = 5
function notUsingThis() {
return 8;
}
console.log(notUsingThis.apply(thisValue)); // 8
console.log(notUsingThis.apply(null)); // 8
However, this module loader passes the module being loaded. It's also a sound decision, since if this is used inside, it's at least something sensible - not the global object, and not null which might cause an error. If this isn't used, then it doesn't matter what the value is.
Also probably worth noting that since the value of this is the function itself, you use this.foo = "hello" inside the module, it gets attached it to the function object. Since a module would only be ran once, that is fine - you can read and write properties without much worry that they'd be trampled over.
Why should i give bar as a function argument in the definition of foo, since it is in the global scope (also in strict mode)?
The reason is that the module loaded and the modules it loads might not be in the same scope. Indeed, if the modules could safely reference each other, then you wouldn't need to "load" them, at all as they have all dependencies ready.
However, over time that leads to very hard to track down dependencies and brittle codebase. Not knowing what each module uses is a bad design in the long term.
If you look around at other module systems, they all make declaring dependencies explicit: CommonJS, Asynchronous module definition (AMD), as well as the ES6 modules all do the same thing. The module loader shown does a very similar thing.
The example is simplified for illustration purposes. Yes, in that case and that case only, you can skip the explicit declaration of dependencies. However, in a real code base, modules are likely to be defined in different places without having access to the same scope to keep things from getting messy. That is when the module loader's current design makes perfect sense.

Related

Node.js and Modules require. Objects are not assigned by reference?

I'm writing this question after 2 days of total struggling during which I couldn't find a solution but also couldn't find an explanation of why this code is not working.
I will report a super simplified simulation of my code.
I have 5 Node.js files:
1) server.js -> Is the main file used to start the server
2) globalVars.js -> Is where all the server "global" objects are stored.
3) globalFunctions.js -> Is where all "general" functions are stored to be used by all other modules
4) helloManager.js -> Example file which contains some standard function used by the server
5) aspect.js -> Same as helloManager.js
//server.js
//Loading the globalVars file. All objects are passed by reference so I use this to store the global variables
var globalVars = require("./globalVars.js");
//Assigning to the respective global object all the functions exported from other modules
globalVars.Modules.globalFunctions = require("./globalFunctions.js");
globalVars.Modules.aspect = require("./aspect.js");
globalVars.Modules.helloManager = require("./helloManager.js");
//After this point, the objects in globalVars.js will be populated with the respective functions exported from the files
//A timeout just to be sure it is not a problem of timing? (Well, it is not...)
setTimeout(function(){
console.log(globalVars.Modules.helloManager.helloOutput());
}, 2000);
/*
Console will throw the following error:
../globalFunctions.js:6
return "MR. " + aspect.getAspect();
^
TypeError: aspect.getAspect is not a function
*/
//globalVars.js
//Objects that will be populated with the functions inside other modules
module.exports.Modules = {
aspect: {},
helloManager: {},
globalFunctions: {}
};
//helloManager.js
var globalVars = require("./globalVars.js");
var { globalFunctions } = globalVars.Modules;
module.exports.helloOutput = function(){
return "hello " + globalFunctions.getHumanData();
};
//aspect.js
module.exports.getAspect = function(){
return "human";
};
//globalFunctions.js
var globalVars = require("./globalVars.js");
var { aspect } = globalVars.Modules;
module.exports.getHumanData = function(){
return "MR. " + aspect.getAspect();
};
Please don't answer me to put everything in the same file, because my code is way more complicated to report so here I'm posting this very simple simulation.
I know that objects are assigned by reference and so if all modules get the variables from "globalVars" they works kinda like "global".
The problem is when in globalFunctions.js I load
var { aspect } = globalVars.Modules;
Since in server.js the module aspect.js is not loaded yet, it will be an empty object.
But I'm expecting that
var { aspect } = globalVars.Modules;
is getting the reference of globalVars and not a copy, so when server.js finishes loading all the modules, the variabile aspect inside globalVars.Modules will point to the correct object and so it would find the function I need!
Infact the console.log inside server.js is executed after all modules have been loaded for this exact reason.
Does anyone know what is the reason of this problem and how could I solve it?
Thank to everyone who will help!
What's happening
It's an issue of what it means to do const { aspect } = globalVars.Modules; (which is the same as const aspect = globalVars.Modules.aspect;). That is, it's a matter of assignment semantics.
Let's look at a simpler example, then we can see hot it applies to what you're doing. Assume you have:
let a = {/*original object*/};
When you do
b = a;
the value in a is copied into b. That value is an object reference, so they now both point to the same object, but there is no ongoing link between a and b. If you then do
a = {/*new object*/};
that has no effect whatsoever on b, because there's no ongoing link between a (the variable) and b (the variable). b still refers to the original object, not the new one.
The same is true of any assignable item, such as an object property or parameter. That's what's happening with globalVars.Modules.aspect. globalFunctions is grabbing the value (simple assignment semantics, though using destructuring), then server.js is replacing that value with a new one.
Here's how that's happening in your code:
// server.js
var globalVars = (function() {
// globalVars.js
return { // this is the `exports` object
Modules: {
aspect: {}, // *** That's the `a = {/*original object*/}`
}
};
})();
// back in server.js
globalVars.Modules.globalFunctions = (function() {
// globalFunctions.js
const { aspect } = globalVars.Modules; // **** That's the `b = a`
return { // this is the `exports` object
getHumanData: function(){
return "MR. " + aspect.getAspect();
}
};
})();
// back in server.js
globalVars.Modules.aspect = (function() { // *** that's the `a = {/*new object*/}
return { // this is the `exports` object
getAspect: function(){
return "human";
}
};
})();
// back in server.js
globalVars.Modules.globalFunctions.getHumanData(); // Fails because the object it's using
// is the old one, not the new one
How to fix it
globalFunctions.js relies on aspect.js, so have it rely on it directly:
// In `globalFunctions.js`
const aspect = require("./aspect.js");
module.exports.getHumanData = function(){
return "MR. " + aspect.getAspect();
};
Assuming there are no cycles, that'll work.
At a larger level: There may be no reason to have globalVars.Modules at all. A module is only loaded once (normally), so rather than globalVars.Modules, just have each module directly rely on the module it needs, rather than funnelling it all through a central object. Node.js' module cache is already the central object.
If you don't want globalFunctions.js to rely on aspect.js directly (why not?), then don't copy the aspect property from Modules, use it as of when you need it:
// In `globalFunctions.js`
const {Modules} = require("./globalVars.js");
module.exports.getHumanData = function(){
return "MR. " + Modules.aspect.getAspect();
};
That'll work assuming nothing reassigns Modules (which nothing seems to in the code you've shown). But again, it makes more sense to rely on aspect.js directly if you can.
It's kind of fun to note that this is one of the reasons that modern ESM modules don't use simple assignment semantics like CommonJS ones do. ESM wouldn't help your specific thing, because you're using your own globalVars.Modules object instead of using the module objects, but it solves a problem that people often had with CommonJS modules which, like your problem, was caused by expecting b (an imported value) to be affected when reassigning a (the exported value). The issue people would have with CommonJS happened mostly when there were cycles between two modules (circular dependencies, directly or indirectly). ESM solves this by making the imported binding (b in my example) a live binding to the exported binding (a in my example). This is the only place JavaScript has what you could argue is a form of pass-by-reference (references to variables).
As you've noticed, var { aspect } = globalVars.Modules; copies the current value of globalVars.Modules.aspect to the local variable aspect. It's just alternative syntax for var aspect = globalVars.Modules.aspect.
If you later change the value of globalVars.Modules.aspect to a new object (as opposed to mutating the object that is already there) then the local variable doesn't update.
If you want the most recent value then you need to continue accessing globalVars.Modules.aspect whenever you need it.

How to finely manage an isolated scope in nodejs?

I am trying to have a scope in javascript where I can run code and which has no visibility to my local scope.
main.js
const isolated = require('./isolated.js');
function f() {return "a";} // invisible by the isolated scope
isolated.eval('function f() {return "b";}'); // invisible by my local scope
let b = isolated.eval("f();"); // I could get the result however
console.log(b); // "b"
A solution with modules would be perfect. But is it even possible?
isolated.js
module.exports = {
eval: ???
}
My best option so far has been to generate a temp file, execute it with process.fork() and get results from log file. But I'm looking for a something lightweight and on which my main.js can have fine control.
Many thanks for your help!
I have a somewhat hacky method of solving this issue, but it might work for your purposes. Instead of having a single eval function in isolated, you could have one that declares a variable/function, and the other that calls your previously declared variable/function. You can keep track of the declarations in a local dictionary that you append to your eval in the call function. Something like this:
isolated.js
var isolatedFunctions = {};
module.exports.declare = function(strName, strContents) {
isolatedFunctions[strName] = eval(`(${strContents})`);
}
module.exports.call = function (strCall) {
return eval(`(isolatedFunctions.${strCall})`);
}
You would then use it like this:
main.js
const isolated = require("./isolated.js");
function f() { return "a" }
isolated.declare ("f", "function() { return 'b' }");
var b = isolated.call("f()");
var a = f();
console.log(a); // Outputs "a"
console.log(b); // Outputs "b"
There are a few obvious limitations to this, the main being that you have to be very specific with the way that you call a function, because in order to accommodate the possibility of parameters, it just prepends "isolatedFunctions." in the eval call.
There is very likely a better way to accomplish what you're looking for, but I figured I would share this possibility in case it suits your needs.

js - avoiding namespace conflict

Thus far I've worked only with relatively small projects (and mostly alone), but this time I have to collaborate with other programmers... basically because of that I must plan the structure of the website very carefully for the avoidance of spending hours debugging the code.
At this point I suppose doing that in the following manner. I divide my code in modules and store each module in a separate file inside an object (or a function) with a made-up name (lzheA, lzheB, lzheC etc.) to avoid conflicts whether an object with the same name was used in an another piece of code. When the document is loaded, I declare a variable (an object) that I use as a main namespace of the application. Properties of the object are the modules I defined before.
// file BI.lib.js
var lzheA = {
foo: function() {
},
bar: function() {
},
}
// file BI.init.js
function lzheK() {
BI.loadPage();
}
// file BI.loadPage.js
function lzheC() {
var result = document.getElementById('result');
result.innerHTML = "that worked";
}
// and so on
var lzheA,lzheB,lzheD,lzheE,lzheF,lzheG,lzheH,lzheI,lzheJ;
// doing the following when the document is loaded
var BI = {
lib: lzheA,
menu: lzheB,
loadPage: lzheC,
customScripts: lzheD,
_index: lzheE,
_briefs: lzheF,
_shop: lzheG,
_cases: lzheH,
_blog: lzheI,
_contacts: lzheJ,
init: lzheK,
}
BI.init();
https://jsfiddle.net/vwc2og57/2/
The question... is this way of structuring worth living or did I miss something because of lack of experience? Would the made-up names of the modules confuse you regardless of the fact that each one used only twice - while declaring the variable and assigning it to a property?
I consider the namespaces a good option when you want to modularize applications in Javascript. But I declare them in a different way
var myModule = myModule || {}; // This will allow to use the module in other places, declaring more than one specificComponent in other js file for example
myModule.specificComponent = (function(){
// Private things
var myVar = {};
var init = function() {
// Code
};
return {
init: init // Public Stuff
};
})();
If you want to call the init method, you would call it like this
myModule.specificComponent.init();
With this approach, i guarantee that the module will not be overwritten by another declaration in another place, and also I can declare internal components into my namespaces.
Also, the trick of just exposing what you want inside the return block, will make your component safer and you will be encapsulating your code in a pretty way.
Hope it helps

How to write testable requirejs modules

I am new to unit testing so I might be missing something, but how am I supposed to structure requirejs modules in order to make them fully testable? Consider the elegant revealing module pattern.
define([], function () {
"use strict";
var func1 = function(){
var data = func2();
};
var func2 = function(){
return db.call();
};
return {
func1 : func1
}
});
As far as I am aware of this is the most common pattern for building requirejs modules. Please correct me if I am wrong! So in this simplistic scenario I can easily test return values and behavior of func1 since it is global. However, in order to test func2 I also would have to return it's reference. Right?
return {
func1 : func1,
_test_func2 : func2
}
This makes the code slightly less pretty, but overall is still ok. However, if I wanted to mock func2 and replace its return value by using Jasmine spy I would not be able to since that method is inside a closure.
So my question is how to structure requirejs modules to be fully testable? Are there better patterns for this situation than revealing module pattern?
Are you sure you want to test the private function func2?
I think developers are missing the point of unit tests when they try writing tests for private functions.
Dependencies are what get us by the balls when developing software. And the more dependencies the tighter the squeeze . So if you have lots of tests dependant on the internal workings of a module it's going to be really painful when you want to change the internal implementation. So keep your tests dependant on the public interface, and keep the private stuff private.
My advice:
Design the public interface to your module.
Write a test against the public interface to specify some expected behaviour.
Implement the code needed to pass that test.
Refactor (if necessary)
Repeat from step 2, until all the functionality has been defined by tests, and all the tests pass.
During the implementation and refactoring stages the internals of the module will change. For example, func2 could be split up into different functions. And the danger is that if you have tests for func2 specifically, then you may have to rewrite tests when you refactor.
One of the main benefits of unit tests is that they ensure we do not break existing functionality when we change a module's internal workings. You start losing that benefit if refactoring means you need to update the tests.
If the code in func2 becomes so complex that you want to test it explicitly, then extract it into a separate module, where you define the behaviour with unit tests against the public interface. Aim for small, well tested modules that have an easy to understand public interface.
If you are looking for help with regards to unit testing I thoroughly recommend Kent Beck's book "TDD by example". Having poorly written unit tests will become a hindrance rather than a benefit, and in my opinion TDD is the only way to go.
If functions in a module call the module's other functions directly (i.e. by using references that are local to the module), there is no way to intercept these calls externally. However, if you change your module so that functions inside it call the module's functions in the same way code outside it does, then you can intercept these calls.
Here's an example that would allow what you want:
define([], function () {
"use strict";
var foo = function(){
return exports.bar();
};
var bar = function(){
return "original";
};
var exports = {
foo: foo,
bar: bar
};
return exports;
});
The key is that foo goes through exports to access bar rather than call it directly.
I've put up a runnable example here. The spec/main.spec.js file contains:
expect(moduleA.foo()).toEqual("original");
spyOn(moduleA, "bar").andReturn("patched");
expect(moduleA.foo()).toEqual("patched");
You'll notice that bar is the function patched but foo is affected by the patching.
Also, to avoid having the exports polluted by test code on a permanent basis, I've sometimes done an environmental check to determine whether the module is run in a test environment and would export functions necessary for testing only in testing mode. Here's an example of actual code I've written:
var options = module.config();
var test = options && options.test;
[...]
// For testing only
if (test) {
exports.__test = {
$modal: $modal,
reset: _reset,
is_terminating: _is_terminating
};
}
If the requirejs configuration configures my module (using config) so that it has a test option set to a true value then the exports will additionally contain a __test symbol that contains a few additional items I want to export when I'm testing the module. Otherwise, these symbols are not available.
Edit: if what bothers you about the first method above is having to prefix all calls to internal functions with exports, you could do something like this:
define(["module"], function (module) {
"use strict";
var debug = module.config().debug;
var exports = {};
/**
* #function
* #param {String} name Name of the function to export
* #param {Function} f Function to export.
* #returns {Function} A wrapper for <code>f</code>, or <code>f</code>.
*/
var _dynamic = (debug ?
function (name, f) {
exports[name] = f;
return function () {
// This call allows for future changes to arguments passed..
return exports[name].apply(this, arguments);
};
} :
_dynamic = function (name, f) { return f; });
var foo = function () {
return bar(1, 2, 3);
};
var bar = _dynamic("bar", function (a, b, c) {
return "original: called with " + a + " " + b + " " + c;
});
exports.foo = foo;
return exports;
});
When the RequireJS configuration configures the module above so that debug is true, it exports the functions wrapped by _dynamic and provides local symbols that allow referring to them without going through exports. If debug is false, then the function is not exported and is not wrapped. I've updated the example to show this method. It's moduleB in the example.

Accessing object literal properties from inside callbacks (async methods)

I'm writing a chrome extension which needs to interact with a subtree of bookmarks. There are a lot of interactions with this subtree, so I'm abstracting this logic into an object literal, like so:
var contextStore = {
'root_id': undefined,
'setup': function() {...}, // populates root_id
'add': function(name) {...}, // uses root_id
'remove': function(name) {...}, // uses root_id
// ... etc ...
};
contextStore.setup(); // only once.
contextStore.add("foo");
contextStore.add("bar");
// ... etc
So far, so good.
The trouble I'm having is caused by the asynchronous Chrome APIs (and my lack of JS-fu). To wit:
var contextStore = {
'root_id': undefined,
'setup': function() {
chrome.bookmarks.getTree(function(tree) {
// do some work to find a given folder in bookmarks.
// now I want to save that folder's id for access in other methods.
// Fail: 'this' refers to chrome.bookmarks.getTree.
this.root_id = computed_thing; // doesn't work!
});
}
// ... etc ...
};
My question is:
How do I go about accessing members of the enclosing object literal from inside the various Chrome API method callbacks?
I looked at using the module pattern, but it doesn't seem to change things, and it's not like this code is going to be consumed by anything outside the extension.
You need to store a reference to the this which points to the contextStore object;
var contextStore = {
'root_id': undefined,
'setup': function() {
var that = this; // Store reference here.
chrome.bookmarks.getTree(function(tree) {
that.root_id = computed_thing; // does work!
});
}
// ... etc ...
};
This is equivalent to doing;
var contextStore = {
'root_id': undefined,
'setup': function() {
chrome.bookmarks.getTree(function(tree) {
contextStore.root_id = computed_thing; // does work!
});
}
// ... etc ...
};
However you gain the benefit of not reusing contextStore everywhere.
The this keyword can be bound to different things depending on how you call it. I'm not a javascript expert, but there is a good explanation at A List Apart.
The solution is to bind explicitly when calling the function using either my_function.apply(obj, [args]), my_function.call(obj, args) (call now) or pre-bind the function for calling later: my_function.bind(obj).
As a python programmer, being explicit probably makes you happy :-)
Matt's answer is eventually the better approach, as it is even more explicit, succinct and doesn't require the function to be called or prepared in a certain way. I just thought I would try to explain what was going on.

Categories