前端面试题整理(都是我自己面试被问到过的)

发布时间 2023-03-22 21:16:21作者: 就这样,

面试了好多次,整理下面试题吧

HTML

语义化:增加代码可读性,有利于搜索引擎识别,爬虫获取更多信息;更好的展示代码结构

script标签中defer和async的区别

  相同点:async和defer都是异步加载js

 

  不同点:async是立刻执行, defer在元素加载完之后执行 

 

  因此, js脚本加上 async或 defer,放在头部可以减少网页的下载加载时间,如果不考虑兼容性,可以用于优化页面加载的性能

webSocket:因为http是客户端到服务端的单向通信,如果服务器有变化,客户端就得不断向服务器发出询问,效率低且占内存,

      所以就诞生了webSocket这种服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息的双向通信模式

      webSocket在创建的时候必须传入一个url,发送数据用send(),只能发送字符串,服务器返回的数据在event.data中,也是字符串格式

      要关闭连接用close()

http和https的区别:HTTP协议以明文方式发送内容,不提供任何方式的数据加密。HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。https则是具有安全性的ssl加密传输协议。

           http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。并且https协议需要到ca申请证书。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、

           身份认证的网络协议,要比http协议安全。

CSS

回流:无论通过什么方式影响了元素在视图窗口内的位置和尺寸大小,浏览器需要重新计算元素在视图窗口内的几何属性,这个过程叫做回流

重绘:通过构造渲染树和回流阶段,我们知道了哪些节点是可见的,以及可见节点的样式和具体的元素在视口内的位置和尺寸大小,接下来就可以将渲染树的每个节点都转换为屏幕上的实际像素,

   这个阶段就叫做重绘

css选择器:.class、#id、标签选择器、伪类选择器......

盒模型:盒模型分为标准盒模型和IE盒模型,两者都由content + padding + border + margin构成,区别在于content的计算不同,标准盒模型是content,IE盒模型是content+padding+border

水平垂直居中:绝对定位、flex、grid布局、table-cell + vertical-align + inline-block/margin: auto

子元素设置position:absolute,如果父元素设置了position那么就依照父元素做定位,如果父元素没设置就向上一级寻找,直到body停止

圣杯布局:

1.外面一个大盒子,里面三个同级盒子

2.再最外测盒子给定外边距为左右盒子留出位置

3.三个盒子给定左浮动并且中间的盒子给定宽度为100% 左右按照外边距宽度设置

4.左侧盒子给margin-left:-100回到上一层 再利用相对定位 reight:200

5.右侧盒子再给margin-reight:-150 ,就完成圣杯布局了

双飞翼布局:

1..外面一个大盒子,中间内容区需要一个嵌套的盒子

2.内容区盒子需要给定外边距为两测盒子预留位置

3.左侧盒子给margin-left:-100 左侧完成

4.右侧盒子再给margin-reight:-150 ,完成

vh vw rem em 分别是什么

px代表物理屏幕上能显示出的最小的一个点,

em 是相对于父级的字体大小,

rem是相对于HTML根元素的字体大小,

vh 和vw相对于视口的高度和宽度,1vh 等于1/100的视口高度,1vw 等于1/100的视口宽度

JS

数据类型:number、string、boolean、undefined、null、function、array、object(es6新增symbol:代表独一无二的值,最大的用法是用来定义对象的唯一属性名)

var、let、const三者的区别:

var在同一作用域中可重复定义相同的变量名,变量值是最后一次定义的值,存在变量 提升;

let,const不能在同一作用域中重复定义相同的变量命,只在最靠近的一个块中有效不存在变量提升,let可以定义变量也可以定义常量,const定义的简单类型的数据不能修改可以修改数组或对象中的元素

this

(1)全局环境中,this默认绑定到window

2)函数独立调用时,this默认绑定到window

3)new调用,this是新建的对象

4)箭头函数中的this是外部作用域中的this

5)可以通过call()/apply()修改this指向

 

call,apply,bind的区别

 

相同点:  都是改变this指向的;

 

第一个参数都是this要指向的对象;

 

都可以利用后续参数传参

 

不同点:call和bind传的参数都是一一对应的

 

apply只有两个参数,第二个参数为数组

 

call和apply都是对函数进行直接调用,而bind方法返回的仍是一个函数

宏任务和微任务的概念和执行逻辑

Js是由上而下执行的,在执行过程中会出现异步和同步的现象。宏任务是由浏览器或者 弄得发起的,而微任务由JavaScript自身发起。

