✨
blog
  • Blog
  • Element-UI
    • 2019-09-04
  • JS
    • ES6 之 Set 和 Map
    • let 和 const 声明常见概念
    • 元编程
    • ES6之字符串的扩展
    • ES6 之异步流程的前世今生(上)
    • ES6之异步流程的前世今生(下)
    • ES6 之模块你知吗
    • ES6 之解构赋值与箭头函数的妙用
    • 迭代器
    • ES5 之原型(一)
    • ES6之类(二)
    • es7之装饰器
    • es6之数组详解
    • js之this指向
    • 对象
    • vue项目配合使用canvas联动
    • 本文解决痛点:对象里面是否有值
  • MAC
    • vue源码之method
    • Mac的使用技巧
    • 前文
    • Mac常用软件(二)
    • 如何查看 Mac 端口号以及占用情况
  • Node
    • Node之Buffer详解
    • 浏览器与 node 的事件循环(event loop)有何区别
    • Node之多线程
    • node之模块解析(一)
    • 错误捕获与内存告警
  • TS
    • Record
    • 使用方法
    • 工具泛型
    • 类型体操
    • 泛型
  • chrome
    • v8 引擎
    • v8 垃圾回收机制
    • 浏览器的知识
  • flutter
    • 路由
    • 页面布局
  • go
    • index
  • html&css
    • 两栏布局
    • ES5和ES6的区别
    • ES5 和 ES6 的区别
    • HTTP详解
    • TCP 与 UDP 的区别
    • MDN
    • css modules 使用教程
    • css 居中
    • 拖拽
    • flex布局
    • h5 新增特性 html5
    • history 与 hash 路由策略
    • position 定位方式
    • rem布局
    • svg
    • web性能优化
    • 事件循环
    • 从输入网址后发生了什么
    • 前端状态管理
    • 圣杯布局与双飞翼
    • 性能优化 页面的性能统计指标
    • 本地存储的几种对比
    • 浏览器的渲染进程
    • 浏览器缓存策略详解
    • 盒模型
    • 为什么要移动端适配
    • 跨域的 N 种实现方式
  • web3
    • 常见概念
    • vue项目配合使用canvas联动
  • webgl
    • Mac使用技巧(二)
    • Node之模块解析path
  • 代码库
    • documeng的一些常见操作
    • eventBus事件
    • jquery提交
    • jquery的一些常见操作
    • 常见操作
    • 数组polyfill
    • TS代码片段
      • 面试官眼中的test unit
  • 全年安排
    • AfterShip
    • 大企业
  • 函数编程题
    • Promise问题
    • 继承
  • 前端早早聊
    • vue生态
    • 开发一款VScode语言插件
    • 简历回顾和进行复盘
    • 重新认知性能优化及其度量方法
    • 2022-09-17-音视频专场.md
      • 2022-09-17-音视频专场
    • 前端晋升专场
      • 成长的诀窍是靠自己
      • 销销帮
    • 前端监控专场
      • 字节前端监控实践
      • 李港:大前端-从无到有搭建自研前端监控系统
    • 前端跳槽
      • 50个面试官在线招聘
      • 如何识别优秀的前端猎头来跳槽大厂
      • 面试套路
    • 支付宝
      • 面试
    • 管理专场
      • 芋头:管理者眼中的web技术发展前沿
    • 组件专场
      • 基于webCompents的跨技术组件库实践
    • 面试
      • 面试辅导问题
      • 早早聊面试
      • 前端沙箱是什么? 怎么实现沙箱环境?
  • 常见总结
    • 2018年终总结-年底了,你总结了吗?我先来
    • 在逆境中成长
    • 2021年终总结
    • 2024年全年总结
    • 项目
    • Tell2.0 前端复盘
    • 复盘
    • 前端工程师素养
    • 学习方法论
    • 希望与破晓| 2022 年终总结
    • 新起点, 新征途 | 掘金年度征文
    • 稳定| 2023 年终总结
    • 趁着有风快飞翔 | 2019 年终总结
    • AfterShip
      • Emotion:用 JavaScript 编写 CSS 的强大工具
      • 个人中长期目标
      • 事故复盘
      • 时间解析
      • 国内外区别
      • 独立站建设
    • MEIZU
      • NativeApp与H5通信原理
      • SSR 原理
      • SSR的常见问题
      • CLI
      • electron 应用发布流程
      • electron
      • electron 面试
      • 数据结构与算法之美
      • mgc 一期复盘
      • 架构原理
      • 喵币管理
      • 三期复盘总结
      • 异常监控之 sentry 实践
      • 微前端
      • qiankun 原理解析
      • 快游戏一期
      • 游戏中心复盘
    • 个人准则
      • index
    • 编程猫
      • pc 接入 micro bit 方案
      • prompt engineer
      • web work 跨域解析与解决方式
      • web 中的 ai
      • 低版本 node 环境下 ffmpeg 的使用
      • 关于 taobao 源 https 过期
      • 加密 json
      • 安卓 5 和 6 的白屏解决
      • 性能排查与优化实践
      • 探月接入
      • 接入硬件
      • 新生态下的state
      • monorepo 包管理方式
      • 自修复 npm 库
      • 音频的绘制
    • 谨启
      • 音视频
      • 小程序
        • taro 规范
        • 结合 mobx 在跳转前预请求
        • Taro 浅析用法与原理
        • 前文
        • 小程序优化指南
        • 小程序内部实现原理
        • 支付相关
    • tencent
      • TAPD
        • MathJax的食用
        • canvas渲染优化策略
        • 为什么 JavaScript 是单线程的呢?
        • svg 总是不对
        • 前端库
        • 原生端和js端如何通信
        • 在旧项目中复用vue代码
        • 提升自我
        • 批量编辑优化
        • 插入业务对象
        • 编辑器
        • 挂载点
        • 性能优化对比
        • 遇到的问题
        • 项目迁移公告
        • 领导力
      • 行家
        • 实战篇
        • 职业发展、领导力、个人成长
        • 高质量沟通
  • 慕课网
    • react-native原理
    • react-native学习
  • 杂文
    • Dom 节点变动检测并录制的简单实现
    • 错误监控&错误捕获
    • NextJS与NuxtJS
    • 负载均衡的几种常用方式
    • PM2
    • service worker 控制网络请求?
    • SSL 和 TLS 的区别
    • Babel 你太美
    • echart踩坑经验
    • keyup、keydown你都知道有什么区别吗
    • 常见概念
    • 首屏加载优化与性能指标分析
    • preload 和 prefetch 的详解
    • 在项目中配置这几个关系
    • roullp 解析
    • tinymce原理浅析
    • wasm 在前端的应用
    • websocket
    • webworker
    • 项目
    • 从 ajax 到 axios
    • 从postcss 到自己开发一款插件
    • 从输入浏览器到页面展示涉及的缓存机制
    • 代码整洁之道
    • 你知道什么是aop吗
    • 函数式编程
    • 函数式编程指南
    • 前端input框文字最大值
    • 攻坚战
    • 前端书写 sdk
    • 前端文字转语音播放
    • 前端领域的 Docker 和 Kubernetes
    • 前端安全
    • 前端进阶之内存空间
    • 前端音频浅析
    • 十分钟搞定多人协作开发
    • 字符串的比较
    • 尾递归
    • 前文
    • 常见的算法可以分为以下三类
    • 手机调试--mac篇
    • 数组的原生系列
    • COOP 和 COEP - 新的跨域策略
    • 浅谈react组件书写
    • 浏览器与 Node.js 事件循环的区别
    • 由三道题引伸出来的思考
    • 移动端300ms点击延迟
    • 移动端和pc端事件
    • Git 常见疑惑
    • 我们离发 npm 包还有多远
    • 重绘和重排
    • AI 时代下的前端编程范式
    • 音频可视化实战
  • 极客时间
    • Serverless入门课
    • 二分查找
    • 二叉树
    • 全栈工程师
    • 动态规划面试宝典
    • 前端与rust
    • 散列表
    • 前端方面的Docker和Kubernetes
    • 栈
    • 深入浅出区块链
    • 玩转 vue 全家桶
    • 玩转 webpack
    • 程序员的个人财富课
    • 算法
    • 说透元宇宙
    • 跳表
    • 链表
    • 10x 程序员工作法
      • index
    • Node开发实战
      • HTTP服务的性能测试
      • JavaScript语言精髓与编程实战
      • 什么是node。js
      • svg精髓
    • ReactHooks核心原理与实战
      • ReactHooks核心原理与实战
    • Rust
      • Rust编程第一课
      • 前置篇
      • 深度思维
      • 重构
      • 类型体操
      • 基础知识
    • WebAssembly入门课.md
      • 基础篇
      • SSR的注水和脱水
      • jsBriage通信原理
      • 基础知识篇
    • 互联网的英语私教课
      • 互联网人的英语私教课
    • 代码之丑
      • 代码之丑
    • 前端全链路优化实战课
      • 网页指标
    • 图解 Google V8
      • 图解 Google V8
    • 浏览器工作原理与实践
      • 浏览器工作原理与实践
    • 算法面试通关 40 讲
      • 算法面试通关40讲
    • 跟月影学可视化
      • index
    • 软件设计之美
      • 软件设计之美
    • 重学前端
      • js
  • 后续的文件增加都会增加到上面并以编号对应
    • 1029. 两地调度
    • 151.翻转字符串里的单词
    • 2022.3.15
    • 前端数据结构
    • 前端常见算法
    • 前端常见排序
    • 恢复一棵树
  • 设计模式
    • 前端常见设计模式之MVC与MVVM
    • 前端之代理模式
    • 前端常见设计模式之单例模式
    • 前端常见设计模式之发布订阅模式
    • 前端之工厂模式
    • 观察者模式
    • 前端常见设计模式之适配器模式
  • 译文
    • [译] 如何使用CircleCI for GitHub Pages持续部署
    • 您是否优化了 API 的性能
    • [译][官方] Google 正式发布 Flutter 1.2 版本
    • 什么是 Deno ,它将取代 NodeJS ?
  • 读后感
    • JavaScript二十年
    • 1368个单词就够了
    • js编程精解
    • labuladong 的算法小抄
    • lodash常用方法
    • vue的设计与实现
    • 所有的静态资源都是get请求
    • 人生
    • 人生护城河
    • 你不知道的JavaScript
    • 前端核心知识进阶
    • 华为工作法
    • 反脆弱
    • 好好学习
    • 左耳听风
    • 摩托车维修之道
    • 数学之美
    • 深入理解svg
    • 浏览器的ESM到底是啥
    • 经济学原理
    • 编程珠玑
    • 防御式 css 精讲
    • 韭菜的自我修养
  • 雪狼
    • 2022-07-17
    • 基础知识
    • 阶一课程
      • 实战辅导一
      • 实战辅导二
  • 嵌入式
    • 树莓派
      • 排序
  • 源码
    • React
      • 核心知识点
      • errorBoundaries
      • immutable.js 的实现原理
      • React.Suspense
      • react源码分析之Fiber
      • batchedUpdate
      • Component
      • Context
      • react 源码分析之 diff 算法
      • React 中的 key 属性:原理、使用场景与注意事项
      • 使用方式
      • react源码分析之memo
      • react 源码分析之mixin
      • 实战篇
      • react源码分析之react-dom
      • 使用方式
      • scheduleWork
      • useImperativeHandle的使用与原理
      • React 书写小技巧
      • 入口和优化
      • 合成事件和原生事件的区别
      • react 性能优化
      • 构建一个 hooks
      • 浅析 styled-components
      • 生命周期
      • 组合 vs 继承
      • 通信机制
      • 高阶组件
      • 慕课网
        • 应用篇
        • 课程导学
    • ReactHook
      • useCallback
      • useContext
      • useEffect 与 useLayoutEffect
      • useHook
      • useMemo
      • useReducer
      • 原理
      • useState
      • 总结
    • Redux
      • mobx 原理解析
      • redux-saga
      • redux-thunk
      • Mobx 和 Redux 对比
      • 使用方法
      • redux 原理
    • Vite
      • Vite原理
      • Vite配置
      • 热更新原理
      • vite 为什么生产环境用 Rollup
    • Webpack
      • PostCSS
      • Webpack5 核心原理与应用实践-loader
      • Webpack5 核心原理与应用实践-plugin
      • Webpack5 核心原理与应用实践
      • 区分
      • 升级详情
      • treeShaking(树摇Tree Shaking)
      • 编写一个自己的webpack插件plugin
      • 代码分离(code-splitting)
      • webpack 打包优化
      • 基础配置
      • webpack 打包优化
      • webpack 工作原理
      • webpack 按需加载原理
      • webpack 热更新 HMR(Hot Module Replacement)
      • 缓存
      • webpack 自定义 plugin
    • next
      • tailwind
      • 什么是水合
    • sveltejs
      • index
    • tinymce
      • 并发篇
    • 源码手写系列
      • create
      • call
      • bind
      • call
      • es6 单例
      • forEach vs Map
      • instanceOf
      • new
      • reduce
      • 取两个重复数组的交集
      • 函数柯理化
      • 动态规划
      • 基于Generator函数实现async
      • 新建 js 文件
      • 手写一个 slice 方法
      • 手写一个 webpack loader
      • Plugin
      • 手写一个寄生组合式继承
      • 二叉树
      • 链表相关的操作
      • 手动实现发布订阅
      • 数组去重
      • 数组扁平化
      • 数组
      • 构造大顶堆和小顶堆
      • 深浅拷贝 深拷贝
      • 两者对比
    • vue
      • vue2
        • vm.attrs与$listeners
        • vue 和 react 的 diff 算法比较
        • vue 源码分析
        • vue 优化的 diff 策略
        • extends
        • 核心原理篇
        • keep-alive
        • vue 源码分析之 mixins
        • vue 源码分析之 nextTick
        • vue之slot
        • vnode
        • vue 源码分析之 watch
        • 原理
        • vue 源码分析之transition
        • vue 源码分析之异步组件
        • 调用的是 watch
        • 安装
        • react源码分析之portals
        • event 的实现原理(事件的实现原理)
        • 什么是h
        • 分析provide 和 inject
        • vue 源码分析之 use
        • v-model
        • vue源码分析之vuex
        • 响应式原理
        • 初始化的流程
        • 组件更新
        • 编译
        • 父子组件生命周期
        • 原理
        • 多实例
        • Vue 面试
        • 源码研读一
        • 响应式原理
        • 常见问题
        • 数组的劫持
        • vue之自定义指令
        • 运行机制全局概览
      • vue3相比vue2的提升点
        • vue composition api
        • vue3的虚拟dom优化
        • vue3层面的双向数据绑定
        • 预处理优化
  • 重构
    • notification
      • 讲解
  • 面试
    • AfterShip经历
      • JS对URL进行编码和解码
      • ShippingLabelTemplate
      • 接入keycloak详解
      • reCAPTCHA接入
      • yalc与动态解决升级的依赖包
      • RBAC 简介
      • 多语言计划
      • 接入Google登录及其主动弹出快捷登录方式
      • 读书计划
        • 传染
        • 这就是OKR
    • 编程猫经历
      • 2024.1.16
      • 2025.2.20
      • 2025.2.21
      • 2025.2.26
      • 2025.3.28
      • 2025.3.3
      • 2025.3.7
      • 行动轨迹
      • 面试主观题
    • 腾讯经历
      • 2022.02.21
      • 2022.03.30
      • 2022.04.24
      • 2022.04.25
      • 2022.04.27
      • 2022.04.28
      • 2022.04.29
      • 2022.05.05
      • 不同公司的面试关注点不同
      • 2022.05.07
      • 2022.05.09
      • 2022.05.10
      • 2022.05.11
      • 2022.05.12
      • 2022.05.13
      • 2022.05.16
      • 2022.05.17
      • 2022.05.19
      • 2022.05.27
      • 面试
      • 行动轨迹
      • 面试主观题
    • 针对字节
      • 2022.05.14
      • 2022.05.17
      • HR面试准备
      • Promise的相关题目
      • React 进阶实践指南(二)
      • React 面试准备
      • vue 与 react 有什么不同 (react 和 vue 有什么区别)
      • TypeScript 全面进阶指南
      • cookie和session区别
      • express 面试准备 koa 中间件原理
      • next面试准备
      • requestCallBack
      • interface 与 type 异同点
      • 取消 promise
      • 如何设计一个前端项目
      • 进阶篇
      • 早早聊面试准备
      • 自动化部署
      • 挖掘项目的深度
      • 面试
      • 出题指数
    • 魅族经历
      • 2020.09.11
      • 一灯
      • 一灯
      • 一灯
      • 2020.09.20
      • 2020.09.21
      • 网易二面
      • 2020.09.23
      • 头条
      • 360 金融面试题
      • 富途一面
      • 算法
      • 字节
      • 2020.11.04
      • baidu 一面
      • meta 标签的作用
      • 字节
      • 2020.11.22
      • 2020.11.25
      • 微前端接入笔记
      • 面试的基本原则
