Using closure for storing and retrieving data - javascript

I am trying to use closure for storing and retrieving variable at the same time.
I am using JSONP and callback to the function
http://freegeoip.net/json/?callback=geoIPInfo
Closure
function geoIPInfo(newGeoData) {
var geoInfo;
if (newGeoData) {
geoInfo = newGeoData;
}
var provideGeoData = function () {
return geoInfo;
};
return provideGeoData();
}
I want firstly to store data and than retrieve last saved data from the closure using simple call like that
geoIPInfo()
If argument provided it will set new info otherwise it will return existing one.
But in my case data is set successfully, but when I try to get set data I get undefined
$("#super_button").click(function (e) {
alert(geoIPInfo());
e.preventDefault();
});
What is wrong with my closure understanding ?
Please explain.
Thank you.

This will work. The idea here is we create a function that returns a function with that accepts a parameter and we store geoInfo in a closure to keep it value. Idk if that makes sense, if you need a better explanation I can give it another try :)
var geoIPInfo = function() {
var geoInfo;
var provideGeoData = function (newGeoData) {
if (newGeoData) {
geoInfo = newGeoData;
}
return geoInfo;
};
return provideGeoData;
}();

Each time you call geoIPInfo(), you're re-declaring the local variable geoInfo. You'll want to declare geoInfo once, and have it accessible to geoIPInfo() via a closure:
//Create a closure
var geoIPInfo = (function(){
//Private variable, available via closure
var geoInfo;
function geoIPInfo(newGeoData) {
if (newGeoData) {
geoInfo = newGeoData;
}
var provideGeoData = function () {
return geoInfo;
};
return provideGeoData();
}
return geoIPInfo;
})();
alert(geoIPInfo()); //undefined
geoIPInfo('Some Data');
alert(geoIPInfo()); //'Some Data'
Here, we're creating a closure using an Immediately-Invoked Function Expression (IIFE).

Related

JavaScript - Get object / call function inside function's return statement

I have a weird situation, I'm working with a 3rd party API and I can use JavaScript but all my code has to be a return function in order for it to work, here is how my app looks like, it gets plugged into their system:
app.js
(function() {
var myVar1 = null;
var globalFunction = function(){
alert('TEST');
}
return {
test: null,
requests: {
},
events: {
'app.activated': 'initApp'
},
insideFunction: function(item){
//some code
},
initApp:function(){
//some code
//I can set the gobal variables using varName = Value
//I can set the return variables using this.varName = Value
//I can call the return functions using this.insideFunction()
//the entire app is basically run from inside 'return'
}
};
}());
I can access the global vars / function from inside the return, but how can I do it vice-versa? I need to call insideFunction from the globalFunction.
Assign the object you're returning to a variable before returning it.
var api = {
test: null,
...
};
var globalFunction = function() {
api.insideFunction();
};
return api;

JS Revealing Pattern event undefined issue

