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 = 111console.log(a)console.log(a)// undefined// 111// 111
因为var 声明会有变量提升,且花括号内部并没有形成一个块级作用域。所以执行的结果就会不符合直觉,造成困惑。
2. let 不能重复声明变量
?
let a = 1// var a = 1let 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 也有其独特的地方
- const 声明初始必须赋值,其已经赋值的的变量。不能再次赋值
const a = 1a = 2// Uncaught TypeError: Assignment to constant variable
- const 赋值的的变量是对象字面量,可以修改对象内部属性的值。
const obj = { name: 'xxx', age: '21'}obj['name'] = 'yyy'obj = {anme: 'xxx', age: 21}// b报错// Assignment to constant variable.
- const 适用于常量
const pi = 3.141const 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

错误分析: 这是因为在循环迭代的过程中,贯穿始终的都只有一个 i 在被共享。并没有新的 i 的值产生,所以最后 循环结束 i 也有最后一次循环的一个值(最后一次循环 i = 5, 但是 i++ 还会执行所以最终i的值为6
)。
解决问题:
- 在内部添加一个 j 存储 的值
for (var i = 1; i <= 5; i++) { var j = i setTimeout(function timer() { console.log(i) }, i*1000)}
打印的结果是5,5,5,5,5
, 如下图所示:

分析:
首先上面的代码等价于:
// var 声明变量提升var ivar jfor (i = 1; i <= 5; i++) { j = i setTimeout(function timer() { console.log(i) }, i*1000)}
首先在花括号内部有一个 j 的值可以存储 i, 但是 j 的并不是存在一个局部作用域而是全局作用域。在 i 的值迭代的过程中,j 的值也是跟着迭代。最终全局作用域下,j 只有一个值(i 最后一次循环的值 i=5
),而不是五个不同的值。
- 构建一个立即执行函数
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
)。
- 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,5console.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,5console.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)
语法:
-
去掉function关键字
-
添加一个 =>
-
多个参数加括号,不同参数用逗号隔开。一个参数括号可选择(
加与不加括号都行
) -
没有参数括号不能省。没有执行语句{}花括号不能省
-
只有一个语句花括号可以省略(
且同时需要省略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)()
这种方式。
箭头函数的运用
运用箭头函数的就必须了解箭头函数的特性,以及传统方式声明的函数的一些弊端
- 箭头函数可以简化代码。
- 箭头函数本身没有this绑定,而传统方式声明的函数
this
的值往往难以确定。
?箭头函数与数组结合简化代码var arr = [10, 0, 100, -3, 2, 99, 8]
。现在的需求是按照从小到大,重排
var arr = [10, 0, 100, -3, 2, 99, 8]// 直接使用arr.sort这个APIarr.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这个方法的。所以会报错。

如果是为了达到想要的结果。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 值只能通过查找作用域链来确定。

参考:
《Understanding ECMAScript 6》(简体中文版)
版权声明:本文为博主原创文章,未经博主许可不得转载
原著是一个有趣的人,若有侵权,请通知删除
还没有人抢沙发呢~