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了,真坑啊

(翻译) Poor misunderstood 'var'的更多相关文章

  1. Javascript——概述 && 继承 && 复用 && 私有成员 && 构造函数

    原文链接:A re-introduction to JavaScript (JS tutorial) Why a re-introduction? Because JavaScript is noto ...

  2. (翻译) How variables are allocated memory in Javascript? | scope chain | lexicial scope

    总结: 阅读下面文章需要15分钟 提问者的问题是JavaScript中内存是怎么分配的,在介绍的过程作者涉及计到了JS中 Scope Chain和调用函数call生成lexicial environm ...

  3. 【uwp】浅谈China Daily 中划词翻译的实现

    学习uwp开发也有一段时间了,最近上架了一个小应用(China Daily),现在准备将开发中所学到的一些东西拿出来跟大家分享交流一下. 先给出应用的下载链接:China Daily , 感兴趣的童鞋 ...

  4. 2018-08-29 浏览器插件实现GitHub代码翻译原型演示

    此原型源自此想法: 中文化源码. 考虑到IDE插件工作量较大, 且与IDE绑定. 在代码转换工具的各种实现中, 综合考虑实用+易用+长远改进潜力, 浏览器插件似乎较有优势. 于是用最快捷的方式实现这一 ...

  5. Go语言使用百度翻译api

    Go语言使用百度翻译api 之前做过一个使用百度翻译api的工具,这个工具用于用户的自动翻译功能,是使用C#调用百度翻译api接口,既然在学习Go语言,那必然也是要使用Go来玩耍一番.这里我是这么安排 ...

  6. ENGLISH抠脚童鞋的福利--GitHub汉化插件

    今天在某前端群看到一个插件,激动万分啊!我就把插件使用实现的步骤分享一下! 打开chrome浏览器输入地址:chrome://extensions/ : 跳转到其他页面,点击左上角--扩展程序: 将T ...

  7. VB CreateObject转C#

    C#调用方法.函数获取属性大致流程如下: System.Type oType = System.Type.GetTypeFromProgID("SomeClass"); objec ...

  8. A Swift Tour(4) - Objects and Classes

    Objects and Classes(对象和类) 用 class 关键字后面跟一个类名来创建一个class,在一个类中声明 常亮或变量,他存在于当前类的上下文,函数的方法是同样的 var numbe ...

  9. 【PHP】PHP5.4.0版本号ChangeLog具体解释(上)

    前言 随着大量的框架使用composer和namespace,渐渐的线上环境也从之前的5.3变成了5.4或者5.5甚至5.6,随着7月份PHP7的公布,会有很多其它的公司採用新版本号. 之前好久就想写 ...

随机推荐

  1. java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present

    @Controller@ComponentScan@Configuration@EnableScheduling@EnableAutoConfiguration(exclude={DataSource ...

  2. Selenium(九)测试用例数据分离与从文件导入数据

    一.测试用例数据与代码分离 1.从之前的脚本来看,我还是把数据写在了脚本中,这样脚本的通用性很差.全局的数据其实可以从数据库.文本文件.Excel中直接读取. 2.代码和用户数据分离: 3.数据设计- ...

  3. JAVA遇见HTML——Servlet篇:应用MVC架构实现项目

    java关键字“this”只能用在方法方法体内.当一个对象创建之后,java虚拟机就会给这个对象分配一个引用自身的指针,这个指针的名字就是this.只能在非静态方法中使用 package servle ...

  4. Nginx HTTP服务器配置模板

    https://github.com/h5bp/server-configs-nginx

  5. Nginx中ngx_stream_core_module和ngx_stream_proxy_module

    ngx_stream_core_module模块该模块模拟基于tcp或udp的服务连接的反向代理理,即⼯工作于传输层的调度器器指令:17.1 streamSyntax: stream { ... }D ...

  6. flutter 编译报错 ../extended_network_image_provider.dart:63:41: Error: Type 'DecoderCallback' not found

    flutter 编译的时候报错 Compiler message:../../../.pub-cache/hosted/pub.dartlang.org/extended_image_library- ...

  7. 009_linuxC++之_友元函数

    (一)定义:友元函数是指某些虽然不是类成员却能够访问类的所有成员的函数.类授予它的友元特别的访问权.通常同一个开发者会出于技术和非技术的原因,控制类的友元和成员函数(否则当你想更新你的类时,还要征得其 ...

  8. Shell 04 字符串处理、正则表达式

    一.字符串的处理   1.字符串截取 1.1 s{}表达式  ${变量名:起始位置:长度} (从0开始) n=number (n="number") echo ${#n}   -- ...

  9. 运算符重载之new与delete

    关于new/delete,援引C++ Primer中的一段话: 某些应用程序对内存分配有特殊的要求,因此我们无法直接将标准的内存管理机制直接应用于这些程序.他们常常需要自定义内存分配的细节,比如使用关 ...

  10. 关于scala

    对函数式编程感兴趣了 雪下scala吧