ES6初探-let const与箭头函数

let const

let

1. let 声明的变量不会有变量提升,且在 『一个花括号{}』 内部声明的变量,会形成块级作用域

🌰

{
  console.log(a)
  // Uncaught ReferenceError: Cannot access 'a' before initialization
  let a = 111
  console.log(a)
  // 打印出 111
}
console.log(a)
// Uncaught ReferenceError: a is not defined

上例中,可以的值 let 并不存在变量提升。所有在 let a 之前使用 a 会报错,所以导致 第一个 console.log(a) 报错, 且在花括号内部 let 声明的变量 会形成块级作用域所以外部是无法取到 a 的值,所有花括号外部的 console.log(a) 会直接报错。

上面的如是在花括号内部使用 var 声明变量

{
  console.log(a)
  var a = 111
  console.log(a)
}
console.log(a)

等价于

var a 
console.log(a)
a = 111
console.log(a)
console.log(a)
// undefined
// 111
// 111

因为var 声明会有变量提升,且花括号内部并没有形成一个块级作用域。所以执行的结果就会不符合直觉,造成困惑。

2. let 不能重复声明变量

🌰

let a = 1
// var a = 1

let a = 2
// Uncaught SyntaxError: Identifier 'a' has already been declared

let 不能重复声明一个变量,但是如果不同的变量是在不同的块级作用域是可行的。

🌰

{
  let a = 1
  {
    let a = 2
    {
      let a = 3
    }
  }
}

以上代码a 隶属于不同的块级作用域所以并不存在重复声明。

const

const的用法与 let 有还多相似之处。但是 const 也有其独特的地方

  1. const 声明初始必须赋值,其已经赋值的的变量。不能再次赋值
const a = 1
a = 2
// Uncaught TypeError: Assignment to constant variable
  1. const 赋值的的变量是对象字面量,可以修改对象内部属性的值。
const obj = {
  name: 'xxx',
  age: '21'
}

obj['name'] = 'yyy'

obj = {anme: 'xxx', age: 21}
// b报错
// Assignment to constant variable.
  1. const 适用于常量
const pi = 3.141
const e = 2.718

let 声明与 for 循环以及循环内的函数

🌰 需求: 在控制台间隔一秒打印出 1, 2, 3, 4, 5

for (var i=1; i<=5; i++) {     
    setTimeout( function timer() {
        console.log(i) }, i*1000 );
}

结果如下图间隔一秒打印出 6, 6, 6, 6, 6

for循环与函数

错误分析: 这是因为在循环迭代的过程中,贯穿始终的都只有一个 i 在被共享。并没有新的 i 的值产生,所以最后 循环结束 i 也有最后一次循环的一个值(最后一次循环 i = 5, 但是 i++ 还会执行所以最终i的值为6)。

解决问题:

  1. 在内部添加一个 j 存储 的值
for (var i = 1; i <= 5; i++) {
  var j = i
  setTimeout(function timer() {
    console.log(i)
  }, i*1000)
}

打印的结果是5,5,5,5,5, 如下图所示:

for循环与函数-1

分析:
首先上面的代码等价于:

// var 声明变量提升
var i
var j
for (i = 1; i <= 5; i++) {
  j = i
  setTimeout(function timer() {
    console.log(i)
  }, i*1000)
}

首先在花括号内部有一个 j 的值可以存储 i, 但是 j 的并不是存在一个局部作用域而是全局作用域。在 i 的值迭代的过程中,j 的值也是跟着迭代。最终全局作用域下,j 只有一个值(i 最后一次循环的值 i=5),而不是五个不同的值。

  1. 构建一个立即执行函数
for (var i = 1; i <= 5; i++) {
  (function() {
    var j = arguments[0]
    setTimeout(function timer() {
    console.log(j)
    }, i*1000)
  })(i)
}

一次打印出 1, 2, 3, 4,5。最后圆括号的 i 的值变成 6。在上面的IIFE中,每次 i 的值迭代就会存储在 IIFE所在的局部作用域中的变量 j 中(IIFE局部作用域中一个存在5个 i 的值分别是 1, 2, 3, 4, 5)。

  1. let 声明 圆括号 i 的值或是花括号内 j 的值

for (let i = 1; i <= 5; i++) {
   // i 的就在(),其实{}是访问不到 i 的值的
  // let i(copy) = i JS自动添加的
    setTimeout(function timer() {
    console.log(i)
    }, i*1000)
  // 结束循环 i(copy)把值赋值给圆括号内部的 i 
}
// 间隔一秒 打印出 1,2,3,4,5

console.log(i)
// 报错
// Uncaught ReferenceError: i is not defined

或是

for (var  i = 1; i <= 5; i++) {
  let j = i
    setTimeout(function timer() {
    console.log(j)
    }, i*1000)
}
// 间隔一秒 打印出 1,2,3,4,5
console.log(i)
// 6

