目录

  1. ES6新特性
    1. 1.变量声明
    2. 2.结构赋值
    3. 3.模板字符串
      1. 3.1字符串新增的一些方法
    4. 4.箭头函数(解决this问题,书写起来更简单)
      1. 箭头函数最直观的三个特点。
      2. 注意:
      3. 细节:
    5. 5.函数
    6. 6.Spread / Rest 操作符
    7. 7.二进制和八进制字面量
    8. 8.for…of 和 for…in
    9. 9.ES6中的类
      1. 有几点值得注意的是:
      2. 要点
    10. 数组常用方式
      1. 数组新增方法
    11. ES6的Class
    12. 迭代器(Iterators)
    13. 生成器(Generators)
    14. 对象和数组解构

转载:
  带你一起敲敲ES6的新特性
  ES6 新增的常用新特性
  ES6中常用的10个新特性讲解

ES6新特性

1.变量声明

1、常量(const, 不会变量提升,块级作用域,作用域内值不能改,const 对象仍然可以被改变的)
如果const的是一个对象,对象所包含的值是可以被修改的。抽象一点儿说,就是对象所指向的地址没有变就行:

1
2
3
4
const student = { name: 'cc' }

student.name = 'yy';// 不报错
student = { name: 'yy' };// 报错

1
2
3
4
const aa = {a: '12'};
aa.a // 12
aa.a = "123456";
console.log(aa); // a: '123456'

2、块级作用域(let,不会变量提升)

1
2
3
4
5
6
7
8
9
10
for (let i = 0; i<5; i++) {
setTimeout(() => {
console.log(i) //0 1 2 3 4
},30)
}
for (var i = 0; i<5; i++) {
setTimeout(() => {
console.log(i) //5 5 5 5 5
},30)
}

注意:

1
2
3
4
5
6
7
8
let 关键词声明的变量不具备变量提升(hoisting)特性
let 和 const 声明只在最靠近的一个块中(花括号内)有效
当使用常量 const 声明时,请使用大写变量,如:CAPITAL_CASING
const 在声明时必须被赋值
let const:
块级作用域
不可重复声明
不存在变量提升

  特别要说明一点的是对于const和let都有暂存死区,所谓暂存死区就是:如果作用域内有这样一个变量那么这个作用域内就会绑定这个变量,不会继续向上查找了,以下代码运行会报错

1
2
3
4
5
6
const a = 1;
{
console.log(a);
const a = 2;
}
console.log(a)

2.结构赋值

  所谓解构赋值就是 声明和赋值都放到了一起一般都是数组 对 数组, 对象 对 对象, 数组能够设置默认值,对象也能够设置默认值,默认值必须采用等号的方式

1
2
3
4
5
let [zhan, si, xl = 5] = [3, 4];
console.log(zhan, si, xl) //3, 4, 5

let {name, age = 23} = {name: 'xl', bigAge: 24}
console.log(name, age) //xl, 23

特别的,可能有时会有关键字的情况可以通过:的形式来更改名字,看下面代码

1
2
let { name, age: xl, default: d } = { name: 'xlei', age: 9, default: 'xxx' };
console.log(name, xl, d);

来一个默认值的具体应用吧:

1
2
3
4
5
6
7
8
9
10
11
function ajax({
url = new Error('url without'),
type = 'get',
data = xxx
}){
console.log(data, type) //{a: 5}, get
}
ajax({
url: '/test',
data: {a:5}
})

3.模板字符串

1、模板字符串(拼接方便,可以换行)

1
2
基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定;
ES6反引号(``)直接搞定;

1
2
let exe1 = '张三'
let exe2 = `我的名字是:${exe1}`

2、startWith, endWith 返回一个布尔值

1
2
3
4
5
let str1 = 'www.bsym.online'
let str2 = 'http://www.bsym.online'
console.log(str1.startsWith('http://')) //false
console.log(str2.startsWith('http://')) //true
console.log(str2.endsWith('online')) //true

3、padStart, padEnd补全 – 不会删除原有内容

