ES6学习笔记
参考书在线阅读:《ES6标准入门(第三版)》
let
1 | let a; |
- 变量不能重复声明。(var可以重复声明)
1
2let a=1;
//let a=2;//× - 块级作用域
ES5中的三种作用域:全局,函数,eval(严格模式)1
2
3
4{
let doge='wangwang';
}
console.log(doge);//取不到,但是var取得到 - 不存在变量提升
1
2
3
4
5
6
7
8
9console.log(song);
var song='海上日记';
//相当于:
var song;
console.log(song);//undefined
var song='海上日记';
console.log(song);//使用let则会报错
let song='一程山路'; - 不影响作用域链以for循环为例:
1
2
3
4
5
6
7{
let master='miaomiao';
function playWithCat(){
console.log(master);
}
playWithCat();//可以读取
}结构为: for(单次表达式1;条件表达式2;末尾循环体3){中间循环体;}1
2
3
4
5for(let i=0;i<items.length;i++){
items[i].onclick=function(){
items[i].style.background='pink';
}
}
相当于:i为全局变量,所以循环结束时,i=item.length,但此时items[i]取不到值。1
2
3
4
5
6
7
8
9{
var i=0;
}
{
var i=1;
}
{
var i=2;
}
将var改为let可解决问题:这样数组的每一项都能得到修改。1
2
3
4
5
6
7
8
9{
let i=0;
}
{
let i=1;
}
{
let i=2;
}
所以以后for循环尽量用let声明初始值。
const
- 一定要赋初始值
- 一般常量使用大写(不强制)
- 常量值不能修改
- 块级作用域
- 对于数组和对象的元素修改,不算作对常量的修改,不会报错
1
2const ANI=['Aria','Digimon','OnePiece'];
ANI.push('IScream');//可以修改
变量的解构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值。
- 数组的解构
1
2
3
4
5
6const F4=['佐助','罗夏','路飞','白龙'];
let [sasuke,rorschach,luffy,haku]=F4;
console.log(sasuke);//佐助
console.log(rorschach);//罗夏
console.log(luffy);//路飞
console.log(haku);//白龙 - 对象的解构
1
2
3
4
5
6
7
8
9
10
11
12const rorschach={
name:'罗夏',
age:28,
money:function(){
console.log('我有很多钱');
}
};
let {name, age, money}=rorschach;
console.log(name);//罗夏
console.log(age);//28
console.log(money);//function...
money();//我有很多钱
模板字符串
ES6引入了新的声明字符串的方式[``]反引号。
- 声明
1
2let str=`我也是一个字符串哦`;
console.log(str, typeof str);//我也是一个字符串哦 string - 内容中可以直接出现换行符
普通的引号无法换行,只能用加号+拼接。1
2
3
4
5
6let str=`
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>`; - 变量拼接
1
2
3let lovest='水星领航员';
let out=`我最喜欢的番剧是${lovest}`;
console.log(out);//我最喜欢的番剧是水星领航员
对象的简化写法
ES6允许在大括号里直接写入变量和函数,作为对象的属性和方法。书写更简洁。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15let name='长门';
let change=function(){
console.log('一袋米哦扛几楼');
}
const juezhao={
name,
change,
//improve:function{
// console.log('神罗天征');
//}
//简写为:
improve(){
console.log('神罗天征');
}
}
箭头函数
let fn=(a,b)=>{...};
- this是静态的,this始终指向函数声明时所在作用域下的this的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function a(){
console.log(this.hh);
}
let b=()=>{
console.log(this.hh);
}
window.hh='window的属性';
const bianliang={
hh:'bianliang的属性'
}
//直接调用:this都是指向window
a();//window的属性
b();//window的属性
//call方法调用:可以改变函数内部this的值
a.call(bianliang);//bianliang的属性
b.call(bianliang);//window的属性 (箭头函数的this不被改变) - 不能作为构造函数实例化对象
1
2
3
4
5
6let Artist=(name,album)=>{
this.name=name;
this.album=album;
}
let hikaru=new Artist('宇多田光','First Love');
console.log(hikaru);//报错 - 不能使用arguments(用于保存实参)变量
1
2
3
4let fn=()=>{
console.log(arguments);
}
fn(1,2,3);//报错 - 箭头函数的简写
(1)省略小括号,当形参有且只有一个的时候(2)省略花括号,当代码体只有一条语句的时候,此时return必须省略,且语句的执行结果就是函数的返回值1
2
3
4let add=n=>{
return n+n;
}
console.log(add(9));1
2let add=n=>n*n;
console.log(add(9)); - 应用箭头函数适合与this无关的回调:定时器,数组的方法回调。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//需求:点击div 2s后变成粉色
let ad=document.getElementById('ad');
//普通函数的写法:
ad.addEventListener('click',function(){
let _this=this;//this的常见命名:_this, that, self
setTimeOut(function(){
_this.style.background='pink';
},2000);
});
//箭头函数的写法:
ad.addEventListener('click',function(){
setTimeOut(()=>{
this.style.background='pink';//这里的this指代的就是该函数外层作用域的事件源ad
},2000);
});
箭头函数不适合与this有关的回调:事件回调,对象的方法。
函数参数的默认值设置
ES6允许给函数参数赋初始值。
- 形参初始值:具有默认值的参数,一般位置靠后(潜规则)。
1
2
3
4
5function add(a,b,c=10){
return a+b+c;
}
let result=add(1,2);
console.log(result);//13 - 与解构赋值结合使用
1
2
3
4
5
6
7
8
9function fn({a,b,c='hhh'}){
console.log(a);
console.log(b);
console.log(c);
}
fn({
a:'111',
b:'222'
});rest
ES6引入rest参数,用于获取函数的实参,用来代替arguments。如有多个参数,rest参数必须要放到最后。1
2
3
4
5
6
7
8
9
10
11//ES5获取实参的方式:
function fn(){
console.log(arguments);
}
fn('哈哈','呵呵','嘿嘿');//打印为对象
//ES6的rest参数:
function fn2(...args){
console.log(args)
}
fn2('哈哈','呵呵','嘿嘿');//打印为数组,可以用数组的方法:filter,some,every,map 提高了对参数处理的灵活程度
扩展运算符[…]
扩展运算符能将数组转换为逗号分隔的参数序列。1
2
3
4
5
6const girls=['花花','泡泡','毛毛'];
function monster(){
console.log(arguments);
}
monster(...girls);//ES5: monster('花花','泡泡','毛毛');
//数组解构
- 数组的合并
1
2
3
4
5
6
7
8const girls=['Alicia','Akira','Athena'];
const girls2=['Akari','Aika','Alice'];
//ES5:
const ariaSpirit1=girls.concat(girls2);
console.log(ariaSpirit1);//['Alicia','Akira','Athena','Akari','Aika','Alice']
//ES6:
const ariaSpirit2=[...girls,...girls2];
console.log(ariaSpirit2);//['Alicia','Akira','Athena','Akari','Aika','Alice'] - 数组的克隆
1
2
3const zm=['A','B','C'];
const zm2=[...zm];
console.log(zm2);//['A','B','C'] 浅拷贝 - 将伪数组转为真正的数组
1
2
3const div=document.querrySelectorAll('div');
const divArr=[...div];
console.log(divArr);
Symbol
ES6引进的新的原始数据类型,表示独一无二的值,是一种类似字符串的数据类型。
特点
(1)Symbol的值是唯一的,用来解决命名冲突的问题;
(2)Symbol的值不能与其它数据进行运算、拼接、对比;
(3)Symbol定义的对象属性不能使用for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名。1
2
3
4
5
6
7
8
9
10
11
12
13let s =Symbol();
console.log(s,typeof s);//Symbol() "symbol"
let s2=Symbol();
let s3=Symbol();
console.log(s2===s3);//false
//symbol好比身份证,参数是姓名
let s4=Symbol.for('One Piece');
console.log(s4, typeof s4);//Symbol(One Piece) "symbol"
let s5=Symbol.for('One Piece');
console.log(s4===s5);//true目前所学的前端数据类型:
USONB you are so niubility
u undefined
s string symbol
o object
n null number
b boolean使用:给对象添加属性和方法
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//写法一:
let game={
name:'扫雷'
};
let methods={
up:Symbol(),
down:Symbol()
};
game[methods.up]=function(){
console.log(1);
};
game[methods.down]=function(){
console.log(2);
}
//写法二:
let youxi={
name:'连连看',
[Symbol('say')]:function(){
console.log(3);
},
[Symbol('run')]:function(){
console.log(4);
}
}(不太清楚该功能的具体使用场景,和ES5相比有什么优势…)
- 11个Symbol内置值(属性)
|Symbol内置值|用法|
|—-|—-|
|Symbol.hasInstance|当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法|
|Symbol.isConcatSpreadable|对象的 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于 Array.prototype.concat() 时, 是否可以展开。|
|Symbol.species|创建衍生对象时,会使用该属性|
|Symbol.match|当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。|
|Symbol.replace|当该对象被 str.replace(myObject) 方法调用时,会返回该方法的返回值。|
|Symbol.search|当该对象被 str.search (myObject) 方法调用时,会返回该方法的返回值。|
|Symbol.split|当该对象被 str.split(myObject) 方法调用时,会返回该方法的返回值。|
|Symbol.iterator|对象进行 for…of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器|
|Symbol.toPrimitive|该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。|
|Symbol. toStringTag|在该对象上面调用 toString 方法时,返回该方法的返回值|
|Symbol. unscopables|该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除。|
迭代器
迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署iterator接口,就可以完成遍历操作。
- ES6创造了一种新的遍历命令for…of循环,iterator接口主要供for…of消费。
- 原生具备iterator接口的数据(可用for…of遍历):
(1)Array
(2)Arguments
(3)Set
(4)Map
(5)String
(6)TypedArray
(7)NodeList - 工作原理
(1)创建一个指针对象,指向当前数据结构的起始位置;
(2)第一次调用对象的next方法,指针自动指向数据结构的第一个成员;
(3)接下来不断调用next方法,指针一致往后移动,直到指向最后一个成员。 - 应用:自定义遍历数据
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
27const std={
name:'Sam',
score:[
60,
80,
90
],
[Symbol.iterator](){
let index=0;
let _this=this;
return{
next:function(){
if(index<_this.score.length){
const result= {value:_this.score[index],done:false};
index++;
return result;
}else{
return {value:undefined,done:true}
}
}
};
}
}
//对对象的遍历
for(let v of std){
console.log(v);
}
生成器
生成器其实就是一个特殊的函数。
用来进行异步编程。
ES5使用纯回调函数、node、fs、ajax、mongodb进行异步编程。
- 使用生成器函数
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
28
29
30
31
32function * gen(){
console.log('hello');
}
let iterator=gen();
console.log(iterator);//直接调用显示函数,函数本身不执行
//在生成器函数可以出现yield语句:yield相当于函数代码的分隔符
function * gen2(){
//三个分隔符产生四块代码,由next方法执行
console.log(111);
yield '哈哈';
console.log(222);
yield '呵呵';
console.log(333);
yield '嘿嘿';
console.log(444);
}
let iterator=gen2();
iterator.next();//111 {value: '哈哈', done: false}
//在上面的代码执行完后再执行这个:
iterator.next();
iterator.next();//222 333 {value: '嘿嘿', done: false}
//在上面的代码执行完后再执行这个:
iterator.next();//444 {value: undefined, done: true}
//遇到yield就暂停,调用next就继续
//遍历的调用方法:
for(let v of gen2()){
console.log(v);
}//111 哈哈 222 呵呵 333 嘿嘿 444 - 生成器函数参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14function * gen(arg){
//三个分隔符产生四块代码,由next方法执行
console.log(arg);
let one= yield '哈哈';
console.log(one);
let two= yield '呵呵';
console.log(two);
let three= yield '嘿嘿';
console.log(three);
}
let iterator=gen('haha');
iterator.next();//haha {value: '哈哈', done: false}
//执行完上面再执行下面:
iterator.next('hehe');//hehe {value: '呵呵', done: false} - 实例一
1秒后输出111,2秒后输出222,三秒后输出333。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
28
29
30
31
32
33
34
35
36
37//ES5的写法:回调地狱
setTimeout(()=>{
consol.log(111);
setTimeout(()=>{
consol.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();
Set
1 | let s= new Set();//集合 |
- 数组去重:
let result=[...new Set(arr)];
- 交集:
let result=[...new Set(arr)].filter(item=>{new Set(arr2).has(item)});
(filter函数体内如果是true就留下重复的,false则去掉) - 并集:
let result=[...new Set([...arr,...arr2])];
- 差集:
let result=[...new Set(arr)].filter(item=>!(new Set(arr2).has(item)))
Map
ES6提供了Map数据结构,它类似对象,也是键值对集合。但是键的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
Map也实现了iterator接口,所以可以使用“扩展运算符”和“for…of”进行遍历。new Map()
- 属性和方法
(1)size:返回Map的元素个数
(2)set:增加一个新元素,返回当前Map
(3)get:返回键名对象的键值
(4)has:检测Map中是否包含某元素,返回boolean
(5)clear:清空集合,返回undefined
(6)delete:删除某个元素
Proxy
Proxy字面翻译为“代理”,从书面理论上来讲,是ES6中全新创建的一种构造函数,用于拦截对象属性的访问、赋值、函数调用的默认事件,从而进行更改。和后端语言的get/set写法类似,更改原来的取值赋值操作,通过一个自定义的过程得到新的结果,并返回出去。
那么这种技术是如何在软件中应用的呢?
比如变量赋值前的校验、记录对象取值赋值的操作日志、监控对象的性能等等,可以增强代码的安全性。var proxy = new Proxy(target, handler);
先创建一个Proxy对象,该对象为ES6原生提供,Proxy对象有两个参数:第一个是目标对象,第二个对象用来放置拦截方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18let my_obj={};
var proxy = new Proxy(my_obj, {
//Proxy对象的第一个参数和get方法的第一个参数本质上是同一个对象,只不过Proxy从外界接收对象,内部传给get。
get:function(target, propKey, receiver){...},//用于拦截“读”请求(my_obj.id 或 my_obj[id]);第一个参数为目标对象,第二个参数为属性名,第三个可选参数与Reflect.get有关
set:function(target, propKey, value, receiver){...},//用于拦截“写”请求(my_obj.id=123 or my_obj[id]=123);不一定要return,直接赋值即可(target[propKey]=value)
apply:function(target, context, args){...},//用于拦截函数的调用、call操作、apply操作,如需在一系列判断后仍执行原函数调用,则在Proxy的apply内,return Reflect.apply(...arguments);三个参数分别为:目标对象,目标对象上下文、目标对象的参数数组
has:function(){...},//下次再学
construct:function(){...},
deleteProperty:function(){...},
defineProperty:function(){...},
getOwnPropertyDescriptor:function(){...},
getPrototypeOf:function(){...},
setPrototypeOf:function(){...},
isExtensible:function(){...},
ownKeys:function(){...},
preventExtensions:function(){...}
});
my_obj.proxy=proxy;//便于通过对象直接调用
Reflect
- ES6提供的新的API,将原本部署在Object上的方法复制到Reflect上(今后Object上的方法会逐渐废除),并做了优化,且未来新增的Object上的方法只部署在Reflect对象上。
- Reflect对象方法与Proxy对象方法一一对应。
- Reflect共有13个静态方法:(具体参数使用时再查)
· (1)Reflect.get
. (2)Reflect.set
· (3)Reflect.apply
· (4)Reflect.construct (提供一种不写new的构造函数写法)
· (5)Reflect.defineProperty
· (6)Reflect.deleteProperty
· (7)Reflect.has (in运算符)
· (8)Reflect.ownKeys (=Object.getOwnPropertyNames+Object.getOwnPropertySymbols, 返回字符串数组)
· (9)Reflect.isExtensible
· (10)Reflect.preventExtensions
· (11)Reflect.getOwnPropertyDescriptor
· (12)Reflect.getPrototypeOf (读取对象的proto属性)
· (13)Reflect.setPrototypeOf
Promise
Promise,译为“承诺”(只有异步操作能改变Promise对象的状态:Pending->Fulfilled(Resolved) or Pending->Rejected(Resolved),且改变之后不会再改变)。
Promise是ES6引入的异步编程的新解决方案。语法上是一个构造函数,用来封装异步操作可以获取其成功或失败的结果。
Promise可以将异步操作以同步操作的流程表达出来。
缺点:一旦执行,无法中途取消。
- Promise构造函数:Promise(excutor){}可以用于封装读取文件;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17const p=new Promise(function(resolve,reject){
//将异步方法放在Promise实例内
setTimeout(function(){
//let data='haha';
//resolve(data);
let err='fail';
reject(err);
},1000);
});
//只要写了.then,实例内的异步方法就会给出回调结果到then内函数里
p.then(function(value){
//resolve走这里(then的第一个参数函数)
console.log(value);
},function(reason){
//reject走这里(then的第二个参数函数)
console.error(reason);
});
可以用于封装ajax请求。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20const p=new Promise((resolve,reject)=>{
const xhr=new XMLHttpRequest();
xhr.open("GET","https://api.apiopen.top/getJoke");
xhr.send();
xhr.onreadystatechange=function(){
if(xhr.readyState===4){
//判断状态响应码:200-299
if(xhr.status>=200&&xhr.status<300){
resolve(xhr.response);
}else{
reject(xhr.status);
}
}
}
});
p.then(function(value){
console.log(value);
},function(reason){
console.error(reason);
}); - Promise.prototype.then(fun)
用于指定回调,为成功和失败结果作处理,对象状态由回调函数的执行结果决定。
如果回调函数中返回的结果是非promise类型的属性,状态为成功,则返回值为对象的成功的值;
如果回调函数中返回的结果是promise类型的,外面的promise返回的结果是什么,内层的promise状态就是什么,也可以抛出错误。new p1.then(return new p2...)
then方法的返回的结果是一个Promise对象:所以可以链式调用,p.then(value=>{}).then(value=>{})...
避免回调地狱。 - Promise.prototype.catch(fun)
用来指定promise对象失败的回调。p.catch(function(){...})
catch写法优于在.then的第二个参数中定义reject函数,即只要写成:catch方法返回的也是一个Promise对象,因此也可以继续链式调用(如果是链式catch,下一个方法会捕获上一个方法中的错误)。1
2
3var p=new Promise(function(resolve,reject){...})
.then(function(data){...})
.catch(function(err){...}); - Promise.all([…])
用于将多个Promise实例包装成一个Promise实例。
All Fulfilled, Fulfilled.
One Rejected, Rejected.(交集)1
2
3var p=Promise.all([p1,p2,p3])
.then(function(data){...})//第一个Rejected的进入这里
.catch(function(err){...}); - Promise.race([…])
One Resolved/Rejected, Resolved/Rejected.(并集) - Promise.resolve(obj)
转为Promise对象
· (1) obj为一个Promise对象:返回obj
· (2) obj为一个普通对象,有then方法:返回Promise对象,并立即执行then方法
· (3) obj不是对象,或没有then方法:返回Promise对象,从生成时就是Resolved状态,没有异步操作
· (4) Promise.resolve()直接执行,不带参数:返回Promise对象,在本轮事件循环结束时置为Resolved状态 - Promise.reject(msg/obj)
· (1) 参数为字符串:返回Promise对象,立即执行后状态为Rejected,msg为reject的理由
· (2) 参数为含有then方法的对象:返回Promise对象,catch得到的参数为obj的then中reject的方法 - 自己部署一个done方法(jquery已提供)done会捕捉到任何错误,并向全局抛出。
1
2
3
4
5
6
7
8
9
10
11Promise.prototype.done=function(onFulfilled, onRejected){
this.then(onFulfilled, onRejected)
.catch(function(reason){
setTimeout(()=>{throw reason},0);
});
};
AsyncFun()
.then(f1)
.catch(r1)
.then(f2)
.done();//放在回调链的尾端
done不返回Promise对象,所以不能再接catch。 - 自己部署一个finally方法
不管Promise对象最后状态如何都会执行的操作。finally并不是最后一环,done才是。1
2
3
4
5
6
7
8
9
10Promise.prototype.finally=function(callback){
let p=this.comstructor;
return this.then(
value=>p.resolve(callback()).then(()=>value),
reason=>p.resolve(callback()).then(()=>{throw reason})
);
};
AsyncFun()
.then(f1)
.finally(f2);//f2是一个回调函数,一定会执行 - Promise.try(fun)
不用区分fun是同步函数还是异步函数,如果是同步函数则同步执行,如果是异步函数则异步执行。1
2
3Promise.try(fun)
.then(...)
.catch(...)
Iterator(遍历器) 和 for…of
- Iterator是一种接口,可用于遍历操作。
- Iterator创造指针对象,调用指针的next方法,指向下一个成员。
- Iterator接口部署在Symbol.iterator属性上,原生具备Iterator接口的数据结构:Array,Map,Set,String,TypedArray,函数的arguments对象,NodeList对象。
- 具有Iterator接口的数据结构,都可以用for…of循环。
class 类
ES6的class可以只看作是一个语法糖,只是让对象的写法更清晰,更像面向对象的编程的语法。
声明类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//ES5:
function Phone(brand,price){//构造函数
this.brand=brand;
this.price=price;
}
Phone.prototype.call=function(){//添加方法
console.log('打电话');
}
let huawei=new Phone('华为',5999);//实例化
huawei.call();
//ES6:
class Phone{
constructor(brand,price){
this.brand=brand;
this.price=price;
}
call(){//不支持ES5的对象完整形式
console.log('打电话');
}
}
let iphonex=new Phone('Apple',10000);constructor()方法就算没写,JS也会自动添加。
静态成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//ES5:
function Phone{...}
Phone.name='手机';//往函数对象上添加属性和方法
Phone.change=function(){...};
Phone.prototype.size='5.5inch';
let nokia=new Phone();
console.log(nokia.name);//undefined 静态成员无法继承
//实例对象身上没有构造函数对象身上的属性
console.log(nokia.size);//5.5inch 原型上的属性是有的
//ES6:
class Phone{
static name='手机';//静态成员,实例化对象无法获取
static change(){...};
}- 对象继承
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43//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(){...}
SmartPhone.prototype.playGame=function(){...}
const chuizi=new SmartPhone('锤子',2499,'黑色','5.5inch');
console.log(chuizi);//brand√ price√ color√ size√ __proto__:Phone photo√ playGame√ __ptoto__: call√
//ES6:
class 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);//super是父类的constructor,包含了call
this.color=color;
this.size=size;
}
photo(){...}
playGame(){...}
}
const xiaomi=new SmartPhone('小米',799,'黑色','4.7inch');
console.log(xiaomi);//子类属性+方法和父类属性+方法都在 - 对象继承二:子类对父类方法的重写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//ES6:
class 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);
this.color=color;
this.size=size;
}
photo(){...}
playGame(){...}
call(){...}//父类包含这个方法,但是内容和父类不同
}
const xiaomi=new SmartPhone('小米',799,'黑色','4.7inch'); - get和set
1
2
3
4
5
6
7
8
9
10
11class Phone{
get price(){
console.log('价格属性被读取了');
return '999';
}
set price(value){
console.log('价格属性被修改了');
}
let s=new Phone();//实例化一个对象
s.price='free';
}
数值扩展
- Number.EPSILON:js的最小精度
Number.EPSILON ≈ 2.220446049250313080808472633361816E-16
用于判断精度:if Math.abs(a-b)<Number.EPSILON
- 二进制、八进制、十六进制
let b=0b1010;//=十进制的10 二进制用0b开头
let o=0o777;//=十进制的511 八进制用0o开头
let x=0xff;//=十进制的255 十六进制用0x开头 - Number.isFinite(n):检测一个数是否为有限数
1
2Number.isFinite(100/0);//false
Number.isFinite(Infinite);//false - Number.isNaN(n):检测一个变量是否为NaN(not a number)
- Number.parseInt(str):字符串转整数 Number.parseFloat(str):字符串转小数
1
2Number.parseInt('5211314love');//5211314 截断
Number.parseFloat('3.1415926率');//3.1415926 - Number.isInteger(n):判断一个数是否为整数
- Math.trunc(n):将数字的小数部分抹掉
- Math.sign(n):判断一个数为正数、负数、还是零
1
2
3Math.sign(-100);//-1
Math.sign(0);//0
Math.sign(33);//1
对象方法扩展
- Object.is(a,b):判断两个值是否完全相等,一般情况下相当于===
1
2Object.is(NaN,NaN);//true
NaN===NaN;//false - Object.assign(a,b):对象的合并
1
2
3
4
5
6
7
8
9
10
11
12const s1={
a:1,
b:2,
c:3
};
const s2={
a:11,
b:22,
d:4
}
console.log(Object.assign(s1,s2));//{a:11,b:22,c:3,d:4}
//结果:对象合并,相同属性以后一个为主 - Object.getPrototypeOf:获取原型对象 Object.setPrototypeOf:设置原型对象
1
2
3
4const a={name:'aaa'};
const b={age:18};
Object.setPrototypeOf(a,b);//将b作为原型对象给a的__proto__属性
//不建议使用,一般在Object.create时就定义好原型
模块化
模块化:将一个大的程序文件,拆分成多个小的文件,然后组合起来。
- 优点:防止命名冲突,代码复用,高维护性
- 已有的模块化产品:CommonJS=>NodeJS、Browserify,AMD=>requireJS,CMD=>seaJS
- 模块化语法:
export:用于规定模块的对外接口
import:用于输入其他模块提供的接口
(1)通用的导入方式1
2
3
4
5
6
7
8
9
10
11
12
13
14//分别暴露
export let a=1;
export function b(){...}
//统一暴露
c:1;
function d(){...}
export {c,d};
//默认暴露
export default{
e:1,
f:function(){...}
}(2)解构赋值形式1
2
3
4
5<script type="module">
import * as m1 from './src/js/m1.js';
m1.default.f();
</script>1
2
3
4
5
6
7
8
9
10
11
12
13//m1.js
export let a=1;
export function b(){...}
//m2.js
export let a=2;
export let c='cc';
//m3.js
export default{
let a=3;
function d(){...}
}(3)简便形式,针对默认暴露1
2
3
4
5<script type="module">
import {a,b} from './src/js/m1.js';//a,b可以直接使用
import {a as a1,c} from './src/js/m2.js';//重命名重名变量
import {default as m3} from './src/js/m3.js';//默认暴露必须要重命名
</script>(4)使用模块化方式二1
2
3<script type="module">
import m3 from './src/js/m3.js';
</script>1
2
3
4//main.js
import * as m1 from './src/js/m1.js';
import * as m2 from './src/js/m2.js';
import * as m3 from './src/js/m3.js';1
<script src="./src/js/main.js" type="module"></script>
- babel对ES6模块化代码转换
有的浏览器不一定支持在script标签里增加src属性的方式,所以用 babel 做一个转换,可以将比较新的ES6语法转为有些浏览器只能识别的ES5语法。
(1)安装工具 babel-cli, babel-preset-env, browserify(webpack)
(2)npx babel src/js -d dist/js
(3)打包 npx browserify dist/js/app.js -o dist/bundle.js - ES6模块化引入NPM包
1
2//先 npm i jquery 安装包
import $ from 'jquery';//原来的写法:const $ =require('jquery');
ES7新特性
- Array.prototypr.includes
1
2
3const z=['a','b','c','d'];
console.log(z.includes('a'));//true
//与之相比,indexOf返回的是数字下标,includes更方便 - 指数操作符 “ ** ”
1
2console.log(2 ** 10);//1024
console.log(Math.pow(2,10));//1024
ES8新特性
- async和await
async和await两种语法结合可以让异步代码像同步代码一样。
(1)async函数
- async函数的返回值为promise对象
- promise对象的结果由async函数执行的返回值决定(2)await表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15async function fn(){
//return 'aaa';
//1.只要函数本身返回的不是一个promise对象,则async就能返回一个成功(resolved)的Promise对象
//throw new Error('出错啦');
//2.抛出错误,返回的就是一个失败的Promise对象
//3.返回的结果是一个Promise对象
return new Promise((resolve,reject)=>{
//resolve('成功的数据');
//reject('失败');
});
}
const result=fn();
console.log(result);//与函数本身返回的结果一致 - await必须写在async函数中
- await右侧的表达式一般为promise对象
- await返回的是promise成功的值
- await的promise失败了,就会抛出异常,需要通过try…catch捕获处理(3)例子:async与await结合读取文件内容
1
2
3
4
5
6
7
8
9
10
11
12
13const p=new Promise((resolve,reject)=>{
//resolve("成功的值");
reject("失败了");
});
async function main(){
try{
let result=await p;
console.log(result);
}catch(e){
console.log(e);
}
}
main();(4)async与await结合发送Ajax请求1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24const fs=require('fs');
function readMd1(){
return new Promise((resolve,reject)=>{
fs.readFile("./xxx1.md",(err,data)=>{
if(err) reject(err);//如果失败
resolve(data);//如果成功
})
})
}
function readMd2(){
return new Promise((resolve,reject)=>{
fs.readFile("./xxx2.md",(err,data)=>{
if(err) reject(err);//如果失败
resolve(data);//如果成功
})
})
}
async function main(){
let result1 = await readMd1();
let result2 = await readMd2();
console.log(result1.toString());
console.log(result2.toString());
}
main();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
26function sendAJAX(url){
return new Promise((resolve,reject)=>{
const x=new XMLHttpRequest();
x.open('GET',url);
x.send();
x.onreadystatechange=function(){
if(x.readyState===4){
if(x.status>=200&&x.status<300){
resolve(x.resopnse);
}else{
reject(x.status);
}
}
}
})
}
//promise then方法测试:
const result_p = sendAJAX("https://api.apiopen.top/getJoke").then(value=>{
console.log(value);
},reson=>{});
//async与await测试:
async function main(){
let result =await sendAJAX("https://api.apiopen.top/getJoke");
consol.log(result);
}
main();
Object.values()
返回一个给定对象的所有可枚举属性值的数组。Object.entries()
返回一个给定对象自身可遍历属性[key,value]的数组。1
2
3
4
5
6
7
8
9const obj={
a:'aaa',
b:['b1','b2','b3'],
c:[333,444]
};
console.log(Object.keys(obj));//['a','b','c']
console.log(Object.values(obj));//['aaa',['b1','b2','b3'],[333,444]]
console.log(Object.entries(obj));//[0:['a','aaa'],1:['b',[...]],2:['c',[...]]]
console.log(new Map(Object.entries(obj)));//{['a','aaa'],['b',[...]],['c',[...]]}- Object.getOwnPropertyDescriptors()
返回指定对象所有自身属性的描述对象。
configurable
enumerable
ES9新特性
- 对象的rest参数和spread扩展运算符
1
2
3
4
5
6
7
8
9
10
11
12function fn({a,b,...c}){...}
fn({
a:'aaa',
b:123,
c1:'cc1',
c2:'cc2',
c3:333
})
const x1={x11:'content1'};
const x2={x22:'content2'};
console.log({...x1,...x2});//{x11:'content1',x22:'content2'} - 正则扩展-命名捕获分组
1
2
3
4let str='<a href="https://www.baidu.com">百度</a>';
const reg=/<a href="(?<url>.*)">(?<text>.*)<\/a>/;
const result=reg.exec(str);
consolelog(result);//可以获取result.groups.url, result.groups.text - 正则扩展-反向断言
- 正则扩展-dotAll模式
ES10新特性
- Object.fromEntries
Object.entries可以将对象转为二维数组,Object.fromEntries可以将二维数组转为对象(键值对) - 字符串方法扩展:trimStart 和 trimEnd
指定清除字符串左侧空格和右侧空格 - 数组方法扩展:flat 和 flatMap
flat:将多维数组转为低维数组,从外往内展开,不加参数默认展开一层,参数为深度。
flatMap:两个操作的结合,先map,后flat。 - Symbol.prototype.description
获取Symbol的字符串描述。1
2let s=Symbol('name');
consol.log(s.description);//name
ES11新特性
- 私有属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Person{
//公有属性
name;
//私有属性
#age;
#weight;
//初始化:构造方法
constructor(name,age,weight){
this.name=name;
this.#age=age;
this.#weight=weight;
}
intro(){
console.log(this.name);
console.log(this.#age);
console.log(this.#weight);
}
}
const girl=new Person('小红',18,'45kg');
//console.log(girl.#age);//报错
girl.intro();//可以获取私有属性 - Promise.allSettled
1
2
3
4const p1=new Promise(...);
const p2=new Promise(...);
const result=Promise.allSettled([p1,p2]);//只要有一个成功就显示成功
const result2=Promise.all([p1,p2]);//全都成功才不报错 - String.prototype.matchAll
适合数据的批量提取。1
2
3const str=`...`;
const reg=/.../;
const result=str.matchAll(reg); - 可选链操作符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function fn(x){
//const con=config && config.db && config.db.host;
const con=x?.name?.a;
}
// fn({
// name:{
// a:'aaa',
// b:'bbb'
// },
// school:{
// c:'ccc',
// d:'ddd'
// }
// })
fn();
//即使fn没有传入用到的参数,也不会报错,免去了层层判断 - 动态import
ES6的模块化本身是静态的。1
2
3
4
5
6const btn=document.getElementById('btn');
btn.onclick=function(){
import('./hello.js').then(module=>{
module.hello();
})
} - BigInt(大整型)数据类型
1
2
3
4
5
6
7let n=521n;
//整型可以转成大整型
let n2=123;
console.log(BigInt(n2));//123n
//应用于大数值运算
let max=Number.MAX_SAFE_INTEGER;
console.log(BigInt(max)+BigInt(1)); - 绝对全局对象globalThis
指向window对象。
编程风格(迷你知识点)
- 建议不再使用var命令
- let和const之间优先使用const
- 静态字符串使用单引号,动态字符串使用反引号,不再使用双引号
- 添加属性用Object.assign(obj,{name:’aa’});
End.🐧