由 GitBook 提供支持
在本页
  • 构造函数和 this
  • 箭头函数中的 this
  • this 优先级
  • 显式和隐式绑定优先级
  • 闭包
  • 异步编程
  • 继承
  • ES next
  • babel
  • webpack
  • 按需加载
  • Rollup
  • monorepo 和 multirepo
  • pnpm 的原理
  • pnpm 和 npm 区别
  • 前端性能监控 前端监控
  • 性能数据和错误信息上报
  • Promise
  • 深入浅出模块化
  • 前端优化方案
  • 不可忽视的前端安全
  • https
  • cookie
  • 前端的发展历程

这有帮助吗?

  1. 读后感

前端核心知识进阶

上一页你不知道的JavaScript下一页华为工作法

最后更新于2个月前

这有帮助吗?

构造函数和 this

构造函数的时候发生了什么

function myNew() {
  const Constructor = Array.prototype.shift.call(arguments);
  let obj = {};
  obj.__proto__ = Constructor.prototype;

  let res = Constructor.apply(obj, arguments);
  return res instanceof Object ? res : obj;
}

箭头函数中的 this

this 优先级

我们把 call, apply, bind, new 对 this 进行绑定的情况称为显示绑定,而把根据调用关系确定 this 指向的情况称为隐式绑定。

