I'm writing a counter to count an object, and it looks like this:
function myFunc(param) {
this.param = param;
param.foo = function() {
var object = window.JSON.parse(data);
for (i in object) {
counter++;
}
}
}
var foo = new myFunc('data.json');
var counter = 0;
document.write(counter); // displays 0
How can I achieve to get the counter value outside the function? I tried almost everything, from window to return to separate functions.
Any clue?
Update
I prefer a better design like this
function myFunc(param) {
this.param = param;
param.foo = function() {
var object = window.JSON.parse(data);
var counter = 0;
for (i in object) {
counter++;
}
return counter;
}
}
var foo = new myFunc('data.json');
document.write(counter); // displays undefined
Update 2
Sorry, thought it would be easier to have a sample code. But here's the real one: https://gist.github.com/BobWassermann/e709ec303477a015b609
I think you have a couple issues here.
First, you're setting your counter to 0 just before you write. It will always be 0 no matter what you do, even with hoisting.
Second, you never call the foo function, so your counter is never incremented.
Third, param.foo isn't public. I think you want it to be this.foo = function(){ ... }.
Here's a simplified version of the code you posted with my tweaks:
var counter = 0;
var foo;
function myFunc() {
this.foo = function() {
counter = 1000;
}
}
foo = new myFunc();
foo.foo();
document.write(counter);
JSFiddle: http://jsfiddle.net/dgrundel/2ojw2332/2/
Note that JSFiddle doesn't allow document.write, so replaced that part.
function myFunc(param) {
this.param = param;
this.foo = function () {
var object = window.JSON.parse(this.param),
counter = 0,
i;
for (i in object) {
counter++;
}
return counter;
};
}
var foo = new myFunc('{"a":99}');
out(foo.foo());
function out(s) {
document.getElementById('out').innerHTML = '<pre>' + s + '</pre>';
}
<div id="out"></div>
As #Nina Scholz pointed out earlier, I'm retrieving the data asynchron. Javascript started painting the dom before all the values where loaded.
This fixed my problem:
if (document.readyState) {
setTimeout(function() {
var objLen = Object.keys(obj).length;
console.log(objLen);
}, 100);
}
I'm waiting for the document to be ready, then add an additional timeout as buffer.
Related
I want the array to call one random variable from it but it calls every variable in it and I don't know why.
Code:
var atk1 = Billy.giveLife("self");
var atk2 = Billy.punch("self");
var attack = [atk1,atk2];
function test(){
var r = Math.round(Math.random()*(attack.length-1));
attack[r];
}
test();
On your first 2 lines you are immediately calling the functions. attack[r] does nothing, square braces are only for selecting an element from an array.
Here’s one solution to what you might be trying to do, using anonymous arrow functions to store your functions to be called later:
var atk1 = () => Billy.giveLife("self");
var atk2 = () => Billy.punch("self");
var attack = [atk1,atk2];
function test(){
var r = Math.round(Math.random()*(attack.length-1));
attack[r]();
}
test();
Okay, I think you are missing braces '()' so I did a little prototype for you, here it is:
http://jsfiddle.net/pvkovalev/e75nx3rn/
I modified your code a little bit to make more visual:
function printf(data) {
$('#out').html($('#out').html() + '<br/>' + data);
}
var atk1 = function() {
printf('atk1');
}
var atk2 = function() {
printf('atk2');
}
var atk3 = function() {
printf('atk3');
}
var atk4 = function() {
printf('atk4');
}
var atk5 = function() {
printf('atk5');
}
var attack = [atk1, atk2, atk3, atk4, atk5];
function test() {
var r = Math.round(Math.random() * (attack.length - 1));
attack[r]();
}
setInterval(function() {
test();
}, 1500)
I am executing closure example, Please check the code below
function Counter()
{
var count=0;
var counterIncrement=function()
{
count++;
console.log(count);
}
return counterIncrement;
}
var v=Counter();
v.counterIncrement();
Error: Please explain
"message": "Uncaught TypeError: v.counterIncrement is not a function",
"filename": "https://stacksnippets.net/js",
"lineno": 26,
"colno": 3
Updated:
This is an extended answer requested by OP in comment.
So OP was asking what if you have 2 functions within it. How would you call it? Easy, just return an object and assign the function handlers to a property each.
Example:
function Counter()
{
var count=0;
var counterIncrement = function()
{
count++;
console.log(count);
}
var increment = function(value) {
count += value;
console.log(count);
}
// return as an object with reference to the functions
return {
counterIncrement : counterIncrement,
increment : increment
}
}
var v= Counter();
v.counterIncrement();
v.increment(100);
---- Previous Answer ------
When you execute var v=Counter();, essentially you are executing the function Counter() and assigning its return value to v.
In this case, Counter() would return a reference to the function counterIncrement.
So variable v now holds a callable function named counterIncrement(). When you do v.counterIncrement(), your javascript engine sees the code as counterIncrement.counterIncrement(), which gets resolved into an undefined property. This produces the error message that you were seeing.
Calling just v() is sufficient.
function Counter()
{
var count=0;
var counterIncrement=function()
{
count++;
console.log(count);
}
return counterIncrement;
}
var v= Counter();
v();
Try like this:
var counter = (function() {
var count = 0;
function countval(val) {
count += val;
}
return {
counterIncrement: function() {
countval(1);
},
counterDecrement: function() {
countval(-1);
},
value: function() {
return count;
}
};
})();
console.log(counter.value());
counter.counterIncrement();
console.log(counter.value());
counter.counterDecrement();
console.log(counter.value());
If you dont want like this you can call v(); instead of v.counterIncrement();..So code would be
var v=Counter();
v();
you can slightly modify your code for create an object of Counter then you can access the function like myObject.counterIncrement()
function Counter()
{
var count=0;
this.counterIncrement = function() //used 'this' for create an object method which can access outside
{
count++;
console.log(count);
}
//return counterIncrement; //no need to return this method,
}
var v = new Counter();//used 'new' keyword to create an object of 'counter'
v.counterIncrement();//access ojbect method.
v.counterIncrement();//out put 2
v.counterIncrement();//out put 3
function Counter()
{
var count=0;
var counterIncrement=function()
{
count++;
console.log(count);
}
return counterIncrement;// Here you are returning function
}
var v=Counter();
//v.counterIncrement();// This will not call like that
//Directly call
v();
Answer as you like is as following
var counter = (function() {
var count = 0;
return {
counterIncrement: function() {
count++;
console.log(count);
},
};
})();
I am trying to call another local function 'MessageScoring' within another function 'NextMessageCalculation' which are both in module.exports, however I am having trouble with getting the scope to change back. The function successfully calls the other local function, however the scope is then changed and variables in the previous scope all return undefined now. How would I fix this or is there a better way to call the local function to avoid this?
module.exports = {
MessageScoring: function(){
var attrs_list = Object.keys(message_attr_obj);
var score = 0;
for (i in attrs_list){
if(user_data[attrs_list[i]]){
score = score + user_data[attrs_list[i]]["beta"];
}
else{
//user has no response with that attr yet so score is not added
}
}
score = score / attrs_list.length;
return score}... //returns an int
NextMessageCalculation: function(){ //has a call to a mongodb and the logic takes place in the callback
MESSAGE_collection.find({'message_set':user_data.message_set,'id':{$nin:[1111]}}).toArray(function(err,doc){
if (doc.length === 0)
{
USERDATA_collection.update({phone:phone_num},{$set:{send_message:false}},function(err,res) {
db.close()
});
}
else{
var highest_score = "";
var next_message = "";
for (i in doc)
{
console.log(doc[i]['id']);
var score = module.exports.MessageScoring(doc[i].attr_list,user_data);
console.log(doc[i]['id']); <---- becomes undefined here after calling scoring function which correctly returns int
if (highest_score === "")
{
console.log(doc[i]);
next_message = doc[i].id;
highest_score = score;
}
if (highest_score < score)
{
next_message = doc[i].id
highest_score = score;
}
console.log(doc[i].id);
}
Use function hoisting, step out from the modules.expors closure, and be sure that MessageScoring doesn't mutate data.
module.exports = {
MessageScoring,
NextMessageCalculation:
}
function MessageScoring() {
}
function NextMessageCalculation(){
const d = [{ id: 'ok' }];
const i = 0;
console.log(d[i]['id']); //returns ok
var score = MessageScoring(doc[i].attr_list,user_data);
console.log(d[i]['id']); //returns ok
}
var add = (function () {
var counter = 0;
return function () {
var reset = function() {
counter = 0;
}
return counter += 1;
}
})();
This is a self-invoking function that creates a "private" variable. How would I create a function reset that will reset the counter to 0? I've tried declaring the function inside the closure, but when I call it using add.reset(), it tells me this method is undefined.
You should return the reset function as a method of the object returned by the IIFE. That object needs to be the add function, so put the reset method on it. Just do it like you would in a global context, but inside a closure and return the add function, e.g.:
var add = (function(){
var counter = 0;
function add(n) {
counter += n || 0;
return counter;
}
add.reset = function(){
counter = 0;
return counter;
}
return add;
}())
console.log(add(1)) // 1
console.log(add(4)) // 5
console.log(add.reset()); // 0
However, it would make more sense (to me) to have a counter object that has add and reset methods.
I would recommend that instead of trying to put the function inside your closure, you put your variable outside your closure, like this:
var counter = 0;
var add = function() {
return counter += 1;
};
var reset = function() {
counter = 0;
};
That way the variable has proper scope for what you are trying to accomplish with it.
If you want to explicitly keep the counter declared inside the closure, you need to declare reset (even if you don't give it a value) outside the closure. To use your code, it would look like this:
var reset;
var add = (function () {
var counter = 0;
return function () {
reset = function() {
counter = 0;
}
return counter += 1;
}
})();
Now reset is outside the scope of the add function, so it keeps the value assigned within it!
To be fair, though, there's no reason to assign reset every time you can the result of add... It might be better to do something like:
var reset;
var add = (function () {
var counter = 0;
reset = function() {
counter = 0;
}
return function () {
return counter += 1;
}
})();
Or better still, if you want add.reset() to work:
var counter = function () {
var counter = 0;
this.reset = function() {
counter = 0;
}
this.add = function () {
return counter += 1;
}
};
var add = new counter();
Then add is a full object, which more or less sounds like what you want.
Or if you want to stick with the self invoking function:
var add = (function () {
var counter = 0;
return function () {
this.reset = function() {
counter = 0;
}
return counter += 1;
}
})();
Would probably work. It would be a slightly unusual paradigm from what I've seen though...
If you would like to keep the privacy of your current count, here is an answer that uses an object:
function counter() {
var count = 0;
this.reset = function() {
count = 0;
return count;
};
this.add = function() {
return ++count;
};
}
Then for instance:
var counter1 = new counter();
counter1.add();
console.log(counter1.add());
console.log(counter1.reset());
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