执行:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步 微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任 务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程 执行,一直循环直至所有任务执行完毕

深浅拷贝

深拷贝:拷贝过程中会创造另一个一摸一样的对象,新旧对象不共享内存,改变新对象原对象不会改变,比如JSON.parse() 和 JSON.stringify()

浅拷贝:只拷贝基本的数据类型,不复制对象本身,新旧对象公用内存,比如赋值

 

cookie / localStorage/ sessionStorage的区别  

 

共同点:都是保存在浏览器端、且同源的 

 

Setitem/gititem  localStorage/ sessionStorage

 

getCookie  cookie

 

区别: 

 

localStorage:最大的容量是5M,是H5新增的特性,本地存储的一个方案,不会每次都会和后台进行交互,除非手动删除,否则一直存在,缓存的数据在同源的窗口是共享的

 

sessionStorage:也是h5新增的特性,不回每次和台进行交互,,关闭浏览器,缓存失效,在同源的窗口是共享的

 

cookie:容量是4k每次都会和后台进行交互,可以设置过期时间,如果不设置过期时间那么视为随着浏览器窗口关闭而消失,如果设置了cookie的过期时间那么会把cookie保存在硬盘上,关闭后打开仍然有效,在同源的窗口是共享的,并且是始终在请求中携带

跨域 

原因同源策略: 协议 域名 端口号

浏览器允许发起跨域请求,但是跨域请求的数据会被拦截

JsonP : 浏览器未对script标签没有采取同源策略

1)前端利用了script发跨域请求【get】,自己定义一个function,function的参数是需要的数据,发请求的时候带着这个function的函数名;

(2)服务器接收到这个请求,在返回结果的时候,用接收到的函数名包裹数据返回,就是相当于服务器响应后直接调用前端定义的function,将前端需要的数据作为入参传进去

CORS:添加请求头信息实现跨域

Proxy:服务器代理

HTTP请求的常用响应状态码及解决方法

200   (成功)  服务器已成功处理了请求

301   (永久移动)  请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。

302   (临时移动)  服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。