const o3 = {
  text: "o3",
  fn: function () {
    return this.text;
  },
};

const o2 = {
  text: "o2",
  fn: function () {
    return o1.fn();
  },
};

const o3 = {
  text: "o3",
  fn: function () {
    var fn = o1.fn();
    return fn();
  },
};

第二个 console 最终调用的还是 o1.fn, 因此运行结果仍是 o1, 最后一个 console 中 o3.fn()通过 var fn = o1.fn()的赋值进行了『裸奔调用』提前

显式和隐式绑定优先级

call, apply 的显式绑定一般来说优先级最高

new 绑定修改了 bind 绑定中的 this 指向, 所以 new 的优先级比 bind 绑定的更高

function foo(a) {
    this.a = a;
}
const obj1 = {};
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a);

当在使用 bar 作为构造函数时,

var baz = new bar(3);
console.log(baz.a);
Function.prototype.bind = function (argument) {
  const that = this;
  let bindArgs = Array.prototype.slice.call(argument, 1);

  return function bound(argument) {
    let innerArgs = Array.prototype.slice.call(argument);
    that.apply(context, [...bindArgs, ...innerArgs]);
  };
};

要在 bound 函数中进行 this instanceof 判断

Function.prototype.bind(context){

  // 1.  申明下this
  let that = this;

  //2. 绑定默认函数
  let bindArgs = Array.prototype.slice.call(argument, 1);
  function Fn() {};

  //3.
  function fBound(params){
    let args = Array.prototype.slice.call(argument);
    return that.apply(this instanceOf fBound ? this : context, bindArgs.concat(args));
  }

  Fn.prototype = this.prototype;
  fBound.prototype = new Fn();

  return fBound;
}

