ES6新特性

let 声明变量以及特点

1.变量不能重复声明

let a = 1;
let a = 2;

// Uncaught SyntaxError: Identifier 'a' has already been declared

2.let 作用域为块级作用域

{
    let b = 1;
}
console.log(b);

// Uncaught ReferenceError: b is not defined

3.不存在变量提升

console.log(c);
let c = 1;

// Uncaught ReferenceError: Cannot access 'c' before initialization
// 代码执行之前不会收集变量,而如果使用var会赋一个初始值undefined

4.不影响作用域链

{
    let d = 1;
    function fn() {
        console.log(d);
    }
    fn();
}

// 函数作用域里没有变量d,会向上一级作用域找

const 声明常量以及特点

1.必须赋初始值

const A;

// Uncaught SyntaxError: Missing initializer in const declaration

2.一般常量使用大写字母

3.常量的值不能修改

const B = 1;
B = 2;

// Uncaught TypeError: Assignment to constant variable.

4.const 作用域为块级作用域

{
    const C = 1;
}
console.log(C);

// Uncaught ReferenceError: C is not defined

5.对于数组和对象的元素修改,不会报错

const NUMS = [1,2,3];
NUMS.push(4);

// 因为NUMS指向的地址没有改变
// 声明数组和对象可以使用const

变量的解构赋值

1.数组的解构赋值

const NUMS = [1,2,3];
let [a,b,c] = NUMS;

2.对象的解构赋值

const STUDENT  = {
    name: '小明',
    age: 20,
    learn: function(){
        console.log('学习');
    }
}
let {name, age, learn} = STUDENT;

模板字符串

1.声明

let str = `字符串`;
console.log(str, typeof str);

// 字符串 string

2.内容中可以直接出现换行符

let str = `<div>
			   <span></span>
		   </div>`;

3.变量拼接

let a = 1;
let b = `${a}>0`;

对象和函数的简化写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法

let name = 'zhangsan';
let change = function(){
    console.log('我是zhangsan');
}

const ZHANGSAN = {
    name,
    change
}

函数简化

test(){
    console.log('test');
}

箭头函数以及声明特点

1.声明

() => {}

2.this 是静态的,始终指向函数声明时所在作用域的 this 的值

function getName() {
    console.log(this.name);
}
let getName2 = () => {
    console.log(this.name);
};
window.name = 'zhangsan';
const STUDENT = {
    name: 'lisi'
};

1.直接调用

getName();  // zhangsan 
getName2(); // zhangsan

2.call方法调用

getName.call(STUDENT.name)  // lisi
getName2.call(STUDENT.name) // zhangsan

2.不能作为构造函数实例化对象

let Person = (name, age) => {
    this.name = name;
    this.age = age;
};
let p1 = new Person('zhangsan', 20);
console.log(p1);

// Uncaught TypeError: Person is not a constructor

3.不能使用 arguments 变量

arguments可以获取函数调用时所有的实参

let test = () => {
    console.log(arguments);
};
test(1,2,3);

// Uncaught ReferenceError: arguments is not defined

4.箭头函数的简写

1.当形参有且只有一个时,可以省略小括号

let add = n => {
    return n + n;
}

2.当代码体只有一条语句时,可以省略花括号,此时 return 也必须省略,语句的执行结果就是函数的返回值

let add = (a, b) => a + b;

箭头函数适合与 this 无关的回调,如定时器和数组的方法回调

箭头函数不适合与 this 有关的回调,如事件回调和对象的方法

函数参数的默认值设置

1.形参初始值具有默认值的设置,一般放在最后

function add(a, b, c=10){
    return a + b + c;
}

console.log(add(1, 2));  // 13
console.log(add(1, 2, 3));  // 6

2.与解构赋值结合

function connect({host='127.0.0.1', username, password, port}){
    console.log(host);
    console.log(username);
    console.log(password);
    console.log(port);
}
connect({
    // host: 'localhost',
    username: 'root',
    password: '123456',
    port: 3306
})

// 127.0.0.1
// root
// 123456
// 3306