303   (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。

304   (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。

401   (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。

403   (禁止) 服务器拒绝请求。

404   (未找到) 服务器找不到请求的网页。

405   (方法禁用) 禁用请求中指定的方法。

502   (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。

503   (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。

504   (网关超时)  服务器作为网关或代理,但是没有及时从上游服务器收到请求。

Post请求及get请求

都是HTTP发送的请求方式

区别 :1.get请求时获取资源

 2.post请求是交换资源

3.传输数据量上,get请求是受浏览器的url限制的,post请求数据一般不在url上不受限制

4.get请求是明文传输,post请求相对安全一些

5.缓存问题

promise

ES6推出的更好的异步编程解决方案,通过promise.then的链式调用解决嵌套回调的回调地狱问题

promise对象有三种状态:pending、resolved、rejected
promise状态的两种变化 pending-->resolved、pending-->rejected
通过调用resolve()、调用reject()、throw error改变promise的状态
Promise.all([p1,p2,p3])
    接收包含多个promise的数组,返回一个新的promise
    只有当所有接收的promise都成功了,返回的promise才成功,且成功的value为所有成功promise的value组成的数组
    一旦有一个失败了,返回的promise就失败了,且失败的reason就是失败promise的reason
Promise.race([p1,p2,p3])
    接收包含多个promise的数组,返回一个新的promise
    返回的promise的结果由第一个完成的promise决定
作用域
用来规定代码作用的范围及变量查询的范围
①全局作用域:变量在函数或者代码块以外定义,即为全局作用域
②局部作用域:也叫函数作用域,在函数内部定义的变量,即为局部作用域,对外是封闭的,从外层的作用域是无法直接访问函数内部的作用域。且在声明变量时,一定要使用var命令,如果不用相当于声明了一个全局变量
    注意:只要函数内定义了一个局部变量,函数在解析的时候都会将这个变量“提前声明”
③块级作用域:Es6新增的,比如if(){}大括号就是‘块’,这个里边的变量就是拥有块级作用域,
 一个变量可以合法使用的范围/区域
     作用域起到了隔离变量,避免了变量重名冲突的问题(也就是允许了不同作用域中可以有同名的变量)
     分类:全局作用域、函数作用域(局部作用域)、块级作用域====>ES6的let或const变量
多个嵌套的作用域形成的由内向外的结构,用于查找变量
    本质:包含由内向外的多个变量对象的数组
    当查找一个变量,在整个作用域链中都找不到时:会报引用错误(RefrenceError),错误信息(message)为这个变量没有定义
变量提升
    变量声明提升:变量的声明部分被提升到了作用域的最前面执行 ===>  预解析
    结果/现象:在变量声明语句前,就可以访问这个变量,只是值为undefined
函数提升:
    函数声明提升:函数声明语句被提升到了作用域的最前边执行 ===>预解析
    结果/现象:在函数声明语句前,就可以调用这个函数了
扩展说明:
    let/count变量没有变量提升
    同名的变量与函数:内部先提升函数,变量被忽略 ===> 这样只用赋值一次
    例:var c = 3     funcetin c(){}   //先提升函数,再提升变量,但是变量c会被忽略
闭包
        1、为什么会产生闭包?
         出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。
        2、什么是闭包
            闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
        3、什么时候产生闭包
            当嵌套的内部函数引用了外部函数的变量时就产生了闭包
        4、闭包的作用
            延长局部变量的生命周期
            让函数外部能操作内部的局部变量

原型

    每个函数都有一个显示原型属性:prototype;

    每个实例都有一个隐式原型属性:__proto__;

    实例__proto__与对应的prototype都指向原型对象;

    函数的默认的原型对象是Object的实例,也就是一个{};

    原型对象上有一个constructor属性指向对应的构造函数;

原型链

    从对象的__proto__开始,连接的所有对象,也可称为“隐式原型链

原型链的作用:
    1、在查找对象属性或调用对象方法时,会先在对象自身上查找,找不到就会沿着原型链查找
    2、利用原型链可以实现继承
同步
    1、从上往下按顺序依次执行
    2、只有一个任务完全执行后,才执行后面的
    3、会阻塞后面的代码执行
异步
    1、启动任务后,立即向下继续执行,等同步代码执行后才执行回调函数
    2、不会阻塞后面的代码执行
    3、异步回调即使触发了,也是要先放入队列中执行,只有当同步任务或前面的异步任务执行完才执行
history与hash路由的区别和原理
区别:
    1、history:路由路径没有#,刷新会携带路由路径,默认会出404问题,需要配置返回首页
        -404:
           history有:刷新请求时会携带前台路由路径,没有对应的资源返回
           hash没有:刷新请求时不会携带#路由路径
        -解决:
           开发环境:如果是脚手架,项目本身就配置好
           ==> webpack ==> devServer:{historyApiFallback:true}
        -生产环境打包运行:
           配置nginx
              location / {
              try_files $uri $uri/ /index.html; 所有404的 请求都返回index页面
              }
    2、hash:路由路径带#,刷新不会携带路由路径,请求的总是根路径,返回首页,没有404

原理:
   history:内部利用的是history对象的pushState()和replaceState()
   hash:内部利用的是location对象的hash语法
      写hash路径location.hash = '#/xxx'
        读hash路径:location.hash
        监视hash路径的变化:window.onhashchange =() =>{}
http请求方式
get:发送一个请求来取得服务器上的某一资源
post:指定的资源提交数据或附加新的数据
put:指定的资源提交数据或附加新的数据,方法指向了资源在服务器上的位置
delete:删除服务器上的某资源
工程化模块化
前端工程化就是用做工程的思维看待和开发自己的项目,而不再是直接撸起袖子一个页面一个页面开写
模块化和组件化是为工程化思想下相对较具体的开发方式,因此可以简单的认为模块化和组件化是工程化的表现形式。
一个模块就是一个实现特定功能的文件,有了模块我们就可以更方便的使用别人的代码,要用什么功能就加载什么模块

模块化开发的4点好处:

  1 避免变量污染,命名冲突

  2  提高代码复用率

  3 提高维护性

  4 依赖关系的管理

git常用命令

git init - 初始化仓库。

git add . - 添加文件到暂存区。

git commit - 将暂存区内容添加到仓库中

git merge 用来合并一个或者多个分支到你已经检出的分支中

1、分支创建 git branch (+分支名称)

2、查看分支 git branch

3、切换分支 git checkout (分支名)

4、合并分支 git merge(分支名)

5、删除分支 git branch -d (分支名)

6、创建并切换 git checkout -b (分支名)

git reset –hard 版本回滚

git pull 本地与服务器端同步

git push (远程仓库名) (分支名) 将本地分支推送到服务器上去。

数组、对象方法

Array.prototype.concat()  合并 返回新数组 不改变原数组

Array.prototype.every()  检测数组(所有元素)是否通过设置的值  返回布尔

Array.prototype.some()检测数组(只要有元素)是否通过设置的值  返回布尔

Array.prototype.filter() 过滤  返回新数组

Array.prototype.find() 顾虑符合条件的第一个值

Array.prototype.flat() 递归遍历数组合并数组(不会去重)

Array.prototype.forEach()

Array.prototype.includes()  判断是否有莫一个值  返回布尔值

Array.prototype.indexOf() 查找目标元素索引 查不到返回-1

Array.isArray()  判断给定值是否为数组

Array.prototype.join() 拼接数组 返回字符串

Array.prototype.map() 加工数组 返回新数组

Array.prototype.reduce()

Array.prototype.slice()  切割数组

Array.prototype.toString() 返回这个数组的字符串

Array.prototype.splice() 增删改一体

Array.prototype.sort() 数组排序 更改原数组

Array.prototype.fill() 填充数组 改变原数组

Array.prototype.reverse()反转数组 改变原数组

Array.prototype.pop() 末尾删   改变原数组

Array.prototype.push() 末尾增  改变原数组

Array.prototype.shift()  头删 改变原数组

Array.prototype.unshift() 头增 改变原数组

 

Object.freeze()  冻结对象

Object.assign() 合并对象

VUE

webpack

本质上就是打包工具,不是框架也不是库。

webpack 本身只能处理原生的 JavaScript 模块,但是 loader 转换器可以将各种类型的资源转换成 JavaScript 模块。这样,任何资源都可以成为 webpack 可以处理的模块。

Loader  因为 Webpack 只认识 JS,所以 Loader 将其他类型的资源进行转译的预处理工作。

Plugin 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

React 或 Vue 的生命周期。

Vue:beforCreated()创建之前,可用于loading加载阶段

Created()创建后,常用来调接口

 beforMounte()dom加载前

 Mounted()dom加载完成

beforUpdated()数据更新页面没更新

Updated()数据页面都更新了

beforDestroyed()销毁前,数据和方法还可以用

Destroyed()销毁后,组件销毁,数据方法不可用

区别v-if与v-show
隐藏: v-if干掉标签, v-show通过样式来隐藏
  - 重新显示: v-if需要重新创建标签对象, v-show只需要修改样式显示出来就可以
  - v-show重新显示更快, 但隐藏时还占用着内存空间: 以空间换时间
  - v-show更适合切换频繁/需要隐藏的DOM结构比较大
为什么v-for与v-if不适合一起使用
每次渲染都会先循环再进行条件判断,使代码运行性能降低
computed与watch以及method的区别
computed与watch的区别:
  计算属性必须同步返回计算结果,而watch中可以在异步操作后更新数据显示
  watch可以进行深度监视,计算属性只是监视了使用到的数据
  选择:
    如果是根据现在的数据同步计算就可以确定要显示的另一个数据 ===> computed
    如果涉及到异步操作/深度监视 ===> watch
    一旦一个数据变化,我们需要做一系列操作 ===> watch
computed与method的区别:
  计算属性有缓存,多次读取显示只计算一次
  method,多处显示计算多次没缓存
keep-alive
缓存动态组件, 可以通过include或exclude指定只缓存特定组件
使用<keep-alive>也可以缓存路由组件
Vue组件间有哪些通信方式
    1) 父向子
        props(非函数)
        v-model
        $refs, $children
        插槽
    2) 子向父
        props(函数)  
        vue自定义事件
        v-model
        .sync    
        $parent
        作用域插槽
    3) 祖孙间
        $attrs与$listeners    与v-bind/v-on配合使用
        provide与inject
    4) 兄弟或其它/任意
        全局事件总线
        Vuex
       
    另一种方式
    1)属性相关
        props、v-model、$attr与$listeners、作用域插槽(子向父传递)
    2)自定义事件相关
        自定义事件、全局事件总线、v-model、.sync
    3)其他
        $refs,$children,$paret
        provide与inject,插槽,vuex
