ES6读书笔记(下)

##Proxy
1.概述
Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,对编程语言进行编程
可以理解成,在目标对象之前架设一层”拦截“,外界对该对象的访问,都必须先通过这
层拦截,可以翻译为”代理器”
2.ES6原生提供Proxy构造函数,用于生成Proxy实例
##Reflect
1.概述
Reflect对象和Proxy对象都是为了操作对象提供的API
2.设计目的
1.将Object对象的一些明显属于语言内部的方法放到Reflect对象上,现阶段,某些方法同时在Object对象和
Reflect对象上部署,未来的新方法只在Reflect对象上部署,可以直接获取语言内部的方法
2.修改某些方法返回的结果,让其变得更加合理
3.让Object操作都变成函数行为
4.Reflect对象和Proxy对象方法一一对应,Proxy对象可以方便调用对应的Reflect方法来完成默认行为
也就是说无论Proxy怎么修改默认行为,我们总可以在Reflect上获取默认行为
##JS异步机制(方便更好地理解Promise对象)
1.同步
函数返回的时候,调用者就能够得到预期结果(即拿到了预期的返回值或者看到了预期的效果)
函数就是同步的
2.异步
1.如果在函数返回的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到
函数就是异步的
2.如果函数是异步的,调用之后,马上返回,但是不会马上返回预期结果。调用者不必主动等待
当被调用者得到结果之后会通过回调函数主动通知调用者
3.单线程和多线程
JS是单线程,怎么还存在异步,耗时操作到底交给谁去执行了?
JS是一门语言,说是单线程还是多线程得结合具体运行环境。
JS的运行通常是在浏览器中进行的,具体由JS引擎去解析和运行
4.浏览器
1.浏览器内部是多线程的
2.常见线程
1.渲染引擎线程:顾名思义,该线程负责页面的渲染
2.JS引擎线程:负责JS的解析和执行
3.定时触发器线程:处理定时事件,比如setTimeout, setInterval
4.事件触发线程:处理DOM事件
5.异步http请求线程:处理http请求
3.渲染线程和JS引擎线程是不能同时进行的。渲染线程在执行任务的时候,
JS引擎线程会被挂起。因为JS可以操作DOM,若在渲染中JS处理了DOM,浏览器就不知所措了
5.渲染引擎和JS引擎
渲染引擎就是如何渲染页面,Chrome用Webkit引擎,IE用Trident引擎,FireFox用Gecko引擎
不同的引擎对同一个样式的实现不一致,就导致了浏览器样式兼容性问题
1.JS引擎
JS引擎可以说是JS虚拟机,负责JS代码的解析和执行
1.词法分析:将源代码分解为有意义的分词
2.语法分析:用语法分析器将分词解析成语法树
3.代码生成:生成机器能运行的代码
4.代码执行
不同浏览器的JS引擎也不同,Chrome用的是V8,FireFox用的是SpiderMonkey
Safari用的是JavaScriptCore,IE用的是Chakra
2.JS是单线程,浏览器在运行时只开启了一个JS引擎线程来解析和执行JS
如果同时有两个线程去操作DOM,浏览器要不知所措
虽然JS是单线程的,可是浏览器内部不是单线程的。一些I/O操作、定时器的计时和事件监听
(click, keydown…)等都是由浏览器提供的其他线程来完成的
6.消息队列和事件循环
1.回调函数具体何时加入到JS引擎线程中执行?
1.左边的栈存储的是同步任务,就是那些能立即执行、不耗时的任务,如变量和函数的初始化
事件的绑定等等那些不需要回调函数的操作
2.右边的堆用来存储声明的变量、对象。下面的队列就是消息队列,一旦某个异步任务有了响应
就会被推入队列中。如用户的点击事件、浏览器收到服务的响应和setTimeout中待执行的事件,
每个异步任务都和回调函数相关联
3.JS引擎线程用来执行栈中的同步任务,当所有同步任务执行完毕后,栈被清空
然后读取消息队列中的一个待处理任务,并把相关回调函数压入栈中,单线程开始执行新的同步任务
JS引擎线程从消息队列中读取任务是不断循环的,每次栈被清空后,都会在消息队列中读取新的任务
等待新的任务出现,这就叫事件循环
7.setTimeout
1.间隔一定的时间后,将回调函数插入消息队列中,等栈中的同步任务都执行完毕后,再执行。
因为栈中的同步任务也会耗时,所以间隔的时间一般会大于等于指定的时间
2.setTimeout(fn, 0)的意思是,将回调函数fn立刻插入消息队列,等待执行,而不是立即执行
##Promise对象
1.promise是异步编程的一种解决方案,比传统的回调函数和事件更合理也更强大
2.简单来说就是一个容器,保存着某个未来才会结束的事件,一个异步操作的结束
3.三种状态:Pending(进行中) Fulfilled(已成功) Rejected(已失败)
4.是一个构造函数用来生成Promise实例
1.Promise是一个构造函数,自己身上有all、reject、resolve这几个眼熟的方法
原型上有then、catch等同样很眼熟的方法。这么说用Promise new出来的对象
2.肯定就有then、catch方法,可以new一个
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log(‘执行完成’);
resolve(‘数据’);
}, 2000);
});
Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject
分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数
按照标准来讲,resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected
在上面的代码中,我们执行了一个异步操作,也就是setTimeout,2秒后,输出“执行完成”,
并且调用resolve方法,运行代码,会在2秒后输出“执行完成”,只是new了一个对象,并没有调用它
3.这是需要注意,所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数
function runAsync(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log(‘执行完成’);
resolve(‘数据’);
}, 2000);
});
return p;
}
runAsync()
runAsync().then(function(data){
console.log(data);
//用传过来的数据做些其他操作
//…
});
在我们包装好的函数最后,会return出Promise对象,执行这个函数我们得到了一个Promise对象
在runAsync()的返回上直接调用then方法,then接收一个参数函数,并且会拿到我们在runAsync中调用
resolve时传的的参数。会在2秒后输出“执行完成”,紧接着输出“数据”
原来then里面的函数就跟我们平时的回调函数一个意思,能够在runAsync这个异步任务执行
完成之后被执行。这就是Promise的作用了就是能把原来的回调写法分离出来,在异步操作执行完后
用链式调用的方式执行回调函数
4.Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用
它比传递callback函数要简单、灵活的多
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
5.function getNumber(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()10);
if(num<=5){
resolve(num);
}
else{
reject(‘大了’);
}
}, 2000);
});
return p;
}
getNumber()
.then(
function(data){
console.log(‘resolved’);
console.log(data);
},
function(reason, data){
console.log(‘rejected’);
console.log(reason);
}
);
“失败”的情况,reject的作用就是把Promise的状态置为rejected,这样我们在then中就能捕捉到
然后执行“失败”情况的回调
6.还有一个catch方法,其实它和then的第二个参数一样,用来指定reject的回调
7.Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调
8.all方法的效果实际上是谁跑的慢,以谁为准执行回调,那么相对的就有另一个方法
谁跑的快,以谁为准执行回调,这就是race方法
##Iterator
1.Iterator遍历器
它是一个接口,为各种不同的数据结构提供统一的访问机制
任何数据结构,只要部署Iterator接口,就可以完成遍历操作
2.作用域
1.为各种数据结构提供一个统一的、简便的访问接口
2.使得数据结构的成员可以按某种次序排序
3.ES6创建了新的遍历命令:for…of循环
3.遍历过程
1.创建指针对象,指向当前数据结构的起始位置,遍历器对象本质是一个指针对象
2.第一次调用指针对象的next方法,指向数据结构的第一个成员
3.第二次调用,指向第二个成员
4.不断调用,直到结束
4.返回
返回一个遍历器对象,拥有两个属性
1.value:当前位置的成员
2.done:一个布尔值,表示遍历是否结束
5.默认Iterator接口
数据结构只要部署了Iterator接口,我们就称其为可遍历的
ES6规定,默认的Iterator接口部署在数据接口的Symbol.iterator属性
6.原生的具备Iterator接口
Array Map Set String TypedArray 函数的argumemnts对象 NodeList对象
7.调用Iterator接口的场合
1.解构赋值
2.扩展运算符
3.yield

