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. Centos7下安装MongoDB4.0.10

    前言 模式自由 :可以把不同结构的文档存储在同一个数据库里 面向集合的存储:适合存储 JSON风格文件的形式 完整的索引支持:对任何属性可索引 复制和高可用性:支持服务器之间的数据复制,支持主-从模式 ...

  2. CSS3媒体查询实例

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  3. IdentityServer(三)密码模式

    前言 用户名密码模式相较于客户端凭证模式,多了用户.通过用户的用户名和密码向Identity Server申请访问令牌.密码模式有两种实现方式. 1.把用户写进内存Identity从中读取账号密码验证 ...

  4. ASP.Net页面生存周期、Page_Load方法

    一.ASP.Net Web应用程序中的请求处理过程 二. http模块生命周期: 三.Asp.NET页面生命周期 1.阶段 阶段 说明 请求页面(Page Request) 严格地说,这个阶段不是页面 ...

  5. c语言1博客作业09

    一.本周作业头 这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/SE2019-3/homework/10033 ...

  6. BZOJ 3566 概率充电器(树形概率DP)

    题面 题目传送门 分析 定义f(i)f(i)f(i)为iii点不被点亮的概率,p(i)p(i)p(i)为iii自己被点亮的概率,p(i,j)p(i,j)p(i,j)表示i−ji-ji−j 这条边联通的 ...

  7. 通过AOP拦截打印日志,出入参数

    import java.lang.reflect.Modifier; import javassist.ClassClassPath; import javassist.ClassPool; impo ...

  8. delegate:动态绑定js事件

    $('.videomodule').delegate("span", "click", function() { var i = $(this).index() ...

  9. keydown([[data],fn]) 当键盘或按钮被按下时,发生 keydown 事件。

    keydown([[data],fn]) 概述 当键盘或按钮被按下时,发生 keydown 事件. 注释:如果在文档元素上进行设置,则无论元素是否获得焦点,该事件都会发生.直线电机滑台 参数 fnFu ...

  10. 应用webservice实现公网天气查询

    1. wsdl网址:http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl 2. URL:http://www.webxml.com.cn/zh ...