javascript scope and global variables - javascript

I'm trying to avoid using global variable when using functions within objects.
I want to invoke a function inside other function and use a variable from the first function's scope.
For example:
var showForecast = {
'init': function () {
this.getData();
},
'buildView': function(){
var code = "Hey, you're from " + this.data.city;
$('body').append(code);
},
'getData': function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
showForecast.buildView();
})
}
}
Clearly it's not working. I want to use data inside buildView without making data a global variable.
I thought using this would be the right course of action because I'm calling buildView from a function where data is defined.
How can this be achieved? Thanks.

You can pass the information along:
var showForecast = {
'init': function () {
this.getData();
},
'buildView': function(data){
var code = 'Hey, you\'re from ' + data.city;
$('body').append(code);
},
'getData': function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
showForecast.buildView(data);
})
}
}

There is no way to access the data variable itself. That is locally scoped to the anonymous function you pass to getJSON (and getJSON passes it as an argument, which is beyond your control).
You have to copy the value somewhere.
In your particular example, there are no scopes shared between getData and buildView other than the global scope. So if you want to pass the value through scopes, then a global is your own (terrible) option.
You can simply pass it as an argument:
showForecast.buildView(data);
Or you can store it as a property:
showForecast.myData = data;

I like Vinny's answer.
One round-bout way is to make a module out of it:
var showForecast = function(){
var data;
var init = function () {
this.getData();
};
var buildView = function(){
var code = 'Hey, you\'re from ' + this.data.city;
$('body').append(code);
};
var getData = function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
this.data = data;
showForecast.buildView();
})
};
return {
'init': init,
'buildView': buildView,
'getData': getData
};
}();
This way the scope of var data is limited to the function. It's like a private variable.

As you are trying to avoid global, you should consider using namespaces. There is no such thing called namespace in Javascript. But you can define yourself using small utility method mentioned here.
http://www.zachleat.com/web/namespacing-outside-of-the-yahoo-namespace/
A utility method which helps creating custom namespaces.
jQuery.namespace = function() {
var a=arguments, o=null, i, j, d;
for (i=0; i<a.length; i=i+1) {
d=a[i].split(".");
o=window;
for (j=0; j<d.length; j=j+1) {
o[d[j]]=o[d[j]] || {};
o=o[d[j]];
}
}
return o;
};
Define name space
jQuery.namespace( 'jQuery.showForecast' );
Define methods using revealing module pattern
https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript
jQuery.showForecast = (function() {
var data;
var init = function() {
getData();
}
var buildView = function() {
var code = "Hey, you're from " + data.city;
$('body').append(code);
}
var getData = function() {
$.getJSON('http://ipinfo.io/', function(_data) {
console.log(data);
data = _data;
buildView();
})
}
return {
init: init
};
})(); // Execute it immediately
Usage:
You can access only init method as it is exposed to outside.
jQuery.showForecast.init()
Define another namespace
jQuery.namespace( 'jQuery.showForecast.extended' );
jQuery.showForecast.extended = {
// Define some more
};

Related

How to overcome "potentially invalid usage of this"?

I get potentially invalid usage of this when calling isDataMatchingnamespace how to overcome and how to call isDataMatchingnamespace in a proper way?
function Client() {
var namespace = "default";
this.addnamespaceTodata = function(data) {
data.namespace = namespace;
return data;
};
this.isdataMatchingnamespace = function(data) {
return data.namespace === namespace;
};
this.filterdatasBynamespace = function(datas) {
var result = [];
_.forEach(datas, function(data) {
if (this.isdataMatchingnamespace(data)) { // I get potentially invalid usage of this so how to overcome and how to call isDataMatchingnamespace in a proper way?
result.push(data);
}
});
}
}
module.exports = Client;
That is an invalid usage of this, since this is undefined inside that function.
underscore.js allows you to pass an optional additional argument to forEach to specify what this should be inside the function. If you want it to be the same as this from outside the function, then pass this as the third argument into _.forEach:
_.forEach(datas, function(data) {
if (this.isdataMatchingnamespace(data)) {
result.push(data);
}
}, this); // Added ", this"
there is other way also by storing this value into variable.
let's say var _thisRef = this; define this below var namespace = "default"; and use _thisRef.isdataMatchingnamespace(data) without changing your code
your updated code as follow :
function Client() {
var namespace = "default";
var _thisRef = this;
this.addnamespaceTodata = function(data) {
data.namespace = namespace;
return data;
};
this.isdataMatchingnamespace = function(data) {
return data.namespace === namespace;
};
this.filterdatasBynamespace = function(datas) {
var result = [];
_.forEach(datas, function(data) {
if (_thisRef.isdataMatchingnamespace(data)) { // I get potentially invalid usage of this so how to overcome and how to call isDataMatchingnamespace in a proper way?
result.push(data);
}
});
}
}
module.exports = Client;
Another better way would be to declare a variable at a class level like:
export class SomeComponent {someVarible = []; }
someFunction() { const tempVar = []; _.forEach(datas, function(data) {
tempVar.push(data) // now use this variable to pass or
assign the data
}, this);
this.someVarible = tempVar;
// OR
otherFunction(tempVar); // where you can use your varuiable
}
es6 arrow functions does that for you automatically !
Just change the syntax to arrow function syntax
data => {
your function goes here
}

