JavaScript inner lambda function without var, let or const - javascript

In the following React-Application, I am using a JS-Feature to declare a function within a function and call it on a certain event:
export default function App() {
OnClick = (input) => { //Why is no var/let needed
//do something...
}
return (<button onClick={this.OnClick}/>); //and it is called like this
}
My resulting questions are:
How is the "feature"/"notation" called? - it seems weird not to use var/let/const
In which ES-Version was it introduced?
Does it only work with Lambda-Functions?

How is the "feature" called? - it seems weard not to use var/let/const
I call it The Horror of Implciit Globals. OnClick gets created as a global variable, not a local one. I don't think it has an official name. A less opinionated one might be "implicit global variable creation." :-)
In wich ES-Version was it introduced?
Before ECMAScript version 1. I think it was in the very first version of JavaScript ever in 1995.
Does it only work with Lambda-Functions?
No, you can do this with any value. OnClick = 42 would also "work."
But don't. Again, it creates a global, not a local. Instead, declare your variables.
Use const for your example (or let if you prefer). I also recommend using JavaScript modules ("ESM" or "ECMAScript" modules), because code in modules is in strict mode and assigning to an undeclared identifier is the error it always should have been in strict mode. If you don't want to use JavaScript modules, add "use strict"; to the top of your files to put them in strict mode.
Here's a demonstration of the fact it creates a global:
function example(value) {
OnClick = () => {
console.log(value);
};
}
example(42);
OnClick(); // Logs 42
console.log(typeof window.OnClick); // "function"

Related

How can I use modules in the browser, but also refer to variables and functions from within DevTools?

