One function to recieve variables from many other functions - javascript

I 'm dealing an issue with some functions in javascript.
So i have this example
function(something) {
var example = 1;
var example2 = 2;
NameReturn(nameBusinessID, function(no) {
console.log(no);
//here is one callback value from another function
});
typeReturn(object, function(na) {
console.log(na);
//here is also a callback value from another function
});
view(example, example2);
}
function vliew(example, example2) {
console.log(example, example2);
//but here i want also to console.log the variables "no" and "na"
}
So this is my issue, is there any way to achieve what i want??
I have no idea how to make those two variables, "no" and "na" to pass them to the function "view"
Could anyone help? Thanks!

the dirty way
function(something) {
var example = 1;
var example2 = 2;
NameReturn(nameBusinessID, function(no) {
console.log(no);
//here is one callback value from another function
typeReturn(object, function(na) {
console.log(na);
//here is also a callback value from another function
view(example, example2,no, na);
});
});
}
function vliew(_example, _example2,_no,_na) {
console.log(_example)
console.log(_example2)
console.log(_no)
console.log(_na)
}
UPDATE
I have slightly modified your use case and implimented again.you can check code in this .
i am listening for button click callbacks and updating values in common object.once all four values are available,im calling the view function.
click both buttons and you will see result.
function collector() {
this.counter = 0;
this.obj = {};
this.setValues = function(name, value) {
this.counter++;
this.obj[name] = value;
if (this.counter == 4) {
view(this.obj.example, this.obj.example2, this.obj.no, this.obj.na);
}
};
}
function view(example, example1, a, b) {
$('.result').append('example :' + example + '<br>');
$('.result').append('example2 :' + example1 + '<br>');
$('.result').append('na :' + a + '<br>');
$('.result').append('no :' + b + '<br>');
}
function load() {
var example = 1;
var example2 = 2;
var valueCollector = new collector();
$('.no').on('click', function() {
var no = 3;
valueCollector.setValues('no', no);
});
$('.na').click(function() {
var na = 4;
valueCollector.setValues('na', na);
});
valueCollector.setValues('example', example);
valueCollector.setValues('example2', example2);
}
load();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="no">no</button>
<button class="na">na</button>
<div class="result"></div>

Related

Can we read real time value of parameters passed to callback function using “.apply”?

In my scenario, I have callback function that accepts some parameters (0 or more). I've declared an object global variable and passing this to my callback function and later in some part of code changing its value, but when I click on okButton (mapped to callback function) then function gets called but parameter is coming as undefined. Does anybody have any clue what is wrong in this approach?
HTML code:
<button id="bindEvents" onclick="BindEvents()">Bind Events</button>
<button id="okButton">Click Me</button>
Some application page:
var tempVariable = {};
function BindEvents() {
tempVariable.item1 = 100;
tempVariable.item2 = 200;
tempVariable.item3 = 300;
tempVariable.item4 = 400;
tempVariable.item5 = 500;
}
function callBackFunction(param) {
var size = Object.keys(param).length;
if (size > 0) {
for (var i = 0; i < size; i++) {
alert((i + 1) + ' parameter value: ' + param[i]);
}
}
}
testMe({
okButtonCallback: callBackFunction,
okButtonCallbackParameters: tempVariable
});
Dynamic.js
var testMe = function (properties) {
$(document).ready(function () {
$(document).on('click', '#okButton', function () { properties.okButtonCallback.apply(this, properties.okButtonCallbackParameters);
});
});
};
Two changes required to make it working.
Dynamic.js
Based on the suggestion from #Bravo and #Robin Zigmond, I've replaced .apply with .call.
var testMe = function (properties) {
$(document).ready(function () {
$(document).on('click', '#okButton', function () {
properties.okButtonCallback.call(this, properties.okButtonCallbackParameters);
});
});
};
Some application page: Changed the way I was reading parameter
function callBackFunction(param) {
var size = Object.keys(param).length;
if (size > 0) {
var valuesArray = Object.values(param)
for (var i = 0; i < size; i++) {
console.log((i + 1) + ' parameter value: ' + valuesArray[i]);
}
}
}

Get object caller name by function call JavaScript

