Have a javascript function privately track it's number of calls
I'm trying to figure out how I can have a javascript function privately track the number of times it has been called. The objective is to be able to query this value in the console during debugging by doing func.run
My first attempt:
function asdf() {
if (!asdf.run) {
开发者_运维百科asdf.run = 0;
} else {
asdf.run++;
console.error('run: ' + asdf.run);
}
console.error('asdf.run def: ');
console.error(asdf.run);
}
asdf();
This is a good lesson of why one should ALWAYS aim to use
===
in nearly all javascript booleans, cause they could secretly be ==
Closures are the way to go here:
var asdf = (function () {
var runs = 0;
var f = function () {
++runs;
// your function here
};
f.runs = function () {
return runs;
};
return f;
}());
Usage:
asdf();
asdf();
asdf.runs(); // 2
asdf();
asdf.runs(); // 3
Or, you could use a mocking framework like (shameless self plug) Myrtle.
Your first try would work fine except you've forgotten that 0
is a "falsy" value in JavaScript, so on the first run and every run thereafter !this.run
will evaluate to true
and your else
block will never be reached. This is pretty easy to work around.
function foo() {
if(typeof(foo.count) == 'undefined') {
foo.count = 0;
} else {
foo.count++;
}
console.log(foo.count);
}
foo(); # => 0
foo(); # => 1
foo(); # => 2
# ...
I haven't actually tried this, but I looked up "static function variables in JavaScript", and I found this resource. I think the main difference between what you wrote and what's in that solution is how the first run of the function is detected. Perhaps your !asdf.run test is not working the way you thought it was, and you should use typeof asdf.run == 'undefined' to test instead.
OK, here is a method that I came up with that does not require the function to be modified at all.
So if you have this.
function someFunction() {
doingThings();
}
you could add a counter like this...
addCounter(this, "someFunction");
where this is the scope you are in, you could use any object that has a method you want to count.
Here's the code for it.
<html>
<head>
<script>
function someFunc() {
console.log("I've been called!");
};
// pass an object, this or window and a function name
function wrapFunction(parent, functionName) {
var count = 0, orig = parent[functionName];
parent[functionName] = function() {
count++;
return orig.call(this, Array.prototype.slice(arguments));
}
parent[functionName].getCount = function() {
return count;
};
}
var someObj = {
someFunc: function() {
console.log("this is someObj.someFunc()");
}
}
wrapFunction(this, "someFunc");
wrapFunction(someObj, "someFunc");
someFunc();
someObj.someFunc();
someObj.someFunc();
someObj.someFunc();
console.log("Global someFunc called " + someFunc.getCount() + " time" + (someFunc.getCount() > 1 ? "s" : "")) ;
console.log("Global someObj.someFunc called " + someObj.someFunc.getCount() + " time" + (someObj.someFunc.getCount() > 1 ? "s" : "")) ;
</script>
</head>
So, !asdf.run
is a form of the double equals operator ==
and I had set asdf.run
to 0
so it was false.
Using the triple equals ===
:
typeof asdf.run === "undefined"
for the boolean solves my issue.
So a final usable and useful version:
function sdf() {
if (typeof sdf.run === "undefined") { sdf.run = 0; }
sdf.run++;
}
To query the number of times sdf
has been called:
sdf.run;
To actually make this variable private and protect it from change, one would implement a closure.
//using a closure and keeping your functions out of the global scope
var myApp = (function() {
//counter is a private member of this scope
var retObj = {}, counter = 0;
//function fn() has privileged access to counter
retObj.fn = function() {
counter++;
console.log(counter);
};
//set retObj to myApp variable
return retObj;
}());
myApp.fn(); //count = 1
myApp.fn(); //count = 2
myApp.fn(); //count = 3
You don't necessarily need a closure. Just use a static variable.
var foo = function(){
alert( ++foo.count || (foo.count = 1) );
}
// test
function callTwice(f){ f(); f(); }
callTwice(foo) // will alert 1 then 2
or
callTwice( function bar(){
alert( ++bar.count || (bar.count = 1) );
}); // will alert 1 then 2
the second one is a named anonymous function. And note this syntax:
var foo = function bar(){ /* foo === bar in here */ }
精彩评论