声明与初始化变量有什么区别?

21

我很好奇声明变量和初始化变量的区别,例如:

var example; // this is declaring

var example = "hi" // initializing? Or just "adding a value"?

我不认为我是正确的,但每个术语的确切定义是什么?还是它们基本上意思相同?


  1. 声明 example,定义并赋值为 undefined
  2. 声明 example,定义为字符串并赋值为 "hi"
- Grim
9个回答

20

编辑:@ThisClark在评论中说了些什么,我去证明他是错的,读完规范后我学到了一些东西:

以下是来自规范的相关内容:

var语句声明的变量作用域为运行执行上下文的VariableEnvironment。在创建词法环境时创建var变量,在创建时被初始化为undefined。[...] 由带有初始化程序的VariableDeclaration定义的变量在执行VariableDeclaration时分配其初始化程序的AssignmentExpression的值,而不是在创建变量时。

根据我的理解,以下几点描述了你在问题中提到的行为(以及术语的“正确-ish”使用):

  • 变量声明(例如,var foo)会导致该变量在“词法环境”实例化时立即被创建。例如,如果该变量在函数体内定义,则该函数是“词法环境”,因此变量的创建与函数本身的实例化同时发生。

  • 显然,变量声明可以有或没有初始化程序(即,解析为变量初始值的右侧表达式)。不过,这是术语“初始值”的相当非规范化的用法...让我们深入探讨一下:

  • 从技术上讲,根据规范说明,所有变量都使用undefined值初始化:

    变量被创建[...]并初始化为未定义

    强调“从技术上讲”,因为如果还提供了一个初始化程序,这在很大程度上是无关紧要的。

  • 根据我们已经讨论的内容,应该明白语句var foo;可以存在于函数体的任何位置,并且将其移动到同一函数内的其他位置对函数本身的运行时语义没有影响(无论是否存在对foo的实际赋值或其他引用发生的位置)。如果仍然感到困惑,请重新阅读前面的内容。

  • 最后一部分的行为是最直观的部分,即初始化程序分配。这个分配是在代码行实际被执行时进行的(再次强调,这并不是变量在技术上得到创建的时间点)。

因此,快速概括一下:

  • 所有使用var声明的变量都在它们的词法环境初始化时始终使用undefined进行初始化。
  • 从技术上讲,这种初始化可能不算作一个赋值。(哈哈,@ThisClark,你错了!)
  • 赋值是简单的部分,因为它们在你预期的时间点和方式上运行。

希望这有所帮助(也希望我没有严重误解规范!)。


1
由于它是JavaScript,变量声明也会将其值分配为“undefined”。 - ThisClark
2
看到了吗?规格说明并不那么难理解。;-) - RobG
@jmar777非常感谢。实际上,这些总结点很有帮助,因为我没有尝试理解“太深奥的技术术语”! :) - lakshman_dev
没错。我应该说“初始化”而不是“赋值”,用于在创建时给已声明变量的初始值。 - ThisClark

19

@jmar777的回答绝对是规范解释和分析。然而,我发现对于我们这些动手学习者来说,一些示例代码会很有帮助!;)


基本思想

  • '声明'使变量在给定范围内可用。
  • '赋值'在代码中的特定位置为变量赋一个具体的值。如果你试图为在该范围或父级范围中从未被声明过的变量赋值,则该变量将被隐式声明在全局范围内(相当于键入window.varName = value)。
  • '初始化'是在运行时“在幕后”或“在引擎盖下”发生的事情。在运行时,所有已声明的变量都会被初始化,并被赋予一个开始的赋值,即undefined(即使它们在代码的第一行立即被赋予不同的值)。

因此,初始化并不是一个对我们有影响的术语。我们声明和赋值,JavaScript引擎进行初始化。

因此,直接回答您问题中的示例:

  • var example; 声明一个变量,在初始化期间被赋值为undefined
  • var example = "hi" 声明一个变量,最初也被赋值为undefined,但当执行到该行代码时,它会被重新赋值为字符串"hi"。


示例代码

function testVariableDeclaration() {

    // This behaves 'as expected'....

    console.log(test2, 'no value assigned yet'); // --> undefined 'no value assigned yet'

    // ....but shouldn't our actual expectation instead be that it'll throw an error since
    // it doesn't exist yet? See the final console.log() below!


    // As we all know....

    test1 = 'global var'; // ....a variable assignment WITHOUT declaration in the current
                          // scope creates a global. (It's IMPLICITLY declared.)

    // But a little counter-intuitively....

    test2 = 'not global'; // Although this variable also appears to be assigned without
                          // declaration like 'test1', the declaration for 'test2' that
                          // appears *later* in the code gets hoisted so that it's already
                          // been declared in-scope prior to this assignment.

    console.log( test1, window.test1 === test1 ); // --> 'global var' TRUE
    console.log( test2, window.test2 === test2 ); // --> 'not global' FALSE

    var test2; // As shown by the above console.log() outputs, this variable is scoped.

    console.log( test3 ); // Throws a ReferenceError since 'test3' is not declared
                          // anywhere, as opposed to the first console.log() for 'test2'.
}


“范围”对声明/赋值的影响

