ES6学习(一): var、let和const

到ES6为止,我们一共有4种方式可以定义变量,今天我们就来聊一聊这4种方式都会有怎样的行为。

一、不使用任何关键字

  a = 1

在大部分的学习资料上对这行代码的解释是:声明一个全局变量。这么说没问题,但是不严谨。有如下代码:

  function fn() {
    var a;
    function fn2() {
      a = 1  //只是单纯的赋值,并没有声明全局变量
    }
  }

从上述代码可以看出,a=1不再是声明全局变量,而仅仅是单纯的赋值。因此,更严谨一点的说法应该是只有在不存在变量a的情况下才会隐式地声明一个全局变量

二、var

  var a = 1  //在当前作用域内声明变量a

但是用var定义变量会存在一个非常大的问题。先看下面的代码

  function fn() {
    if (true) {
      console.log(a)  //理论上此处应打印 a is not defined
    } else {
      var a;
    }
  }
  fn()

按照我们正常的思路,应该打印出'a is not defined',但是事实上代码执行完打印出的结果是undefined,这不符合我们一般的理解。实际上,用var定义的变量会存在一种叫做变量/函数声明提升的行为。上述代码和下面代码等价:

  function fn() {
    var a;
    if (true) {
      console.log(a)  //undefined
    } else {}
  }
  fn()

下面再看一个例子:

  {
    var a = 1;
    window.fn = function() {
      console.log(a)
    }
  }
  //需求: 有且仅有fn为全局函数,a不可被外部(window)访问

通过上面的解释我们知道,a变量声明会提升,于是全局存在了变量a和函数fn,显然不符合我们的要求。

我们知道要想一个变量不被外部访问,可以用函数的方式来包裹变量,于是有了以下的代码:

  function fn2() {
    var a = 1;
    window.fn = function() {
      console.log(a)
    }
  }
  fn2()

这种写法下变量a的确不会被外部(window)访问,但是问题由来了,全局下多了fn2函数,这也不符合我们的需求。那既然这样,我们会想到,那我把fn2藏起来不就行了,于是我写出了这样的代码:

  //立即执行匿名函数
  (function () {
    var a = 1;
    window.fn = function() {
      console.log(a)
    }
  }()) 

我们可以看到,我仅仅是想要让变量a不可被外部(window)访问,就得写一个立即执行匿名函数,太麻烦了。于是let应运而生!

三、let

同样是上述的代码:

  {
    let a = 1;
    window.fn = function() {
      console.log(a)  //1
    }
  }
  console.log(a)  // a is not defined

这种情况下,a变量就像'被困在了大括号里面',你只能在大括号之间活动,出了大括号,就再也访问不到a了。

let还有另外两个特点,其中一个叫做Temp Dead Zone(临时死区),看如下代码:

  {
    let a = 1
    {
      console.log(a)  // a is not defined   
      let a = 2
      {
        let a = 3
      }
    }
  }

也就是说,在let定义的变量还没有初始化之前是无法使用该变量的。

另外一个特点就是不能重复定义

  {
    let a = 1;
    let a = 2;  //报错
  }

四、const

const只有一次赋值机会,而且必须在声明的时候立马赋值

  {
    const a;  //报错 必须赋值
    const b = 1;
    b = 2 //  报错
  }

刚刚我们用const定义了基本数据类型的值,接下来我们看下定义引用数据类型的值。

  const a = {
    b: 4
  }
  //第一种
  a.b = 5;
  console.log(a)  // {b:5}
  //第二种
  a = {
    b: 6
  }
  console.log(a)  //Uncaught TypeError: Assignment to constant variable.

也就是说,用const定义的对象内部的值是可以改变的,但是不能改变对象的引用地址。

到此为止,ES6的4种定义变量的方式就说完了,但是还有一个问题,为什么会发生Temp Dead Zone(临时死区),那我们就说说创建变量的过程。变量创建大体分为3步(忽略分配内存空间什么的):

  1. 变量声明
  2. 变量初始化
  3. 变量赋值

那我们就用var/function/let/const来举例:

  • var
  function fn(){
    var x = 1
  }
  fn()

fn执行大致进行了以下步骤:

  1. 进入fn后,声明x
  2. x初始化为undefined
  3. 执行代码, x赋值为1

即代码执行之前就已经完成了声明和初始化的过程

  • function
  fn()
  function fn() {
    console.log(1)
  }

执行步骤大致如下:

  1. 找到所有用function声明的变量
  2. 初始化并赋值
  3. 执行代码

即代码执行之前就已经完成了声明、初始化和赋值的过程

  • let
  {
    let x = 1  //x初始化为1
    //let x //x初始化为undefined
    x = 2
  }

执行步骤大致如下:

  1. 找到所有用let声明的变量
  2. 执行代码
  3. 初始化x=1
  4. x赋值为2
  • const

const由于必须在声明的时候初始化,所以只存在声明和初始化的过程,不存在赋值过程

由上述解释我们知道: 临时死区产生的原因是用let定义的变量在没有被初始化之前是无法被使用的

下面有个有意思的现象:

1.jpg

就是说,如果let声明变量的初始化过程失败了,那么:

  • 变量将永远处于声明状态
  • 无法再次对变量进行初始化
  • 由于变量无法被初始化,所以永远处在临时死区

面试题

  for (var i=0; i<6;i++) {}
  console.log(i)  //6

这个没啥好说的,由于变量提升,i上升为全局变量,执行完循环之后被赋值为6

  for (var i=0; i<6;i++) {
    function fn() {
      console.log(i)
    }
    /** 第一种情况,如果直接调用fn
     * fn()  // 0, 1, 2, 3, 4, 5
     */
    
    /**
     * 第二种情况,绑定在button的点击事件上
     * btn.onclick = fn  // 6
     * 这是因为在点击时循环已经结束,i的值是6
     */
  }
  var tags = document.querySelectorAll('li') //6个
  for (var i=0; i<tags.length; i++) {
    tags[i].onclick = function() {
      console.log(i)  //6
    }
  }

那么怎么才能输出0 1 2 3 4 5呢?

  • 第一种: 利用let保存每一次循环的变量
  var tags = document.querySelectorAll('li') //6个
  for (var i=0; i<tags.length; i++) {
    let j = i;
    tags[j].onclick = function() {
      console.log(j)
    }
  }
  • 第二种: 利用立即执行匿名函数,把每次循环产生的变量传入
  var tags = document.querySelectorAll('li') //6个
  for (var i=0; i<tags.length; i++) {
    (function(j) {
      //如果不传入,可以用argument[0]
      tags[j].onclick = function() {
        console.log(j)
      }
    })(i)
  }
  • 第三种: 将循环的变量用let定义
  var tags = document.querySelectorAll('li') //6个
  for (let i=0; i<tags.length; i++) {
    //let i = i;  //js自动加的
    //块里面的i=圆括号里面i的值
    tags[i].onclick = function() {
      console.log(i)
    }
    //圆括号里面i的值=块里面的i
  }

  //这种情况下,js会自动在每次进入循环体中的时候帮我们加一个同名变量赋值为循环条件中的变量,每次循环最后会把循环条件中的变量赋值为循环体中的变量
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容