共计 14379 个字符,预计需要花费 36 分钟才能阅读完成。
从开发 & 运维角度方面来看,总体来说分为以下几个过程:
- DNS 解析: 将域名解析成 IP 地址
- TCP 连贯:TCP 三次握手
- 发送 HTTP 申请
- 服务器解决申请并返回 HTTP 报文
- 浏览器解析渲染页面
- 断开连接:TCP 四次挥手
一、什么是 URL?
URL(Uniform Resource Locator),对立资源定位符,用于定位互联网上资源,俗称网址。
scheme: // host.domain:port / path / filename ? abc = 123 # 456789
scheme - 定义因特网服务的类型。常见的协定有 http、https、ftp、file,其中最常见的类型是 http,而 https 则是进行加密的网络传输。host - 定义域主机(http 的默认主机是 www)domain - 定义因特网域名,比方 baidu.com
port - 定义主机上的端口号(http 的默认端口号是 80)path - 定义服务器上的门路(如果省略,则文档必须位于网站的根目录中)。filename - 定义文档 / 资源的名称
query - 即查问参数
fragment - 即 # 后的 hash 值,个别用来定位到某个地位
二、DNS 域名解析
在浏览器输出网址后,首先要通过域名解析,因为浏览器并不能间接通过域名找到对应的服务器,而是要通过 IP 地址。
- IP 地址
IP 地址是指互联网协议地址,是 IP Address 的缩写。IP 地址是 IP 协定提供的一种对立的地址格局,它为互联网上的每一个网络和每一台主机调配一个逻辑地址,以此来屏蔽物理地址的差别。
- 什么是域名解析
DNS 协定提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务。DNS 是一个网络服务器,咱们的域名解析简略来说就是在 DNS 上记录一条信息记录。
- 浏览器如何通过域名去查问 URL 对应的 IP 呢?
DNS 域名解析分为递归查问和迭代查问两种形式,现个别为迭代查问。
DNS 的优化与利用
- DNS 缓存
DNS 存在着多级缓存,从离浏览器的间隔排序的话,有以下几种: 浏览器缓存,零碎缓存,路由器缓存,IPS 服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。
- DNS 负载平衡
(DNS 重定向) DNS 负载平衡技术的实现原理是在 DNS 服务器中为同一个主机名配置多个 IP 地址,在应答 DNS 查问时,DNS 服务器对每个查问将以 DNS 文件中主机记录的 IP 地址按程序返回不同的解析后果,将客户端的拜访 疏导到不同的机器下来,使得不同的客户端拜访不同的服务器,从而达到负载平衡的目标。
- 大家耳熟能详的 CDN(Content Delivery Network)就是利用 DNS 的重定向技术,DNS 服务器会返回一个跟
用户最靠近的点的 IP 地址给用户,CDN 节点的服务器负责响应用户的申请,提供所需的内容。 - dns-prefetch
DNS Prefetch 是一种 DNS 预解析技术。当你浏览网页时,浏览器会在加载网页时对网页中的域名进行解析缓存,这样在你单击以后网页中的连贯时就无需进行 DNS 的解析,缩小用户等待时间,进步用户体验。
OSI 参考模型与 TCP/IP 四层模型
三、TCP 三次握手
- 客户端发送一个带 SYN=1,Seq=X 的数据包到服务器端口
(第一次握手,由浏览器发动,通知服务器我要发送申请了)
- 服务器发回一个带 SYN=1,ACK=X+1,Seq=Y 的响应包以示传播确认信息
(第二次握手,由服务器发动,通知浏览器我筹备承受了,你连忙发送吧)
- 客户端再回传一个带 ACK=Y+1,Seq=Z 的数据包,代表“握手完结”
(第三次握手,由浏览器发送,通知服务器,我马上就发了,筹备承受吧)
四、发送 HTTP 申请
TCP 三次握手完结后,开始发送 HTTP 申请报文。
为防止篇幅过长,http 协定、缓存等相干内容请参阅:
从 HTTP 到 WEB 缓存
五、服务器解决申请并返回 HTTP 报文
每台服务器上都会装置解决申请的利用——Web server。常见的 web server 产品有 apache、nginx、IIS、Lighttpd 等。
伪装我是一个传统的 MVC 模型,RD 同学请忽视
六、浏览器解析渲染页面
浏览器的次要形成
用户界面 (User Interface) - 包含地址栏、后退 / 后退按钮、书签目录等,也就是你所看到的除了用来显示你所申请页面的主窗口之外的其余局部
浏览器引擎 (Browser Engine) - 用来查问及操作渲染引擎的接口
渲染引擎 (Rendering Engine) - 用来显示申请的内容,例如,如果申请内容为 html,它负责解析 html 及 css,并将解析后的结果显示进去
网络 (Networking) - 用来实现网络调用,例如 http 申请,它具备平台无关的接口,能够在不同平台上工作
JS 解释器 (JS Interpreter) - 用来解释执行 JS 代码
UI 后端 (UI Backend) - 用来绘制相似组合抉择框及对话框等根本组件,具备不特定于某个平台的通用接口,底层应用操作系统的用户接口
数据存储 (DB Persistence) - 属于长久层,浏览器须要在硬盘中保留相似 cookie 的各种数据,HTML5 定义了 web database 技术,这是一种轻量级残缺的客户端存储技术
1. 多过程的浏览器
浏览器是多过程的,有一个主控过程,以及每一个 tab 页面都会新开一个过程(某些状况下多个 tab 会合并过程), 参考:前端进阶面试题具体解答
过程可能包含主控过程,插件过程,GPU,tab 页(浏览器内核)等等
- Browser 过程:浏览器的主过程(负责协调、主控),只有一个
- 第三方插件过程:每种类型的插件对应一个过程,仅当应用该插件时才创立
- GPU 过程:最多一个,用于 3D 绘制
- 浏览器渲染过程(内核):默认每个 Tab 页面一个过程,互不影响,管制页面渲染,脚本执行,事件处理等(有时候会优化,如多个空白 tab 会合并成一个过程)
2. 多线程的浏览器内核
每一个 tab 页面能够看作是浏览器内核过程,而后这个过程是多线程的,它有几大类子线程:
- GUI 线程
- JS 引擎线程
- 事件触发线程
- 定时器线程
- 网络申请线程
浏览器内核拿到内容后,渲染步骤大抵能够分为以下几步:
1. 解析 HTML,构建 DOM 树
2. 解析 CSS,生成 CSS 规定树
3. 合并 DOM 树和 CSS 规定,生成 render 树
4. 布局 render 树(Layout/reflow),负责各元素尺寸、地位的计算
5. 绘制 render 树(paint),绘制页面像素信息
以 webkit 内核为例
1. HTML 解析,构建 DOM
简略的了解,这一步的流程是这样的:浏览器解析 HTML,构建 DOM 树。
解析 HTML 到构建出 DOM 当然过程能够简述如下:
Bytes → characters → tokens → nodes → DOM
其中比拟要害的几个步骤
1. Conversion 转换:浏览器将取得的 HTML 内容(Bytes)基于他的编码转换为单个字符
2. Tokenizing 分词:浏览器依照 HTML 标准规范将这些字符转换为不同的标记 token。每个 token 都有本人独特的含意以及规定集
3. Lexing 词法剖析:分词的后果是失去一堆的 token,此时把他们转换为对象,这些对象别离定义他们的属性和规定
4. DOM 构建:因为 HTML 标记定义的就是不同标签之间的关系,这个关系就像是一个树形构造一样
例如:body 对象的父节点就是 HTML 对象,而后段略 p 对象的父节点就是 body 对象
2. 解析 CSS,生成 CSS 规定树
同理,CSS 规定树的生成也是相似。
Bytes → characters → tokens → nodes → CSSOM
3. 合并 DOM 树和 CSS 规定,生成 render 树
当 DOM 树和 CSSOM 都有了后,就要开始构建渲染树了
一般来说,渲染树和 DOM 树绝对应的,但不是严格意义上的一一对应, 因为有一些不可见的 DOM 元素不会插入到渲染树中,如 head 这种不可见的标签或者 display: none 等
4. 布局 render 树(Layout/Reflow),负责各元素尺寸、地位的计算
布局:通过渲染树中渲染对象的信息,计算出每一个渲染对象的地位和尺寸。
5. 绘制 render 树(Paint),绘制页面像素信息
绘制阶段,零碎会遍历出现树,并调用出现器的“paint”办法,将出现器的内容显示在屏幕上。
这张图片中重要的四个步骤
1. 计算 CSS 款式
2. 构建渲染树
3. 布局,次要定位坐标和大小,是否换行,各种 position overflow z-index 属性
4. 绘制,将图像绘制进去
- Layout,也称为 Reflow,即回流。个别意味着元素的内容、构造、地位或尺寸产生了变动,须要从新计算款式和渲染树
- Repaint,即重绘。意味着元素产生的扭转只是影响了元素的一些外观之类的时候(例如,背景色,边框色彩,文字色彩等),此时只须要利用新款式绘制这个元素就能够了
七、断开连接
当数据传送结束,须要断开 tcp 连贯,此时发动 tcp 四次挥手。
- 发动方向被动方发送报文,Fin、Ack、Seq,示意曾经没有数据传输了。并进入 FIN_WAIT_1 状态。
(第一次挥手:由浏览器发动的,发送给服务器,我申请报文发送完了,你筹备敞开吧)
- 被动方发送报文,Ack、Seq,表示同意敞开申请。此时主机发起方进入 FIN_WAIT_2 状态。
(第二次挥手:由服务器发动的,通知浏览器,我申请报文承受完了,我筹备敞开了,你也筹备吧)
- 被动方向发起方发送报文段,Fin、Ack、Seq,申请敞开连贯。并进入 LAST_ACK 状态。
(第三次挥手:由服务器发动,通知浏览器,我响应报文发送完了,你筹备敞开吧)
- 发动方向被动方发送报文段,Ack、Seq。而后进入期待 TIME_WAIT 状态。被动方收到发起方的报文段当前敞开连贯。发起方期待肯定工夫未收到回复,则失常敞开。
(第四次挥手:由浏览器发动,通知服务器,我响应报文承受完了,我筹备敞开了,你也筹备吧)
Promise.resolve().then(() => {console.log('1');
throw 'Error';
}).then(() => {console.log('2');
}).catch(() => {console.log('3');
throw 'Error';
}).then(() => {console.log('4');
}).catch(() => {console.log('5');
}).then(() => {console.log('6');
});
执行后果如下:
1
3
5
6
在这道题目中,咱们须要晓得,无论是 thne 还是 catch 中,只有 throw 抛出了谬误,就会被 catch 捕捉,如果没有 throw 出谬误,就被继续执行前面的 then。
new 操作符的实现原理
new 操作符的执行过程:
(1)首先创立了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象增加属性)
(4)判断函数的返回值类型,如果是值类型,返回创立的对象。如果是援用类型,就返回这个援用类型的对象。
具体实现:
function objectFactory() {
let newObject = null;
let constructor = Array.prototype.shift.call(arguments);
let result = null;
// 判断参数是否是一个函数
if (typeof constructor !== "function") {console.error("type error");
return;
}
// 新建一个空对象,对象的原型为构造函数的 prototype 对象
newObject = Object.create(constructor.prototype);
// 将 this 指向新建对象,并执行函数
result = constructor.apply(newObject, arguments);
// 判断返回对象
let flag = result && (typeof result === "object" || typeof result === "function");
// 判断返回后果
return flag ? result : newObject;
}
// 应用办法
objectFactory(构造函数, 初始化参数);
实现 call、apply 及 bind 函数
(1)call 函数的实现步骤:
- 判断调用对象是否为函数,即便是定义在函数的原型上的,然而可能呈现应用 call 等形式调用的状况。
- 判断传入上下文对象是否存在,如果不存在,则设置为 window。
- 解决传入的参数,截取第一个参数后的所有参数。
- 将函数作为上下文对象的一个属性。
- 应用上下文对象来调用这个办法,并保留返回后果。
- 删除方才新增的属性。
- 返回后果。
Function.prototype.myCall = function(context) {
// 判断调用对象
if (typeof this !== "function") {console.error("type error");
}
// 获取参数
let args = [...arguments].slice(1),
result = null;
// 判断 context 是否传入,如果未传入则设置为 window
context = context || window;
// 将调用函数设为对象的办法
context.fn = this;
// 调用函数
result = context.fn(...args);
// 将属性删除
delete context.fn;
return result;
};
(2)apply 函数的实现步骤:
- 判断调用对象是否为函数,即便是定义在函数的原型上的,然而可能呈现应用 call 等形式调用的状况。
- 判断传入上下文对象是否存在,如果不存在,则设置为 window。
- 将函数作为上下文对象的一个属性。
- 判断参数值是否传入
- 应用上下文对象来调用这个办法,并保留返回后果。
- 删除方才新增的属性
- 返回后果
Function.prototype.myApply = function(context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {throw new TypeError("Error");
}
let result = null;
// 判断 context 是否存在,如果未传入则为 window
context = context || window;
// 将函数设为对象的办法
context.fn = this;
// 调用办法
if (arguments[1]) {result = context.fn(...arguments[1]);
} else {result = context.fn();
}
// 将属性删除
delete context.fn;
return result;
};
(3)bind 函数的实现步骤:
- 判断调用对象是否为函数,即便是定义在函数的原型上的,然而可能呈现应用 call 等形式调用的状况。
- 保留以后函数的援用,获取其余传入参数值。
- 创立一个函数返回
- 函数外部应用 apply 来绑定函数调用,须要判断函数作为构造函数的状况,这个时候须要传入以后函数的 this 给 apply 调用,其余状况都传入指定的上下文对象。
Function.prototype.myBind = function(context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {throw new TypeError("Error");
}
// 获取参数
var args = [...arguments].slice(1),
fn = this;
return function Fn() {
// 依据调用形式,传入不同绑定值
return fn.apply(
this instanceof Fn ? this : context,
args.concat(...arguments)
);
};
};
对 rest 参数的了解
扩大运算符被用在函数形参上时,它还能够把一个拆散的参数序列整合成一个数组:
function mutiple(...args) {
let result = 1;
for (var val of args) {result *= val;}
return result;
}
mutiple(1, 2, 3, 4) // 24
这里,传入 mutiple 的是四个拆散的参数,然而如果在 mutiple 函数里尝试输入 args 的值,会发现它是一个数组:
function mutiple(...args) {console.log(args)
}
mutiple(1, 2, 3, 4) // [1, 2, 3, 4]
这就是 … rest 运算符的又一层威力了,它能够把函数的多个入参收敛进一个数组里。这一点 常常用于获取函数的多余参数,或者像下面这样解决函数参数个数不确定的状况。
JavaScript 有哪些内置对象
全局的对象(global objects)或称规范内置对象,不要和 “ 全局对象(global object)” 混同。这里说的全局的对象是说在
全局作用域里的对象。全局作用域中的其余对象能够由用户的脚本创立或由宿主程序提供。
规范内置对象的分类:
(1)值属性,这些全局属性返回一个简略值,这些值没有本人的属性和办法。例如 Infinity、NaN、undefined、null 字面量
(2)函数属性,全局函数能够间接调用,不须要在调用时指定所属对象,执行完结后会将后果间接返回给调用者。例如 eval()、parseFloat()、parseInt() 等
(3)根本对象,根本对象是定义或应用其余对象的根底。根本对象包含个别对象、函数对象和谬误对象。例如 Object、Function、Boolean、Symbol、Error 等
(4)数字和日期对象,用来示意数字、日期和执行数学计算的对象。例如 Number、Math、Date
(5)字符串,用来示意和操作字符串的对象。例如 String、RegExp
(6)可索引的汇合对象,这些对象示意依照索引值来排序的数据汇合,包含数组和类型数组,以及类数组构造的对象。例如 Array
(7)应用键的汇合对象,这些汇合对象在存储数据时会应用到键,反对依照插入程序来迭代元素。
例如 Map、Set、WeakMap、WeakSet
(8)矢量汇合,SIMD 矢量汇合中的数据会被组织为一个数据序列。
例如 SIMD 等
(9)结构化数据,这些对象用来示意和操作结构化的缓冲区数据,或应用 JSON 编码的数据。例如 JSON 等
(10)管制形象对象
例如 Promise、Generator 等
(11)反射。例如 Reflect、Proxy
(12)国际化,为了反对多语言解决而退出 ECMAScript 的对象。例如 Intl、Intl.Collator 等
(13)WebAssembly
(14)其余。例如 arguments
总结: js 中的内置对象次要指的是在程序执行前存在全局作用域里的由 js 定义的一些全局值属性、函数和用来实例化其余对象的构造函数对象。个别常常用到的如全局变量值 NaN、undefined,全局函数如 parseInt()、parseFloat() 用来实例化对象的构造函数如 Date、Object 等,还有提供数学计算的单体内置对象如 Math 对象。
箭头函数和一般函数有啥区别?箭头函数能当构造函数吗?
- 一般函数通过 function 关键字定义,this 无奈联合词法作用域应用,在运行时绑定,只取决于函数的调用形式,在哪里被调用,调用地位。(取决于调用者,和是否独立运行)
-
箭头函数应用被称为“胖箭头”的操作
=>
定义,箭头函数不利用一般函数 this 绑定的四种规定,而是依据外层(函数或全局)的作用域来决定 this,且箭头函数的绑定无奈被批改(new 也不行)。- 箭头函数罕用于回调函数中,包含事件处理器或定时器
- 箭头函数和 var self = this,都试图取代传统的 this 运行机制,将 this 的绑定拉回到词法作用域
- 没有原型、没有 this、没有 super,没有 arguments,没有 new.target
-
不能通过 new 关键字调用
- 一个函数外部有两个办法:[[Call]] 和 [[Construct]],在通过 new 进行函数调用时,会执行 [[construct]] 办法,创立一个实例对象,而后再执行这个函数体,将函数的 this 绑定在这个实例对象上
- 当间接调用时,执行 [[Call]] 办法,间接执行函数体
- 箭头函数没有 [[Construct]] 办法,不能被用作结构函数调用,当应用 new 进行函数调用时会报错。
function foo() {return (a) => {console.log(this.a);
}
}
var obj1 = {a: 2}
var obj2 = {a: 3}
var bar = foo.call(obj1);
bar.call(obj2);
isNaN 和 Number.isNaN 函数的区别?
- 函数 isNaN 接管参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因而非数字值传入也会返回 true,会影响 NaN 的判断。
- 函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再持续判断是否为 NaN,不会进行数据类型的转换,这种办法对于 NaN 的判断更为精确。
懒加载的概念
懒加载也叫做提早加载、按需加载,指的是在长网页中提早加载图片数据,是一种较好的网页性能优化的形式。在比拟长的网页或利用中,如果图片很多,所有的图片都被加载进去,而用户只能看到可视窗口的那一部分图片数据,这样就节约了性能。
如果应用图片的懒加载就能够解决以上问题。在滚动屏幕之前,可视化区域之外的图片不会进行加载,在滚动屏幕时才加载。这样使得网页的加载速度更快,缩小了服务器的负载。懒加载实用于图片较多,页面列表较长(长列表)的场景中。
常见的 HTTP 申请办法
- GET: 向服务器获取数据;
- POST:将实体提交到指定的资源,通常会造成服务器资源的批改;
- PUT:上传文件,更新数据;
- DELETE:删除服务器上的对象;
- HEAD:获取报文首部,与 GET 相比,不返回报文主体局部;
- OPTIONS:询问反对的申请办法,用来跨域申请;
- CONNECT:要求在与代理服务器通信时建设隧道,应用隧道进行 TCP 通信;
- TRACE: 回显服务器收到的申请,次要⽤于测试或诊断。
参考:前端进阶面试题具体解答
作用域
- 作用域:作用域是定义变量的区域,它有一套拜访变量的规定,这套规定来治理浏览器引擎如何在以后作用域以及嵌套的作用域中依据变量(标识符)进行变量查找
- 作用域链:作用域链的作用是保障对执行环境有权拜访的所有变量和函数的有序拜访,通过作用域链,咱们能够拜访到外层环境的变量和 函数。
作用域链的实质上是一个指向变量对象的指针列表。变量对象是一个蕴含了执行环境中所有变量和函数的对象。作用域链的前 端始终都是以后执行上下文的变量对象。全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最初一个对象。
- 当咱们查找一个变量时,如果以后执行环境中没有找到,咱们能够沿着作用域链向后查找
- 作用域链的创立过程跟执行上下文的建设无关 ….
作用域能够了解为变量的可拜访性,总共分为三种类型,别离为:
- 全局作用域
- 函数作用域
- 块级作用域,ES6 中的
let
、const
就能够产生该作用域
其实看完后面的闭包、this
这部分外部的话,应该根本能理解作用域的一些利用。
一旦咱们将这些作用域嵌套起来,就变成了另外一个重要的知识点「作用域链」,也就是 JS 到底是如何拜访须要的变量或者函数的。
- 首先作用域链是在定义时就被确定下来的,和箭头函数里的 this 一样,后续不会扭转,JS 会一层层往上寻找须要的内容。
- 其实作用域链这个货色咱们在闭包小结中曾经看到过它的实体了:
[[Scopes]]
图中的 [[Scopes]]
是个数组,作用域的一层层往上寻找就等同于遍历 [[Scopes]]
。
1. 全局作用域
全局变量是挂载在 window 对象下的变量,所以在网页中的任何地位你都能够应用并且拜访到这个全局变量
var globalName = 'global';
function getName() {console.log(globalName) // global
var name = 'inner'
console.log(name) // inner
}
getName();
console.log(name); //
console.log(globalName); //global
function setName(){vName = 'setName';}
setName();
console.log(vName); // setName
- 从这段代码中咱们能够看到,globalName 这个变量无论在什么中央都是能够被拜访到的,所以它就是全局变量。而在 getName 函数中作为局部变量的 name 变量是不具备这种能力的
- 当然全局作用域有相应的毛病,咱们定义很多全局变量的时候,会容易引起变量命名的抵触,所以在定义变量的时候应该留神作用域的问题。
2. 函数作用域
函数中定义的变量叫作函数变量,这个时候只能在函数外部能力拜访到它,所以它的作用域也就是函数的外部,称为函数作用域
function getName () {
var name = 'inner';
console.log(name); //inner
}
getName();
console.log(name);
除了这个函数外部,其余中央都是不能拜访到它的。同时,当这个函数被执行完之后,这个局部变量也相应会被销毁。所以你会看到在 getName 函数里面的 name 是拜访不到的
3. 块级作用域
ES6 中新增了块级作用域,最间接的体现就是新增的 let 关键词,应用 let 关键词定义的变量只能在块级作用域中被拜访,有“暂时性死区”的特点,也就是说这个变量在定义之前是不能被应用的。
在 JS 编码过程中 if 语句
及 for
语句前面 {...}
这外面所包含的,就是 块级作用域
console.log(a) //a is not defined
if(true){
let a = '123';console.log(a);// 123
}
console.log(a) //a is not defined
从这段代码能够看出,变量 a 是在
if 语句 {...}
中由let 关键词
进行定义的变量,所以它的作用域是 if 语句括号中的那局部,而在里面进行拜访 a 变量是会报错的,因为这里不是它的作用域。所以在 if 代码块的前后输入 a 这个变量的后果,控制台会显示 a 并没有定义
HTTP 1.0 和 HTTP 1.1 之间有哪些区别?
HTTP 1.0 和 HTTP 1.1 有以下区别:
- 连贯方面,http1.0 默认应用非长久连贯,而 http1.1 默认应用长久连贯。http1.1 通过应用长久连贯来使多个 http 申请复用同一个 TCP 连贯,以此来防止应用非长久连贯时每次须要建设连贯的时延。
- 资源申请方面,在 http1.0 中,存在一些节约带宽的景象,例如客户端只是须要某个对象的一部分,而服务器却将整个对象送过来了,并且不反对断点续传性能,http1.1 则在申请头引入了 range 头域,它容许只申请资源的某个局部,即返回码是 206(Partial Content),这样就不便了开发者自在的抉择以便于充分利用带宽和连贯。
- 缓存方面,在 http1.0 中次要应用 header 里的 If-Modified-Since、Expires 来做为缓存判断的规范,http1.1 则引入了更多的缓存控制策略,例如 Etag、If-Unmodified-Since、If-Match、If-None-Match 等更多可供选择的缓存头来管制缓存策略。
- http1.1 中 新增了 host 字段,用来指定服务器的域名。http1.0 中认为每台服务器都绑定一个惟一的 IP 地址,因而,申请音讯中的 URL 并没有传递主机名(hostname)。但随着虚拟主机技术的倒退,在一台物理服务器上能够存在多个虚拟主机,并且它们共享一个 IP 地址。因而有了 host 字段,这样就能够将申请发往到同一台服务器上的不同网站。
- http1.1 绝对于 http1.0 还新增了很多 申请办法,如 PUT、HEAD、OPTIONS 等。
什么是物理像素,逻辑像素和像素密度,为什么在挪动端开发时须要用到 @3x, @2x 这种图片?
以 iPhone XS 为例,当写 CSS 代码时,针对于单位 px,其宽度为 414px & 896px,也就是说当赋予一个 DIV 元素宽度为 414px,这个 DIV 就会填满手机的宽度;
而如果有一把尺子来理论测量这部手机的物理像素,理论为 1242*2688 物理像素;通过计算可知,1242/414=3,也就是说,在单边上,一个逻辑像素 = 3 个物理像素,就说这个屏幕的像素密度为 3,也就是常说的 3 倍屏。
对于图片来说,为了保障其不失真,1 个图片像素至多要对应一个物理像素,如果原始图片是 500300 像素,那么在 3 倍屏上就要放一个 1500900 像素的图片能力保障 1 个物理像素至多对应一个图片像素,能力不失真。当然,也能够针对所有屏幕,都只提供最高清图片。尽管低密度屏幕用不到那么多图片像素,而且会因为下载多余的像素造成带宽节约和下载提早,但从后果上说能保障图片在所有屏幕上都不会失真。
还能够应用 CSS 媒体查问来判断不同的像素密度,从而抉择不同的图片:
my-image {background: (low.png); }
@media only screen and (min-device-pixel-ratio: 1.5) {#my-image { background: (high.png); }
}
CDN 的应用场景
- 应用第三方的 CDN 服务:如果想要开源一些我的项目,能够应用第三方的 CDN 服务
- 应用 CDN 进行动态资源的缓存:将本人网站的动态资源放在 CDN 上,比方 js、css、图片等。能够将整个我的项目放在 CDN 上,实现一键部署。
- 直播传送:直播实质上是应用流媒体进行传送,CDN 也是反对流媒体传送的,所以直播齐全能够应用 CDN 来进步访问速度。CDN 在解决流媒体的时候与解决一般动态文件有所不同,一般文件如果在边缘节点没有找到的话,就会去上一层接着寻找,然而流媒体自身数据量就十分大,如果应用回源的形式,必然会带来性能问题,所以流媒体个别采纳的都是被动推送的形式来进行。
代码输入后果
const promise = new Promise((resolve, reject) => {console.log(1);
console.log(2);
});
promise.then(() => {console.log(3);
});
console.log(4);
输入后果如下:
1
2
4
promise.then 是微工作,它会在所有的宏工作执行完之后才会执行,同时须要 promise 外部的状态发生变化,因为这里外部没有发生变化,始终处于 pending 状态,所以不输入 3。
程度垂直居中的实现
- 利用相对定位,先将元素的左上角通过 top:50% 和 left:50% 定位到页面的核心,而后再通过 translate 来调整元素的中心点到页面的核心。该办法须要 思考浏览器兼容问题。
.parent {position: relative;} .child {position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);}
- 利用相对定位,设置四个方向的值都为 0,并将 margin 设置为 auto,因为宽高固定,因而对应方向实现平分,能够实现程度和垂直方向上的居中。该办法实用于 盒子有宽高 的状况:
.parent {position: relative;}
.child {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
- 利用相对定位,先将元素的左上角通过 top:50% 和 left:50% 定位到页面的核心,而后再通过 margin 负值来调整元素的中心点到页面的核心。该办法实用于 盒子宽高已知 的状况
.parent {position: relative;}
.child {
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px; /* 本身 height 的一半 */
margin-left: -50px; /* 本身 width 的一半 */
}
- 应用 flex 布局,通过 align-items:center 和 justify-content:center 设置容器的垂直和程度方向上为居中对齐,而后它的子元素也能够实现垂直和程度的居中。该办法要 思考兼容的问题,该办法在挪动端用的较多:
.parent {
display: flex;
justify-content:center;
align-items:center;
}
代码输入后果
const promise = new Promise((resolve, reject) => {console.log(1);
setTimeout(() => {console.log("timerStart");
resolve("success");
console.log("timerEnd");
}, 0);
console.log(2);
});
promise.then((res) => {console.log(res);
});
console.log(4);
输入后果如下:
1
2
4
timerStart
timerEnd
success
代码执行过程如下:
- 首先遇到 Promise 构造函数,会先执行外面的内容,打印
1
; - 遇到定时器
steTimeout
,它是一个宏工作,放入宏工作队列; - 持续向下执行,打印出 2;
- 因为
Promise
的状态此时还是pending
,所以promise.then
先不执行; - 继续执行上面的同步工作,打印出 4;
- 此时微工作队列没有工作,继续执行下一轮宏工作,执行
steTimeout
; - 首先执行
timerStart
,而后遇到了resolve
,将promise
的状态改为resolved
且保留后果并将之前的promise.then
推入微工作队列,再执行timerEnd
; - 执行完这个宏工作,就去执行微工作
promise.then
,打印出resolve
的后果。
网络劫持有哪几种,如何防备?
⽹络劫持分为两种:
(1)DNS 劫持: (输⼊京东被强制跳转到淘宝这就属于 dns 劫持)
- DNS 强制解析: 通过批改运营商的本地 DNS 记录,来疏导⽤户流量到缓存服务器
- 302 跳转的⽅式: 通过监控⽹络出⼝的流量,分析判断哪些内容是能够进⾏劫持解决的, 再对劫持的内存发动 302 跳转的回复,疏导⽤户获取内容
(2)HTTP 劫持: (拜访⾕歌然而⼀直有贪玩蓝⽉的⼴告), 因为 http 明⽂传输, 运营商会批改你的 http 响应内容(即加⼴告)
DNS 劫持因为涉嫌守法,曾经被监管起来,当初很少会有 DNS 劫持,⽽ http 劫持仍然⾮常盛⾏,最无效的方法就是全站 HTTPS,将 HTTP 加密,这使得运营商⽆法获取明⽂,就⽆法劫持你的响应内容。
僵尸过程和孤儿过程是什么?
- 孤儿过程 :父过程退出了,而它的一个或多个过程还在运行,那这些子过程都会成为孤儿过程。孤儿过程将被 init 过程(过程号为 1) 所收养,并由 init 过程对它们实现状态收集工作。
- 僵尸过程:子过程比父过程先完结,而父过程又没有开释子过程占用的资源,那么子过程的过程描述符依然保留在零碎中,这种过程称之为僵死过程。
如何优化动画?
对于如何优化动画,咱们晓得,个别状况下,动画须要频繁的操作 DOM,就就会导致页面的性能问题,咱们能够将动画的 position
属性设置为 absolute
或者fixed
,将动画脱离文档流,这样他的回流就不会影响到页面了。
HTTP 响应报文的是什么样的?
申请报⽂有 4 局部组成:
- 响应⾏
- 响应头
- 空⾏
- 响应体
- 响应⾏:由网络协议版本,状态码和状态码的起因短语组成,例如 HTTP/1.1 200 OK。
- 响应头:响应部⾸组成
- 响应体:服务器响应的数据