var关键字的目的是什么?何时应使用(或省略)?


1543

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

varJavaScript中关键字的功能到底是什么,它们之间有什么区别

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

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

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


3
链接var声明时,在逗号后加上换行符会影响行为吗?var x = 1,y = 2,[return] z = 3;
Alfabravo

4
如果您选择的变量名称恰好是先前定义的全局变量,则不使用“ var”也会使您暴露。看到我的悲伤的旅程在这里:stackoverflow.com/questions/16704014/...
斯科特·威尔逊ç

5
@Ray Toal的瓜卡博客帖子(绝对值得一读)已移至blog.safeshepherd.com/23/how-one-missing-var-ruined-our-launch
Hephaestus

我从来没有想到一首诗能激励我考虑一个程序问题
费利克斯·加侬-格雷尼尔

1
@Gibolt,但看看提问日期,这是2009年提出的一个不公平的传唤。即使如此,它对于当前的可维护性仍然有效,因为那里仍然存在许多非“现代JS”代码。
安德烈·菲盖雷多

Answers:


1355

如果您位于全球范围内,则没有太大区别。阅读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

31
是“没有太大差异” ==“没有差异”吗?
亚历克斯

65
好吧,实际上是的,有区别:)区别是否重要是另一个问题。进一步查看我的答案:stackoverflow.com/questions/1470488/…–
kangax

4
我认为这可能是Alex的观点,这就是为什么他使用“等于”运算符编写它的原因!
詹姆斯·贝德福德

18
这就像用电子枪开枪射击...忘记在变量前放置“ var”,最后在作用域链中的某个位置修改变量...尝试说服Java / C / Python / etc。开发人员认为JavaScript值得。哈!相比之下,C / C ++陷阱看起来不错。想象一下必须调试JavaScript ...当然,有些人会这样做。而且有很多用JavaScript编写的代码(不是简单的代码,请注意)……
Albus Dumbledore

6
如果您在全球范围内,那没有什么区别。>>区别在于以下答案
Max Koretskyi 2013年

746

有区别

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

x = 1另一方面,仅仅是属性分配。它首先尝试解决x范围链问题。如果在范围链中的任何地方找到它,它将执行分配;否则,它将执行分配。如果找不到x,则只有在全局对象(范围链中的顶级对象)创建x属性

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

除非您了解变量声明还创建属性(仅在变量对象上),并且Javascript中的每个属性(以及ECMAScript)都有某些描述其属性的标志,否则两者之间的区别可能很小,并且可能令人困惑。不要删除。

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

但这当然只是理论,实际上,由于实现中的各种错误(例如来自IE的错误),两者之间甚至存在更多差异

希望这一切有意义:)


[2010年12月16日更新]

在ES5(ECMAScript 5;最近标准化的语言的第5版)中,有一个所谓的“严格模式” —选择加入语言模式,它会稍微改变未声明分配的行为。在严格模式下,分配给未声明的标识符是ReferenceError。这样做的理由是抓住意外的任务,防止创建不希望的全局属性。一些较新的浏览器已经开始对严格模式进行滚动支持。参见,例如,我的compat表


如果我没记错的话,我想我曾经找到了一种方法,可以delete通过一些eval破解对var声明的变量进行处理。如果我记得确切的窍门,我会在这里发布。

3
@Mageek他可能正在使用eval声明的可删除变量。我曾经写过一篇博客文章
kangax 2012年

1
没什么大不了的话题,但是在这里提到以供参考。“ let”与“ var”非常相似,并且在Mozilla中受支持。主要区别在于var变量的范围是整个封闭函数,其中“ let”仅限其块
mac

@kangax如果将Alex的示例的后两行混合在一起var someObject = {},该someObject.someProperty = 5怎么办?将someProperty成为全球性的,而对象是遗迹的属性本地?
snapfractalpop 2012年

