前端核心知识进阶
构造函数和 this
构造函数的时候发生了什么
箭头函数中的 this
this 优先级
我们把 call, apply, bind, new 对 this 进行绑定的情况称为显示绑定,而把根据调用关系确定 this 指向的情况称为隐式绑定。
第二个 console 最终调用的还是 o1.fn, 因此运行结果仍是 o1, 最后一个 console 中 o3.fn()通过 var fn = o1.fn()的赋值进行了『裸奔调用』提前
显式和隐式绑定优先级
call, apply 的显式绑定一般来说优先级最高
new 绑定修改了 bind 绑定中的 this 指向, 所以 new 的优先级比 bind 绑定的更高
当在使用 bar 作为构造函数时,
要在 bound 函数中进行 this instanceof 判断
闭包
函数作用域
全局作用域
函数作用域链:如果在自身的函数作用域内并未找到变量, 就会往更上层作用域,例如函数或者全局作用域
执行上下文和调用栈
代码预编译阶段
在预编译阶段进行变量声明
在预编译阶段堆变量进行声明提升, 但是值为 undefined
在预编译阶段对所有非表达式的函数声明进行提升
执行上下文: 变量对象, 作用域链, this
代码执行阶段
在函数执行完毕并出栈时, 函数内的局部变量在下一个垃圾回收 gc 节点会被回收, 该函数对应的执行上下文将会被销毁, 这也是我们在外界无法访问函数内定义的变量的原因。
闭包: 函数嵌套函数, 内层函数引用了外层函数作用域下的变量, 并且内层函数在全局环境下可访问, 进而形成闭包。
变量作用域的查找是一个扩散的过程
起始于函数开头, 终止于相关变量声明语句的所在行。
作用域在预编译阶段确定,但是作用域链是在执行上下文的创建阶段完全生成的 执行上下文包括变量对象、作用域链及 this 的指向
jquery offset 方法实现
koa 的 only 模块
异步编程
表面看 1ms 和 0ms 的延迟是完全等价的。有点类似『最小延迟时间』这个概念。 直观上看, 最小延迟时间是 1ms, 在 1ms 以内的定时器都以最小延迟时间处理。
async 函数内部同步执行。 await 之间相当于.then() async 函数外部的调用异步执行 需要 try、catch 应对 promise reject 的情况。
// 控制最大并发数为 3, 最多一起发出 3 个请求,剩下 2 个一起发出
继承
不同的 Child 实例的 proto 会引用同一 Parent 的实例
在这样的实现中, 不同的 child 实例的proto会引用同一 Parent 实例
Child 实例会存在 Parent 的实例属性, 这是因为我们在 Child 构造函数中执行了 Parent 构造函数。同时, Child.proto 中也会存在同样的 Parent 的实例属性,且所有 Child 实例的proto都指向同一块内存地址。
ES next
[NaN].include(NaN) // true [NaN].indexOf(NaN) // -1
SameValueZero()这个方法使引擎内置的比较方式。 并没有对外接口, 其实采用了 Map 和 Set.
Obejct spread 和 Object.assign Obejct.assign 将会修改它的第一个参数对象, 这个修改可以触发其第一个参数对象的 setter。 Obejct spread 操作符会创建一个对象副本, 而不会修改任何值, 在对不可变性有要求的情况下是更好的选择,
箭头函数不适用的场景
构造函数的原型方法需要通过 this 获得实例, 因此箭头函数不可以出现在构造函数的原型方法中
箭头函数不具有 arguments 属性, 因此在其函数体内无法访问这一特殊的伪数组
babel
在编译阶段将会处理 let|const 变量重新命名, 同时在 JavaScript 严格模式下不允许使用未声明的变量。
babel 只要检测到 const 声明的变量被改变赋值, 就会主动插入一个_readOnlyError 函数, 并执行函数。 这个函数的执行内容就是报错, 因此代码执行时就会直接跑出异常。
CommonJs 具有以下几个特点
文件即模块
模块可以被多次引用、加载。 在第一次被加载时,会缓存。之后都从缓存中直接读取结果。
modlule.export 属性输出的是值的拷贝, 一单这个值被输出, 模块内再发生变化也不会影响到输出的值
模块按照代码引入的顺序进行加载
注意 module.exports 和 exports 的区别
AMD
规范定义了如何定义模块,如何对外输出,如何引入依赖。 require.js 的实现很简单: 通过 defined 方法, 将代码定义为模块;通过 require 方法, 实现代码的模块加载。
define 和 require 就是 require.js 再全局注入的函数。
我们再加载完 require 脚本后,并没有发现额外多出来的 script 标签, 因为 checkLoaded 方法会把已经加载完毕的脚本删除, 因为我们需要的是模块内容。
规范标准时代:CMD
CMD 整合了 CommonJs 和 AMD 规范的特点, 它全称 Common Module Definition,与 require.js 类似, CMD 规范为 sea.js
AMD 需要通过异步加载模块, 而 CMD 在加载模块时, 可以通过同步的形式(requiure), 也可以通过异步的形式(require.async)
CMD 遵循依赖就近原则, 则 AMD 遵循依赖前置原则。 也就是说,在 AMD 中,我们需要把模块需要的依赖都提前声明在依赖数组中; 而在 CMD 中,我们只需要在具体代码逻辑内,使用依赖前,引入依赖的模块即可。
规范标准时代:UMD
利用立即执行函数根据环境来判断需要的参数类别
差异: CommonJs 模块输出的时一个值的拷贝 改变不会跟着改变
ES 模块输出的是一个值的应用 改变会跟着改变
webpack
webpack_require.r(_webpack_exports) 这个方法是用来给模块的 exports 对象加上 ES 模块化规范的标记的。
如果当前环境支持 symbol 对象, 则可以通过 Object.defineProperty 为 exports 对象的 Symbol.toStringTag 属性赋值 Module, 这样做的结果时 exports 对象在调用 toString 方法时会返回 Module,同时将 exports.__esModule 赋值给 true
按需加载
多了一个 webpack_requiure.e
多了一个 webpackJsonp
webpack_requiure.e 其中实现非常重要, 他初始化一个 promise 数组, 使用 promise.all 异步插入 script 脚本; webpackJsonp 会挂载到全局对象 window 上, 进行模块安装
Rollup
依赖解析, 打包构建
仅支持 ES Next 模块
内置支持 tree shaking 功能
monorepo 和 multirepo
multirepo 将应用按照模块分别在不同的仓库中进行管理
monorepo 将应用中所有的模块一股脑全部放在同一个项目中
pnpm 的原理
硬链接与符号链接
pnpm 通过硬链接(hard link)和符号链接(symbolic link)来优化依赖存储:
硬链接:所有依赖包都存储在全局的 ~/.pnpm-store 目录中,项目中的 node_modules 通过硬链接指向这些包,避免重复下载和存储。
符号链接:项目中的 node_modules 通过符号链接指向 .pnpm 目录中的依赖,确保每个包只存储一份,减少磁盘占用。
扁平化与嵌套结构
pnpm 采用半扁平化结构
扁平化:直接依赖放在 node_modules 顶层,便于访问。
嵌套结构:间接依赖放在 .pnpm 目录中,保持依赖关系的完整性,避免依赖冲突。
依赖隔离
每个包及其依赖都存储在 .pnpm 目录中,确保依赖之间互不干扰,避免版本冲突。
全局存储
所有下载的包都存储在全局的 ~/.pnpm-store 中,多个项目共享同一份包,减少磁盘占用和下载时间。
pnpm 和 npm 区别
npm3 以下的版本在安装依赖时非常直接,它会按照包依赖的树形结构将其下载到本地 node_modules 目录中, 也就是说,每个包都会将该包的依赖放到当前包所在的 node_modules 目录中
npm3 采用扁平结构,在安装依赖包时更加智能,会按照 package.json 中声明的顺序依次安装包, 遇到新的包就把它放在第一级 node_modules 目录中。后面在安装进行时,如果遇到一级 node_modules 目录已经存在的包, 就会先判断包版本,如果版本一样则跳过安装,否则会按照 npm2 的方式安装在树形目录结构下。
ESLint 为代表的 linter。 code linting 表示基于静态分析代码原理找出代码反模式的过程
前端性能监控 前端监控
常见的性能监控指标:
FMP 的智能获取算法
获取有意义的渲染元素
体积占比比较大
屏幕内可见占比大
属于资源加载元素(img、svg、video、object、embed、canvas)
由具备以上特征的多种元素共同组成的元素
对整个页面进行深度优先遍历搜索,之后对每一个元素进行分数计算, 具体通过 elemenet.getBoundingClientReact 获取元素的位置和大小, 然后通过计算所有元素的"widthheigt weight* 元素在 viewport 中的面积占比" 的乘积,确定元素的最终得分。
跨域脚本错误处理 crossorigin = "anonymous"
window.onerror 事件是通过事件冒泡获取 error 信息的,而网络加载错误是不会进行事件冒泡的。 不支持冒泡的事件还有鼠标聚焦、失焦(focus/blus)、与鼠标移动相关的事件(mouseleave,mouseenter)、一些 UI 事件(如 scroll,resize)
window.addEventListener 不同于 window.onerror, 它是事件捕获获取 error 信息,从而对网络资源的加载异常进行处理的。
页面崩溃收集和处理
service worker 和网页的主线程是相互独立,因此即使网页发生了崩溃现象,也不会影响到, service worker 所在的线程的工作。
性能数据和错误信息上报
何时上报数据
页面加载和重新刷新
页面切换路由
页面所在的 tab 标签重新变得可见
页面关闭
Google Analytics 使用 navigator.sendBeacon 来上报数据,通过动态创建 img 标签, 以及在 img.src 中拼接 URL 的方式发送请求,不存在跨域限制。如果 URL 太长,就会采用 sendBeacon 的方式发送请求,如果浏览器不支持,则发送 AJAX POST 同步请求。
如果网页访问量很大,我们可以设置一个采集率
useMemo 返回缓存的变量 useCallback 返回缓存的函数
React Fiber 每次检查虚拟 DOM 的一小部分, 在检查期间会检查是否还有时间继续执行下一个虚拟 DOM 树上的某个分支任务,同时观察时候还有更优先的任务需要响应,如果没有时间执行下一个虚拟 DOM 树上的某个分支任务,且有更高优先级的任务,React 就会退让出主线程, 直到主线程不忙的时候继续执行那个分支任务。
将堆栈协调过程分为块,一次执行一块,执行完一块之后需要将结果保存起来,根据是否还有空闲的响应时间(requestIdleCallback)来决定下一步策略
Promise
每个 Promise 实例的 then 方法逻辑都是一致的, 实例在调用该方法时, 可以通过原型(promise.prototype)来调用,而不需要每次实例化都新创建一个 then 方法, 以便节省内存,显然更合适。
一个 Promise 实例 then 方法的 onfulfilled 函数和 onrejected 函数是支持再次返回一个 Promise 实例, 也支持返回一个非 Promsie 实例的普通值, 并且,返回这个 promise 实例或这个非 promise 实例的普通值将会传给下一个 then 方法的 onfulfilled 函数或者 onrejected 函数。
Async/Await 就是一个自执行的 generate 函数。利用 generate 函数的特性把异步的代码写成“同步”的形式。
前端响应式布局:
em 相当于当前元素或当前元素继承来的字体的宽度。 这里有个要注意的点: 对于 font-size 来说,em 是相对于父元素的字体大小; 在 line-height 中,em 却是相对于自身的字体大小。
rem 相对于根节点(html)的字体大小
vw 是相对于视口宽度,100vw 就相当于一个视口宽度
vh 与 vw 同理, 1vh 表示一个视口高度的 1/100, 100vh 就是一个视口高度
css 变量和主题变换
如何让 chrome 支持小于 12px 的文字
谷歌浏览器最小字体为 12px, 小于 12px 的字体 它都以 12px 显示。
设置-webkit-text-size-adjust:none (最新 chrome 已不支持)
设置-webkit-transform:scale(0.8)
面试: 100% 相对于谁
position: absolute 中的% 对于设置绝对定位的元素, 我们使用 left、top 表示其偏移量, 我们把这个元素的祖先元素中第一个存在定位属性的元素当做参照物, 其中的%是相对于参照物的.left 相对于参照物的 width, top 相对于这个参照物的 height
position: relative 中的% 对于设置相对定位的元素, %的数值是相对于自身的,left 相对于自身的 width; top 相对于自身的 height;
position:fixed 中的%; 对于设置固定定位的元素, %的数值是相对于视口的, left 相对于视口的 width, top 相对于视口的 height;
margin 和 padding 中的%; 它是相对于父元素的宽高的,没错,margin-top:30%相当于视口的 height
transform:translate 中的%; 是相对于自身的宽高的
面试: 自适应 iphoneX 齐刘海
微信环境 WeixinJSBridgeReady
面试: 1px 问题
深入浅出模块化
前端优化方案
页面工程优化: 涉及页面请求开始网络协议、资源配置、浏览器性能、缓存等
代码细节优化: js 对 dom 的操作过程、宿主环境以及单线程的相关内容
webP 图片优化:
按需加载
React 的优化
减少 prop 的改变, 更细粒程度的控制组件的渲染行为
recompose 内部的原理 在于在高阶组件中调用 shouldComponentUpdate 方法, 并在该方法中将比较对象由完整的 props 转为传入的指定的 props 即可
规避 inline function 反模式
当使用 render 方法时, 要留意 render 方法内创建的函数或数组等, 它们可能是显示生成 , 也可能是隐式生成的。
对性能比较友好的, 只创建一次自定义函数, 而不是每次渲染时都创建一次。
pureComponent 会自动帮助开发者使用 shouldComponentUpdate 生命周期方法, 也就是说当组件 state 和 props 发生变化时, 正常的 component 都会自动进行重新渲染, 在这种情况下, shouldComponentUpdate 会默认返回 true。 但是 pureComponent 会先进行比较, 即比较前后的 state 和 props 是否相等,
开发者要避免共享(mutation)带来的问题 如果一个父组件对 object 进行了 mutation 操作,且子组件依赖此数据,并采用了 pureComponent 声明, 那么子组件将无法进行更新。 尽管 props 中的某一项值发生了变化, 但是由于它的引用并没有发生变化, 因此 pureComponent 的 shouldComponentUpdate 会返回 false。 更好的做法是在更新 props 或者 state、时, 返回一个新的对象或数组。
从 Vue3.0 动静结合的 Dom diff 谈起
预编译阶段 能够进行优化处理,在预编译时标记出模板可能变化的组件节点, 再次进行 diff 操作时就可以跳过: 』永远不会变化的节点『
React 在将 jsx 编译成 React。createElement 的整个过程 优化 优化 jsx => React.createElment => React
静态元素提升
在运行时删除 propTypes
在运行时代码取出内联函数
不可忽视的前端安全
https
不要使用 URL query 传递敏感数据 URL query 会通过服务器短日志、浏览器日志、浏览器历史记录查到
content-security-policy 响应头, 它可以设置应用是否可以引用某些来源的内容, 进而防止 xss
JWT 和 Authentication cookie
为了防范 xss, 攻击者可以主动诸如恶意脚本或者使用户输入,通过 JavaScript 代码来偷取 token, 然后通过 token 冒充受害用户, 一般的防御手段有 HTML 转义
cookie
cookie 可以由 js 创建
也可以由服务端通过设置响应头创建
session cookie , 这种 cookie 会随着用用户关闭浏览器而被清除,不会被标记任何过期时间 expires 或最大时限 Max-age
permanent cookie, 与 session cookie,相反, 会在用户关闭浏览器之后被浏览器持久化存储
HttpOnly cookie, 浏览器端, JavaScript 没有读 cookie 的权限
secure cookie, 只有在特定安全通道(https)下, 传输链路的请求中才会自动加入相关 cookie
samesite cookie, 在跨域情况下, 相关 cookie 无法被请求携带, 这里主要为了防止 csrf 攻击
Authentication cookie 隐患
xss 如果没有使用 httpOnly 选项, 那么攻击者可能会通过注入恶意脚本任意读取用户 cookie, 而 cookie 直接存储了用户的身份认证信息
前端的发展历程
2009 年 Node.js 发布以及 ES5 定稿发布
2013React 2014Vue、webpack
2016 年的 next.js 发布可以看做前端向全栈化、BFF 前端框架基于服务端渲染做全战或者是静态生成站点
技术趋势:
基于原生语言和原生 ESM 的新一代工具链。
SSR 全栈场景下,从框架设计层面出发的前后端一体的性能优化。
以编译期优化为前提进行的框架设计。
AI 和 WASM 在前端可以成为齐头并进的技术,一个算法,一个实现。对于前端来说,性能问题一直是核心问题,而交互与表现必将大型与复杂化,例如虚拟现实交互,以及模拟反馈等等,而 WASM 是应对这些问题的有效手段。
最后更新于
这有帮助吗?