HTML
元素分类
- 行内元素(7):a b(加粗) span img input selcet strong
- 块级元素(9):div ul ol li dl dt dd h1 p
- 空元素(6):br hr(直线) img input link meta
link 和 @import 的区别
- 加载顺序(link 同时加载 @import 等页面加载完再加载)
- 分类不同(link 是 XHTML 标签,除 CSS 还可加载其他文件 @import 由 css 提供,只能加载 css)
- 兼容性(@import IE5 以上)
HTML5 新特性
- 语义新特性(结构元素:article、footer、header、nav、section;表单控件:calendar、date、time、email、url、search;)
- 本地存储特性
- 设备访问特性
- 连接特性(WebSocket)
- 网络多媒体特性(Audio、Video)
- 三维、图形及特效特性(SVG、Canvas、WebGL)
- 性能与集成特性(WebWorkers)
HTML5 离线缓存
Manifest文件组成:
- CACHE(必须 需要缓存的文件)
- FALLBACK(可选 后备页面)
如何更新缓存?
给manifest添加或删除文件,都可更新缓存。
1 | window.applicationCache.update(); |
CSS
盒模型
CSS选择器
- id选择器
- 类选择器
- 标签选择器
- 相邻选择器
- 子选择器
- 后代选择器
- 通配符选择器
- 属性选择器
- 伪类选择器
选择器权重
内联样式表(标签内部)> 嵌入样式表(当前文件中)> 外部样式表(外部文件中)。
!important > id > class > tag
!important 比 内联优先级高
CSS3 新特性
CSS3 选择器
1 | p:first-of-type 选择属于其父元素的首个 <p> 元素的每个 <p> 元素。 |
DIV 居中
1 | div{ |
1 | div { |
1 | div { |
1 | div { |
1 | .container { |
display有哪些值
table凉了,自行点击上面网站查看👆
inline、inline-block、block的区别
inline
不能设置高宽
margin只对横向生效,纵向无效
关于padding比较迷,下面来细细说明一下
给两个inline元素分别设置了100px的padding,我以为会出现上下padding不生效,左右padding生效的效果,而结果比较意外。
读完这篇文章你会发现,其实对于inline元素来说,padding四个方向的值都是生效的,只是他不会向块级元素一样影响其他元素的布局,而是产生一种堆叠效果。
Inline-block:
- 可以设置高宽
- margin和padding四个方向均生效
- 默认不换行
弹性盒模型
常见布局
多列等高布局
padding补偿法
1 | 在需要等高布局等每个元素都设置 很大的正的padding-bottom, 很大的负的margin-bottom; |
BFC(块级格式化上下文)
BFC应用场景
在讲解BFC之前我们首先看一下BFC有哪些应用场景
- 垂直方向上margin叠加问题
- overflow:hidden 清除浮动
BFC特性
- 同一个BFC的两个相邻Box的margin会发生叠加
- 计算BFC的高度时,浮动元素也参与计算
BFC触发条件
- float 除了none以外的值
- overflow 除了visible 以外的值(hidden,auto,scroll )
- display (table-cell,table-caption,inline-block, flex, inline-flex)
- position值为(absolute,fixed)
- fieldset元素
JavaScript
基本类型
- Number
- String
- Boolean
- Null
- Undefined
- Symbol
内置对象
- 数据封装类对象:Object Array Boolean Number String
- 其他对象:Function Arguments Math Date RegExp Error
如何判断一个数据的类型
typeof
1 | typeof '' // string 有效 |
instanceof
instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。
constructor
1 | ''.constructor == String // true |
toString
1 | Object.prototype.toString.call('') // [object String] |
原型链
三日不见,又把原型链还给爸爸了(现在简直是锥心般疯狂焦虑)。幸好我有先见之名,在之前好不容易理清楚后整理了一段录音。现在我们用流程图来展现一下。
每个函数创建后都会有prototype的属性,而对象有[[prototype]]的属性
原型链继承遵循如下一条规则:
1 | instance.constructor.prototype = instance.__proto__ |
而查找属性和方法是沿着实例的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
```javascript
function Parent() {
this.property = true
}
Parent.prototype.getParentValue = function () {
return this.property
}
function Child() {
this.Childproperty = false
}
Child.prototype = new Parent()
Child.prototype.getChildValue = function () {
return this.Childproperty
}
var instance = new Child()
console.log(instance.getParentValue())
所以上述instance.getParentValue()的查找顺序如下:
1 | instance.__proto__ --> Child.prototype --> (new Parent()).__proto__ --> Parent.prototype |
理清楚了原型链关系,我们来看一道经典原型链的题型:
1 | function Base(name){ |
做完题后的感想是以后若是我有机会面试其他人,一定要用这道题爆锤。
梳理思路的途中,看到一个比较诡异的题:
1 | Function instanceof Object // true |
要搞清楚这道题,我们首先需要搞清楚Function和Object的关系。
看完可能还是比较晕,我们来玄学解题,先来看下比较常规的原型链
1 | // 这个就很好解释 |
创建对象的方式
工厂模式
1 | function createPerson(name, job) { |
构造函数模式
1 | function Person(name, job) { |
new 的过程发生了什么
- 创建一个新对象
- 将构造函数的作用域赋值给新对象(这个对象会执行[[prototype]]链接)
- 执行构造函数中的代码
- 返回对象
原型模式
1 | function Person() {} |
另一种写法
1 | function Person() {} |
这里补充一下defineProperty的几个属性:
- configurable 可配置
- enumerable 可枚举
- value
- writable
- get
- set
这里插播一篇vue.js关于Object.defineProperty的利用原理
组合模式
1 | function Person(name) { |
动态原型模式
1 | function Person(name, job) { |
寄生构造函数模式
1 | function Person(name, job) { |
构造函数中重写了返回值,这个乍一看除了new和工厂没什么区别。
那么,这个new有毛用呢?
“寄生器构造函数”可以在构造函数不适应的情况使用,比如要创建一个数组类型,像题主给的代码一样(因为构造函数只能创建对象类型)。为了让人一看就知道是在构造一个新的对象类型的实例,所以虽然它写的和工厂模式一样,但是创建时用了new,因此使得实现的过程不一样,和楼上的回答一样(但是实现过程不重要)。 具体作用,比如创建具有额外方法的已有类型(如数组,Date类型等),但是又不污染原有的类型。 所以就算没有new也一样,只不过加上new让人清楚这是新对象类型的实例,也是“寄生器构造函数”里有个“构造函数”的原因。
那么就是没毛用咯。
稳妥构造函数模式
1 | function Person(name, job) { |
闭包
闭包是指有权访问另外一个函数作用域中的变量的函数。
应用场景:
- 结合Symbol实现私有属性
1 | const People = (function() { |
- 循环绑定事件时利用闭包保存变量
1 | <html> |
理解闭包之前,我们需要先理解JavaScript的作用域。
总结一下上面几篇文章的内容:
当函数被创建的同时会产生一个执行上下文(对象)
js执行的时候会维持一个执行上下文栈,随着函数的执行,执行上下文(对象)进栈出栈。
这个对象包括几个属性,其中有 变量对象、作用域链
变量对象包括:
- 建立arguments对象
- 找到这个将要执行的函数内的所有函数声明
- 找到这个将要执行的函数内的所有变量声明
作用域链里,大概是这样的(看图体会)
这里的0 1,就分别代表爸爸的活动对象,爸爸的爸爸的活动对象。
当搜索引擎解析一个变量的时候,会先在当前的变量对象里查找,没有的话就会沿着作用域链查找。
ES6
挑几个常用的总结一下,其他的看阮老师的文章。
箭头函数
关于箭头函数的几个注意点:
- 函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象。 - 不可以当作构造函数,也就是说,不可以使用
new
命令,否则会抛出一个错误。 - 不可以使用
arguments
对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
Web 综合
常见的浏览器内核
- Trident: IE
- Gecko: FireFox
- Presto: Opera
- Webkit: Safari, Chrome
介绍一下你对浏览器内核的理解?
从浏览器多进程到 JS 单线程,JS 运行机制最全面的一次梳理
进程和线程的区别
- 进程是 cpu 资源分配的最小单位
- 线程是 cpu 调度的最小单位
浏览器是多进程的
浏览器有以下几个进程:
- Browser 进程(主程)
- 负责浏览器界面显示,与用户交互。如前进,后退等
- 负责各个页面的管理,创建和销毁其他进程
- 将 Renderer 进程得到的内存中的 Bitmap,绘制到用户界面上
- 网络资源的管理,下载等
- 第三方插件进程
- GPU 进程(3D 绘制)
- 浏览器渲染进程 = 浏览器内核(每个 Tab 一个进程,多个空白页自动合并成一个进程。用于页面渲染、脚本运行、事件处理等)
敲黑板,划重点,浏览器的渲染进程是多线程的。那么我们来看看它包括哪些线程。
- GUI 渲染线程(Graphical User Interface)
- 负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等。
- 注意,GUI 渲染线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起(相当于被冻结了),GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行。
- JS 引擎线程
- 也称为 JS 内核,负责处理 Javascript 脚本程序。
- 事件触发线程
- 定时触发器线程
- 异步 http 请求线程
从打开浏览器到页面渲染的过程
- 用户输入 URL
- 判断是否命中强缓存,若命中,直接返回缓存资源。
- 开始 DNS 解析,详细过程参考从输入一个 url 后到页面加载完成都发生了什么
- 建立 TCP 连接(三次握手)
- TCP 建立完成后发送 HTTP 请求
- 服务器接受请求并解析,如果头部有缓存相关信息如 if-none-match 与 if-modified-since,则验证缓存是否有效,若有效则返回 304
- 服务器将响应报文通过 TCP 连接发送回浏览器
- 浏览器接收响应,并关闭 TCP 连接(四次挥手)
- 浏览器检查状态码并进行对应操作
- Browser 进程收到用户请求,首先需要获取页面内容(譬如通过网络下载资源),随后将该任务通过 RendererHost 接口传递给 Render 进程
- Renderer 进程的 Renderer 接口收到消息,简单解释后,交给渲染线程,然后开始渲染
- 解析 html 建立 dom 树
- 解析 css 构建 render 树(将 CSS 代码解析成树形的数据结构,然后结合 DOM 合并成 render 树)
- 布局 render 树(Layout/reflow),负责各元素尺寸、位置的计算
- 绘制 render 树(paint),绘制页面像素信息
- 浏览器会将各层的信息发送给 GPU,GPU 会将各层合成(composite),显示在屏幕上。
TCP 三次握手和四次挥手
首先我们来看看 TCP 的报文里都有哪些字段。
下面我们来分别解释一下在连接中我们使用到的字段分别代表什么意思:
- SYN: 同步序号(synchronous),用来发起一个连接。当 SYN=1 而 ACK=0 时,表明这是一个连接请求报文段,若对方同意建立连接,则应在响应的报文段中使 SYN=1 和 ACK=1。
- ACK: 确认字符(acknowledgement),仅当 ACK=1 时确认号字段才有效,当 ACK=0 时,确认号无效。TCP 规定,在连接建立后所有的传送报文段都必须把 ACK 置 1。
三次握手
第一次握手:客户端的 TCP 进程首先创建传输控制模块 TCB,然后向服务端发出连接请求报文段,该报文段首部中的 SYN=1,ACK=0,同时选择一个初始序号 seq=i。TCP 规定,SYN=1 的报文段不能携带数据,但要消耗掉一个序号。这时,TCP 客户进程进入 SYN—SENT(同步已发送)状态,这是 TCP 连接的第一次握手。
第二次握手:服务端收到客户端发来的请求报文后,如果同意建立连接,则向客户端发送确认。确认报文中的 SYN=1,ACK=1,确认号 ack=i+1,同时为自己选择一个初始序号 seq=j。同样该报文段也是 SYN=1 的报文段,不能携带数据,但同样要消耗掉一个序号。这时,TCP 服务端进入 SYN—RCVD(同步收到)状态,这是 TCP 连接的第二次握手。
第三次握手:TCP 客户端进程收到服务端进程的确认后,还要向服务端给出确认。确认报文段的 ACK=1,确认号 ack=j+1,而自己的序号为 seq=i+1。TCP 的标准规定,ACK 报文段可以携带数据,但如果不携带数据则不消耗序号,因此,如果不携带数据,则下一个报文段的序号仍为 seq=i+1。这时,TCP 连接已经建立,客户端进入 ESTABLISHED(已建立连接)状态。这是 TCP 连接的第三次握手,可以看出第三次握手客户端已经可以发送携带数据的报文段了。
知乎上一生动的段子:
- 第一次握手:约吗
- 第二次握手:约
- 第三次握手:那我出发了
四次挥手
浏览器缓存
强缓存
命中缓存后返回 200。
字段:
- expires
- cache-control
禁止缓存:Cache-Control: no-cache, no-store, must-revalidate
缓存静态资源 Cache-Control:public, max-age=31536000
- pragma
建议只在需要兼容 HTTP/1.0 客户端的场合下应用 Pragma 首部。
要注意,强缓存通常都是针对静态资源使用,动态资源需要慎用。
这里回溯一下之前项目中碰到的缓存问题。在之前的项目中发送 Ajax 请求时,我们通过了在请求的 URL 后面添加时间戳破缓存,这里解释一下原理。
浏览器缓存是基于 url 进行缓存的,如果页面允许缓存,则在一定时间内(缓存时效时间前)再次访问相同的 URL,浏览器就不会再次发送请求到服务器端,而是直接从缓存中获取指定资源。
1 | axios.get(`/user/check?timestamp=${new Date().getTime()}`); |
关于缓存更新请看这里:大公司里怎样开发和部署前端代码?
协商缓存
命中缓存后返回 304。
字段:
- Last-Modified
- If-Modified-Since
如果 Last-Modified 比 If-Modified-Since 晚,就从服务器端拉取数据
- ETag
- If-None-Match
如果 If-None-Match 和 Etag 不一致时,从服务器端拉取数据
浏览器行为对缓存的影响
当 ctrl+f5 强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;
当 f5 刷新网页时,跳过强缓存,但是会检查协商缓存;
load 事件和 DOMContentLoaded 事件的先后
当 DOMContentLoaded 事件触发时,仅当 DOM 加载完成,不包括样式表,图片。 (譬如如果有 async 加载的脚本就不一定完成)
当 onload 事件触发时,页面上所有的 DOM,样式表,脚本,图片都已经加载完成了。 (渲染完毕了)
DOMContentLoaded -> load
location.Reload()和location.href 区别
window.location.reload()和window.location.href=””的区别
React
React性能提升
- 避免不必要的render
- 无状态组件
Immutable
计算机网络
HTTP请求方法
HTTP请求方法:GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE
前端构建工具
代码片段
数组随机排序
方法一:数组元素交换位置
1 | function randSort(arr) { |
方法二:将原数组数据随机塞入新数组
1 | function randSort(arr) { |
方法三:sort
1 | arr.sort(() => Math.random() - 0.5) |
数组去重
方法一:set
1 | const unique = arr => [...new Set(arr)] |
方法二:利用key的唯一性进行去重
1 | const unique = arr => { |
数组降维
concat
1 | function reduceDimension(arr) { |
apply 和 concat
1 | function reduceDimension(arr) { |
全排列
1 | function permutate(str) { |