1
@kangax称为DontDelete标志的规范名称是可配置的(= false,您可以阅读有关Object.definePropertyObject.getOwnPropertyDescriptor
Paul S.

137

说这是“ 本地”和“ 全球 ” 之间的差异并不完全准确。

最好将其视为“ 本地”和“ 最近 ” 之间的差异。最接近的肯定是全局的,但情况并非总是如此。

/* global scope */
var local = true;
var global = true;

function outer() {
    /* local scope */
    var local = true;
    var global = false;

    /* nearest scope = outer */
    local = !global;

    function inner() {
        /* nearest scope = outer */
        local = false;
        global = false;

        /* nearest scope = undefined */
        /* defaults to defining a global */
        public = global;
    }
}

3
outer您定义的范围不是最近的范围var global = false;吗?
Snekse

@Snekse:当声明<code> var global = false; </ code>时,“ nearest”不适用。在该声明中,因为在声明中使用了“ var”,所以将“ global”放置在outer()的范围内。由于在inner()中未使用“ var”,因此它将在下一个向上级(即external())中更改值。
米奇

我想知道如果您更改该行是否会更改注释,var global = local;在这种情况下,本地的Nears范围将是正在主动定义的“本地”外部范围。如果将同一行更改为var global = global这种情况会很奇怪,在这种情况下,当搜索值时,最接近的作用域global将位于全局窗口作用域的水平。
Snekse15年

80

在浏览器中执行Javascript时,所有代码都被with语句包围,如下所示:

with (window) {
    //Your code
}

有关更多信息with-MDN

由于在当前作用域中var声明了变量,因此在内部窗口声明与完全不声明之间没有区别。var

当您不直接在窗口内部(例如在函数内部或在块内部)时,就会出现区别。

使用var可以隐藏具有相同名称的外部变量。这样,您可以模拟“私有”变量,但这是另一个主题。

经验法则是始终使用var,因为否则会冒引入细微错误的风险。

编辑:在收到我的批评之后,我想强调以下几点:

  • var声明当前范围内的变量
  • 全局范围是 window
  • 在全局范围内不使用var隐式声明var(窗口)
  • 在全局范围(窗口)中使用声明变量var与省略变量相同。
  • 声明在使用窗口不同的范围的变量var 是不一样的东西作为声明的变量,而不var
  • 始终var明确声明,因为这是一个好习惯

1
我没有投票给你,但是范围可能比window更好。您的整体解释有些晦涩。
罗伯特·哈维

4
我只是简单地用它的名称来命名事物,您想将其命名为“全局范围”,没关系,但是按照惯例,客户端是窗口对象,它是范围链的最后一个元素,因此您可以调用每个对象函数,并且窗口中的每个对象均未写入“窗口”。
kentaromiura

2
+1这是一个非常好的解释-我以前从未听说过这样的var / no var问题(没有双关语)。
doug 2012年

letES6中不赞成使用大多数答案。
埃文·卡罗尔

3
@EvanCarroll这个答案在技术上也是错误的,因为省略var不声明任何变量,而是在全局对象上创建了可删除的属性,此外,对于ES5“使用严格”模式,大多数答案显然是不正确的,也就没有了。甚至没有考虑此答案,因为在提出问题时,没有引用任何JavaScript版本(昨天添加),这暗示参考标准(当时)是ECMA 262第三版。
kentaromiura 2014年

43

始终使用var关键字声明变量。为什么?良好的编码习惯本身就足够了,但是省略它意味着在全局范围内声明它(像这样的变量称为“隐式”全局)。道格拉斯·克罗克福德(Douglas Crockford)建议不要使用隐式全局变量,并且根据Apple JavaScript编码指南

没有var 关键字创建的任何变量都在全局作用域创建,并且在函数返回时不会被垃圾回收(因为它不会超出作用域),这会导致内存泄漏。


17
“良好的编码习惯”本身不应成为充分的理由。这相当于“互联网上的一些人说这就是我的代码的外观”。甚至比“我的老师说的”要有效,除非至少有人模糊地理解了该规则背后的原因。
cHao

@cHao我认为,good coding practice如果这是推荐的最佳实践,那么它总是足够的理由,这是由多个Javascript作者推荐的。
克里斯·S

8
@ChrisS:不,“良好的编码习惯”本身并不是原因。它被认为是良好实践的原因很重要。除非那些作者告诉您为什么推荐它,否则他们的推荐将不具有任何意义。如果您不同意这些原因,那么您可以随意考虑这是不好的建议。如果您不问原因就遵循它,那就是货物崇拜的开始。
cHao 2014年

30

这是一个很好的例子,说明如何通过不声明局部变量而陷入困境var

<script>
one();

function one()
{
    for (i = 0;i < 10;i++)
    {
        two();
        alert(i);
    }
}

function two()
{
    i = 1;
}
</script>

i在循环的每次迭代中重置,因为它不是在for循环中本地声明,而是在全局中声明)最终导致无限循环


