# ECMAScript 6

目标:初步掌握ES6,方便后续的开发,未来工作中大量使用 ES6 开发

  1. ECMAScript 6 介绍
  2. ECMAScript 6 新增语法
  3. 内置对象的扩展
  4. ECMAScript 6 降级处理

# ES6介绍

ECMAScript262 标准,最初的JS标准。

2015年发布了ES6,并且更名为ES2015。决定以年号为版本号,并且每年发布一个新版本。

网上所说的ES7、ES8等等,并不可信,因为官方从未承认什么ES7。正确的是:

ES3 --> ES4 --> ES5 --> ES6(2015) --> ES2016 --> ES2017....

为什么要学习ES6?

  • 提供了更加方便的新语法,弥补 JS 语言本身的缺陷

  • 新增了便捷的语法

  • 对内置对象进行了扩展(增加了一些新方法)

  • ES6 让 JS 可以开发复杂的大型项目,成为企业级开发语言

  • 新的前端项目中大量使用 ES6 的新语法

  • ES6 是新的 JS 的代码规范,提供了一些新特性,使我们可以开发大型应用

# let 和 const

☞ let和var一样,用于声明变量;

☞ const用于声明常量,相对于变量来说,常量的值不能够改变

具体区别见下表:

关键字 重复声明 变量提升 块级作用域 初始值 更改值 通过win调用
let × √(暂时性死区) - Yes No
const × √(暂时性死区) Yes No No
var × - Yes Yes

下面分别讨论这几点区别

# 重复声明

// 1. let
let name = 'zhangsan';
let name = 'wangwu'; // 再次声明,报错

// 2. const 
const age = 20;
const age = 30; // 报错

// 3. var
var sex = '男';
var sex = '女';

如果是这样 let a = 1; var a = 2 或者翻过来也会报错;即,有let参与的变量不能重复声明。

# 变量提升

和var一样,代码在执行时,会把声明变量的过程提升;

和var不一样的是,提升的变量在给初始值之前是不能使用的,这就是所谓的暂时性死区。

// 1. let
console.log(height); // 报错,在给值之前不能使用变量。
let height = 170;

// 2. const 和 let一样
console.log(weight); // 报错
const weight = 75;

// 3. var
console.log(color); // undefined
var color = 'yellow';

# 块级作用域

ES6新增块的概念,通俗的说,块就是JS中的大括号;比如 if 条件判断中的大括号;for 循环中的大括号;

块形参块级作用域,也就是块里面的变量,在外部不可用。

/************************************************************/
if (true) {
    // 这里成为一个块
    let a = 1;
    const b = 2;
    var c = 3;
}

console.log(a); // 报错
console.log(b); // 报错
console.log(c); // 3

/************************************************************/
for (let i = 0; i < 3; i++) {
    
}
console.log(i); // 报错,i只能在大括号里面用

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

# 初始值

let、var 声明变量的时候,可以不给初始值;

const 必须给初始值;

# 更改值

let、var 声明的变量,变量,即可以更改值;

const 叫做常量,不能更改值,这也是常量的最大特点;

let a = 1;
a = 2; // 修改值,肯定没问题

const b = 3;
b = 4; // 报错

# 通过window对象调用

let、const 声明的变(常)量,不能通过window调用;

var 声明的变量,可以通过window调用;

let a = 1;
console.log(window.a); // undefined

var b = 2;
console.log(window.b); // 2

# 解构赋值

  1. 数组的解构
  2. 对象的解构

ES 6 允许按照一定**==模式==**,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

# 数组的解构

情况1,变量和值一一对应

// 情况1,变量和值一一对应
let arr = [5, 9, 10];
let [a, b, c] = arr;
console.log(a, b, c); // 输出 5 9 10

情况2,变量多,值少

// 情况2,变量多,值少
let arr = [5, 9, 10];
let [a, b, c, d] = arr;
console.log(a, b, c, d); // 输出 5 9 10 undefined

情况3,变量少,值多

// 情况3,变量少,值多
let arr = [5, 9, 10, 8, 3, 2];
let [a, b] = arr;
console.log(a, b); // 5, 9

情况4,按需取值

// 情况4,按需取值
let arr = [5, 9, 10, 8, 3, 2];
let [, , a, , b] = arr; // 不需要用变量接收的值,用空位占位
console.log(a, b); // 10, 3 

情况5,剩余值

// 情况5,剩余值
let arr = [5, 9, 10, 8, 3, 2];
let [a, b, ...c] = arr; // ...c 接收剩余的其他值,得到的c是一个数组
console.log(a, b, c); 
// 结果:
// a = 5, 
// b = 9, 
// c = [10, 8, 3, 2]

情况6,复杂的情况,只要符合模式,即可解构

// 情况6,复杂的情况,只要符合模式,即可解构
let arr = ['zhangsan', 18, ['175cm', '65kg']];
let [, , [a, b]] = arr;
console.log(a, b); // 175cm 65kg

# 对象的解构

