Promise
Promise概述
Promise 是 ES6 新增的引用类型,可以使用 new 关键字实例化一个 Promise 对象 接收一个函数作为参数 函数内部可以执行一些异步的操作 (同步执行)
new Promise(function()) // 该方法会返回一个Promise对象
- Promise 有三种状态
等待态(Pending)执行态(Fulfilled)拒绝态(Rejected) - Promise的状态 只能由 Pending 修改为 Fulfilled或 Rejected 且修改后不能再变
- Promise 状态改变时可以传递一个参数 如下:
new Promise(function(resolve, reject){
setTimeout(()=>{
resolve('执行成功') // 此时状态更改为 Fulfilled 接收一个参数
})
})
- 函数内部状态确定后可以使用 .then 方法分别指定 Fulfilled(resolve) 状态 和 Rejected(reject) 状态的回调函数 , 这两个函数都是可选的 (任何传给then方法的 非函数型参数 都会被忽略 )
- then方法返回一个 新的 Promise 对象才能实现链式调用
- catch方法接收一个回调函数可以捕获执行链中的错误
new Promise(...)
.then((resolve)=>{
return new Premise()
},(reject)=>{...})
.then()
.catch((err)=>{...})
Promise的几种方法
- Promise.all()创建的Promise 会在这一组Promise全部完成后 再完成并返回一个新的Promise
- 这组Promise 都成功后 , 则返回的Promise 的状态就是 resolve 并接收一个包含这组promise返回值的数组
- 如果有一个失败 , 第一个失败的会把自己的的失败状态 传递给返回的 promise对象
- Promise.race()是一组集合中最先解决或最先拒绝的Promise,返回一个新的Promise。
- Promise.resolve() 返回一个成功的状态
- Promise.reject() 返回一个失败的状态
- Promise.finally() Promise结束后执行(不论失败还是成功)
浏览器渲染机制、重绘、重排
网页生成过程:
- HTML 被HTML解析器解析成 DOM 树
- css 被css解析器解析成 CSSOM 树
- 结合 DOM 树和 CCSOM 树,生成一棵渲染树(Render Tree)
- 生成布局(flow),即将所有渲染树的所有节点进行平面合成
- 将布局绘制(paint)在屏幕上
重排(回流 )
当 DOM 元素的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面当中的正确位置,这个过程叫做重排
重排会引起重绘
触发:
- 添加或者删除可见的DOM元素
- 元素的尺寸改变---边距,填充,边框,宽高
重绘
当一个元素的外观发生改变,但是没有改变布局,重新把元素绘制出来的过程,叫做重绘
触发:
- 改变元素的 color,background,box-shadow
transform 不重绘,不回流 transform属于合成属性,对合成属性进行 transition/animation 动画时,将会创建一个合成层 这使得动画元素 在一个独立的层中进行渲染 当元素的内容没有发生改变,就没有必要进行重绘 浏览器会通过重新复合来创建动画帧
BFC
BFC是Block Formatting Context的缩写,即块格式化上下文。BFC是CSS布局的一个概念,是一个环境,里面的元素不会影响外面的元素。
布局规则:Box是CSS布局的对象和基本单位,页面是由若干个Box组成的。元素的类型和display属性,决定了这个Box的类型。不同类型的Box会参与不同的Formatting Context。
创建:浮动元素 display:inline-block position:absolute
应用: 1.分属于不同的BFC时,可以防止margin重叠 2.清除内部浮动 3.自适应多栏布局
词法作用域
词法作用域 其实是作用域工作模型的一种,JavaScript 遵循的正是这种工作模型。它是定义在词法阶段的作用域,我们了解编译过程中在词法分析的阶段会形成抽象语法树(AST),在同一时间还会根据相应的分析生成对应的作用域。根据这种工作机制我们不难知道,某个标识符属于哪个作用域、作用域的嵌套关系(作用域链)其实在书写时已经决定了。
- 词法作用域是一种作用域工作模型,函数、块作用域在代码书写时就已经确定。
- eval 和 with 可以在代码运行时动态修改作用域,但是在实际编码中请不要使用这两个方法,它们会大大影响代码的执行效率。
Event Loop
Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
队列(Queue)
特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。
进行插入操作的端称为队尾,进行删除操作的端称为队头。 队列中没有元素时,称为空队列。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)
MacroTask(宏任务)
在JavaScript中,任务被分为两种,一种宏任务(MacroTask)也叫Task,一种叫微任务(MicroTask)。
script全部代码、setTimeout、setInterval、setImmediate(浏览器暂时不支持,只有IE10支持,具体可见MDN)、I/O、UI Rendering。
MicroTask(微任务)
Process.nextTick(Node独有)、Promise、Object.observe(废弃)、MutationObserver(具体使用方式查看这里)
浏览器中的Event Loop
Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。
JS调用栈
JS调用栈采用的是后进先出的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移出,直到栈内被清空。
同步任务和异步任务
Javascript单线程任务被分为同步任务和异步任务,同步任务会在调用栈中按照顺序等待主线程依次执行,异步任务会在异步任务有了结果后,将注册的回调函数放入任务队列中等待主线程空闲的时候(调用栈被清空),被读取到栈内等待主线程的执行。
浏览器线程
-
GUI渲染线程
- 绘制页面,解析HTML、CSS,构建DOM树等
- 页面的重绘和重排
- 与JS引擎互斥(JS引擎阻塞页面刷新)
-
JS引擎线程
- js脚本代码执行
- 负责执行准备好的事件,例如定时器计时结束或异步请求成功且正确返回
- 与GUI渲染线程互斥
-
事件触发线程
- 当对应的事件满足触发条件,将事件添加到js的任务队列末尾
- 多个事件加入任务队列需要排队等待
-
定时器触发线程
- 负责执行异步的定时器类事件:setTimeout、setInterval等
- 浏览器定时计时由该线程完成,计时完毕后将事件添加至任务队列队尾
-
HTTP请求线程
- 负责异步请求
- 当监听到异步请求状态变更时,如果存在回调函数,该线程会将回调函数加入到任务队列队尾
const s = new Date().getSeconds(); // 定时器并不是指定事件后立即执行 而是指定事件之后放入调用栈等待执行栈清空(全部执行完毕)后执行 setTimeout(function() { // 输出 "2",表示回调函数并没有在 500 毫秒之后立即执行 console.log("Ran after " + (new Date().getSeconds() - s) + " seconds"); }, 500); while(true) { if(new Date().getSeconds() - s >= 2) { console.log("Good, looped for 2 seconds"); break; } }
break,continue,return
3个关键词的含义和比较
在 break,continue和return 三个关键字中, break,continue是化为一类的,return 是函数返回语句,但是返回的同时也将函数停止。
相同之处:三个都会将此时进行的语句停止。
不同之处:
**1、**break:break 语句中止当前循环,switch语句或label 语句,并把程序控制流转到紧接着被中止语句后面的语句。
**2、**continue:是停止当前语句,并从头执行该语句。(跳出本次循环或迭代,开启下一次)
continue语句只能用在while语句、do/while语句、for语句、或者for/in语句的循环体内,在其它地方使用都会引起错误!
continue:和break语句相似。所不同的是,它不是退出一个循环,而是开始循环的一次新迭代。
**3、**return:停止函数。
返回控制
无函数结果,语法为:return; 在大多数情况下,为事件处理函数返回false,可以防止默认的事件行为。 Return False 就相当于终止符,Return True 就相当于执行符。 返回的false和true通常用在需要进行布尔类型判断时。 比如你单击一个链接,除了触发你的onclick事件(如果你指定的话)以外还要触发一个默认事件就是执行页面的跳转。所以如果你想取消对象的默认动作就可以return false。我们也常用return false来阻止提交表单或者继续执行下面的代码。
**4、**使用的语句环境不一样,break和continue是用在循环或switch语句中,return是用在函数语句中。
JavaScript 中的执行上下文和执行栈
什么是执行上下文?
简而言之,执行上下文是评估和执行 JavaScript 代码的环境的抽象概念。每当 Javascript 代码在运行的时候,它都是在执行上下文中运行。
执行上下文的类型
JavaScript 中有三种执行上下文类型。
- 全局执行上下文 — 这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置
this的值等于这个全局对象。一个程序中只会有一个全局执行上下文。 - 函数执行上下文 — 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序(将在后文讨论)执行一系列步骤。
- Eval 函数执行上下文 — 执行在
eval函数内部的代码也会有它属于自己的执行上下文,但由于 JavaScript 开发者并不经常使用eval,所以在这里我不会讨论它。
严格模式
"use strict"
严格模式下创建变量
在非严格模式下,可以直接声明一个全局变量,而不是用 var、let 或 const 关键字,并且还不报错
a = 1;
如果是在严格模式下,以上面方式声明变量,则不被允许,会在执行代码是抛出ReferenceError。 并且在严格模式下,不允许在变量上调用 delete,这也会导致ReferenceError异常,如果在普通模式下,即使失败也是静默返回一个 false。
let a = 1;
delete a;
严格模式下的对象
在严格模式下操作对象,以下这些情况也会引发错误抛出异常:
- 给只读属性赋值会抛出TypeError。
- 在不可配置属性上使用delete会抛出TypeError。
- 给不存在的对象添加属性会抛出TypeError。
此外,相对于非严格模式,声明两个重复的属性名也会抛出语法错误,在普通模式下则不会,最后声明的属性才会生效。
不过值得注意的是,在 ES6 中又删除了这个限制,也就是说在严格模式下声明重复属性也不会抛出异常了。
闭包
闭包是一个可以访问外部作用域的内部函数,即使这个外部作用域已经执行结束。