kes!我可以想像所有可能由该错字引起的错误。
BonsaiOak 2014年

2
我很好奇,为什么要将i作为参数传递给two()?(在for循环内)是否多余?
kalin

由于函数two()是在没有参数的情况下定义的,因此在one()函数中封装的two()函数中将忽略该参数。您是正确的,因为它没有作用,所以不需要它。
KK。

错误或功能?
TheMaster

13

我会说最好var在大多数情况下使用。

局部变量总是比全局范围内的变量快。

如果不使用var声明变量,则该变量将处于全局范围内。

有关更多信息,您可以在Google中搜索“范围链JavaScript”。


如果使用var关键字声明变量,则将在运行时创建该变量,因此它会不会更慢?因为另一个是在解析时创建的。
巴里斯Velioğlu

@RyuKaplan-嘿,是真的吗?我尝试使用Google谷歌搜索,但未获得有关该主题的任何信息!您有该断言的来源授权吗?Thx
麦克啮齿动物

@RyuKaplan解析/编译与实际运行代码不同。
gcampbell '16

11

不要使用var

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

使用constlet

const应该用于95%的情况。这样一来,变量引用就不会更改,因此数组,对象和DOM节点属性可以更改,并且应该更改为const

let应该用于期望重新分配的任何变量。这包括在for循环中。如果您写的内容varName =超出初始化范围,请使用let

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


2
用“ const”替换所有“ var”(全部替换)。您将很快注意到重新分配的变量在哪里。如果它们太多,则可能采用反模式编码:大多数可重新分配的变量可以嵌入在闭包中或作为对象属性。如果您有几个:对它们使用'let'。最后,如果有些变量根本没有用'var'修饰,它们将保持未声明状态,并且仍然存在于全局空间中,请注意。关于@Gibolt注释“在for循环内”,还建议在“ 95%的情况”下避免此类循环;-):数组方法很棒。
allez l'OM

通过说const应该在95%的情况下使用,似乎我们正在远离良好的实践而陷入教条。
Agamemnus

9

另一个区别,例如

var a = a || [] ; // works 

a = a || [] ; // a is undefined error.

1
您能否解释一下为什么变量定义为'var'且变量未定义为var的原因?如果是,在赋值右侧评估之前是否创建了变量var
马特2013年

6
@Lucek因为var a被提升到了作用域的顶部,并设置为null,它声明了但未初始化变量,所以在赋值中,您引用了一个未定义的null变量,其结果为false,并将赋值设置为[]。在后者中,您对property a的属性进行了分配a。您可以将一个不存在的属性分配给它-在赋值时创建它,但是如果您不ReferenceError抛出该属性,就无法读取不存在的属性。
埃文·卡罗尔

1
@EvanCarroll:它被提升到范围的顶部,并被设置为undefined而不是null。
mithunsatheesh 2014年

8

使用var始终是一个好主意,以防止变量造成全局范围混乱和变量彼此冲突,从而导致不必要的覆盖。


8

没有var-全局变量。

强烈建议始终使用use var语句,因为在局部上下文中初始化全局变量是有害的。但是,如果您需要这个肮脏的把戏,则应在页面开头写评论:

/* global: varname1, varname2... */

3

这是我为您编写的示例代码,用于理解此概念:

var foo = 5; 
bar = 2;     
fooba = 3;

// Execute an anonymous function
(function() {    
    bar = 100;             //overwrites global scope bar
    var foo = 4;           //a new foo variable is created in this' function's scope
    var fooba = 900;       //same as above
    document.write(foo);   //prints 4
    document.write(bar);   //prints 100
    document.write(fooba); //prints 900
})();

document.write('<br/>');
document.write('<br/>');
document.write(foo);       //prints 5
document.write(bar);       //prints 100
document.write(fooba);     //prints 3