首先两种方式都可以达成目的,3-①每次迭代都存在 {} 内部都会新建一个同名变量,并把迭代的值赋值给这个同名变量let i = i,所以循环结束也就是会在花括号内部存在5个副本i, 分别是i1 i2 i3 i4 i5。循环结束 副本 i 的值,会赋值给i圆括号内部的 i ,(let i = 1; i <= 5; i+=) i 的值值存在这个圆括号内部。


箭头函数

箭头函数的语法

箭头函数的基本用法:
🌰

var fn = function(a) {
  console.log(a)
}

// 等价于下面的箭头函数
var fn1 = a => console.log(a)

语法:

  1. 去掉function关键字

  2. 添加一个 =>

  3. 多个参数加括号,不同参数用逗号隔开。一个参数括号可选择(加与不加括号都行)

  4. 没有参数括号不能省。没有执行语句{}花括号不能省

  5. 只有一个语句花括号可以省略(且同时需要省略return 关键字),多个语句必须加花括号(return 关键字不能省略)

更多细节下面通过🌰来展示:

  • 当箭头函数返回的值是一个对象字面量:
var fn = function() {
  return {name: 'xxx', age: 18}
}

var fn1 = () => {name: 'xxx', age: 18}
// Uncaught SyntaxError: Unexpected token ':'

上面函数 fn 返回的是一个对象字面量,按着下面的方式简化成箭头函数 fn1 会发现报错。所以如果是return 返回一个对象字面量应该用 ()括号 包裹该对象字面量

var fn1 = () => ({name: 'xxx', age: 18})
fn1()
// {name: 'xxx', age: 18}
  • 当箭头函数与IIFE(立即执行函数):
    普通构建的立即执行函数:
(function() {
  console.log('IIFE')
}.call())
// IIFE

(function() {
  console.log('IIFE')
}).call()
// IIFE

箭头函数的立即执行函数

(() => console.log('IIFE'))()
// IIFE

(() => console.log('IIFE')())
// () => console.log('IIFE')()

所以使用箭头函数构造IIFE,只能用(arrow Function)()这种方式。

箭头函数的运用

运用箭头函数的就必须了解箭头函数的特性,以及传统方式声明的函数的一些弊端

  1. 箭头函数可以简化代码。
  1. 箭头函数本身没有this绑定,而传统方式声明的函数this的值往往难以确定。

🌰箭头函数与数组结合简化代码
var arr = [10, 0, 100, -3, 2, 99, 8]。现在的需求是按照从小到大,重排

var arr = [10, 0, 100, -3, 2, 99, 8]
// 直接使用arr.sort这个API
arr.sort(function(v1, v2) {
  return v1 - v2
})
// [-3, 0, 2, 8, 10, 99, 100]

上面的代码如果使用箭头函数会更加简便

arr.sort((v1, v2) => v1 -v2)
// [-3, 0, 2, 8, 10, 99, 100]

所以在使用Array.sort Array.reduce Array.forEach Array.map,对这些API的回调函数使用箭头函数,可以使代码更加简洁。

🌰箭头解决传统声明的函数 this的值在调用之前难以确定难点。

// HTML
// <div>Hello World!</div>

var ARROW = {
  init: function() {
    this.node = document.body
    this.method()
  },
  method: function () {
    this.node.addEventListener('click', function() {
      // 回调函数this的值 
      this.printInfo()
    })
  },
  printInfo: function () {
    console.log(this)
    console.log(this.node)
  }
}

ARROW.init()

上面调用对象ARROW 的 init 方法,最为直观的的感觉就是会执行 ARROW 对象内部的 printIinfo,但真实的的结果是会报错。报错显示回调函数的 this.printInfo() 不是一个函数。

在原生JS函数内部的 this 始终指的是当前调用函数的这个对象,所以 this.printInfo 中的 this 指向的其实是事件的绑定的节点,即是 this.node(body),this.node本身是没有 printInfo这个方法的。所以会报错。

事件监听的回调函数的this的值

如果是为了达到想要的结果。ARROW.method正确的形式应该如下形式

method: function () {
    // that 把 this即是ARROW存储到method函数内部。
    var that = this
        this.node.addEventListener('click', function() {
      // that等同于this,而不是this.node
            that.printInfo()
    })
  }

ARROW.method 的回调函数改成箭头函数的形式,也可以达成上面改进代码的效果

method: function () {
        this.node.addEventListener('click', () => this.printInfo())
    // 箭头函数的this指向与init()方法中this的值形同
  }

在箭头函数没有 this 绑定,所以箭头函数内部的 this 值只能通过查找作用域链来确定。

箭头函数this的值

参考:

《Understanding ECMAScript 6》(简体中文版)

ECMAScript 6 入门-阮一峰


版权声明:本文为博主原创文章,未经博主许可不得转载

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