admin管理员组

文章数量:1433496

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 ==

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 ==

Share Improve this question edited Sep 24, 2011 at 5:21 Devin Rhode asked Sep 24, 2011 at 4:57 Devin RhodeDevin Rhode 25.4k8 gold badges68 silver badges84 bronze badges 3
  • I think you forgot to include the output of your original function in your post :) – acjay Commented Sep 24, 2011 at 5:08
  • I have no idea which answer to accept. – Devin Rhode Commented Sep 24, 2011 at 5:27
  • I found this post helpful when learning about javascript coercion rules webreflection.blogspot./2010/10/… most of the rules make sense (I actually like that 0 and "" are falsey), but there are definately some areas that will trip you up – Matt Briggs Commented Sep 24, 2011 at 5:33
Add a ment  | 

7 Answers 7

Reset to default 4

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 */ }

本文标签: closuresHave a javascript function privately track it39s number of callsStack Overflow