2
该功能绝不是“匿名的”。实际上,它的名称尽可能地看起来很明显。
IngoBürk2014年

感谢您根据IngoBürk的评论编辑答案,使“匿名函数”实际上变为匿名。
戴夫·伯顿

3

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

如您所料,以下代码段输出["text"]

function var_fun() {
  let array = []
  array.push('text')
  return array
}

console.log(var_fun())

以下代码段也是如此(请注意let之前缺少的内容array):

function var_fun() {
  array = []
  array.push('text')
  return array
}

console.log(var_fun())

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

function var_fun() {
  array = [];
  return new Promise(resolve => resolve()).then(() => {
    array.push('text')
    return array
  })
}

var_fun().then(result => {console.log(result)})

但是在多个行为上却有所不同:

function var_fun() {
  array = [];
  return new Promise(resolve => resolve()).then(() => {
    array.push('text')
    return array
  })
}

[1,2,3].forEach(i => {
  var_fun().then(result => {console.log(result)})
})

但是使用let:

function var_fun() {
  let array = [];
  return new Promise(resolve => resolve()).then(() => {
    array.push('text')
    return array
  })
}

[1,2,3].forEach(i => {
  var_fun().then(result => {console.log(result)})
})


感谢@thisismydesign的示例!关于最后两个示例,为什么倒数第二个示例记录由3个元素组成的数组,而文本却写三次,而最终示例仅对数组中的每个元素记录一次“文本”?(我知道最后一个变量将“数组”声明为变量,因此位于局部范围内,而倒数第二个示例忽略了这一点,使“数组”成为隐含的​​全局范围的一部分。)但是,为什么这会影响变量输出?是因为forEach“ i”遍历函数和所有全局变量吗?
AlmostPitt

2

当有人试图学习这一点时,这就是我的看法。对于初学者来说,以上示例可能有点过于复杂。

如果运行此代码:

var local = true;
var global = true;


function test(){
  var local = false;
  var global = false;
  console.log(local)
  console.log(global)
}

test();

console.log(local);
console.log(global);

输出将显示为:false,false,true,true

因为它认为函数中的变量与函数外的变量是分开的,所以使用术语局部变量,这是因为我们在赋值中使用了var。如果删除函数中的var,则它现在如下所示:

var local = true;
var global = true;


function test(){
  local = false;
  global = false;
  console.log(local)
  console.log(global)
}

test();

console.log(local);
console.log(global);

输出为false,false,false,false

这是因为与其在局部作用域或函数中创建新变量,不如使用全局变量并将它们重新分配为false。


2

我看到人们在声明带或不带var以及函数内部或外部的变量时感到困惑。这是一个深入的示例,将带您完成以下步骤:

在jsfiddle上查看下面的脚本

a = 1;// Defined outside the function without var
var b = 1;// Defined outside the function with var
alert("Starting outside of all functions... \n \n a, b defined but c, d not defined yet: \n a:" + a + "\n b:" + b + "\n \n (If I try to show the value of the undefined c or d, console.log would throw 'Uncaught ReferenceError: c is not defined' error and script would stop running!)");

function testVar1(){
    c = 1;// Defined inside the function without var
    var d = 1;// Defined inside the function with var
    alert("Now inside the 1. function: \n a:" + a + "\n b:" + b + "\n c:" + c + "\n d:" + d);

    a = a + 5;
    b = b + 5;
    c = c + 5;
    d = d + 5;

    alert("After added values inside the 1. function: \n a:" + a + "\n b:" + b + "\n c:" + c + "\n d:" + d);
};


testVar1();
alert("Run the 1. function again...");
testVar1();

function testVar2(){
    var d = 1;// Defined inside the function with var
    alert("Now inside the 2. function: \n a:" + a + "\n b:" + b + "\n c:" + c + "\n d:" + d);

    a = a + 5;
    b = b + 5;
    c = c + 5;
    d = d + 5;

    alert("After added values inside the 2. function: \n a:" + a + "\n b:" + b + "\n c:" + c + "\n d:" + d);
};

testVar2();

