目录

引言

       在学习javascript的过程中,变量是无时无刻不在使用的。那么相对应的,变量声明方法也如是。变量是由自己决定,但变量声明方法是早已经定义好的。那么在使用变量之前,了解变量声明方法,就变得尤为重要。在ES6推出之前,最常用的声明变量方法就是var。但是由于var自身的缺陷,ES6推出了let和const替代var。虽然修正了var的缺陷,但不能改变的,是之前已经用var写了多少年的项目,这些项目是无法随着var的被取代而轻易更改的。所以仍存在着使用var的公司和项目,这也使得了解var、let、const的区别变得有必要。

一、var

       在说明var并看代码之前,我们先统一思路,将变量的声明及使用过程分为:创建→初始化→赋值→修改四步。

首先,来看var的声明及使用:

  1. function test() {
  2.   /*
  3.   预解析,初始化与赋值分离
  4. var a = undefined // 创建变量a,并初始化为undefined。此时变量已经存在与环境中
  5. */
  6.   
  7. console.log(a) //undefined
  8. var a = 0; //赋值为0
  9. console.log(a); // 0
  10. a = 1; //可以修改变量值
  11. console.log(a) // 1
  12. }
  13. test();

以上代码可以看出,var在它的执行环境中的声明及操作过程为:

( 创建→初始化 )→赋值→修改

  1. 预解析:在环境最顶端,创建变量,并初始化为undefined—— 变量提升;
  2. 为变量赋值;
  3. 对变量进行操作,可以在后续操作中对变量值进行修改;

       通过console.log的打印结果,我们可以清晰的认识到一点——var的初始化与赋值是分离的,而且初始化的过程优先于执行环境中的所有操作。这就是为什么在var声明赋值前console.log变量,会打印出undefined,而不是报错的原因。

       var声明的初始化先于赋值的现象,就叫做变量提升

然后着重说一下变量提升

  1. if判断中的变量提升
  1. function test() {
  2.   /*
  3.   预解析
  4. 虽然if判断没有执行,但var的变量提升已经发生,此时执行环境中,已经存在变量a,值为undefined;
  5. */
  6.   
  7. console.log(a) // undefined,变量提升
  8.   
  9. if (false) {
  10.   var a = 1;
  11. console.log(a) //不执行
  12. }
  13. console.log(a); //undefined,变量未赋值
  14. a = 1; //注意,此时因为a的变量提升,未加声明符号的赋值,并没有提升到全局环境中
  15. console.log(a); // 1
  16. }
  17. test();
  18. console.log(a); // 报错,因为if中a的变量提升,a = 1的赋值并没有存在与全局中。如果注释掉if判断中的内容,a = 1因为没加变量声明符号,相当于在与全局中声明,那么最后的console.log(a)将打印1

