Define all external JavaScript in a pre-existing namespace - javascript

I am fully aware of the dangers of eval and such, so please don't waste your time commenting on how these hacks are hacks.
Say I'm using a third-party JavaScript library, but I'd like all of it's code to be defined within a particular namespace... that I also don't control. For example:
<script src="http://example.com/library.js>
<!-- library.js contains:
var x = 0;
function foo() {}
-->
I'd like to be able to define x and foo in the context of, say, namespace Bar, instead of the global context (window).
Assuming Bar is defined, but in another external js file that I don't control, I have tried some hacks, for example:
<script>
var externalCode = $.ajax("http://example.com/library.js");
eval.call(Bar, externalCode);
// and also tried:
(function(str){ eval(str); }).call(Bar, externalCode);
</script>
But I am not able to access Bar.x nor invoke Bar.foo().
Basically, I want to achieve the results I would get if I pasted the contents of library.js inside my Bar namespace, a la:
var Bar = {
x: 0,
foo: function() {}
}
However, I am trying to do this when I also lack control over the Bar namespace. Hence I can't "copy and paste." Another related example is how we can "punch in" new methods into pre-existing class definitions in Ruby.
Lastly, my specific use case is that I'm trying to "inject" everything from p5.js into the Opal namespace.

A simple solution that works in browsers today is:
<script src="http://example.com/library.js>
<!-- library.js contains:
var x = 0;
function foo() {}
-->
<script>
$.ajax('/library.js', function(jsString) {
var wrapped = `function() {
${jsString};
Bar.x = x;
Bar.foo = foo;
}();`
eval(wrapped);
});
</script>
However, a solution that doesn't require listing modules is EcmaScript 6 modules. If you transpile it with https://babeljs.io/, it can work with today's browsers.
//------ main.js ------
import * as Bar from 'lib';
console.log(Bar.foo()); // no error
console.log(Bar.x); // 0
If you need it to work in all browsers today, and don't want to transpile your code, you'd need the first example, which could be generalized as
function importIntoNs(ns, scriptUrl, symbolNames) {
$.ajax(scriptUrl, function(jsString) {
var wrapped = `(function() {
${jsString};`;
symbolNames.forEach(symbolName => {
wrapped += 'this.' + symbolName + ' = symbolName;\n' ;
});
wrapped += '})';
eval(wrapped).apply(ns);
}
}
importIntoNs(Bar, 'http://example.com/library.js', ['x', 'foo']);
If anyone wants to downvote because of the eval, remember that the OP is already going to execute the script no matter what if they put it in a <script> tag.
Here's a working example without fetching the script.
function importScriptIntoNs(ns, jsString, symbolNames) {
var addSymbols = '';
symbolNames.forEach(symbolName => {
addSymbols += 'this.' + symbolName + ' = ' + symbolName + ';\n';
});
var wrapped = `(function() {;
${jsString}
${addSymbols}
})`;
eval(wrapped).apply(ns);
}
var Bar = {};
var jsString = `
var x = 'It works';
function foo() {alert(x)}
`;
importScriptIntoNs(Bar, jsString, ['x', 'foo']);
Bar.foo()

One way to handle libraries and other dependencies without polluting the global scope is to use some kind of Asynchronous Module Definition. A good one that I like to use and recommend is Require.js.
The whole idea behind it is that you're able to bundle your libraries and dependencies into modules and call on them when you need, without simply loading everything on the global scope.
Here is an example of how you might use it in your situation:
require(['http://example.com/library.js'], function(Bar){
// library.js is now bound within this scope under the alias Bar
});

Related

Javascript global variable undefined in function scope

I am new to JavaScript and trying to make a simple node server. Here is my code:
var activeGames = {}
exports.initialize = function(){
var gameID = "game12345"
activeGames.gameID = new Game(gameID, "player1", "player2")
}
I call the initialize function from another module, and I get an error stating that activeGames is undefined. activeGames is at the outermost scope of the file. I tried adding 'this' before activeGames.gameID but that did not fix it. Why is activeGames undefined? Thanks in advance.
EDIT: Here's how I'm calling this code.
In my base index file I have
const handler = require("./request-handler.js")
handler.initialize()
In request-handler.js, I have
var gameManager = require('./game-manager')
exports.initialize = function(){
gameManager.initialize()
}
JavaScript has lexical scope, not dynamic scope.
ref: https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping
Lexical scope means that whether a variable is accessible or not depends on where they appear in the source text, it doesn't depend on runtime information.
example:
function foo() {
var bar = 42;
baz();
}
function baz() {
console.log(bar); // error because bar is not in the scope of baz
}
the same problem happens in your code,
var activeGames
is not in scope.
try this variation:
exports.initialize = function(){
var activeGames = {}
var gameID = "game12345"
activeGames.gameID = new Game(gameID, "player1", "player2")
}
A good solution could be to use a class and export it:
--THIS CODE IS NOT TESTED--
class gamesManager {
var activeGames = {}
initialize() {
var gameID = "game12345"
activeGames.gameID = new Game(gameID, "player1", "player2")
}
}
exports.gamesManager = gamesManager
USE:
const activeGames = require('./game-manager');
const activeGamesInstance = new activeGames.gamesManager();
activeGamesInstance.initialize();
Need a code sample for this one. I ran this locally and it worked fine, although your code has a big issue which may be a part of your problem. It looks like you want to keep track of multiple games in activeGames. You need to use this syntax instead:
activeGames[gameID] = new Game(gameID, "player1", "player2")
Here's my working code:
index.js:
const handler = require("./request-handler");
handler.initialize('game-1234');
handler.initialize('game-5678');
request-handler.js:
var gameManager = require('./game-manager');
exports.initialize = function(gameID) {
gameManager.initialize(gameID);
}
game-manager.js:
var activeGames = {};
class Game {
constructor(id, player1, player2) {
this.id = id;
this.player1 = player1;
this.player2 = player2;
}
}
exports.initialize = function(gameID) {
activeGames[gameID] = new Game(gameID, "player1", "player2");
console.log(`game initialized! ${ Object.keys(activeGames).length } active games`);
}
Running node index results in this:
game initialized! 1 active games
game initialized! 2 active games
When you require a script file in Node.js, it is compiled as part of a function called with named parameters require, module, exports and other exposed variables as arguments1. Variables declared at file level within the required script become function level variables in the enclosing module wrapper and retained inside its closure.
Hence your "global variable" is no such thing: it's a variable defined inside a closure...
An important question then is does the module loader make variables declared in a parent module available to scripts required inside the parent. A quick test shows that the answer is general: no, modules do not have automatic access to variables declared in other modules - those variables are inside closures.
This indicates that to pass variable values to scripts that have been required, generally pass them as argument values to exported functions.
It is also possible to export any javascript value as a property of module.exports from within a required script, or add properties to an exports object after it has been returned from requiring a script. Hence it is technically feasible to pass information up and down between modules by adding properties to exports objects.
Redesigned code has multiple options to
define activeGames at the application level and pass it down as a parameter to modules needing access to it, or
export activeGames from game-manager.js by adding
exports.activeGames = activeGames
to the end of the file. This will not take care of exporting activeGames out of the parent module request-manager.js for use elsewhere, but it could be a start. Or
define activeGames as a global variable (in node) using
global.activeGames = {} // define a global object
Defining global variables is not encouraged as it can lead to collisions (and consequent program failure) between names used by applications, code libraries, future library updates and future versions of ECMAScript. Or,
Define an application namespace object for data global to the application. Require it wherever access to application data is needed:
create appdata.js as an empty file.
Optionally include a comment:
// this files exports module.exports without modification
require appdata.js wherever needed.
var appData = require('./appdata.js')
appData.gameData = {}; // for example
This relies on node.js maintaining a cache of previously required modules and does not recompile modules simply because they have been required a second time. Instead it returns the exports object of the previous require.
Happy festive season.
References
1The Node.js Way - How require() Actually Works

node "require" hoisted to top outside of script -- loses access to variables from outer function

I'm requiring different files at the top of my main script in node. All my require statements are hoisted to the top. This creates a problem because when the methods within those imported scripts are invoked they do not have access to the function within which they are invoked (Because they are inevitably defined outside those functions due to the hoisting issue). Therefore, I must always pass variables in an options object. Has anyone experiences a similar issue? Is there some sort of standard workaround that people use? Thanks!
function outer(){
//let's pretend we're in a node environment
//this required script will get hoisted to the very top and therefore lose access to the "something" variable!
var common = require('../globals/common.js');
var something = "something";
common.printsomething();//will return "something is not defined"
};
outer();
Hm.
I would assume that it'd ultimately be better to pass 'something' to the printsomething method, like so.
common.printfoo('bar'); //returns 'bar'
Typically, what you're doing there isn't how modules in node works. Yes, breaking up a large program into separate files is an excellent way to organize a project, but I'm afraid that I have to say you're doing it wrong here. In the context of 'outer', you could do:
/*script.js*/
var common = require('../globals/common.js');
function outer(str){
common.printsomething(str);//will return "something"
};
var something = 'something';
outer(something);
/*common.js*/
function printthing(str){
console.log(str);
}
module.exports = {
printsomething: function(str){
printthing(str)
}
}
module.js:
module.exports.print = function (data) {
console.log(data);
}
module.exports.add = function (a, b, callback) {
callback(a + b);
}
main.js
var mymodule = require('module');
module.print('Some data'); //Will print "Some data" in the console
module.add(25, 12, function (result) {
console.log(result); //Will print 37
});
As you can see, in main.js, I do not need to know the content of module.js to wrk. that is the goal of modules: put the hard logic somewhere else, to build better code. Modules like async or fs are huge and complex, but I just have to import them to work with it, and don't need to know how it does it.
While building your own module, think of it as a new library of tools, so that you can reuse it in another project without the need to set specific variables to use them. Imagine the chaos it would be if two module were able to get the content of your var something for unrelated goal!
Modules are self contained, to be reusable. A "de hoisting" of thoses would reduce their efficacity.
EDIT:
If you have a lot of environment variable, you can try a pattern where you set them once inside the module, but you have to make sure to provide a way to interact with them.
module:
var data = {};
function set(key, value) {
data[key] = value;
}
function get(key) {
return data[key];
}
function print(key) {
console.log(data[key]);
}
function add(keyA, keyB) {
return data[keyA] + data[keyB];
}
module.exports = {
set: set,
get: get,
print: print,
add: add
};
main.js
var mymod = require('mymod');
mymod.set('data', 'something');
mymod.set('a', 25);
mymod.set('b', 12);
mymod.print('data'); //Print "something"
var c = mymod.add('a', 'b');
console.log(c); //Print 32

What is the benefit of this JavaScript pattern based on an IIFE?

I master the Javascript IIFE essence, but while reading this page, I'm kinda confused of the usage.
It sounds that the following JavaScript pattern is pretty common:
(function(something) {
something.foo = 123;
})(something || something = {})
A usage example:
(function(something) {
something.foo = 123;
})(something || something = {})
console.log(something); // {foo:123}
(function(something) {
something.bar = 456;
})(something || something = {})
console.log(something); // {foo:123, bar:456}
As something is defined in the global scope, what is the benefit of this pattern in relation to the following trivial code:
var something;
something.foo = 123;
something.bar = 456;
Which use case would it make sense to use it?
You get a closure over something which ensured that even though some other code overrides it's in global scope you will still have the right reference to that something, it's better illustrated with jQuery and jQuery.noConflict();:
<script src="/path/to/jquery.js"></script>
<script>
(function($) {
// Here $ is jQuery
// Something asynchrone is happening:
setTimeout(function() {
// But $ is still jQuery
}, 1000);
})($);
</script>
<script>
// Someone calls jQuery.noConflict();
jQuery.noConflict();
// And now $ is not jQuery
</script>
Lets say you have a namespace for your application myApp. You have several scripts that add functionality because like a good developer you've broken your application up into different chunks. Also like a good web developer you're loading those scripts asynchronously, you don't know what order they are going to be added in. So you have each module conditionally create the namespace for the app if it doesn't exist (the (myApp || {}) part). You also want to hide the internal functionality of the module from stuff that doesn't need to know about it, so you pass your 'namespace' object into a function that creates a closure and attaches the module's API to the namespace object myApp.
// async loaded script for the foo 'module'
(function(myApp) {
var myPrivate = 3; // hidden in the closure
myApp.getFoo = function() { return myPrivate; };
}(myApp || {});
This pattern is not without its flaws (what if myApp.foo depends on myApp.bar?) which is why its largely been replaced by browserify/require.js/webpack/etc.

How to use a JavaScript method/object that has been defined in another file?

I have a some JavaScript with a complex structure. Because I'm new comer to JavaScript (only understanding some basic concepts) I don't know how to use it properly.
I have two files : Circle.js and Line.js. In Circle.js, I want to use a class object defined in Line.js:
In file Circle.js :
Helper.using('py.Figures', function (ns) {
ns.Circle = function (params) {
// some additional methods and code here
}
}
And in Line.js is :
Helper.using('py.Figures', function (ns) {
ns.Line2Point = function (params) {
// some addition methods and code here
};
}
In Figures.Circle, in ns.Circle I want to use Line2Point but I don't know how.
I think it should be :
line = new ns.Line2Point(params);
But It seem doesn't work.
According to Helper Class, ns will point to helper.using, in this case py.Figures. Does it mean, ns is the same object/reference in both the files?
I don't think this is doable in Javascript directly across files. If they are part of the same namespace you could share some 'global' objects to achieve this have the line2points and circles attach themselves to that global object:
Ex:
var myShapesNameSpace = {};
Circle.js:
(function(){
var circle = {};
circle.method1 = function(){...}
circle.method2 = function(){...}
myShapesNameSpace.Circles = circle;
})(window.myShapesNameSpace = window.myShapesNameSpace || {}); //check if namespace object exists. Else create a new blank one.
Line.js:
(function(){
var line= {};
line.method1 = function(){...}
line.method2 = function(){...}
myShapesNameSpace.Lines= line;
})(window.myShapesNameSpace = window.myShapesNameSpace || {});
Now you can check for the existence of myShapesNameSpace.Circles or .Lines and call the corresponding methods accordingly.
You can include files in javascript and reference objects across files unless they are exported in some global form either via window or your define global
Welcome to Javascript, the shit parts. Require.js was designed precisely for this because the creators of JS, well, I guess thought that everyone would write every program in one file.
RequireJS
It was designed for web use but can be used elsewhere too (locally, with Node, etc.)

Javascript Module Pattern Help

The problem I have is that there are a set of variable values / properties in one file and a library in another file. I have started refactoring the code but still need to keep variable values(dynamic) and library(static) differently.
I am using namespacing and overall want only one global namespace.
The problems I have at the moment:
1. How can I still keep one global namespace
2. What is the best way to read the values from one file and use it in the library present in another file.
e.g I came up with something like
//File ONE with values
var main.dynamicvalues = (function(){
var a = 10,
b = 20,
c = 30;
return {
a:a,
b:b,
c:c
}
}());
//File TWO with core Library
var main.library = (function(){
//Various Private functions that need to use a,b,c variables from above main.dynamicvalues namespace
return {
//Public functions again need to use a,b,c from above namespace.
}
}());
Is there a way I can have a pattern so that I keep only one global namespace and can refer to variables directly without having to use maincode.values.a, maincode.values.b, maincode.values.c or something like this in maincode.library.functions
Thanks
Sparsh Gupta
This approach is a little better, but it's not exactly what you're looking for.
var main = {};
main.dynamicvalues = (function() {
// same as before
})();
main.library = (function(dyn){
// use dyn.a, dyn.b etc
return {
// same in here
}
}(main.dynamicvalues));
Create a new file (maybe name it something like "common.js") and put the values there.
You can try RequireJS. This will let you do what you want with no global namespace at all (if you'd like to). In addition it will give you non-blocking script loading, easy way to handle dependencies and a build tool.
On the other hand, it can deprive you of the joy of investigating things for your own and better understanding of js architectural patterns.
Your code with RequireJs could have looked like this:
// File one with values, let's name it values.js
define([], function() {
var a = 10,
b = 20,
c = 30;
return {
a: a,
b: b,
c: c
}
})
// File two with library
define([
// load values.js as a dependency
'values'
// what is returned in values.js can be passed as an argument to the callback
], function( values ) {
values.a === 10 // true
})

Categories

Resources