I am using the modular design pattern for JS and I keep running into issues when using arguments bound functions. I have a particular function that I would like to bind to different events to keep from having to write the function for each bound event. The only difference in the function, or the argument, is the table that will be updated. The problem is that when I build a function with the arguments I need and pass those arguments to bound events, I get an undefined error, in the console, on load. Keep in mind, I want to stick with this design pattern for the security it offers.
Here is my JS:
var Users = (function(){
var $addRoleForm = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
$addRoleForm.submit(ajaxUpdate(event, $rolesTableBody));
function ajaxUpdate(event, tableName) {
event.preventDefault();
event.stopPropagation();
var url = this.action;
var data = $(this).serialize();
var $this = $(this);
$.ajax({
type: 'POST',
url: url,
dataType: 'json',
data: data,
success: function(data) {
if(data.st === 0){
$messageContainer.html('<p class="alert alert-danger">' + data.msg + '</p>');
setTimeout(function(){
$messageContainer.hide();
}, 7000);
} else {
$messageContainer.html('<p class="alert alert-success">' + data.msg + '</p>');
tableName.fadeOut().html('').html(data.build).fadeIn();
$this.find('input').val('');
setTimeout(function(){
$messageContainer.hide();
}, 7000);
}
},
error: function(xhr, status, error){
console.log(xhr.responseText);
}
});
}
})();
Here is the error I get in the console, on load:
Uncaught TypeError: Cannot read property 'preventDefault' of undefined
I have tried to bind the event like this: $addRoleForm.on('submit', ajaxUpdate(event, $rolesTableBody)); and receive the same results.
Any ideas how to fix this?
You're seeing that issue, because the way you have it written now, ajaxUpdateexecutes, returns undefined and THEN passes undefined to the event listener, so you're basically doing this: $addRoleForm.submit(undefined).
2 Choices here:
1) You can wrap it in an anonymous function:
$addRoleForm.submit(function(event) {
//pass the value of "this" along using call
ajaxUpdate.call(this, event, someValue);
});
$someOtherForm.submit(function(event) {
//pass the value of "this" along using call
ajaxUpdate.call(this, event, someOtherValue);
});
2) You can set the first argument in-advance using bind:
$addRoleForm.submit(ajaxUpdate.bind($addRoleForm, someValue));
$someOtherForm.submit(ajaxUpdate.bind($someOtherForm, someOtherValue));
Using this way, you're binding the value of this to be $addRoleForm, setting the first argument to always be someValue, so it's the same as:
ajaxUpdate(someValue, event) {
//value of "this" will be $addRoleForm;
}
To pass the event, and the custom argument, you should be using an anonymous function call
$addRoleForm.submit(function(event) {
ajaxUpdate(event, $rolesTableBody));
});
This is by far the easiest and most readable way to do this.
What you're doing right now equates to this
var $addRoleForm = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
var resultFromCallingFunction = ajaxUpdate(event, $rolesTableBody); // undefined
$addRoleForm.submit(resultFromCallingFunction);
Where you're calling the ajaxUpdate function, as that's what the parentheses do, and pass the returned result back to the submit callback, which in your case is undefined, the default value a function returns when nothing else is specified.
You could reference the function, like this
$addRoleForm.submit(ajaxUpdate);
but then you can't pass the second argument
The question refers to the Revealing Module pattern. Benefit of using this design is readability. Going with the anon function may work, but defeats the overall purpose of the module pattern itself.
A good way to structure your module to help maintain your scope is to setup helper functions first, then call a return at the end.
Example use case with events:
var User = function() {
// local VARS available to User
var addRoleForm = document.querySelector('#addUserRole');
var rolesTableBody = document.querySelector('#table-roles tbody');
// Helper function 1
function ajaxUpdate(tableName) {
...
}
// Helper function 2
function someFunc() {
...
}
function bindEvents() {
addRoleForm.addEventListener('submit', ajaxUpdate, false);
addRoleForm.addEventListener('click', someFunc, false);
}
function init() {
bindEvents();
}
return {
runMe:init
}
}().runMe();
Helps to "modularize" your workflow. You are also writing your revealing pattern as an IIFE. This can cause debugging headaches in the future. Editing the IIFE to instead invoke via the return is easier to maintain and for other devs to work with and learn initially. Also, it allows you to extend outside of your IFFE into another Module, example:
var Clothes = function() {
function anotherFunc() {
...
}
init() {
User.runMe();
anotherFunc();
}
return {
addClothes: init
}
}().addClothes();
I hope this helps to give you a better understanding of how/when/why to use the JS revealing pattern. Quick note: You can make your modules into IIFE, that's not a problem. You just limit the context of the scope you can work with. Another way of doing things would be to wrap the var User and var Clothes into a main module, and then make that an IIFE. This helps in preventing polluting your global namespace.
Example with what I wrote above:
// MAIN APPLICATION
var GettinDressed = (function() {
// MODULE ONE
///////////////////////////
Var User = function() {
// local VARS available to User
var addRoleForm = document.querySelector('#addUserRole');
var rolesTableBody = document.querySelector('#table-roles tbody');
// Helper function 1
function ajaxUpdate(tableName) {
...
}
// Helper function 2
function someFunc() {
...
}
function bindEvents() {
addRoleForm.addEventListener('submit', ajaxUpdate, false);
addRoleForm.addEventListener('click', someFunc, false);
}
function init() {
bindEvents();
}
return {
runMe:init,
style: someFunc
}
}();
// MODULE TWO
//////////////////////////
var Clothes = function() {
function anotherFunc() {
...
}
init() {
User.style();
anotherFunc();
}
return {
dressUp: init
}
}();
// Define order of instantiation
User.runMe();
Clothes.dressUp();
}());

How to update global object

Iam trying to run an external function inside nightmarejs evalute function...As you can see my code below...
function get_my_links(url){
vo(function* () {
var nightmare = Nightmare();
var href_link = []; // i have tried making it as global without var but did not work
var title = yield nightmare
.goto('https://examply/'+url)
.evaluate(function (href_link,url,get_my_links) {
$('.myclass').each(function() {
href_link.push($(this).attr("href"));
});
if($.isNumeric($("#someid").val()))
{
get_my_links(1)
}
else{
return href_link;
}
},href_link,url);
console.log(title);
yield nightmare.end();
})(function (err, result) {
if (err) return console.log(err);
});
}
get_my_links(0)
By above code I am trying to update href_link ...
1) How to make it Global object,so that everytime the function is called new value should be added with the existing values?
1st The reason
// i have tried making it as global without var but did not work
is not working because though you making the object global but every time you call get_my_links function, it will update the global object to empty array.
For your use case, define href_link before defining get_my_links function. Like
var href_link =[];
function get_my_links() {
...
}
Defining href_link after function definition like ->
function get_my_links() {
...
}
var href_link =[];
will throw an error of undefined value of href_link inside get_my_links function due to hoisting which must be the case you have mentioned in above comment.
electron uses node.js, so you can use the global object of node.js to store the value.
https://nodejs.org/api/globals.html#globals_global
When you use this solution you should be able to access the value also from other parts of your app.