1
2
3
4
5
// padStart padEnd 补全(记住只能增加,不能减少)
let str1 = 'nihao'
let newStr = str1.padStart(8,'xl')
let newStr2 = str1.padEnd(8,'xl')
console.log(newStr, newStr2) //xlxnihao, nihaoxlx

3.1字符串新增的一些方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
let str = 'imshusheng.com';

# 查看字符串中是否包含某些字符
console.log(str.includes('.'));
console.log(str.includes('shu'));

# 查看字符串是否以某个字符串开头
console.log(str.startsWith('i'));
console.log(str.startsWith('im'));

# 查看字符串是否以某个字符串结尾
console.log(str.endsWith('com'));
console.log(str.endsWith('m'));

# 字符串重复3遍
console.log(str.repeat(3))

# 如果字符串的长度不满20位,则在其后面补字符s
console.log(str.padEnd(20, 's'));

# 如果字符串的长度不满20位,则在其前面补字符s
console.log(str.padStart(20, 's'));
}

4.箭头函数(解决this问题,书写起来更简单)

  传统函数内的this是定义时所在的环境,而箭头函数内的this是使用时上下文的环境。
  ES6 中,箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体;

箭头函数最直观的三个特点。
1
2
3
不需要 function 关键字来创建函数
省略 return 关键字
继承当前上下文的 this 关键字
1
2
3
4
5
6
7
8
9
10
var add = (a, b) => a + b;
[1,2,3].map(x => x + 1);

let aa = (arg1, arg2) => {
console.log(arg1, arg2)
}
aa(1, 2) //1, 2
;((arg1, arg2) => {
console.log(arg1, arg2)
})(3, 4);
注意:

  这里顺带提一下,像上面的自执行匿名函数前后都要加分号,这样既不会被坑,也不会坑别人。另外不要使用箭头函数的argeuments

细节:

  当你的函数有且仅有一个参数的时候,是可以省略掉括号的。当你函数返回有且仅有一个表达式的时候可以省略{}return

5.函数

  在ES6之前,我们往往这样定义参数的默认值:

1
2
3
4
5
6
7
8
9
10
11
12
13
# ES6之前,当未传入参数时,text = 'default';
function printText(text) {
text = text || 'default';
console.log(text);
}

# ES6;
function printText(text = 'default') {
console.log(text);
}

printText('hello'); // hello
printText();// default

6.Spread / Rest 操作符

Spread / Rest 操作符指的是 ...,具体是 Spread 还是 Rest 需要看上下文语境。
  当被用于迭代器中时,它是一个 Spread 操作符:

1
2
3
4
5
function foo(x,y,z) {
console.log(x,y,z);
}
let arr = [1,2,3];
foo(...arr); // 1 2 3

  当被用于函数传参时,是一个 Rest 操作符:当被用于函数传参时,是一个 Rest 操作符:

1
2
3
4
function foo(...args) {
console.log(args);
}
foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]

7.二进制和八进制字面量

  ES6 支持二进制和八进制的字面量,通过在数字前面添加 0o 或者0O 即可将其转换为八进制值:

1
2
3
4
5
let oValue = 0o10;
console.log(oValue); // 8

let bValue = 0b10; // 二进制使用 `0b` 或者 `0B`
console.log(bValue); // 2

8.for…of 和 for…in

  for…of 用于遍历一个迭代器,如数组:

1
2
3
4
5
6
let letter = ['a', 'b', 'c'];
letter.size = 3;
for (let letter of letters) {
console.log(letter);
}
// 结果: a, b, c

  for…in 用来遍历对象中的属性:

1
2
3
4
5
6
let stu = ['Sam', '22', '男'];
stu.size = 3;
for (let stu in stus) {
console.log(stu);
}
// 结果: Sam, 22, 男

9.ES6中的类

  ES6 中支持 class 语法,不过,ES6的class不是新的对象继承模型,它只是原型链的语法糖表现形式。
  函数中使用 static 关键词定义构造函数的的方法和属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Student {
constructor() {
console.log("I'm a student.");
}

study() {
console.log('study!');
}

static read() {
console.log("Reading Now.");
}
}

console.log(typeof Student); // function
let stu = new Student(); // "I'm a student."
stu.study(); // "study!"
stu.read(); // "Reading Now."

  类中的继承和超集:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Phone {
constructor() {
console.log("I'm a phone.");
}
}