alert("Now outside of all functions... \n \n Final Values: \n a:" + a + "\n b:" + b + "\n c:" + c + "\n You will not be able to see d here because then the value is requested, console.log would throw error 'Uncaught ReferenceError: d is not defined' and script would stop. \n ");
alert("**************\n Conclusion \n ************** \n \n 1. No matter declared with or without var (like a, b) if they get their value outside the function, they will preserve their value and also any other values that are added inside various functions through the script are preserved.\n 2. If the variable is declared without var inside a function (like c), it will act like the previous rule, it will preserve its value across all functions from now on. Either it got its first value in function testVar1() it still preserves the value and get additional value in function testVar2() \n 3. If the variable is declared with var inside a function only (like d in testVar1 or testVar2) it will will be undefined whenever the function ends. So it will be temporary variable in a function.");
alert("Now check console.log for the error when value d is requested next:");
alert(d);

结论

  1. 无论使用或不使用var声明(如a,b),如果它们在函数之外获取值,它们将保留其值,并且还将保留通过脚本添加到各种函数内部的任何其他值。
  2. 如果在函数内部(例如c)中声明了不带var的变量,它将像以前的规则一样工作,从现在开始,它将在所有函数中保留其值。它要么在函数testVar1()中获得了第一个值,要么仍然保留该值,并在函数testVar2()中获得了附加值。
  3. 如果仅在函数内部使用var声明变量(例如testVar1或testVar2中的d),则在函数结束时将是未定义的。因此它将是函数中的临时变量。

感谢您抽出宝贵的时间来创建一个示例来演示此主题。上面的代码缺少下面的部分,因此您可能需要编辑答案:a = 1; //在没有var var的情况下定义函数var b = 1; //在有var alert(“在所有函数外部开始... \ n \ na,b已定义,但c,d尚未定义:\ na:“ + a +” \ nb:“ + b +” \ n \ n(如果我尝试显示未定义c的值或d,console.log将抛出“未捕获的ReferenceError:未定义c”错误,脚本将停止运行!)“);
Sankofa

1

在代码内部,如果您使用变量而不使用var,那么会发生的是自动将var var_name放置在全局范围内,例如:

someFunction() {
    var a = some_value; /*a has local scope and it cannot be accessed when this
    function is not active*/
    b = a; /*here it places "var b" at top of script i.e. gives b global scope or
    uses already defined global variable b */
}

1

除了范围问题外,有些人还提到了吊装,但没有人举任何例子。这是全球范围的一种:

console.log(noErrorCase);
var noErrorCase = "you will reach that point";

console.log(runTimeError);
runTimeError = "you won't reach that point";


0

不使用“ var”变量只能在设置值时定义。例如:

my_var;

无法在全球范围任何其他范围内工作。它应具有以下值:

my_var = "value";

另一方面,您可以定义可变的like;

var my_var;

它的值是undefined(它的值不是,null并且null有趣地不等于。)。


my_var;实际上是一个有效的表达式语句。
lexicore 2014年

如果之前定义了变量,则该语句有效。否则会抛出错误“ ...未定义”。
umut 2014年

3
无论变量是否在此之前定义,它都是有效的语句。:)有效的语句可以引发错误,但不会使语句无效。
lexicore 2014年

我对此感到困惑。什么是有效声明?您能给我一个无效的陈述示例吗?
umut 2014年

我不得不道歉-最近有太多ECMAScript语法。 my_var;是有效的表达式语句/my_var;将是无效的陈述。但是,正如我所说的,这是语法原因,我很抱歉,我的评论实际上是不合适的。
lexicore 2014年

0

除非打算在浏览器中将变量附加到窗口对象,否则应使用var关键字。这是一个链接,它解释了作用域以及使用和wihtout var关键字进行的glocal作用域和局部作用域之间的区别。

当在不使用var关键字的情况下定义变量时,看起来像是一个简单的“赋值”操作。

当将值分配给javascript中的变量时,解释器首先尝试在与赋值相同的上下文/作用域中查找“变量声明”。解释器执行时dummyVariable = 20,它将在函数开始处查找dummyVariable的声明。(由于所有变量声明都已由javascript解释器移动到上下文的开头,因此称为提升)

您可能还想看看javascript中的吊装

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.