How can rewrite function instead of reference?

var BigObject = (function() {
function deepCalculate(a, b, c) {
return a + b + c;
}
function calculate(x) {
deepCalculate(x, x, x);
}
return {
calculate: calculate,
api: {
deepCalculate: deepCalculate
}
}
})();
This is basic self executing function with private function I keep in api.
The problem I have is that now I can't overwrite deepCalculate from the outside of the function.
How is that a problem? I use Jasmine and want to test if function was called. For example:
spyOn(BigObject, 'calculate').andCallThrough();
expect(BigObject.api.deepCalculate).toHaveBeenCalled();
fails. However as I debug, I am sure that Jasmine binds BigObject.api.deepCalculate as a spy, however from the inside calculate still calls original deepCalculate function and not the spy.
I would like to know how can I overwrite the function and not just a reference for it.
The simple answer would be:
(function ()
{
var overWriteMe = function(foo)
{
return foo++;
},
overWrite = function(newFunc)
{
for (var p io returnVal)
{
if (returnVal[p] === overWriteMe)
{//update references
returnVal[p] = newFunc;
break;
}
}
overWriteMe = newFunc;//overwrite closure reference
},
returnVal = {
overWrite: overWrite,
myFunc: overWriteMe
};
}());
Though I must say that, I'd seriously think about alternative ways to acchieve whatever it is you're trying to do. A closure, IMO, should be treated as a whole. Replacing parts of it willy-nilly will soon prove to be a nightmare: you don't know what the closure function will be at any given point in time, where it was changed, what the previous state was, and why it was changed.
A temporary sollution might just be this:
var foo = (function()
{
var calc = function(x, callback)
{
callback = callback || defaultCall;
return callback.apply(this, [x]);
},
defaultCall(a)
{
return a*a+1;
},
return {calc: calc};
}());
foo(2);//returns 5
foo(2,function(x){ return --x;});//returns 1
foo(2);//returns 5 again
IMO, this is a lot safer, as it allows you to choose a different "internal" function to be used once, without changing the core behaviour of the code.

Store jquery function on variable

I have this code in .js file:
$.getJSON('http://api.wipmania.com/jsonp?callback=?', function (data) {
if (data.address.country='spain') {
var a="http://www.link1.com";
} else {
var a="http://www.link2.com";
}
return a;
});
var fan_page_url = data();
How can I store var a in var fan_page_url ?
Thank you very much.
Try getting rid of a and assigning the links directly.
var fan_page_url;
$.getJSON('http://api.wipmania.com/jsonp?callback=?', function(data) {
if (data.address.country = 'spain') {
fan_page_url = "http://www.link1.com";
} else {
fan_page_url = "http://www.link2.com";
}
});
You have two options: assigning the external variable directly, as depot suggested, or treating the $.getJSON return value as a Deferred:
$.when($.getJSON(...)).done(function(returnValue) {
fan_page_url = returnValue;
});
The latter is useful in case you don't want to hardcode the variable you'll store the results (though in general it's not a problem, and the former option is easier/cleaner).
It's an old question on which I just stumbled (looking for something slightly different) but as answers did not seem to hit the nail on the head, I thought I'd add 2 cents: if you (op) had success with a variable that was hardcoded and set manually in config.js, that you were able to grab from start.js, why not simply:
keep the variable declared like you did in the global scope
assign it a default or null or empty value:
var fan_page_url = null; // or
var fan_page_url = ''; // or
var fan_page_url = 'http://url_default'; // etc...
then update the global variable inside the json function:
$.getJSON('http://api.wipmania.com/jsonp?callback=?', function(data) {
if (data.address.country='spain') {
fan_page_url = "http://url1";
} else {
fan_page_url = "http://url2";
}
});
in your start.js page, you can always perform a check to see if the variable is set or not, or still carries it's default value or not and act accordingly...
It is likely that if it used to work with your normally, manually declared variable, it would work the same here as you would have changed nothing structurally, only would be updating the variable after the json response.
Answer posted for the posterity, it may help someone in the future.

Categories

Resources