class MI extends Phone {
constructor() {
super();
console.log("I'm a phone designed by xiaomi");
}
}
let mi8 = new MI();

  extends 允许一个子类继承父类,需要注意的是,子类的constructor 函数中需要执行 super() 函数。
当然,你也可以在子类方法中调用父类的方法,如super.parentMethodName()
在 这里 阅读更多关于类的介绍。

有几点值得注意的是:

  类的声明不会提升(hoisting),如果你要使用某个 Class,那你必须在使用之前定义它,否则会抛出一个 ReferenceError 的错误
  在类中定义函数不需要使用 function 关键词

要点

1、数组的扩展运算符:将一个数组转为用逗号分隔的参数序列

1
2
3
let arr = [...[1, 2, 3], ...[4, 5, 6]]
console.log(arr) // 1, 2, 3, 4, 5, 6
console.log(Math.min(...arr)) // 1

2、对象的解构赋值
  对象的 Rest 解构赋值用于从一个对象取值,相当于将所有可遍历的、但尚未被读取的属性,分配到指定的对象上面,注意Rest 解构赋值必须是最后一个参数,否则会报错。Rest解构赋值所在的对象,拷贝了对象obj的属性,Rest解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么Rest解构赋值拷贝的是这个值的引用,而不是这个值的副本,解构赋值不会拷贝继承自原型对象的属性

1
2
3
4
5
6
7
let obj = {name: 'xl', age: 23, say:'ok', eat: {xl: 'okok'}}
//尚未被读取的属性,分配到指定的对象上面,浅拷贝了对象obj的属性
let {name, age, ...z} = obj
obj.say = 'oo'
obj.eat.xl = 'o?o?'
console.log(name, age, z)
//xl 23 { say: 'ok', eat: { xl: 'o?o?' } }

1
2
3
4
5
6
7
8
9
let z = {a: 3, b: 4, c:{
eat:'ok'
}}
// 注意这个地方和直接赋值的区别 let n = z;
// 一个是浅拷贝对象属性,一个是浅拷贝对象
let n = {...z}
z.a = 5
z.c.eat = 'ok?'
console.log(n) //{ a: 3, b: 4, c: { eat: 'ok?' } }

  那么要是想实现一个深拷贝,怎么实现呢?其实就是遍历属性如果属性是一个普通值就赋值,不是普通值就递归知道是普通值为止,然后赋值,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 实现深拷贝 保留继承关系 可以实现各种类型的拷贝 实现递归拷贝
function deepClone(obj) {
if (typeof obj !== 'object') return obj;
if (obj == null) return null;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
let o = new obj.constructor(); // 保留类的继承关系

Object.keys(obj).forEach((key, index) => {
if(typeof (obj[key]) == 'object'){
o[key] = deepClone(obj[key])
}else{
// console.log(obj[key])
o[key] = obj[key]
}
})
return o;
}
let o = { a: { a: 1 }, b: function(){
console.log(this.a)
} }
let newObj = deepClone(o);
o.a.a = 2;
console.log( newObj.b());

数组常用方式

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
32
// (1)map返回值 返回值是一个新数组
Array.prototype.map = function (fn) {
let arr = [];
for (let i = 0; i < this.length; i++) {
arr.push(fn(this[i], i));
}
return arr;
};
let arr = [1, 2, 3].map(item => {
return item * 2;
});
console.log(arr);
// (2)filter 过滤 如果返回true表示留下 返回false表示删除
let arr = [1, 2, 3];
let filterArr = arr.filter(item => {
return item > 2;
});
console.log(filterArr);
// (3)some找到后返回true,找false可以用every
let r = [2, 1, 3].some(item => {
return item > 2;
});
console.log(r); //true
// (4)every 检测数组 ages 的所有元素是否都符合条件 :

var ages = [2, 1, 3];