rest 参数

ES6 引入 rest 参数,用于获取函数的实参,代替 arguments

1.rest 参数放在函数形参位置,是一个数组

function test(...args) {
    console.log(args);
}
test(1, 2, 3);

// [1, 2, 3]

2.rest 参数必须要放到最后

function test(a, b, ...args) {
    console.log(a);
    console.log(b);
    console.log(args);
}
test(1, 2, 3, 4, 5, 6);

// 1
// 2
// [3, 4, 5, 6]

扩展运算符

扩展运算符可以把数组转化为参数的序列

1.数组的合并

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr = [...arr1, ...arr2];
console.log(arr);

// [1, 2, 3, 4, 5, 6]

2.数组的拷贝(浅拷贝)

const arr1 = [1, 2, 3];
const arr2 = [...arr1];
console.log(arr2);

// [1, 2, 3]

3.将伪数组转换为真数组

const divs = document.querySelectorAll('div');
console.log(divs);
const divArr = [...divs];
console.log(typeof divArr);

Symbol

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值,给对象添加属性或方法。它是 JavaScript 的第七种数据类型,是一种类似于字符串的数据类型

JavaScript 数据类型:USONB

U:undefined

S:String Symbol

O:Object

N:Null Number

B:Boolean

1.Symbol 的值是唯一的,用来解决命名冲突的问题

2.Symbol 的不能与其他数据进行运算

3. Symbol 定义的对象属性不能使用 for...in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名

用 Symbol 给对象添加方法

let game = {
    name: 'LOL',
    up(){
        console.log('up');
    },
    down(){
        console.log('down');
    }
};
let methods = {
    up1: Symbol(),
    down: Symbol()
};
game[methods.up1] = function () {
    console.log('up up');
};
game[methods.down] = function () {
    console.log('down down');
};
console.log(game);

// {name: "LOL", up: ƒ, down: ƒ, Symbol(): ƒ, Symbol(): ƒ}
let game2 = {
    name: 'LOL',
    [Symbol('say')]: function () {
        console.log('发言');
    }
};
console.log(game2);

// {name: "LOL", Symbol(say): ƒ}

迭代器

迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要不输 Iterator 接口,就可以完成遍历操作

1.ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费

2.原生具备 Iterator 接口的数据(可用 for...of 遍历)

  1. Array
  2. Arguments
  3. Set
  4. Map
  5. String
  6. TypedArray
  7. NodeList

3.工作原理

  1. 创建一个指针对象,指向当前数据结构的起始位置
  2. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  4. 每调用 next 方法返回一个包含 value 和 done 属性的对象

数组使用 for...of

const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
for(let v of xiyou){
    console.log(v);
}

// 唐僧
// 孙悟空
// 猪八戒
// 沙僧

数组使用 for...in

const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
for(let k in xiyou){
    console.log(k);
}

// 0
// 1
// 2
// 3

可以看出 for...in 返回的是键名,而 for...of 返回的是键值

为什么数组可以使用 for...of 遍历?

const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
console.log(xiyou);

发现其中包含一个 Symbol(Symbol.iterator): *ƒ values()* 属性

console.log(xiyou[Symbol.iterator]());

其中就包含一个 next 方法,用来遍历

生成器函数

生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同

1.声明

function * gen(){}

生成器函数的特殊:

function * gen(){
    console.log('hello generator');
}
let iterator = gen();
console.log(iterator);

查看控制台并没有输出 hello generator,而返回的 iterator 是一个迭代器对象

iterator.next();

控制台输出 hello generator

生成器函数直接调用并不会执行,必须调用 iterator 的 next 方法才会执行

2.yield

yield 是什么?

  • yield 是 ES6 的新关键字,使生成器函数执行暂停,yield 关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器版本的 return 关键字。
  • yield 关键字实际返回一个 IteratorResult(迭代器)对象,它有两个属性,value 和 done,分别代表返回值和是否完成。
  • yield 无法单独工作,需要配合 generator (生成器)的其他函数,如 next,懒汉式操作,展现强大的主动控制特性。

使用 yield