mvc和mvvm

M - Model:模型,是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据

V - View: 视图,是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。

C - Controller: 控制器, 是应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据

M - Model,Model 代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑

V - View,View 代表 UI 组件,它负责将数据模型转化为 UI 展现出来

VM - ViewModel,ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步 View 和Model 的对象,连接 Model 和 View

vue双向绑定原理

通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调

订阅者发布者

提前写好n个方法,在未来的某个时间段执行

路由守卫

全局路由守卫router.beforeEnter((to,from,next)=>{}),进路由前使用、router.afterEnter((to,from,next)=>{})进路由后使用

组件守卫beforeRouteEnter((to,from,next)=>{}),进组件时调用,beforeRouteLeave((to,from,next)=>{}),离开组件时调用

组件独享守卫beforeEnter((to,from,next)=>{})进入组件时被调用,区别就在于,想对那个路由进行权限控制就直接在其路由配置项中添加守卫,作用域也仅限于该路由

vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化

State提供唯一的公共数据源,所有共享的数据统一放到store的state进行储存,相似与data

mutations : 使用它来修改数据(类似于methods)

getters: 类似于computed(计算属性,对现有的状态进行计算得到新的数据-------派生 )

actions: 发起异步请求

modules: 模块拆分

Vue2及Vue3

利用Proxy(代理) 拦截data属性的操作

