万字前端面试八股文,建议打印出来放床头
万字前端面试八股文,建议打印出来放床头
众所周知啊,前端是一个要求你啥都会的岗位。不会算法的前端一定开发不出一个好的后台。
话不多说,直接上干货前端知识点干货。
不知道牛客上这个导航好使不。
JavaScript
基本数据类型
js 一共有7种基本数据类型,分别是 Undefined、Null、Boolean、Number、String。
(注意大写字母开头)(大写字母开头)(大写字母开头)
ES6 中新增的 Symbol 类型,代表创建后独一无二且不可变的数据类型,主要是为了解决可能出现的全局变量冲突的问题。ES2020新增BigInt,用于处理任意精度整数的新数字基元n,比如3n。
ES5中有6种数据类型:(另一个说法,注意没有基本2字)
null,undefined,number,string,boolean,object。
- 复杂类型是指object即广义的对象类型,可由多个简单类型的值的合成,可以看作是一个存放各种值的容器。包括Array,Math,Date,RegExp,Function等
- 基础类型指number,string,boolean,null,undefined。
基础类型和复杂类型的区别:
- 基础类型将内容直接存储在栈中(大小固定位置连续的存储空间),记录的是该数据类型的值,即直接访问,基础类型赋值是复制(copy);
- 复杂类型将内容存储在堆中,堆所对应的栈中记录的是指针(堆的地址),外部访问时先引出地址,再通过地址去找到值所存放的位置。复杂类型赋值是地址引用。
//经典考题1地址引用 let a = {name:'du',age:19}; let b =a; console.log(b.name);//du b.name = 'lee'; console.log(b.name);//lee console.log(a.name);//lee a中的属性改变,说明a和b指向同一地址//经典考题2 let a = {name:'du',age:19}; function change (obj){ obj.age=29; //obj作为对象参数,引用地址传入,修改age属性影响了原对象 obj = { //赋值操作,相当新建了一个对象让obj指向他 name:'lee', age:39 } return obj; //返回新对象 } let b=change(a); console.log(a.age);//29,a中已改变 console.log(b.age);//39,b为新对象
js是一种解释性:不用编译;动态:不用声明类型,弱类型:类型转换,语言
JavaScript数字全部是浮点数。 根据 IEEE 754标准中的64位二进制(binary64), 也称作双精度规范(double precision)来储存。这些字节按照以下规则分配:
0 - 51 位是 分数f(fraction )
52 - 62 位是 指数(exponent )
63 位 是 标志位 (sign),结果二进制形式为是+- 1.f * 2e,最大精确的整数为2**53
//经典考题 0.1+0.1===0.2 //true 0.1+0.2===0.3 //false
原理解析见本文,帮助理解。
null 和 undefined 的区别
- Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
- undefined 代表的含义是未定义,null 代表的含义是空对象。一般变量声明了但还没有定义的时候会返回 undefined,null主要用于赋值给一些可能会返回对象的变量,作为初始化。
- undefined 在 js 中不是一个保留字,这意味着我们可以使用 undefined 来作为一个变量名,这样的做法是非常危险的,它会影响我们对 undefined 值的判断。但是我们可以通过一些方法获得安全的 undefined 值,比如说 void 0。
- 当我们对两种类型使用 typeof 进行判断的时候,null 类型化会返回 “object”,这是一个历史遗留的问题。
undefined==null(true)
undefined===null(false)
判断数据类型的方式
typeof 缺陷:可以判断null以外的基础类型,复杂类型除funiton以外均判断为object
// typeof(null)===‘’object’;typeof (()=>{} ))===‘function’;
instanceof 可以准确判断复杂引用类型,但不能判断基础类型
//Object instanceof Object//true
Object.prototype.toString.call() ;可以判断所有类型,返回示例 ''[object Array]"
注意后面的类型一定是大写字母开头。
Object.prototype.toString.call(null); //"[object Null]"
.constructor=== //返回构造器,例如(2.1).constructor === Number//true
一些隐式转换规则(选择题爱考这个)
字符串转换 .toString()或者String():
null 和 undefined 类型 ,null 转换为 "null",undefined 转换为 "undefined",
Boolean 类型,true 转换为 "true",false 转换为 "false"。
Number 类型的值直接转换,不过那些极小和极大的数字会使用指数形式。
数字转换:
(1)undefined 类型的值转换为 NaN。
(2)null 类型的值转换为 0。
(3)Boolean 类型的值,true 转换为 1,false 转换为 0。
(4)String 类型的值转换如同使用 Number() 函数进行转换,包含非数字值则转换为 NaN,空字符串为 0,"-Infinity"转化为-Infinity
{}转换为NaN。[]转为0.
布尔值转换:
以下均为false,除外都为true 例如Boolen(' ')为true,Boolen([])为true
• undefined
• null
• false
• +0、-0 和 NaN
• ""
如何将字符串转化为数字,例如 '12.3b'
(1)使用 Number() 方法,前提是所包含的字符串不包含不合法字符。
(2)使用 parseInt() 方法,parseInt() 函数可解析一个字符串,并返回一个整数。还可以设置要解析的数字的基数。当基数的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。
经典题:[1,2,3].map(parseInt) ===[1,NaN,NaN]
(3)使用 parseFloat() 方法,该函数解析一个字符串参数并返回一个浮点数。
内容有点多,做一个html的隐藏折叠显示
遍历对象
<details> <summary>点击展开</summary>- Object.keys()
返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性).数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
let obj = {name: 'lee',sex: 'male',age: 18} Object.keys(obj).forEach(key => { console.log(key, obj[key]); })
- for...in
循环遍历对象自身的和继承的可枚举属性(不含Symbol属性).
- Object.getOwnPropertyNames()
返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性).
- Reflect.ownKeys()
</details> <details> <summary>点击展开</summary>返回一个数组,包含对象自身的所有属性(包括Symbol属性和不可枚举属性).
### 遍历数组
- forEach()
let arr = [1,2,,3]; arr.forEach((v,k) => { console.log(v); })//跳过空
- for...in
> 注意for...in遍历的是索引 let arr = [1,2,,3]; for(let ele in arr) { console.log(ele); } //0 1 3
- for...of
let arr = [1,2,,3]; for(let ele of arr) { console.log(ele); } //1,2,undefined,3</details>
js的对象的常用的方法
<details> <summary>点击展开</summary>Object.assign() //浅复制对象
Object.entries() //返回自身可枚举的[key,value]
Object.keys(),Object.values()
Object.hasOwnProperty(key)//是否有这个属性 true/false
Object.getOwnPropertyNames() //取得对象自身可枚举的属性名
//for in 对对象进行遍历,可以拿到自身以及原型链上的可枚举的属性
Object.freeze()//冻结一个对象,不可修改,不可删除。不可添加新的属性
Object.prototype.toString()// 返回数组[object,object/array/function等]
//判断是数组还是对象就是用的这个方法
js的字符串的常用的方法
<details> <summary>点击展开</summary>str.concat()//拼接
str.includes()//判断字符串是否包含在另外一个字符串中
str.indexOf(),str.lastIndexOf()
str.split() //按特定的符号分割成字符串数组!
str.toLowerCase() //转换成小写的形式
str.toUpperCase() //转换成大写的形式
str.trim()//去空格
js的数组的常用的方法
<details> <summary>点击展开</summary>arr.push(),arr.pop(), arr.shift(),arr.unshift(),arr.reverse(),arr.every(),arr.some()
arr.forEach()不会迭代空元素,arr.filter()实现过滤,arr.map()返回数组,arr.reduce()前后
arr.includes(),arr.indexOf(),arr.lastIndexOf() //索引正序,但是从后往前找
arr.findIndex() //找索引,
arr.find() //找满足条件的元素
arr.join()//默认以逗号隔开
arr.splice(3,1,'o','i')//从索引3开始,删除1个,添加两个字符串。
arr.flat() //数组降维 ,返回新数组
arr.flat(1)
arr.entries() //将数组返回一个对象,包含对象索引的键值对
push()
将一个或多个元素添加到数组的末尾,并返回该数组的新长度pop()
方法从数组中删除最后一个元素,并返回该元素的值- substring(star,stop) ,star>stop时会交换两者;相等时返回空串;为负数时会将负数变为0,奇怪点较多。
- substr(star,length),star为负数时,表示倒数位置;length<=0时,返回空串
- slice(star,stop), 可以应用在数组和字符串;star>=stop时返回空串/数组;为负数时代表倒数位置
- splice(start,length,items,只能用于数组,会改变原始数组;satr<0代表倒数,length<0返回空,length过大时截取全部。
JS中的深拷贝与浅拷贝
<details> <summary>点击展开</summary>浅copy复制,创建一个新对象,接受原对象的值或者地址,改变新对象会引起原对象的变。
深拷贝完全是另一个对象,
Object.assign(newobj,obj)//ES6浅拷贝,遍历所有可枚举属性,然后赋值。把obj的可枚举属性拷贝给newobj,如果属性的值是复杂对象,则会指向同一地址,因此是浅拷贝
let newobj = {...obj} //浅拷贝 同上
Object.create(obj)//不能算是拷贝?该是继承,此方法使用指定的原型对象及其属性去创建一个新的对象
let newObj = JSON.parse(JSON.stringify(oldObj));//不完全的深拷贝
</details>js垃圾回收机制
基本类型:栈空间,引用类型:堆空间;池:常量
回收方法/:标记清除法和引用计数法
js 原型,原型链以及特点
<details> <summary>点击展开</summary>JavaScript 只有一种结构:对象。例子:
function Class(){
this.name='name';
}
class = new Class();//小写class为Class类的实例;Class为构造函数
每个实例对象(object)都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype)。
即class.proto === Class.prototype; //true
prototype中一般包含2个属性,一个是constructor,指向Class函数自身;一个是proto,指向更高一级的原型对象。可以在prototype中添加新的属性方法,实例上也能访问到。
原型对象也有一个自己的原型对象(proto),层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
ES5 中Object.getPrototypeOf() 方法来获取对象的原型。
当访问一个实例对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。
特点:
JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。
JavaScript 继承的方式
<details> <summary>点击展开</summary>- 原型链继承,分清构造函数,原型,实例:1,每一个构造函数都有一个原型对象;2,原型对象包含一个指向构造函数的指针;3,实例中包含一个原型对象的指针。缺点,在包含有引用类型的数据时,会被所有的实例对象所共享,容易造成修改的混乱。
使用构造函数的方式。
组合继承。
原型式继承。
寄生式继承。
寄生组合继承。
new 运算符的过程
<details> <summary>点击展开</summary>1. 创建一个空对象{};
- 构造函数中的 this 指向该空对象
- 执行构造函数为这个空对象添加属性
- 判断构造函数有没有返回值,如果返回值是个对象,则返回这个对象;否则返回创建的对象
function Child1(){this.name='child1';} var p = new Child1(); p//Child1 {name: "child1"} 一个Child1的实例 var p = Child1() //不使用new p //undefined 因为Chilid1函数结果返回值为undefined name // child1方法中this指向window,所以window由了一个全局属性name</details>
JS 作用域
<details> <summary>点击展开</summary>ES5 只有全局作用域和函数作用域
全局作用域:代码在程序的任何地方都能被访问,window 对象的内置属性都存在全局作用域
函数作用域:在固定的代码片段才能被访问
末定义直接赋值的变量自动声明为拥有全局作用域
var num1 = 1; function fun1 (){ num2 = 2; } num2;//报错 fun1(); num2;//2 需先调用函数后才能完成num2的定义和赋值
var num1 = 1; function fun1 (num1){ console.log(num1); num1 = 2; console.log(num1); } fun1();//undefined,2 未传递参数 num1;//1, var num1 = 1; function fun1 (){ console.log(num1);num1 = 2;console.log(num1); } fun1();//1,2 访问全局变量 num1;//2 num1被改变 var num1 = 1; function fun1 (){ console.log(num1); var num1 = 2;console.log(num1); } fun1();//undefined,2 函数内作用域,尚未赋值 num1;//1 全局num1不改变 var num1 = 1; function fun1 (){ console.log(num1); let num1 = 2;console.log(num1); } fun1();//报错,暂时性死区,let不提升
ES6 有块级作用域
</details>ES6 新特性
<details> <summary>点击展开</summary>- let const 块级作用域
- 模板字符串
- 解构赋值
- 箭头函数,函数参数默认值
- 扩展运算符(...)
- forEach for...of for...in
- 数组方法map reduce includes
- map和set
- 模块化
- promise proxy
- async
- class
let 和 var 的区别
- let 是块级作用域,var 是函数作用域
- var 存在变量提升,而 let 没有
- let变量不能覆盖作用域中已定义的变量,const变量定义同时必须赋值
- 在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区” (TDZ)
闭包的特性以及优缺点
<details> <summary>点击展开</summary>闭包有三个特性:
- 函数嵌套函数;
- 内部函数使用外部函数的参数和变量;
- 参数和变量不会被垃圾回收机制回收。
闭包的优点:
- 希望一个变量长期保存内存中;
- 避免全局变量污染;
- 私有成员的存在。
闭包的缺点:
- 常驻内存,增加内存使用量;
- 使用不当造成内存泄漏。
箭头函数与普通函数的区别
<details> <summary>点击展开</summary>1. 箭头函数是匿名函数,不能作为构造函数,不能使用new
箭头函数不能绑定arguments,取而代之用...解决
let C = (...c) => { console.log(c); } C(3,82,32,11323); // [3, 82, 32, 11323]
箭头函数没有原型属性
箭头函数的this永远指向其上下文的this,没有办改变其指向,
普通函数的this指向调用它的对象
ES6 中箭头函数 VS 普通函数的 this 指向
<details> <summary>点击展开</summary>普通函数中 this
- 查看函数在哪被调用。
- 点左侧有没有对象?如果有,它就是 “this” 的引用。如果没有,继续第 3 步。
- 该函数是不是用 “call”、“apply” 或者 “bind” 调用的?如果是,它会显式地指明 “this” 的引用。如果不是,继续第 4 步。
- 该函数是不是用 “new” 调用的?如果是,“this” 指向的就是 JavaScript 解释器新创建的对象。如果不是,继续第 5 步。
- 是否在“严格模式”下?如果是,“this” 就是 undefined,如果不是,继续第 6 步。
- JavaScript 很奇怪,“this” 会指向 “window” 对象。
ES6 箭头函数中 this
- 默认指向定义它时,所处上下文的对象的 this 指向;
偶尔没有上下文对象,this 就指向 window
// 例1 function hello() { console.log(this) // window } hello() // 例2 function hello() { 'use strict' console.log(this) // undefined } hello() // 例3 const obj = { num: 10, hello: function () { console.log(this) // obj setTimeout(function () { console.log(this) // window }) }, } obj.hello() // 例4 const obj = { num: 10, hello: function () { console.log(this) // obj setTimeout(() => { console.log(this) // obj }) }, } obj.hello() // 例5 const obj = { radius: 10, diameter() { return this.radius * 2//diameter是普通函数,里面的this指向直接调用它的对象obj。 }, perimeter: () => 2 * Math.PI * this.radius,//这里上下文没有函数对象,就默认为window。 } console.log(obj.diameter()) // 20 console.log(obj.perimeter()) // NaN</details>
Event Loop 事件循环
<details> <summary>点击展开</summary>微任务: promise.then(不是promise,promise里是立即执行) MutationObserver process.nextTick(Node.js 环境)
宏任务: script(整体代码) setTimeout setInterval I/O setImmediate(Node.js 环境) UI 交互事件
同一次事件循环中: 微任务永远在宏任务之前执行
事件循环的过程:
</details>首先script脚本整体是一个大的异步任务,先执行script脚本。这个script脚本会包含同步任务和异步任务,同步任务会先在主线程上执行,异步任务(分为宏任务和微任务)会添加到任务队列中,任务队列分为宏任务队列和微任务队列。
当同步任务执行完毕后,此时的执行栈已经被清空,会去执行异步任务。此时会先从微任务队列中取一个微任务放到执行栈中执行,若有新的微任务或宏任务产生,添加到相应的任务队列中,循环往复,直至微任务队列清空。
紧接着会从宏任务队列取一个宏任务放到执行栈中执行,此时可能会产生新的微任务,将微任务放到微任务队列中,当这个宏任务执行完后会继续执行微任务队列,如果没有产生就继续执行下一个宏任务。循环往复,直至所有任务执行完毕。
HTML
HTML5 的新特性
<details> <summary>点击展开</summary>1. 语义化标签:header、footer、section、nav、aside、article
增强型表单:input的多个type(color, date, datetime, email, month, number, range, search, tel, time, url, week)
新增表单元素:
- datalist 定义选项列表。请与 input 元素配合使用该元素,来定义 input 可能的值。
- keygen 规定用于表单的密钥对生成器字段。
- output 定义不同类型的输出,比如脚本的输出。
新增表单属性:placehoder, required, min和max, step, height 和 width, autofocus, multiple
音频视频:
- audio 定义音频内容
- video 定义视频(video 或者 movie)
- source 定义多媒体资源 video 和 audio
- embed 定义嵌入的内容,比如插件。
- track 为诸如 video 和 audio 元素之类的媒介规定外部文本轨道。
canvas:
拖拽:drag
本地存储:localStorage、sessionStorage
新事件:onresize、ondrag、onscroll、onmousewheel、onerror、onplay、onpause
WebSocket
移除元素
acronym,applet,basefont ,big ,center,dir,font,frame,frameset,noframes,strike,tt
DOCTYPE的作用是什么
(1) 作用:告诉浏览器以什么样的模式来解析文档。DOCTYPE 不存在或格式不正确会导致文档以兼容模式呈现。
(2) 一般指定了之后会以标准模式来进行文档解析,否则就以兼容模式进行解析。
语义化标签的作用
- 去掉或样式丢失的时候能让页面呈现清晰的结构
- 方便其他设备解析(如屏幕阅读器、盲人阅读器、移动设备)以意义的方式来渲染网页
- 有利于SEO
- 便于团队开发和维护,遵循W3C标准,可以减少差异化
事件模型以及三种事件绑定方法
<details> <summary>点击展开</summary>现代浏览器事件模型二个过程:捕获;冒泡
事件捕获阶段。捕获指的是事件从html根元素向下传播到实际点击的目标元素(指在dom文档树中向下层),依次检查经过的节点是否绑定了该事件监听函数,如果有且该事件是设定在捕获阶段执行,便执行。
冒泡阶段:捕获阶段结束后,反向再检查一次,冒泡回根元素
现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段执行。
事件绑定方法:
- 行内事件,过时了,不建议使用,缺点行为与结构不分离
<button onclick="alert('ok')">Press me</button>
js中在元素节点上添加事件属性,缺点:重复定义后者会覆盖前者
document.querySelector('button').onclick=alert('ok')
addEventListener(eventName,function,useCaptrue),常用,可取消
const btn = document.querySelector('button'); function bgChange() { const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')'; document.body.style.backgroundColor = rndCol; } btn.addEventListener('click', bgChange); btn.removeEventListener('click', bgChange);
addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。默认是false,在冒泡阶段执行。
事件代理/事件委托 以及 优缺点
<details> <summary>点击展开</summary>> 事件委托本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理。
使用事件代理我们可以不必要为每一个子元素都绑定一个监听事件,这样减少了内存上的消耗。并且使用事件代理我们还可以实现事件的动态绑定,比如说新增了一个子节点,我们并不需要单独地为它添加一个监听事件,它所发生的事件会交给父元素中的监听函数来处理。
事件委托的优点:
- 减少内存消耗,不必为大量元素绑定事件
- 为动态添加的元素绑定事件
事件委托的缺点:
- 部分事件如 focus、blur 等无冒泡机制,所以无法委托。
- 事件委托有对子元素的查找过程,委托层级过深,可能会有性能问题
- 频繁触发的事件如 mousemove、mouseout、mouseover等,不适合事件委托
如何阻止事件冒泡:e.stopPropagation()
如何阻止事件默认行为:e.preventDefault();
js 延迟加载的方式有哪些
<details> <summary>点击展开</summary>> js 延迟加载,也就是等页面加载完成之后再加载 JavaScript 文件。 js 延迟加载有助于提高页面加载速度。
一般有以下几种方式:
- defer 属性
- async 属性
- 动态创建 DOM 方式
- 使用 setTimeout 延迟方法
- 让 JS 最后加载
defer 和 async 的区别
<details> <summary>点击展开</summary>- defer 属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未停止解析,这两个过程是并行的。当整个 document 解析完毕后再执行脚本文件,在 DOMContentLoaded 事件触发之前完成。
多个脚本按顺序执行。
async 属性表示异步执行引入的 JavaScript,与 defer 的区别在于,如果已经加载好,就会开始执行,也就是说它的执行仍然会阻塞文档的解析,只是它的加载过程不会阻塞。
多个脚本的执行顺序无法保证。
broswer
OSI七层模型
<details> <summary>点击展开</summary>物理层:通过媒介传输比特,确定机械及电气规范(比特Bit)
数据链路层:将比特组装成帧和点到点的传递(帧Frame)
网络层:负责数据包从源到宿的传递和网际互连(包PackeT)
传输层:提供端到端的可靠报文传递和错误恢复(段Segment)
会话层:建立、管理和终止会话(会话协议数据单元SPDU)
表示层:对数据进行翻译、加密和压缩(表示协议数据单元PPDU)
应用层:允许访问OSI环境的手段(应用协议数据单元APDU)
HTTP 状态码
<details> <summary>点击展开</summary>1. 1XX 信息性状态码
- 100 继续
- 101 切换协议
- 2XX 成功状态码
- 200 OK 成功处理了请求
- 204 No Content 请求处理成功,但没有资源可返回
- 206 Partial Content 请求资源的某一部分
- 3XX 重定向状态码
- 301 永久性重定向,表示请求的资源已被分配了新的 URI
- 302 临时性重定向,资源的 URL 已临时定位到其他位置
- 303 告诉客户端应该用另一个 URL 获取资源
- 304 表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但未满足条件的情况
- 4XX 客户端错误状态码
- 400 表示请求报文中存在语法错误
- 401 未授权
- 403 服务器拒绝了请求
- 404 服务器无法找到所请求的 URL
- 5XX 服务器错误状态码
- 500 内部服务器错误
- 502 错误网关
- 503 服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
- 504 响应超时
HTTP 与 HTTPS 的区别
<details> <summary>点击展开</summary>1. HTTP 传输的数据都是未加密的,也就是明文的,HTTPS 协议是由 HTTP 和 SSL 协议构建的可进行加密传输和身份认证的网络协议,比 HTTP 协议的安全性更高。
- HTTPS 协议需要 CA 证书,费用较高;
- 使用不同的链接方式,端口也不同,一般而言,HTTP 协议的端口为 80,HTTPS 的端口为 443;
HTTP/2.0 特性:
- 首部压缩
- 多路复用
- 二进制分帧
- 服务端推送
TCP 和 UDP 之间的区别
<details> <summary>点击展开</summary>TCP:传输控制协议 UDP:用户数据报协议
- TCP 是面向连接的,UDP 是无连接的即发送数据前不需要先建立链接;
- TCP 提供可靠的服务。也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付。
- TCP 是面向字节流,UDP 面向报文;
- TCP 只能是 1 对 1 的,UDP 支持 1 对 1,1 对多;
- TCP 的首部较大为 20 字节,而 UDP 只有 8 字节;
TCP 的三次握手和四次挥手
<details> <summary>点击展开</summary>三次握手:
第一次握手:客户端发送syn包(syn=1,seq=x)到服务器,并进入SYN_SENT状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
四次挥手
与建立连接的“三次握手”类似,断开一个TCP连接则需要“四次挥手”。
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
HTTP 请求的方式
<details> <summary>点击展开</summary>1. GET:请求指定的页面信息,并返回实体主体。
- HEAD:类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
- POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。
- PUT:从客户端向服务器传送的数据取代指定的文档的内容。
- DELETE:请求服务器删除指定的页面。
- CONNECT:HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
- OPTIONS:允许客户端查看服务器的性能。
- TRACE:回显服务器收到的请求,主要用于测试或诊断。
GET 和 POST 的区别
<details> <summary>点击展开</summary>两者本质上都是 TCP 链接
- get 参数通过 url 传递,post 放在请求体 (request body) 中。
- get 请求在 url 中传递的参数是有长度限制的,而 post 没有。
- get 比 post 更不安全,因为参数直接暴露在 url 中,所以不能用来传递敏感信息。
- get 请求只能进行 url 编码,而 post 支持多种编码方式。
- get 请求参数会被完整保留在浏览历史记录里,而 post 中的参数不会被保留。
- get 产生一个 TCP 数据包;post 产生两个 TCP 数据包。
对于 get 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据);
而对于 post,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)。
浏览器输入 URL 之后发生了什么
<details> <summary>点击展开</summary>> 参考链接:[在浏览器输入 URL 回车之后发生了什么(超详细版)](https://4ark.me/post/b6c7c0a2.html)
- DNS 解析
- TCP 连接
- 发送 HTTP 请求
- 服务器处理请求并返回 HTTP 报文
- 浏览器解析渲染页面
- 连接结束
localStorage,sessionStorage,cookie 的区别
<details> <summary>点击展开</summary>共同点:都是保存在浏览器端,同源限制。
区别:
(1) cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递
sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存
(2) 存储大小限制也不同
cookie数据不能超过4k
sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大(实际上指键值与键共10M)
(3) 数据有效期不同
sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;
localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;
cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭;
(4) 作用域不同
sessionStorage不在不同的浏览器窗口***享,即使是同一个页面;
localStorage 在所有同源窗口中都是共享的;
cookie也是在所有同源窗口中都是共享的
浏览器的同源政策
<details> <summary>点击展开</summary>一个域下的 js 脚本在未经允许的情况下,不能够访问另一个域的内容。这里的同源的指的是两个
域的协议、域名、端口号必须相同,否则则不属于同一个域。
同源政策主要限制了三个方面
第一当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。
第二当前域下的 js 脚本不能够操作访问其他域下的 DOM。
第三当前域下 ajax 无法发送跨域请求。
跨域方法全
<details> <summary>点击展开</summary>1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域
前端攻击
<details> <summary>点击展开</summary>XSS攻击(cross-site script)
XSS攻击形式:
主要是通过html标签注入,篡改网页,插入恶意的脚本,前端可能没有经过严格的校验直接就进到数据库,数据库又通过前端程序又回显到浏览器
攻击的目的:
攻击者可通过这种方式拿到用户的一些信息,例如cookie 获取敏感信息,甚至自己建网站,做一些非法的操作等;或者,拿到数据后以用户的身份进行勒索,发一下不好的信息等。
攻击防御
方法1:cookie中设置 HttpOnly 属性
方法2:首先前端要对用户输入的信息进行过滤,可以用正则,通过替换标签的方式进行转码或解码,例如<> 空格 & '' ""等替换成html编码
CSRF攻击(cross site request forgery,跨站请求伪造)
CSRF攻击形式:
CSRF也是一种网络攻击方式,通过伪装成站点用户进行攻击,
csrf攻击形式:攻击者盗用用户的身份信息,并以用户的名义进行发送恶意的请求等,例如发邮件,盗取账号等非法手段
- 攻击防御:
- 验证HTTP Referer字段
- 在请求地址中添加token并验证
- 在HTTP头中自定义属性并验证
浏览器缓存机制
<details> <summary>点击展开</summary>浏览器缓存分为:强缓存和协商缓存
在浏览器第一次发起请求时,本地无缓存,向 web 服务器发送请求,服务器端响应请求,浏览器端缓存。在第一次请求时,服务器会将页面最后修改时间通过 Last-Modified 标识由服务器发送给客户端,客户端记录修改时间;服务器还会生成一个 Etag,并发送给客户端。
强缓存 Expires 和 Cache-Control 的使用
强缓存是利用 http 头中的 Expires 和 Cache-Control 两个字段来控制的,用来表示资源的缓存时间。强缓存中,普通刷新会忽略它,但不会清除它,需要强制刷新。浏览器强制刷新,请求会带上Cache-Control:no-cache
和 Pragma:no-cache
Expires
Expires 的值是一个绝对时间的 GMT 格式的时间字符串。比如 Expires 值是:expires:Fri, 14 Apr 2017 10:47:02 GMT
。这个时间代表这这个资源的失效时间,只要发送请求时间是在 Expires 之前,那么本地缓存始终有效,则在缓存中读取数据。
缺点:
由于失效的时间是一个绝对时间,所以当服务器与客户端时间偏差较大时,误差很大,就会导致缓存混乱。
Cache-Control
Cache-Control 主要是利用该字段的 max-age
值来进行判断,它是一个相对时间,例如Cache-Control:max-age=3600
,代表着资源的有效期是 3600 秒。
Cache-Control 与 Expires 可以在服务端配置同时启用,同时启用的时候 Cache-Control 优先级高。
协商缓存
协商缓存就是由服务器来确定缓存资源是否可用,所以客户端与服务器端要通过某种标识来进行通信,从而让服务器判断请求资源是否可以缓存访问。
Etag 和 If-None-Match
Etag/If-None-Match
返回的是一个校验码。Etag
可以保证每一个资源是唯一的,资源变化都会导致Etag
变化。服务器根据浏览器发送的If-None-Match
值来判断是否命中缓存。
与Last-Modified
不一样的是,当服务器返回 304 (Not Modified) 的响应时,由于Etag
重新生成过,response header 中还会把这个Etag
返回,即使这个Etag
跟之前的没有变化。
Last-Modify / If-Modify-Since
浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-Modify是一个时间标识该资源的最后修改时间,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。当浏览器再次请求该资源时,request的请求头中会包含If-Modify-Since,该值为缓存之前返回Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。如果命中缓存,则返回304,并且不会返回资源内容,并且不会返回Last-Modify。
</details>CSS
CSS 盒模型
<details> <summary>点击展开</summary>盒模型总共包括4个部分:
- content:内容,容纳着元素的”真实“内容,例如文本,图像或是视频播放器
- padding:内边距
- border:边框
- margin:外边距
两种盒模型的区别:
W3C盒模型
box-sizing: content-box
(常用的是这种)W3C盒模型中,通过CSS样式设置的width的大小只是content的大小
IE盒模型
box-sizing: border-box
IE盒模型中,通过CSS样式设置的width的大小是content + padding + border的和
设置一个元素的背景颜色,背景颜色会填充哪些区域
border + padding + content
margin/padding 设置百分比是相对谁的
margin/padding设置百分比都是相对于父盒子的宽度(width属性)来计算量的
CSS 选择器优先级
<details> <summary>点击展开</summary>选择器按优先级先后排列:!important>内联>id>class = 属性 = 伪类 >标签 = 伪元素 > 通配符 \*
- important 声明 1,0,0,0
- ID 选择器 0,1,0,0
- 类选择器 0,0,1,0
- 伪类选择器 0,0,1,0
- 属性选择器 0,0,1,0
- 标签选择器 0,0,0,1
- 伪元素选择器 0,0,0,1
- 通配符选择器 0,0,0,0
伪类与伪元素的区别
<details> <summary>点击展开</summary>一般在css3中使用单冒号来表示伪类,用双冒号来表示伪元素。
伪类一般匹配的是元素的一些特殊状态,如hover、link ,active等,而伪元素一般匹配的特殊的位置,比如after、before等。 伪元素默认行内元素,css中其必须有content属性,就算是content:""也必须写,否则无效。伪元素可以用于添加文字,或者小图标。
box::before {
content:'';
}
CSS 中哪些属性可以继承
<details> <summary>点击展开</summary>每一个属性在定义中都给出了这个属性是否具有继承性,一个具有继承性的属性会在没有指定值的时候,会使用父元素的同属性的值来作为自己的值。
一般具有继承性的属性有,字体相关的属性,font-size和font-weight等。文本相关的属性,color和text-align等。
表格的一些布局属性、列表属性如list-style等。还有光标属性cursor、元素可见性visibility。
当一个属性不是继承属性的时候,通过将它的值设置为inherit来使它从父元素那获取同名的属性值来继承。
</details>BFC的概念,
BFC 即 Block Formatting Context (块格式化上下文), 是Web页面的可视化CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。
简单来说就是一个封闭的黑盒子,里面元素的布局不会影响外部。
float 的元素,display 是block
visibility: hidden
, opacity: 0
,display: none
opacity: 0,该元素隐藏起来了,但不会改变页面布局,并且,如果该元素已经绑定一些事件,如 click 事件,那么点击该区域,也能触发点击事件的;
visibility: hidden,该元素隐藏起来了,不会改变页面布局,但是不会触发该元素已经绑定的事件;
display: none,把元素隐藏起来,并且会改变页面布局,可以理解成在页面中把该元素删除掉一样。
CSS 实现垂直居中
<details> <summary>点击展开</summary>- 定位 + 负边距 元素绝对定位,top:50%,margin-top:-(高度/2)
高度不确定用transform:translateY(-50%)
父元素display:flex,align-items:center;
display: table-cell+vertical-align:middle;
div {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
margin塌陷问题
是在父级相对于浏览器进行定位时但子级没有相对于父级定位,子级相对于父级就像塌陷了一样,父子嵌套元素垂直方向的margin,父子元素是结合在一起的,他们两个会取其中最大的值
或者兄弟元素上下边距会重叠在一起,取较大值。
解决方法:触发bfc,绝对定位,float,overflow:hidden,display:inline-block
图片底部与父容器之间有一段空隙
原因:行内块级元素会和文字的基线对齐。
解决方法:
(1)给图片添加 vertical-align middle | top | bottom
(2) font-size:0
响应式设计
<details> <summary>点击展开</summary>- 媒体查询:@media screen and (min-width:800px){}
多列布局:column-count:3;flex布局;网格布局
响应式图像使用了picture元素的srcset和sizes 特性提供多种资源
使用百分号单位,或视口单位,或者vw加上绝对单位
<meta name="viewport" content="width=device-width,initial-scale=1">
常用单位px 像素 % 百分比 rem 和em vw和vh
<details> <summary>点击展开</summary>em
子元素字体大小的 em 是相对于父元素字体大小 元素的width/height/padding/margin用em的话是相对于该元素的font-size
rem
rem 是全部的长度都相对于根元素,根元素是谁?<html>元素。 通常做法是给html元素设置一个字体大小,然后其他元素的长度单位就为rem。
vw(Viewport Width)、vh(Viewport Height)是基于视图窗口的单位,是css3的一部分,基于视图窗口的单位,除了vw、vh还有vmin、vmax。
- vw:1vw 等于视口宽度的1%
- vh:1vh 等于视口高度的1%
- vmin: 选取 vw 和 vh 中最小的那个,即在手机竖屏时,1vmin=1vw
- vmax:选取 vw 和 vh 中最大的那个 ,即在手机竖屏时,1vmax=1vh
不得不说牛客这个编辑器只能显示1w字了。
还有一些js高级问题,react框架内容就不放上来了。
想要完整原项目,评论区给地址。
求个收藏不过分吧!
inline-block、inline 和 block 的区别;可替换元素
<details> <summary>点击展开</summary>Block 是块级元素,独占一行,能设置宽度,高度,margin/padding 水平垂直方向都有效。
Inline:设置 width 和 height 无效,margin 在竖直方向上无效,padding 在水平方向垂直方向都有效,前后无换行符
Inline-block:能设置宽度高度,margin/padding 水平垂直方向 都有效,前后无换行符
img 是可替换元素。
在 CSS 中,可替换元素(replaced element)的展现效果不是由 CSS 来控制的。一种外部对象,它们外观的渲染,是独立于 CSS 的。
例如 <iframe>
元素,可能具有自己的样式表,但它们不会继承父文档的样式。
典型的可替换元素有: iframe> video> embed> img>
</details>#高频知识点汇总##学习路径#