简介

  • 2015 年 6 月 17 日,ECMAScript 6 发布正式版本,即 ECMAScript 2015。
  • ES6 是继 ES5 之后的一次主要改进,ES6 增添了许多必要的特性,例如:模块和类,以及一些实用特性,例如 Maps、Sets、Promises、生成器(Generators)等。
  • 尽管 ES6 做了大量的更新,但是它依旧完全向后兼容以前的版本,标准化委员会决定避免由不兼容版本语言导致的“web 体验破碎”。结果是,所有老代码都可以正常运行,整个过渡也显得更为平滑,但随之而来的问题是,开发者们抱怨了多年的老问题依然存在。

let 声明变量

let 是新的变量声明

  • 如果声明的位置是在代码块中,那么变量只能在代码块里有用
  • 代码块,类似于 iffor 循环 等等。
1
2
3
4
5
6
if (true) {
let a = 1;
}
console.log(a);

//Uncaught ReferenceError: a is not defined

变量不提升

不像 var ,这个没有变量提升
所以,必须先声明,再使用

1
2
3
4
console.log(b);
let b;

//Uncaught ReferenceError: Cannot access 'b' before initialization

暂时性死区 TDZ

全称:temporal dead zone

声明变量之前不允许使用

1
2
3
4
5
6
7
8
9
if (true) {
/*TDZ start*/
typeof temp;
let temp; /* TDZ end*/

console.log(temp);
}

//Uncaught ReferenceError: Cannot access 'temp' before initialization
  • 注意 TDZ 的区域,从代码块开始,到定义完变量之后,这一小段是 TDZ,
  • 引申:在 es6 中 typeof 不是绝对安全了
1
2
3
4
5
6
7
8
9
10
11
12
//多看代码,加深理解
var c = 1;
function test() {
console.log(c);
if (true) {
//这个c只是在if中有效
let c = 10;
}
}
test();

//1
1
2
3
4
5
6
7
8
9
10
11
12
//跟上一个对比
var c = 1;
function test() {
console.log(c);
if (true) {
//这里有变量提升
var c = 10;
}
}
test();

//undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//这个要注意一下
var a = 1;
function test() {
console.log(a);

//条件是false
if (false) {
/*先进去if中探索一下是否有 var 声明,然后再去判断语句*/
/*这个if在刚开始还是进去的,遇到var也还会提升,只是语句在执行的时候,没有进入if*/
var a = 10;
}
}
test();

//undefined
  • 这里有个情况
1
2
3
4
5
let x;
//这把{x}当作了代码块,解决这个问题的话,整体加圆括号
{x} = {x:1}
console.log(x);
//Uncaught SyntaxError: Unexpected token '='
1
2
3
4
5
6
7
//解决方法
let x;
//{x} = {x:1}
({ x } = { x: 1 });
console.log(x);

//1

const 静态变量 (常量*)

有如下属性

  • 不允许重复声明
  • 声明的时候赋值
  • 声明之后不允许修改
  • let 的作用域一个德行,都是代码块里有效

实质

  • const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动
  • 对于 简单类型 的数据(数值,字符串,布尔值),存在 中,值就保存在变量指向的那个内存地址。
  • 对于 复杂对象(主要是对象和数组),变量指向的内存地址存在 中,保存的是指向,指向的数据怎么变,它管不到

引申

  • 栈:大小固定,自动分配,能由系统自动释放掉
  • 堆:可变大小,不能自动释放掉

对象冻结

用到了 Object.freeze(); 方法

1
const obj = Object.freeze({ name: "张三" });
  • 建议前面写上 const,因为如果你这样,之后也能改变指向,如果你用了 const,就改变不了了
1
2
3
4
5
6
//用var,声明不变的量,也不安全
var obj = Object.freeze({ name: "张三" });
obj = {};
console.log(obj);