利用Reflect(反射) 对被代理对象的相应属性进行操作

组合API

Setup 所有组合API都在这个函数里操作

Ref定义基本数据类型的响应式

Reactive定义引用数据类型的相应式

Computed 定义计算属性

Wactch 监视

Vue3的生命周期函数

setup()方法实在beforeCreate及created前调用的,所以不需要了

beforeCreate -> use setup()

created -> use setup()

beforeMount -> onBeforeMount

mounted -> onMounted

beforeUpdate -> onBeforeUpdate

updated -> onUpdated

beforeDestroy -> onBeforeUnmount

destroyed -> onUnmounted

errorCaptured -> onErrorCaptured

axios和fatch的区别

 axios传一个对象,里面包含请求url和请求方法,参数

 fetch传两个参数,第一个是请求url,第二个是请求的一些参数

 Axios还有非常好的一点就是会自动对数据进行转化,而Fetch则不同,它需要使用者进行手动转化

axios提供了请求和相应拦截器

Fetch没有拦截器功能,但是要实现该功能并不难,直接重写全局Fetch方法就可以办到

最大的不同点在于Fetch是浏览器原生支持,而Axios需要引入Axios库

React

生命周期

componentWillMount()该方法会创建一个虚拟DOM

componentDidMount()类似Mounted()在render 之后调用,可获取dom

componentWillUpdate()数据更新

componentDidUpdate()数据页面更新

componentWillUnmount()销毁前,可用于移除定时器等

项目上常见的hooks

为什么用hooks?组件更好的复用,易拆解测试

useState 初始化数据,初始化只初始化一次

useEffect 内部不能修改 state

useRef用来获取dom节点

useContext也就是和 class 组件的 context 同样的效果,将状态传递给孙子组件

useReducer是单个组件状态管理,组件通讯还需要 props

类组件和函数组件之间的区别
类组件可以使用其他特性,如状态和生命周期钩子,并且他有this

函数组件只能接收props渲染到页面,无状态组件,没有this,不能使用生命周期钩子

函数组件性能要高于类组件,因为类组件使用要实例化,而函数组件直接执行取返回结果即可,为了提高性能,尽量使用函数组件

受控组件和非受控组件

受控组件是 React 控制中的组件,并且是表单数据真实的唯一来源。

非受控组件是由 DOM 处理表单数据的地方,而不是在 React 组件中。

react组件传值方法

1.父子传值,2.有共同父组件通过父子传值实现同级传值,3.用useContext传值,4.自定义事件传值

redux

Redux是一个流行的JavaScript框架,为应用程序提供一个可预测的状态容器,Redux严格限制了数据只能在一个方向上流动

所有的数据(state)存储在store中,通过接收组件指令分发一份action,当一个store接收到一个action,它将把这个action代理给相关的reducer

reducer是一个纯函数,它可以查看之前的状态,执行一个action并且返回一个新的状态

优化

 

css 放 ,js 脚本放 最底部。

减少 DOM 操作。

手写

求出字符串中所有数字的和

let str = "-2h11v2gfdai123g1325i1l"
    sunStr = (str) => {
        let strArr = str.split("")
        console.log(strArr,11111);//字符串转为数组
        let res = 0
        for(var i = 0;i<strArr.length;i++){
            if(+strArr[i]){
                console.log(strArr[i],22222);//字符串中所有的数字
                res += +strArr[i]
            }
        }
        console.log(res,33333);//求和
        return res
    }