In this case, How do I access the variable and method declared in a file from another file?
File one
jQuery(function(t) {
var myVar = 'myValue',
e = function(t) {
console.log('myLog');
}
});
File two
jQuery(function($){
// ????
});
You don't. It has nothing to do with files (JavaScript largely doesn't care about files unless they're ES2015+ modules), it has to do with the fact that both myVar and e are entirely private to the anonymous function you're passing into jQuery in the first code block. Even other code outside that function in the same file would be unable to access them.
You'd have to change the first file to make that information accessible outside that function. You could do that by making them globals (blech), or by having a single global you use for all of your things like this with an object with properties for these things (slightly less "blech" :-) ), or by using something like Webpack and true modules.
It really depends on how you setup your scripts. For instance:
<script src="fileOne.js"></script>
<script src="fileTwo.js"></script>
Then you will be able to do the following:
File One:
- Declare variable x
File Two:
- Access variable x
I recommend taking a look at this, it'll help with understanding variable scope (however this doesn't cover ES6's let): https://www.w3schools.com/js/js_scope.asp
Related
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);
};
Every function and variable that I create in KotlinJs project gets into a module. But I need to define some functions in global scope.
I use p5js library (pure js). It allows user to define event handling functions in global scope. I'm trying to use KotlinJS in this project. But I don't know how to create global functions to handle p5js's events. All my Kotlin functions are inside of the module. And to call my Kotlin code I need to specif the full name mymodule.draw()
Currently I have to make an additional layer of pure JS code with global funcs that translate execution to kotlin functions which looks like this:
function setup() {
mymodule.setup();
}
function draw() {
mymodule.draw();
}
The problem with this approach is a lot of boilerplate and repetitive code.
In case this will be useful for somebody I will leave another workaround here:
import kotlin.browser.window
fun main() {
window.asDynamic()["setup"] = ::setup
window.asDynamic()["draw"] = ::draw
}
fun setup() {}
fun draw() {}
What it actually does, it creates functions in kotlin module as usual and then assigns them to window object, which makes it global.
This solution is still not ideal, cause it needs a manual assignment for every function. At least it does it right in Kotlin project, no need to maintain a separate pure js file.
Maybe it is possible to create an annotation and leverage kotlin reflection(no idea how it's supported in KotlinJS).
Although this solution works for me, I would like to have some out of the box solution like they do for #JsNonModule external functions.
Adding on top of #Sergey's Answer, one can also use this work around when dealing with libs like p5.js
fun main() {
window.asDynamic().setup = {
// your setup code here
}
window.asDynamic().draw = {
// your draw code here
}
}
This approach minimizes the definition and the declaration of the two functions (looking at you C Language). Thanks
Unfortunately there is no way to define global function in Kotlin/JS. It is possible to use Plain module type where you have global symbols in module object which is defined in global scope.
// module M
fun foo() {}
which is accessible via M.foo
I have two JS files. One is called common.js and it use $.getScript to include other file with my js code. Part of including looks like this:
jQuery.getScript(js_path + "table.js", function(){
generateTable(chartData, dataTypes);
});
This file (common.js) also contains function compare(a, b).
Now, the second one (table.js) has declared different function which uses the compare function from the first file. Something like this:
function someName() {
var a = 2,
b = 5;
var test = compare(a, b);
return test;
}
When I run the code it gives me:
Uncaught ReferenceError: compare is not defined
How can I use function from the first file.
jQuery.getScript first fetches the JS file from the server, then executes it. If you want to work with global functions (as it seems) you need to pay attention to the following:
Your compare function must be declared before the table.js file is executed.
The compare function must be declared on the global namespace of table.js.
Sorry but without more info this is all you can get.
If your main file, as something like:
(function() {
function compare(){...}
}());
Then the compare function is not declared in the global namespace.
did you check the order of imports? The file with 'compare' method should be first. It should solve the problem.
What I would suggest is to skip getScript if it's just for separating the code.
<script src="common.js"></script>
<script src="table.js"></script>
<script src="app.js"></script>
Where common functions go into common, your table stuff goes into table. This way you get the ordering right. This also clears out the circular dependency one might see a hint of if table depends on common that depends on table by extracting all but the 'common' parts into some form of 'app'.
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.
I'm going to build a rather complicated application in html5 with some heavy javascripting.
In this I need to make some objects which can be pass around. But since there is no like #import foobar.js.
Then I assume that if my html page loads the scripts, then all the scripts can access eachother?
I read (here) that ajax somehow is able to load a .js file from within another .js file. But i dont think this is what I need?
Can go more into details if needed, thanks in advance.
In our projects, we do the following: Suppose you're going to call your project Foo, and have a module called Bar in it,
Then what we do is declare a file called Foo.js that just defines an equivalent to a Foo namespace:
Foo = (function(){
return {
};
})();
Then we create a file called Foo.Bar.js that contains the code for the Bar module:
Foo.Bar = (function(){
// var declarations here that should be invisible outside Foo.Bar
var p, q;
return {
fun1 : function(a, b){
// Code for fun1 here
},
fun2 : function(c) {
// Code for fun2 here
}
} // return
})();
Note that how it is a function that executes immediately, and returns an object that gets assigned to Foo.Bar. Any local variables, like p and q are available to fun1 and fun2 because they're in a closure, but they are invisible outside of Foo.Bar
The functions in Foo.Bar can be constructors for objects and so on.
Now in your HTML you simple include both files like so:
<script type="text/javascript" src="Foo.js"></script>
<script type="text/javascript" src="Foo.Bar.js"></script>
The result will be that you can call Foo.Bar's functions in the JavaScript of your main HTML file without any problems.
You should check out the module pattern:
http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth/
This describes alternatives for creating modular code in javascript, how you can protect your code and share APIs and data among them.
You should also consider using and AMD. Require.js is quite popular, but I tend to prefer head.js for this. Keep in mind that these put some requirements on how you structur your code in files, and personally, I don't think it's worth it, compared to a concatenated and minified file included in the bottom of the page.
Then I assume that if my html page loads the scripts, then all the scripts can access eachother?
Yes.