I have my HTML setup like this:
<script type="module" src="main.js"></script>
and all the ES6 modules work fine. The only problem is I now can't refer to anything from within DevTools (like using the Console and typing in a variable to see it's value or using a function manually).
How do I import modules whilst being able to use the DevTools? Thanks!
One way to make a variable accessable within DevTools is to create it on the window object:
// Variable in your module
var importantNumber = 1;
window.importantNumber = importantNumber;
This method works fine if you just have a couple of variables, but if you need to have access to a lot more variables within DevTools, I would recommend you go to the sources-tab in DevTools, search for your module and adding a breakpoint. When the execution pauses, you have access to all the variables within that module on the DevTools console.
If you want to be able to refer to variables created within the module from the console's global scope, you'll have to deliberately expose each such variable that you want to be visible from the console. Either assign each variable to window (probably not a good idea - the whole point of modules is to make things more modular, without global pollution), or perhaps assign a single object to window, to which you assign module variables. For example:
// in the console:
setTimeout(() => {
window.myModule.foo();
console.log(window.myModule.bar);
});
<script type="module">
function foo() {
console.log('doing foo');
}
const bar = 'a string bar';
const thisModule = { foo, bar };
window.myModule = thisModule;
// If you ever reassign variables, you'll have to reassign them on thisModule too
// or, only reference and reassign properties of thisModule, rather than create independent variables
</script>
For anyone else interested, if you're comfortable with it, use a bundler like Webpack. I don't believe (at least at this point) that the browser will by itself be able to use the DevTools on modules (the other solutions are quite janky, and aren't fantastic to work with).
Hopefully in the future, when all major browsers will be able to support ES6 modules without a bundler, we'll be able to use DevTools.
Using a Helper
I personally use a little helper function in development that allows me to expose a bunch a variables in a single expression. For example, it makes the following two blocks equivalent:
window.playerOne = playerOne;
window.someClass = someClass;
window.localData = localData;
globalize({playerOne, someClass, localData});
The helper looks like this:
const globalize = function(variables) {
Object.entries(variables).forEach(([name, value]) => window[name] = value);
};

How to avoid accidentally implicitly referring to properties on the global object?

Is it possible to execute a block of code without the implicit with(global) context that all scripts seem to have by default? For example, in a browser, would there be any way to set up a script so that a line such as
const foo = location;
throws
Uncaught ReferenceError: location is not defined
instead of accessing window.location, when location has not been declared first? Lacking that, is there a way that such an implicit reference could result in a warning of some sort? It can be a source of bugs when writing code (see below), so having a way to guard against it could be useful.
(Of course, due to ordinary scoping rules, it's possible to declare another variable with the same name using const or let, or within an inner block, to ensure that using that variable name references the new variable rather than the global property, but that's not the same thing.)
This may be similar to asking whether it's possible to stop referencing a property from within an actual with statement:
const obj = { prop: 'prop' };
with (obj) {
// how to make referencing "prop" from somewhere within this block throw a ReferenceError
}
It's known that with should not be used in the first place, but unfortunately it seems we have no choice when it comes to the with(global), which occasionally saves a few characters at the expense of confusing bugs which pop up somewhat frequently: 1 2 3 4 5 6. For example:
var status = false;
if (status) {
console.log('status is actually truthy!');
}
(the issue here: window.status is a reserved property - when assigned to, it coerces the assigned expression to a string)
These sorts of bugs are the same reason that explicit use of with is discouraged or prohibited, yet the implicit with(global) continues to cause issues, even in strict mode, so figuring out a way around it would be useful.
There are some things you need to consider before trying to answer this question.
For example, take the Object constructor. It is a "Standard built-in object".
window.status is part of the Window interface.
Obviously, you don't want status to refer to window.status, but do you want Object to refer to window.Object?
The solution to your problem of it not being able to be redefined is to use a IIFE, or a module, which should be what you are doing anyways.
(() => {
var status = false;
if (!status) {
console.log('status is now false.');
}
})();
And to prevent accidentally using global variables, I would just set up your linter to warn against it. Forcing it using a solution like with (fake_global) would not only have errors exclusively at run time, which might be not caught, but also be slower.
Specifically with ESLint, I can't seem to find a "good" solution. Enabling browser globals allows implicit reads.
I would suggest no-implicit-globals (As you shouldn't be polluting the global scope anyways, and it prevents the var status not defining anything problem), and also not enabling all browser globals, only, say, window, document, console, setInterval, etc., like you said in the comments.
Look at the ESLint environments to see which ones you would like to enable. By default, things like Object and Array are in the global scope, but things like those listed above and atob are not.
To see the exact list of globals, they are defined by this file in ESLint and the globals NPM package. I would would pick from (a combination of) "es6", "worker" or "shared-node-browser".
The eslintrc file would have:
{
"rules": {
"no-implicit-globals": "error"
},
"globals": {
"window": "readonly",
"document": "readonly"
},
"env": {
"browser": false,
"es6": [true/false],
"worker": [true/false],
"shared-node-browser": [true/false]
}
}
If you're not in strict mode, one possibility is to iterate over the property names of the global (or withed) object, and create another object from those properties, whose setters and getters all throw ReferenceErrors, and then nest your code in another with over that object. See comments in the code below.
This isn't a nice solution, but it's the only one I can think of:
const makeObjWhosePropsThrow = inputObj => Object.getOwnPropertyNames(inputObj)
.reduce((a, propName) => {
const doThrow = () => { throw new ReferenceError(propName + ' is not defined!'); };
Object.defineProperty(a, propName, { get: doThrow, set: doThrow });
return a;
}, {});
// (using setTimeout so that console shows both this and the next error)
setTimeout(() => {
const windowWhichThrows = makeObjWhosePropsThrow(window);
with (windowWhichThrows) {
/* Use an IIFE
* so that variables with the same name declared with "var" inside
* create a locally scoped variable
* rather than try to reference the property, which would throw
*/
(() => {
// Declaring any variable name will not throw:
var alert = true; // window.alert
const open = true; // window.open
// Referencing a property name without declaring it first will throw:
const foo = location;
})();
}
});
const obj = { prop1: 'prop1' };
with (obj) {
const inner = makeObjWhosePropsThrow(obj);
with (inner) {
// Referencing a property name without declaring it first will throw:
console.log(prop1);
}
}
.as-console-wrapper {
max-height: 100% !important;
}
Caveats:
This explicitly uses with, which is forbidden in strict mode
This doesn't exactly escape the implicit with(global) scope, or the with(obj) scope: variables in the outer scope with the same name as a property will not be referenceable.
window has a property window, which refers to window. window.window === window. So, referencing window inside the with will throw. Either explicitly exclude the window property, or save another reference to window first.
Somewhat simpler to implement than #CertainPerformance's answer, you can use a Proxy to catch implicit access to everything except window. The only caveat is you can't run this in strict mode:
const strictWindow = Object.create(
new Proxy(window, {
get (target, property) {
if (typeof property !== 'string') return undefined
console.log(`implicit access to ${property}`)
throw new ReferenceError(`${property} is not defined`)
}
}),
Object.getOwnPropertyDescriptors({ window })
)
with (strictWindow) {
try {
const foo = location
} catch (error) {
window.console.log(error.toString())
}
// doesn't throw error
const foo = window.location
}
Notice that even console has to have an explicit reference in order to not throw. If you want to add that as another exception, just modify strictWindow with another own property using
Object.getOwnPropertyDescriptors({ window, console })
In fact, there are a lot of standard built-in objects you may want to add exceptions for, but that is beyond the scope of this answer (no pun intended).
In my opinion, the benefits this offers fall short of the benefits of running in strict mode. A much better solution is to use a properly configured linter that catches implicit references during development rather than at runtime in non-strict mode.
Perhaps slightly cleaner (YMMV) is to set getter traps (like you did), but in a worker so that you don't pollute your main global scope. I didn't need to use with though, so perhaps that is an improvement.
Worker "Thread"
//worker; foo.js
addEventListener('message', function ({ data }) {
try {
eval(`
for (k in self) {
Object.defineProperty(self, k, {
get: function () {
throw new ReferenceError(':(');
}
});
}
// code to execute
${data}
`);
postMessage('no error thrown ');
} catch (e) {
postMessage(`error thrown: ${e.message}`);
}
});
Main "Thread"
var w = new Worker('./foo.js');
w.addEventListener('message', ({data}) => console.log(`response: ${data}`));
w.postMessage('const foo = location');
Another option that may be worth exploring is Puppeteer.
Just use "use strict". More on Strict Mode.

What is the purpose of this eval conditional?

I was browsing through the source code here: http://js-dos.com/games/doom2.exe.html and noticed a few things:
if (typeof Module === 'undefined')
{
Module = eval('(function() {try { return Module || {} } catch(e) { return {} }})()');
}
The Module function is defined with an inline script tag
It is later declared again with var in another inline tag, this time it checks if the Module exists.
My question: What is the point of declaring Module with a self invoking function if it'll only try to return the Module again? Hasn't it already been proven that it doesn't exist? Why not just explicitly declare Module as {}?
typeof Module might be undefined if Module is a local variable that happens to contain undefined. This code is meant to support a few cases, Module might be local or global, and defined or undefined. We want to avoid polluting the global scope, so we don't just do Module = ... if it's undefined.
First, the usual case is emscripten-generated code in the global scope. In this case, Module may or may not be defined, and may be local but still undefined, so we need to handle both.
Second, emscripten code may be just a module, like a game that uses ammo.js. In that case, the usage is
function Ammo(Module) {
// emscripten-generated code, uses the Module
return something;
}
so Module in this case is a function local, given as a param already defined for us.
We can't just declare var Module because that means Module is a local variable. So we need eval. For eval, we need a function that returns a value, because we need a try-catch. The try-catch uses Module, and will throw if Module is not a local (regardless of whether it contains undefined or not), which is exactly what we want.
It's possible this code could be simplified, though!

Nodejs: Wrapping entire script in a function call

I have been writing modules in nodejs as following :
module.exports = function (logger, db, external,constants) {
return {
//something
}
}
Recently someone in my team suggested that whole script should be wrapped in a function to avoid global confusion of variables i.e. like this :
(function () {
'use strict';
module.exports = function (logger, db, external,constants) {
return {
//something
}
}
}());
I understand that this practice is generally used at client side code. But during server side in nodejs is this required? I think that in nodejs there is really no global scope and only module.exports is the one which is accessible really irrespective of whatever we write in script file (ofcourse don't go wild over here).
No, IIFEs aren't required with Node.js.
They can be useful for any scripts that may be used in multiple environments (UMD).
But, each module/file executed by Node.js is given a "module scope," similar to the scope an IIFE provides, as described under "Globals":
In browsers, the top-level scope is the global scope. That means that in browsers if you're in the global scope var something will define a global variable. In Node this is different. The top-level scope is not the global scope; var something inside a Node module will be local to that module.
Though, there is still a global scope with Node.js. When a module creates a global, it will be accessible in other modules used by the same process.
foo = 'bar'; // lack of `var` defines a global
console.log(global.foo); // 'bar'
You're actually already doing this.
What he's suggesting is to wrap the entire script in a function like this:
function () {
}
That's all. Nothing special. Of course, in regular javascript a function definition just defines a function and the code inside the function doesn't run. So to automatically run the function you wrap it in an expression context and invoke it:
(function () {
})()
However, in node.js you don't need to do this. Instead you can simply call the function when you require the module. So in node.js, this does the exact same thing in terms of creating a private scope:
module.exports = function () {
}
So tell your friend you're already wrapping the entire script in a function.
This is perhaps the first case that I see the harm in naming things and design patterns. In this case your friend is thinking IIFE. But IIFE is nothing special. IIFE doesn't create a private scope. It's functions that create scope. IIFE is just a means for the function to call itself. Which is why I prefer calling it self-calling-function to avoid giving it a sense of magic which may cause some people to perceive it as something special.

Namespace import in node.js

I've got some function that allows to merge namespace, very similar to import when the module contains lot's of function (I expose an API with dozens of combinators)
It generates lots of var f = target.f; for every item from the export
function getNamespace(name, exports){
var output='';
for(var item in exports){
output += 'var ' + item + ' = '+name+ '.'+item + ';';
}
return output;
}
and usage:
var paco = require('./paco.js');
eval(paco.getNamespace('paco', paco));
// instead of paco.between(paco.start(),paco.content(),paco.end())
between(start(), content(), end())
Question:
I there a way to 'hide' the eval into the some function ? I don't want neither to mutate global namespace nor to call vm.runInThisContext, just need to add some local variables into the calling context after call function similar to require.
I mean I need something like
import('./paco');
// this should work like this
// var paco = require('./paco.js');
// var between = paco.between;
but without mutation of global and without eval in the calling scope.
tl;dr: No.
In order to understand why this is impossible, it's important to understand what Node is doing behind the scenes.
Let's say we define a function in test.js:
function foo() {
var msg = 'Hello world';
console.log(msg);
}
In traditional browser JavaScript, simply putting that function declaration in a file and pulling the file in with a <script> tag would cause foo to be declared in the global scope.
Node does things differently when you require() a file.
First, it determines exactly which file should be loaded based on a somewhat complex set of rules.
Assuming that the file is JS text (not a compiled C++ addon), Node's module loader calls fs.readFileSync to get the contents of the file.
The source text is wrapped in an anonymous function. test.js will end up actually looking like this:
(function (exports, require, module, __filename, __dirname) {
function foo() {
var msg = 'Hello world';
console.log(msg);
}
});
This should look familiar to anyone who has ever wrapped their own code in an anonymous function expression to keep variables from leaking into global scope in a browser. It should also start making sense how "magic" variables in Node work.
The module loader evals1 the source text from step 3 and then invokes the resulting anonymous function, passing in a fresh exports object. (See Module#_compile.)
1 - Really vm.runInThisContext, which is like eval except it does not have access to the caller's scope
After the anonymous wrapper function returns, the value of module.exports is cached internally and then returned by require. (Subsequent calls to require() return the cached value.)
As we can see, Node implements "modules" by simply wrapping a file's source code in an anonymous function. Thus, it is impossible to "import" functions into a module because JavaScript does not provide direct access to the execution context of a function – that is, the collection of a function's local variables.
In other words, there is no way for us to loop over the local variables of a function, nor is there a way for us to create local variables with arbitrary names like we can with properties of an object.
For example, with objects we can do things like:
var obj = { key: 'value' };
for (var k in obj) ...
obj[propertyNameDeterminedAtRuntime] = someValue;
But there is no object representing the local variables of a function, which would be necessary for us to copy the properties of an object (like the exports of a module) into the local scope of a function.
What you've done is generate code inside the current scope using eval. The generated code declares local variables using the var keyword, which is then injected into the scope where eval was called from.
There is no way to move the eval call out of your module because doing so would cause the injected code to be inserted into a different scope. Remember that JavaScript has static scope, so you're only able to access the scopes lexically containing your function.
The other workaround is to use with, but you should avoid with.
with (require('./paco.js')) {
between(start(), content(), end())
}
with should not be used for two reasons:
It absolutely kills performance because V8 cannot perform name lookup optimizations.
It is deprecated, and is forbidden in strict mode.
To be honest, I'd recommend that rather than doing something tricky with eval, do your future maintainers a favor and just follow the standard practice of assigning a module's exports to a local variable.
If you're typing it that often, make it a single-character name (or use a better editor).
According to this answer Global variables for node.js standard modules? there is global object the same as in browser there is window. So you can add key to that object
function getNamespace(exports) {
for(var item in exports){
global[item] = exports[item];
}
}
and use it as:
paco.getNamespace(paco);
no need for eval at all.
No. It's not possible to modify the local scope from an external module. Reason being, when eval is called in the external module, its context will be the external module, not the scope requiring the module.
In addition, vm.runInThisContext does not have access to the local scope, so that wont help you either.

Categories

Resources