I'm writing a piece of code to easily save error logs in an object for debugging.
What I'm trying to achieve is to get the Object name from the function it was called from like so:
var MainObject = {
test : function() {
return MainObject.test.caller;
// When called from MainObject.testcaller,
// it should return MainObject.testcaller.
},
testcaller : function() {
return MainObject.test(); // Should return MainObject.testcaller, Returns own function code.
},
anothercaller : function() {
return MainObject.test(); // Should return MainObject.anothercaller, Returns own function code.
}
}
However when I run this code it returns the function code from MainObject.testcaller.
JSFiddle example
Is there any way this is possible?
Update
After looking at Rhumborl's answer, I discovered that assigning the value through another function would lead it to point back at the function name without the object itself.
Code:
(function (name, func) {
MainObject[name] = func;
})('invalid', function() {
return MainObject.test("blah");
});
// This now points at invalid() rather than MainObject.invalid()
Updated fiddle
There is a non–standard caller property of functions that returns the caller function, however that is a pointer to a function object and doesn't tell you the object it was called as a method of, or the object's name. You can get a reference to the function through arguments.callee.
There is also the obsolete arguments.caller, but don't use that. It also provides a reference to the calling function (where supported).
Once you have a reference to the calling function (if there is one), you then have the issue of resolving its name. Given that Functions are Objects, and objects can be referenced by multiple properties and variables, the concept of a function having a particular name is alluvial.
However, if you know that the function is a property of some object, you can iterate over the object's own enumerable properties to find out which one it is.
But that seems to be a rather odd thing to do. What are you actually trying to do? You may be trying to solve a problem that can be worked around in a much more robust and simpler way.
Edit
You can do what you want in a very limited way using the method described above for the case in the OP, however it is not robust or a general solution:
var mainObject = {
test : function() {
var obj = this;
var caller = arguments.callee.caller;
var global = (function(){return this}());
var fnName, objName;
for (var p in global) {
if (global[p] === obj) {
objName = p;
}
}
for (var f in obj) {
if (obj[f] === caller) {
fnName = f;
}
}
return objName + '.' + fnName;
},
testcaller : function() {
return mainObject.test();
},
anothercaller : function() {
return mainObject.test();
}
}
console.log(mainObject.testcaller()); // mainObject.testcaller
console.log(mainObject.anothercaller()); // mainObject.anothercaller
but it's brittle:
var a = mainObject.anothercaller;
console.log(a()); // mainObject.anothercaller
var b = {
foo : mainObject.anothercaller
}
console.log(b.foo()); // mainObject.anothercaller
Oops.
You can use this trick at http://www.eriwen.com/javascript/js-stack-trace/ which throws an error, then parses the stack trace.
I have updated it for the latest versions of Firefox, Chrome and IE. Unfortunately it doesn't work well on my IE9 (and I haven't tested it on Opera).
function getStackTrace() {
var callstack = [];
var isCallstackPopulated = false;
try {
i.dont.exist += 0; //doesn't exist- that's the point
} catch (e) {
if (e.stack) { //Firefox/Chrome/IE11
var lines = e.stack.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
var line = lines[i].trim();
if (line.match(/^at [A-Za-z0-9\.\-_\$]+\s*\(/)) {
// Chrome/IE: " at Object.MainObject.testcaller (url:line:char)"
var entry = line.substring(3, line.indexOf('(') - 1);
// Chrome appends "Object." to the front of the object functions, so strip it off
if (entry.indexOf("Object.") == 0) {
entry = entry.substr(7, entry.length);
}
callstack.push(entry);
} else if (line.match(/^[A-Za-z0-9\.\-_\$]+\s*#/)) {
// Firefox: "MainObject.testcaller#url:line:char"
callstack.push(line.substring(0, lines[i].indexOf('#')));
}
}
//Remove call to getStackTrace()
callstack.shift();
isCallstackPopulated = true;
} else if (window.opera && e.message) { //Opera
var lines = e.message.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
var entry = lines[i];
//Append next line also since it has the file info
if (lines[i + 1]) {
entry += lines[i + 1];
i++;
}
callstack.push(entry);
}
}
//Remove call to getStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
}
if (!isCallstackPopulated) { //IE9 and Safari
var currentFunction = arguments.callee.caller;
while (currentFunction) {
var fn = currentFunction.toString();
var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf('')) || 'anonymous';
callstack.push(fname);
currentFunction = currentFunction.caller;
}
}
return callstack;
}
var MainObject = {
test: function (x) {
// first entry is the current function (test), second entry is the caller
var stackTrace = getStackTrace();
var caller = stackTrace[1];
return caller + "()";
},
testcaller: function () {
return MainObject.test(1, null);
}
}
function SomeFunction() {
return MainObject.test("blah");
}
document.body.innerHTML += '<b style="color: red">' + MainObject.testcaller() + '</b>';
document.body.innerHTML += '<div>Calling SomeFunction() returns: <b style="color: red">' + SomeFunction() + '</b></div>';
MainObject.test() should return: <b style="color: blue">MainObject.testcaller()</b>
<hr />
MainObject.test() returns:
Updated fiddle here

