注意:这个问题是从ECMAScript版本3或5的角度提出的。随着ECMAScript 6版本中新特性的引入,答案可能会过时。

JavaScript中var关键字的功能是什么

var someNumber = 2;
var someFunction = function() { doSomething; }
var someObject = { }
var someObject.someProperty = 5;

and

someNumber = 2;
someFunction = function() { doSomething; }
someObject = { }
someObject.someProperty = 5;

?

你什么时候会使用其中一个,为什么/做什么?


当前回答

@Chris S给出了一个很好的例子,展示了var和no var之间的实际差异(和危险)。这是另一个例子,我发现这一点特别危险,因为这种差异只在异步环境中可见,因此在测试过程中很容易溜走。

正如您预期的那样,以下代码段输出[“text”]:

函数var_fun(){let数组=[]array.push('text')返回数组}console.log(var_fun())

下面的代码段也是如此(请注意缺少的let before数组):

函数var_fun(){数组=[]array.push('text')返回数组}console.log(var_fun())

异步执行数据操作仍然会使用单个执行器产生相同的结果:

函数var_fun(){array=[];return new Promise(resolve=>resolve()).then(()=>{array.push('text')返回数组})}var_fun().then(result=>{console.log(result)})

但对多个对象的行为不同:

函数var_fun(){array=[];return new Promise(resolve=>resolve()).then(()=>{array.push('text')返回数组})}[1,2,3].对于每个(i=>{var_fun().then(result=>{console.log(result)})})

但是,使用let:

函数var_fun(){let数组=[];return new Promise(resolve=>resolve()).then(()=>{array.push('text')返回数组})}[1,2,3].对于每个(i=>{var_fun().then(result=>{console.log(result)})})

其他回答

除了范围问题,一些人也提到了吊装,但并没有人举例说明。这里有一个全局范围:

console.log(noErrorCase);var noErrorCase=“您将到达该点”;

console.log(runTimeError);runTimeError=“您不会到达该点”;

如果你在全球范围内,那就没有太大区别了。阅读Kangax的答案以获得解释

如果你在一个函数中,那幺var将创建一个局部变量,“no var”将查找作用域链,直到它找到变量或到达全局作用域(此时它将创建它):

// These are both globals
var foo = 1;
bar = 2;

function()
{
    var foo = 1; // Local
    bar = 2;     // Global

    // Execute an anonymous function
    (function()
    {
        var wibble = 1; // Local
        foo = 2; // Inherits from scope above (creating a closure)
        moo = 3; // Global
    }())
}

如果您没有执行赋值,则需要使用var:

var x; // Declare x

@Chris S给出了一个很好的例子,展示了var和no var之间的实际差异(和危险)。这是另一个例子,我发现这一点特别危险,因为这种差异只在异步环境中可见,因此在测试过程中很容易溜走。

正如您预期的那样,以下代码段输出[“text”]:

函数var_fun(){let数组=[]array.push('text')返回数组}console.log(var_fun())

下面的代码段也是如此(请注意缺少的let before数组):

函数var_fun(){数组=[]array.push('text')返回数组}console.log(var_fun())

异步执行数据操作仍然会使用单个执行器产生相同的结果:

函数var_fun(){array=[];return new Promise(resolve=>resolve()).then(()=>{array.push('text')返回数组})}var_fun().then(result=>{console.log(result)})

但对多个对象的行为不同:

函数var_fun(){array=[];return new Promise(resolve=>resolve()).then(()=>{array.push('text')返回数组})}[1,2,3].对于每个(i=>{var_fun().then(result=>{console.log(result)})})

但是,使用let:

函数var_fun(){let数组=[];return new Promise(resolve=>resolve()).then(()=>{array.push('text')返回数组})}[1,2,3].对于每个(i=>{var_fun().then(result=>{console.log(result)})})

这是不同的。

varx=1在当前范围(也称为执行上下文)中声明变量x。如果声明出现在函数中,则声明局部变量;如果它在全局范围内,则声明一个全局变量。

另一方面,x=1仅仅是一项财产转让。它首先尝试根据作用域链解析x。如果它在该范围链中的任何位置找到它,它将执行赋值;如果找不到x,只有在全局对象(这是作用域链中的顶级对象)上创建x属性。

现在,请注意,它没有声明全局变量,而是创建了一个全局属性。

这两者之间的区别很微妙,可能会令人困惑,除非您了解变量声明也会创建财产(仅在variable Object上),并且Javascript(好吧,ECMAScript)中的每个属性都有描述其财产的特定标志——ReadOnly、DontEnum和DontDelete。

由于变量声明使用DontDelete标志创建属性,var x=1和x=1(在全局范围内执行时)之间的区别在于前者-变量声明-创建DontDelete'able属性,而后者不创建。因此,可以从全局对象中删除通过此隐式赋值创建的属性,而不能删除前一个属性(通过变量声明创建的属性)。

但这当然只是理论,在实践中,由于实现中的各种错误(例如IE中的错误),两者之间的差异甚至更大。

希望一切都有意义:)


[更新2010/12/16]

在ES5(ECMAScript 5;最近标准化,该语言的第5版)中,有一种所谓的“严格模式”——一种选择性语言模式,它稍微改变了未声明赋值的行为。在严格模式下,分配给未声明的标识符是ReferenceError。这样做的理由是捕捉意外分配,防止创建不需要的全局财产。一些较新的浏览器已经开始滚动支持严格模式。例如,请参见我的同胞表。

不要使用var!

var是ES6之前声明变量的方法。我们现在在未来,你应该这样编码。

使用常量和let

约95%的病例应使用常量。它使变量引用不能更改,因此数组、对象和DOM节点财产可以更改,并且应该是常量。

let应该用于任何期望重新分配的变量。这包括在for循环中。如果在初始化之后写入varName=,请使用let。

这两种语言都具有块级作用域,正如大多数其他语言所期望的那样。