闭包

函数作用域

全局作用域

函数作用域链:如果在自身的函数作用域内并未找到变量, 就会往更上层作用域,例如函数或者全局作用域

执行上下文和调用栈

  • 代码预编译阶段

  • 在预编译阶段进行变量声明

  • 在预编译阶段堆变量进行声明提升, 但是值为 undefined

  • 在预编译阶段对所有非表达式的函数声明进行提升

执行上下文: 变量对象, 作用域链, this

  • 代码执行阶段

在函数执行完毕并出栈时, 函数内的局部变量在下一个垃圾回收 gc 节点会被回收, 该函数对应的执行上下文将会被销毁, 这也是我们在外界无法访问函数内定义的变量的原因。

闭包: 函数嵌套函数, 内层函数引用了外层函数作用域下的变量, 并且内层函数在全局环境下可访问, 进而形成闭包。

变量作用域的查找是一个扩散的过程

起始于函数开头, 终止于相关变量声明语句的所在行。

作用域在预编译阶段确定,但是作用域链是在执行上下文的创建阶段完全生成的 执行上下文包括变量对象、作用域链及 this 的指向

jquery offset 方法实现

const offset = (ele) => {
  let result = {
    top: 0,
    left: 0,
  };

  // 如果当前浏览器为ie11以下的版本, 则直接返回{top:0, left:0}
  if (!ele.getClientRects().length) {
    return reslut;
  }

  if (window.getComputedStyle(ele)["display"] === "none") {
    return result;
  }
  result = ele.getBoundingClientReact();
  var docElement = ele.ownerDocument.documentElement;

  return {
    top: result.top + window.pageYOffset - docElement.clientTop,
    left: result.left + window.pageXOffset - docElement.clientLeft,
  };
};