//{}
1
2
3
4
5
6
//使用const声明
const obj = Object.freeze({ name: "张三" });
obj = {};
console.log(obj);

//Uncaught TypeError: Assignment to constant variable.
  • 冻结之后,如果想要修改内容的话,是不会报错的,但是并不能修改内容(以为对象冻结住了)
  • 如果想要修改内容的话,就需要解冻。
1
2
3
4
5
const obj = Object.freeze({ name: "张三" });
obj["name"] = "小子";
console.log(obj);

//{name: "张三"}


解构赋值

起步

可迭代的数据都可以解构赋值(数字就不行了)

  • 简单的解构赋值如下
1
2
3
4
5
6
7
8
let [a, b, c] = [1, 2, 3];
console.log(a);
console.log(b);
console.log(c);

//1
//2
//3
  • 快速互换两个值:比方说
1
2
3
4
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log("a,b", a, b); // a,b 2 1
  • 也可以用来当作函数的实参、形参,而且 解构赋值 有个好处,可以传 ""undefinednullfalse 这些东西。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//例子1:普通给默认值的方法
//如果你传的值是undefined等,他是走默认值0的,而我们希望走的是undefined
function test_bad(val) {
val = val || 0;
console.log(val);
}
test_bad("");
test_bad(undefined);
test_bad(null);
test_bad(false);

//0
//0
//0
//0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//例子1的对比,优化
//如果你传来的不是严格的 undefined,那么就会走默认值
function test_good([a, b, c = 0, d = 4, e]) {
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(e);
}
test_good([false, null, "", undefined, "undefined"]);

//false
//null
//
//4
//undefined
  • 如果想要让 obj2 拿到 obj 的内容,修改 obj2 还不影响 obj,那么可以解构 obj (解构之后就变成了个新的对象),然后在赋值给 obj2
1
2
3
4
5
6
7
8
9
let obj = {
a: 1,
b: 2,
};
// let obj2 = {...obj}
let obj2 = obj;
// 如果想要让obj2 拿到obj 的内容,修改obj2还不影响obj,那么可以解构obj,然后在赋值给obj2
obj2.a = 10;
console.log(obj);

…arr 展开运算符

先来一个例子,慢慢理解一下

  • ...arr 一定要保证是在后面,因为它只能解析从某个地方到 数组尾部
1
2
3
4
5
6
7
8
//不能这样:let[...arr,a] = ["ar",2,3,4];

let [a, ...arr] = ["ar", 2, 3, 4];
console.log(a);
console.log(arr);

//ar
//(3) [2, 3, 4]
  • 如果没有值得话,就会生成一个空数组
