🐱 个人主页:不叫猫先生,公众号:前端舵手
🙋♂️ 作者简介:2022年度博客之星前端领域TOP 2,前端领域优质作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀!
💫优质专栏:vue3+vite+typeScript从入门到实践
📢 资料领取:前端进阶资料可以找我免费领取
🔥 摸鱼学习交流:我们的宗旨是在「工作中摸鱼,摸鱼中进步」,期待大佬一起来摸鱼(文末有我wx或者私信)。
文章目录
- CSS
- HTML字符实体
- bfc(black format content)
- 1.外边距重叠
- 2.清除浮动
- 3.阻止元素被浮动元素覆盖
- 4.包含塌陷
- CSS盒模型
- 1.标准盒子模型
- 2.怪异盒子模型
- vmin/vmax 概述(B站为什么使用的是vmin)
- CSS变量
- 1.:root
- 2.var()
- JS
- 执行上下文
- 执行上下文栈
- 作用域
- 闭包
- 为什么不使用全局变量,而是用局部变量呢?
- V8垃圾回收机制
- 背景
- V8引擎的内存限制
- V8的垃圾回收策略
- 新生代
- 晋升老生代条件
- 老生代
- 引用计数
- Mark-Sweep
- Mark-Compact
- Incremental Marking(增量标记)
- 浏览器缓存
- 1.按缓存位置分类
- (1)memory cache
- ①preloader
- ①preload
- (2)disk cache,又称HTTP cache
- (3)service work
- 1.按失效策略
- (1)强缓存(常说的cache-control)
- ①Expires
- ②Cache-control
- (2)对比缓存,又称协商缓存
- ①Last-modified & If-modified-Since
- ②Etag & If-None-Match
- script标签中的defer和async
- 1.普通脚本下载
- 2.使用defer
- 3.使用async
- HTTP1.0 和 HTTP2.0的区别
- 1.新的传输格式
- 2.多路复用
- 3.头部header压缩
- 4.HTTP2支持服务器推送
- new的时候做了什么?
- 1.创建实例对象,继承构造函数原型上的方法
- 2.实例对象的_proto_ 指向 构造函数的原型对象prototype
- 3.this指向新创建的对象
- 4.返回值对象
- typeof null 为什么是object
- H5新特性
- H5新特性延伸
- 1.localstorage
- (1)API
- (2)原理
- (3)跨域解决方案
- 4.safari浏览器处理方式
- PWA
- 1.manifest.json文件
- 2.sw.js(Service Workers)
- 3.优点
- (1)脱机游戏
- (2)加速相应
- (3)应用市场
- SEO(Search Engine Optimization)搜索引擎优化
- 1.四个部分:信息采集,信息分析,信息查询,用户接口四部分。
- 2.SEO如何优化
- (1)内部优化
- (2)外部优化
- map set weakMap weakSet
- js的数据类型有哪些?
- 强引用和弱引用之间的区别是什么?
- 1.map
- 与Object的区别:
- 2.weakMap
- 3.set
- 4.weakSet
- Arraybuffer应用场景
- 1.响应拦截器
- 2.Ajax
- 3.Canvas
- 4.WebSocket
- 5.Fetch API
- 6.File API
- 深拷贝实现方式
- 1.手写
- 2.structuredClone
- 同步、异步、宏任务、微任务
- 1.同步
- 1.异步
- (1)宏任务
- (2)微任务
- vite 和 webapck 对比
- vite不能使用 requrire 的替换方案
- 1.动态引入图片
- 2.全局替换
- 纯函数和副作用函数
- 1.纯函数
- 2.副作用函数
- 防抖
- 节流
- webRTC
CSS
HTML字符实体
字符实体通俗讲就是网页文件中复杂的符号代码和一些标点的代码。例如小于号 < 大于号 > 双引号 "这些符号要在浏览器中显示,在HTML文档中都必需被转化成字符实体。字符实体有三部分:一个和号 (&),一个实体名称及一个分号(;),或者 # 和一个实体编号,以及一个分号 (😉
在 HTML 中不能使用小于号(<)和大于号(>),这是因为浏览器会误认为它们是标签。通常情况下,我们都是用实体名(前者),而不是数字(后者),原因就在于实体名(前者)名称易于记忆。但是浏览器也许并不支持所有实体名称(但是对实体数字的支持却很好)。
显示结果 | 描述 | 实体名称 | 实体编号 |
---|---|---|---|
空格 | |   | |
< | 小于号 | < | < |
> | 大于号 | > | > |
" | 引号 | " | " |
bfc(black format content)
块级格式化上下文
BFC区域包含创建该上下文元素的所有子元素,但是不包括创建了新的BFC的子元素的内部元素。
BFC是一块独立的渲染区域,可以将BFC看成是元素的一种属性,拥有了这种属性的元素就会使他的子元素与世隔绝,不会影响到外部其他元素
-
body根元素
-
设置浮动,不包括none
-
设置定位,absoulte或者fixed
-
行内块显示模式,inline-block
-
设置overflow,即hidden,auto,scroll
-
表格单元格,table-cell
-
弹性布局,flex
1.外边距重叠
.one,
.two{
width: 100px;
height: 100px;
background-color: cyan;
margin: 100px;
}
<div class="one"></div>
<div class="two"></div>
解决
.one,
.two{
width: 100px;
height: 100px;
background-color: cyan;
margin: 100px;
}
.container{
overflow: hidden;
}
<div class="container">
<div class="one"></div>
</div>
<div class="container">
<div class="two"></div>
</div>
2.清除浮动
.parent{
border: 10px solid red;
}
.child{
width: 100px;
height: 100px;
background-color: blue;
float: left;
}
<div class="parent">
<div class="child" id="one"></div>
</div>
解决
.parent{
border: 10px solid red;
overflow: hidden;
}
3.阻止元素被浮动元素覆盖
.one{
width: 100px;
height: 100px;
background-color: blue;
float: left;
}
.two{
width: 200px;
height: 200px;
background-color: red;
}
<div class="one"></div>
<div class="two"></div>
解决
.two{
width: 200px;
height: 200px;
background-color: red;
overflow: hidden;
}
4.包含塌陷
.father{
width:400px;
height:400px;
background:blue;
}
.son{
width:200px;
height:200px;
margin-top:20px;
background:red;
}
<div class="father">
<div class="son"></div>
</div>
解决
.father{
width:400px;
height:400px;
background:blue;
overflow:hidden;
}
CSS盒模型
当设置为box-sizing:content-box时,将采用标准模式解析计算(默认模式);
当设置为box-sizing:border-box时,将采用怪异模式解析计算;
1.标准盒子模型
with = content.with;
height = content.height;
2.怪异盒子模型
IE盒子模型仅仅只是在ie浏览器 ie8以下版本出现
with = content.with + padding + border;
height = content.height + padding + border;
vmin/vmax 概述(B站为什么使用的是vmin)
B站使用的是vmin,是为了横屏下显示效果更佳
- vw:与视口的宽度有关,1vw 就是视口宽度的 1%
- vh:与视口的高度有关,1vh 就是视口高度的 1%
- vmin:与当下视口的宽度和高度的最小值有关,取值为 vw 和 vh 中较小的那个
- vmax:与当下视口的宽度和高度的最大值有关,取值为 vw 和 vh 中较大的那个
CSS变量
1.:root
:root是一个伪类,表示文档根元素,所有主流浏览器均支持。。在:root中声明相当于全局属性,只要当前页面引用了:root segment所在文件,都可以使用var()来引用。
2.var()
var()函数可以代替元素中任何属性中的值的任何部分。var()函数不能作为属性名、选择器或者其他除了属性值之外的值。(这样做通常会产生无效的语法或者一个没有关联到变量的值。)
:root{
--blue:#007bff;
}
body {
background-color: var(--blue); /* 设置背景颜色为蓝色 */
}
JS
执行上下文
当函数执⾏时,会创建⼀个称为执⾏上下⽂(execution contex)的环境,分为创建、执⾏和销毁2个阶段
- 创建阶段,指函数被调⽤但还未执⾏任何代码时,此时创建了⼀个拥有3个属性的对象
{
scopeChain: {}, // 创建作⽤域链(scope chain)
variableObject: {}, // 初始化变量、函数、形参
this: {} // 指定this
}
- 代码执⾏阶段主要的⼯作是:1、分配变量、函数的引⽤,赋值。2、执⾏代码
- 销毁阶段:函数执行完毕出栈,等待回收被销毁
执行上下文栈
js是单线程,自上而下,代码中只有一个全局执行上下文和无数个函数执行上下文,构成了执行上下文栈。⼀个函数的执⾏上下⽂,在函数执⾏完毕后,会被移出执⾏上下⽂栈。
作用域
当⼀个块或函数嵌套在另⼀个块或函数中时,就发⽣了作⽤域的嵌套。在当前函数中如果js引擎⽆法找 到某个变量,就会往上⼀级嵌套的作⽤域中去寻找,直到找到该变量或抵达全局作⽤域,这样的链式 关系就称为作⽤域链(Scope Chain)
闭包
指有权访问另外⼀个函数作⽤域中的变量的函数.可以理解为(能够读取其他 函数内部变量的函数)
为什么不使用全局变量,而是用局部变量呢?
1.局部作用域小,更安全,方便管理
2.局部变量跟全局变量同名时,会被局部变量覆盖
总结:避免污染全局作用域。局部变量在函数运行以后被删除;而全局变量在页面关闭后被删除;
V8垃圾回收机制
背景
在V8引擎逐行执行JavaScript代码的过程中,当遇到函数的情况时,会为其创建一个函数执行上下文(Context)环境并添加到调用堆栈的栈顶,函数的作用域(handleScope)中包含了该函数中声明的所有变量,当该函数执行完毕后,对应的执行上下文从栈顶弹出,函数的作用域会随之销毁,其包含的所有变量也会统一释放并被自动回收。试想如果在这个作用域被销毁的过程中,其中的变量不被回收,即持久占用内存,那么必然会导致内存暴增,从而引发内存泄漏导致程序的性能直线下降甚至崩溃,因此内存在使用完毕之后理当归还给操作系统以保证内存的重复利用。
V8引擎的内存限制
- JS单线程机制:JS是单线程的,意味着在V8执行垃圾回收时,程序中的其他各种逻辑都要进入暂停等待阶段,直到垃圾回收结束后才会再次重新执行JS逻辑。因此,由于JS的单线程机制,垃圾回收的过程阻碍了主线程逻辑的执行。****
- 垃圾回收机:垃圾回收本身也是一件非常耗时的操作,假设V8的堆内存为1.5G,那么V8做一次小的垃圾回收需要50ms以上,而做一次非增量式回收甚至需要1s以上,可见其耗时之久,而在这1s的时间内,浏览器一直处于等待的状态,同时会失去对用户的响应,如果有动画正在运行,也会造成动画卡顿掉帧的情况,严重影响应用程序的性能。因此如果内存使用过高,那么必然会导致垃圾回收的过程缓慢,也就会导致主线程的等待时间越长,浏览器也就越长时间得不到响应。
V8的垃圾回收策略
基于分代式垃圾回收机制,垃圾回收的过程主要出现在新生代和老生代:
-
新生代(new_space):大多数的对象开始都会被分配在这里,这个区域相对较小但是垃圾回收特别频繁,该区域被分为两半,一半用来分配内存,另一半用于在垃圾回收时将需要保留的对象复制过来。
-
老生代(old_space):新生代中的对象在存活一段时间后就会被转移到老生代内存区,相对于新生代该内存区域的垃圾回收频率较低。老生代又分为老生代指针区和老生代数据区,前者包含大多数可能存在指向其他对象的指针的对象,后者只保存原始数据对象,这些对象没有指向其他对象的指针。
新生代
- 新生代采用Scavenge垃圾回收算法,在算法实现时主要采用Cheney算法。Cheney算法将内存一分为二,叫做semispace,一块处于使用状态,一块处于闲置状态。
- Scavenge算法是一种典型的牺牲空间换取时间的算法,对于老生代内存来说,可能会存储大量对象,如果在老生代中使用这种算法,势必会造成内存资源的浪费,但是在新生代内存中,大部分对象的生命周期较短,在时间效率上表现可观,所以还是比较适合这种算法。
处于激活状态的区域我们称为From空间,未激活(inactive new space)的区域我们称为To空间。这两个空间中,始终只有一个处于使用状态,另一个处于闲置状态。我们的程序中声明的对象首先会被分配到From空间,当进行垃圾回收时,如果From空间中尚有存活对象,则会被复制到To空间进行保存,非存活的对象会被自动回收。当复制完成后,From空间和To空间完成一次角色互换,To空间会变为新的From空间,原来的From空间则变为To空间。
Scavenge由于只复制存活的对象,并且对于生命周期短的场景存活对象只占少部分,所以它在时间效率上有优异的体现。Scavenge的缺点是只能使用堆内存的一半,这是由划分空间和复制机制所决定的。
由于Scavenge是典型的牺牲空间换取时间的算法,所以无法大规模的应用到所有的垃圾回收中。但我们可以看到,Scavenge非常适合应用在新生代中,因为新生代中对象的生命周期较短,恰恰适合这个算法。
晋升老生代条件
当一个对象在经过多次复制之后依旧存活,那么它会被认为是一个生命周期较长的对象,在下一次进行垃圾回收时,该对象会被直接转移到老生代中,采用新的算法进行管理。对象从新生代移动到老生代的过程叫作晋升。
-
对象从From空间复制到To空间时,会检查它的内存地址来判断这个对象是否已经经历过一次Scavenge回收。如果已经经历过了,会将该对象从From空间移动到老生代空间中,如果没有,则复制到To空间。总结来说,如果一个对象是第二次经历从From空间复制到To空间,那么这个对象会被移动到老生代中。
-
当要从From空间复制一个对象到To空间时,**如果To空间已经使用了超过25%,则这个对象直接晋升到老生代中。**设置25%这个阈值的原因是当这次Scavenge回收完成后,这个To空间会变为From空间,接下来的内存分配将在这个空间中进行。如果占比过高,会影响后续的内存分配。
老生代
V8在老生代中主要采用了Mark-Sweep(标记清除) 和 Mark-Compact(标记整理) 相结合的方式进行垃圾回收。
引用计数
最早用的,该算法的原理比较简单,就是看对象是否还有其他引用指向它,如果没有指向该对象的引用,则该对象会被视为垃圾并被垃圾回收器回收。
弊端:循环引用时候,变量均存在指向自身的引用,因此依旧无法被回收,导致内存泄漏。
Mark-Sweep
Mark-Sweep(标记清除)在标记阶段会遍历堆中的所有对象,然后标记活着的对象,在清除阶段中,会将死亡的对象进行清除。Mark-Sweep算法主要是通过判断某个对象是否可以被访问到,从而知道该对象是否应该被回收。
- 垃圾回收器会在内部构建一个根列表,用于从根节点出发去寻找那些可以被访问到的变量。比如在JavaScript中,window全局对象可以看成一个根节点。
- 然后,垃圾回收器从所有根节点出发,遍历其可以访问到的子节点,并将其标记为活动的,根节点不能到达的地方即为非活动的,将会被视为垃圾。
- 最后,垃圾回收器将会释放所有非活动的内存块,并将其归还给操作系统。
在进行一次清除回收以后,内存空间会出现不连续的状态。这种内存碎片会对后续的内存分配造成问题。如果出现需要分配一个大内存的情况,由于剩余的碎片空间不足以完成此次分配,就会提前触发垃圾回收,而这次回收是不必要的。
Mark-Compact
目的:解决内存碎片
Mark-Compact在标记完存活对象以后,会将活着的对象向内存空间的一端移动,移动完成后,直接清理掉边界外的所有内存Mark-Compact在标记完存活对象以后,会将活着的对象向内存空间的一端移动,移动完成后,直接清理掉边界外的所有内存。
在标记阶段同样会阻碍主线程的执行,一般来说,老生代会保存大量存活的对象,如果在标记阶段将整个堆内存遍历一遍,那么势必会造成严重的卡顿。
Incremental Marking(增量标记)
为了减少垃圾回收带来的停顿时间,V8引擎又引入了Incremental Marking(增量标记)的概念,即将原本需要一次性遍历堆内存的操作改为增量标记的方式,先标记堆内存中的一部分对象,然后暂停,将执行权重新交给JS主线程,待主线程任务执行完毕后再从原来暂停标记的地方继续标记,直到标记完整个堆内存。这个理念其实有点像React框架中的Fiber架构,只有在浏览器的空闲时间才会去遍历Fiber Tree执行对应的任务,否则延迟执行,尽可能少地影响主线程的任务,避免应用卡顿,提升应用性能。
得益于增量标记的好处,V8引擎后续继续引入了延迟清理(lazy sweeping)和增量式整理(incremental compaction),让清理和整理的过程也变成增量式的。同时为了充分利用多核CPU的性能,也将引入并行标记和并行清理,进一步地减少垃圾回收对主线程的影响,为应用提升更多的性能。
浏览器缓存
浏览器缓存
1.按缓存位置分类
(1)memory cache
短期存储,关闭tab即失效,保证页面有多个相同资源请求时只请求一次,有两种形式:
①preloader
在解析js/css资源时会请求下一个js/css资源
①preload
显式指定的预加载资源,比如<link rel="preload"/>
(2)disk cache,又称HTTP cache
持久存储,存储硬盘上,允许跨会话、跨站点使用,比内存读取慢,受HTTP请求头信息中字段限制
(3)service work
可以操作缓存,永久存储,除非手动调用 API cache.delete(resource)
或者容量超过限制,可以在Application -> Cache Storage
中找到。如果 Service Worker 没能命中缓存,一般情况会使用 fetch() 方法继续获取资源。
1.按失效策略
disk cache遵守 HTTP 协议头中的字段
(1)强缓存(常说的cache-control)
强制缓存直接减少请求数,是提升最大的缓存策略。可以造成强制缓存的字段是cache-control
、Expirse
①Expires
HTTP/1.0的字段,表示缓存到期时间,是绝对时间,会受到服务端或客户端时间(可修改)的影响导致失效
②Cache-control
HTT/1.1中的字段,表示资源缓存的最大有效时间,是相对时间,优先级大于Expires
。
- max-age:即最大有效时间,在上面的例子中我们可以看到
- must-revalidate:如果超过了 max-age 的时间,浏览器必须向服务器发送请求,验证资源是否还有效。
- no-cache:虽然字面意思是“不要缓存”,但实际上还是要求客户端缓存内容的,只是是否使用这个内容由后续的对比来决定。
- no-store: 真正意义上的“不要缓存”。所有内容都不走缓存,包括强制和对比。
- public:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)
- private:所有的内容只有客户端才可以缓存,代理服务器不能缓存。
(2)对比缓存,又称协商缓存
强制缓存失效(超过规定时间)时,就需要使用对比缓存,由服务器决定缓存内容是否失效。通过减少响应体体积,来缩短网络传输时间,所以和强制缓存相比提升幅度较小
浏览器->请求缓存数据->得到缓存标识->缓存标识与服务器通讯
->缓存未失效->返回304继续使用;
->缓存失效->返回新的数据和缓存规则->浏览器响应数据后->把规则写入到缓存数据库。
①Last-modified & If-modified-Since
- 服务器通过 Last-Modified 字段告知客户端,资源最后一次被修改的时间
- 浏览器将这个值和内容一起记录在缓存数据库中。
- 下一次请求相同资源时时,浏览器从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-Since 字段
- 服务器会将 If-Modified-Since 的值与 Last-Modified 字段进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。
缺陷:
- 资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
- 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。
②Etag & If-None-Match
为了解决上述问题,出现了一组新的字段 Etag 和 If-None-Match,Etag 的优先级高于 Last-Modified。
Etag 存储的是文件的特殊标识(一般都是 hash 生成的),服务器存储着文件的 Etag 字段。之后的流程和 Last-Modified 一致,只是 Last-Modified 字段和它所表示的更新时间改变成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 变成了 If-None-Match。服务器同样进行比较,命中返回 304, 不命中返回新资源。
script标签中的defer和async
1.普通脚本下载
脚本的下载可以并行,执行的时候需要等所有的脚本下载完按顺序执行,执行会阻塞页面渲染。脚本执行完且页面渲染后,才会依次执行DOMContentLoaded, loaded事件。
2.使用defer
脚本并行下载,下载完按顺序执行,下载和执行不影响页面渲染,页面渲染完且脚本全部执行完后才执行DOMContentLoaded。
3.使用async
脚本并行下载,下载完可直接执行,执行会阻止页面渲染。
- 脚本执行完,dom还没渲染完,则脚本执行影响了执行DOMContentLoaded事件触发事件。
- dom渲染完后脚本还没下载完,脚本的下载和执行不影响执行DOMContentLoaded事件触发事件。
HTTP1.0 和 HTTP2.0的区别
HTTP1.0 | HTTP2.0 |
---|---|
文件格式 | 二进制 |
线端阻塞:一个请求连接一次只提交一次效率高,多了就慢 | 多路复用:允许单一的 HTTP/2 连接同时发起多重的请求-响应消息 |
在 HTTP/1 中,HTTP 请求和响应都是由「状态行、请求 / 响应头部、消息主体」三部分组成。一般而言,消息主体都会经过 gzip 压缩,或者本身传输的就是压缩过后的二进制文件(例如图片、音频),但状态行和头部却没有经过任何压缩,直接以纯文本传输。随着 Web 功能越来越复杂,每个页面产生的请求数也越来越多,导致消耗在头部的流量越来越多,尤其是每次都要传输 UserAgent、Cookie 这类不会频繁变动的内容,完全是一种浪费。 | 报头压缩,降低开销 |
1.新的传输格式
- HTTP1.0 文件格式传输
- HTTP2.0 二进制帧格式传输
2.多路复用
-
HTTP1.0:规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个 TCP 连接,服务器完成请求处理后立即断开 TCP 连接,服务器不跟踪每个客户也不记录过去的请求。
-
HTTP1.0:支持持久连接 Persistent Connection, 并且默认使用 persistent connection。 在同一个 TCP 的连接中可以传送多个 HTTP 请求和响应。 多个请求和响应可以重叠,多个请求和响应可以同时进行。更加多的请求头和响应头(比如 HTTP 1.0 没有 host 的字段),但是出现了两个问题,如下:
- 串行的文件传输。当请求 a 文件时,b 文件只能等待,等待 a 连接到服务器、服务器处理文件、服务器返回文件,这三个步骤。我们假设这三步用时都是1秒,那么 a 文件用时为3秒,b 文件传输完成用时为6秒,依此类推。(注:此项计算有一个前提条件,就是浏览器和服务器是单通道传输)
- 连接数过多。我们假设 Apache 设置了最大并发数为300,因为浏览器限制,浏览器发起的最大请求数为6,也就是服务器能承载的最高并发为50,当第51个人访问时,就需要等待前面某个请求处理完成。
-
HTTP2.0:为HTTP 2.0引入二进制数据帧和流的概念,其中帧对数据进行顺序标识,也就是说,我们传递一个字符串的时候,比如说,我要传递一个,“别说了,我爱你”,HTTP 1.1是按照一个顺序传输,那么 HTTP 2.0是并行传输。浏览器收到数据之后,就可以按照序列对数据进行合并,而不会出现合并后数据错乱的情况。同样是因为有了序列,服务器就可以并行的传输数据,这就是流所做的事情。
这也就是解决了问题1,那么怎么解决问题2呢?HTTP 2.0 对同一域名下所有请求都是基于流,也就是说同一域名不管访问多少文件,也只建立一路连接。同样 Apache 的最大连接数为300,因为有了这个新特性,最大的并发就可以提升到300,比原来提升了6倍。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xstVnHuO-1675906930808)(evernotecid://9F9295DA-46A3-4E92-AC15-00E6649D8054/appyinxiangcom/24873861/ENResource/p107)]
3.头部header压缩
HTTP 1.x的 header 带有大量信息,而且每次都要重复发送,HTTP 2.0使用 encoder 来减少需要传输的 header 大小,通讯双方各自 cache 一份 header fields 表,既避免了重复 header 的传输,又减小了需要传输的大小。
在 HTTP/1 中,HTTP 请求和响应都是由「状态行、请求 / 响应头部、消息主体」三部分组成。一般而言,消息主体都会经过 gzip 压缩,或者本身传输的就是压缩过后的二进制文件(例如图片、音频),但状态行和头部却没有经过任何压缩,直接以纯文本传输。
随着 Web 功能越来越复杂,每个页面产生的请求数也越来越多,导致消耗在头部的流量越来越多,尤其是每次都要传输 UserAgent、Cookie 这类不会频繁变动的内容,完全是一种浪费。
我们再用通俗的语言解释下,压缩的原理。头部压缩需要在支持 HTTP/2 的浏览器和服务端之间:
维护一份相同的静态字典(Static Table),包含常见的头部名称,以及特别常见的头部名称与值的组合;
维护一份相同的动态字典(Dynamic Table),可以动态的添加内容;
支持基于静态哈夫曼码表的哈夫曼编码(Huffman Coding);
静态字典的作用有两个:
1)对于完全匹配的头部键值对,例如 “:method :GET”,可以直接使用一个字符表示;
2)对于头部名称可以匹配的键值对,例如 “cookie :xxxxxxx”,可以将名称使用一个字符表示。
浏览器和服务端都可以向动态字典中添加键值对,之后这个键值对就可以使用一个字符表示了。需要注意的是,动态字典上下文有关,需要为每个 HTTP/2 连接维护不同的字典。在传输过程中使用,使用字符代替键值对大大减少传输的数据量。
4.HTTP2支持服务器推送
服务端推送是一种在客户端请求之前发送数据的机制。当代网页使用了许多资源:HTML、样式表、脚本、图片等等。在HTTP/1.x中这些资源每一个都必须明确地请求。这可能是一个很慢的过程。浏览器从获取HTML开始,然后在它解析和评估页面的时候,增量地获取更多的资源。因为服务器必须等待浏览器做每一个请求,网络经常是空闲的和未充分使用的。
为了改善延迟,HTTP/2引入了server push,它允许服务端推送资源给浏览器,在浏览器明确地请求之前。一个服务器经常知道一个页面需要很多附加资源,在它响应浏览器第一个请求的时候,可以开始推送这些资源。这允许服务端去完全充分地利用一个可能空闲的网络。其实说白了,就是HTTP2.0中,浏览器在请求HTML页面的时候,服务端会推送css、js等其他资源给浏览器,减少网络空闲浪费。****
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hxv4Dgzc-1675906930809)(evernotecid://9F9295DA-46A3-4E92-AC15-00E6649D8054/appyinxiangcom/24873861/ENResource/p108)]
new的时候做了什么?
1.创建实例对象,继承构造函数原型上的方法
2.实例对象的_proto_ 指向 构造函数的原型对象prototype
3.this指向新创建的对象
4.返回值对象
- 无显示返回值:返回this
- 有显示返回值:
- 基本类型:直接返回基本类型
- 引用类型:返回对象H5新特性
typeof null 为什么是object
在 javascript 的最初版本中,使用的32位系统,js为了性能优化,使用低位来存储变量的类型信息。判断数据类型时使用机器码低位标识来判断。
数据类型 | 机器码 |
---|---|
对象 | 000 |
null | 全为0 |
undefined | (-2^30) 全为1 |
整数 | 0 |
浮点数 | 010 |
布尔 | 110 |
H5新特性
- 1、 拖拽释放(Drapanddrop)APIondrop
拖放是一种常见的特性, 即抓取对象以后拖到另一个位置
在HTML5中, 拖放是标准的一部分, 任何元素都能够拖放 - 2、 自定义属性data-id
- 3、 语义化更好的内容标(header,nav,footer,aside,article,section)
- 4、 音频,视频(audio,video)如果浏览器不支持自动播放怎么办?在
属性中添加autoplay(谷歌浏览器不支持音频自动播放, 但是视频支
持静音自动播放) - 5、 画布Canvas
- 5.1) getContext()方法返回一个用于在画布上绘图的环境
Canvas.getContext(contextID)参数contextID指定了您想要在画布上
绘制的类型。 当前唯一的合法值是“2d” , 它指定了二维绘图, 并且导
致这个方法返回一个环境对象, 该对象导出一个二维绘图API - 5.2) cxt.stroke()绘制线条
- 5.3) canvas和image在处理图片的时候有什么区别?
image是通过对象的形式描述图片的,canvas通过专门的API将图片绘制
在画布上.
- 5.1) getContext()方法返回一个用于在画布上绘图的环境
- 6、 地理(Geolocation)API其实Geolocation就是用来获取到当前设备的经
纬度(位
置) - 7、 本地离线存储localStorage用于长久保存整个网站的数据, 保存的数
据没有过期时间,直到手动去删除 - 8、 sessionStorage该数据对象临时保存同一窗口(或标签页)的数据, 在关
闭窗口或标签页之后将会删除这些数据。 - 9、 表单控件calendar,date,time,email,url,search,tel,file,
number - 10、 新的技术webworker,websocket,Geolocation
H5新特性延伸
1.localstorage
(1)API
#localStorage和sessionStorage的一些方法:
#添加键值对: setItem(key,value);
#获取键值对: getItem(key);
#删除键值对: removeItem(key);
#清除所有键值对: clear();
#获取属性名称(键名称): key(index);
#获取键值对的数量: length;
(2)原理
localstorage,sessionstorage都是Stroage对象的实例对象
localstorage.constructor == Stroage;//true
localstorage instanceof Stroage;//true
Stroage的原型对象上有clear,getItem,key,length,removeItem,setItem等方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SQ5QdNPq-1675906930809)(evernotecid://9F9295DA-46A3-4E92-AC15-00E6649D8054/appyinxiangcom/24873861/ENResource/p109)]
(3)跨域解决方案
同一浏览器在满足同源策略条件下,可共享localStorage数据。
不同源时可以采用postMessage和iframe相结合的方法,postMessage(data,origin)方法允许来自不同源的脚本采用异步方式进行通信,可以实现跨文本档、多窗口、跨域消息传递。
- data:传递参数,该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器支持任意类型的参数,部分浏览器只能处理字符串参数,需要使用JSON.stringify()方法对对象参数序列化。
- origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,只是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然也可以将参数设置为"*“,这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/"。
http://www.test.com/A.html
<body>
<h2>Status</h2>
<p></p>
<a href="http://www.test2.com/B.html">去index_b查看结果</a>
<iframe src="http://www.test2.com/C.html" frameborder="0"></iframe>
<script>
window.onload = function(){
//在页面加载完成后主页面向iframe发送请求
window.frames[0].postMessage('jogging, reading and writing','http://www.test2.com');
}
// 主页面监听message事件,
window.addEventListener('message', function(e){
var data = e.data;
document.querySelector('p').innerHTML = data;
}, false);
</script>
</body>
http://www.test2.com/C.html
<body>
<script>
//iframe接收消息,并把当前颜色发送给主页面
window.addEventListener('message', function(e) {
if (e.source != window.parent)
return;
console.log(e.data);
localStorage.setItem('task',e.data);
window.parent.postMessage('finished', '*');
}, false);
</script>
</body>
http://www.test2.com/B.html
<body>
<div>点击获取任务</div>
<p></p>
<script>
document.querySelector('div').addEventListener('click', function(){
document.querySelector('p').innerHTML = localStorage.getItem('task');
}, false);
</script>
</body>
4.safari浏览器处理方式
safari浏览器的默认限制,父页面无法向iframe里的跨域页面传递信息。故需要特殊处理,safari浏览器可以支持超过64k个字符的长度。一般服务器默认支持2~3万个字符长度的url不成问题。所以只要需要传输的数据量不是非常大的话,可以直接通过url来进行传递。
<h2>Status</h2>
<p></p>
<a href="http://www.test2.com/index_b.html">去index_b查看结果</a>
<iframe src="http://www.test2.com/getmessage.html" frameborder="0"></iframe>
<script>
var isSafari = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") < 0;
window.onload = function(){
if(isSafari){
var aobj = document.querySelector('a');
var href = 'http://www.test2.com/index_b.html?message='+ encodeURIComponent('jogging, reading and writing');
aobj.setAttribute('href', href);
}else{
//在页面加载完成后主页面向iframe发送请求
window.frames[0].postMessage('jogging, reading and writing','http://127.0.0.1:8082');
}
}
// 主页面监听message事件,
window.addEventListener('message', function(e){
var data = e.data;
document.querySelector('p').innerHTML = data;
}, false);
</script>
<body>
<div>点击获取任务</div>
<p></p>
<script>
var isSafari = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") < 0 ;
function GetQueryString(name)
{
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if(r!=null)return decodeURIComponent(r[2]); return null;
}
if(isSafari){
var data = GetQueryString('message');
localStorage.setItem('task',data);
}
document.querySelector('div').addEventListener('click', function(){
document.querySelector('p').innerHTML = localStorage.getItem('task');
}, false);
</script>
</body>
PWA
Progressive Web Application,全称“渐进式网页应用”,是谷歌主导的一种新时代网页(应用)。简单的理解,就是你的网页,可以通过某种方式达到离线使用。牵涉到主要两个文件
1.manifest.json文件
这个严格意义上来说,与离线的关系不大,更确切的讲,他是手机上用到的关键。让你的网页可以具有类似App的效果,比如logo,启动页面等等。
{
"name": "GoTop",
"short_name": "冲顶大会",
"display": "standalone",
"orientation": "landscape",
"start_url": "/",
"theme_color": "purple",
"background_color": "purple",
"icons": [
{
"src": "logo_48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "logo_144.png",
"sizes": "144x144",
"type": "image/png"
}
]
}
2.sw.js(Service Workers)
让你的Web App离线使用的关键js配置。Service Worker是一种离线缓存技术,属于web worker的一种,处理网络请求的后台服务。适用于离线和后台同步数据或推送信息。通过postMessage方法交互。
web work 多线程,运行复杂的脚本在后台运行而不会阻碍其他脚本运行,适用于处理器占用量大而又不阻碍的情形。通过postMessage方法进行数据传递。。
var cacheName = 'gotop-01' // 决定是否更新本地资源,每次要更新记得替换
var cacheList = [ // 需要保存本地资源列表,支持*匹配
"/",
"index.html",
"dist/app.js",
]
// Service Worker 注册完成事件,写入缓存
self.addEventListener('install', e => {
e.waitUntil(
caches.open(cacheName)
.then(cache => cache.addAll(cacheList))
.then(() => self.skipWaiting())
)
})
// Service Worker 启动事件,处理更新缓存
self.addEventListener('activate', e => {
e.waitUntil(
Promise.all(
caches.keys().then(keys => keys.map(key => {
if(key !== cacheName) {
return caches.delete(key)
}
}))
).then(() => {
self.clients.claim()
})
)
})
// 请求接口事件,处理相关逻辑
self.addEventListener('fetch', e => {
e.respondWith(
caches.match(e.request)
.then(res => res || fetch(e.request.url))
)
})
3.优点
(1)脱机游戏
(2)加速相应
(3)应用市场
SEO(Search Engine Optimization)搜索引擎优化
1.四个部分:信息采集,信息分析,信息查询,用户接口四部分。
- 信息采集:就是搜索引擎会派出蜘蛛沿着网站链接采集相关的网页信息,并且为了保证采集的资料为最近新,蜘蛛 也会回访抓取过的网页。
- 信息分析:通过分析程序,将采集的信息中符合规矩的提取索引项, 用索引项表示文档并生成文档库对的索引表,从而建立索引数据库
- 信息查询:当用户以关键词查找信息时,搜索引擎会根据用户的查询条件在索引库中快速检索文档,
- 用户接口:对检索出的文档进行分析相关度,并综合排序。
2.SEO如何优化
(1)内部优化
- 网页标签化优化,title,img标签中的alt,突出H1H2H3,添加keyword关键词
- 内部链接优化:各个详情页和首页的标签还有tab页相互链接,增加黏性
- 定时内容更新,增加原创性
- 服务端渲染,会让爬虫效率更高
- 在网站上合理设置robots.txt文件,文件位于网站根目录下
- 控制页面大小,减少http请求
(2)外部优化
- 友情链接
- 发布文章链接到自己网页
map set weakMap weakSet
参考:https://zhuanlan.zhihu.com/p/545305730
js的数据类型有哪些?
js的数据类型分为两种1是基础类型包含字符串(string)、数字(number)、布尔值(Boolean)、空(null)、未定义(undefined)、还有ES6中新增的Symbol类型2是引用类型(也统一称为object对象),主要包括对象(Object)、数组(Array)、函数(Function)、日期(Date)、正则(RegExp)等等
强引用和弱引用之间的区别是什么?
如果一个变量保存着对一个对象的强引用,那么这个对象将不会被垃圾回收,但是如果一个变量只保留着对这个对象的弱引用,那么这个对象将会被垃圾回收。比如weakMap数据存储
1.map
对象的数据类型,用键值对表示,键和值可以使任何数据类型,本质是空间换时间,数据量足够大时,空间代价的影响小。
- 声明时候直接赋值
(1)二维数组的形式
(2)二维数组的键名是Map单元数据的键名
(3)二维数据的数值是Map单元数据的数值
const m = new Map([['name','张三'],['age',19])
- 构造函数之后再赋值
const m = new Map()
m.set('class','信管')
与Object的区别:
一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。
Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突,而map健不可重复,如果键名冲突则会覆盖对应的值。
2.weakMap
WeakMap的数据类型只支持对象,WeakMap的对象是弱引用,弱引用的只是键名,而不是键值,键值依然是正常引用。。weakmap对象是不可枚举的,无法获取大小。只有set、get、has、delete。在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),相应的key则变成无效的。
- 用WeakMap来实现私有属性
- 缓存器函数
const cache = new WeakMap();
const process = function (obj) {
console.log(cache.has(obj), 'obj')
if (!cache.has(obj)) {
const result = bigOperation(obj);
cache.set(obj, result)
}
return cache.get(obj)
}
let obj = {};
const firstResult = process(obj)
console.log(firstResult);
const secondResult = process(obj)
console.log(secondResult);
// 源对象从weakmap移除
obj = null;
function bigOperation(obj) {
return 6
}
- 访问计数器
3.set
类似于数组的集合,集合中的值唯一,Set本身是一个构造函数,可用new创建对象,Set函数可接受一个数组作为参数用来初始化集合。
对于原始数据类型(boolean,number,string,null,undefined)如果存储相同值则只会保存一个,对于引用类型做“==”判断即引用地址完全相同则只会存一个。
undefined、null和NaN只会存储一次。undefined和infinity在Set集合中只会存在一个。
4.weakSet
WeakSet只能存放对象类型,不能存放基础类型;WeakSet对元素引用是弱引用,没有其他引用的会被回收。只有add、delete、has方法
- 存储DOM节点
Arraybuffer应用场景
rrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。
- 1.ArrayBuffer 是固定长度连续内存空间的引用;
- 2.ArrayBuffer 对象代表存储一段二进制数据的内存;
- 3.ArrayBuffer 并不能直接读写,只能通过视图(Typed Array View | Data View)来读写
1.响应拦截器
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
2.Ajax
传统上,服务器通过 AJAX 操作只能返回文本数据,即responseType属性默认为text。XMLHttpRequest第二版XHR2允许服务器返回二进制数据,这时分成两种情况。如果明确知道返回的二进制数据类型,可以把返回类型(responseType)设为arraybuffer;如果不知道,就设为blob。
let xhr = new XMLHttpRequest();
xhr.open('GET', someUrl);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {let arrayBuffer = xhr.response;// ···
};
xhr.send();
3.Canvas
网页Canvas元素输出的二进制像素数据,就是 TypedArray 数组。
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const uint8ClampedArray = imageData.data;
需要注意的是,上面代码的uint8ClampedArray虽然是一个 TypedArray 数组,但是它的视图类型是一种针对Canvas元素的专有类型Uint8ClampedArray。这个视图类型的特点,就是专门针对颜色,把每个字节解读为无符号的 8 位整数,即只能取值 0 ~ 255,而且发生运算的时候自动过滤高位溢出。这为图像处理带来了巨大的方便。
4.WebSocket
WebSocket可以通过ArrayBuffer,发送或接收二进制数据
let socket = new WebSocket('ws://127.0.0.1:8081');
socket.binaryType = 'arraybuffer';
// Wait until socket is open
socket.addEventListener('open', function (event) {// Send binary dataconst typedArray = new Uint8Array(4);socket.send(typedArray.buffer);
});
// Receive binary data
socket.addEventListener('message', function (event) {const arrayBuffer = event.data;// ···
});
5.Fetch API
Fetch API 取回的数据,就是ArrayBuffer对象。
fetch(url)
.then(function(response){return response.arrayBuffer()
})
.then(function(arrayBuffer){// ...
});
6.File API
如果知道一个文件的二进制数据类型,也可以将这个文件读取为ArrayBuffer对象。
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function () {const arrayBuffer = reader.result;// ···
};
深拷贝实现方式
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
1.手写
function deepClone(val){
//如果是基本类型直接返回
if(val===null&& val == 'object'){
return val
}
if(Object.prototype.toString(val)=='[object RegExp]')
return new RegExp(val)
}else if(Object.prototype.toString(val)=='[object Date]')
return new Date(val)
}else if(Object.prototype.toString(val)=='[object Undefined]')
return new Error(val)
}
let newObj = Array.isArray(val)?[]:{}
for(key in val){
if(typeof newObj[key] == 'object'){
newObj[key] = deepClone(val[key])
}else{
newObj[key] = val[key]
}
}
return newObj
}
2.structuredClone
structuredClone() 是一个新功能,现在已经被大多数浏览器使用。在不支持的平台上structuredClone,可以使用polyfill代替。
structuredClone(obj)
同步、异步、宏任务、微任务
JS单线程导致任务阻塞,为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制。于是,JS 中出现了同步任务和异步任务。
1.同步
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
1.异步
不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,任务队列开始通知主线程,请求执行任务,该任务才会进入主线程执行
先微任务再进行宏任务
(1)宏任务
- 定时器
- 事件绑定
- ajax
- 回调函数
(2)微任务
- process.nextTick :(node中实现的api,把当前任务放到主栈最后执行,当主栈执行完,先执行nextTick,再到等待队列中找)
- MutationObserver :监测node变化(创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。)
- Promise(async/await) => Promise并不是完全的异步,在promise中是同步任务,执行resolve或者reject回调的时候,此时是异步操作,会先将then/catch等放到微任务队列。当主栈完成后,才会再去调用resolve/reject方法执行
vite 和 webapck 对比
比较方面 | webpack | vite | 总结 |
---|---|---|---|
底层语言 | 基于nodejs构建,js是以毫秒计数 | 基于esbulid预构建依赖 | esbulid是采用go语言编写的,go语言是纳秒级别的,vite比webpack打包器快10-100倍 |
打包过程 | 分析各个模块之间的依赖=>然后进行编译打=>打包后的代码在本地服务器渲染。随着模块增多,打包的体积变大,造成热更新速度变慢 | 启动服务器=>请求模块时按需动态编译显示。(vite遵循的是ES Modlues模块规范来执行代码,不需要打包编译成es5模块即可在浏览器运行。) | vite启动的时候不需要分析各个模块之间的依赖关系、不需要打包编译。vite可按需动态编译缩减时间。当项目越复杂、模块越多的情况下,vite明显优于webpack |
热更新 | 模块以及模块依赖的模块需重新编译 | 浏览器重新请求该模块即可 | |
使用方面 | webpack更加灵活,api以及插件生态更加丰富 | vite开箱即用,更加简单,基于浏览器esm,使得hmr更加优秀,达到极速的效果 | |
原理不同 | webpack是bundle,自己实现了一套模块导入导出机制 | vite是利用浏览器的esm能力,是bundless |
vite不能使用 requrire 的替换方案
vue2中是不存在require is not defined问题的,那是因为webpack帮我们解决了,开发时在内部对其了转换。
1.动态引入图片
<img :src="getIcon()"></img>
const getIcon = ()=>{
return new URL('../img.svg',import.meta.url).href
}
2.全局替换
使用@originjs/vite-plugin-commonjs
插件
npm install @originjs/vite-plugin-commonjs -D
const { viteCommonjs } = require("@originjs/vite-plugin-commonjs");
module.exports = {
pluginOptions: {
vite: {
plugins: [
viteCommonjs({
// lodash不需要进行转换
exclude: ["lodash"],
}),
],
},
},
};
-
但是标签上的 require 并不支持,所以建议全面拥抱 ES Module
-
路由使用
resolve => require(['../components/views/Home.vue'], resolve)
导入的,可以通过 vscode 使用下面的正则全局替换
搜索:\(?resolve\)?\s*=>\s*require\(\[(.\*)\], resolve\)
替换:() => import($1)
纯函数和副作用函数
1.纯函数
即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。
var xs = [1,2,3,4,5];
var xp = [1,2,3,4,5];
//纯的
xs.slice(0,3); //=>[1,2,3]
xs.slice(0,3); //=>[1,2,3]
xs.slice(0,3); //=>[1,2,3]
console.log(xs); //=>[1,2,3,4,5];
//不纯的
xp.splice(0,3); //=>[1,2,3]
xp.splice(0,3); //=>[4,5]
xp.splice(0,3); //=>[]
console.log(xp); //=>[];
2.副作用函数
系统状态的一种变化,或者与外部世界进行的可观察的交互。只要是跟函数外部环境发生的交互就都是副作用——这一点可能会让你怀疑无副作用编程的可行性。函数式编程的哲学就是假定副作用是造成不正当行为的主要原因。
- 往数据库插入记录
- 发送一个 http 请求
- 可变数据
- 打印/log
- 获取用户输入
- DOM 查询