koa 的 only 模块

var only = function (obj, keys) {
  obj = obj = {};
  if ("string" == typeof keys) keys = keys.split(/ +/);
  return keys.reduce(function (ret, key) {
    if (null == obj[key]) return ret;
    ret[key] = obj[key];
    return ret;
  }, {});
};

异步编程

setTimeout(() => {
  console.log("here 100");
}, 1);

setTimeout(() => {
  console.log("here 2");
}, 0);

表面看 1ms 和 0ms 的延迟是完全等价的。有点类似『最小延迟时间』这个概念。 直观上看, 最小延迟时间是 1ms, 在 1ms 以内的定时器都以最小延迟时间处理。

async 函数内部同步执行。 await 之间相当于.then() async 函数外部的调用异步执行 需要 try、catch 应对 promise reject 的情况。

// 控制最大并发数为 3, 最多一起发出 3 个请求,剩下 2 个一起发出

const loadByLimit = (urlIds, loadImg, limit) => {
  const urlIdsCopy = [...urlIds];

  if (urlIdsCopy.length < limit) {
    const promiseArr = urlIdsCopy.map((urlId) => loadImg(urlId));
    return Promise.all(promiseArr);
  }

  //
  const promiseArr = urlIdsCopy.splice(0, limit).map((urlId) => loadImg(urlId));

  urlIdsCopy
    .reduce(
      (prevPromise, urlId) =>
        prevPromise
          .then(() => Promise.race(promiseArr))
          .catch((error) => console.log(error))
          .then((resolvedId) => {
            // 将 resolvedId从promiseArr 数组中删除
            // 这里用于删除操作的只是伪代码
            let resolveIdPostion = promiseArr.findIndex(
              (id) => (resolvedId = id)
            );
            promiseArr.splice(resolveIdPostion, 1);
            promiseArr.push(loadImg(urlId));
          }),
      Promise.resolve()
    )
    .then(() => Promise.all(promiseArr));
};