声明和赋值之间的区别更加明显的原因是,声明一个变量总是在当前作用域内创建一个新变量(myVarscopeB),即使同名变量已经存在于父级作用域中(myVarscopeA)。然后我们可以在当前作用域的任何点上给myVarscopeB赋一个新值而不必重新声明它。(此赋值不会影响myVarscopeA的值。)一旦到达scopeB的结尾,myVarscopeB将不再可用于赋值。

另一方面,如果我们在给定作用域内将一个变量赋值,而没有在该作用域中声明它,则赋值将被分配到“作用域链”的下一个声明(如果找不到更高的声明,则隐式声明为全局变量)。

因此,每个作用域只需要声明一次变量,每个声明都会覆盖父级作用域中所做的任何声明(即创建一个不同的变量)。但是,如果要使变量在该作用域内唯一,则必须在该作用域内声明。赋值可以发生多次,但仅影响最“密切声明”的变量(没有更好的术语)。


非常好的解释,附有适当的代码示例块..!谢谢 - Amit Shah
1
这是一个很棒的答案! - Mehraj Malik

2
唯一的区别是var语句将会对任何声明但没有赋值的变量进行初始化,其值为undefined

在这两个例子中,您都在声明一个变量。

如果您在不使用var语句的情况下给变量赋值,它会沿着作用域链寻找已声明的变量,最终回到全局的window对象。


在声明变量时没有必要使用加粗,因为所有声明的变量在创建时都会初始化为 undefined - RobG

2

声明:在相应的作用域内(例如函数内部),使用给定名称注册变量。

初始化:当您声明一个变量时,它会自动初始化,这意味着JavaScript引擎为变量分配内存。

赋值:这是将特定值赋给变量的过程。

let x; // Declaration and initialization
x = "Hello World"; // Assignment

// Or all in one
let y = "Hello World";

Source: SitePoint


1

你在这里忽略了一个小细节。理解这个概念的一种比喻是这样的:每个变量都必须分配一些值。

如果没有明确说明,默认情况下所有变量的值为未定义(undefined)。

1) let example; // this is declaring and initializing with undefined

2) example="hi"; // this is assigning the value to hi

3) let example = "hi" // this is declaring and initializing with "hi"

因此,第三条语句实际上与1 + 2相同。

现在,可能会出现一个问题,当第三个语句是可能的时,我们为什么需要第一个语句?

原因是扩大变量的范围。

例如,假设在第8行需要一个变量。但是该值直到较晚时刻才在代码块中可用。

1) {
2)  let a;
3)  try{
4)   a=someFunctionWhichMayThroeException();
5)  }
6)    catch(e){
7)         a=100;
8)  }
9)  someFunctionOnA(a);// the variable is required here
10)
11)  }

通过在上面声明变量,我们扩大了变量的作用域,因此它可以在try块之外使用。
PS:这只是使用的一个微不足道的例子。

1
即使变量的值在稍后才被分配,将变量声明放在其作用域顶部也很常见。有些人喜欢在首次使用它们的代码块或部分之前立即声明它们。这完全是风格问题。 - RobG
是的,大多数情况下它只是风格问题,有时也像示例中所述那样是必需的。 - Mohit Kanwar

0

声明基本上意味着向程序引入一个新实体。初始化是给变量赋予它的第一个值。因此,你上面的例子是正确的。


0
直接引用自MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/undefined
全局 undefined 属性表示原始值 undefined。它是 JavaScript 的原始类型之一。
在 JavaScript 中简单地声明一个变量,例如 var example,将其初始化为 undefined 的原始值。这意味着以下两个表达式是等价的:

//equivalent expressions
var ex1;
var ex2 = undefined;

//true!
alert(ex2 === ex1);

目前我不知道也无法测试的是,这个警告框在浏览器历史记录中能够显示多少步。例如,在IE6或某些鲜为人知的黑莓手机上,这个警告框是否有效?我不能确定这是普遍适用的,但至少在撰写本文时,它可以在最新版本的Firefox、Chrome和Safari中正常工作。


它一直都有效。ECMAScript 规范的先前版本可在ecma-international.org上找到。Ed 1 第10.2.3节,第3条说:“对于代码中的每个VariableDeclaration,创建一个变量对象的属性,其名称为VariableDeclaration中的Identifier,其值为undefined…”。;-) 在这个上下文中,undefined 指的是原始的 undefined 值(不是“未定义”)。 - RobG

0
var example; // this is declaring

var example = "hi" // initializing? Or just "adding a value"?

它们是两件事,


var example; 的意思是声明了变量 example,并且初始化为默认值 'undefined'


var example = "hi" 表示 example 被声明并且用字符串 "hi" 进行了初始化。



-1

声明是程序中引入新名称的介绍。

var test;        // Is this a declaration ?

初始化是指“赋值”的过程。

var test = {first:"number_one"}  // Now that object is initialized with value

由于它是JavaScript,变量声明也会将其值分配为“undefined”。 - ThisClark
1
不完全正确。'初始化'是指在运行时引擎准备所有内容时发生的事情。在初始化期间,将分配一个undefined的初始值。你回答中的第二个例子实际上是'赋值',正如你所说,但由于变量可以多次被赋予新值,因此重要的是要注意它并不会在每次赋值时都被'初始化'。它只会被初始化一次。 - Marcus Hughes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接