1
2
3
let [a, b, ...arr] = ["ar"];
console.log(arr);
//Array(0)
  • 也可以遍历字符串
  • 可以设置默认值,如果严格等于 undefined,那么就走默认值(底层是 ===
1
2
3
4
5
6
7
8
9
10
11
12
13
//严格等于 undefined,走默认的值
let [a = 100, b = "poi"] = [undefined];
console.log(a);
console.log(b);
//100
//poi

//不严格等于 undefined ,走赋值
let [a = 100, b = "poi"] = ["undefined"];
console.log(a);
console.log(b);
//undefined
//poi
1
2
3
4
5
6
7
8
9
//再来一个引申
var a = undefined;
var b = "undefined";
//0 == 1
console.log(a == b);
//两等都不行了,三等就更没戏了
console.log(a === b);
//false
//false
  • 还能承接传来的参数,这个叫做 rest
1
2
3
4
5
6
function test(...arr) {
console.log(arr);
}
test(1, 2, 3, 4);

//(4) [1, 2, 3, 4]

…[] 数组拓展运算符

  • 这个就相当于 rest 的逆运算,一个是聚合,一个是拆散,...[1,2,3] 会被拆成 1 2 3
1
2
3
4
5
6
7
8
9
10
11
12
var f1 = (a, b, c, d) => {
console.log(a);
console.log(b);
console.log(c);
console.log(d);

//1
//2
//3
//4
};
f1(1, ...[2, 3, 4]);
  • 这个功能就很强大了,可以跟选择器结合使用
1
2
3
4
5
6
7
8
<body>
<div>1</div>
<div>2</div>
<div>3</div>
</body>
<script>
console.log(...document.querySelectorAll("div"));
</script>

传参

  • 而且与 rest 不同的是,可以不用放到最后
1
2
3
4
5
6
7
8
9
10
11
12
13
//1.解析的时候和rest不同的是,可以不用放到最后
function f1(a, b, c, d, e) {
console.log(a, b, c, d, e);
}
//[2,3,4]放到了参数们的中间,而rest必须放到后面才能正常使用
arr1 = [1, ...[2, 3, 4], 5];
//es5传参
f1.apply(null, arr1);
//1,2,3,4,5

//es6
f1(...arr1);
//1,2,3,4,5

找最大值

1
2
3
4
5
6
7
8
9
var arr1_1 = [10, 2, 4654, 789, 56, 6, 131, 32];

//es5的写法
console.log(Math.max.apply(null, arr1_1));
//4654

//es6的写法
console.log(Math.max(...arr1_1));
//4654

与三目运算符结合

1
2
3
4
var x = 2;
var arr2 = [...(x == 1 ? [1, 2, 3] : [4, 5, 6]), "x"];
console.log(arr2);
//(4) [4, 5, 6, "x"]

空的话,不会报错,控制台里就没有它的打印信息

1
2
var arr3 = [];
console.log(...arr3);

模拟深复制

1
2
3
4
5
const a3 = [3, 4, 5];
//2.模拟深复制
const a4 = [...a3];
console.log(a4);
//(3) [3, 4, 5]

模拟 concat

1
2
3
4
5
6
7
8
var arr3_1 = [1, 2, 3];
var arr3_2 = [4, 5, 6];

console.log(arr3_1.concat(arr3_2));
//(6) [1, 2, 3, 4, 5, 6]

console.log([...arr3_1, ...arr3_2]);
//(6) [1, 2, 3, 4, 5, 6]

结构解析

  • 对象解析当中根据 key 来解析
  • let {匹配模式:变量}
1
2
3
4
5
6
let { c, a } = { b: "6666", a: "555" };
console.log(a);
console.log(c);

//555
//undefined
  • 数组相当于一个特殊的对象,可以用 下标 当作 key 值去解析
1
2
3
4
5
6
7
/*数组的结构解析*/
var arr = ["dsada", "ewq", "zxcvc", 16, 18, "135"];
let { 1: fir, [arr.length - 1]: last } = arr;
console.log(fir);
console.log(last);
//ewq
//135

详细步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//目的:我想解析shuai
var obj = {
foo: ["ufo", { sss: "shuai" }],
};
/*结构解析*/
/*第一步:等号左右类型要一样*/
let {} = obj;

/*第二步:添加匹配模式foo*/
let { foo } = obj;

/*第三步,这个时候b就代表了对象,数组的话就不需要匹配模式,随便一个符号占着茅坑找位置就行*/
let {
foo: [a, b],
} = obj;

/*第四步:把b变成括号,然后再用匹配模式sss,来进入对象中*/
let {
foo: [a, { sss }],
} = obj;

/*第五步:你可以给sss赋值变量l,然后输出l就可以了*/
let {
foo: [a, { sss: l }],
} = obj;
console.log(l);
1
2
//觉得你行了?来试试这个,少年,解析girl
var sister = [{ age: [18, { sex: "girl" }] }];
  • 加深理解接着玩 ,console 也能解析
1
2
3
4
5
/*console是个对象,他有log方法,我把log方法解析出来*/
let { log } = console;
log("111");

//111
1
2
3
4
5
6
/*对象 = 对象,把 math 对象中的属性承接出来,放到 PI 中,因为 PI 是属性值,所以打印就好
* 如果是函数体的话就给参数调用*/
const { PI } = Math;
console.log(PI);

////3.141592653589793
1
2
3
4
5
6
7
8
//let {匹配模式:变量}

//默认是let {foo:foo} = {foo:"cszsdad"};
let { foo: fooValue } = { foo: "cszsdad" };

console.log(fooValue);

//cszsdad
  • 结构解析也能分析继承的
1
2
3
4
5
6
7
Person.prototype.name = "lsl";
function Person() {}
var p = new Person();
let { name: n } = p;
console.log(n);

//lsl
  • 理解了之后自己玩了一个比较瑟琴的
1
2
3
4
5
6
7
8
//给你们答案,不唯一
var sister = [{ age: [18, { sex: "girl" }] }];
let [
{
age: [a, { sex: x }],
},
] = sister;
console.log(x);

惰性求值

1
2
3
4
5
6
7
8
function f() {
throw new Error("this is error");
}
/*惰性求值*/
/*如果有值得话,除了undefined,就一直不走默认的*/
let [x = f()] = [1];
console.log(x);
//1
1
2
3
4
5
6
7
8
9
function f() {
throw new Error("this is error");
}
//这样就走了默认的
let [x = f()] = [undefined];
/*let [x = f()] = [1];*/
console.log(x);

//Uncaught Error: this is error

模板字符串

  • 优点:不需要我们拼接变量(原生的不是得用 + 来拼接成很长的句子嘛)
  • 符号: `` (TAB上面的那个~)
  • 想在模板字符串中插入变量的话用 ${}
  • 还是函数调用的另一种形式
1
2
//函数调用,括号变成``也可以
alert`4`;
1
2
3
4
5
6
7
8
9
10
//模板字符串的简单使用
const obj = {
name: 18,
age: "xiaopang",
habit: "sing",
};
var str = `my name is ${obj.name} ,age is ${a}`;
console.log(str);

//my name is 18 ,age is xiaopang
  • 如果你非要在模板字符串中打出 `` 这个符号,那么可以用转义字符 \
1
2
3
4
5
//转移字符
var str1 = `\`\``;
console.log(str1);

//``
  • 这个还可以插标签,可以进行变量的运算,注意写法、还可以插入方法,而且还会 解析换行,你怎样写,就会在页面上解析成相应的样子,包括换行,空格。
1
2
3
4
5
6
7
8
9
10
11
12
//综合应用
var x = 5;
var arr = [1, 2, 3, 4, 5];
//插入标签,运算,arr.reverse()方法,对象引用
$("#test").html(`
<ul>
<li style="font-size: ${x + 40}px;">1</li>
<li>22\`\`</li>
<li>${arr.reverse()}</li>
<li>${obj.name + x}</li>
</ul>
`);

网页里是这样的

  • 如果当作实参的话,字符串里穿插变量,那么形参中可以接到如下的东西
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var xx = 1;
var yy = 2;
var zz = 3;
function show(a, b, c, d, e) {
/*这个a是实参中所有字符串的字段,并放到数组中*/
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(e);
}
show`this ${xx} is ${yy} a ${zz} iu`;
//(4) ["this ", " is ", " a ", " iu", raw: Array(4)]
//1
//2
//3
//undefined

function show1(a, ...arr) {
console.log(a);
console.log(arr);
}
/*如果变量放到了第一个,前面没有字符串,那么a[0]会被解析成空字符串*/
show1`${xx} is ${yy} a ${zz} this`;
//["", " is ", " a ", " this", raw: Array(4)]
//(3) [1, 2, 3]

//解释:
/*最前面的和最后面的变量,在这里是${xx}和${zz}*/
//最前面的变量${xx},看它的前面,有字符串那么就拿,如果没有就会拿空
//最后面的变量${zz},看他的后面,有字符串那么就拿,如果没有就会拿空