##Generator函数
1.ES6提供的异步编程解决方案,是Iterator接口的具体实现
2.执行Generator函数会返回一个遍历器对象,Generator函数除了状态机,还是一个遍历器对象生成函数。
返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。
3.Generator函数是一个普通函数,但是有两个特征:
Function关键字与函数名之间有一个*
函数体内部使用yield语句,定义不同的内部状态(yield意思产出)
4.Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号,不同的是,
调用Generator函数后,该函数并不执行,返回的也不是函数运行的结果,而是一个指向内部状态的指针对象
下一步必须调用遍历器对象的next方法,使得指针移向下一个状态。Generator函数是分段执行的
yield语句是暂停执行的标记,而next方法可以恢复执行。
5.可以在Generator函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为
6.Generator和协程
1.协程是一种程序运行的方式,可以理解成"协作的线程"
子例程:协程单线程实现
特殊的线程:多线程实现
2.协程与子例程的差异
1.子例程
堆栈式"后进先出",只有当调用的子函数完全执行完成,才会结束执行父函数
2.协程
多个线程可以并行执行,但只有一个线程处在正在运行状态,其他线程处于暂停态
线程之间可以交换执行权,并行执行、交换执行权的线程称为协程
3.普通线程
抢占式,到底哪个线程先得到资源,必须由运行环境决定
4.JS是单线程语言,只能保持一个调用栈,引入协程后,每个任务可以保持自己的调用栈
抛出错误,可以找到原始的调用栈,不像异步操作的回调函数一旦出错,原始调用栈早就结束
5.完全协程:任何函数都可以让暂停的协程继续执行
半协程:只有Generator函数的调用者才能转移程序的执行权
7.应用:
1.异步操作的同步化表达
2.多步操作,依次执行
3.部署Iterator接口
4.作为数据结构
##Generator函数的异步应用
1.异步编程对Javascript太重要,Javascript语言执行环境是单线程的,如果没有异步编程,
根本没法用,非卡死不可
2.ES6之前,异步编程
1.回调函数
2.事件监听
3.发布/订阅
4.Promise对象
3.异步
向操作系统发出请求,等待操作系统返回文件后再接着执行任务的第二段
4.回调函数
JS异步编程的实现就是回调函数,指把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候
便直接调用函数:callback
fs.readFile(’/etc/passwd’, ‘utf-8’, function(err, data){
if (err) throw err;
console.log(data);
});
执行分为两段,第一段完成以后,任务所在的上下文环境就已经结束了,在这之后跑出的错误
在原来的上下文环境已经无法捕捉,因此只能当作参数传入第二段
5.Promise
1.回调函数并没有问题,问题出现在多个回调函数嵌套上
现多重嵌套,代码不是纵向发展,而是横向发展,很快就会乱成一团
多个异步操作形成了强耦合,只要有一个操作需要操作,它的上层回调函数和下层回调函数都要跟着修改
这就是回调函数地狱
2.Promise对象为了解决回调函数地狱,允许将回调函数的嵌套改写成链式调用
3.缺点:代码冗余,无论什么操作,都是许多then的堆积
6.Generator函数
奥妙在于其中的yield命令,执行到此处,执行权交给其他协程
遇到yield就暂停,等执行权返回,再从暂停的地方继续往后执行
7.Thunk函数
自动执行Generator的一种方法
##async函数
1.ES7提供了async函数,该函数就是Generator函数的语法糖。
2.Async函数就是将Generator函数的替换成async,将yield替换成await。
3.改进:
1.自带内置执行器
2.更好的语义
3.更广的适用性
4.返回值是Promise对象
async函数完全可以看作多个异步操作,包装成的一个Promise对象
而await命令就是内部then命令的语法糖
4.语法:
1.async函数返回一个Promise对象。
2.只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
3.await命令后面是一个promise对象,如果不是会被转成一个立即resolve的Promise对象。
4.如果await后面的异步操作出错,那么async函数返回的Promise对象被reject。
5.async的实现原理
将Generator函数和自动执行器包装在一个函数里
##Class的基本语法
1.ES6引入class作为对象的模板,通过class关键字定义类
2.constructor方法
类的默认方法,通过new命令来生成对象实例时自动调用此方法
默认返回实例对象(即this),也可以返回另一个对象
##Class的继承
1.使用extends关键字来实现继承,ES5通过修改原型链实现继承
class Ponit{}
class ColorPoint extends Point{}
2.super:表示父类的构造函数,用来新建父类的this对象
子类必须在constructor方法中调用super方法,否则新建实例的时候就会出错,子类没有自己的this对象
而是继承的父类的this对象,然后对其加工,否则子类得不到this对象
3.Object.getPrototypeOf()
可以用来从子类上获取父类
4.super关键字
5.Mixin模式:将多个类的接口"混入"另一个类中
##修饰器
1.Decorator是一个函数,用来修改类的行为,ES7引入,已被Babel支持
@decorator
class A {}
2.修饰器还可以修饰类的属性
3.因为存在函数提升,不能用于函数
##Module的语法
1.JS一直没有模块功能,无法将大程序拆分成相互依赖的小文件,再用简单的方法将它们拼接起来
ES6之前就提出来CommonJS和AMD前者用于服务器,后者用于浏览器
ES6在语言规格的层面上实现了模块功能
ES6模块的设计思想尽量静态化,使得编译的时候就可以确定模块之间的依赖关系,以及输入和输出的变量
CommonJS和AMD只能在运行时确定这些东西
2.ES6模块不是对象,通过export命令显式指定输出的代码,再通过import命令输入
可以在编译的时候完成了模块加载,导致ES6模块本身无法被引用,因为它不是对象
3.export
模块功能主要由两个命令构成,export/import
1.export:规定模块的对外接口
2.import:输入其他模块的功能
一个模块就是一个独立的文件,该文件内部的所有变量,外部是无法获取的,必须使用export输出这个变量
还可以输出函数或类
必须和模块内部的变量建立一一对应的关系
4.import
1.使用import加载这个模块
2.使用整体加载(
)来指定一个对象
3.使用export default命令为模块指定默认输出
4.模块之间也可以继承
##Module的加载实现
1.默认情况下,浏览器同步加载JS脚本,即渲染到

发布了34 篇原创文章 · 获赞 34 · 访问量 1108

猜你喜欢

转载自blog.csdn.net/qq_45517916/article/details/103151556