function * gen(){
    yield 'AAA';
    yield 'BBB';
    yield 'CCC';
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

/*
{value: "AAA", done: false}
{value: "BBB", done: false}
{value: "CCC", done: false}
{value: undefined, done: true}
*/

三个 yield 把 gen 函数分成四块,通过 next 方法控制代码的执行

因为 gen 返回的是一个迭代器对象,所以还可以使用 for...of 进行遍历

function * gen(){
    yield 'AAA';
    yield 'BBB';
    yield 'CCC';
}
for(let v of gen()){
    console.log(v);
}

/*
AAA
BBB
CCC
*/

每一次返回的结果就是 yield 后面表达式的值

3.生成器函数参数

function * gen(arg){
    console.log(arg);
    yield 'AAA';
    yield 'BBB';
    yield 'CCC';
}
let iterator = gen(111);
console.log(iterator.next());

/*
111
{value: "AAA", done: false}
*/

而 next 方法也可以传递参数

function * gen(){
    let one = yield 'AAA';
    console.log('第一个yield的返回结果:'+one);
    yield 'BBB';
    yield 'CCC';
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next(222));

/*
{value: "AAA", done: false}
第一个yield的返回结果:222
{value: "BBB", done: false}*/

其中第二次调用 next 方法传入的实参将作为第一个 yield 语句的返回结果

4.生成器函数实例

需求:在控制台经过1s输出111,2s输出222,3s输出333

传统做法:

setTimeout(() => {
    console.log(111);
    setTimeout(() => {
        console.log(222);
        setTimeout(() => {
            console.log(333);
        },3000)
    },2000)
},1000)

但是这样就使得代码一层套一层,从而造成回调地狱,不利于维护

使用生成器函数:

function one() {
    setTimeout(() => {
        console.log(111);
        iterator.next();
    },1000)
}
function two() {
    setTimeout(() => {
        console.log(222);
        iterator.next();
    },2000)
}
function three() {
    setTimeout(() => {
        console.log(333);
        iterator.next();
    },3000)
}

function * gen() {
    yield one();
    yield two();
    yield three();
}

let iterator = gen();
iterator.next();

这样观感就很好,也利于维护,成功解决了回调地狱的问题

Promise

Promise 是 ES6 引入的异步编程的新解决方案,语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果

1. Promise 对象的特点

  1. 对象的状态不受外界影响,Promise 对象代表一个异步操作,有三种状态:pending (进行中)、fulfilled (已成功)和 rejected (已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected,这两种情况只要发生,状态就凝固了,不会再变了。

2.Promise 的 then 方法

new Promise((resolve, reject) => {
    setTimeout(() => {
        let data = '用户数据';
        resolve(data);
    },2000)
}).then(value => {
    console.log(value);
}, reason => {
    console.error(reason);
})

resolve 表示将 Promise 对象的状态改为成功,此时就会执行 then 中的第一个回调函数

new Promise((resolve, reject) => {
    setTimeout(() => {
        let err = '读取数据失败';
        reject(err);
    },2000)
}).then(value => {
    console.log(value);
}, reason => {
    console.error(reason);
})

reject 表示将 Promise 对象的状态改为失败,此时就会执行 then 中的第二个回调函数

3.Promise 的 catch 方法

new Promise((resolve, reject) => {
    setTimeout(() => {
        let err = '读取数据失败';
        reject(err);
    },2000)
}).then(value => {
    console.log(value);
}).catch(reason => {
    console.error(reason);
})

catch 用来捕获异常,同 then 方法中的第二个回调函数

Set 集合

ES6 提供了新的数据结构 Set。它类似于数组,但成员的值都是单一的,集合实现了 Iterator 接口,所以可以使用扩展运算符和 for...of 进行遍历

1.Set 的属性和方法

  1. size:返回 Set 的元素个数
  2. add:增加一个新元素,返回当前集合
  3. delete:删除元素,返回 boolean 值
  4. has:检测集合中是否包含某个元素,返回 boolean 值
  5. clear:清空集合,返回 undefined

2.声明 Set

let s = new Set()

3.集合实践

1.数组去重

const arr = [1,2,2,3,4,4,5];
let result = [...new Set(arr)];
console.log(result);

2.求交集

const arr = [1,2,2,3,4,4,5];
const arr2 = [1,2,3];
let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
console.log(result);

3.求并集

const arr = [1,2,2,3,4,4,5];
const arr2 = [1,2,3];
let result = [...new Set([...arr, ...arr2])];
console.log(result);

4.求差集

const arr = [1,2,2,3,4,4,5];
const arr2 = [1,2,3];
let result = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
console.log(result);

Map 集合

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 Iterator 接口,所以可以使用扩展运算符和 for...of 进行遍历

1.Map 的属性和方法

  1. size:返回 Map 的元素个数
  2. set:增加一个新元素,返回当前 Map
  3. delete:删除元素,返回 boolean 值
  4. has:检测 Map 中是否包含某个元素,返回 boolean 值
  5. clear:清空集合,返回 undefined

2.声明 Map

let m = new Map()

class 类

ES6 提供了更接近传统语言的写法,引入了 class 这个概念,作为对象的模板。通过 class 关键字,可以定义类。ES6 的 class 可以看做只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰,更像面向对象编程的语法。

1.ES5 与 ES6 实例化对象

ES5 通过构造函数实例化对象

function Phone(brand, price) {
    this.brand = brand;
    this.price = price;
}
Phone.prototype.slogan = () => {
    console.log('永远相信美好的事情即将发生');
};
let xiaomi = new Phone('小米', 4999);
console.log(xiaomi);
xiaomi.slogan();

ES6 通过类实例化对象

class Phone{
    constructor(brand, price) {
        this.brand = brand;
        this.price = price;
    }
    slogan(){
        console.log('Never Settle');
    }
}
let yijia = new Phone('一加', 4999);
console.log(yijia);
yijia.slogan();

2.class 静态成员

ES5 通过构造函数实例化对象时,构造函数也是一个对象

function Phone() {}
Phone.name = '小米';
Phone.slogan = () => {
    console.log('永远相信美好的事情即将发生');
};
Phone.prototype.brand = 'XIAOMI';
let xiaomi = new Phone();
console.log(xiaomi.name);   // undefined
console.log(xiaomi.brand);  // XIAOMI
xiaomi.slogan();            // xiaomi.slogan is not a function

函数对象的属性和方法不属于实例化对象,但是函数对象的原型对象上的属性和方法属于实例化对象

ES6 通过class 实例化对象,添加静态成员

class Phone{
    static name = '一加';
    static slogan(){
        console.log('Never Settle');
    }
}
let yijia = new Phone();
console.log(yijia.name);   // undefined
yijia.slogan();            // yijia.slogan is not a function

static 标注的属性和方法,它们属于类而不属于实例化对象

3.对象继承

ES5 构造函数继承

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.say = () => {
    console.log('我可以说话');
};

function Student(name, age, id, classname) {
    Person.call(this, name, age);
    this.id = id;
    this.classname = classname;
}

// 设置子级构造函数的原型
Student.prototype =  new Person;

Student.prototype.study = () => {
    console.log('我会学习');
};

const xiaoming = new Student('小明', 10, '001', '1班');
console.log(xiaoming);    // {name: "小明", age: 10, id: "001", classname: "1班"}
xiaoming.study();		  // 我会学习	
xiaoming.say();           // 我会说话

ES6 类继承

class Person{
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    say(){
        console.log('我会说话');
    }
}

class Student extends Person{
    constructor(name, age, id, classname) {
        super(name, age);
        this.id = id;
        this.classname = classname;
    }

    study(){
        console.log('我会学习');
    }
}

const xiaoming = new Student('小明', 10, '001', '1班');
console.log(xiaoming);
xiaoming.study();
xiaoming.say();

ES6 语法更加简洁,更加符合传统语言面向对象编程的语法

4.子类对父类方法的重写

say(){
    console.log('我会说英语');
}
xiaoming.say();   //我会说英语

但是在子类重写的方法中不能直接调用对应父类的方法

5.class 中的 getter 和 setter

class Phone{
    get price(){
        console.log('读取价格');
        return '1999';
    }

    set price(newVal){
        console.log('修改价格'+newVal);
    }
}

const p = new Phone();
p.price;
p.price = '2999';

数值扩展

1.Number.EPSILON

JavaScript 表示的最小精度,如果两数之差小于这个数,就可以认为两数相等

function equal(a, b) {
    return Math.abs(a - b) < Number.EPSILON;
}

console.log(0.1+0.2 === 0.3);	// false
console.log(equal(0.1+0.2, 0.3));	// true

2.进制

二进制

console.log(0b1010);	// 10

八进制

console.log(0o777);		// 511

十六进制

console.log(0x123);		// 291		

十进制

console.log(100);		// 100

3.Number.isFinite

判断一个数是否有限

console.log(Number.isFinite(100));		// true
console.log(Number.isFinite(100/0));	//false	
console.log(Number.isFinite(Infinity)); //false

4.Number.isNaN

判断一个数是否为 NaN

console.log(Number.isNaN(100));		// false

5.Number.parseInt

字符串转整数

console.log(Number.parseInt('123456七八九'));		// 123456

6.Number.parseFloat

字符串转浮点数

console.log(Number.parseFloat('3.14156九'));		// 3.14156

7.Number,isInteger

判断一个数是否为整数

console.log(Number.isInteger(123));		// true
console.log(Number.isInteger('123'));	// false

8.Math.trunc

去除数字的小数部分

console.log(Math.trunc(1.23));		// 1

9.Math.sign

判断一个数是正数、负数还是0

console.log(Math.sign(100));	// 1
console.log(Math.sign(-100));	// -1	
console.log(Math.sign(0));		// 0

对象方法扩展

1.Object.is

判断两个对象是否相等

console.log(Object.is(100, 100));
console.log(Object.is(NaN, NaN));		//注意这种方法判断两个NaN是相等的

2.Object.assign

合并两个对象

const obj1 = {
    name: 'obj1',
    test1: 'test1'
};
const obj2 = {
    name: 'obj2',
    test2: 'test2'
};

const result = Object.assign(obj1, obj2);
console.log(result);

// {name: "obj2", test1: "test1", test2: "test2"}

3.Object.setPrototypeOf 和 Object.getPrototypeOf

设置原型对象和获取原型对象,不推荐使用

模块化

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件整合起来

1.模块化的好处

  1. 防止命名冲突
  2. 代码复用
  3. 高维护性

2.ES6 模块化语法

模块功能主要由两个命令构成:export 和 import

  • export 命令用于规定模块的对外接口
  • import 命令用于输入其他模块提供的功能

3.export 暴露语法

1.分别暴露

export const name = '小明';

export function showName() {
    console.log('My Name is '+name);
}

// 导入
<script type="module">
    import * as m1 from './m1.js'

    console.log(m1);
</script>

2.统一暴露

const name = '小明';

let showName = () => {
    console.log('My Name is '+name);
};

export {name, showName}

3.默认暴露

export default {
    name : '小明',
    showName : () => {
        console.log('My Name is '+name);
    }
}

// 导入
<script type="module">
    import * as m3 from './m3.js'

    console.log(m3);
    m3.default.showName();	// 使用方法需要加上default
</script>

4.import 导入语法

1.通用导入

import * as m1 from './m1.js'

2.解构赋值形式导入

import {name, showName} from './m1.js';

// 还可以设置别名
import {name as xiaoming, showName} from './m1.js';

import {default as m3} from './m3.js'

3.简便形式,只适用于默认暴露

import m3 from './m3.js'

5.ES6 模块化方式2

创建一个入口文件 app.js

import * as m1 from './m1.js'
import * as m2 from './m2.js'
import * as m3 from './m3.js'

在需要使用的页面引入 app.js

<script src="app.js" type="module"></script>
posted @ 2020-07-12 16:42  codeDD  阅读(310)  评论(0编辑  收藏  举报