前端2-4年开发经验常见面试题汇总

2024-06-04 2792阅读

常见面试题

http协议

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。常见的网络协议有:IP TCP SMTP POP3 HTTPS HTTP

http1.0,1.1,2.0,3.0的区别

HTTP协议又叫做超文本传输协议,是一种用于在web浏览器和web服务器之间交换数据的应用层协议,http协议到目前为止,所有的版本可以分为http0.9,1.0,1.1,2.0,3.0,其中普遍应用的是1.1版本,正在推进是2.0版本,以及未来的3.0版本,

http1.0:HTTP1.0规定浏览器和服务器保持短连接,浏览器每次请求都需要与服务器建立一个TCP连接,http1.0还规定下一个请求必须在前一个请求响应到达之前才能发送,如果前一个请求的响应一直不到达,那么下一个请求就不发送,后面的请求就都阻塞了,所以http1.0存在请求的队头阻塞,http1.0还不支持断点续传,每次都会传送全部的页面和数据,在只需要部分数据的情况下就会浪费多余带宽。

http1.1:不等待响应,直接发送下一个请求,HTTP1.1解决了1.0版本存在的问题,它可以保持长连接,避免每次请求都要重复建立TCP连接,提高了网络的利用率,HTTP1.1可以使用管道传输,支持多个请求同时发送,但服务器还是按照顺序先回应前面的请求,在回应后面的请求,如果前面的回应特别慢,后面就会有许多请求排队等着处理,所以http1.1还是存在响应的队头阻塞问题,另外http1.1已经可以断点续传。

http2.0:HTTP2.0是http协议的第一个主要修订版,它与前面的版本用于传递数据的方法有很大的差异,http2.0会压缩头部,如果同时有多个请求其投不一样或相似,那么协议会消除重复部分,http2.0将请求和响应消息编码为二进制,而不再使用之前的纯文本消息,增加了数据传输的效率,http2.0可以在一个TCP连接中并发多个请求或回应,而不用按照顺序一一对应,从而彻底解决了HTTP层面队头阻塞的问题,大幅度提高了连接的利用率,HTTP2.0还在一定程度上改善了传统的请求应答工作模式,服务端不再是被动地响应,而是可以主动向客户端发送消息,推送额外的资源,http2.0虽然通过多个请求复用一个TCP连接解决了HTTP的对头阻塞,但是一旦发生丢包,就会阻塞住所有的http请求,这就属于TCP层队头阻塞,为了解决这个问题HTTP3.0直接放弃使用TCP。

http3.0:HTTP3.0直接放弃使用TCP,将传输层协议改成UDP,但是因为UDP是不可靠传输,所以这就需要QUIC实现可靠机制,QUIC全称“快速UDP互联网连接”,是由Google提出的使用UDP进行多路并发传输的协议,QUIC有自己的一套机制可以保证传输的可靠性的,当某一对请求响应发生丢包时,只会阻塞当前的请求响应,其他请求响应不会受到影响,因此完全不存在队头阻塞问题,http3.0使用了UDP作为传输层协议,能够减少三次握手的时间延迟,从而达到快速建立连接的效果,此外QUIC协议可以使用连接ID来标记通信的两个端点,即使移动设备的网络发送变化,导致IP地址变化了,只要还有连接ID和TLS密钥等上下文信息,就可以复用原连接,从而实现连接迁移

闭包

1,背景:在函数内部定义的变量是局部变量,局部变量没办法直接修改;局部变量使用完毕后,会被JS垃圾回收清理

2,如果形成了闭包,局部变量就不会被清理

3,闭包的基本结构:函数嵌套函数;外层函数有局部变量;里层函数使用到了外层函数的局部变量;里层函数可以反复被调用

function wai(){
    var count=0;
    return function li(){//里函数返回给外部,里函数生命周期得到延续
        count++;//这里的局部变量count也被延续
        console.log(count);
    }
}
var res=wai();
res();
res();

闭包和递归的区别:

相同点:都是函数;在函数的内部都会调用函数,都是低效率;
不同点:
		1,闭包相当于函数的返回值为另一个函数,而递归则相当于函数的参数为另一个函数;
        2,闭包函数调用的内部函数需要引用外部函数的变量、参数;递归函数是自己调用自己,不需要引用变量参数
        3,闭包函数调用一次结束调用,递归函数在满足递归条件时需要多次调用;
        4,闭包函数会比其他函数更占内存。

js中堆和栈的区别

变量接受的是基本数据类型,则存储它的值。存储在栈里面。

变量接受的是引用数据类型,则存储它的地址。值则存储在堆里面。

深拷贝和浅拷贝

1,浅拷贝只能拷贝一层,如果第一层是基本数据类型,可以用浅拷贝

注:如果第一层包含了复杂数据类型,复杂数据类型的赋值是引用关系,所以还是会互相影响

//浅拷贝,直接赋值会影响原数组
var arr1=[11,22,33,44]
var arr2=arr1
arr2[0]=6666
console.log(arr1);//此时结果是arr1=[6666,22,33,44]
//深拷贝
var arr1=[11,22,33,44]
var arr2=[]
for(var i=0;i
    arr2[i]=arr1[i]
}
arr2[0]=666
console.log(arr1,arr2)//此时结果是arr1=[11,22,33,44]  arr2=[666,22,33,44]
{}}计算和处理props或$emit的传值{str}},从而实现了数据双向绑定原理。
    for(var j=i+1;j
        if(arr[i]==arr[j]){
            arr.splice(j,1)
            len--;
            j--;
        }
    }
}
console.log(arr);

    color:['red','green']
}
const oldArrayProto=Array.prototype;
const newArrProto=Object.create(oldArrayProto);
['push','pop','shift','unshift','splice'].forEach(methodName={
    newArrProto[methodName]=function(){
        console.log('数组视图更新');
        oldArrayProto[methodName].call(this,...arguments);
    }
})
observer(data);
function observer(target){
    if(typeof target !=='object' || target === null){return target;}
    if(Array.isArray(target)){target._proto_=newArrProto};
    /*for(let key in target){
        defineReactive(target,key,target[kery])
    }*/
}
//function defineReactive(target,key,value){...}
console.log(data.colors.push)//此时打印出来的数组push方法是上面改写后的push方法
data.colors.push('blue')
 name: 'lx' }, { age: 23 }]
    for (const val of list) {
      console.log(val) // 输出{ name: 'lx' }, { age: 23 }
      for (const key in val) {
        console.log(val[key]) // 输出 lx,23
      }
    }