情况1,要求变量名和属性名一样,即可取值,和顺序无关

// 情况1,默认要求变量名和属性名一样
let { bar, foo } = {foo: 'aaa', bar: 'bbb'};
console.log(foo, bar); // aaa, bbb

let {a, c} = {a: 'hello', b: 'world'};
console.log(a, c); // hello, undefined

情况2,可以通过 ":xxx" 为变量改名

// 情况2,可以通过:为变量改名
let {a, b:c} = {a: 'hello', b: 'world'};
console.log(a, c); // hello, world

情况3,剩余值

// 情况3,剩余值
let obj = {name:'zs', age:20, gender:'男'};
let {name, ...a} = obj;
console.log(name, a);
// 结果:
// name = zs
// a = {age: 20, gender: "男"};

情况4,复杂的情况,只要符合模式,即可解构

// 情况4,复杂的情况,只要符合模式,即可解构
let obj = {
    name: 'zhangsan',
    age: 22,
    dog: {
        name: '毛毛',
        age: 3
    }
};
let {dog: {name, age}} = obj;
console.log(name, age); // 毛毛 3

简单应用

// 定义形参,直接使用解构模式
function ajax ({ type, url, data, success }) {
    console.log(type); // GET
    console.log(url); // http://www.liulongbin.top:3006/api/getbooks
    // 。。。
}

// 调用函数,传递一个对象形式的实参
ajax({
    type: 'GET',
    url: 'http://www.liulongbin.top:3006/api/getbooks',
    data: { id: 1 },
    success: function (res) {
        
    }
});

# 箭头函数

ES6 中允许使用箭头定义函数 (=> goes to),目的是简化函数的定义重新定义了函数里面this的指向

# 基本定义

// 非箭头函数
let fn = function (x) {
    return x * 2;
}
// 箭头函数,等同于上面的函数
let fn = (x) => {
    return x * 2;
}
  • () 和 => 之间,不能换行
  • => 和 {} 之间,能够换行

# 写法特点

形参只有一个,可以省略小括号

let fn = (x) => {
    return x * 2;
}
// 等同于
let fn = x => {
    return x * 2;
}

函数体只有一行,可以省略大括号,并且表示返回函数体的内容

let fn = (x, y) => {
    return x + y;
}
// 等同于
let fn = (x, y) => x + y;

# 其他特点

箭头函数内部没有 arguments,可以使用剩余参数获取实参

/************************************************************/
let fn = () => {
    console.log(arguments); // 报错,arguments is not defined
};
fn(1, 2);

/************************************************************/
// 使用 ...c 语法,接收剩余所有实参
// ...c 必须放到形参列表的末尾
let sayHi = (a, b, ...c) => {
    console.log(a); // 1
    console.log(b); // 2
    console.log(c); // [3, 4, 5]
}
sayHi(1, 2, 3, 4, 5);

/************************************************************/
// 直接使用一个 ...c ,可以接收所有实参
let sayHaha = (...c) => {
    console.log(c); // [1, 2, 3, 4, 5]
}
sayHaha(1, 2, 3, 4, 5);

箭头函数内部没有this。会按照作用域链去外层查找。

事件处理函数,不适合使用箭头函数。

let obj = {
    name: 'zhangsan',
    fn: function () {
        setTimeout(() => {
            // 箭头函数,使用this,和使用其他变量一样,需要按照作用域链查找
            console.log(this.name); // zhangsan
        }, 1000)
    }
};
obj.fn();

箭头函数不能作为构造函数

let Person = () => {
	
};
let obj = new Person(); // 报错,Person is not a constructor

# 展开运算符

  1. 展开运算符,可以在构造数组时,把字符串或其他数组展开
  2. 展开运算符,可以在为函数传参时,把字符串或数组展开
  3. 展开运算符,可以在构造字面量对象时,把其他对象展开
// 1. 展开运算符,可以在构造数组时,把字符串或其他数组展开
let x = ['f', 'g'];
let s = '杜海';
let arr = ['a', 'b', ...s, ...x];
console.log(arr); // ["a", "b", "杜", "海", "f", "g"]



// 2. 展开运算符,可以在为函数传参时,把字符串或数组展开
function abc (...val) {
     console.log(val);
}
abc(...'abcd'); // 等同:abc('a', 'b', 'c', 'd');
abc(...['宋江', '吴用']); // 等同 abc('宋江', '吴用')

// 典型应用:查找数组的最大值
let arr = [5, 9, 8, 10, 3, 2, -20, 7];
console.log(  Math.max(...arr)  );



// 3. 展开运算符,可以在构造字面量对象时,把其他对象展开
let obj1 = { id: 1, age: 20 };
let obj2 = { name: 'zs', sex: '男' };

let obj = {
    height: 180,
    weight: 80,
    ...obj1,
    ...obj2
}
console.log(obj);

# 内置对象的扩展

