2022.05.07

闭包

闭包其实只是一个绑定了执行环境的函数

闭包是一种能够在函数声明过程中,将环境信息与所属函数绑定在一起的数据结构。它是基于函数声明的文本位置的,因此也被称为围绕函数定义的静态作用域或词法作用域。

闭包不仅应用于函数式编程的高阶函数中,也可用于事件处理和回调中。

作用域链和闭包

作用域链

作用域链是JavaScript引擎查找变量的一个过程。在每个执行上下文的变量环境中,都包含了一个外部引用(outer),用来指向外部的执行上下文。当访问一个变量时,JavaScript引擎会:

  1. 首先在当前作用域中查找

  2. 如果没找到,就会沿着作用域链向上查找外部作用域

  3. 一直查找到全局作用域为止

词法作用域

词法作用域(Lexical Scope)是JavaScript采用的作用域模型,它有以下特点:

  1. 静态作用域:作用域是由代码中函数声明的位置来决定的,而不是调用的位置

  2. 编译时确定:在代码编译阶段就已经确定,与函数的调用方式无关

  3. 嵌套规则:内部函数可以访问外部函数中声明的变量,但外部函数不能访问内部函数的变量

闭包

闭包是基于词法作用域的一个特性,它具有以下特点:

  1. 形成条件:当内部函数引用了外部函数的变量时

  2. 数据保持:即使外部函数执行完毕,被引用的变量仍然会保存在内存中

  3. 访问规则:内部函数可以访问外部函数的变量和参数

闭包使用注意事项:

  • 全局变量形式的闭包会一直存在直到页面关闭

  • 不再使用的闭包应及时解除引用,避免内存泄漏

  • 根据使用频率和内存占用考虑是否使用全局变量形式

讲讲你对 promise 的理解

1.对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:

pending: 初始状态,不是成功或失败状态

fulfilled: 意味着操作成功完成

rejected: 意味着操作失败

2.只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

Promise 对象的状态改变,只有两种可能:

1.从 Pending 变为 Resolved

2.从 Pending 变为 Rejected。

只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,再对 Promise 对象添加回调函数,也会立即得到这个结果

Promise 有哪些用法

Promise.all(): Promise.all 可以将多个 Promise 实例包装成一个新的 Promise 实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值。

Promise.all 获得的成功结果的数组里面的数据顺序和 Promise.all 接收到的数组顺序是一致的,即 p1 的结果在前,即便 p1 的结果获取的比 p2 要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用 Promise.all 毫无疑问可以解决这个问题。

Promise.race: Promse.race 就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

Promise.any: 当传入的 promise 数组中有任意一个完成时就会终止,会忽略到所有被拒绝掉的 promise,直到第一个 promise 完成。若传入所有的 promise 被拒绝则会执行拒绝回调。

Promise.allSettled: 当给定的 promise 数组中的所有 promise 被拒绝后会返回一个拒绝的 promise 数组,与[]一一对应。

Promise.reject 里面写 try catch

  1. try catch 不能捕获异步代码,所以不能捕获 promise.reject()的错误,并且 promise 期约故意将异步行为封装起来,从而隔离外部的同步代码

  2. try catch 能对 promise 的 reject()落定状态的结果进行捕获

  3. try catch 能捕捉到的异常,必须是主线程执行已经进入 try catch, 但 try catch 尚未执行完的时候抛出来的, 意思是如果将执行 try catch 分为前中后.只有中才能捕获到异常

  4. 应该只在确切知道接下来该做什么的时候捕获错误(这里我单指 try catch)

try catch 不能捕获到 promise.resolve()的报错

js 错误捕获

讲讲你做了哪些主动发现问题的事情

旧版前端三剑客与现代框架 vue

视图和数据耦合起来,开始效率和定位问题的速度,其次就是他们对数据的处理,如果是数据的处理,我们可能会是,我们可能是直接 document.getElmentById 或者 class,我们只要在 vnode 层面的话 直接修改数据,框架层面会自动帮我们在视图上面修改,像 vue 的话相当于在虚拟 dom 层面 因为他这个虚拟 dom 是一个 js 对象,我们只要改这个对象的节点数据即可,他会比对完新旧 vnode 之后 去修改相对应的真实 dom。采用现代化的话 工程化层面会更好,如果做了工程化,页面的复用。

vue 和 jquery 的性能

你做了哪些工程化

(1) 使用 webpack 实现项目构建

设计架构这块

php 方面除了要提供挂载的 dom 给我们,还有一个权限之间的校验,前端做的层面的话会跟后端同学进行商量

性能方面的优化

在这里面有遇到什么困难吗

小程序登录流程

1.wx.login() 获取登录凭证 code,并回传到开发者服务器 2.调用 auth.code2Session 接口,获取用户唯一标识 OpenID、用户在微信开发平台账号下的唯一标识 uNionID 和回话密钥 session_key

临时登录凭证 code 只能使用一次

之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

对于小程序来说,也需要一个唯一的标识符来区分用户,也就是 session 来保持会话,但是小程序没有 cookie, 因此我们的唯一标识符会被存储在 localstorage 里面,每次发请求时,都会从 localStorage 里面拿到这个唯一标识符,带在请求中。

