Is it possible to do achieve this behavior using node modules?
module.js:
module.exports = function () {
var test.fn = function () {
console.log("$test.fn()");
}
var test.fn2 = function () {
console.log("$test.fn2()");
}
var variable = "test";
};
app.js:
require("./module.js")();
test.fn();
test.fn2();
otherFunction(variable);
I dont want to do anything like this $ = require("./module.js")(); $.test.fn();
I want to inject this variables into app.js scope without wrapper variable.
Edit:
I have ended up using this:
module.js:
module.exports = function () {
eval(String(inject));
var inject = (
this.$module1 = {},
this.$module1.fn = function () {
console.log("$module1.fn()");
}
);
};
app.js:
require("./module.js")();
$module1.fn();
The top level scope in a module is actually a function scope (the node.js loader wraps each module in a function that it then calls to execute the code in the module). Thus, there is no publicly available "root" object that we can programmatically assign properties to.
So, that means that it is not possible in a module to programmatically add new variables at the top level of the module scope without using eval() in a fairly big hack. Function scopes just don't work that way in Javascript.
You could also have the module assign things to the global object where they can be used without a prefix, but this is NOT a recommended practice in any way. The whole point of node.js modules is to avoid using any globals and to make code entirely self contained with little chance of global collisions.
Or, you could have your module export a giant string of Javascript and then eval() it inside of app.js in order to define new variables in the module scope. Again - not recommended.
Your best best is do things the "node.js way" and put everything on an object and export that object. Here's one variation:
app.js
var test = require("./module.js")();
test.fn(); // "executing fn"
test.fn2(); // "executing fn2"
console.log(test.myVar); // "test"
module.js
module.exports = function () {
return {
fn: function () {
console.log("executing fn");
},
fn2: function() {
console.loog("executing fn2");
},
myVar: "test"
}
};
You can do something like that using an IIFE. The method, when required, will automatically run and return an object, which you can then use in your application.
module.js:
global.test = (function() {
return {
fn: function() {
console.log("executing fn");
},
fn2: function() {
console.log("executing fn2");
},
variable: "test"
}
})();
app.js
require("./module.js"); //just include, no need for a wrapper variable
test.fn();
test.fn2();
console.log(test.variable);
Note that this technique will overwrite the global variable test if it already exists.
This answer should not encourage you to use global within your node.js modules (see jfriend00's comments above) as it makes your code vulnerable for name collisions with other modules and thus your modules less portable.
Within module.js you have access to the global object of node.js runtime environment.
module.js:
global.test = {
fn: function() {
//Do something
},
fn2: function() {
//Do something different
},
variable: "test variable"
}
app.js
require("./module.js"); //just include, no need for a wrapper variable
test.fn();
test.fn2();
console.log(test.variable);
Note that this technique will overwrite the global variable test if it already exists.
Related
I've got a school assignment of creating an app and one of the restrictions were that only one global variable was allowed, named "App". So I need to put the whole JS inside this variable.
I thought of doing something like:
App = function() { this.test = () => alert("test"); }
And then calling it with App.test().
But this doesn't work, the error I'm getting is:
Uncaught TypeError: App.test is not a function at HTMLLIElement.onclick (index.html:25)
Am I doing something wrong?
You need to define your app in a variable as an object and then you can use those members of the object such as:
// Object creation
window.App = {};
Then you add more properties like functions or variables and use it later inside of that variable.
window.App.test = function() {
alert('test');
}
window.App.variable = 'my variable';
console.log( App.test() );
console.log( App.variable );
Another thing is you can omit the word window from App.
Keeping most of your approach as it is, you could return an object that has functions as properties.
App = function() {
return {
test: () => alert("test"),
test2: () => alert("test2")
};
}
App().test();
App().test2();
To be able to use your function that contains this.test..., you'll need to use it as a "constructor function" because this in a function declaration means "instance property", meaning you will need to explicitly create an instance of App with the new keyword:
// Declare the global
var App = function() { this.test = () => alert("test"); }
// Make an instance of an object via the constructor function
var myApp = new App();
// Invoke the functionality via the instance
myApp.test()
Or, set up App as an object, connect that object to the Global window object and set test as a property of App all without any instance properties (this references), which avoids having to make the explicit instance:
// Declare the global property as an Object
// and set up a "test" property that stores
// a function in that object:
window.App = { test: function(){alert("test");}}
// Invoke the functionality directly
App.test()
The test function is not defined before executing the App function:
App = () => test = () => alert("test")
<button onclick='App(); test()'>:</button>
App can be defined as object instead:
App = { test: () => alert("test") }
<button onclick='App.test()'>:</button>
Just to piggyback on to what the other answers have suggested, this technique is most commonly used (in browser developement) when you "namespace" some JS code. Namespacing is useful because it helps the developer reduce global variable pollution by adding all their code under one namespace under window.
This also helps the developer modularise the code.
Here's a pattern you might see from time to time that adds code to a single namespace under window.
// This first function is what's known as an Immediately-invoked
// function expression (IIFE). `window` is passed in as an argument
// to the function - all other declared variables are local to the scope
// of the function so you don't get anything leaking to global.
// PS, the semi-colon is there before the opening parenthesis to prevent
// any errors appearing if you minimise your code
;(function (window) {
// Here we declare something new to add to the namespace
// It could be another object, a string, an array, or a constructor
// like Model
function Model() {...}
// If namespace `window.app` doesn't exist instantiate it with
// an empty object. If it _does_ exist it probably means that another
// module like this one has already been added to the namespace.
window.app = window.app || {};
// Then assign your new module as a property under that namespace
window.app.Model = Model;
})(window);
You can then use it later something like this:
var model = new app.Model();
var game = new Game(model);
For further reading: Addy Osmani on namespacing.
I am trying to set a global variable on protractor to use in all describe blocks.
var glob = 'test';
describe('glob test', function () {
it('should set glob', function () {
browser.get('http://example.com/test');
browser.executeScript(function () {
window.glob = glob;
});
});
});
But this returns me the following error:
Message:
[firefox #2] UnknownError: glob is not defined
I also looked at this question: protractor angularJS global variables
so i tried to set the variable glob in conf.js in this way:
exports.config = {
...,
onPrepare: function () {
global.glob = 'test';
}
};
Still, having the same error.
How can i add properly global variables in protractor tests?
It is possible to set globals from the Protractor config file with the help of params property:
exports.config = {
// ...
params: {
glob: 'test'
}
// ...
};
And you can access it in the specs using browser.params.glob.
See the reference config file.
The params object will be passed directly to the Protractor instance, and can be accessed from your test as browser.params. It is an arbitrary object and can contain anything you may need in your test. This can be changed via the command line as:
protractor conf.js --params.glob 'other test'
Update:
From the docs for browser.executeScript:
If the script is provided as a function object, that function will be converted to a string for injection into the target window. Any arguments provided in addition to the script will be included as script arguments and may be referenced using the arguments object.
So, JavaScript scope in this case does not work, you function that is passed to browser.executeScript won't have any closure variables from spec like browser. But you can pass these variables explicitly:
browser.executeScript(function (glob) {
// use passed variables on the page
console.log(glob);
}, browser.params.glob);
You can also set global variables in onPrepare() using global:
onPrepare: function () {
global.myVariable = "test";
},
Then, you would just use myVariable throughout the tests as is.
This is actually how protractor, browser and other built-in global variables were made available globally:
Runner.prototype.setupGlobals_ = function(browser_) {
// Export protractor to the global namespace to be used in tests.
global.protractor = protractor;
global.browser = browser_;
global.$ = browser_.$;
global.$$ = browser_.$$;
global.element = browser_.element;
global.by = global.By = protractor.By;
// ...
}
Note that with this approach you are polluting your global scope/namespace, be careful.
Another option is to use a process variable
Protractor is a node process. Any node process can be started with custom node variables. Not sure how it's done in windows (please comment if you know how) but for mac and any linux/unix OS you can
Start protractor with environment variable like this
MY_VAR=Dev protractor tmp/config.js
And then it will be available anywhere within your process and even in your config
console.log(process.env.MY_VAR)
I know I'm a bit late to the answer but here's another way of setting global variables that can be used throughout the file
describe("Some Global most outer describe", function(){
var glob = "some global variable";
var blob = "some other global variable";
it('should test glob', function(){
expecte(glob).toEqual("some global variable");
});
it('should test blob', function(){
expecte(glob).toEqual("some other global variable");
});
describe('Some inner describe', function(){
//Some other inner tests can also see glob and blob
});
});
I'm new to js and trying to understand global and private functions. I understand global and local variables. But if I have an html named test.html and a 2 js files named test1.js and test2.js. Now I include the test1.js and test2.js in test.html and call the functions written in test2.js inside test1.js and test.html.
The functions that I have written in test2.js are in this form
function abc(){...}
function pqr(){...} etc.
are these above functions global? If they are , how can I not make them global and still access them in test1.js and test.html?
As I have read global functions or global variables are bad right?
Everything in JS is bound to containing scope. Therefore, if you define a function directly in file, it will be bound to window object, i.e. it will be global.
To make it "private", you have to create an object, which will contain these functions. You are correct that littering global scope is bad, but you have to put something in global scope to be able to access it, JS libraries do the same and there is no other workaround. But think about what you put in global scope, a single object should be more than enough for your "library".
Example:
MyObject = {
abc: function(...) {...},
pqr: function(...) {...}
// other functions...
}
To call abc for somewhere, be it same file or another file:
MyObject.abc(...);
in test2.js you can write this to make the function global
window.abc = function(){...}
and then in test1.js yo can access it like this
window.parent.abc();
I hope it will help you
Anything defined in a file without any sort of wrapper will be bound to the window object. Anything bound to the window object is global.
Example:
//these are global variables
foo = 123;
var ABC = 'school';
//these are "private" variables
test = function(){
var foo = 123
}
(function(){
var ABC = 'school';
}).call(this);
Since global variables in every file will be part of the window object, you can access them between files. It is important when creating "private" variables you add var. This says override any global variables in the current "wrapper". If I have a global variable foo and I define it again in a function with var they will be separate.
var foo = 123;
(function(){
var foo = 987; //this foo is separate from the above foo
}).call(this);
If you do have a "wrapper" and you want to define a global function you can do it like this:
window.foo = 123;
(function(){
window.foo = 123;
}).call(this);
Both functions will do the same thing.
Personally, I prefer to put everything in a wrapper and only define global variables when I need them using window.
(function(){
//all code goes here
//define global variable
window.foo = 123;
})call(this);
var SomeName = function() {
var function1 = function (number) {
return number+1;
}
var anotherFunction = function (number) {
return number+2;
}
return {
function1: function (number) {
return function1(number);
},
function2: function (number) {
return anotherFunction(number);
}
}
}();
calling
console.log(SomeName.function1(1)); //logs 2
console.log(SomeName.function2(1)); //logs 3
A modern approach (2020) to using global data is by using a global object literal and define there all your needed logic.
const Website = {
foo () {
console.log('foo')
},
bar () {
console.log('bar')
}
}
document.addEventListener('DOMContentLoaded', () => {
Website.foo()
Website.bar()
})
If your code is more complex than a couple of lines you will need to separate your code into multiple files and with webpack you merge them together into one file.
import Foo from './js/Foo.js'
import Bar from './js/Bar.js'
// define here another object literal or setup document events.
// webpack will merge all this together into one file
<script src="js/webpack-merged.js"></script>
The reasons you don't want to import individual js files with html is described here. The jist of it is you will have poor performance, so you must bundle all your js.
If you don't understand why global variables are bad, then why are you trying to avoid them?
Global functions aren't necessarily bad. What's bad is state that anyone and anything and change.
In general since you're new to Javascript, it's fine to start out with global functions spread out across multiple javascript files that you include in your html file via script tags.
As you transition from beginner to intermediate, you will have to look into some "module" solution (I personally recommend RequireJS).
For now though, you can make do with a simpler module pattern:
var T1 = function() {
return < some module object >
})(); // notice the parenthesis
Google "Javascript module pattern".
Also see this answer.
vos fonctions ne sont pas global si vous faite l'erreur de les incorporé dans par exemple :
$( document ).ready(function() {
function myglobalfunction() { ... }
});
vous devez retirer
$( document ).ready(function() {
your functions are not global if you make the mistake of embedded them in for example:
$(document) .ready (function () {
function myglobalfunction () {
...
}
});
you must remove
$ (document) .ready (function () {
I was probing my code for errors with JSLint and so far I managed to fix all bugs except the problem when my global application variable is undefined.
I was using this code before JSlint which worked properly:
var APP = APP || (function {
return {
init: function () {
}
};
}(window.document));
then I would call
APP.init();
to initialize. But JSlint doesn't like global variables so I changed my code to this:
(function (global){
var APP = {
init: function () {
}
};
return APP;
}(window.document));
This code passes the JSLint test with no problem, but then
when I call APP.init(); it says that APP is used before it was defined, which is true.
What would you recommend so that I don't use global variables but still create my application object, while my code passes JSlint test?
First code
In your original code, the actual error you would have gotten was
#1 'APP' used out of scope.
var APP = APP || (function() { // Line 1, Pos 11
It is not because JSLint hates global variables, but because it doesn't like the variable being assigned to it is defined in the same var statement.
Changing your code to use a different variable name would fix this problem
var app = APP || (function() {...}(..));
but then, one would expect that APP is already defined in the current scope. Otherwise you will get
#1 'APP' was used before it was defined.
Second code
It doesn't work because, APP is visible only inside the function object you created. It is not visible anywhere outside it. Since you want to define APP in the global scope, you just have to attach it to the window object, like this
(function(global) {
global.APP = {
init: function() {
}
};
}(window));
Then
APP.init()
will work fine.
Instead of going through all these, you can straight away define APP like this
window.APP = {
init: function () {
"use strict";
...
}
};
(function (global){
var APP = {
init: function () {
}
};
return APP;
}(window.document));
the above creates a scope/closure, so that only code inside your anonymous function can access the APP variable. JSHint has settings so you can set acceptable globals (such as jquery etc), I'd add APP to that and if the rest pass you know you only have that one variable/module as global. If you are using any 3rd party libs chances are you already have several global vars anyway
Before you give up on JSLint, know that it doesn't mind globals at all; it just wants you to declare them with a special format so that others reading your code know exactly what's going on.
The only "trick" here is if there's a chance your global APP isn't initialized yet and you really need to check for its "truthiness", as you do with APP = APP || (function.... Then you do need to use the window (or whatever your global object is -- it's different if you use Node, for example) prefix trick.
Here's how:
/*jslint white:true, sloppy:true, browser:true */
/*global APP */
window.APP = window.APP || (function () {
return {
init: function () {
window.alert('JSLint doesn\'t like empty blocks');
}
};
}(window.document));
APP.init();
And JSLint is happy!
I'm not against JSHint in theory, but that same flexibility that allows you to set all sorts of extra settings also tends to erode conformity, which is largely what I find the best benefit of a code linter to be. Give both a good, solid run, and see which you like best!
I have defined the following piece of Coffeescript code below, which defines a function using RequireJS:
define 'PersonService', ['jquery'] ($) ->
getPerson = (id) ->
person = dataService.GetPerson id
{ getPerson: getPerson}
It produces the following:
(function() {
define('PersonService', ['jquery'](function($) {
var getPerson;
getPerson = function(id) {
var person;
return person = dataService.GetPerson(id);
};
return {
getPerson: getPerson
};
}));
}).call(this);
I guess the issue I have is with the self-executing function that is produced from the Coffeescript code. Will this cause issues that I am possibly not tracking? Or is this proper.
Thank you.
It's correct
The difference between not having a wrapped function and having one is to do with scope.
If you define var a = 10; in the global scope a becomes global with or without the var keyword.
When wrapped all variables are local to the wrapped function so do not end up global.
In you example everything is already wrapped in a function so yes you don't need the extra wrapping!
You can tell coffee-script not to add the wrapper function using
coffee -b, --bare compile without a top-level function wrapper
IMHO: It's better to always have the wrapper function so you don't need to think about it on a file to file basis.