总结:for in适合遍历对象,for of适合遍历数组。for in遍历的是数组的索引,对象的属性,以及原型链上的属性。
h3cookie和本地存储的区别/h3 pre class="brush:python;toolbar:false" cookie localStorage sessionStorage 大小 4Kb 10Mb 5Mb 兼容 H4/H5 H5 H5 访问 任何窗口 任何窗口 同一窗口 有效期 手动设置 无 到窗口关闭 存储位置 浏览器和 浏览器 浏览器 服务器 与请求一起发送 是 否 否 语法 复杂 简易 简易 /pre p​ 注:对cookie,编码 encode,解码 decode/p h3vue2和vue3的区别/h3 pre class="brush:python;toolbar:false"Vue2和Vue3最大的区别: 1,Vue2的对象响应式原理是通过Object.defineProperty来监听每一个key的变化。所以带来的缺点显而易 见,当对象的key增加或者删除之后,Object.defineProperty无法get到上述key的变化,无法及时响应。 2,Vue2的数组响应式原理,通过重写数组后,此时栈内存中的数组地址也产生了变化,接收者就会响应数组的变 化,缺点是当数组内数据产生变化,栈内存地址没有变更时,响应式失效。 VUe3是通过proxy(代理)来劫持每一个数据的变化,数据每一个值产生变动时,都会走到Proxy(代理)里 面,劫持数据的变化并通知接收者,然后再通过reflect反射回原数据进行更改。 VUe2 Diff 是服务器返回的HTML文本,浏览器解析,构建真实DOM,然后vue会构建虚拟DOM(js对象),如果数据更新,会先反应到虚拟DOM上,也就是一个新的js对象,然后会有diff算法对比两个虚拟DOM对象,然后patch方法,仅仅对发生变化的DOM节点进行更新。 VUe3 Diff 上述说了Vue2会对比两个新旧虚拟DOM对象,这种是全量对比,比较消耗性能,vue3在动态的DOM节点上新增了patch flag,只需要对比动态节点,触发更新即可。 host static 静态提升 Vue2中的DOM元素无论是否是静态DOM,在DOM更新时都会重新参与渲染,而在Vue3中,通过提升静态DOM达成复用避免重复渲染,以此提升性能。 /pre h3vue2源码学习/h3 pre class="brush:python;toolbar:false"1,源码用到的方法:Object.defineProperty,Object.keys; 2,当创建vue实例时,利用Object.keys将属性转为数组,进行属性的遍历,并且为每个属性添加getter和setter函 数,对数据的读取进行劫持,getter用来收集依赖,setter用来派发更新,并且通过回调函数的形式通知订阅者进行 更新。 3,组件也一样,每个组件都有相应的watcher实例,会在组件渲染过程中进行依赖收集,当依赖项被改动时,setter方 法会通知watcher实例派发更新,从而使关联的组件重新渲染。 4,为了方便对属性进行读取,Vue构造函数中为data执行了一个proxy代理函数,配和call借用函数,将属性代理到vue 实例上。 自我学习重写响应式的总结: 重写源码流程: 1,首先实现new Vue初始化选项和数据 2,通过代理,实现vm属性访问,原理是Object.defineProperty 3,将data所有的变量,实现第一层响应式变化:通过Observer观察者函数的walk方法循环每一个属性去defineReactiveData,深层次原理还是Object.defineProperty.value涉及到闭包知识。 4,变量的值可能是对象,实现对象深层次的属性响应式变化:defineReactiveData内部,对value值Observer,同时新赋予的值可能是对象,同样要对新值newValue进行Observer,这样会达到一个不同模块之间的递归。 5,变量的值也可能是数组,在对于数组也要实现每一项的响应式变化,对每一项进行Observer通过Array.isArray进行判断data是数组还是对象。 6,重写数组的7个方法,首先通过Object.create创建一个以Array的原型为原型的对象,在新对象上添加7个方法,同时保留原有7个方法的功能,通过apply借用函数改变里面的this指向来保留原来的功能。同时对每个插入的项又要进行Observer观察。当改写完7个方法后,在Observer观察者函数去改变数组的原型,通过setPrototypeOf指向刚才改造的方法。 /pre h3父子组件加载顺序/h3 pre class="brush:python;toolbar:false"1,父子组件加载顺序: 父beforeCreated-父created->父beforeMounted->子beforeCreated->子created->子beforeMounted-> 子mounted->父mounted 2,父组件更新顺序: 父beforeUpdate->父updated 3,父子组件更新顺序: 父beforeUpdate->子beforeUpdate->子updated->父updated 4,父子组件销毁顺序: 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

vue中$set方法实现思路

1,在创建Observer实例时,也创建一个新的“筐”,挂在Observer实例上,然后把Observer实例挂载到对象的_ob_属性上;
2,触发getter时,不光把watcher收集一份到之前的“筐”中,也收集一份在这个新的“筐”中;
3,用户调用$set时,手动的触发_ob_.dep.notify();
4,最后别忘了在notify()之前调用defineReactive把新的属性也定义成响应式的;

面试学习资料

