i searched stackoverflow before asking this, none really solved my issue
I'm writing a discord bot and I'm writing a way to have custom prefixes. So far, I have this for handling situations where the prefix for a guild isn't in the json file.
const prefixfile = require('./prefixes.json');
function writePrefix(guildid) {
const currentprefixes = prefixfile;
currentprefixes[guildid] = "!";
fs.writeFile('./prefixes.json', JSON.stringify(currentprefixes), output => {
console.log(output);
});
delete require.cache[require.resolve(`./prefixes.json`)];
const prefixfile = require('./prefixes.json');
}
I get the error ReferenceError: Cannot access 'prefixfile' before initialization
EDIT: I've fixed that issue and got another:
fs.writeFile('./prefixes.json', JSON.stringify(currentprefixes), output => {
console.log(output);
});
that doesn't write anything to ./prefixes.json, even though currentprefixes has data that should be written.
any ideas?
Even though a variable named prefixfile is declared outside your function, the first line of your function references the variable declared locally with same name - even though it is still not declared at that point. This happens because all the variables of your program are added to their respective lexical scopes at compile time - before any code has a chance to run.
However, since the local variable is declared with const, trying to access it before it has actually been declared will cause the ReferenceError that you see.
See the TDZ (Temporal Dead Zone) notes for further information on this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#temporal_dead_zone_tdz
like IAmDranged said, the variable prefixfile was not declared in the function. I fixed that by not using a function and moving it all inside my if statement. My other issue was that fs.writeFile() didn't write anything - I fixed by using fs.writeFileSync().
Related
I am using a global variable job_name. I will set the value for this variable in file1.ts. I would like to use the global variable in file2.ts. However, in file2.ts, job_name is undefined though I am setting in file1.ts. Seems like since file1 and file2 are loaded as soon as project is initiated (even before job_name is set in file1), file2 is assuming a value of undefined for job_name. Please let me know how I can use job_name in file2.ts with the value set in file1.ts.
global.d.ts
declare global {
var job_name: any
}
export { };
Extra info for reference:
Original question (this is my actual scenario) -
Global variable for 'log' in other files is undefined since it is set after project is started in nodejs
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"
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);
};
I'm currently working with the discord.js library.
I guess I can call it by this name, but whenever I want to access a file, this doesn't work.
Let's say I have a file called calc.js and I want to access the main.js file and take a variable out of there using exports and require it to just take the value out of it.
But I haven't found even one way online to modify the variables and return another value to the file.
Can someone help me?
As noted, JavaScript doesn't pass every variable by reference. If you need to access a primitive value like a number, you could declare it as a local variable and export functions to access and modify it. Something like this rough example:
increment.js
let count = 0;
module.exports = {
get: () => count,
increment: () => ++count
};
main.js
const { get, increment } = require('./increment.js');
console.log(get());
console.log(increment());
console.log(get());
Edit: You should probably not name your accessor get, as that's the key word used to describe getters in ES6. Or better yet, turn such a get function into a getter with a more suitable name.
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.