# Array 的扩展

  • forEach() -- 遍历数组
  • filter() -- 过滤数组中的元素
  • map() -- 对每个数组元素应用自定义函数
  • some() -- 判断数组的每个元素是否有满足条件的
  • every() -- 判断数组的每个元素是否都满足条件
  • find() -- 根据条件查找数组中的元素
  • findIndex() -- 根据条件查找元素的下标
  • reduce() -- 计算数组元素的和
// - forEach()  -- 遍历数组
[3, 2, 8, 5, 9].forEach((item, index, self) => {
  // item表示数组的每个元素
  // index表示元素的下标
  // self表示当前的数组
});


// - filter() -- 过滤数组中的元素
// let result = [3, 2, 8, 5, 9].filter(item => {
//   // return 条件
//   return item > 4;
// });
// console.log(result); // 把符合条件的留下,其他的过滤掉

// let result = [3, 2, 8, 5, 9].filter(item => item > 4);
// console.log(result);



// - map() -- 对每个数组元素应用自定义函数
// console.log([3, 2, 8, 5, 9].map(item => item * item));



// - some() -- 判断数组的每个元素是否有满足条件的
// console.log([3, 2, 8, 5, 9].some(item => item > 5))


// - every() -- 判断数组的每个元素是否都满足条件
// console.log([3, 2, 8, 5, 9].every(item => item > 1))


// - find() -- 根据条件查找数组中的元素
// console.log([3, 2, 8, 5, 9].find(item => item > 5)); // 8


// - findIndex() -- 根据条件查找元素的下标
// console.log([3, 2, 8, 5, 9].findIndex(item => item > 5)); // 2


// - reduce() -- 计算数组元素的和
// 写法1:给初始值
// let result = [3, 2, 8, 5, 9].reduce(箭头函数, 100);
let result = [3, 2, 8, 5, 9].reduce((sum, item) => {
  // sum = 100 初始值
  // item = 3 数组的第1个元素
  return sum += item;
}, 100);

console.log(result);


// 写法2:不给初始值
// let result = [3, 2, 8, 5, 9].reduce((sum, item) => {
//   // 默认,sum = 3(数组的第一个元素)
//   // 默认,item = 2(数组的第二个元素)
//   return sum += item;   // sum = 27
// });

// console.log(result);

// console.log([3, 2, 8, 5, 9].reduce((sum, item) => sum += item))

# String 的扩展

  • includes(str, [position])

    返回布尔值,找到了 str ,返回true;否则返回false。

  • startsWidth(str, [position])

    返回布尔值,表示参数字符串是否在原字符串的头部或指定位置

  • endsWith(str, [length])

    返回布尔值,表示参数字符串是否在原字符串的尾部或指定位置。

console.log('hello world'.includes('e', 2)); // false 从位置2开始查找e,没有找到
console.log('hello world'.includes('e')); // true

console.log('hello world'.startsWith('h')); // 未指定位置,看开头是否是h,返回true
console.log('hello world'.startsWith('l', 2)); // 指定位置的字符是l,返回true

console.log('hello world'.endsWith('d')); // 未指定位置,结尾是d,返回true
console.log('hello world'.endsWith('r', 9)); // 先截取9个字符,然后看这9个字符的结尾是不是r。返回true
  • repeat(n)

    将原字符串重复n次。

    let html = '<li>itheima</li>';
    html = html.repeat(10);
    
  • trim()

    去除字符串两边的空白

    console.log('    hello        '.trim()); // hello
    console.log('    hello        '); //    hello        
    

# Set结构

ES6 提供了新的数据结构 Set。它类似于数组,但是==成员的值都是唯一的==,没有重复的值。

Set本身是一个构造函数,用来生成 Set 数据结构。

Set的特点就是该对象里面的成员不会有重复。

// 1. 基本使用
let s = new Set();
// 得到一个空的Set对象
// 调用add方法,向s中添加几个值
s.add(3);
s.add(7);
s.add(9);
s.add(7); // Set对象中的成员都是唯一的,前面添加过7了,所以这里添加无效

console.log(s.size); // 3
console.log(s); // {3, 7, 9}

Set 的成员

  • size:属性,获取 set 中成员的个数,相当于数组中的 length
  • add(value):添加某个值,返回 Set 结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。

另外初始化Set的时候,也可以为其传入数组或字符串,得到的Set对象中的成员不会有重复。

根据这个特点可以完成数组或字符串去重。

// Set 可以通过一个数组初始化
let set = new Set([1, 2, 1, 5, 1, 6]);
console.log(set); //Set(4) {1, 2, 5, 6}
// 数组去重
console.log([...set]); // [1, 2, 5, 6]

// 完成字符串去重
let str = [...new Set('ababbc')].join('');
console.log(str); // abc

# 定义对象的简洁方式

let id = 1;
let name = 'zs';
let age = 20;

// ES6的新方案
let obj = {
    id,  // 属性名id和前面的变量id名字相同,则可以省略 :id
    name,
    nianling: age,
    // 下面的函数是上面函数的简化写法,可以省略 :function 
    fn () {
        console.log(this.name);
    }
};
obj.fn();