I am a beginner and I want to bind 'this' to helper-functions which I have imported from an other file (that I can use the variables, I created in the current lexical environment ).
But I have recognized, that I can bind these imported functions to any object - which I have created in the file, into I have imported them - but not to the current this.
node.js example:
https://github.com/sdeli/issues-with-this
// random-js-file.js
// this is the function I import in app.js
function logOutThis() {
console.log(this);
}
module.exports = logOutThis;
// --------------------------------
// app.js
const importedFn = require('./random-js-file.js');
// this is now the global of random-js-file.js
importedFn();
console.log('--------------------------------');
var monkey = {
chimp : 'chimp'
}
// this is now the object 'monkey'
var myfunction = importedFn.bind(monkey);
myfunction();
console.log('---------------------------------');
//this should be now the current this
var myfunction2 = importedFn.bind(this);
myfunction2();
// console.log displays '{}' and i can not refer to the variable in this lexical environment
So I dont understand why I can not bind 'this' into a function which I have imported but I can bind it to any object.
Thank you for your suggestions
There isn't a way to access the inner context of an imported module unless it explicitly exports the bits you want (or provides functions that expose them). Javascript this doesn't behave like Java this, and modules aren't classes.
There's some other cases, but basically, if the function you're currently is called someFunc and it was called with syntax a.someFunc(), then this will be the same as a. Here's a worked example:
const someObject = {
someFunc() {
//Here, "this" will be the same as someObject, because of the way we called it
someOtherFunction();
}
};
someObject.someFunc();
function someOtherFunc() {
//"this" is still someObject because it's inherited from the calling function which was not called using a method syntax
x.y(); //inside the y() function, "this" will now be x
}
Basically, IMHO, this is a broken mess in Javascript and is one of the key reasons I avoided using it whereever possible. That means I don't use classes, and I get everything I need from closures.
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.
How do I test the following iife javascript within Jasmine, if I want to mock the parameter y being passed in?
As the script files are referenced before the specs script, the y is immediately invoked before I can mock/spy on the y object.
I can create a dummy mock object on the spec runner html (or even another js file), but this then doesn't allow me to change the mock value on each test, as I cannot reference the variable checkObject, thus I cannot test a pass/fail scenario.
Am I missing something?
Thanks
file 1.js
var y = {
age:25
}
file 2.js
var helper = (function(checkObject){
function isValid(itemToCheck){
return itemToCheck > checkObject.age ;
}
return {isValid:isValid}
})(y);
I see what you're trying to do. I think you should redesign your code. When a peace of code is not testable, it's a hint of a poor design.
An iife is basically used to create a new lexical scope to protect against polluting the global scope. In your case, you are using iife as an object factory. One solution could be to create a helper factory that takes a checkObject and return a helper object.
var helperFactory = {};
helperFactory.createHelper = function (checkObject){
function isValid(itemToCheck){
return itemToCheck > checkObject.age ;
}
return {isValid:isValid}; // this is the helper object returned by the factory
}
Now you can test helperFactory and helper object are testable. To use the y variable, you can just do something like this :
var helper = helperFactory.createHelper(y);
remember, an iife is mainly used to hide variables (create new scope). If you want to hide the helperFactory, you have to use some mechanism to make this object visible in the global scope. you can for example attach the helperFactory to the window global object like this :
(function(){
window.helperFactory = {};
window.helperFactory.createHelper = function (checkObject){
function isValid(itemToCheck){
return itemToCheck > checkObject.age ;
}
return {isValid:isValid}; // this is the helper object returned by the factory
}
})();
var helper = window.helperFactory.createHelper(y);
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 () {
this.func0 = function() {
console.log('hi')
}
this.func0() // this works
func0() // but not this?
Why func0() is not working?
Aren't this.f() and just f() both supposed to access global object?
The important thing to understand here is, in Node.js, each and every JavaScript file will be treated as a separate module.
So, within the JavaScript file, this will refer to the current module object, not the global object. We have something called global object in Node.js, which is somewhat similar to browsers' window object.
This is what you are doing with your program
this.func0 = function() {
console.log('hi')
}
you are creating a function object and assigning it to the current module's func0 attribute.
this.func0();
you are calling current module's func0.
func0();
JavaScript, searches for func0 in the current scope, then in the global scope but it doesn't find it anywhere. That is why it fails with the ReferenceError
ReferenceError: func0 is not defined
If you really wanted to set that in the global scope, then you should do it like this
global.func0 = function() {
console.log('hi')
}
func0();
// hi
My application is accessing a third party external JavaScript file that I cannot alter.
Within the file is an object defined similarly to as follows:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
}
}
})(window);
I need to be able access the object_1_var within the object, but I'm struggling to access it.
object_1.v // returns undefined
object_1.obj_1_func() // returns the value via console, but I need to assign it to a var.
I have tried extending the object using as follows: (Using jQuerys $.extend())
object_2 = (function(){
return {
obj_2_func: function() {
return object_1_var;
}
}
})(window);
$.extend(object_1, object_2);
var my_var = object_1.obj_2_func(); // returns 'Uncaught ReferenceError: object_1_var is not defined'
What can I do to be able to access object_1_var?
You will not be able to access the variable. It happens to be a private member. Private members of an object can be accessed only by its member functions.
Read this.
object_1_var is a lexically scoped local variable.
That means that it can't be accessed by extending object_1 outside of its original definition.
The only way it can be accessed is by adding functions within the original lexical scope in which it was declared:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
}
var_1: function(x) {
if (typeof x !== 'undefined') {
object_1_var = x;
} else {
return object_1_var;
}
}
}
})(window);
but since you can't modify object_1, you're out of luck, I'm afraid!
Make it public, like this:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
},
object_1_var: object_1_var
}
})(window);
EDIT
If unable to edit the javascript (such as in a third party library - sorry for omission) then you will not be able to have access to object_1_var as it's scope is local to the closure object_1.
What you are trying to accomplish is impossible in JS.
With the construction of object_1 the variable goes out of scope of that method. The reason why the logging function can access the variable is what we call 'a closure'.
Sadly, object_1_var isn't accessible in this example. The variable is defined as local to within that particular function - the only reason that the other functions can see it is because they are also defined within that function. This "closure scoping" is an interesting feature in JavaScript that you don't see very often elsewhere, but is the only real way of defining "private" variables in JavaScript Objects.
Hope that helps!
In a worst case scenario, in the past I've worked around this sort of issue by effectively overwriting the definition of an object that was previously defined elsewhere - mainly in Greasemonkey scripts - but I wouldn't condone this for production uses!
The trick here is to just copy the entire piece of script into your own. It's ugly as hell, but it might just work! (YMMV)