JS
数据类型
面试官:JavaScript中什么是基本数据类型什么是引用数据类型?以及各个数据类型是如何存储的?⭐⭐⭐⭐⭐
答:
基本数据类型有
- Number
- String
- Boolean
- Null
- Undefined
- Symbol(ES6新增数据类型)
- bigInt
引用数据类型统称为Object类型,细分的话有
- Object
- Array
- Date
- Function
- RegExp
基本数据类型的数据直接存储在栈中,引用数据类型的数据存储在堆中,在栈中保存数据的引用地址
栈内存是自动分配内存的,堆内存是动态分配内存的,不会自动释放
判断数据类型
面试官:判断数据类型有几种方法⭐⭐⭐⭐⭐
- typeof
- 缺点:typeof null的值为Object,无法分辨是null还是Object
let a ="hello"
console.log(typeof a)
- instanceof
- 缺点:只能判断对象是否存在于目标对象的原型链上
- 原理: 就是查找目标对象的原型链
let test = new String("hello")
console.log(test instanceof String)
- constructor
console.log(a.constructor == String)
- Object.prototype.toString.call()
- 一种最好的基本类型检测方式 Object.prototype.toString.call() ;它可以区分 null 、 string 、boolean 、 number 、 undefined 、 array 、 function 、 object 、 date 、 math 数据类型。
- 缺点:不能细分为谁谁的实例
function _typeof(item){
let res = Object.prototype.toString.call(item)
res = res.split(" ")[1]
res = res.substr(0,res.length-1)
return res
}
- 纯数组方法
let arr = new Array(1,2,3)
console.log(Array.isArray(arr))
instanceof原理⭐⭐⭐⭐⭐
- instanceof原理实际上就是查找目标对象的原型链
面试官:==
和===
有什么区别⭐⭐⭐⭐⭐
===是严格意义上的相等,会比较两边的数据类型和值大小
- 数据类型不同返回false
- 数据类型相同,但值大小不同,返回false
==是非严格意义上的相等,
- 两边类型相同,比较大小
- 两边类型不同,根据下方表格,再进一步进行比较。
- Null == Undefined ->true
- String == Number ->先将String转为Number,在比较大小
- Boolean == Number ->现将Boolean转为Number,在进行比较
- Object == String,Number,Symbol -> Object 转化为原始类型
手写call apply bind
面试官:手写call、apply、bind⭐⭐⭐⭐⭐
- call和apply实现思路主要是:
- 判断是否是函数调用,若非函数调用抛异常
- 通过新对象(context)来调用函数
- 给context创建一个fn设置为需要调用的函数
- 结束调用完之后删除fn
- bind实现思路
- 判断是否是函数调用,若非函数调用抛异常
- 返回函数
- 判断函数的调用方式,是否是被new出来的
- new出来的话返回空对象,但是实例的__proto__指向_this的prototype
- 判断函数的调用方式,是否是被new出来的
- 完成函数柯里化
- Array.prototype.slice.call()
/*call apply bind的区别
三兄弟接收的第一个参数都是要绑定this指向
apply的第二个参数是一个数组,call和bind的第二个及之后的参数作为函数实例
bind不会立即调用,其他两个会立即调用*/
const obj = {name:'dzy'}
function foo(a,b){
console.log(this.name,a,b)
}
//手写call apply与call相同,只是第二个参数变成数组了
Function.prototype.myCall = function(obj,...args){
obj.fn = this
obj.fn(...args);
delete obj.fn
}
foo.myCall(obj,1,2)
//手写bind
Function.prototype.myBind = function(obj,...args){
var that = this
return function(){
that.apply(obj,args)
}
}
foo.myBind(obj,1,2)
手写new
面试官:字面量创建对象和new创建对象有什么区别,new内部都实现了什么,手写一个new⭐⭐⭐⭐⭐
答:
字面量:
- 字面量创建对象更简单,方便阅读
- 不需要作用域解析,速度更快
new内部:
- 创建一个新对象
- 使新对象的__proto__指向原函数的prototype
- 改变this指向(指向新的obj)并执行该函数,执行结果保存起来作为result
- 判断执行函数的结果是不是null或Undefined,如果是则返回之前的新对象,如果不是则返回result
//创建一个新对象
function myNew(fn,...args){
//创建一个空对象
let obj = {}
//使新对象的proto指向原函数的prototype
obj._proto_ = fn.prototype
//改变this指向 保存结果作为result
let result = fn.apply(fn,args)
//判断执行结果是不是null或undefined 如果是则返回之前的新对象 不是则返回result
return result instanceof Object? result:obj
}
闭包
面试官:什么是闭包?闭包的作用?闭包的应用?⭐⭐⭐⭐⭐
- 什么是闭包
- 闭包的一大特性就是内部函数总是可以访问所在外部函数所声明的参数和变量
- 函数嵌套函数
- 内部函数引用外部函数的局部变量
- 闭包的作用
- 实现函数外部访问私有变量
- 避免全局变量的污染 实现封装
- 希望一个变量长期驻扎在内存中
- 闭包的应用
- 设计模式中的单例模式
- for循环中的保留i的操作
- 函数柯里化
- 防抖和节流
//函数防抖
//一个需要频繁触发的函数,在规定时间内只让最后一次生效,前面的不生效
function debounce(fn,delay){
var timer = null
return function(){
clearTimeout(timer)
timer = setTimeou(()=>{
fn.apply(this)
},delay)
}
}
//<button id="btn">click</button>
var oBtn = document.getElementById('btn')
oBtn.onclick = debounce(function(){
console.log(Date.now())
console.log(this)
},1000)
//节流函数
//一个函数执行一次后,只有大于设定的执行周期才会执行第二次,出于性能优化的角度,在规定时间只让函数触发第一次生效,后面的不生效
function throttle(fn,delay){
var startTime =0
return function(){
var nowTime = Date.now()
if(nowTime - startTime >delay){
fn.call(this)
startTime = nowTime
}
}
}
document.onmousemove = throttle(function(){
console.log(Date.now())
console.log(this)
},1000)
- 闭包的缺点
- 会出现内存泄漏的问题
function fn1(val){
var a = 20
return function(){
a++
console.log(a,val)
}
}
var f = fn1
f()
f()
f()
内存泄露、垃圾回收机制
面试官:什么是内存泄漏⭐⭐⭐⭐⭐
答:
内存泄露是指不再用的内存没有被及时释放出来,导致该段内存无法被使用就是内存泄漏
面试官:为什么会导致的内存泄漏⭐⭐⭐⭐⭐
答:
内存泄漏指我们无法在通过js访问某个对象,而垃圾回收机制却认为该对象还在被引用,因此垃圾回收机制不会释放该对象,导致该块内存永远无法释放,积少成多,系统会越来越卡以至于崩溃
面试官:垃圾回收机制都有哪些策略?⭐⭐⭐⭐⭐
答:
- 标记清除法
- 垃圾回收机制获取根并标记他们,然后访问并标记所有来自它们的引用,然后在访问这些对象并标记它们的引用…如此递进结束后若发现有没有标记的(不可达的)进行删除,进入执行环境的不能进行删除
- 引用计数法
- 当声明一个变量并给该变量赋值一个引用类型的值时候,该值的计数+1,当该值赋值给另一个变量的时候,该计数+1,当该值被其他值取代的时候,该计数-1,当计数变为0的时候,说明无法访问该值了,垃圾回收机制清除该对象
- 缺点: 当两个对象循环引用的时候,引用计数无计可施。如果循环引用多次执行的话,会造成崩溃等问题。所以后来被标记清除法取代。
原型和原型链
面试官:什么是原型?什么是原型链?如何理解⭐⭐⭐⭐⭐
答:
原型: 原型分为隐式原型和显式原型,每个对象都有一个隐式原型,它指向自己的构造函数的显式原型。
原型链
- 当从⼀个对象那⾥调取属性或⽅法时,如果该对象⾃身不存在这样的属性或⽅法,就会去⾃⼰关联的prototype对象那⾥寻找,如果prototype没有,就会去prototype关联的前辈prototype那⾥寻找,如果再没有则继续查找Prototype.Prototype引⽤的对象,依次类推,直到Prototype.….Prototype为undefined(Object的Prototype就 是undefined)从⽽形成了所谓的"原型链"
继承
面试官:说一说 JS 中的常用的继承方式有哪些?以及各个继承方式的优缺点。⭐⭐⭐⭐⭐
原型继承、组合继承、寄生组合继承、ES6的extend
原型继承
// ----------------------方法一:原型继承
// 原型继承
// 把父类的实例作为子类的原型
// 缺点:子类的实例共享了父类构造函数的引用属性 不能传参
var person = {
friends: ["a", "b", "c", "d"]
}
var p1 = Object.create(person)
p1.friends.push("aaa")//缺点:子类的实例共享了父类构造函数的引用属性
console.log(p1);
console.log(person);//缺点:子类的实例共享了父类构造函数的引用属性
组合继承
// ----------------------方法二:组合继承
// 在子函数中运行父函数,但是要利用call把this改变一下,
// 再在子函数的prototype里面new Father() ,使Father的原型中的方法也得到继承,最后改变Son的原型中的constructor
// 缺点:调用了两次父类的构造函数,造成了不必要的消耗,父类方法可以复用
// 优点可传参,不共享父类引用属性
function Father(name) {
this.name = name
this.hobby = ["篮球", "足球", "乒乓球"]
}
Father.prototype.getName = function () {
console.log(this.name);
}
function Son(name, age) {
Father.call(this, name)
this.age = age
}
Son.prototype = new Father()
Son.prototype.constructor = Son
var s = new Son("ming", 20)
console.log(s);
寄生组合继承
// ----------------------方法三:寄生组合继承
function Person(uName,age){
this.name = uName;
this.age = age;
}
//父类下的原型方法
Person.prototype.say = function(){
console.log("hello");
}
function Teacher(uName,age,school){
//调用父类的Person.call() 继承属性
Person.call(this,uName,age)
this.school = school;
}
//继承父类的方法
Teacher.prototype = new Person();
//修改原型对象下的方法constructor指向Teacher
Teacher.prototype.constructor = Teacher
var teacher1 = new Teacher("张爱文",40,"黑工程")
console.log(teacher1.name,teacher1.age,teacher1.school);
teacher1.say();
console.log(teacher1.constructor);
extend
// ----------------------方法四:ES6的extend(寄生组合继承的语法糖)
// 子类只要继承父类,可以不写 constructor ,一旦写了,则在 constructor 中的第一句话
// 必须是 super 。
class Son3 extends Father { // Son.prototype.__proto__ = Father.prototype
constructor(y) {
super(200) // super(200) => Father.call(this,200)
this.y = y
}
}
深拷贝和浅拷贝
- 浅拷贝/浅克隆:将一个变量赋值给另一个变量 修改其中一个,另外一个也跟着修改 (引用数据类型)
- 深拷贝/深克隆:将一个变量赋值给另一个变量 修改其中一个,另外一个不会修改 (基本数据类型)
手写浅拷贝深拷贝⭐⭐⭐⭐⭐
<script>
var person = {
name: 'zs',
age: 18,
school: {
name: 'heida',
count: 10000
}
}
// 封装深克隆方法
function deepClone(obj){
// 定义一个空对象 用于接收克隆后的结果
var tmp = {}
// 循环对象中的每一项
for(key in obj){
// 判断一下当前对象的属性值是基本数据类型还是引用数据类型
if(typeof(obj[key]) == 'object' ) {
// 引用数据类型 递归调用函数本身 在实现一次深克隆的过程
tmp[key] = deepClone(obj[key])
} else {
// 基本数据类型直接复制
tmp[key] = obj[key]
}
}
return tmp
}
var p1 = deepClone(person)
console.log(p1)
p1.school.count = 9999
console.log(p1)
console.log(person)
</script>
单线程,同步异步,promise
面试官:为什么JS是单线程的?⭐⭐⭐⭐⭐
答:
因为JS里面有可视的Dom,如果是多线程的话,这个线程正在删除DOM节点,另一个线程正在编辑Dom节点,导致浏览器不知道该听谁的
面试官:说说 Promise 的原理?你是如何理解 Promise 的?⭐⭐⭐⭐⭐
可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统⼀的接⼝,使得控制异步操作更加容易
对promise的理解
- 一个promise可能有三种转态,pending(进行中) ,fulfilled(已成功)和rejected
- 一个promise的状态只可能从 "等待" 转到 "成功" 态或者 "失败"态,不能逆向转换,同时 "成功" 态和 "失败" 态不能相互转换
- promise必须实现then方法(可以说then就是promise的核心)而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟她们被定义时的顺序一致
- then方法接收两个参数,第一个参数是成功时的回调,另一个是失败时的回调
- 解决函数的回调嵌套问题
promise的缺点
首先,无法取消promise,一旦新建它就会立即执行,无法中途取消
其次,如果不设置回调函数,promise内部抛出的错误,不会反应到外部
第三,当处于pending状态时,无法得知目前进展到哪一个阶段
promise的一些方法
promise.all([p1,p2,p3]) 都执行完再执行
promise.race()最快的那个执行完了就执行
面试官:以下代码的执行顺序是什么⭐⭐⭐⭐⭐
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
async1()
console.log('script start')
//执行到await时,如果返回的不是一个promise对象,await会阻塞下面代码(当前async代码块的代码),会先执行async外的同步代码(在这之前先看看await中函数的同步代码,先把同步代码执行完),等待同步代码执行完之后,再回到async内部继续执行
//执行到await时,如果返回的是一个promise对象,await会阻塞下面代码(当前async代码块的代码),会先执行async外的同步代码(在这之前先看看await中函数的同步代码,先把同步代码执行完),等待同步代码执行完之后,再回到async内部等promise状态达到fulfill的时候再继续执行下面的代码
//所以结果为
//async1 start
//async2
//script start
//async1 end
宏任务和微任务
面试官:宏任务和微任务都有哪些⭐⭐⭐⭐⭐
宏任务(macrotask) | 微任务(microtask) | |
---|---|---|
由谁发起 | 宿主(Node、浏览器) | JS引擎 |
具体事件 | 1.script(可以理解为外层同步代码) 2.setTimeout/setInterval 3.UIendering/UI事件 4.postMessage,MessageChannel5. setImmediate,I/O(Node.js) | 1.Promise 2. MutaionObserver 3. Object.observe(已废弃;Proxy 对象替代) 4.process.nextTick(Node.js) |
谁先运行 | 后运行 | 先运行 |
会触发新一轮task吗 | 会 | 不会 |
面试官:宏任务和微任务都是怎样执行的⭐⭐⭐⭐⭐
答:
- 执行宏任务script,
- 进入script后,所有的同步任务主线程执行
- 所有宏任务放入宏任务执行队列
- 所有微任务放入微任务执行队列
- 先清空微任务队列,
- 再取一个宏任务,执行,再清空微任务队列
- 依次循环
例题1
setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3')
});
console.log('4');
new Promise(function(resolve){
console.log('5');
resolve();
}).then(function(){
console.log('6')
});
setTimeout(function(){
console.log('7')
});
function bar(){
console.log('8')
foo()
}
function foo(){
console.log('9')
}
console.log('10')
bar()
首先浏览器执行Js代码由上至下顺序,遇到setTimeout,把setTimeout分发到宏任务Event Queue中
new Promise属于主线程任务直接执行打印2
Promis下的then方法属于微任务,把then分到微任务 Event Queue中
console.log(‘4’)属于主线程任务,直接执行打印4
又遇到new Promise也是直接执行打印5,Promise 下到then分发到微任务Event Queue中
又遇到setTimouse也是直接分发到宏任务Event Queue中,等待执行
console.log(‘10’)属于主线程任务直接执行
遇到bar()函数调用,执行构造函数内到代码,打印8,在bar函数中调用foo函数,执行foo函数到中代码,打印9
主线程中任务执行完后,就要执行分发到微任务Event Queue中代码,实行先进先出,所以依次打印3,6
微任务Event Queue中代码执行完,就执行宏任务Event Queue中代码,也是先进先出,依次打印1,7。
最终结果:2,4,5,10,8,9,3,6,1,7
var let const的区别
面试官:var let const 有什么区别⭐⭐⭐⭐⭐
答:
- var
- var声明的变量可进行变量提升,let和const不会 (变量提升:通过var定义变量,会在当前作用域的顶端,提升只是将变量声明提升到变量所在的变量范围的顶端)
- var可以重复声明
- var在非函数作用域中定义是挂在到window上的
- let
- let声明的变量只在局部起作用
- let防止变量污染
- 不可重复声明
- const
- 具有let的所有特征
- 不可被改变
- 如果使用const声明的是对象的话,是可以修改对象里面的值的
箭头函数
面试官:箭头函数和普通函数的区别?箭头函数可以当做构造函数 new 吗?⭐⭐⭐⭐⭐
- 箭头函数是普通函数的简写,但是它不具备很多普通函数的特性
- 第一点,this指向问题,箭头函数的this指向它定义时所在的对象,而不是调用时所在的对象
- 不会进行函数提升
- 没有arguments对象,不能使用arguments,如果要获取参数的话可以使用rest(...)运算符
- 不能new
- 没有自己的this,不能调用call和apply
- 没有prototype,new关键字内部需要把新对象的_proto_指向函数的prototype
跨域
面试官:跨域的方式都有哪些?他们的特点是什么 ⭐⭐⭐⭐⭐
JSONP⭐⭐⭐⭐⭐
-
jsonp的原理就是利用<script>标签没有跨域限制,通过<script>标签src属性,
发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,
返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。
-
JSONP
只能get请求
源码:
function addScriptTag(src) {
var script = document.createElement("script")
script.setAttribute('type','text/javascript')
script.src = src
document.appendChild(script)
}
// 回调函数
function endFn(res) {
console.log(res.message);
}
// 前后端商量好,后端如果传数据的话,返回`endFn({message:'hello'})`
CORS
⭐⭐⭐⭐⭐
- CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
- 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
- CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
计算机网络原理
三次握手四次挥手
面试官:讲一讲三次握手四次挥手,为什么是三次握手四而不是两次握手?⭐⭐⭐⭐⭐
- 客户端和服务端之间通过三次握手建立连接,四次挥手释放连接
- 三次握手,客户端先向服务端发起一个SYN包,进入SYN_SENT状态,服务端收到SYN后,给客户端返回一个ACK+SYN包,表示已收到SYN,并进入SYN_RECEIVE状态,最后客户端再向服务端发送一个ACK包表示确认,双方进入establish状态。
- 之所以是三次握手而不是两次,是因为如果只有两次,在服务端收到SYN后,向客户端返回一个ACK确认就进入establish状态,万一这个请求中间遇到网络情况而没有传给客户端,客户端一直是等待状态,后面服务端发送的信息客户端也接受不到了。
- 三次握手,客户端先向服务端发起一个SYN包,进入SYN_SENT状态,服务端收到SYN后,给客户端返回一个ACK+SYN包,表示已收到SYN,并进入SYN_RECEIVE状态,最后客户端再向服务端发送一个ACK包表示确认,双方进入establish状态。
- 四次挥手,首先客户端向服务端发送一个FIN包,进入FIN_WAIT1状态,服务端收到后,向客户端发送ACK确认包,进入CLOSE_WAIT状态,然后客户端收到ACK包后进入FIN_WAIT2状态,然后服务端再把自己剩余没传完的数据发送给客户端,发送完毕后在发送一个FIN+ACK包,进入LAST_ACK(最后确认)状态,客户端收到FIN+ACK包后,再向服务端发送ACK包,在等待两个周期后在关闭连接
- 之所以等待两个周期是因为最后客户端发送的ACK包可能会丢失,如果不等待2个周期的话,服务端在没收收到ACK包之前,会不停的重复发送FIN包而不关闭,所以得等待两个周期
状态码
面试官:说说你知道的状态码⭐⭐⭐⭐⭐
- 2开头的表示成功
- 一般见到的就是200
- 3开头的表示重定向
- 301永久重定向
- 302临时重定向
- 304表示可以在缓存中取数据(协商缓存)
- 4开头表示客户端错误
- 403跨域
- 404请求资源不存在
- 5开头表示服务端错误
- 500
http
面试官:https和http有什么区别,https的实现原理?⭐⭐⭐⭐⭐
- http无状态无连接,而且是明文传输,不安全
- https传输内容加密,身份验证,保证数据完整性
- https实现原理⭐⭐⭐⭐⭐
- 首先客户端向服务端发起一个随机值,以及一个加密算法
- 服务端收到后返回一个协商好的加密算法,以及另一个随机值
- 服务端在发送一个公钥CA
- 客户端收到以后先验证CA是否有效,如果无效则报错弹窗,有过有效则进行下一步操作
- 客户端使用之前的两个随机值和一个预主密钥组成一个会话密钥,在通过服务端传来的公钥加密把会话密钥发送给服务端
- 服务端收到后使用私钥解密,得到两个随机值和预主密钥,然后组装成会话密钥
- 客户端在向服务端发起一条信息,这条信息使用会话秘钥加密,用来验证服务端时候能收到加密的信息
- 服务端收到信息后返回一个会话秘钥加密的信息
- 都收到以后SSL层连接建立成功
localStorage SessionStorage cookie session
面试官:localStorage、SessionStorage、cookie、session 之间有什么区别⭐⭐⭐⭐⭐
- 共同点:都是保存在浏览器端,且同源的。
- 区别:
- 1. cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器
- 和服务器间来回传递。⽽sessionStorage和localStorage不会⾃动把数据发给服
- 务器,仅在本地保存。 cookie数据还有路径(path)的概念,可以限制cookie
- 只属于某个路径下。
- 2. 存储⼤⼩限制也不同,cookie数据不能超过4k,同时因为每次http请求都会携
- 带cookie,所以cookie只适合保存很⼩的数据,如会话标识。sessionStorage
- 和localStorage虽然也有存储⼤⼩的限制,但⽐cookie⼤得多,可以达到5M或
- 更⼤。
- 3. ⽣命周期不同,sessionstorage:关闭浏览器就删除, localstorage:⼀直有,除
- ⾮⼿动删除,Cookie:可以设置过期时间 sessionStorage:仅在当前浏览器窗
- ⼝关闭前有效,⾃然也就不可能持久保持;localStorage:始终有效,窗⼝或浏
- 览器关闭也⼀直保存,因此⽤作持久数据;cookie只在设置的cookie过期时间
- 之前⼀直有效,即使窗⼝或浏览器关闭。
- 4. 作⽤域不同, sessionStorage不在不同的浏览器窗⼝中共享,即使是同⼀个⻚
- ⾯;localStorage在所有同源窗⼝中都是共享的;
Get和Post的区别
面试官:Get和Post的区别⭐⭐⭐⭐⭐
- 冪等/不冪等(可缓存/不可缓存)
- get请求是冪等的,所以get请求的数据是可以缓存的
- 而post请求是不冪等的,查询查询对数据是有副作用的,是不可缓存的
- 传参
- get传参,参数是在url中的
- 准确的说get传参也可以放到body中,只不过不推荐使用
- post传参,参数是在请求体中
- 准确的说post传参也可以放到url中,只不过不推荐使用
- get传参,参数是在url中的
- 安全性
- get较不安全
- post较为安全
- 准确的说两者都不安全,都是明文传输的,在路过公网的时候都会被访问到,不管是url还是header还是body,都会被访问到,要想做到安全,就需要使用https
- 参数长度
- get参数长度有限,是较小的
- 准确来说,get在url传参的时候是很小的
- post传参长度不受限制
- get参数长度有限,是较小的
- 发送数据
- post传参发送两个请求包,一个是请求头,一个是请求体,请求头发送后服务器进行验证,要是验证通过的话就会给客户端发送一个100-continue的状态码,然后就会发送请求体
- 字符编码
- get在url上传输的时候只允许ASCII编码
http缓存
面试官:讲讲http缓存⭐⭐⭐⭐⭐
- 强缓存和协商缓存
- 强缓存: 客户端和代理服务区都可以缓存该资源
- 客户端在有效期内,如果有请求该资源的需求的话就直接读取缓存,code200即使用户做了刷新操作,也不向服务器发起http请求
- 协商缓存: 请求资源时,把用户本地该资源的 etag 同时带到服务端,服务端和最新资源做对比。
- 如果资源没更改,返回304,浏览器读取本地缓存。
- 如果资源有更改,返回200,返回最新的资源。
tcp和udp的区别
面试官:tcp
和udp
有什么区别⭐⭐⭐⭐⭐
- 连接方面
- tcp面向连接,udp不需要连接
- tcp需要三次握手四次挥手请求连接
- tcp面向连接,udp不需要连接
- 可靠性
- tcp是可靠传输;一旦传输过程中丢包的话会进行重传
- udp是不可靠传输,但会最大努力交付
- 工作效率
- UDP实时性高,比TCP工作效率高
- 因为不需要建立连接,更不需要复杂的握手挥手以及复杂的算法,也没有重传机制
- UDP实时性高,比TCP工作效率高
- 是否支持多对多
- TCP是点对点的
- UDP支持一对一,一对多,多对多
- 首部大小
- tcp首部占20字节
- udp首部占8字节
浏览器输入回车
面试官:在浏览器地址栏里输入一个地址回车⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
- 浏览器发送get请求 请求转发到DNS服务器 DNS解析域名 转发至相应IP服务器
- 在服务器端由 Apache这样的Web Server来接受请求
- 浏览器接收响应的结果 语法检查 有错误提示 没错误直接渲染
- 浏览器渲染
- 浏览器获取html文档解析成dom树
- 处理css标记 构成层叠样式表模型cssom
xss csrf
什么是xss?什么是csrf?⭐⭐⭐⭐⭐
- xss脚本注入
- 不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)。
- 防御
- 编码:对用户输入的数据进行HTML Entity 编码。把字符转换成 转义字符。Encode的作用是将$var等一些字符进行转化,使得浏览器在最终输出结果上是一样的。
- 过滤:移除用户输入的和事件相关的属性。
- csrf跨域请求伪造
- 在未退出A网站的前提下访问B,B使用A的cookie去访问服务器
- 防御:token,每次用户提交表单时需要带上token(伪造者访问不到),如果token不合法,则服务器拒绝请求
回流重绘
面试官:什么是重排(回流) 什么是重绘?⭐⭐⭐⭐⭐
- 重排(回流)
- dom的变化影响到了元素的几何属性,浏览器会重新计算元素的几何属性
- 重绘
- 在一个元素的外观被改变时改变布局,单单改变元素的外观不会引起网页重新生成布局,当浏览器完成重排之后,将会重新绘制收到此次重排影响的部分重排和重绘会破坏用户体验,让ui展示变得迟缓,相比之下重排的性能影响更大如果无法避免,宁可选择代价更小的重绘重绘不一定会出现重排,重排必然会出现重绘
-
如何避免重绘或者重排
尽量减少dom操作
集中改变样式,通过改变class的方式集中改变样式
冒泡和捕捉
事件冒泡和事件捕捉有什么区别⭐⭐⭐⭐⭐
- 事件冒泡
-
事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点,如果单机了页面的div元素,那么这个click事件沿dom树向上传播,在每一级节点上都会发生先捕获后冒泡
-
- 事件捕捉
- 在addEventListener中的第三属性设置为true
- 从上至下(祖宗到儿子)执行
函数柯里化原理⭐⭐⭐⭐⭐
js常见的设计模式⭐⭐⭐⭐⭐
- 单例模式、工厂模式、构造函数模式、发布订阅者模式、迭代器模式、代理模式
- 单例模式
-
不管创建多少个对象都只有一个实例
-
- 工厂模式
-
代替new创建一个对象,且这个对象想工厂制作一样,批量制作属性相同的实例对象(指向不同)
-
-
构造函数模式
-
发布订阅者模式
-
代理模式
-
迭代器模式
JS性能优化的方式⭐⭐⭐⭐⭐
- 垃圾回收
- 闭包中的对象清楚
- 防抖节流
- 分批加载(setInterval,加载10000个节点)
- 事件委托
- 少用with
- requestAnimationFrame的使用
- script标签中的defer和async
- CDN
Vue
Vue双向绑定
- vue双向数据绑定是通过数据劫持结合发布订阅者模式的方式来实现的,也就是说数据和视图同步,数据发生变化,试图跟着变化,数据也随之发生改变
- 核心:关于vue的数据绑定,其核心是 Object.defineProperty()方法
- 介绍一下Object.defineProperty()方法
(1)Object.defineProperty(obj, prop, descriptor) ,这个语法内有三个参数,分别为 obj (要定义其上属性的对象) prop (要定义或修改的属性) descriptor (具体的改变方法)
(2)简单地说,就是用这个方法来定义一个值。当调用时我们使用了它里面的get方法,当我们给这个属性赋值时,又用到了它里面的set方法