let r = ages.every((item) => {
return item > 2
})
console.log(r) //false
// (5)Array.from(); 将类数组转为数组
数组新增方法
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
{
// 将几个值转成数组
let a = Array.of(1, 3, 5, 7, 9);
console.log(a)

// 将集合等数据类型转成数组
let b = new Set([1, 3, 5, 7, 9]);
console.log(b);
console.log(Array.from(b));

// 将数组中的值替换成我们设置的值
let d = ['a', 'b', 'c', 'd', 'e'];
console.log(d.fill(100));
console.log(d.fill(101, 2));
console.log(d.fill(102, 2, 3));

}
let e = ['a', 'b', 'c', 'd', 'e'];

//之前遍历数组
for (let i in e){
console.log(i)
}

//es6 中新增的几种遍历数组方式
for (let i of e){
console.log(i)
}

for (let i of e.keys()){
console.log(i)
}

for (let i of e.values()){
console.log(i)
}

for (let [i, j] of e.entries()){
console.log(i, j)
}
let f = [1, 3, 5, 7, 9];
// find 会将数组的每一个元素放入到函数中进行迭代处理
// 将第一个满足条件的值取出来
console.log(f.find(function (x) {
return x > 5
}));

// findIndex 会将数组的每一个元素放入到函数中进行迭代处理
// 将第一个满足条件的值的键取出来
console.log(f.findIndex(function (x) {
return x > 5
}));

// 检验某个值是否在数组内
console.log(f.includes(5))

ES6的Class

先复习一下es5中的几个名词:

1
2
3
4
成员属性(方法)| 实例属性(方法) :在构造函数中通过this.属性声明的
静态属性(方法):通过类来声明的 类.xxx
私有属性(方法):只有在类的内部可以使用,其他任何地方都不可以使用的
公有属性(方法)|原型属性(方法):在原型上声明的属性或者方法 xx.prototype.xxx

1
2
3
4
5
6
7
8
9
10
11
12
function Parent(name) {
this.name = name; //成员属性|实例属性
this.say = function() { //成员方法
console.log(this.name)
}
}
//静态属性
Parent.smoking = 'no'
//公有方法|原型方法
Parent.prototype.up = function() {
console.log('ok')
}

再来说说es6中的class(es6中不考虑私有属性和方法):
ES6 中支持 class 语法,不过,ES6的class不是新的对象继承模型,它只是原型链的语法糖表现形式。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class Parent{
constructor(x, y){
this.x = x;
//成员属性|实例属性 可遍历 打印实例可直接打印出来,
// 与 ES5 一样,实例的属性除非显式定义在其本身(即定义在this对象上),
// 否则都是定义在原型上(即定义在class上)。
this.y = y;
//如果不返回 默认返回实例对象 this
return this.x
}
static b(){ // 属于类上的方法 也称静态方法
return 2;
}
eat(){
//原型上的方法 | 公有方法 并且都是不可枚举的
// 打印实例不能显示的打印出来
console.log(this.x);
return this.x
}
}
class Child extends Parent{ //
constructor(x, y, z){
// Parent.call(this);返回的是子类的实例,
super(x, y);
this.age = z; // 成员属性|实例属性
}
static a(){ // 属于类上的方法
return 1;
}
smoking(){ // 原型上的方法
return super.eat() + this.age
//需要说明的是 super不仅可以调用父类的原型方法
// 还可以调用父类的静态方法,方法内部的this指向当前的子类,
// 而不是子类的实例
// console.log(this.age)
}
}
let child = new Child(2, 3, 4);
// console.log(child);
console.log(child.smoking())

class Foo {
constructor() {
//constructor函数默认返回this,
//这里返回一个全新的对象,结果导致实例对象不是Foo类的实例
return Object.create(null);
}
}

new Foo() instanceof Foo
// false

//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}

var point = new Point(2, 3);
console.log(point)
point.toString() // (2, 3)

point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true

class A {
}
class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

迭代器(Iterators)

生成器(Generators)

对象和数组解构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 对象
const student = {
name: 'Sam',
age: 22,
sex: '男'
}
// 数组
// const student = ['Sam', 22, '男'];

// ES5;
const name = student.name;
const age = student.age;
const sex = student.sex;
console.log(name + ' --- ' + age + ' --- ' + sex);

// ES6
const { name, age, sex } = student;
console.log(name + ' --- ' + age + ' --- ' + sex);