微信的 openid 和 code

openid 用来标识这个唯一的微信用户,也就是说,一个微信用户相对于一个公众号(主体)的 openid 是唯一的,是不会变的。

就是通过 code, 对于同一个用户,每次获取到的 code 都会改变,有有效期。我们把 code 作为参数,调用指定的微信服务器的接口,就可以拿到用户的 openid 。

如何拿到 code

微信内 h5 页面的方法是:跳到指定的微信的承接页面,再跳回到本页面,url 链接上就会被拼上 code 。 小程序的方法是: 通过调用 wx.login() 方法,就可以拿到用户的 code

微信小程序登录体系

  1. 通过 wx.login()获取到用户的 code

  2. 通过 wx.request() 方法请求我们自己的后端,我们自己的服务端把 appid , appsecret 和 code 一起发送到微信服务器。 appid 和 appsecret 都是微信提供的,可以在管理员后台找到

  3. 微信服务器返回了 openid

  4. 我们在自己的数据库中,查找 openid ,如果没有查到记录,说明该用户没有注册,如果有记录,则继续往下走 5.我们生成一个第三方 session , 也就是 session_id , 也就是用户唯一标识符。在 redis 中,把 session_id 和用户的身份存进去。 6.返回 3rd_session

  5. 小程序把 3rd_session 存到 storage 里面 8.下次请求时,先从 storage 里面读取,然后带给服务端 9.服务端从 redis 里面找到 3rd_session 对应的记录,然后校验有效期

讲讲 ssr 的流程

第一步: 服务端拼接 html     当用户请求某个页面的时候, server 端会拼接好一个页面的 html 结构返回给客户端, 例如下面的结构:

第二步: 客户端加载好的 html 展示出来了     上面的代码可以看出, html 结构加载完就可以展示出来了, 但是比如点击事假, 这类交互事件还是没有的, 需要加载/_ssr/2046328.js 后页面才能有交互(活起来), 所以我们还是要请求一堆 js 文件到本地。

第三步: js 执行 hydration 阶段完毕才可交互     hydration 字面意思类似'注水', 也就是通过 js 代码的执行, 动态的为当前页面上的 dom 绑定事件, 你可把当前获取到的 html 代码当做一根干货海参, js 代码理解成水, 而 hydration 过程就是用水把海参泡发, 达到可以食用的状态, 也就是页面可正常交互的状态。

看完上述 ssr 的流程后你有什么感觉? 有没有感觉 ssr 可能是个"视觉骗子", 我们简单罗列几个可优化的点:

虽然首屏展示的速度快了, 但是不可交互, 所以他的 tti(页面可交互时间)并没有太大的优化, 但不可否认也是有提升的只是不太多。 下载的 js 仍然是比较全量的 js 代码。 js 代码执行的时候, 仍然需要处理大量的逻辑, 还要重新处理一遍页面上的 dom。

组件基于 Vnode 来实现渲染 VNode 本身是 js 对象,兼容性极强,不依赖当前的执行的环境,从而可以在服务端渲染及原生渲染。虚拟 DOM 频繁修改,最后比较出真实 DOM 需要更改的地方,可以达到局部渲染的目的,减少性能损耗。

vue-server-renderer 是一个具有独立渲染应用程序能力的包,是 Vue 服务端渲染的核心代码。

浏览器渲染机制

浏览器的渲染进程

如何变成合成层(GPU)

  • 3D 或透视变换(perspective transform) CSS 属性

  • 使用加速视频解码的 元素 拥有 3D

  • (WebGL) 上下文或加速的 2D 上下文的 元素

  • 混合插件(如 Flash)

  • 对自己的 opacity 做 CSS 动画或使用一个动画变换的元素

  • 拥有加速 CSS 过滤器的元素

  • 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)

  • 元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)

合成层的优点

一旦 renderLayer 提升为了合成层就会有自己的绘图上下文,并且会开启硬件加速,有利于性能提升

  • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快

  • 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层

  • 对于 transform 和 opacity 效果,不会触发 layout 和 paint

注意:

提升到合成层后合成层的位图会交 GPU 处理,但请注意,仅仅只是合成的处理(把绘图上下文的位图输出进行组合)需要用到 GPU,生成合成层的位图处理(绘图上下文的工作)是需要 CPU。 当需要 repaint 的时候可以只 repaint 本身,不影响其他层,但是 paint 之前还有 style, layout,那就意味着即使合成层只是 repaint 了自己,但 style 和 layout 本身就很占用时间。 仅仅是 transform 和 opacity 不会引发 layout 和 paint,那么其他的属性不确定。 总结合成层的优势:一般一个元素开启硬件加速后会变成合成层,可以独立于普通文档流中,改动后可以避免整个页面重绘,提升性能。

合成层的缺点

利用合成层可能踩到的坑 合成层占用内存的问题 层爆炸,由于某些原因可能导致产生大量不在预期内的合成层,虽然有浏览器的层压缩机制,但是也有很多无法进行压缩的情况,这就可能出现层爆炸的现象

获取随机数

最后更新于