继承

不同的 Child 实例的 proto 会引用同一 Parent 的实例

Child.prototype = new 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 获得实例, 因此箭头函数不可以出现在构造函数的原型方法中

Person.prototype = () => {};

箭头函数不具有 arguments 属性, 因此在其函数体内无法访问这一特殊的伪数组

babel

在编译阶段将会处理 let|const 变量重新命名, 同时在 JavaScript 严格模式下不允许使用未声明的变量。

"use strict";
var foo = 123;
{
  _foo = "abc";
  var _foo;
}

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

利用立即执行函数根据环境来判断需要的参数类别

function (factory){
  module.exports = factory
}

差异: 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 的原理

  1. 硬链接与符号链接

pnpm 通过硬链接(hard link)和符号链接(symbolic link)来优化依赖存储:

  • 硬链接:所有依赖包都存储在全局的 ~/.pnpm-store 目录中,项目中的 node_modules 通过硬链接指向这些包,避免重复下载和存储。

  • 符号链接:项目中的 node_modules 通过符号链接指向 .pnpm 目录中的依赖,确保每个包只存储一份,减少磁盘占用。

  1. 扁平化与嵌套结构

pnpm 采用半扁平化结构

  • 扁平化:直接依赖放在 node_modules 顶层,便于访问。

  • 嵌套结构:间接依赖放在 .pnpm 目录中,保持依赖关系的完整性,避免依赖冲突。

  1. 依赖隔离

  • 每个包及其依赖都存储在 .pnpm 目录中,确保依赖之间互不干扰,避免版本冲突。

  1. 全局存储

  • 所有下载的包都存储在全局的 ~/.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 信息,从而对网络资源的加载异常进行处理的。

