前端笔记

337 阅读11分钟

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的几种方法

  1. Promise.all()创建的Promise 会在这一组Promise全部完成后 再完成并返回一个新的Promise
    1. 这组Promise 都成功后 , 则返回的Promise 的状态就是 resolve 并接收一个包含这组promise返回值的数组
    2. 如果有一个失败 , 第一个失败的会把自己的的失败状态 传递给返回的 promise对象
  2. Promise.race()是一组集合中最先解决或最先拒绝的Promise,返回一个新的Promise。
  3. Promise.resolve() 返回一个成功的状态
  4. Promise.reject() 返回一个失败的状态
  5. Promise.finally() Promise结束后执行(不论失败还是成功)

浏览器渲染机制、重绘、重排

网页生成过程:

  1. HTML 被HTML解析器解析成 DOM 树
  2. css 被css解析器解析成 CSSOM 树
  3. 结合 DOM 树和 CCSOM 树,生成一棵渲染树(Render Tree)
  4. 生成布局(flow),即将所有渲染树的所有节点进行平面合成
  5. 将布局绘制(paint)在屏幕上

重排(回流 )

当 DOM 元素的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面当中的正确位置,这个过程叫做重排

重排会引起重绘

触发:
  1. 添加或者删除可见的DOM元素
  2. 元素的尺寸改变---边距,填充,边框,宽高

重绘

当一个元素的外观发生改变,但是没有改变布局,重新把元素绘制出来的过程,叫做重绘

触发:
  1. 改变元素的 color,background,box-shadow

transform 不重绘,不回流 transform属于合成属性,对合成属性进行 transition/animation 动画时,将会创建一个合成层 这使得动画元素 在一个独立的层中进行渲染 当元素的内容没有发生改变,就没有必要进行重绘 浏览器会通过重新复合来创建动画帧

BFC

BFCBlock Formatting Context 的缩写,即块格式化上下文。BFC是CSS布局的一个概念,是一个环境,里面的元素不会影响外面的元素。

布局规则:Box是CSS布局的对象和基本单位,页面是由若干个Box组成的。元素的类型和display属性,决定了这个Box的类型。不同类型的Box会参与不同的Formatting Context

创建:浮动元素 display:inline-block position:absolute

应用: 1.分属于不同的BFC时,可以防止margin重叠 2.清除内部浮动 3.自适应多栏布局

词法作用域

词法作用域 其实是作用域工作模型的一种,JavaScript 遵循的正是这种工作模型。它是定义在词法阶段的作用域,我们了解编译过程中在词法分析的阶段会形成抽象语法树(AST),在同一时间还会根据相应的分析生成对应的作用域。根据这种工作机制我们不难知道,某个标识符属于哪个作用域、作用域的嵌套关系(作用域链)其实在书写时已经决定了。

  1. 词法作用域是一种作用域工作模型,函数、块作用域在代码书写时就已经确定。
  2. eval 和 with 可以在代码运行时动态修改作用域,但是在实际编码中请不要使用这两个方法,它们会大大影响代码的执行效率。

Event Loop

Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

队列(Queue)

特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和一样,队列是一种操作受限制的线性表。 进行插入操作的端称为队尾,进行删除操作的端称为队头。 队列中没有元素时,称为空队列

队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出FIFO—first in first out

MacroTask(宏任务)

JavaScript中,任务被分为两种,一种宏任务(MacroTask)也叫Task,一种叫微任务(MicroTask)。

  • script全部代码、setTimeoutsetIntervalsetImmediate(浏览器暂时不支持,只有IE10支持,具体可见MDN)、I/OUI Rendering

MicroTask(微任务)

  • Process.nextTick(Node独有)PromiseObject.observe(废弃)MutationObserver(具体使用方式查看这里

浏览器中的Event Loop

Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。

JS调用栈

JS调用栈采用的是后进先出的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移出,直到栈内被清空。

同步任务和异步任务

Javascript单线程任务被分为同步任务异步任务,同步任务会在调用栈中按照顺序等待主线程依次执行,异步任务会在异步任务有了结果后,将注册的回调函数放入任务队列中等待主线程空闲的时候(调用栈被清空),被读取到栈内等待主线程的执行。

浏览器线程

  1. GUI渲染线程

    • 绘制页面,解析HTML、CSS,构建DOM树等
    • 页面的重绘和重排
    • 与JS引擎互斥(JS引擎阻塞页面刷新)
  2. JS引擎线程

    • js脚本代码执行
    • 负责执行准备好的事件,例如定时器计时结束或异步请求成功且正确返回
    • 与GUI渲染线程互斥
  3. 事件触发线程

    • 当对应的事件满足触发条件,将事件添加到js的任务队列末尾
    • 多个事件加入任务队列需要排队等待
  4. 定时器触发线程

    • 负责执行异步的定时器类事件:setTimeout、setInterval等
    • 浏览器定时计时由该线程完成,计时完毕后将事件添加至任务队列队尾
  5. 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 中又删除了这个限制,也就是说在严格模式下声明重复属性也不会抛出异常了。

闭包

闭包是一个可以访问外部作用域的内部函数,即使这个外部作用域已经执行结束。