B站 前端乐哥  学习资料
【HTML+CSS+项目】
链接:https://pan.baidu.com/s/1fnlVtN0UBXRwmWep1HhBIA 
提取码:ixkl     
【Ts+Vue3.0(基础+项目实战)】
链接:https://pan.baidu.com/s/1OEcduMKzn4hvr8Ct8rwcFQ 
提取码:fegc 
【javascript基础】
链接:https://pan.baidu.com/s/1YfPnzyOIrTnXdBROP8287Q 
提取码:m8fk 
【javascript进阶】
链接:https://pan.baidu.com/s/1bC7d9EEgit_ro-kCCYhmGg 
提取码:g7wi 
【jQuery及项目实战】
链接:https://pan.baidu.com/s/1LlI8gCMgwAPW52ynY2Ff6A 
提取码:mwwe 

一些标签简洁写法

ul>li{这是第$个li}*9

XSS和CSRF攻击

明确考察点:明确xss和csrf是什么;明确xss和csrf的特点;如何防范xss和csrf攻击。

首先明确xss(cross-site-scripting)是跨站脚本攻击;csrf(Cross-Site-Request-Forgery)是跨站请求伪造,然后分别举一下两种攻击的例子,最后谈下两种攻击的防范措施。

xss 跨站脚本攻击   csrf 跨站请求伪造
xss:跨站脚本攻击 黑客攻击你的浏览器,篡改浏览器正常显示,窃取用户信息
xss:
	浏览器向服务器请求的时候被注入脚本攻击;
    分成三种类型:反射型(非持久型),存储型(持久型),基于DOM
    
    防范手段:
    	1,输入过滤,进行验证
        2,输出过滤,对字符进行编码转义
        3,加httponly请求头,锁***cookie
        
 对编码转义案例如下:
 	function htmlEncodeByRegExp(str){
        var s='';
        if(str.length==0)return '';
        s=str.replace(/&/g,"&");
        s=s.replace(//g,">");
        s=s.replace(/ /g," ");
        s=s.replace(/\'/g,"'");
        s=s.replace(/\"/g,"&quto;");
        return s;
    }
var tmpStr="

123

"; var html=htmlEncodeByRegExp(tmpStr); console.log(html)//<p>123</p> document.querySelector(".conent").innerHtml=html;//

123

csrf 黑客通过网站B诱使用户去访问已经登录了的网站A进行一些违法用户意愿的请求 造成用户损失 防范手段: 1,服务器验证 http请求的refer头信息 2,请求的时候 传token 3,加验证码 跨站请求伪造防范 主要是在服务端做的: 1,服务器中验证请求头refer字段; 2,加token 3,加验证码

XSS:侧重于脚本,千方百计的注入并执行恶意脚本;

CSRF:不注入恶意脚本,侧重于请求伪造,借刀杀人,在用户不知情的情况下,借用户的名义干坏事。

XSS:跨站脚本攻击,用户提交的数据中可以构造恶意代码,并且执行,从而实现窃取用户信息等攻击。
修复方式: 
	1,对实体字符进行转义
    2,使用http Only来禁止javascript读取cookie值
    3,输入时校验,浏览器与web应用端采用相同的字符编码
    
CSRF:跨站请求伪造攻击
修复方式:筛选出需要防范CSRF的页面,然后嵌入Token,再次输入密码,校验referer
XEE:XML外部实体注入攻击,敏感文件读取
修复方式:XML解析库在调用时严格禁止对外部实体的解析

路由懒加载

在配置路由的时候这样写组件的导入:

component:()=>import(/*.webpackChunkName: "about".*/.'../views/About.vue');
加上这个:/*.webpackChunkName: "about".*/.  可以实现按需加载对应的js文件,可以在network看到文件名.js和这个文件的大小,不然就是默认:0.js 0kb

函数柯里化

特点:函数嵌套函数

函数柯里化,柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数进行处理,并返回一个更具体的函数接收剩下的参数,

这中间可嵌套多层这样的接收部分参数函数,直至返回最后结果

案例:

const nameList1=[
    {mid:'aaa',profession:'aaref'}
]
const namelist2=[
    {adc:"bbb",profession:'bbref'}
]
const curring=name=>val=>val[name];
const name_mid=curring('mid');
const name_adc=curring('adc');
console.log(nameList1.map(name_mid))
console.log(nameList2.map(name_adc));
2,function add(){
    let args=Array.prototype.slice.call(arguments);
    let inner=function(){
        args.push(...arguments);
        return inner;
    }
    inner.toString=function(){
        return args.reduce(function(prev,cur){
            return prev+cur;
        })
    }
    return inner;
}
const result=add(1)(2)(3)(4);
console.log(result);
  // 数组求和
  const sumFn = (...args) => {
    return args.reduce((a, b) => {
      return a + b
    })
  }
  //进行升序排序
  const sortFn = (...args) => {
    return args.sort((a, b) => a - b)
  }
  const currying = function (func) {
    const args = []
    return function result(...rest) {
      if (rest.length === 0) {
        return func(...args)
      } else {
        args.push(...rest)
        return result
      }
    }
  }
  console.log(sumFn(1, 3))
  console.log(currying(sumFn)(1)(2)(3)()) // 6
  console.log(currying(sumFn)(1, 2)(3, 4)(5)()) // 15
  console.log(currying(sumFn)(1)(2, 3, 4, 5)(6)()) // 21
  console.log(currying(sortFn)(1)(3)(2)(6, 4)(5)()) // [1,2,3,4,5,6]
// 最简单的柯里化
 function add(x) {
    return function (y) {
      return x + y
    }
  }
  console.log(add(2)(3)) // 5

call,apply,bind的区别

var name = 'amy',age=17;
var obj = {
    		name:'tom',
          	objAge:this.age,
    		myFun:function(fm,t){
                console.log(this.name+'年龄'+this.age,'来自'+fm+'去往'+t)
            }
          }
var db ={
    name:'lucy',
    age:99
}
obj.myFun.call(db,'成都','上海');    // lucy 年龄99 来自成都去往上海
obj.myFun.apply(db,['成都','上海']);    // lucy 年龄99 来自成都去往上海
obj.myFun.bind(db,'成都','上海')();    // lucy 年龄99 来自成都去往上海
obj.myFun.bind(db,['成都','上海'])();    // lucy 年龄99 来自成都,上海去往 undefined

JS判断英文字母大小写(两种方法)

在javascript中,ASCII的值在65-90这个范围内,则是大写的A-Z;ASCII的值在97-122这个范围内,则是小写的a-z;ASCII码的值在45-57这个范围内,则是数字0-9;其他的值则为其他字符。

浏览器事件循环,宏任务和微任务

JavaScript是单线程语言,所以一次只能够执行一个任务,但是遇到一些耗时任务也没有阻塞,这是因为浏览器是多线程相互协作运行的。

宏任务

宏队列,macrotask,也叫tasks。常见的宏任务有:

script(整体代码)
setTimeout
setInterval
requestAnimationFrame(浏览器独有)
。。。

微任务

微队列,macrotask,包括:

Promise
MutationObserver(html5新特性)
。。。
(注:这里只针对浏览器)

JS数据结构与算法大纲

一,初识数据结构与算法
常见的数据结构;算法
二,数组结构
创建一个数组;数组的length;数组的索引;数组的常用方法
三,栈结构
认识栈结构
封装栈结构
应用:十进制转二进制,进制转换法
四,队列
队列是什么;队列的封装;队列的应用-击鼓传花;双端队列
五,链表
单链表;双向链表;循环链表
六,集合
集合类;ES6的Set;集合的运算
七,字典
字典的封装;散列表;ES6的Map
八,树
二叉树
二叉搜索树:遍历;移除
九,二叉堆
最小堆;最大堆
十,排序算法
冒泡排序;选择排序;插入排序;归并排序;快速排序;计数排序;桶排序;基数排序
十一,搜索算法
顺序搜索;二分搜索;内插搜索
十二,随机算法
十三,算法设计
分而治之
动态规划:背包问题;最长公共子序列
贪心算法
回溯算法
十四,算法复杂度

    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]