window.addEventListener(
  "error",
  (error) => {
    if (!error.message) {
      // 网络资源加载错误
      console.log();
    }
  },
  true
);

页面崩溃收集和处理

window.addEventListener("load", () => {
  sessionStorage.setItem("good_exit", "pending");
});

window.addEventListener("beforeunload", () => {
  sessionStorage.setItem("good_exit", "true");
});

service worker 和网页的主线程是相互独立,因此即使网页发生了崩溃现象,也不会影响到, service worker 所在的线程的工作。

性能数据和错误信息上报

何时上报数据

  • 页面加载和重新刷新

  • 页面切换路由

  • 页面所在的 tab 标签重新变得可见

  • 页面关闭

Google Analytics 使用 navigator.sendBeacon 来上报数据,通过动态创建 img 标签, 以及在 img.src 中拼接 URL 的方式发送请求,不存在跨域限制。如果 URL 太长,就会采用 sendBeacon 的方式发送请求,如果浏览器不支持,则发送 AJAX POST 同步请求。

如果网页访问量很大,我们可以设置一个采集率

const reportData = (url) => {
  // 只采集30%
  if (Math.random() < 0.3) {
  }
};

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 函数。

// Promise rejects
var thenable = {
  then: function (resolve) {
    throw new TypeError("Throwing");
    resolve("Resolving");
  },
};

var p2 = Promise.resolve(thenable);
p2.then(
  function (v) {
    // 不会被调用
  },
  function (e) {
    console.log(e); // TypeError: Throwing
  }
);
// 12:46:48.836 VM359:11 TypeError: Throwing at Object.then (<anonymous>:3:9)
// 12:46:48.844 Promise {<fulfilled>: undefined}