Mutable variable is accessible from closure. How can I fix this?

I am using Typeahead by twitter. I am running into this warning from Intellij. This is causing the "window.location.href" for each link to be the last item in my list of items.
How can I fix my code?
Below is my code:
AutoSuggest.prototype.config = function () {
var me = this;
var comp, options;
var gotoUrl = "/{0}/{1}";
var imgurl = '<img src="/icon/{0}.gif"/>';
var target;
for (var i = 0; i < me.targets.length; i++) {
target = me.targets[i];
if ($("#" + target.inputId).length != 0) {
options = {
source: function (query, process) { // where to get the data
process(me.results);
},
// set max results to display
items: 10,
matcher: function (item) { // how to make sure the result select is correct/matching
// we check the query against the ticker then the company name
comp = me.map[item];
var symbol = comp.s.toLowerCase();
return (this.query.trim().toLowerCase() == symbol.substring(0, 1) ||
comp.c.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1);
},
highlighter: function (item) { // how to show the data
comp = me.map[item];
if (typeof comp === 'undefined') {
return "<span>No Match Found.</span>";
}
if (comp.t == 0) {
imgurl = comp.v;
} else if (comp.t == -1) {
imgurl = me.format(imgurl, "empty");
} else {
imgurl = me.format(imgurl, comp.t);
}
return "\n<span id='compVenue'>" + imgurl + "</span>" +
"\n<span id='compSymbol'><b>" + comp.s + "</b></span>" +
"\n<span id='compName'>" + comp.c + "</span>";
},
sorter: function (items) { // sort our results
if (items.length == 0) {
items.push(Object());
}
return items;
},
// the problem starts here when i start using target inside the functions
updater: function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, target.destination);
return item;
}
};
$("#" + target.inputId).typeahead(options);
// lastly, set up the functions for the buttons
$("#" + target.buttonId).click(function () {
window.location.href = me.format(gotoUrl, $("#" + target.inputId).val(), target.destination);
});
}
}
};
With #cdhowie's help, some more code:
i will update the updater and also the href for the click()
updater: (function (inner_target) { // what to do when item is selected
return function (item) {
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
return item;
}}(target))};
I liked the paragraph Closures Inside Loops from Javascript Garden
It explains three ways of doing it.
The wrong way of using a closure inside a loop
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Solution 1 with anonymous wrapper
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
Solution 2 - returning a function from a closure
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
Solution 3, my favorite, where I think I finally understood bind - yaay! bind FTW!
for(var i = 0; i < 10; i++) {
setTimeout(console.log.bind(console, i), 1000);
}
I highly recommend Javascript garden - it showed me this and many more Javascript quirks (and made me like JS even more).
p.s. if your brain didn't melt you haven't had enough Javascript that day.
You need to nest two functions here, creating a new closure that captures the value of the variable (instead of the variable itself) at the moment the closure is created. You can do this using arguments to an immediately-invoked outer function. Replace this expression:
function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, target.destination);
return item;
}
With this:
(function (inner_target) {
return function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
return item;
}
}(target))
Note that we pass target into the outer function, which becomes the argument inner_target, effectively capturing the value of target at the moment the outer function is called. The outer function returns an inner function, which uses inner_target instead of target, and inner_target will not change.
(Note that you can rename inner_target to target and you will be okay -- the closest target will be used, which would be the function parameter. However, having two variables with the same name in such a tight scope could be very confusing and so I have named them differently in my example so that you can see what's going on.)
In ecmascript 6 we have new opportunities.
The let statement declares a block scope local variable, optionally initializing it to a value.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Since the only scoping that JavaScript has is function scope, you can simply move the closure to an external function, outside of the scope you're in.
Just to clarify on #BogdanRuzhitskiy answer (as I couldn't figure out how to add the code in a comment), the idea with using let is to create a local variable inside the for block:
for(var i = 0; i < 10; i++) {
let captureI = i;
setTimeout(function() {
console.log(captureI);
}, 1000);
}
This will work in pretty much any modern browser except IE11.

JavaScript building JSON asynchronous and dynamicall

I have some trouble with asynchronous JavaScript. I call a function within a jQuery AJAX call, and in this function there are probably other asynchronous methods calls. I stuck in there on the moment.
Here I have the code snippet which is called by the jQuery AJAX function: Here I build dynamically a JSON object.
function getJSONObjektList() {
//MainJSON
var jsonObjekt = {};
jsonObjekt.ObjektId = [];
jsonObjekt.Selected = [];
doc = XYZ.GetCurrentDocument();
//probably also an asynchrounous call
doc.GetAllObjects(function (objects) {
for (var i = 0; i < objects.length; i++) {
var obj = objects[i];
var id = obj.id;
var caption = obj.caption;
var type = obj.type;
var my = obj.my;
console.log("[obj:" + obj + " id:" + id + " caption:" + caption + " type:" + type + " my: " + my + "]");
//liste alle verfuegbaren Objekte auf
jsonObjekt.ObjektId.push(id);
if (type === "Statusbox") {
doc.GetObject(id, function () {
var statusboxInhalt = this.Data.Rows;
//inner JSON object
var utilJSONObjekt;
for (var j = 0; j < statusboxInhalt.length; j++) {
// make sure to re-initialize so we don't update the same reference
utilJSONObjekt = {};
utilJSONObjekt.SelectedObjektId;
utilJSONObjekt.SelectedObjektWerte = [];
var inhalt = statusboxInhalt[j];
console.log("Name: " + inhalt[0].text + " Wert: " + inhalt[2].text);
utilJSONObjekt.SelectedObjektId = inhalt[0].text;
var valAr = inhalt[2].text.split(",");
for (var k = 0; k < valAr.length; k++) {
utilJSONObjekt.SelectedObjektWerte.push($.trim(valAr[k]));
}
jsonObjekt.Selected.push(utilJSONObjekt);
//**till here is the jsonObject not null or empty, there are some values in there**
}
});
}
}
});
//**but on the return statment is the jsonObjekt empty**
return jsonObjekt;
}
Have someone some tips how can I solve my problem, or how can I make JavaScript best asynchronously working.
Yeah, simply use callback pattern:
function getJSONObjektList(callback) { // <--- note callback
// asynchronous code starts here...
for (var k = 0; k < valAr.length; k++) {
utilJSONObjekt.SelectedObjektWerte.push($.trim(valAr[k]));
}
jsonObjekt.Selected.push(utilJSONObjekt);
callback(jsonObjekt);
// ...and ends here
}
and in your code you can use it like that:
getJSONObjektList(function(jsonObjekt) {
console.log(jsonObjekt);
// other code
});
EDIT Here's an example of two solutions to the same problem. One with callback pattern and one without it:
no callback
var add = function(x,y) {
return x+y;
};
var x = 1;
var sum = add(x, 1);
var sum2 = add(sum, 1);
console.log(sum2);
callback
var add = function(x,y,callback) {
callback(x+y);
}
var x = 1;
add(x, 1, function(sum) {
add(sum, 1, function(sum2) {
console.log(sum2);
});
});
Obviously the callback pattern is more messy, but if you are dealing with asynchronous operations then you absolutely need it, for example:
var add = function(x, y, callback) {
// add numbers after 2 seconds
setTimeout(function() {
callback(x+y);
}, 2000);
}
add(1, 2, function(sum) {
console.log(sum);
// will produce 3 after 2 seconds
// you can continue your code here
});
The idea is to pass a function which will be called after the asynchronous operation is done.

replace class value with javascript

I would like to replace abcName with xyzName?
<tr>
<td>
<b class="abcName">Bob</b>
</td>
</tr>
Using this, but on page load nothing changes:
var bobo = document.getElementsByTagName("td");
function user(a, b, c) {
try {
if (bobo[c].innerHTML.match('>' + a)) {
bobo[c].className = b;
}
} catch (e) {}
}
function customs() {
for (i = 0; i < bobo.length; i++) {
classChange("Bob", "xyzName", i);
}
}
setInterval("customs()", 1000);
Although I'm not real sure if what you're doing with the interval and whatnot is the best approach, you can:
var tidi = document.getElementsByTagName("td");
function changeStyleUser(a, b, c) {
try {
if (tidi[c].children[0].innerHTML.indexOf(a) === 0) {
tidi[c].className = b;
}
} catch (e) {}
}
function customizefields() {
for (i = 0; i < tidi.length; i++) {
changeStyleUser("Bob", "xyzName", i);
}
}
setInterval("customizefields()", 1000);
http://jsfiddle.net/zBmjb/
Note, you also had the wrong function name in your for loop as well.
If you use jQuery, this would be MUCH simpler.
EDIT
Using jQuery:
function customizefields(a) {
$('td b').each(function(){
if ($(this).text().indexOf(a) === 0) {
this.className = 'xyzName';
}
});
}
setInterval(function(){customizefields('Bob')}, 1000);
http://jsfiddle.net/zBmjb/1/
Also, note the use of the anonymous function instead of using a string in setInterval(). This allows you to not use eval(), which is considered expensive and potentially harmful.
EDIT 2
If you wanted to pass in a list of name and class associations, you could use an array with objects, like so:
function customizefields(arrNames) {
$('td b').each(function(){
for (var i = 0; i < arrNames.length; i++) {
if ($(this).text().indexOf(arrNames[i].name) === 0) {
this.className = arrNames[i].class;
}
}
});
}
var namesToChange = [
{'name':'Bob','class':'Bob'},
{'name':'Bert','class':'Bert'},
{'name':'Jeff','class':'Jeff'}
];
setInterval(function(){customizefields(namesToChange)}, 1000);
http://jsfiddle.net/zBmjb/4/
It feels messy though, since it searches for all selected nodes, then for each found, loops over the current node for each name looking for a matched value, all while doing this once a second. The cleaner approach would be to see if the name value from the current node was found:
function customizefields(objNames) {
$('td b').each(function(){
name = $(this).text();
if (name.indexOf(" ") != -1) {
name = name.substring(0, name.indexOf(" "));
}
if (objNames[name]) {
this.className = objNames[name];
}
});
}
var namesToChange = {
'Bob':'Bob',
'Bert':'Bert',
'Jeff':'Jeff'
};
setInterval(function(){customizefields(namesToChange)}, 1000);
http://jsfiddle.net/zBmjb/5/
EDIT 3
If you need multiple values, you can make the value for the object an object as well.
function customizefields(objNames) {
$('td b').each(function(){
name = $(this).text();
if (name.indexOf(" ") != -1) {
name = name.substring(0, name.indexOf(" "));
}
if (objNames[name]) {
this.className = objNames[name].class;
this.style.backgroundImage = "url(" + objNames[name].img + ")";
}
});
}
var namesToChange = {
'Bob':{'class':'Bob','img':'bobspic.jpg'},
'Bob':{'class':'Bert','img':'Bertphoto.jpg'},
'Bob':{'class':'Jeff','img':'jeff.jpg'}
};
setInterval(function(){customizefields(namesToChange)}, 1000);
Grab the element you want to change (can do this multiple ways, in this example i used ID).
var x = document.getElementById( 'id-of-my-element' );
x.className = 'b';
To remove a class use:
x.className = x.className.replace(/\bCLASSNAME\b/,'');
Hope this helps.
Change this line:
classChange("Bob", "xyzName", i);
to this:
changeStyleUser("Bob", "xyzName", i);
Your script will work fine then. Do yourself a favor and use a debugger. :-)
if (tidi[c].innerHTML.search("class=\"abcName\"")>=0) {
tidi[c].children[0].className = b;
}

Categories