Quantcast
Channel: HTML5
Viewing all articles
Browse latest Browse all 663

Static Javascript Patterns

$
0
0

So what do you do when you want the functionality of a static variable in Javascript? As previously discussed, using a global instead of a static is not a great idea. One might be tempted to use some sort of naming scheme to distinguish global vars, intended to be used as static, from others (e.g. '_static_i'), but that really doesn't solve the problem because two different functions might end up with similar static vars overwriting each other. Best to avoid global vars except when you really need global vars.

Dmitry was kind enough to post a suggestion along the lines of this:

function f(x) {
  if (typeof f.i === 'undefined') {
    f.i = 0;
  }
  return ++f.i+x;
}

There's a lot of interesting things about this code. First is the fact that, as he points out, everything is an object in javascript, including the function f, and so you can add attributes like 'f.i'. Another is the use of the typeof operator and 'undefined', which is a nice trick to avoid causing an exception. All of this is fodder for a future blog post, but what I want to focus on for now is solving the static variable problem.

Dmitry's suggestion is a solid one. It meets all the requirements. The static variable is initialized once, in this case the first time the function is called. It retains its value from one call to the next. As to the scope, well, that's an interesting question which I'll table for the moment.

The drawbacks are, to my mind, mostly asthetic. The main one for me is that I have to prepend '<code>f.</code>' at the beginning of what would normally be my variable name. I suppose if I learned to use a sophisticated modern editor I'd hardly notice, but being an old geek I'm stuck on 'vi' (aka 'vim') and have to type nearly every character. Also, my years of performance analysis have made me skittish where branches are concerned, though in reality in Javascript there's so much branching going on under the hood that I doubt another 'if' at the beginning of the function will matter. But there's still that question of scoping.

While 'i' is associated with function 'f', naming a global variable 'i' won't interfere with it, but it turns out that it is still accessible outside the scope of the function. I can still refer to 'f.i' and both read and update it:

> f(0)     // i = 1
1
> f(33)    // i = 2
35
> f(33)    // i = 3
36
> f.i=-25  // i = -25
-25
> f(0)     // i = -24
-24
> f(33)    // i = -23
10
>

Now, it's harder to accidentally overwrite this, so this is still a useful approach for well behaved programs, and indeed the ability to update 'i' could be used properly in the right circumstances, but there is another approach that avoids this issue altogether.

One aside first. When you learn Javascript, at least the way I learned it, one of the first things you learn is how to declare a function:

function f(x) { return x+1 }

That's all fine and good, but it turns out that Javascript also allows you to declare "Anonymous" functions, which don't have a name. Many languages have a similar feature, sometimes called "Lambda functions", a term dating back to that hallowed ancestor of all functional languages, "Lisp", which borrowed the term from "Lambda Calculus", but I digress. What advantage is there in an anonymous function? Well it makes naming a lot easier :-). Generally it's used in places where you don't need a name for it. You'll see it very often in Javascript as a slightly different way of defining a function:

var f = function (x) {return x+1}

For most purposes this is the same as the standard function declaration, but it does help one think outside the box (at least if you're an old C programmer whose tidy box may be showing it's age). In our case, we can use an anonymous function that is only called once to hold the scope of our "static" variable, but then from that (which is really an initializer of sorts) we can return the real function we're interested in. I can't take credit for this idea, but we end up with something like this:

var f = function () {
  var i = 0

  return function (x) {
    return ++i+x;
  }
}()

If you're like me, you need to look at this a few times before fully grokking it, so observe carefully. There are actually two functions being declared here. At first it looks like f is being assigned the outer anonymous function, that is until you notice the seemingly misplaced pair of parentheses at the end of the function declaration. This creates an anonymous function, then immediately calls it. That function's sole purpose is to initialize the "static" variable 'i' and return the inner function that really does the work we're interested in. That inner function is what gets assigned to 'f', so when we call 'f', we provide a parameter 'x' and it increments 'i', adds it to 'x' and returns the result. 'i' only exists inside the scope of the initializer function, which is no longer accessible from anywhere, therefore no one can mess with 'i'. Even if the initializer were accessible, the variable 'i' isn't a property of 'f', so it's not something that anyone outside of 'f' can mess with.

Now you might guess that this and similar techniques could be used for all kinds of language features, and you'd be right. If you poke around Javascript code on the web, you'll see patterns like this all over the place. If you're like me you may have done a fair bit of head scratching to figure out why one would use such a roundabout method of declaring a function, now you may start to see why.

  • javascript
  • JavaScript Tutorial
  • Javascript Patterns
  • static
  • html5
  • 图标图像: 


    Viewing all articles
    Browse latest Browse all 663


    <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>