一、let和const命令
let不存在变量提升
var命令会发生”变量提升“现象,即变量可以在声明之前使用,值为undefined。let命令改变了语法行为
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
let会形成块级作用域
一般认为块级作用域就是使用{}包起来的块。变量只在这个块里起作用
{
let a = 1;
}
console.log(a);// 报错,a is not defined
块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了,来,说一道面试题
var funcs = []
for (var i = 0; i < 10; i++) {
funcs.push(function() { console.log(i) })
}
funcs.forEach(function(func) {
func()
})
这样的面试题是大家很常见,很多同学一看就知道输出十次10 但是如果我们想依次输出0到9呢?
// 再来看看es6怎么处理的
const funcs = []
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i)
})
}
funcs.forEach(function(func) {
func()
})
达到相同的效果,ES6 简洁的解决方案是不是更让你心动!!!
暂时性死区
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”简称 TDZ
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
不允许重复声明
let不允许在相同作用域内,重复声明同一个变量
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
const基本用法
const声明一个只读的常量。一旦声明,常量的值就不能改变,
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
我们通常用 let 和 const 来声明,let 表示变量、const 表示常量。const的作用域与let命令相同:只在声明所在的块级作用域内有效,const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用,const声明的常量,也与let一样不可重复声明。
const本质
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
const a = [];
a.push('Hello'); // 可执行
a.length = 0; // 可执行
a = ['Dave']; // 报错
上面代码中,常量a储存的是一个地址,这个地址指向一个数组。不可变的只是这个地址,即不能把a指向另一个地址,但数组本身是可变的,如果将另一个数组赋值给a,就会报错。
二、变量的解构赋值
数组的解构赋值
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
解构赋值允许指定默认值.
let [x, y = 'b'] = ['a']; // x='a', y='b'
如果解构不成功,变量的值就等于undefined。
let [foo] = [];
let [bar, foo] = [1];
对象的解构赋值
解构不仅可以用于数组,还可以用于对象。
let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
解构赋值的用途
(1)交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
(2)从函数返回多个值
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
三、模板字符串
模板字符串使用十分方便,经常用于输出一个结果。举个例子
var name = "小强"
console.log("我的名字叫"+name)
//模板字符串表达
console.log(`我的名字叫${name}`)
四、箭头函数
ES6 允许使用“箭头”(=>)定义函数
var f = function (v) {
return v*v;
};
//转化为箭头函数如下
var f = (v)=>{
return v*v;
}
//当你的函数有且仅有一个参数的时候,是可以省略掉括号的。当你函数返回有且仅有一个表达式的时候可以省略{} 和 return
var f = v => v*v;
五、数组的扩展
扩展运算符
扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
扩展运算符的应用
(1)复制数组 (注意:使用扩展运算符复制一个数组属于深拷贝,两个数组之间互相独立,改变其中一个的值不会影响另一个的值)
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
(2)合并数组
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
(3)与解构赋值结合
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = ["foo"];
first // "foo"
rest // []
(4)扩展运算符还可以将字符串转为真正的数组
[...'hello']
// [ "h", "e", "l", "l", "o" ]
Array.from()
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
值得提醒的是,扩展运算符(...)也可以将某些数据结构转为数组。
Array.of()
Array.of方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。
数组实例的 find() 和 findIndex()
数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
[1, 4, -5, 10].find((n) => n < 0)
// -5
数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
数组实例的 fill()
fill方法使用给定值,填充一个数组。
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
上面代码表示,fill方法从 1 号位开始,向原数组填充 7,到 2 号位之前结束。
数组实例的 includes()
Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
六、class
class创建类
(1)class 是关键字,后面紧跟类名,类名首字母大写,采取的是大驼峰命名法则。类名之后是{}。
(2)在{}中,不能直接写语句,只能写方法,方法不需要使用关键字
(3)方法和方法之间没有逗号。不是键值对
举个栗子:
class NBAPlayer{
constructor(name,age,height){
this.name = name
this.age = age
this.height = height
}
say(){
console.log(`我是${this.name},我今年${this.age},我的身高${this.height}`)
}
}
var rs = new NBAPlayer("姚明","35","226")
rs.say()
使用extends实现继承
注意:在子类中的构造器 constructor 中,必须要显式调用父类的 super 方法,如果不调用,则 this 不可用
class NBAPlayer{
constructor(name,age,height){
this.name = name
this.age = age
this.height = height
}
say(){
console.log(`我是${this.name},我今年${this.age},我的身高${this.height}`)
}
}
class MVP extends NBAPlayer{
constructor(name,age,height,year){
super(name,age,height)
this.year = year
}
sayi(){
console.log(`我是${this.name},我在${this.year}拿MVP`)
}
}
var rs = new MVP("姚明","35","226","2014")
rs.say()
rs.sayi()
七、set和map
(1)set
set和数组差不多,也是一种集合,区别在于:它里面的值都是唯一的,没有重复的。Set 本身是一个构造函数,用来生成 Set 数据结构。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
上面代码通过add方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。for of 一种遍历方式
// 去除数组的重复成员
[...new Set(array)]
(2)map
它类似于对象,里面存放也是键值对,区别在于:对象中的键名只能是字符串,如果使用map,它里面的键可以是任意值。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
上面代码使用 Map 结构的set方法,将对象o当作m的一个键,然后又使用get方法读取这个键,接着使用delete方法删除了这个键
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"
作为构造函数,Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
八、严格模式
JS语法非常灵活,JS中这个灵活的特性,弊大于先利。后来增加了严格模式。使用严格模式的目的:规则,提高编译效率。
怎么去启动严格模式: "use strict"
严格模式和非严格模式有什么区别:
1.在严格模式下不能使用没有var的变量
2.在严格模式下不能8进制的数字
3.在严格模式下不能把函数定义在if语句中
4.在严格模式下函数不能有重名的形参
5.在严格模式下不能arguments就不能使用了
6.在严格模式下不能function中的this就再在是window
初学ES6,文章有误请指点,文内部分用词不准确也请谅解