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 遍历)
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
3.工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
- 每调用 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 对象的特点
- 对象的状态不受外界影响,Promise 对象代表一个异步操作,有三种状态:pending (进行中)、fulfilled (已成功)和 rejected (已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。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 的属性和方法
- size:返回 Set 的元素个数
- add:增加一个新元素,返回当前集合
- delete:删除元素,返回 boolean 值
- has:检测集合中是否包含某个元素,返回 boolean 值
- 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 的属性和方法
- size:返回 Map 的元素个数
- set:增加一个新元素,返回当前 Map
- delete:删除元素,返回 boolean 值
- has:检测 Map 中是否包含某个元素,返回 boolean 值
- 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.模块化的好处
- 防止命名冲突
- 代码复用
- 高维护性
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>