scope of "this" in async function of ionic angular app

I'm trying to execute a function, which is not found, UNLESS I save a reference to the function in a seperate variable:
function updateCheck() {
if (this.isNewVersionNeeded()) {
var buildFunc = this.buildObject();
this.updateBiography().then(function(){
buildFunc();
})
}
};
The buildObject function only executes if I save it before executing this.updateBiography (async function) and execute it via the variable I saved it in (buildFunc).
The following does NOT work:
function updateCheck() {
if (this.isNewVersionNeeded()) {
this.updateBiography().then(function(){
this.buildObject();
})
}
};
I expose all functions via a service object:
var service = {
all: all,
updateBiography: updateBiography,
get: get,
updateCheck: updateCheck,
isNewVersionNeeded:isNewVersionNeeded,
buildObject:buildObject
};
return service;
When I log the "this" object while Im right before the execution of buildFunc, it logs window/global scope. Why is this and how should I deal with this? I do not want to save all my async methods in a seperate variable only to remember them. How should I deal with this problem and why does it not work?
The entire service:
(function () {
angular
.module('biography.services', [])
.factory('Biography', Biography);
Biography.$inject = ['$http'];
function Biography($http) {
var biographyObject = { } ;
var service = {
all: all,
updateBiography: updateBiography,
get: get,
updateCheck: updateCheck,
isNewVersionNeeded:isNewVersionNeeded,
buildObject:buildObject
};
return service;
var self = this;
function updateCheck() {
if (this.isNewVersionNeeded()) {
this.updateBiography().then(function(){
self.buildObject();
})
}
};
function updateBiography() {
return $http.get("Apicall adress")
.then(function (resp) {
window.localStorage.setItem('biography', resp.data);
window.localStorage.setItem('biographyTimeStamp', Date.now());
}, function (err) {
console.log('ERR', err);
});
}
function all() {
return biographyObject;
}
function get(name) {
var biography = biographyObject;
for (var i = 0; i < biography.length; i++) {
if (biography[i].name === name) {
return biography[i];
}
}
return null;
}
function buildObject() {
var temp = JSON.parse(window.localStorage.getItem('biography'));
biographyObject = temp;
};
function isNewVersionNeeded() {
prevTimeStamp = window.localStorage.getItem('biographyTimeStamp');
var timeDifference = (Date.now() - prevTimeStamp);
timeDifference = 700000;
if (timeDifference < 600000) {
return false;
}
else {
return true;
}
}
}
})();
The context (different from function scope) of your anonymous function's this is determined when it's invoked, at a later time.
The simple rule is - whatever is to the left of the dot eg myObj.doSomething() allows doSomething to access myObj as this.
function updateCheck() {
if (this.isNewVersionNeeded()) {
this.updateBiography().then(function() {
// whichever object has this anonymous function defined/invoked on it will become "this"
this.buildObject();
})
}
};
Since you're just passing your function reference, you can just use this
function updateCheck() {
if (this.isNewVersionNeeded()) {
this.updateBiography().then(this.buildObject);
}
};
and if this.buildObject is dependent on the context (uses the this keyword internally), then you can use
function updateCheck() {
if (this.isNewVersionNeeded()) {
this.updateBiography().then(this.buildObject.bind(this));
}
};
this is determined by whatever context (object) the function is invoked on, and it appears that an anonymous function, or a function not referenced through an object defaults to having a window context. the bind function replaces all instances of this with an actual object reference, so it's no longer multi-purpose
same function invoked in different contexts (on different objects)
var obj = {
a: function () {
console.log(this);
}
};
var aReference = obj.a;
aReference(); // logs window, because it's the default "this"
obj.a(); // logs obj
The reason is here 'this' refers to callback function.You can't access 'this' inside callback.Hence solution is,
function Biography($http) {
var self = this;
function updateCheck() {
if (this.isNewVersionNeeded()) {
this.updateBiography().then(function(){
self.buildObject();
})
}
};
Using ES6 syntax:
function updateCheck() {
if (this.isNewVersionNeeded()) {
this.updateBiography().then(()=>{
this.buildObject();
})
}
};

Proper way to reference js property from another property

I am working on an angular project and I have a factory providing some global database methods. I tested this in a jsfiddle and it works, but I want to know if it is the right way to do it.
So here's the jsFiddle.
function DB () {
return {
newRecord: function () {
//create new record
var id = 3;
//this is the part I am wondering about
//is it ok to use this in this way??
this.setCurrentRecordId(id);
},
setCurrentRecordId: function (id) {
alert('setting id');
return localStorage.setItem('id', id);
},
getCurrentRecordId: function () {
return localStorage.getItem('id');
}
}
}
var dbStuff = new DB();
dbStuff.newRecord();
alert(dbStuff.getCurrentRecordId());
Like I said, it seems to be working; just want to know if there is a better way or not.
Thanks!
The "standard" way of using constructor functions in JavaScript would be the following:
function DB () {
this.newRecord = function () {
var id = 3;
// yes, since you invoked the DB constructor using
// using the new keyword, this will be pointing to
// the created instance
this.setCurrentRecordId(id);
};
this.setCurrentRecordId = function (id) {
alert('setting id');
return localStorage.setItem('id', id);
};
this.getCurrentRecordId = function () {
return localStorage.getItem('id');
};
}
var dbStuff = new DB();
dbStuff.newRecord();
alert(dbStuff.getCurrentRecordId());
In case you need to reference the instance in a callback or some other situation in which context is lost there are two common patterns to deal with this.
Either store a reference to this (considered "ugly" by some, very convenient though):
function Ctor(){
var self = this;
this.getSomething = function(id){
asyncThing(id).then(function(result){
// here, `this` will refer to the global object
self.doSomethingWith(result);
});
};
this.doSomethingWith = function(result){
// do something
};
}
Or use .bind() to create a new function with a predefined context:
function Ctor(){
this.getSomething = function(id){
var processResult = function(arg){
this.doSomethingWith(arg);
}.bind(this); // bind sets the function's context no matter where you'll use it
asyncThing(id).then(processResult);
};
this.doSomethingWith = function(result){
// do something
};
}
Since you are using localstorage, there isn't any problem.
function DB () {
return {
setCurrentRecordId: function (id) {
alert('setting id');
return localStorage.setItem('id', id);
},
getCurrentRecordId: function () {
return localStorage.getItem('id');
}
}
}
var dbstuff = new DB();
dbstuff.setCurrentRecordId(3);
dbstuff.getCurrentRecordId() // 3

Declare variable in another scope

Look at this snipped code:
main = function() {
alert('This is ' + T);
}
caller = function() {
var T = 'me';
main();
}
caller();
as you see, I wanna function main identify the value of variable T, but the browser appears this error: T is undefined.
I can handle this error with change the scope of variable T to global scope or even pass the T variable to function main, but for some reason I don't want to use those and I want to declare variable T in scope of function main. Is it possible or not? How can I handle this scenario?
Thanks.
You have 3 options:
to declare T outside both
to pass T to main(T) as a parameter
to write main inside caller
T is a local variable to caller so it will not be visible inside main, one easy solution is to pass T as a parameter to caller from main
you need to pass T as a parameter
main = function(T) {
alert('This is ' + T);
}
caller = function() {
var T = 'me';
main(T);
}
caller();
Another solution is to declare T in a shared scope, this this case the global scope or declare main as a closure function inside main
The way I see it, you have a few options beside the obvious ones you have already stated.
One way is to declare main in caller:
caller = function() {
var T = 'me',
main = function() {
alert('This is ' + T);
};
main();
}
caller();
An other case would be to wrap both caller and main into an object, but that could be overkill. Still another way could be to set the this variable using Function.prototype.call or Function.prototype.bind:
main = function() {
alert('This is ' + this);
}
caller = function() {
var T = 'me';
main.call(T);
}
caller();
Or
main = function() {
alert('This is ' + this);
}
caller = function() {
var T = 'me',
newMain = main.bind(T);
newMain();
}
caller();
The best way to do this is to define a new function which declares T, main and caller. This way both functions have access to the value but it is not global
var pair = (function() {
var T;
var main = function() {
alert('This is ' + T);
};
var caller = function() {
T = 'me';
main();
};
return { 'main': main, 'caller': caller}
})();
pair.main(); // Call main
pair.caller(); // Call caller
Try to pass the object of the variable T to the function main and access it via this
main = function() {
alert('This is ' + this);
}
caller = function() {
var T = 'me';
main.call(T);
}
caller();
I can recall for three options:
make T global;
make a getter in caller object and use it to get the value
pass T as a parameter
Try this
var T = '';
main = function() {
alert('This is ' + T);
}
caller = function() {
T = 'me';
main();
}
caller();
I think something like this can handle it.
main = function() {
alert('This is ' + T);
}
caller = function() {
var T = 'me';
eval('var func = ' + main);
func();
}
caller();

Invoke javascript function from string

I have the following code in my javascript module, however this requires me to make the functions visible to the outside world.
var mymodule = function() {
var self = null,
init = function () {
self = this;
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'),
action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
},
send = function () {
// some logic
},
finish = function () {
// some logic
},
delete = function () {
// some logic
};
return {
init: init,
send: send,
finish: finish,
delete: delete
};
}();
mymodule.init();
So the only thing I want to return in my module is the init function. However when I do this I cant invoke the functions, because the object (self) only contains the init function visible on the outside.
return {
init: init
};
Is there any solution to invoke my functions like this without making them visible to the outside world? Please no if else statements, because my workflow is bigger then the 3 actions in this example. I want to make my module as closed as possible because this reduces the dependencies.
Update
Here is a updated jsfiddle with one of the proposed solutions, however this is giving me another issue. http://jsfiddle.net/marcofranssen/bU2Ke/
Something like this would work:
var mymodule = function() {
var self = this;
init = function () {
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'), action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
}
self.send = function () {
console.log('send');
}
self.finish = function () {
console.log('finish');
}
self.delete = function (item) {
console.log('delete');
};
return {
init: init,
};
}();
mymodule.init();​
Here's the fiddle:
http://jsfiddle.net/yngvebn/SRqN3/
By setting the self-variable to this, outside the init-function, and attaching the send, finish and delete functions to self, you can use the self[action] syntax from within the init-function
Yes, there is an easy (but perhaps slightly messy) way you can do this without making the functions visible to the global object:
var privateFunctions = { deleter: deleter, send: send};
Then, instead of self[action]();, just do privateFunctions[action](); and you're good to go.
Note that I changed delete to deleter, because delete is a reserved keyword...
var mymodule = function() {
var self = {},
init = function () {
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'),
action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
};
self.send = function () {
// some logic
};
self.finish = function () {
// some logic
};
self.delete = function () {
// some logic
};
return{
init:init
}
}();
mymodule.init();
This should Work!!
Even if you return an object just with the init property and you populate the rest dynamically such that your module uses them, you would still be making them visible to the outside at runtime. Anyone who wants to debug your module would easily get to them.
You can still create anonymous methods at runtime and they would also be visible together with their implementation.
In your code example, it is vague what "self" really is. You should keep it simple, use encapsulated functions as "private" methods and return a "public" (or "privileged" as Crockford calls it) function that have access to them.
This is the YUI way of doing singletons with private functions and variables. Example pattern:
var mymodule = (function() {
var internal = {
'send': function() {},
'finish': function() {},
'delete': function() {}
};
return {
'init': function(action) {
// access to internals, f.ex:
if ( internal.hasOwnProperty(action) ) {
internal[action].call(this); // bring the caller context
}
}
};
}());
mymodule.init('send');

Categories

Resources