let和var声明变量的区别:
1.let所声明的变量只在let命令所在的代码块内有效。(块级作用域)

  1. for(let i=0;i<10;i++){
  2. // ...
  3. }
  4. console.log(i);
  5. // ReferenceError: i is not defined
    当前的i只在for循环中有效,当在全局环境中去寻找时是找不到的,所以程序报错
  1. var a = [];
  2. for (var i = 0; i < 10; i++) {
  3. a[i] = function () {
  4. console.log(i);
  5. };
  6. }
  7. a[6](); // 10
    上面代码中,变量ivar命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10
  1. var a = [];
  2. for (let i = 0; i < 10; i++) {
  3. a[i] = function () {
  4. console.log(i);
  5. };
  6. }
  7. a[6](); // 6
    上面代码中,变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

  1. for (let i = 0; i < 3; i++) {
  2. let i = 'abc';
  3. console.log(i);
  4. }
  5. // abc
  6. // abc
  7. // abc
    上面代码正确运行,输出了 3 abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域(重点记忆)

2.let命令不存在变量提升

var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。按照一般的逻辑,变量应该在声明语句之后才可以使用,为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

  1. // var 的情况
  2. console.log(foo); // 输出undefined
  3. var foo = 2;
  4.  
  5. // let 的情况
  6. console.log(bar); // 报错ReferenceError
  7. let bar = 2;

3.let声明变量存在暂时性死区(即变量会绑定某个区域,不受外部影响)

  1. var tmp = 123;
  2. if (true) {
  3. tmp = 'abc'; // ReferenceError
  4. let tmp;
  5. }
    //上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。

ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。(简称TDZ)

  1. if (true) {
  2. // TDZ开始
  3. tmp = 'abc'; // ReferenceError
  4. console.log(tmp); // ReferenceError
  5.  
  6. let tmp; // TDZ结束
  7. console.log(tmp); // undefined
  8.  
  9. tmp = 123;
  10. console.log(tmp); //
  11. }
    上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。

“暂时性死区”也意味着typeof不再是一个百分之百安全的操作。

  1. typeof x; // ReferenceError
  2. let x;
  1. console.log(typeof x) //undefiend

第一块代码,变量x使用let命令声明,所以在声明之前,都属于x的“死区”,只要用到该变量就会报错。因此,typeof运行时就会抛出一个ReferenceError

第二块代码,x是一个不存在的变量名,结果返回“undefined”。所以,在没有let之前,typeof运算符是百分之百安全的,永远不会报错。现在这一点不成立了。

有些“死区”比较隐蔽,不太容易发现。(重点理解)

  1. function bar(x = y, y = 2) {
  2. return [x, y];
  3. }
  4.  
  5. bar(); // 报错

上面代码中,调用bar函数之所以报错(某些实现可能不报错),是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于“死区”。如果y的默认值是x,就不会报错,因为此时x已经声明了。

  1. function bar(x = 2, y = x) {
  2. return [x, y];
  3. }
  4. bar(); // [2, 2]

4.let命令不允许重复定义但是var可以而且后定义的变量会覆盖掉前面的变量

  1. // 报错
    function func() {
  2. let a = 10;
  3. var a = 1;
  4. }
  5.  
  6. // 报错
  7. function func() {
  8. let a = 10;
  9. let a = 1;
  10. }

因此,不能在函数内部重新声明参数,但是可以在函数内部不同块级作用域内声明同名参数。

  1. function func(arg) {
  2. let arg;
  3. }
  4. func() // 报错
  5.  
  6. function func(arg) {
  7. {
  8. let arg;
  9. }
  10. }
  11. func() // 不报错,因为这里let声明的arg变量和函数参数arg参数属于不同的块级作用域
  1. function bar(y) {
  2. var y=3;
  3. console.log(y);
  4. }
  5.  
  6. bar(2);//使用var可以重复定义变量,而且后定义的变量会覆盖前面的变量

const声明变量

const声明一个只读的常量。一旦声明,常量的值就不能改变。const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

  1. const PI = 3.1415;
  2. PI // 3.1415
  3.  
  4. PI = 3;
  5. // TypeError: Assignment to constant variable.
  6.  
  7. const foo;
  8. // SyntaxError: Missing initializer in const declaration

const的作用域与let命令相同:只在声明所在的块级作用域内有效。

  1. if (true) {
  2. const MAX = 5;
  3. }
  4.  
  5. MAX // Uncaught ReferenceError: MAX is not defined

const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。