2.for循环中的变量提升
```
function test() {
  /*
  预解析
虽然if判断没有执行,但var的变量提升已经发生,此时执行环境中,已经存在变量a,值为undefined;
*/
   
console.log(a) // undefined,变量提升
  
console.log(i); //undefined,变量提升
for (var i = 0;i console.log(a); //undefined,变量未赋值

a = 1;

console.log(a);

}

test();

console.log(a); //报错,变量不存在

  1.        从上边代码中可以看出,**当var声明存在于iffor循环中时,不管赋值有没有执行,创建及初始化的过程都已经提升到了执行环境中**。
  2. > **最后说明var的一个缺陷:**

function test() {

  var a = 0;

var a = 1;

var a = 2;

console.log(a); // 2

}

test();

  1.        由以上代码可以看出,var声明一个变量后,可以**无限次的以同一个变量名不断的重复 创建→初始化→赋值**,这跟直接修改变量值的结果是一样的。但是实际操作中不会有人通过这种方式操作变量,而且如果项目很大,很难保证不出现给不同变量声明同一个变量名的情况,很容易出现错误。
  2. ### 二、let
  3. > **首先,来看var的声明及使用:**

function test() {

/*

预解析,没有变量提升,啥也没有。创建、初始化与赋值同时进行

*/

  console.log(a); //报错,变量仍未创建

  1. let a = 0; // 创建变量a,初始化并赋值为0.不赋值的话,则为undefined;
  2. console.log(a); // 0
  3. a = 2; // 可以修改变量值
  4. console.log(a); // 2

}

test();

  1. 同样,先分析过程:
  2. **( 创建→初始化→赋值 )→修改**
  3. 1. 预解析,啥也没有;~~在环境最顶端,创建变量,并初始化为undefined—— 变量提升;~~
  4. 2. 创建变量、初始化并赋值;
  5. 3. 对变量进行操作,可以在后续操作中对变量值进行修改;
  6.        通过上方代码,我们可以看出let声明变量时,**( 创建→初始化→赋值 )是在一步完成的,不存在变量提升的现象**。所以在let声明前console.log(a),报错a is not defined,因为此时a还没有被创建。而let初始化前的执行区域就叫做**暂存死区**。
  7. > **然后,来看let在重复声明时的表现:**

function test() {

  let a = 1;

let a = 2;

let a = 3;

console.log(a); // 报错

}

test();

  1.        以上说明了let区别于var的另一个特性——**变量的唯一性**。**同一个变量名,不能在let中重复使用**,所以执行上方代码操作的结果,就是报错**Identifier 'a' has already been declared**;
  2. > **最后,letvar最大的区别——块级作用域**
  3.        首先说明,**块级作用域**的概念——简单理解,**{}一个大括号就是一个代码块,一个单独的执行环境**。那么它理应不受外部影响(如果不是刻意为之的话,它也不应该影响外部环境)。
  4.        但是在let之前,JS中的变量声明是**没有块级作用域**的属性的。这其中最典型的案例,就是**for循环中的var声明i**。

function a() {

  for (var i = 1;i < 5;i++) {

  console.log(i);

for( var i = 10;i < 20;i++) {

  console.log(i);

}

}

}

a(); //只执行外部循环一次,因为内部循环由于var声明的缘故,执行过后i值已经为11,外部循环判断后,将不再执行

function testA() {

  /*

  预解析

创建i并赋值为undefined

注意,此时实际上是两个i的变量提升

*/

console.log(i); // undefined

var i = 0; // 赋值i = 0

console.log(i); // 0;

  {

  console.log(i); // 0,访问第一个i

var i = 10 // 赋值i = 10,覆盖第一个i

console.log(i); // 10,访问第二个i

}

console.log(i); // 10,访问第二个i,i已经被覆盖

}

testA();

  1. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;以上代码,通过testA函数,侧面解释了一下a函数中的行为原因。这一行为模式,充分暴露了var声明的缺陷。
  2. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;**然后再来看let声明中的块级作用域:**

function b() {

  for (let i = 0;i < 5;i++) {

  console.log(i);

for(let i = 10;i < 20;i++) {

  console.log(i);

}

}

}

b(); //因为块级作用域的存在,两个循环的i互不影响,所有循环次数都会被执行。实际使用时,建议还是不要都用同一变量名

function testB() {   

/*预解析啥也没有 */

console.log(i); // 报错,暂存死区

let i = 0;

// 赋值i = 0

console.log(i); // 0;

{

console.log(i); // 报错,暂存死区,因为块中又声明了变量i。如果块中没有let i的话,则按作用域链向上查找,打印外部i值0

let i = 10 // 赋值i = 10,不覆盖第一个i

console.log(i); // 10,访问第二个i

}

console.log(i); // 0,访问第一个i,两个i相互不影响

}

testB();

  1. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;以上代码,通过testB函数,侧面解释了一下b函数中的行为原因。这一行为模式,体现了let生命中块级作用域的存在,并暴露出了letvar的最大区别。
  2. ### 三、const

function test() {

  /*

  预解析啥也没有,创建、初始化与赋值同时进行

*/

console.log(a); // 报错,变量仍未创建

const a = {

  name: "Lyu" // 创建对象a,并赋值为一个对象地址

}

console.log(a); //{name: "Lyu"}

console.log(a.name); // Lyu

a = 1; //报错,常量值不可修改

a.name = "Jack";

  console.log(a); // {name: "Jack"}

console.log(a.name); // Jack

}

test();

  1. 分析过程:
  2. **( 创建→初始化→赋值 )→修改**
  3. 1. 预解析,啥也没有;~~预解析在环境最顶端,创建变量,并初始化为undefined—— 变量提升;~~
  4. 2. 创建变量、初始化并赋值。必须赋值,不赋值会报错;
  5. 3. 对变量进行操作,可以在后续操作中对变量值进行修改,不可以对变量进行修改,但是可以对变量的属性进行修改;
  6. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了说明const的特性,**特意声明了一个对象**。在理解了varlet的过程之后,再来看const的整个过程,会发现**在( 创建→初始化→赋值 )的过程中,constlet是没有区别的**。唯一的区别在于**→修改**。如果执行了上方的代码,在**a = 1**那步会报错**Assignment to constant variable**。其中的constant就是const的英文全拼,它的意思的**不变的、恒定的、恒量**。那么从字面上就能理解,通过const声明的变量,**是一个恒定值,即无法更改的值**。所以当通过**a = 1**试图修改a的值时,报错。
  7. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;虽然**a本身的值**无法修改,但是a为对象,值为地址,所以**a的属性是可以修改的**。从代码最后一步可以看出。a.name的值成功修改为"Jack"。这就是const 的第二个特性。
  8. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;同let一样的,const的第三个特性也是**变量的唯一性**,不再过多阐述。
  9. ### 四、function
  10. > **首先,来看function的声明及使用:** 

/*

  funtcion test() {...}

预解析,创建、初始化、赋值三位一体

此时函数已经完成变量声明的所有操作,在执行环境的任何位置都可以调用函数

*/

test(); //调用函数,只要函数确实存在并可用,那么在执行环境任何位置都可以调用

function test() {...}

  1. 分析过程:
  2. **( 创建→初始化→赋值 )→执行/修改**
  3. 1. 预解析,在环境最顶端,创建函数,初始化并赋值为函数定义;
  4. 2. 执行函数,无论函数在何位置,只要可用,就可以调用;
  5. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function是专门用于函数声明的方法,由于函数的复杂性,以及利用性。function声明的函数,会在整个环境变量最顶端完成**创建、初始化、赋值三位一体**的操作。这样一来,不管在何处声明了函数,可以在任何地方调用函数方法。这是比较合乎常理的性质。
  6. > **然后,functionvar一样,同样存在变量声明的特性:**
  7. 1. if判断中的变量提升

function test() {

  /*

  预解析

  虽然if判断没有执行,但function的变量提升已经发生,此时执行环境中,已经存在变量a,值为undefined;

*/

  

console.log(a) // undefined,变量提升

  

if (false) {

  function a(){}; // 变量提升

console.log(a) //不执行

}

console.log(a); //undefined,变量未赋值

a = 1; //注意,此时因为a的变量提升,未加声明符号的赋值,并没有提升到全局环境中

console.log(a); // 1

}

test();

console.log(a); // 报错,因为if中a的变量提升,a = 1的赋值并没有存在与全局中。如果注释掉if判断中的内容,a = 1因为没加变量声明符号,相当于在与全局中声明,那么最后的console.log(a)将打印1

  1. <br>
  2. 2. for循环中的变量提升

function test() {

  /*

  预解析

虽然if判断没有执行,但function的变量提升已经发生,此时执行环境中,已经存在变量a,值为undefined;

*/

   

console.log(a) // undefined,变量提升

  

console.log(i); //报错,暂存死区

for (let i = 0;i < 0;i++) {

  function a() {}; //变量提升

console.log(a) //未执行

}

console.log(i); //报错,let不存在变量提升

console.log(a); //undefined,变量未赋值

a = 1;

console.log(a);

}

test();

console.log(a); //报错,变量不存在


  1. > **最后,functionvar一样,它也可以对同一变量重复声明,而且后边的函数定义会覆盖前边的函数定义:**

test(); // 2

function test() {

  console.log(1);  

}

function test() {

  console.log(2);

}

  1. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;结果打印2,说明前边声明的函数方法被后边所覆盖。
  2. ### 五、总结
  3. 综上所述,可以总结为如下几点:
  4. 1.varletconst的区别在于**变量提升**,以及**变量的唯一性**;
  5. 2.constlet的区别,**除了变量值不能修改,其他性质一样**;
  6. 3.function由于其自身的需要,**创建→初始化→赋值三位一体**,在环境最顶端完成;也正因为这种性质,**函数声明的函数可以在任何位置被调用**;
  7. 4.如果可以,尽量**使用letconst代替var**;
  8. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;本文仅仅简单罗列了在函数声明及操作方面,varletconstfunction的区别,意在为初学者理清概念偏差,少走弯路,并通过理解学习,早日跨入JS门槛。
  9. **以上,如有错误或是纰漏,欢迎批评指正~**

javascript精雕细琢(一):var let const function声明的区别的更多相关文章

  1. 浅谈JS中 var let const 变量声明

    浅谈JS中 var let const 变量声明 用var来声明变量会出现的问题: 1. 允许重复的变量声明:导致数据被覆盖 2. 变量提升:怪异的数据访问.闭包问题 3. 全局变量挂载到全局对象:全 ...

  2. 来简单说说var,let,const,function,import,class

    一.var和let var已经在JavaScript中存在很长一段时间了,但是它存在了一些不足的地方,接下来我们就来看看吧 首先var存在变量提升,这是怎么一回事呢,我们看下面代码 为什么是它呢,是因 ...

  3. javaScript 变量提升 var let const,以及JS 的解析阶段和执行阶段

    我们先来看一道面试题,大家猜想一下,下面这段代码,打印出来的结果是什么 var name = 'World!'; (function () { if (typeof name === 'undefin ...

  4. var和const和let的区别

    简述: 1.前端的变量申明,可以用到var,ES6的const(衡量)/let(变量) 2.在ES5用的都是var,到ES6之后,也就是2015年开始出现const/let. var 不会报错,有声明 ...

  5. javascript中的var,let,const关键字

    文章:JavaScript 中 var 和 let 和 const 关键字的区别 比较全面的文章.

  6. JavaScript:学习笔记(7)——VAR、LET、CONST三种变量声明的区别

    JavaScript:学习笔记(7)——VAR.LET.CONST三种变量声明的区别 ES2015(ES6)带来了许多闪亮的新功能,自2017年以来,许多JavaScript开发人员已经熟悉并开始使用 ...

  7. javascript中var let const三种变量声明方式

    javascript中var let const三种变量声明方式 1.var  ①var表示声明了一个变量,并且可以同时初始化该变量. ②使用var语句声明的变量的作用域是当前执行位置的上下文:一个函 ...

  8. let、var、const声明的区别

    前言 看了方应杭老师的一篇解释let的文章,对JavaScript中的声明有了深刻的理解,这里也就有了总结一下JavaScript中各种声明之间区别的这篇文章. JavaScript中变量声明机制 首 ...

  9. JavaScript中的var,const,let区别与用法(浅谈)

    let 和 const是(ES6) 新增加了两个重要的 JavaScript 关键字. 1.var全局变量 //全局变量在 JavaScript 程序的任何地方都可以访问 //定义的变量可以修改,如果 ...

随机推荐

  1. 文件名命工具类(将指定目录下的文件的type类型的文件,进行重命名,命名后的文件将去掉type)

    import java.io.File; /** * <b>function:</b> 文件命名工具类 * @author hoojo * @createDate 2012-5 ...

  2. 小学四则运算结对项目报告【GUI】

    写在前面 这次的结对项目我做了很长时间,感触也很多.在这次项目中我使用了Java GUI作为和用户的交互方式,但是在上Java课的时候我对GUI和事件驱动这里并没有学的多好,可能是当时对编程还没有什么 ...

  3. [二叉树建树&完全二叉树判断] 1110. Complete Binary Tree (25)

    1110. Complete Binary Tree (25) Given a tree, you are supposed to tell if it is a complete binary tr ...

  4. 使用Crash工具查看一个TCP listen sock内存布局实例

    利用crash工具,我们可以很方便的查看正在运行内核的一些全局变量的数据结构,如TCP的ehash.bhash哈希桶,全局变量的查看比较简单.Crash工具还允许我们查看调用堆栈内部的局部变量,下面示 ...

  5. C1WPF制作OLAP Cube浏览工具

    经过前期一段时间对WPF的学习了解,相信大家对WPF有了一定的了解.今天我们一起来了解使用Component One(简称C1)的WPF控件制作CUBE浏览工具.其实这个OLAP控件官方已经有了很详细 ...

  6. windows下的C++ socket服务器(1)

    windows下的一个C++ socket服务器,用到了C++11的相关内容,现在还不是很完善,以后会不断改进的! #include <winsock2.h>//1 以后会用这种方式对特定 ...

  7. (转)web开发流程

    a.项目经理与公司决策层的沟通,以确定这个需求有没有足够的人手和可行性去实现,以及与现有产品的依存关系. b.公司决策层与市场/策划部门的交流,这个过程将进行的相当充分,并且是反复.长期的,它致力于从 ...

  8. mysql内外连接

    更新于2017-12-13,在今天的一个面试里面被问到了left/right outer join,回答上来了.但又问了一下inner join ,一下子记不清inner jion是个什么东西了.这次 ...

  9. oracle 存储过程创建报错 Procedure created with compilation errors

    出现这错误的话,存储过程还是会成功创建的,创建好后再逐个打开查找存储过程的问题 问题:基本上就是存储过程里面的表不存在,dblink 不存在    ,用户名.xx表  要么用户名不存在要么表不存在 创 ...

  10. 【NOI 2018】冒泡排序(组合数学)

    题意大概是给定一个长度为$n$的排列$p$,求有多少长度为$n$的排列满足冒泡排序的交换次数为$\frac{1}{2} \sum\limits_{i = 1}^{n}|i - p_{i}|$. 可以发 ...