01 数据类型
基本数据类型:
- Null
typeof null 等于 object
- Undefined
- Let var 声明未初始化值为 undefined
- Boolean
- falsely===
null undefined 0 '' NaN
- falsely===
- Number
- 10 进制
1
- 8 进制 0 开头
070
- 16 进制 0x 开头
0x1
- 浮点数 包含小数点
1.1
1.0
.1
3.14e10
科学计数法 - NaN 不是数值
Object.is(NaN,NaN) 返回true
- 10 进制
- String
- Symbol
(栈)值类型:String 、Number、 Boolean、 Symbol
(堆)引用类型:object{}、 Array、 null、function
02 数组方法
splice 增(return [])
删return删除数组
改return[]
不影响原数组
splice(initIndex, deleteNum, InsertElement) 返回数组
slice (from, to) 截取数组,不会影响原数组,返回新数组
-
增
- push() 尾部增加 返货数组新长度
- unshift 首部增加 返回数组新长度
- concat(“yellow”, [“red”, “green”]) 不会改变原数组
-
删
-
pop() 返回删除元素
-
shift 返回删除元素
-
-
查
- indexOf() 返回元素所在索引,没有返回-1
- includes() 返回 true/false
- find 返回第一个查找元素, 没有返回 undefined (finnIndex、findLast、findLastIndex)
排序方法:
- sort(?compareFn) —toSorted
- reverse --toReverse
转换
- join(?separator) 转为由分隔符分隔的字符串
迭代
(都有 thisArg 形参,更改回调函数体内 this 指向,但是直接使用箭头函数 this 仍指向上一层 this)
- some() 某一个符合条件 true,every 全部符合条件 true
- forEach、filter、map
03 字符串方法
- 增 (返回副本)
+
${}
- concat()
- 删(返回副本)
- slice(startIndex, endIndex)(与 substring 差不多,但 substring 不支持负数索引,start > end 交换索引)
- substr(startIndex, length)
- 改
- trim、trimLeft/Right 删空格
- repeat(count) 重复几次这个字符串
- padStart/End(maxLength, ?fillString) 不符合 maxLength 就长度填充, 默认填充空格
- 查
- charAt(index) 根据索引返回元素,没有返回空串
- indexOf
- startWith include 查找返回 true/false
转换方法
- split() 字符串转数组
模板匹配
- match(
whichpattern
) - search
- replace(originString, replaceString) (reg, function(match, …))
04 AJAX
Ajax 允许我们利用 js 和 xmlHttpRequest 在后台与服务器交换数据
允许我们不用刷新网页,与后端交换数据,局部更新部分网页
通过 XmlHttpRequest 向服务器发送异步请求,从服务器获得数据,利用 js 操作 dom 更新页面
1 | //1. 创建xhr对象实例 |
05 数据传输
multipart/form-data 可以传输二进制文件
application/x-www-form-urlencoded 用于传输普通的表单数据,
application/json 用于传输 JSON 对象,
二者都不会传输文件,主要传输字符串、数字类型的数据。
1 | function upload(url, Form) { |
06 URL To Browser
url
协议 + 域名 + 资源名(目录名/文件名)
DNS 解析
找缓存-
- 浏览器
- 操作系统
- 本地 host
没有缓存
向域名服务器发送请求(dig +trace baidu.com)
- 根域名 .
- 顶级域名 .com
- 权威域名 baidu.com
发送网络请求
建立 TCP 链接 三次握手
发送 HTTP 请求
options 请求,在其他请求之前发送
post 请求格式为 application/json 格式才会触发 options
触发 options
- 跨域,options 请求预先检测
- 自定义请求头
浏览器缓存
强缓存 浏览器强制缓存服务端的数据(静态资源)
res.setHeader('Cache-Control', 'max-age=10')
或者设置res.setheader(expires GMT时间))
form disk cahce 磁盘缓存服务端的资源
from memory cache 内存缓存服务端资源 ,多次刷新,就读内存里的
协商缓存
Last-Modified GMT 时间 最后更改时间
if-Modified-Since
如果前后时间(或 xxx 字段)是一致的那么就代表资源没有改变,后端返回 304,
变了代表资源改变,后端返回新的资源,返回 200
ETAG:‘xxx’ (文件 hash,or 版本号) 唯一标识符,用于标识特定版本的资源
if-None-Match
TCP 断链 4 次挥手
HTML 渲染
html 解析器 解析标签为 DOM 树
样式计算
渲染引擎格式化计算 css 样式(rem->px bold->700…) CSSOM 构建
回流
计算得到几何信息、大小
触发回流
- 首次渲染
- resize
- 跟大小宽高有关的改变
- hover
- js 获取 clientWidth
重绘
元素样式的改变并不影响它在文档流中的位置(color backgoroung-color,visibility),浏览器将新样式给他并重新绘制
GPU 渲染
v8 解析 js
渲染树??
07 TCP HTTP
OSI 七层
应用层 (应用进程之间的交互规则) 万维网 HTTP 邮件 SMTP 域名系统 DNS
表示层 (通信的应用程序能够解释交换数据的含义)
会话层 (建立、管理和终止表示层实体之间的通信会话)
传输层 (为进程之间的通信提供服务,处理数据包错误、数据包次序,以及其他)TCP UDP
网络层 (选择合适的网间路由和交换节点,确保数据按时成功传送)
数字链路层 (两台主机之间的数据传输,总是在一段一段的链路上传送的,将 IP 数据组合成帧,相邻节点传输帧)
物理层 (定义传输媒介)
All People need data processing
08 VUE
v-if 切换有一个局部编译/卸载的过程,切换过程中合适的销毁和重建内部事件监听和子组件,v-show 只是简单的进行 css 的切换
vif 切换消耗
Vshow 初始渲染消耗
09 定义列表
自定义
1 | <dl> |
有序
1 | <ol> |
无序
1 | <ul> |
选择框—分组
filedset legend
10 拖拽
11 ==
和===
==
1 | // 双等号隐式类型转换 ALL true |
开发尽量使用===
,
但是这种情况可以,两者等价 obj==null
与obj===null || obj===undefined
12 闭包
指函数和其周围的状态(即词法环境)的组合,作用域的特殊运用
内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后
自由变量
在闭包函数中使用但不是在该函数作用域内声明的变量
自由变量的值:在函数定义的地方向上层寻找,与函数调用的地方无关
以下是一些常见的触发闭包的情况:
- 函数嵌套:内部函数可以访问外部函数的变量,导致形成了闭包。
- 函数作为返回值:如果一个函数返回了一个内部函数,那么该内部函数可以访问到外部函数的变量,从而形成闭包。
- 事件监听:如果在事件监听函数内部定义了函数,那么该内部函数可以访问到事件监听函数的变量,形成闭包。
- 延迟调用:如果在一个函数内部调用了另外一个函数,并且该函数有一个定时器或者是回调函数,那么该回调函数可以访问到外部函数的变量,形成闭包。
- IIFE(立即调用的函数表达式):如果在一个函数内部定义了一个函数并立即调用,那么该内部函数可以访问到外部函数的变量,形成闭包。
13 防抖节流
事件被频繁触发时,减少事件执行的次数
防抖 Debounce
在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。(input 输入,resize )
1 | <input type="text" id="ipt" /> |
节流 Throttle
规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。(拖拽、scoll)
1 | <div |
14 浏览器相关
l 进程:一个程序的运行实例
运行一个程序,操作系统会创建一块内存, 给代码和运行时的数据使用 ,并且创建一个线程来处理任务
线程是 一个进程的执行任务或者控制单元,负责当前进程中程序的执行
一个进程至少有一个线程,多个线程可以进行数据的共享
特点:
- 一个线程崩溃,整个进程崩溃
- 同一进程中,线程可以数据共享
- 进程关闭后,内存会正确回收
普通单线程浏览器
- 不稳定,
- 容易卡,
- 内存泄漏(所有页面在一个进程中,单个页面因为代码编写问题数据泄露。关闭页面时,泄露的内存不会回收)
- 安全问题(插件和渲染线程拥有很高权限,各种脚本代码(通常第三方编写))
Chrome 多线程
每个页面单独渲染进程和插件进程,装进沙箱,不能获取系统权限
权限由浏览器主线程操作
主进程:页面的展示,页面的交互,管理子进程,提供存储功能
网络进程:下载网络资源
GPU 进程:绘制网页和 UI 界面
渲染进程:js v8 引擎,排版引擎 Blink (h5 css js 转换为网页)
网络协议
互联网协议 Internet protocol IP
网络层协议
计算机系统通过 ip 协议在网络中互相传输数据
IP 不好记 ----> 域名
DNS 域名系统 Domain name system
找到具体服务器的系统服务器的 IP 地址,如何指定访问具体程序
UDP 用户数据包协议 user datagram protocol 端口号访问指定的程序 传输层协议
UDP 协议发送数据时,不能保证接收端一定收到
TCP 数据传输(控制)协议 transmission control protocol 传输层协议
将数据拆分成数据包的形式传输
数据包的丢失,提供重传的机制
面向连接,在传输数据之前,他会和目标设备进行连接,在传输完成后和目标断开连接 (创建和断开的过程,三次握手,四次挥手)
特性 | TCP | UDP |
---|---|---|
连接方式 | 有连接 | 无连接 |
可靠性保证 | 提供可靠性保证 | 不提供可靠性保证 |
传输数据顺序保证 | 传输数据顺序得到保证 | 不保证传输数据顺序 |
传输数据开销较大 | 是 | 否 |
适用场景 | 适用于数据传输要求可靠的应用程序,如文件传输、电子邮件等 | 适用于数据传输速度要求快的应用程序,如在线游戏、实时视频和音频通信等 |
TCP 🤝👋🏻
三次握手(建立连接)
1. 客户端请求连接的请求 SYN,随机起始序列号 Sequence Number 给 服务端
2. 服务端接受到请求,发送确认信息 ACK、SYN(伴随随机起始序列号)、客户端发送来的序列号+1(aka确认序列号 Acknowledgement Number)
3. 客户端收到服务器的 SYN 和 ACK,向服务器发送一个 ACK 确认信息(服务器随机序列号+1)
通过握手过程,TCP 协议可以保证连接的可靠性和正确性,同时还可以进行流量控制和拥塞控制,以确保网络的稳定性和可靠性。
四次挥手 (关闭连接)
- 🅰️ 发送一个 FIN 消息,表示没有数据传输,但 🆎 双方仍可接受数据
- 🅱️ 接受到 FIN,向对方发送 ACK,表示已经接受到该消息
- 🅱️ 也发送一个 FIN 消息,表示数据全部传输完毕,但 🆎 双方 🈲 不可发送数据
- 🅰️ 收到 FIN,返回一个 ACK 消息,表示已经接受到该消息
通过挥手过程,TCP 协议可以保证数据的完整性和可靠性,在数据传输结束后及时关闭连接,释放资源,以便其他应用程序可以使用它们。
规定传输格式
浏览器 http 协议
http 请求流程
url 给我网页数据 查找缓存(存在且没过期,暂停请求 网页更快加载,减轻服务器压力
没有缓存,http 做应用层协议,tcp ip 发送到网络中(之前通过域名获取 IP 地址,找到缓存,下次直接使用对应 ip,访问域名没有加端口,默认加:80(http 默认端口)
为 TCP 建立连接做 3 次握手,完成 发送 http 请求 ||服务端接受返回数据
输入 URL 到页面展示
-
浏览器判断输入的内容,不符合 URL 规则,内置搜索引擎搜索;符合规则,加上协议
-
请求阶段:浏览器主进程 =URL(进程之间通信) => 网络进程=(转圈,接受服务器返回的 http 相应头 html),让浏览器开启一个渲染进程,发送一个提交文档命令。
网络进程=(HTTP 文档)=> 渲染进程
渲染完毕 ,接受完毕指令,当前页面白屏
渲染进程联合 GPU 进程渲染,并返回
网络进程 ?缓存资源(返回给浏览器主进程,中断请求)
没有,进入网络流程
DNS 解析 ip 地址
同一站点(根域名,协议相同 )公用一条渲染进程
15 面向对象
类的封装、继承、多态
-
封装:低内聚高耦合
-
多态:重载和重写
重载:方法名相同,形参个数或者类型不一样
JS 中不存在真正意义上的重载,JS 重载指的是使用同一个方法,根据传参不同,实现不同的效果
JS 中的面向对象是基于原型和原型链的
- 继承:子类继承父类的方法
JS 继承:
- 原型继承
- 优点:父类方法可以被复用
- 缺点:1. 父类的引用类型数据会被子类共享篡改 2. 子类实例不能给给父类构造函数传参
- call 继承 借用构造函数
- 优点:父类的引用类型数据不会被子类共享篡改
- 缺点:不能访问父类原型属性上的方法
- **组合继承(**原型继承和 call 继承的结合)
- 优点:
- 父类方法复用
- 父类引用数据不会被子类共享篡改
- 子类可以访问父类原型上的方法
- 缺点:
- 会调用两次父类的数据,会有两份一样的属性和方法,影响性能
- 优点:
- 寄生组合继承
- ES6 extends、super
16 判断数据类型
typeof
Array Object null 都是返回 object 类型 (set、map 也是 object,symbol 返回 symbol)
Object.prototype.toString.call()
都可以区分 [] {} null
17 手写代码
instanceof
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
判断继承关系,在同一条原型链上,返回 true
1 | function myInstanceof(obj1, obj2) { |
bind
1 | Function.prototype.myBind = function () { |
深拷贝
1 | function deepClone(obj) { |
需要注意的是,在一些复杂的对象中,比如包含函数、正则表达式、日期等类型的对象时,使用JSON.parse(JSON.stringify(obj))
可能会导致数据丢失或类型错误,因为这些类型的对象不能被序列化为 JSON 字符串。这时候需要采用其他的深度复制方法,比如手动递归复制、使用第三方库等。
- 递归
- (没有考虑循环引用) 如果对象 A 引用了对象 B,而对象 B 又引用了对象 A,那么在进行深拷贝的时候会陷入死循环。(哈希表)
1 | function deepClone(obj) { |
lodash.cloneDeep()
扁平化数组
借助 Array.prototype.concat.apply([], arr)
18 预编译
JS 预编译
js 属于解释性语言,在执行过程中顺序执行,但是会分块先预编译再执行。因此 JS 存在一种变量提升的现象。
但是是因为有预编译才有 所谓的变量提升
函数先于变量提升
变量提升
var 声明变量提升、function 声明函数提升
无论这两者声明或调用的位置是前是后,系统总是会将其提升到调用前面,因此只值为 undefined
JS 代码运行的三大步骤
- 词法分析
- 预编译
- 解释执行
暗示全局变量 imply global
任何变量,未经声明就赋值,此变量为全局所有
1 | <script> |
、、、、、在 ES5 下、、、、、
Global Object 和 Activition Object
所谓的全局作用域,局部作用域
- 创建 GO 对象
- 寻找变量声明 var,值设定为 undefined
- 寻找函数声明,将函数名作为 GO 属性名,值为函数体
- 创建 AO 对象
- 寻找函数的形参和变量声明var,将变量和形参名作为 AO key,value 为 undefined
- 统一形参和实参,即更改形参后的 undefined 为实参值
- 寻找函数声明,将函数作为 AO key,value 为函数体
匿名函数、函数表达式不参与预编译
ES6 新增的块级作用域
1 | function test(a, b) { |
预编译
AO:{
a:
undefined1 b:
undefinedfn c:undefined
d: fn
}
执行
AO:{
a:
undefined13 b:
undefinedfn2 c:
undefined0 d: fn
}
1 | function test(a, b) { |
预编译
AO:{
a:
undefined1fn b:undefined
}
执行
AO:{
a:
undefined1fn2 b:
undefined13fn}
1 | a = 100 |
预编译
AO:{
e:
undefined1fn(e) b: undefined
c: undefined
a:undefined
}
GO:{
a: undefined
test: fn
}
执行
AO:{
e:
undefined1fn(e)2 b: undefined
c: undefined
a:
undefined2}
GO:{
a:
undefined100 test: fn
}
19 for in of
For in 遍历得到 key
- 可枚举的数据 (
Object.getOwnPropertyDescriptors()
)
数组、字符串、对象
for...in
循环可以遍历对象的可枚举属性,包括自有属性和继承属性。但是需要注意的是,它不能保证属性的遍历顺序,而且会遍历对象的原型链上的属性,所以需要使用 hasOwnProperty()
方法来判断属性是否为自有属性。
for of 遍历得到 value
- 可迭代的数据 (
arr[Symbol.iterator]()
)
数组、字符串、set、map
For await of
遍历一组 Promise
1 | <script> |
20 JS 类型转换
js 是动态类型语言,我们在定义一个变量其实并没有指定这个变量到底属于那种类型,只有到程序执行阶段才确定当前数据类型。
而各种运算符对数据类型是有要求的,所以就会触发类型转换机制(no matter 人为 or 隐式触发)
显示转换:
通过 JS 内置的函数明确转换的数据类型
-
Number
-
parseInt(string, ?进制) 比 Number 宽松,一位一位解析遇到不能解析的停止
-
String
-
Boolean
隐式转换:
运算操作符两边数据类型不一致(比较运算符、算术运算符)
-
转为 Boolean (需要布尔值的地方,借助的
Boolean()
函数)- falsely 变量:
undefined
、null
、false
、+/-0
、NAN
、""
- falsely 变量:
-
转为 String (复合类型—>原始类型---->字符串)
- 常发生在
"5" + xxx
加法运算符号
- 常发生在
-
转为 Number
- 除了加法运算符号,其他都有可能
other
===
在不进行类型转换情况下,双方的类型与值都相等
21 JS 事件机制
事件
就是 html 文档与浏览器的一些交互操作,使网页具备互动性
由于DOM是一个树结构,如果在父子节点绑定事件时候,当触发子节点的时候,就存在一个顺序问题,这就涉及到了**事件流**的概念
捕获 目标 冒泡
1 | addEventLisener("click", () => {}, false) //默认false,不在捕获阶段执行 |
click 事件 冒泡
22 JS 异步机制
JS 异步编程
单线程
DOM 操作,必须要使用单线程模型,否则出现多线程同步问题(一个线程增,一个线程删,麻烦)
假如说只用同步来解决,即排队执行代码,那么则简单、安全。但是遇到一段非常耗时的代码段,导致整个程序被拖延,导致假死(阻塞)情况。所以 JS 带来同步模式异步模式来解决此问题。
- 同步模式与异步模式
- 事件循环与消息队列
- 异步编程几种方式
- Promise 异步方案、宏任务/微任务队列 (ES2015)
- Generator 异步方案(ES2015),Async/Await 语法糖(ES2017)
同步模式与异步模式
同步模式
排队一步一步执行
开始执行 JS 引擎将全部代码加载进来,call stack
压入一个(anonymous)
(全部代码放入匿名函数中执行,call stack
是调用栈,函数声明不会压入栈中)
异步模式
不会等待这个任务结束,才开始下一个任务。开启过后,立即开始下一个任务。后续逻辑一般会通过回调函数的方式定义
代码执行顺序混乱??
倒计时器根本不会管 call stack 和 Queue,放在 WebAPI 中
call stack 正在执行的工作表
Queue 待办工作表
Event Loop 监听 call stack 和 Queue,
一旦 call stack 任务结束,事件循环会从 Queue 中取出第一个回调函数压入到 call stack
JS 单线程,是指执行代码是单线程的,但浏览器并不是单线程。
而我们所说的同步异步问题是指运行环境提供的 API 是以同步或异步模式的方式工作
回调函数
所有异步编程方案的根基
理解为:一件你想要做的事--->但不知道这件事所依赖的事情什么时候结束--->交给异步任务执行者,他知道
调用者定义 交给执行者执行
事件机制、发布订阅基于回调
Promise
一种更优秀的异步编程统一方案(CommonJS 社区提出,被纳入 ES2015)
明确成功或失败的结果(不可被修改)后,都会有相应的任务自动执行。
链式调用(不是通过函数 return this 达到的链式调用)
.then( )方法
返回的是全新的 Promise 对象 (返回
每一个 then 方法实际上都是为上一个 then 返回的 Promise 对象添加状态明确的回调
-
Promise 的 then 方法返回一个全新的 Promise 对象(所以可以采用.then 链式调用)
- .then 后面会返回一个新的 promise 实例,又可以继续调用 then 或者 catch 方法
-
后面的 then 方法就是在为上一个 then 方法返回的 Promise 对象注册回调(添加状态明确的回调)
-
前面 then 方法 return 的返回值会作为下一个 then 方法回调的参数
- 如果 return 的是 Promise 对象,那么后面的 then 方法会等待到他的结束
异常处理
两个回调(onFulfilled,onRejected)
(只能在当前 then 方法调用中接住)
.catch (相当于两个回调 其中(undefined,onRejected)
,异常可以被传递,而被 catch 接住
unHandledRejection(全局)
静态方法
Promise.reslove()
将一个值快速转换为 Promise 对象
用此方法包装一个 Promise 对象得到的还是原本的 Promise 对象
1 | let p = new Promise((resolve, reject) => { |
如果用此方法,包装如下带有 then 方法的对象 (thenable 可以被 then 的对象)
1 | Promise.resolve({ |
Promise.reject()
无论传什么参数,都会作为 Promise 失败的理由
Priomse 并行执行
在页面请求多个接口的情况,这些请求没有过多依赖,那么就可同时请求
怎么知道这些请求完成?传统用计数器
Promise.all()
多个 Promise 集中管理 (同步执行多个 Promise 的方式)等待所有 Promise 结束后
1 | Promise.all([xxx, yyy]) // xxx,yyy为Promise对象 |
Promise.race()
只会等待第一个完成的任务,就结束
Promise 执行时序 宏任务 vs 微任务
Promise 执行时序有点特殊
回调队列 Queue 中的任务 『宏任务』,宏任务执行过程期间可能会加上额外的需求,
这些需求可以选择作为新的宏任务进入到队列 Queue 中排队
也可以作为当前任务的『微任务』,直接在当前任务结束之后立即执行,而不是进入队列排队
Promise 回调作为微任务 执行的 (本轮结束的末尾,自动执行,不进入队列)
如果处于 pending 状态就不能进入这一次微任务
await 后面的语句加入到微任务队列
微任务:提高整体的响应能力
目前绝大多数异步调用都是作为宏任务执行
微任务:Promise、mutationObserver 以及 Node 中的 process.nextTick
Generator 异步方案
Promise 处理异步任务的串联执行,仍然有大量的回调函数,虽然没有嵌套,影响阅读
传统异步模式,更简洁,更利于阅读
Generator ES2015
Generator 函数返回的遍历器对象都有一个 throw 方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。外面 try catch 捕获
生成器函数执行器
1 | function* main() { |
异步调用扁平化
Async Await
ES2017
不在需要类似 co 的执行器,语言层面的标准一步语法
async 函数返回的是一个 Promise 对象,利于控制整体
1 | console.log("start") //1 |
async/await 执行顺序
我们知道async
隐式返回 Promise 作为结果的函数,那么可以简单理解为,await 后面的函数执行完毕时,await 会产生一个微任务(Promise.then 是微任务)。
但是我们要注意这个微任务产生的时机,它是执行完 await 之后,直接跳出 async 函数,执行其他代码(此处就是协程的运作,A 暂停执行,控制权交给 B)。
其他代码执行完毕后,再回到 async 函数去执行剩下的代码,然后把 await 后面的代码注册到微任务队列当中。我们来看个例子:
1 | console.log("script start") |