const声明的常量,也与let一样不可重复声明。

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。(重点)

  1. const foo = {};
  2.  
  3. // 为 foo 添加一个属性,可以成功
  4. foo.prop = 123;
  5. foo.prop //
  6.  
  7. // 将 foo 指向另一个对象,就会报错
  8. foo = {}; // TypeError: "foo" is read-only
    //上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。
  1. const a = [];
  2. a.push('Hello'); // 可执行
  3. a.length = 0; // 可执行
  4. a = ['Dave']; // 报错
    //上面代码中,常量a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错。
  1.  
  1.  

var、let、const声明变量的区别的更多相关文章

  1. 【JS学习】var let const声明变量的异同点

    [JS学习]var let const声明变量的异同点 前言: 本博客系列为学习后盾人js教程过程中的记录与产出,如果对你有帮助,欢迎关注,点赞,分享.不足之处也欢迎指正,作者会积极思考与改正. 总述 ...

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

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

  3. PHP 闭包获取外部变量和global关键字声明变量的区别

    最近在学习workerman的时候比较频繁的接触到回调函数,使用中经常会因为worker的使用方式不同,会用这两种不同的方式去调用外部的worker变量,这里就整理一下PHP闭包获取外部变量和glob ...

  4. JavaScript中var和this定义变量的区别

    JavaScript中var和this定义变量的区别 在js中声明变量时可以使用var和this,但使用this的有很大一部分参考书是没有的,经过查阅相关资料总结如下: 用var和this声明变量,存 ...

  5. 网站开发进阶(六)JSP两种声明变量的区别

    JSP两种声明变量的区别 在JSP中用两种声明变量的方法,一种是在<%! %>内,一种是在<% %>内.他们之间有什么区别呢?我们直接看一个JSP文件来理解. 代码如下: &l ...

  6. ES6之用let,const和用var来声明变量的区别

    var(掌握) 不区分变量和常量   用var声明的变量都是变量,都是可变的,我们可以随便对它进行运算操作.这样当多个人进行同一个项目时,区分变量和常量会越来越难,一不小心就会把设计为常量的数据更改了 ...

  7. ES6的let和var声明变量的区别

    关于let的描述 let允许你声明一个作用域被限制在块级中的变量.语句或者表达式.与var关键字不同的是,它声明的变量只能是全局或者整个函数块的. 作用域规则 let声明的变量只在其声明的块或子块中可 ...

  8. 关于Let和var声明变量的区别

    Let是ES6中添加进来的一个关键字,用于声明变量,其法与var声明变量相同,不同点在于其作用域(块级). 举例可以看出其具体差别 for(var i=0;i<5;i++){ console.l ...

  9. es6中的let声明变量与es5中的var声明变量的区别,局部变量与全局变量

    自己通过看typescript官方文档里的let声明,与阮一峰老师翻译的的es6学习文档,总结以下三点 1.var声明可以多次重复声明同一个变量,let不行 2.let变量只在块级作用域里面有效果,v ...

随机推荐

  1. RXSwift01

    //创建 Observable 序列 func createObservable(){ /* let observable = Observable<Int>.just(5) let ob ...

  2. QRegExp解析

    正则表达式(regular expression)就是用一个“字符串”来描述一个特征,然后去验证另一个“字符串”是否符合这个特征.比如 表达式“ab+” 描述的特征是“一个 'a' 和 任意个 'b' ...

  3. 用生产者消费模型爬取智联招聘python岗位信息

    爬取python岗位智联招聘 这里爬取北京地区岗位招聘python岗位,并存入EXECEL文件内,代码如下: import json import xlwt import requests from ...

  4. HDU-1556:Color the ball(前缀和)

    Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  5. 最耗资源的10条sql

    ----当前最耗资源的10个cpu select * from (select address,hash_value, round(cpu_time/1000000) cpu_time_s, roun ...

  6. C. Functions again DP + 分类讨论

    http://codeforces.com/contest/789/problem/C 首先按题目要求处理出dis数组. 那么对于任意一个区间,[L, R],是dis[L] - dis[L + 1] ...

  7. [异常]undefined method `visit' for #<RSpec::Core::ExampleGroup::Nested_1:0x16529f8 @example=nil>

    在进行Rspec 编译测试: bundle exec rspec spec/requests/static_pages_spec.rb 提示错误: FF Failures: 1) Static pag ...

  8. lowbit

    树状数组(lowbit) Time Limit:1000ms   Memory Limit:128MB 题目描述 这天,LYK在学习树状数组. 当它遇到一个叫lowbit的函数时有点懵逼.lowbit ...

  9. MD5加密的方法

    #region MD5加密 /// <summary> /// MD5加密 /// </summary> /// <param name="strPwd&quo ...

  10. LeetCode 100 及 101题

    100. 相同的树 给定两个二叉树,编写一个函数来检验它们是否相同. 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的. 示例 1: 输入: 1 1 / \ / \ 2 3 2 3 [ ...