ES6-ES11 学习笔记
概述
什么是 ECMAScript
ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制造商协会,这个组织的目标是评估、开发和认可电信和计算机标准。1994 年后该组织改名为 Ecma 国际;
ECMAScript 是由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言;
ES6新特性
1. let关键字
let 关键字用来声明变量
let a; // 单个声明
let b,c,d; // 批量声明
let e = 100; // 单个声明并赋值
let f = 521, g = 'abc', h = []; // 批量声明并赋值
使用 let 声明的变量有几个特点:
不允许重复声明
let a = 1;
let a = 2;
块儿级作用域(局部变量)
{
// 这里通过括号定义了局部变量,同样适用于if、else、for、while、函数等能创建局部变量的操作
let test = "123";
console.log(test);
}
// 在外部拿不到局部变量中定义的test
console.log(test)
不存在变量提升
变量提升:就是在变量创建之前使用(比如输出:输出的是默认值),let不存在,var存在;
// 使用var创建的变量,在程序开始运行时,先通过var p1;创建变量p1,所以能打印出来undefined (即变量提升)
// 使用let创建的变量不会存在变量提升问题
console.log(p1);
console.log(p2);
var p1 = 123;
let p2 = 123;
不影响作用域链
// 在父级作用域创建变量p
let p = 123;
function fn() {
// let p = 456; 如果在子级作用域重新定义了 变量p,不会影响父级作用域,并且子级作用域中引用的也是子级的变量p
// 子级作用域引用变量p,如果自己作用域找不到就会在父级作用域中找
console.log(p);
}
fn();
let应用
使用var创建变量
<div class="container">
<h2 class="page-header">let案例:点击div更改颜色</h2>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<script>
let items = document.getElementsByClassName('item');
// 使用var创建变量i
for (var i=0; i<items.length; i++) {
items[i].onclick = function () {
items[i].style.background = "pink";
}
}
</script>
使用var创建变量i,相当于全局变量,而onclick绑定的方法相当于异步事件。由于i是全局变量,等for循环结束时i=3。点击的时候items[3]是undefined,所以会报错。
使用let创建变量
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>let案例:点击div更改颜色</title>
<link crossorigin="anonymous" href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<style>
.item {
width: 100px;
height: 50px;
border: solid 1px rgb(42, 156, 156);
float: left;
margin-right: 10px;
}
</style>
</head>
<body>
<div class="container">
<h2 class="page-header">let案例:点击div更改颜色</h2>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<script>
let items = document.getElementsByClassName('item');
// 使用let创建局部变量,每个变量都有自己的作用域,不会互相影响
for (let i=0; i<items.length; i++) {
items[i].onclick = function () {
items[i].style.background = "pink";
}
}
</script>
</body>
</html>
2. const关键字
const 关键字用来声明常量。
// const声明常量
const DOG = "旺财";
console.log(DOG);
const 声明有以下特点:
声明必须赋初始值
不允许重复声明
值不允许修改
- const 关键字用于创建常量,而常量创建后就不允许修改;
- 数组或对象等添加/删除/修改元素可以正常执行,可以理解变量指向的地址没有变化,只是内容变化了。
块儿级作用域(局部变量)
{
const CAT = "喵喵";
console.log(CAT);
}
console.log(CAT);
3. 变量和对象的结构赋值
什么是解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值;
// 1、数组的解构赋值
let a = [1, 2, 3];
let [b, c, d] = a;
console.log(a)
console.log(b)
console.log(c)
console.log(d)
// 2、对象的解构赋值
const tony = {
name: "aaa",
age: 26,
test: function () {
console.log("LALALA")
}
}
let {name, age, test} = tony; // 注意解构对象这里用的是{}
console.log(name)
console.log(age)
test(); // 此方法可以正常调用
4. 模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:
字符串中可以出现换行符
以前的语法如果字符串中需要换行,只能通过 + 拼接,否则会报错。
ES6支持通过反引号来描述字符串:
可以使用 ${xxx} 形式引用变量
5. 简化对象和函数写法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁;
const a = "我是 a";
let b = function () {
console.log("lalala");
}
const c = {
// a: a, 完整写法
a,
// b: b 完整写法
b,
// 声明方法的简化
say() {
console.log("4545445")
}
}
6. 箭头函数
ES6允许使用箭头(=>)定义函数,箭头函数提供了一种更加简洁的函数书写方式,箭头函数多用于匿名函数的定义;
箭头函数定义
// ES5写法:
let a = function () {
console.log("hello a");
}
a();
// ES6+写法 箭头函数相当于用 => 代替了 function 关键字
let b = () => {
console.log("hello b");
}
b();
箭头函数的特点
- 如果形参只有一个,则小括号可以省略;
// 完整写法
let b = (c) => {
console.log("hello " + c);
}
b("haha");
// 如果形参只有一个则可以省略小括号
let c = d => {
console.log("hello " + d);
}
c("haha");
- 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果;
// 完整写法
let a = (n) => {
return n * n
}
console.log(a(9));
// 一个形参省略小括号写法
let b = n => {
return n * n
}
console.log(b(9));
// 函数只有一行语句时,省略花括号和return关键字写法
let c = n => n * n;
console.log(c(9));
// 函数只有一行语句时,省略花括号和return关键字写法;
// 多个形参不能省略小括号
let d = (n1, n2) => n1 * n2;
console.log(d(9, 10));
- 箭头函数的this是静态的,始终指向函数声明时所在作用域下的this的值;
// 普通函数
let a = function () {
console.log(this.name);
}
// 箭头函数
let b = () => {
console.log(this.name);
}
// 因为当前函数a和b都定义在全局变量中,this实际上指向的是window,所以这里给window添加name属性
window.name = "HAHAHA";
// 调用后都打印的"HAHAHA"
a();
b();
// 定义变量c
let c = {
name: "HEIHEIHEI"
}
// 通过call方法可以改变this指向,但是箭头函数this不会改变,只会指向创建时的作用域window
a.call(c);
b.call(c);
- 箭头函数不能作为构造函数实例化;
let Person = (name, age) => {
this.name = name;
this.age = age;
}
let me = new Person("aaa", 24);
5. 不能使用 arguments 变量:
// 普通函数可以使用arguments 变量,其中保存的是实参
let a = function (a1, a2) {
console.log(arguments);
}
a("a", "b");
// 箭头函数不能使用arguments变量
let b = (a1, a2) => {
console.log(arguments);
}
b("a", "b");
箭头函数的应用
箭头函数适合与this无关的回调,如:定时器。数组的回调方法;
箭头函数不适合与this有关的回调, 如:事件回调,对象的方法;
需求1
要求:点击div 2s后颜色变为粉色
版本1:
<body>
<div id="ad"></div>
<script>
// 需求: 点击div 2s后变为粉色
// 1. 根据id找到元素
let ad = document.getElementById("ad");
// 2. 绑定click事件
ad.addEventListener("click", function () {
setTimeout(function () {
// 这里会报错
this.style.background = "pink";
// 打印this,发现指向的是window,而window没有style属性,所以会报错
console.log(this);
}, 2000);
})
</script>
</body>
版本2:
// 1. 根据id找到元素
let ad = document.getElementById("ad");
// 2. 绑定click事件
ad.addEventListener("click", function () {
// 这里的this指向的是div元素本身,用_this保存指向this,即指向div。
// _this实际上是绑定到了window._this
console.log(this);
_this = this;
setTimeout(function () {
// 在setTimeout内部调用_this,会从window获取_this拿到div元素
_this.style.background = "pink";
console.log(this); //这里打印this是指向window的
console.log(_this); //这里打印_this是div,属于window._this
}, 2000);
})
版本3:箭头函数
// 1. 根据id找到元素
let ad = document.getElementById("ad");
// 2. 绑定click事件
ad.addEventListener("click", function () {
setTimeout(() => {
// 箭头函数this是指向创建函数时的作用域,即div元素本身
this.style.background = "pink";
console.log(this); //这里打印this是div元素
}, 2000);
})
需求2
要求:从数组中返回偶数的元素
// 普通函数
const result = arr.filter(function (item) {
return item % 2 === 0
})
console.log(result)
// 使用箭头函数
const result1 = arr.filter(item => item % 2 === 0)
console.log(result1)
7. ES6中函数参数的默认值
ES允许给函数的参数赋初始值;
函数设置默认值
function add(a, b, c) {
return a + b + c;
}
// 传入3个参数,结果为 6
let result = add(1, 2, 3);
console.log(result);
// 只传入2个参数,结果为NaN
let result1 = add(1, 2);
console.log(result1);
// 定义函数时,指定参数默认值
function add1(a, b, c=10) {
return a + b + c;
}
// 传入2个参数,第3个参数会使用默认值,结果为13
let result2 = add1(1, 2);
console.log(result2);
与解构赋值结合使用
function connect({host, username, password="123456"}) {
console.log(host);
console.log(username);
console.log(password);
}
connect({
host: "localhost",
username: "root",
password: "root"
})
connect({
host: "localhost",
username: "root"
})
8. rest参数
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments;
参考文章: https://www.jianshu.com/p/50bcb376a419
// 普通函数通过arguments能获取所有的实参,arguments是一个对象
function date(a, b) {
console.log(arguments);
}
date("hahaha", "heiheihei");
// rest 参数, ES6中通过...args获取实参,并且...args必须放最后
function date1(a, ...args) {
console.log(a);
console.log(args);
}
// 注意:这里传入两个参数,"haha"会赋值给a实参; "heihei"会赋值给args,
// 也就是说剩余的参数都会付给args,args是一个数组
date1("haha", "heihei");
9. 扩展运算符
… 扩展运算符能将数组转换为逗号分隔的参数序列;
扩展运算符(spread)也是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包;
// ... 扩展运算符能将数组转换为逗号分隔的参数序列
// 声明一个数组 ...
const tfboys = ['易烊千玺', '王源', '王俊凯'];
// => '易烊千玺','王源','王俊凯'
// 声明一个函数
function chunwan() {
console.log(arguments);
}
chunwan(tfboys);
chunwan(...tfboys); // chunwan('易烊千玺','王源','王俊凯')
扩展运算符应用
//1. 数组的合并 情圣 误杀 唐探
const kuaizi = ['王太利', '肖央'];
const fenghuang = ['曾毅', '玲花'];
// 传统的合并方式
const zuixuanxiaopingguo = kuaizi.concat(fenghuang);
const zuixuanxiaopingguo1 = [...kuaizi, ...fenghuang];
console.log(zuixuanxiaopingguo);
console.log(zuixuanxiaopingguo1);
//2. 数组的克隆
const sanzhihua = ['E', 'G', 'M'];
const sanyecao = [...sanzhihua];// ['E','G','M']
console.log(sanyecao);
//3. 将伪数组转为真正的数组
const divs = document.querySelectorAll('div');
const divArr = [...divs];
console.log(divArr);
10. Symbol
Symbol 概述
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型;
Symbol 的特点:
- Symbol 的值是唯一的,用来解决命名冲突的问题;
- Symbol 值不能与其他数据进行运算;
- Symbol 定义的对象属性不能使用for…in循环遍历 ,但是可以使用Reflect.ownKeys 来获取对象的所有键名;
参考文章:https://blog.csdn.net/fesfsefgs/article/details/108354248
//创建Symbol
let s = Symbol();
console.log(s, typeof s);
let s2 = Symbol('尚硅谷');
let s3 = Symbol('尚硅谷');
console.log(s2 === s3); // false
// Symbol.for 创建的symbol,如果描述符一样,那么得到的对象也一样
let s4 = Symbol.for('尚硅谷');
let s5 = Symbol.for('尚硅谷');
console.log(s4 === s5); // true
// 不能与其他数据进行运算--会报错
let result = s + 100;
let result1 = s > 100;
let result2 = s + s;
// JS 7种数据类型 USONB you are so niubility
// u undefined
// s string symbol
// o object
// n null number
// b boolean
Symbol创建对象属性
// 假如有一个很复杂的对象,需要向对象中添加方法 up down 方法,不确定是不是原来已经右up和down方法了
let game = {
name: '俄罗斯方块',
up: function () {
},
down: function () {
}
};
// 我们要往game对象里面添加方法,但是怕game对象已经存在同名方法,所以可以使用Symbol
// 方法一:
let methods = {
up: Symbol(),
down: Symbol()
};
game[methods.up] = function () {
console.log("up")
}
game[methods.down] = function () {
console.log("down")
}
console.log(game);
// 调用
game[methods.up]();
game[methods.down]();
// 方法二:
let say = Symbol('say');
let youxi1 = {
name: "狼人杀",
[say]: function () {
console.log("我可以发言")
}
}
// 调用
youxi1[say]();
Symbol内置值
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行;
class Person {
// 当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用Symbol.hasInstance方法
static [Symbol.hasInstance](param) {
console.log(param);
console.log("我被用来检测类型了");
return false;
}
}
let o = {};
console.log(o instanceof Person);
const arr = [1,2,3];
const arr2 = [4,5,6];
console.log(arr.concat(arr2));
// 合并数组:false数组不可展开,true可展开
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr.concat(arr2));
11. 迭代器
迭代器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作;
ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费;
原生具备 iterator 接口的数据(可用 for of 遍历),例如:Array、Arguments、Set、Map、String、TypedArray、NodeList;
工作原理
- 创建一个指针对象,指向当前数据结构的起始位置;
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员;
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员;
- 每调用 next 方法返回一个包含 value 和 done 属性的对象;
注意:需要自定义遍历数据的时候,要想到迭代器;
// 声明一个数组
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧'];
// 使用 for...of 遍历数组, 得到的是数组中的元素本身
for (let v of xiyou) {
console.log(v);
}
// 使用 for...in 遍历数组, 得到的是数组中元素的下标
for (let i in xiyou) {
console.log(i);
}
// 初始化迭代器对象
let iterator = xiyou[Symbol.iterator]();
console.log(iterator); // Array Iterator 对象,迭代器的next方法指向下一个元素,类似于链表解构。
// 通过调用迭代器的next方法获取下一个元素。 返回对象中value字段代表值,done代表是否还有元素没有遍历。
console.log(iterator.next()); // {value: "唐僧", done: false}
console.log(iterator.next()); // {value: "孙悟空", done: false}
console.log(iterator.next()); // {value: "猪八戒", done: false}
console.log(iterator.next()); // {value: "沙僧", done: false}
// 遍历完成后,再调用next方法,done字段为true,代表已经遍历完成
console.log(iterator.next()); // {value: undefined, done: true}
自定义迭代器遍历对象
// 要求遍历banji对象stus数组
const banji = {
name: "终极一班",
stus: ['xiaoming', 'xiaoning', 'xiaotian', 'knight'],
// 自定义迭代器
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
// console.log(this);
if (index < this.stus.length) {
const result = {
value: this.stus[index],
done: false
};
index++;
return result
} else {
return {
value: undefined,
done: true
}
}
}
}
}
}
// 注意:for..of 遍历出来的是value值,而不是{value: xxx, done: ture/false}
for (let v of banji) {
console.log(v)
}
12. 生成器
生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同;
// 生成器其实就是一个特殊的函数
// 异步编程 纯回调函数 node、fs、ajax等
// 1. 定义生成器函数
function * gen () {
console.log(111);
// 函数会执行到yield时停下来不再向下执行,直到下一次再调用next方法,从下一行开始继续执行,直到遇到下一个yield
// yield后面的值作为返回值
yield '一只没有耳朵';
console.log(222);
yield '一只没有尾部';
console.log(333);
yield '真奇怪';
console.log(444);
}
// 2. 生成器执行
// 2.1 生成器函数返回的是迭代器对象,因此可以通过next方法遍历
let g = gen();
console.log(g);
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
// 2.2 用for..of 也可以执行生成器对象
for (let v of gen()) {
console.log(v);
}
生成器函数的参数传递
function * gen (arg) {
console.log(arg);
let one = yield '一只没有耳朵';
console.log(one);
let two = yield '一只没有尾部';
console.log(two);
let three = yield '真奇怪';
console.log(three);
}
let g = gen("A");
console.log(g);
console.log(g.next()); // 打印 A, {value: '一只没有耳朵', done: false}
// 传入的参数"B",传给了 one
// 上一次调用next,执行到了 let one = yield '一只没有耳朵'; 等号右侧结束,
// 这一次再调用next("B"),即 let one = "B";
console.log(g.next("B")); // 打印 B, {value: '一只没有尾部', done: false}
console.log(g.next("C")); // 打印 C, {value: '真奇怪', done: false}
console.log(g.next("D")); // 打印 D, {value: undefined, done: true}
生成器应用
// 异步编程 文件操作 网络操作(ajax,request) 数据库操作
// 需求: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();
}, 1000)
}
function three() {
setTimeout(() => {
console.log(333);
iterator.next();
}, 1000)
}
function * gen() {
yield one();
yield two();
yield three();
}
// 调用生成器函数
let iterator = gen();
iterator.next();
// 模拟获取: 用户数据 订单数据 商品数据
function getUsers() {
setTimeout(() => {
let data = "用户数据"; // 第二次调用next,传入参数,作为第一个的返回值
iterator.next(data); // 这里将data传入赋值给gen中的users
}, 1000);
}
function getOrders() {
setTimeout(() => {
let data = "订单数据";
iterator.next(data); // 这里将data传入赋值给gen中的orders
}, 1000);
}
function getGoods() {
setTimeout(() => {
let data = "商品数据";
iterator.next(data); // 这里将data传入赋值给gen中的goods
}, 1000);
}
function * gen() {
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods); // 这种操作有点秀啊!
}
let iterator = gen();
iterator.next();
13.Promise
Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果;
- Promise 构造函数: Promise (excutor) {};
- Promise.prototype.then 方法;
- Promise.prototype.catch 方法;
基本用法
let p = new Promise(function (resolve, reject) {
setTimeout(function () {
// 获取成功--调用resolve并传入获取的数据--> p.then的第一个参数进行处理
let data = "success";
resolve(data);
// 获取失败--调用reject并传入错误信息--> p.then的第二个参数进行处理
// let err = "failed";
// reject(err);
}, 2000)
});
// 通过p.then 处理成功和失败的逻辑
// then方法的两个参数都是函数,分别代表获取成功和获取失败的逻辑
p.then(function (data) {
console.log("获取成功" + data);
}, function (err) {
console.log("获取失败" + err);
})
Promise封装Ajax请求
// 请求地址:https://api.apiopen.top/getJoke
let p = new Promise(function (resolve, reject) {
// 原生请求
// 1. 创建对象
const xhr = new XMLHttpRequest();
// 2、初始化
xhr.open("GET","https://api.apiopen.top/getJoke");
// 3、发送
xhr.send();
// 4、绑定事件,处理响应结果
xhr.onreadystatechange = function () {
// 判断状态
if (xhr.readyState === 4) { // 判断响应状态码 200-299
if (xhr.status >= 200 && xhr.status <= 299) {
// 成功
resolve(xhr.response);
} else {
// 失败
reject(xhr.status);
}
}
}
});
p.then(function (data) {
console.log("获取成功" + data.toString());
}, function (err) {
console.log("获取失败" + err);
})
promise链式调用
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("用户数据");
}, 1000);
})
const result = p.then(data => {
// console.log(data);
}, err => {
console.error(err);
});
// 打印result可以到到result也是promise类型,因此then方法可以链式调用
console.log(result);
p.then(data => {
console.log(data)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("订单数据");
}, 2000);
})
}).then(data => {
console.log(data)
})
Promise对象catch方法
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("用户数据");
}, 1000);
})
// then方法的两个参数也可以只写一个,例如:只处理成功的情况
p.then(data => {
console.log(data)
return new Promise((resolve, reject) => {
setTimeout(() => {
reject("获取订单失败");
}, 2000);
})
}).then(data => {
console.log(data)
}).catch(err => {
// catch方法用于捕获reject时的错误,特别是多个then链式调用时,
// then只处理resolve的情况,再catch中处理reject的情况
console.log(err)
})
14. Set集合
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
集合的属性和方法:
- size 返回集合的元素个数;
- add 增加一个新元素,返回当前集合;
- delete 删除元素,返回 boolean 值;
- has 检测集合中是否包含某个元素,返回 boolean 值;
- clear 清空集合,返回 undefined;
// Set集合
let s = new Set();
console.log(s, typeof s);
let s1 = new Set(["大哥", "二哥", "三哥", "四哥", "三哥"]);
console.log(s1); // 自动去重
// 1. size 返回集合的元素个数;
console.log(s1.size);
// 2. add 增加一个新元素,返回当前集合;
s1.add("大姐");
console.log(s1);
// 3. delete 删除元素,返回 boolean 值;
let result = s1.delete("三哥");
console.log(result);
console.log(s1);
// 4. has 检测集合中是否包含某个元素,返回 boolean 值;
let r1 = s1.has("二姐");
console.log(r1);
// 5. clear 清空集合,返回 undefined;
s1.clear();
console.log(s1);
集合的应用
// Set集合实践
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1];
// 数组去重
let s1 = new Set(arr);
console.log(s1);
// 去重后求交集
let arr2 = [3, 4, 5, 6, 5, 4, 3];
let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
console.log(result);
// 去重后求并集
// ... 为扩展运算符,将数组转化为逗号分隔的序列
let union = [...new Set([...arr, ...arr2])];
console.log(union);
// 去重后差集:比如集合1和集合2求差集,就是1里面有的,2里面没的
let result1 = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
console.log(result1);
15. Map集合
ES6 提供了 Map 数据结构。
它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历;
Map 的属性和方法:
- size 返回 Map 的元素个数;
- set 增加一个新元素,返回当前 Map;
- get 返回键名对象的键值;
- has 检测 Map 中是否包含某个元素,返回 boolean 值;
- clear 清空集合,返回 undefined;
// 创建一个空 map
let m = new Map();
// 创建一个非空 map
let m2 = new Map([['name', '尚硅谷'], ['slogon', '不断提高行业标准']]);
// 1. size 返回 Map 的元素个数;
console.log(m2.size);
// 2. set 增加一个新元素,返回当前 Map;
m.set("皇帝", "大哥");
m.set("丞相", "二哥");
console.log(m);
// 3. get 返回键名对象的键值;
console.log(m.get("皇帝"));
// 4. has 检测 Map 中是否包含某个元素,返回 boolean 值;
console.log(m.has("皇帝"));
// 5. clear 清空集合,返回 undefined;
m.clear();
console.log(m);
16. class类
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已;
class初体验
// 1. ES5写法 实现类
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
// 添加方法
Phone.prototype.call = function () {
console.log("Phone 我可以打电话!");
}
// 实例化对象
let HuaWei = new Phone("华为", 5999);
HuaWei.call();
console.log(HuaWei);
// 2. ES6语法实现类
class Phone1{
// 构造方法
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
call() {
console.log("Phone1 我可以打电话!");
}
}
// 实例化对象
let xiaomi = new Phone1("小米", 3999);
xiaomi.call();
console.log(xiaomi);
class静态成员
// class静态成员
// 1. ES5写法
function Phone() {
}
// 这里是构造函数对象添加属性和方法,是静态属性/方法,只能通过“ 类名.属性/方法 ”调用,不能通过实例化的对象调用
Phone.name = "手机";
Phone.change = function () {
console.log("我可以改变世界!");
}
// 实例化对象
let nokia = new Phone();
console.log(nokia.name); // 报:undefined,name属性是静态属于,是属于类对象本身的,不属于实例化对象的
nokia.change(); // 报错:Uncaught TypeError: nokia.change is not a function
// 在构造函数原型对象上添加color属性,实例化后的对象可以拿到color属性
Phone.prototype.color = "黑色";
console.log(nokia.color); // 黑色
console.log(Phone.name);
// 静态属性和方法要通过 “ 类名.属性/方法 ” 调用
Phone.change();
// 2. ES6 写法
class Phone1 {
// 通过static关键字定义静态属性
static name = "手机";
static change() {
console.log("我可以改变世界!");
}
}
let xiaomi = new Phone1();
console.log(xiaomi.name); // undefined
console.log(Phone1.name); // 手机
Phone1.change();
继承和多态
// 1. ES5构造函数继承
// 手机
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
Phone.prototype.call = function () {
console.log("我可以打电话!");
}
// 智能手机
function SmartPhone(brand, price, color, size) {
Phone.call(this, brand, price);
this.color = color;
this.size = size;
}
// 设置子级构造函数的原型
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = SmartPhone; //这行不是必须的
// 声明子类的方法
SmartPhone.prototype.photo = function () {
console.log("我可以拍照!");
}
SmartPhone.prototype.game = function () {
console.log("我可以玩游戏!");
}
// 实例化子类
const chuizi = new SmartPhone("锤子", 2499, "黑色", "5.5inch");
console.log(chuizi);
chuizi.call();
chuizi.photo();
chuizi.game();
// 2. ES6 写法
class Phone1 {
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
call() {
console.log("我可以打电话!");
}
}
class SmartPhone1 extends Phone1 {
// 构造函数
constructor(brand, price, color, size) {
// 调用父类构造函数
super(brand, price);
this.color = color;
this.size = size;
}
photo() {
console.log("我可以拍照!");
}
game() {
console.log("我可以玩游戏!");
}
// 重写父类的call方法
call() {
super.call();
console.log("我是子类的call方法")
}
}
const chuizi1 = new SmartPhone1("小米", 1999, "黑色", "5.15inch");
console.log(chuizi1);
chuizi1.call();
chuizi1.photo();
chuizi1.game();
class中的getter和setter设置
class Phone {
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
get getPrice() {
return this.price
}
set setPrice(price) {
// 这里可以加一些对price的校验
this.price = price
}
}
let p = new Phone("xiaomi", 4444);
console.log(p.price);
p.price = 5555;
console.log(p.price);
17. 数值扩展
-
Number.EPSILON:Number.EPSILON 是 JavaScript 表示的最小精度;EPSILON 属性的值接近于2.2204460492503130808472633361816E-16;
-
二进制和八进制:ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示;
-
Number.isFinite() 与 Number.isNaN() :
-
Number.isFinite() 用来检查一个数值是否为有限的;
-
Number.isNaN() 用来检查一个值是否为 NaN;
-
Number.parseInt() 与 Number.parseFloat():ES6 将全局方法 parseInt 和 parseFloat,移植到 Number 对象上面,使用不变;
-
Math.trunc:用于去除一个数的小数部分,返回整数部分;
-
Number.isInteger:Number.isInteger() 用来判断一个数值是否为整数;
// 数值扩展
// 0. Number.EPSILON 是 JavaScript 表示的最小精度 // EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16
function equal(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log("0、Number.EPSILON 是 JavaScript 表示的最小精度");
// 箭头函数简化写法
equal = (a, b) => Math.abs(a - b) < Number.EPSILON;
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3); // false
console.log(equal(0.1 + 0.2, 0.3)); // true
// 1. 二进制和八进制
console.log("1、二进制和八进制");
let b = 0b1010;
let o = 0o777;
let d = 100;
let x = 0xff;
console.log(x);
// 2. Number.isFinite 检测一个数值是否为有限数
console.log("2、Number.isFinite 检测一个数值是否为有限数");
console.log(Number.isFinite(100));
console.log(Number.isFinite(100 / 0));
console.log(Number.isFinite(Infinity));
// 3. Number.isNaN 检测一个数值是否为 NaN
console.log("3. Number.isNaN 检测一个数值是否为 NaN");
console.log(Number.isNaN(123));
// 4. Number.parseInt Number.parseFloat字符串转整数
console.log("4. Number.parseInt Number.parseFloat字符串转整数");
console.log(Number.parseInt('5211314love'));
console.log(Number.parseFloat('3.1415926神奇'));
// 5. Number.isInteger 判断一个数是否为整数
console.log("5. Number.isInteger 判断一个数是否为整数");
console.log(Number.isInteger(5));
console.log(Number.isInteger(2.5));
// 6. Math.trunc 将数字的小数部分抹掉
console.log("6. Math.trunc 将数字的小数部分抹掉 ");
console.log(Math.trunc(3.5));
18.对象扩展
ES6 新增了一些 Object 对象的方法:
- Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN);
- Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象;
- proto、setPrototypeOf、 setPrototypeOf 可以直接设置对象的原型;
// 对象扩展
// 1. Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN);
console.log(Object.is(120,120)); // ===
// 注意下面的区别
console.log(Object.is(NaN,NaN));
console.log(NaN === NaN); // NaN与任何数值做===比较都是false,跟他自己也如此!
// 2. Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象;
const config1 = {
host: "localhost",
port: 3306,
name: "root",
pass: "root",
test: "test" // 唯一存在
}
const config2 = {
host: "http://zibo.com",
port: 300300600,
name: "root4444",
pass: "root4444",
test2: "test2"
}
// 如果前边有后边没有会添加,如果前后都有,后面的会覆盖前面的
console.log(Object.assign(config1,config2));
// 3. __proto__、setPrototypeOf、 getPrototypeOf 可以直接设置对象的原型;
const school = {name: "尚硅谷"}
const cities = {xiaoqu: ['北京', '上海', '深圳']}
// 并不建议这么做
Object.setPrototypeOf(school, cities);
console.log(Object.getPrototypeOf(school));
console.log(school);
19.模块化
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来;
模块化的好处
模块化的优势有以下几点:
- 防止命名冲突;
- 代码复用;
- 高维护性;
模块化规范产品
ES6 之前的模块化规范有:
- CommonJS => NodeJS、Browserify;
- AMD => requireJS;
- CMD => seaJS;
ES6 模块化语法
模块功能主要由两个命令构成:export 和 import;
- export 命令用于规定模块的对外接口(导出模块);
- import 命令用于输入其他模块提供的功能(导入模块);