I have the following two options to run a script with a string:
Option A:
var script = document.createElement('script');
script.innerHTML = "function foo(){console.log('Hello!')}";
script.type = "text/javascript";
document.body.appendChild(script);
foo();
Option B:
eval("function foo(){console.log('Hello!')}");
foo();
They both print Hello in the console.
If I want to let users dynamically run scripts in my app, which one is a better(safer, faster) option?
If I want to let users dynamically run scripts in my app ...
There's nothing safe with this requirement, and none of the options is CSP friendly.
The "best" approach you could use though is Function, 'cause at least that doesn't reaches/target the local scope, like eval would do, keeping the execution within its function body scope.
The question now would be how to make the code available to the user without interfering with the rest of the code, or the previous call, or the global scope.
For this specific case I suggest a CommonJS like approach, so that your users must define what they are exporting in their code.
Example
const CommonJS = code => {
const exports = {};
const module = {exports};
const runtime = Function(
'module', 'exports',
`"use strict";${code};return module`
);
return runtime(module, exports);
};
const {exports} = CommonJS(`
module.exports = function foo(){console.log('Hello!')}
`);
exports();
Using this approach your users can define a single, or multiple, exports, like in Node.js, becoming familiar with the CommonJS module system, and avoiding any sort of global scope pollution, thanks to the "use strict" directive and the Function closure itself.
Alternatively, you can const foo = Function('return(' + code + ')')() but that requires code to be a single expression, so I would personally stick with the previous approach.
Related
I'm studying module pattern and have a question.
I want to use 'settings' anywhere.
script1.js
var MODULE = (function() {
var t = {};
this.settings = { // this == Window
users: ['john', 'emma']
}
return t;
})()
script2.js
MODULE.dom = function() {
var t = this; // this == MODULE
document.querySelector('p').textContent = t.settings.users[0]; // error
function _say() {
return t.settings.users[1] // error
}
return {
say: _say
}
}
MODULE.DOM = MODULE.dom.call(MODULE)
When use this.settings = {...}, 'this' means Window so code doesn't work.
When use t.settings = {...}, 'this' means MODULE so code works but when write MODULE in dev console, settings is exposed in MODULE variable. Is it ok?
I'd greatly appreciate any help, thank you!
When use t.settings = {...}, 'this' means MODULE so code works
That's the right way to do it.
but when write MODULE in dev console, settings is exposed in MODULE variable. Is it ok?
It's mostly OK.
If you're worried about the client being able to type in the variable name and see the code that gets run - there's no way to avoid that. They can just look at the devtools to see what the network requests are, and what is downloaded - or look at the Sources panel.
If you're worried about running into naming collisions with larger scripts, then - sometimes, libraries deliberately assign themselves to the window to allow other parts of the code to access them. Perhaps you'd like your MODULE to be like this. If not, then you should utilize JavaScript modules instead, which allow for scripts to be imported inside other scripts without polluting the global namespace at all or having any possibility of naming collisions. For example, you could do
// script1.js
export const MODULE = {
settings: {
users: ['john', 'emma'];
}
};
// script2.js
import { MODULE } from './script1.js';
// proceed to use MODULE
And you can do import { MODULE } from './script1.js'; from any script file, not just script2.js.
Personally, I consider the IIFE module pattern in JavaScript to be mostly obsolete nowadays. For any reasonable-sized script, better to write code in separate files and then import and export as needed. (A 1000 line file is somewhat hard to debug and refactor. Ten 100 line files are easier to debug and refactor.)
So I have an existing application which uses IIFEs extensively in the browser. I'm trying to introduce some unit testing into the code and keep with the pattern of IIFE for new updates to the code base. Except, I'm having trouble even writing a test which gives me a handle to the code. For example I see this type of logic all over the code base:
var Router = (function (router) {
router.routeUser = function(user) {
console.log("I'm in! --> " + user)
};
return router;
})(Router || {});
Then the JS file is included in a script tag in the markup:
<script src="js/RouteUser.js"></script>
and called like this in the production code:
Router.routeUser(myUser)
So my question is, how do I write a test which tests the method routeUser? I've tried this in my Mocha Test:
var router = require('../../main/resources/public/js/RouteUser');
suite('Route User Tests', function () {
test('Route The User', function () {
if (!router)
throw new Error("failed!");
else{
router.routeUser("Me")
}
});
});
But I get an exception:
TypeError: router.routeUser is not a function
at Context.<anonymous> (src\test\js\RouteUser.test.js:8:20)
Then I tried returning the method, which gives the same error:
var Router = (function (router) {
return {
routeUser: function (user) {
console.log("I'm in! --> " + user)
}
}
}
)(Router || {});
Can anyone point me the right direction here?
It sounds that...
you have a codebase of scripts that are only used in the browser context (usage of IIFE suggests this)
you'd like to introduce browserless unit tests (Jest, Mocha?) using node.js (good idea!)
but you probably don't want to migrate the whole codebase to a different coding style at this moment in time (can be a lot of work depending on the size of your codebase)
Given these assumptions, the problem is that you want your code to...
act as a script when used on production (set global window.Router etc)
act as a module when used in unit tests so that you can require() it in unit tests
UMD
UMD, or universal module definition, used to be a common way to write code so that it can work in multiple environments. Interesting approach, but very cumbersome, and I like to think UMD is a thing of the past nowadays...
I'll leave it here for completeness.
Just take UMD's idea
If the only thing you want for now to make a specific script act as a module too, so that it's importable in tests, you could do a small tweak:
var Router = (function (router) {
router.routeUser = function(user) {
console.log("I'm in! --> " + user)
};
if (typeof exports === "object") {
module.exports = router;
// now the Mocha tests can import it!
}
return router;
})(Router || {});
Long term solution
In the long run, you can get lots of benefits by rewriting all your code to use ONLY modules and use a tool like webpack to package it for you. The above idea is a small step in your direction that gives you one specific benefit (testability). But it is not a long term solution and you'll have some trouble handling dependencies (what if your Router expects some globals to be in place?)
If you intend to run your Mocha tests in the browser, you do not have to alter your existing code.
Let's walk through the IIFE pattern, because based on your code, I think you may misunderstand how it works. The basic shape is this:
var thing = (function() {
return 1;
})();
console.log(thing) // '1'
It's a var declaration setting thing equal to the value on the right side of the equals sign. On the right, the first set of parens wraps a function. Then, a second set of parens sits right next to it, at the end. The second set invokes the function expression contained in the first set of parens. That means the return value of the function will be the right-side value in the var statement. So thing equals 1.
In your case, that means that the outer Router variable is set equal to the router variable returned by your function. That means you can access it as Router in your tests, after including the script in the DOM:
suite('Route User Tests', function () {
test('Route The User', function () {
if (!Router) // <- Notice the capital 'R'
throw new Error("failed!");
else {
Router.routeUser("Me") // <- capital 'R'
}
});
});
If you intend to run your tests with node, see Kos's answer.
I have my code:
var User = function() {
...
}
and the test code using IIFE:
(function() { // test new user
var user = new User();
if (typeof user === 'undefined') {
console.log("Error: user undefined");
}
...
}());
both in the same js file. Works great! But, as the program grows, this is becoming too refractory for me to manage, as I have a piece of test code for every piece of business logic.
I've been taught to keep all my js in the same file, (minified is good) in production, but is there a best-practical way to keep my test code in a different file during development?
I was thinking I could use a shell script to append the test code to the production code when I want to run the tests, but I'd prefer a cross-platform solution.
I don't want or need a framework solution, I want to keep it light -- does node have anything built-in for this sort of thing?
Node has two expressions for this case. First:
module.exports = name_of_module;
Which is to export module for example function, object or something similar. And the second:
var module_name = require('Path/to/module');
to import it from other file. If you want to export IIFE you must assign it to global variable and module.export name of variable.
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.
So I've been playing with JS and browserify to allow to split my JS into smaller file chunks. It works great, however, I'm still lost on how to properly use the require function.
For me, it acts as a the Service Locator, because it looks for the proper "file" to load, and return an object. (For example in PHP, require somewhat load the file in the memory but doesn't construct).
Example:
var Foo = function() {
console.log("I'm the Foo object");
};
module.exports = Foo;
Then, to use it I'll do:
var Foo = require('foo');
and
var foo = new Foo();
Note, that the exported function is NOT constructed.
I could have done:
var foo = require('foo')();
None of those methods seems right to me (I may are wrong).
1) Is it common to do it like this? Or should exported the executed function?
Anyway, this introduction is to understand how I should play with the require function.
For example if I've a Foo object, which is depends of Bar, I've two way to do:
Service Location:
var Foo = function() {
var Bar = require('bar')();
Bar.doSomethingAwesome();
};
module.exports = Foo;
or I can do:
Dependency Injection
var Foo = function(bar) {
bar.doSomethingAwesome();
};
module.exports = Foo;
// And at a latter time
var foo = require('foo')(require('bar')); /// eurk
I obviously know that that's two different things and serve different purposes.
2) But I'm wondering what is the common/right way to do in JS, is there any commonly admitted rules?
Browserify allows you to program with modules, there's not much more to it. It's not really a DI container or service locator per se, although you can probably make it work like one.
So doing this is perfectly fine:
var Foo = require('foo');
var foo = new Foo();
In that case, it makes sense to simply place all require calls at the top of your file, similar like you would do with using in C# or import in Java. I personally wouldn't scatter require calls since don't help much with readability.
You can also export an instance which doesn't have to be newed up anymore, as long as that is appropriate for what you want to do (in that case module.exports = Foo() would lead to a singleton).
Also see this related question:
Dependency Injection with RequireJS
The rewire library provides module injection for Node.js.
A couple of solutions have been discussed and presented in this Github issue to support browserify.
Usage example from the project README:
var myModule = rewire("../lib/myModule.js");
myModule.__set__("fs", fsMock); // set private variable
Use Browserify so you can require npm packages from your browser just like node. Then you can use Weather.js or require it, then inject it in any way you like.