JavaScript高级
day01
1) 从输入url到渲染页面的整个过程
- dns解析, 把域名地址解析ip地址
- TCP链接: TCP三次握手 ===> 建立连接
- 客户端发送服务端: 我准备好了, 请你准备一下
- 服务端发送客户端: 我也准备好了, 请你确认一下
- 客户端发送服务端: 确认完毕
- 发送请求(请求头/请求行/请求体)
- 将请求报文发送过去
- 返回响应(响应头/响应行/响应体)
- 将响应报文发送过来
- 解析渲染页面 (由浏览器内核进行解析)
- 遇到HTML, 调用HTML解析器(HTML Parser), 解析成DOM树
- 遇到CSS, 调用CSS解析器, 解析成CSSOM树(样式规则)
- 遇见js, 调用JS解析器(js引擎), 解析js代码
- 可能要修改元素节点, 重新调用HTML解析器, 解析更新DOM树
- 可能要修改样式节点, 重新调用CSS解析器, 解析更新CSSOM树
- 将DOM + CSSOM = render Tree(渲染树) 合并 形成渲染树
- layout布局: 计算元素的位置和大小信息
- render渲染: 将颜色/文字/图片等渲染上去
- 断开链接: TCP四次挥手
- 客户端发送服务端: 请求数据发送完毕, 可以断开了
- 服务端发送客户端: 请求数据接收完毕, 可以断开了
- 服务端发送客户端: 响应数据发送完毕, 可以断开了
- 客户端发送服务端: 响应数据接收完毕, 可以断开了
2) 浏览器的工作原理
- 输入服务器的地址之后返回 html文件
- 遇见link标签就下载css文件
- 遇见script标签就下载 javaScript文件
3) javaScript引擎 (V8引擎)
我们所编写的代码, 由 js引擎
翻译成机器代码, 然后由 CPU
来执行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0IYFkhl3-1650093612801)(G:\js高级+vue3\js高级\images\javaScript引擎.png)]
4) V8 引擎的原理
javaScript源代码 ==> 解析(parse) ==> 成AST抽象语法树 ==> Ignition转换成 ==> bytecode字节码 ==> 转换成机器码 运行
day02
1) 执行上下文
var message = "Hello Global"
function foo() {
console.log(message) //Hello Global
}
function bar() {
var message = "Hello Bar"
foo()
}
bar()
foo() 执行 跟它的定义位置有关系, 跟它调用的位置没有关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tAKuDkxT-1650093612804)(G:\js高级+vue3\js高级\images\函数执行上下文栈.png)]
2) 基本数据类型和引用数据类型的区别(内存管理)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9RDkZtq5-1650093612806)(G:\js高级+vue3\js高级\images\基本数据类型和引用数据类型的区别.png)]
3) 垃圾回收
1. 引用计数法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jVQzRl4G-1650093612808)(G:\js高级+vue3\js高级\images\引用计数法.png)]
2. 标记清除法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rOsYrIXD-1650093612812)(G:\js高级+vue3\js高级\images\标记清除法.png)]
day03 闭包内存泄漏和this的四个绑定规则
1)闭包
产生闭包的条件: 1.函数嵌套 2.内部函数引用外部函数的局部变量 3.调用函数
闭包的作用:延长局部变量的生命周期 让函数外部操作函数内部的局部变量
闭包的应用举例一下:
在elementUI中删除列表中某个商品的例子(带确定框)
2) call和apply的区别
function sum(num1, num2, num3) {
console.log(num1 + num2 + num3, this)
}
sum.call("call", 20, 30, 40)
sum.apply("apply", [20, 30, 40])
都会调用函数 apply是传递的数组
3) this 优先级
1.默认规则的优先级最低
- 毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this
2.显示绑定优先级高于隐式绑定
3.new绑定优先级高于隐式绑定
4.new绑定优先级高于bind
- new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
- new绑定可以和bind一起使用,new绑定优先级更高
new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)
4) 伪数组转换成真数组
Array.from(arguments)
[...arguments]
day04
1) 面向对象怎么理解
把所用东西放到一个对象里面, 更好的描述一个事物,现实世界描述,面向对象编程
2) 工厂模式(缺点 看不到具体类型 都是Object类型)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w1Rt6jKT-1650093612814)(./images/工厂模式.png)]
3) padStart进行填充(ES8)
某些字符串我们需要对其进行前后的填充,来实现某种格式化效果
我们简单具一个应用场景:比如需要对身份证、银行卡的前面位数进行隐藏:
const cardNumber = "321324234242342342341312"
const lastFourCard = cardNumber.slice(-4)
const finalCard = lastFourCard.padStart(cardNumber.length, "*")
console.log(finalCard) // **************1312
4) 可选链(info.friend?.girlFriend?.name)
const info = {
name: "why",
friend: {
girlFriend: {
name: "hmm"
}
}
}
console.log(info.friend.girlFriend.name)
if (info && info.friend && info.friend.girlFriend) {
console.log(info.friend.girlFriend.name)
}
// ES11提供了可选链(Optional Chainling)
console.log(info.friend?.girlFriend?.name)
day05
1) Object.defineProperty()的原理
// 新增属性、删除属性Object.defineProperty是无能为力的
let info = {
name: "张三",
age: 18
}
Object.keys(info).forEach(item => {
let value = info[item]
Object.defineProperty(info, item, {
get() {
console.log(`我读取了${item}的value`);
return value
},
set(newValue) {
console.log(`我修改了${item}的value`);
value = newValue
}
})
})
2) 浏览器的事件循环
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GWnD5d0d-1650093612816)(./images/浏览器事件循环.png)]
js是单线程运行的
js通过事件循环机制来实现对js的单线程异步
异步要基于回调来实现
事件循环机制的2个重要部分
- 在浏览器其他线程管理模块: 定时器/ajax/dom事件
- 保存待执行的回调函数的事件队列(Event queue)/任务队列(Task queue)
比如定时器是异步的,放到浏览器其他线程, 等2秒钟放到事件队列里面, 首先js引擎把前面同步的任务执行完成, 在执行定时器
宏任务和微任务
- 先执行 main Script 宏任务
- 在依次取出微队列中所有微任务
- 在取出宏队列中第一个宏任务执行
- 在依次取出微队列中的所有微任务执行
- 3, 4 步进行重复执行
day06
Underscore 库
- 我们可以理解成lodash是underscore的升级版,它更重量级,功能也更多;
- 但是目前我看到underscore还在维护,lodash已经很久没有更新了;
1). 防抖(如果频繁触发, 只执行最后一次) 只有在某个时间内,没有再次触发某个函数时,才真正的调用这个函数
就是根据定时器来操作, 触发了本次清除上一次的定时器
function debounce(fn, delay) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null
// 2.真正执行的函数
const _debounce = function(...args) {
// 取消上一次的定时器
if (timer) clearTimeout(timer)
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
fn.apply(this, args)
}, delay)
}
return _debounce
}
2). 节流(有规律的进行触发,比如间隔0.5s来进行操作)
定义两个个变量记录上一次时间,另外一个记录当前操作的时间, 根据这触发的时间, 减去上次的触发的时间; 只有大于等于了才触发下一次
function throttle(fn, interval, options) {
// 1.记录上一次的开始时间
let lastTime = 0
// 2.事件触发时, 真正执行的函数
const _throttle = function() {
// 2.1.获取当前事件触发时的时间
const nowTime = new Date().getTime()
// 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数
const remainTime = interval - (nowTime - lastTime)
if (remainTime <= 0) {
// 2.3.真正触发函数
fn()
// 2.4.保留上次触发的时间
lastTime = nowTime
}
}
return _throttle
}
3). rem进行深入理解
function setRem() {
// 设计稿宽度为1440;屏幕宽度为1440时 1rem == 100px
const size = document.documentElement.clientWidth * 100 / 1440;
document.documentElement.style.fontSize = size + "px";
}
// 初始化
setRem();
// 改变窗口大小时重新设置 rem
window.onresize = setRem;
day07 ES Module
1) 暴露和导入模式
-
分别暴露
foo.js export const name = "张三" export const age = 18 export function foo() { console.log("foo") } 导入 import {name, age, foo} from "./foo.js"
-
统一暴露
foo.js let name = "zhangsan" let age = 18 export { name, age } 导入 import {name, age} from "./foo.js" import * as foo from "./foo.js"
-
默认暴露
export default { name: "张三", age: 18 } 导入 export { default as user } from "./systemConfig/user";
day08
定时器的缺陷
1.定时器最大的优点,同时也是它最容易出错的一点:定时器所有任务都是由同一个线程来调度。
正是因此,定时器简单易用。同时这也导致了定时器所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务,即定时器的重叠。
2.定时器不会被自动销毁,即它所占内存无法被自动回收。如果不手动清除定时器,它会一直占用内存资源。更可怕的是,一旦使用定时器进行轮询,定时器所占的内存资源将会不断上升,若与定时器重叠问题一起出现,常导致页面卡顿。
所以,我们必须学会清除定时器。
{ default as user } from “./systemConfig/user”;
### day08
#### 定时器的缺陷
1.定时器最大的优点,同时也是它最容易出错的一点:定时器所有任务都是由同一个线程来调度。
正是因此,定时器简单易用。同时这也导致了定时器所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务,即定时器的重叠。
2.定时器不会被自动销毁,即它所占内存无法被自动回收。如果不手动清除定时器,它会一直占用内存资源。更可怕的是,一旦使用定时器进行轮询,定时器所占的内存资源将会不断上升,若与定时器重叠问题一起出现,常导致页面卡顿。
所以,我们必须学会清除定时器。