(翻译) Poor misunderstood 'var'
It seems most programmers coming to JavaScript from C, C++, Java, and the like equate the var statement with variable declaration statements in the languages they come from and use it the same way. And at some casual level that's reasonable; but it can lead you down a misleading path...
很多JS的程序员来自C,C++,Java,他们喜欢将var statement和他们语言中的变量声明语句等同起来,有时候好像没问题,但其实这种做法有时会给你带来坑…
Consider this code:
function foo()
{
var ar;
// ...do some stuff, create an array
in 'ar'...
for (var index = 0; index <
ar.length; ++index)
{
doSomethingWith(ar[index]);
}
}
This is a common idiom, but a misleading one. You might think that index is only defined within the for loop (that's certainly the impression we're giving in the code). But it's not true: In fact, index is defined throughout the function -- within the loop, outside the loop, above the loop, and below the loop. The var statement defines a variable within the current scope (all of it, not just "from here on"), and unlike some other languages, in JavaScript blocks don't have any effect on scope; only functions introduce a new scope.
这段代码中,你可能认为index只是定义在for 循环里了,但并不是这样,实际上,index被定义在整个函数里了,在loop内,在loop外,在loop上,在loop下… var statement在当前的scope内声明了一个变量, 和其他语言不一样,Javascript中 blocks 对scope没有什么影响,只有函数会创建新的scope
Consequently, the above function can be
written as it is above, but also with the index declaration...
所以,上面的函数可以写成下面这样..
function foo()
{
var ar;
var index;
// ...do some stuff, create an array
in 'ar'...
for (index = 0; index < ar.length;
++index)
{
doSomethingWith(ar[index]);
}
}
...at the bottom:
function foo()
{
var ar;
// ...do some stuff, create an array
in 'ar'...
for (index = 0; index < ar.length;
++index)
{
doSomethingWith(ar[index]);
}
var index;
}
...anywhere in the middle:
function foo()
{
var ar;
// ...do some stuff, create an array
in 'ar'...
for (index = 0; index < ar.length;
++index)
{
var index;
doSomethingWith(ar[index]);
}
}
...or even all of them!
function foo()
{
var ar;
var index;
// ...do some stuff, create an array
in 'ar'...
for (var index = 0; index <
ar.length; ++index)
{
doSomethingWith(ar[index]);
}
var index;
}
We can get away with that last one because a var
statement
defining a variable that already exists in the current scope does not replace
the variable (this is what keeps you from accidentally masking your function's
arguments, or even the arguments
array that's
provided for you).
最后一个的操作很骚,但这段代码却没有问题,因为var statement来声明一个已经存在的变量,在当前的对象中,不会取代变量.(这还可以防止你意外将你的函数参数或者arguments数组给覆盖没了)
This seems like an odd way to define the var
statement
until you get into the plumbing of JavaScript and how it sets up calls to
functions. You can get into some of that by reading my earlier post, Closures are not complicated, but the net
effect of the plumbing is that all var
statements
are treated as though they were at the top of the function (if they have
initializers, those become assignments and stay where they are).
这种定义 var statement的方式看起来很奇怪 直到你明白了js怎么处理函数的调用(call),你可以看一下我之前的那篇文章.
最后的直接结果就是所有的var statement会被处理好像他们都在函数的开头一样(如果他们被赋值初始化了,这些值会停留在他们被赋值的地方)
So does that mean that the common idiom of declaring an
indexer within the loop statement is "wrong"? Well, that's a matter
of perspective, and the older I get the more experience I accumulate, the less I think in
terms of absolutes like right and wrong. The language spec allows it, so in
that sense it's not "wrong". In some ways, it's sort of a shorthand
way of telling the next person reading the code that you're going to use it for
the loop (and only for the loop, right?), so in that sense perhaps it's not "wrong".
所以,这种在循环体中定义 indexer的方法是错的吗? 语言标准允许了这种情况,所以好像其实不是”wrong”的 在某种程度上来说 这是一种简写的方式,在告诉下一个读代码的人 你将要用这个变量来在loop中使用,所以..emmm这个好像也不是错的
But the further your code gets from expressing what's really
happening, the easier it is for someone reading the code later (perhaps you!)
to get the wrong end of the stick and introduce a problem. For example, suppose
you have a 30-some-odd-line function and the loop appears in within the body of
a conditional about two-thirds of the way down:
但是这样做其实很容易搞出问题,举例:
function foo(someArray)
{
var thingy;
var otherThingy;
// ...20 lines of code...
if (thingy > otherThingy)
{
for (var index = 0; index <
someArray.length; ++index)
{
doSomethingWith(someArray[index]);
}
}
// ...10 more lines of code...
}
Six months after you write this, Mike edits the function and needs to remember the index of something at the top so he can do something with it at the bottom; he declares an "index" variable, sets index at the top, and then uses it at the bottom, having missed the loop:
你写完这段代码的6个月后,mike编辑了这段函数 并且 需要拿上面一些东西的index , 他声明了index变量,然后在下面使用它,
function foo(someArray)
{
var thingy;
var otherThingy;
var index;
index = findSomething(someArray);
// ...20 lines of code...
if (thingy > otherThingy)
{
for (var index = 0; index < someArray.length; ++index)
{
doSomethingWith(someArray[index]);
}
}
// ...10 more lines of code...
restoreSomething(someArray, index);
}
Mike's introduced a bug, an irritating, intermittent bug. Sometimes the restoreSomething
call at the end fails for some reason; not always, mind, but sometimes. (Because index
gets set by the loop, but only when thingy > otherThingy
.)
Obviously, this bug could have been avoided if Mike had read
through the entire function carefully before making his mods. Or if you'd
chosen a different name for your index variable (in hopes of reducing the odds
of Mike using it). Or it could have been caught by thorough unit tests that
explore all conditions (and then Mike would have to go back and fix it).
But let's throw Mike a bone, eh? If we declare the variable
in the text in the same place it's defined by the interpreter at runtime, we
help him avoid making the mistake in the first place. And we like Mike, we
don't want to trip him up...right?
Regardless of your decision about how to write your code,
though, understanding what var
is really
doing can help you get that code doing what you want it to do.
然后就出现bug了,真坑啊