浅谈ES6-11
本文主要用于记录es6-11的学习过程,参考视频【尚硅谷Web前端ES6教程,涵盖ES6-ES11】
- ECMA:
ECMA 欧洲计算机制造商协会 - ECMAScript:
是ECMA国际通过ECMA-262标准化的脚本程序设计语言 - ECMA-262:
它定制的一种标准 - 历史:
每年都会更新一个版本 截至写稿已经是第十二版 即es12
let
和var类似 又有点不同
声明变量
1
2
3let a;
let b = 521,g = 'dasda',h = [];
//可以一次声明多个变量不能重复声明,但var可以。作用 防止变量被污染
块级作用域 es5 全局 函数 eval
1
2
3
4
5{
let a = 'casc';
}
console.log(a);
//这里会出错,但var可以,因为let只能在这个块级作用域里面生效不存在变量提升
1
2
3
4console.log(song);
var song = 'aaa';
//此时输出undefined 因为var声明的变量会提前赋一个undefined
//但let不行不影响作用域链
遍历绑定事件 假设数组长度为3
1
2
3for(let i=0;i<items.length;i++){
items[i].style.background = 'pink';
}如果使用var修改 因为var没有块级作用域 它会一直执行下去,轮到里面的时候,i已经变成了3 而数组没有三的内容。
而let由于有块级作用域,里面的内容独立存在,所以不会有全局影响
const
用于定义常量
一定要赋初始值
常量的值不能被修改
块级作用域
对于数组和元素的修改不算作对常量的修改 不会报错
1
2const team = ['scasc','casca'];
team.push('ascasc');
解构赋值
es6允许用户按照一定模式从数组和对象中提取值
两种模式
数组的解构
1
2const shuzu = ['aaa','cssca','casc'];
let [a,b,c] = shuzu;对象的解构
1
2
3
4
5
6
7
8const obj = {
name:'zzl',
age:'20',
function(){
console.log("scasca");
}
}
let {name,age,f} = obj;
模板字符串
引入新的声明字符方式 反引号`
一
1
let str = `dadadada`;
内容中可以直接出现换行符
1
2
3let str = `<ul>
<li>aaaa</li>
</ul>`变量拼接 ${}
1
2let a = '123';
let b = `${a}被引用了`
简化对象
es6允许在大括号里面直接写入变量和函数作为对象的属性和方法
es6允许在大括号里面直接写入变量和函数作为对象的属性和方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20let name = 'zzl';
let change = function(){
console.log('asca');
}
const school = {
//以前写法
//name:name;
//change:change;
//improve:function(){
//do sth.
//}
//现在写法
name,
change,
improve(){
cosole.log('new method');
}
}
箭头函数
允许使用箭头来定义函数=>
允许使用箭头来定义函数
1
2
3
4
5
6
7
8//before
let fn = function(){
//do sth.
}
//now
let fn ()=>{
//do sth.
}
区别
this指向的是静态资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21let getName1 = function(){
console.log(this.name);
}
let getName2 = ()=>{
console.log(this.name);
}
//设置window对象的name属性
window.name = 'lalalla';
const schoolName = {
name: "babababab";
}
//直接调用 此时输出一致
getName1();
getName2();
//call方法调用 此时 一输出babababab 二还是lalalal
getName1.call(schoolName);
getName2.call(schoolName);不能作为构造实例化对象
1
2
3
4let Person = (name,age) =>{
this.name = name;
this.age = age;
}不能使用arguments变量
1
2
3
4let fn = () =>{
console.log(arguments);
}
fn(1,2,3);箭头函数的简写
1
2
3
4
5
6
7
8
9
10
11//当形参只有一个的时候可以省略小括号
let add = n =>{
return n + n;
}
console.log(add(9));
//省略花括号 当代码体只有一条语句的时候 此时return必须省略
//而且语句的执行结果就是函数的返回值
let pow = n => n * n;
console.log(pow(2));
需求1 点击div 2s之后变成粉色
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
28case1 wrong
<div id = "ad"></div>
let ad = doucument.getElementById('ad');
ad.addEventListener('click',function(){
setTimeout(function(){
this.style.background = 'pink';//此时由于定时器的this指向window 所以不会变色
},2000);
})
case2 success
<div id = "ad"></div>
let ad = doucument.getElementById('ad');
ad.addEventListener('click',function(){
let that = this;//提前存储this
setTimeout(function(){
this.style.background = 'pink';
},2000);
})
case3 success
<div id = "ad"></div>
let ad = doucument.getElementById('ad');
ad.addEventListener('click',function(){
setTimeout(()=>{
this.style.background = 'pink';
//使用箭头函数 会找到外面一级的对象
},2000);
})需求二 从数组中返回偶数元素
1
2const arr = [1,2,33,4342,4];
const result = arr.filter(item => item%2 ===0 );
总结
箭头函数适合与this无关的回调,定时器,数组的方式回调
不适合与this有关的回调,比如事件回调,对象的方法
1
2
3
4
5
6
7
8
9
10
11
12{
name:'abc';
getName:function(){
console.log(this.name);//可以返回abc
}
}
{
name:'abc';
getName:()=>{
console.log(this.name);//不可以返回abc,因为此时this指向了window
}
}
参数默认值
ES6允许给函数形参赋初始值
形参初始值具有默认值的参数 一般位置要靠后 (潜规则)
1
2
3
4
5
6function add(a,b,c=10){
return a+b+c;
}
console.log(add(1,2));//此时如果不给c传值 那么c默认是10
结果:13与解构赋值结合
1
2
3
4
5
6
7
8connect({
host:'localhost',
port:3000
})
function connect({host='127.0.0.1",port}){
console.log(host);
console.log(port);
}
rest参数
ES6引入rest参数,用于获取函数的实参,用来代替arguments
获取实参的方式
arguments 此时获取的是一个对象
1
2
3
4function getShican(){
console.log(arguments);
}
getShican('leb','zzl');rest 参数 三个点加args 此时获取的args是数组 意味着可以用数组的方法
1
2
3
4function getS(...args){
console.log(args);
}
getS('leb','zzl');rest参数必须放到最后
1
2
3
4
5
6
7function getS(a,b,...args){
console.log(args);
}
getS('leb','zzl',1,2,3,4);
a=leb
b=zzl
args=[1,2,3,4]
spread拓展运算符
… 拓展运算符可以将数组转换为逗号分隔的参数序列
例子
声明一个函数
1
2
3
4
5
6
7
8const jojo = ['jotailang','jolunuo','jolin'];
function getS(){
console.log(arguments);
}
gets(...jojo);
此时arguments里面有三个参数:'jotailang','jolunuo','jolin'
否则就是一个参数
和rest区别
- rest的声明放在函数声明的形参中,这个是放在函数调用的形参中
拓展运算符的运用
数组的合并
1
2
3
4
5
6const jojo = ['jolin','jostar'];
const kingdomrank = ['boji','kake'];
//const theworld = jojo.contat(kingdomrank);
//or
//注意使用逗号分割
const theworld = [...jojo,...kingdomrank];数组的克隆
1
2const jojo = ['saigouda'];
const clonejojo = [...jojo];将伪数组转化为真正的数组
1
2
3
4
5
6<div></div>
<div></div>
<div></div>
const divs = document.querySelectorAll('div');
const divarr = [...divs];
Symbol基本使用
ES6引入一种新的原始数据类型Symbol,表示独一无二的值,它是js语言中的第七种数据类型,是一种类似字符串的数据类型。
特点
- 它的值是唯一的,用来解决命名冲突的问题
- 不能和其他数据进行运算
- 它定义的对象属性不能使用for…in进行遍历,但是可以使用Reflect.ownkeys来获取对象的所有键名
创建
创建Symbol
1
2
3
4
5
6
7
8
9
10
11
12let s = Symbol();
let s2 = Symbol('jojo');
let s3 = Symbol('jojo');
console.log(s2 === s3);
//false
//another way
let s4 = Symbol.for('jojo');
let s5 = Symbol.for('jojo');
console.log(s4===s5);
//true不能与其他数据进行运算
向对象中添加方法up down
1
2
3
4
5
6
7
8
9
10
11
12
13let game = {...}
let methods = {
up:Symbol(),
down:Symbol()
};
game[methods.up] = function(){
console.log('i can up');
}
game[methods.down] = function(){
console.log('i can down');
}当想往对象中添加方法的时候,一般要了解对象中的参数,而且存在风险,用这种方式声明的方法,没有风险,可以不用看对象的内容就可以添加
方法2
1
2
3
4
5
6
7let youxi = {
name:'狼人杀',
[Symbol('say')]: function(){
console.log('i can speak');
}
}
console.log(youxi)Symbol内置值
JS的七种数据类型
USONB u are so niubility
- u undefined
- s string symbol
- o object
- n null number
- b boolean
迭代器
迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作。
es6创造了一种新的遍历命令for…of循环,Iterator接口主要供其消费
原生具备iterator接口的数据(可以用for..of遍历)
1
2
3
4
5
6
7a Array
b Arguments
c Set
d Map
e String
f TypedArray
g NodeList
工作原理
首先创建一个指针对象,指向当前数据结构的起始位置
第一次调用对象的next方法,指针自动指向数据结构的第一个成员
接着不断调用next方法,指针一直往后移动,直到指到最后一个成员
每调用next方法返回一个包含==value和done==属性的对象
1
2value:当前成员的值
done:布尔值 表示遍历是否结束 若未结束 返回false
注意
- 需要自定义遍历数据的时候,要想到迭代器
生成器
- 生成器是一个特殊的函数
- 生成器目的是解决异步编程中的回调问题
- 回调函数 node fs ajax mongodb
写法上特殊
1
2
3
4function * gen(){
console.log('hello generator');
}输出上特殊
1
2let iterator = gen();
iterator.next();yield分割符
1
2
3
4
5
6
7
8
9
10
11
12function * gen(){
console.log('111');
yield '分割1';
console.log('222');
yield '分割2';
console.log('333');
yield '分割3';
}
let iterator = gen();
iterator.next();
iterator.next();
iterator.next();输出yield的值 遍历
1
2
3for(let v of gen()){
console.log(v);
}迭代器方法
1
2
3
4let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
生成器函数传参
整体传参
1
2
3
4
5
6
7
8
9
10function * gen(args){
console.log(args);
yield 111;
}
let iterator = gen('aaa');
console.log(iterator.next());
logout:
aaa
value:111 done:false方法传参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function * gen(args){
console.log(args);
let one = yield 111;
console.log(one);
yield 222;
}
let iterator = gen('aaa');
console.log(iterator.next());
console.log(iterator.next('bbb'));
logout:
aaa (args)
value 111 done false
bbb (one)
value 222 done false
注意 此时的bbb是第一个yield的整体结果
对于异步编程的作用案例
假如需要1s 输出aaa 2s输出bbb 3s输出ccc
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常规操作 setTimeout嵌套
现在利用生成器
function one(){
setTimeout(()=>{
console.log('aaa');
iterator.next();
},1000)
}
function two(){
setTimeout(()=>{
console.log('bbb');
iterator.next();
},2000)
}
function three(){
setTimeout(()=>{
console.log('ccc');
iterator.next();
},3000)
}
function * gen(){
yield one();
yield two();
yield three();
}
let iterator = gen();
iterator.next();
Promise
Promise是es6引入的异步编程的新解决方法,语法上他是一个构造函数,主要用来封装异步操作获取他失败或者成功的结果。(为了解决回调地狱的问题)
实例化Promise对象
1 | const p = new Promise(function(resolve,reject){ |
- promise对象有三种状态
- pending 进行中
- fulfilled 已成功
- rejected 已失败
- then有两个形参,成功一般叫value 失败叫reason
只要执行了resolve 就会调用then函数里面的value方法,反之调用reason方法
调用then方法,then方法返回的结果是一个promise对象,对象有对象状态和对象值这两个参数,对象的状态由方法的的执行结果而定
如果回调函数中返回的结果是一个非promise类型的属性 则状态为成功,返回改属性值
1
2
3
4
5
6
7const result = p.then(function(value){
console.log(value);
return 'jojo';
})
logout:
promise.status:'resolve';
promise.value:'jojo';
- 此外如果不写return 那么返回的是undefined 也是非promise类型的属性 所以也会成功 并且返回 undefind
如果回调函数中返回的结果是一个promise类型的属性,则该属性的状态决定then方法的状态
1
2
3
4
5
6const result = p.then(function(value){
console.log(value);
return new Promise(function(resolve,reject){
resolve('jojo');
})
})抛出错误 那么失败值就是抛出的值
1
2
3
4const result = p.then(function(value){
console.log(value);
throw new Error('出错了');
})
链式调用
1
2
3
4
5p.then(value=>{
}).then(value=>{
});promise-catch方法 作用原理和不写第一个value的then方法相同
1
2
3
4//用于写错误情况
p.catch((reason)=>{
console.warn(reason);
})
promise读取文件 注意err和data不要写反
1
2
3
4
5
6
7
8
9
10
11
12
13const fs = require('fs');
const p = new Promise(function(resolve,reject){
fs.readFile('./lalala.md',(err,data)=>{
if(err) reject(err);
resolve(data);
})
})
p.then(function(value){
console.log(value.toString());
},function(reason){
console.error(reason);
})promise封装ajax 在promise之前,所有成功或者失败的操作都要在事件里面操作,不方便阅读,现在使用promise封装,函数成功和失败的操作都在p.then()里面完成
promise读取多个文件 下面以三个文件为例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23const fs = require('fs');
const p = new Promise((resolve, reject)=> {
fs.readFile('./a.md', (err, data) => {
resolve(data);
})
})
p.then((value) => {
return new Promise((resolve, reject) => {
fs.readFile('./b.md', (err, data) => {
resolve([value,data]);//将结果转化为数组的形式发送到下一个,因为这个数组的结果就是当前then方法的值
})
})
}).then((value) => {
return new Promise((resolve, reject) => {
fs.readFile('./c.md', (err, data) => {
value.push(data);//因为上一个传入的是数组 所以现在要读取这个数据需要将其压入数组
resolve(value);
})
})
}).then((value) => {
console.log(value.join('\r\n'));
});
Set
ES6提供了新的数据结构set集合,类似于数组,但成员的值都是唯一的,集合实现了iterator接口,所以可以使用拓展运算符…和for..of进行遍历。
基本定义
1
2let s = new Set();
console.log(s, typeof s);可以实现自动去重 但注意这里还不是数组
1
2let s2 = new Set(['aaa','bbb','aaa','ccc']);
log out: aaa,bbb,cccsize 返回集合的元素个数
1
s2.size;
add 增加一共新元素 返回当前集合
1
s2.add('ddd');
delete 删除当前元素 返回boolean值
1
s2.delete('aaa');
has 检测集合中是否包含某个元素,返回boolean值
1
s2.has('bbb');
clear 清空元素
1
s2.clear();
数组去重 上面说到去重的办法,但是他还是集合的形式,要通过拓展运算符把他变成数组
1
2
3let arr = [1,2,3,4,5,4,3,2,1];
let result = [...new Set(arr)];
console.log(result);交集 注意先去重
1
2
3
4
5
6let arr = [1,2,3,4,5,4,3,2,1];
let arr2 = [1,2,3];
let s2 = new Set(arr2);
//去重之后和arr2进行比对
let result = [...new Set(arr)].filter(item=> new Set(arr2).has(item));
console.log(result);并集 注意数组拼接之前需要转化,以及最后去重之后要转化为数组
1
let result = [...new Set([...arr,...arr2])];
差集 就是交集的取反
1
2
3
4
5
6let arr = [1,2,3,4,5,4,3,2,1];
let arr2 = [1,2,3];
let s2 = new Set(arr2);
//去重之后和arr2进行比对
let result = [...new Set(arr)].filter(item=> !(new Set(arr2).has(item));
console.log(result);
Map
ES6提供了Map数据结构,它类似于对象,也是键值对的集合,但是键的范围不仅限于字符串,各种类型的值,包括对象 都可以作为键。Map也实现了iterator接口,可以使用拓展运算符…和for..of遍历
基本方法
- size 返回map的元素个数
1
2const m = new Map();
m.size(); set 增加一个新的元素
1
m.set('jojo','saigouda');
get 返回键名对象的键值
1
m.get('jojo');
has 检测其中是否含有某个元素,返回boolean值
1
console.log(m.has('jojo'))
- clear 清空map
1
m.clear();
- 遍历
1
2
3for(let v of m){
console.log(v);
}
Class
ES6提供了更接近传统的写法,引入了class类这个概念,作为对象的模板。通过class关键字,可以定义类,基本上,es6的class可以看作是一个语法糖,它的绝大部分功能es5都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语法
之前es5的方法1
2
3
4
5
6
7
8
9
10
11
12function Phone(brand,price){
this.brand = brand;
this.price = price;
}
//添加方法
Phone.prototype.call = function(){
console.log('i can call');
}
let huawei = new Phone('huawei','2999');
huawei.call();
console.log(huawei)
现在的方法1
2
3
4
5
6
7
8
9
10
11
12class JOJO{
constructor(name,stand){
this.name = name;
this.stand = stand;
}
//方法必须使用该语法 不能使用es5的call:function(){}
call(){
console.log('oraoraoraoraora!!!');
}
}
let jotaro = new JOJO("jotaro","starplj");
console.log(jotaro);
- 类的静态成员
1
2
3
4
5
6
7
8
9
10
11class Phone{
static name = 'lalala';
static change(){
console.log('cascacs')
};
}
let nokia = new Phone();
console.log(nokia.name);
//此时会输出undefine 因为name是类的静态属性,不是实例对象的属性
console.log(Phone.name);
//logout:lalala - 类的继承
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//先写一个父类
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.playGame = function(){
console.log('我可以玩游戏');
}
const chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch');
console.log(chuizi); - 类的继承2
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
28class Phone{
constructor(brand,price){
this.brand = brand;
this.price = price;
}
call(){
console.log('我可以打电话');
}
}
class SmartPhone extends Phone{
constructor(brand,price,color,size){
super(brand,price);//Phone.call(this,brand,price)
this color = color;
this size = size;
}
photo(){
console.log('拍照');
}
playGame(){
console.log('玩游戏');
}
}
const xiaomi = new SmartPhone('小米',799,'黑色','4.22inch');
console.log(xioami);
xiaomi.call();
xiaomi.photo();
xiaomi.playGame(); - 子类重写父类方法 重写方法 这个方法可以重写但不能调用父类的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Phone{
constructor(brand,price){
this.brand = brand;
this.price = price;
}
call(){
console.log('我可以打电话');
}
}
class SmartPhone extends Phone{
constructor(brand,price,color,size){
super(brand,price);//Phone.call(this,brand,price)
this color = color;
this size = size;
}
call(){
}
}
const xiaomi = new SmartPhone('小米',799,'黑色','4.22inch');
xiaomi.call(); - get与set
1
2
3
4
5
6
7
8
9
10
11
12class Phone{
get price(){
console.log('价格属性被读取了');
return 'i love u';
}
set price(newVal){
console.log('价格属性被修改了');
}
}
let s = new Phone();
//console.log(s.price);
s.price = 'free';
数值拓展
ES6定义了几种数值拓展,对于数值检测很有帮助
- Number.EPSILON 是js表示的最小精度,约等于2.2x10的-16次方
1
2
3
4function equal(a,b){
return Math.abs(a-b)<Number.EPSILON;
}
console.log(equal(0.1+0.2,0.3)); - 进制数
1
2
3
4
5
6
7
8
9
10
11
12// 二进制
let b = 0b1010;
// 八进制
let o = 0o777;
// 十进制
let d = 10;
// 十六进制
let x = 0xff;
console.log(b);
console.log(o);
console.log(d);
console.log(x); - Number.isFinite 检测一个数是否为有限数
1
2console.log(Number.isFinite(Infinity));
//返回false - Number.isNaN 检测一个数值是否为NaN
- Number.parseInt Number.parseFloat 字符串转整数
- Number.isInteger() 判断整数
- Number.trunc 将小数部分抹掉
- Math.sign 判断是否为正数 负数 0 如果是正数为1 负数为-1 0为0
对象方法拓展
- Object.is 判断两个值是否完全相等 可以判断NaN这种特殊类型
1
2
3
4console.log(Object.is(NaN,NaN));
//true
console.log(NaN === NaN);
//false - Object.assign 对象的合并 会覆盖原先对象有的属性 没有的属性不会覆盖
- Object.setPrototypeOf Object.getPrototypeOf
1
2
3
4
5
6
7
8
9const school = {
name:'广师大'
}
const cities = {
xiaoqu:['gz','hy']
}
Object.setPrototypeOf(school,cities);
console.log(Object.getPrototypeOf(school));
console.log(school);
ES6模块化
模块化是指将一个大的文件,拆分成许多小的文件,然后将小的文件组合起来
优点:
- 防止命名冲突
- 代码复用
- 高维护性
模块功能主要由两个命令组成:export和import
- export用于规定模块的对外接口
- import命令用于输入其他模块提供的功能
需要在vscode里面安装liveserver插件
- export 分别暴露
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//新建一个new.js
export let jojo = 'jojo';
export function change(){
console.log('balalalalnananan1');
}
//再新建一个index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module">
import * as m1 from "./new.js";
console.log(m1);
</script>
</body>
</html> export 统一暴露
1
2
3
4
5
6
7let jojo = 'jojo';
function change(){
console.log('balalalalnananan1');
}
export {jojo,change};export 默认暴露
1 | export default{ |
解构赋值
- 应对统一暴露 当已经有声明的对象的时候 可以采用取别名的方法
1
2import {jojo,change} from './newjs';
import {jojo as newjojo,change} from './newnewjs'; - 应对默认暴露 写法必须起别名
1
import {default as m3} from './newjs'
简便形式 只能应对默认暴露1
import m3 from './newjs'
以上的方法如果在多个模块的时候很不方便 可以使用下面的方法1
2
3
4//新建一个app.js
import * as m1 from './m1.js';
//在html中引入
<script src='./app.js' type="module"></script>
Babel
一款代码兼容性转化软件/网站。可以将新特性的代码转化为兼容性的代码
需要安装三个工具
- babel-cli babel的一个命令行工具
- babel-preset-env 预设包 可以将最新的es特性转化为es5的语法
- browserify 一个打包工具 (比较简单)
流程
- 初始化
npm init --yes
npm i babel-cli babel-preset-env browserify -D
- 局部安装使用
npx
全局安装使用babel
npx babel js路径 -d 转移到新js路径 --presets=babel-preset-env
- 打包
npx browserify dist/js/app.js -o dist/bundle.js
(前者入口js,后者保存路径) - 总结 先编译 再打包
引入NPM包