Async/Await 就是一个自执行的 generate 函数。利用 generate 函数的特性把异步的代码写成“同步”的形式。

var fetch = require("node-fetch");

function* gen() {
  // 这里的*可以看成 async
  var url = "https://api.github.com/users/github";
  var result = yield fetch(url); // 这里的yield可以看成 await
  console.log(result.bio);
}
var g = gen();
var result = g.next();

result.value
  .then(function (data) {
    return data.json();
  })
  .then(function (data) {
    g.next(data);
  });

前端响应式布局:

em 相当于当前元素或当前元素继承来的字体的宽度。 这里有个要注意的点: 对于 font-size 来说,em 是相对于父元素的字体大小; 在 line-height 中,em 却是相对于自身的字体大小。

rem 相对于根节点(html)的字体大小

vw 是相对于视口宽度,100vw 就相当于一个视口宽度

vh 与 vw 同理, 1vh 表示一个视口高度的 1/100, 100vh 就是一个视口高度

css 变量和主题变换

:root {
  --bg: white;
  --text-color: #555;
  --link-color: #639a67;
}

如何让 chrome 支持小于 12px 的文字

谷歌浏览器最小字体为 12px, 小于 12px 的字体 它都以 12px 显示。

  • 设置-webkit-text-size-adjust:none (最新 chrome 已不支持)

  • 设置-webkit-transform:scale(0.8)

.chrome_adjust{
    font-size: 10px; //针对可以识别12px以下字体大小的浏览器
    -webkit-transform: scale(0.8);
    -webkit-transform-origin-X: left; //定义文字相对于X轴的位置
    -o-transform: scale(1); //针对能识别-webkit的opera browser设置
    display: inline-block;
}

//至于为什么要设置display:inline-block; 因为 scale 属性只对可以定义宽高的元素有效。

面试: 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 问题

媒体查询,加小数的写法
.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
    .border { border: 0.5px solid #999 }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
    .border { border: 0.333333px solid #999 }
}

深入浅出模块化

前端优化方案

页面工程优化: 涉及页面请求开始网络协议、资源配置、浏览器性能、缓存等

代码细节优化: js 对 dom 的操作过程、宿主环境以及单线程的相关内容

webP 图片优化:

const supportWebp = ()=> new Promise(reslove=>{
  const img = new Image();
  img.onerror = ()=> reslove(false)
  img.onload = ()=> reslove(img.width  === 1)
  img.src = 'data:imgxxxxxx/webp'
}).catch(()=> false)


使用 canvas 的 toDataURL 进行判断

toDataURL方法在MDN解释如下:

HTMLCanvasElement.toDataURL() 方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。
如果画布的高度或宽度是0,那么会返回字符串“data:,”。
如果传入的类型非“image/png”,但是返回的值以“data:image/png”开头,那么该传入的类型是不支持的。
Chrome支持“image/webp”类型。
toDataURL方法将图片转化为包含dataURI的DOMString,通过 base64 编码前面的图片类型值是image/webp进行判断。

比如在谷歌浏览器使用toDataURL方法转成image/webp:

var isSupportWebp = function () {
  try {
    return document.createElement('canvas').toDataURL('image/webp', 0.5).indexOf('data:image/webp') === 0;
  } catch(err) {
    return false;
  }
}

isSupportWebp()

// google
function check_webp_feature(feature, callback) {
    var kTestImages = {
        lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA",
        lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",
        alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
        animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"
    };
    var img = new Image();
    img.onload = function () {
        var result = (img.width > 0) && (img.height > 0);
        callback(feature, result);
    };
    img.onerror = function () {
        callback(feature, false);
    };
    img.src = "data:image/webp;base64," + kTestImages[feature];
}

按需加载

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 创建

document.cookie = `my_cookie_name=my_cookie_value`;

也可以由服务端通过设置响应头创建

Set-cookie: my_cookie_name=my_cookie_value;

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 是应对这些问题的有效手段。

箭头函数和普通函数的区别