前端常见问题

发布于 2022-06-21  38 次阅读


1、说一说cookie sessionStorage localStorage 区别?

共同点都是存储在浏览器本地的,都遵循同源原则(sessionStorage还必须是同一个页面)
cookie是由服务端写入的,后两者是前端写入的。
cookie的生命周期是服务端设置好的,sessionStorage在浏览器关闭后就被删除,localStorage生命周期一直存在除非手动删除
cookie的存储空间只有4KB,后两者为5M
在前端请求后端时会自动携带cookie,后两者不会
cookie一般用于存储登录的信息(如sessionId,token),sessionStorage可以用于检测用户是否时页面刷新进入的,localStorage一般用于存储不易改变的数据

2、说一说JS数据类型有哪些,区别是什么?

基本数据类型:Number、Boolen、String、NaN、Symbol、Null、Undefined、BigInt
复杂数据类型:Object(function),也被称为引用类型,常见的有数组、对象、Math函数等
区别:基本数据类型存储在栈中,数据结构简单,占用空间小,使用比较频繁。复杂数据类型存储在堆中,数据结构复杂,占用空间较大。在栈中存储的是指向堆中的指针地址。

3、说一说你对闭包的理解?

闭包指的是有权访问另一作用域中变量的函数。
闭包会造成内存泄露的问题。
闭包应用:防抖节流

  • 防抖:一定时间内多次触发事件则重新计时

  • 节流:一定时间内多次触发事件只执行一次

  • 举个例子:
    防抖:小明借了小红100元,小红说2天后还钱,如果小明不停催促,重新开始计算2天,若中途不再催促,2天后还钱。
    节流:小明借了小红100元,小红说2天后还钱,如果小明不同催促,还是2天后才会还钱。

  • 实现方式:

<!-- 防抖节流 -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>test</title>
</head>
<style>
    #btn,
    #btn2 {
        width: 60px;
        border: 1px solid #676767;
        padding: 5px 10px;
        text-align: center;
        user-select: none;
        cursor: pointer;
        margin-bottom: 10px;
    }
</style>

<body>
    <div id="btn" title="防抖:一定时间内多次触发事件则重新计时">点击我</div>
    <div id="btn2" title="节流:一定时间内多次触发事件只生效一次">点击我</div>
</body>
<script>
    let btn = document.getElementById('btn')
    // 防抖事件
    function fd(fn, time, immediate=false) {
        let timer = null;
        let isImmidiate = false; // 是否立刻执行
        return function () {
            if (timer) clearTimeout(timer)
            if (!isImmidiate && immediate) {
                fn.call(this, arguments)
                isImmidiate = true
            } else {
                timer = setTimeout(() => {
                    fn.call(this, arguments)
                    isImmidiate = false
                }, time)

            }
        }
    }
    // 节流事件
    function jl(fn, time) {
        let lastTime = 0;
        return function () {
            let nowTime = new Date().getTime();
            let remainTime = time - (nowTime - lastTime);
            if (remainTime <= 0) {
                fn.call(this, arguments);
                lastTime = nowTime;
            }
        }
    }
    btn.addEventListener('click', fd(function () {
        console.log(this);
        console.log('123');
    }, 1000, true))

    btn2.addEventListener('click', jl(function () {
        console.log(this);
        console.log('456');
    }, 1000))
</script>

</html>

4、什么是promise,怎么使用?

promise是异步编程的解决方案,可以解决回调地狱的问题
有三种状态,分别是pending(执行中)、resolved(成功,也可以是fulfilled)、rejected(失败),一旦状态发生改变就不会再变。
使用new Promise((resolved,rejected)=>{}).then(res=>{}).catch(err=>{})

5、什么是跨域,怎么解决?

网页中请求地址的协议、域名、端口号不同,就会产生跨域。
jsonp,通过script标签可以跨域请求资源的特性,将回调函数作为参数拼接在url中,后端收到请求调用该函数,并将数据作为参数返回即可实现跨域。
cors,res.setHeader('Access-Control-Allow-Origin', '*');res.setHeader("Access-Control-Allow-Methods", "GET, PUT, OPTIONS, POST");
nginx反向代理,利用代理服务器和页面同源的特性来实现跨域。
postmessage,H5新增的Api,通过发送和接收API实现跨域。

6、什么是BFC?

BFC,块级格式化上下文。页面中独立的渲染区域,不会影响其它的元素。
条件:float是left或right,position是absolute或fixed,overflow不是visible

7、vuex是什么,有哪些属性,怎么使用?

vuex是vue的状态管理工具。包括actions、state、mutations、getters、module。
其中,state存放数据(通常以对象方式);actions包含异步操作(ajax);
mutations包含改变state中数据的方法(同步方法),可通过store.commit调用actions,通过store.dispatch触发getters;
getters中定义store的计算属性,可通过store.getters调用;module将store划分成模块。

8、JavaScript怎么判断变量类型?

typeof,可用于判断基本数据类型,判断引用数据类型时,除了函数返回function,其他返回object。
instanceof,不能用于检测symbol,undefined,null。
Object.prototype.toString.call(),判断对象原型链
constructor,用于检测引用数据类型

9、说说样式的优先级是什么?

!important > 行内样式 > 嵌入样式/外链样式(比较同级后面会覆盖前面的)> ID选择器 > Class选择器/伪类选择器/属性选择器 > 标签选择器 > 通配符选择器

10、JS实现异步的方式?

回调函数、定时器、迭代器和生成器函数、async/await

11、说说vue2和vue3的双向绑定原理?

  • vue2双向绑定原理
    通过数据劫持结合发布者-订阅者模式来实现数据的响应式,也就是通过Object.defineProty来劫持数据的getter和setter,在数据变化后发布消息给订阅者,订阅者收到消息进行处理。缺陷是如果数据量很大,一次性递归会导致栈溢出,不能监听对象的新增和删除属性,不能监听通过下标改变的数组

  • vue3双向绑定原理
    通过es6新增的proxy劫持整个对象并返回新的对象。
    proxy使用方法:var proxy = new Proxy(target, handler),其中,target表示被代理的对象,handler表示代理的操作。

12、说说数组去重的方法?

1、使用ES6新增语法set
2、使用indexof
3、使用数组方法includes

13、null和undefined区别?

null指的是变量定义并赋值成null,undefined指的是变量定义但未赋值
console.log(null == undefind) // true
console.log(null !== undefind) // true

14、什么是浮动,有哪些属性,怎么清除浮动?

浮动float常用属性是left、right、none
设置了浮动的元素会脱离文档流,会导致盒子坍塌。
清楚浮动的方法:
1、标签法,在浮动的元素下方添加一个标签,并设置样式clear: both
2、给父元素设置样式overflow: hidden
3、通过伪元素清除浮动。设置样式div::after{content: ''; display: table; clear: both}

15、谈谈对箭头函数的理解?

箭头函数是ES6新增语法。简化了函数定义,是一种匿名函数。当函数体是单条语句可以省略{}和return。
箭头函数没有自己的this,只能够继承外部函数的上下文,没有arguments,不能够作为构造函数(不能new)
当使用call和apply方法时只传递一个参数(第一个参数也就是this会被忽略)
箭头函数没有原型,没有super,不能使用yield关键字,不能够作为生成器(generator)

16、call、apply和bind的区别?

都可以改变this的指向。call和apply可以直接调用。
call(this,a,b,c),第二个参数表示具体的函数参数。
apply(this,arr),第二个参数表示函数参数数组。
bind(this),返回一个执行上下文的函数,需要自行调用。
使用场景:call用于对象继承,伪数组转真数组;apply用于找出数组的最大值和最小值以及数组合并;bind用于vue和react中函数指向。

17、谈谈对语义化的理解?

语义化:根据页面结构选择合适的标签。
作用:有利于SEO(搜索引擎优化)、代码可读性更高、利于页面内容的结构化

18、谈谈this的指向?

普通函数指向window,严格模式下为undefined,node环境下指向global
箭头函数本身没有this。继承函数所在上下文
函数中的this,随调用指向谁。

19、谈谈css中的尺寸单位?

px:绝对像素
em:相对父元素像素
rem:相对根元素像素
vw:视口宽度
vh:视口高度

20、在未知宽高实现元素水平垂直居中?

1、使用弹性盒布局
2、使用transform布局
3、使用table-cell布局

21、什么是变量提升?

变量提升是指使用var关键字声明的变量在解析时会被提升到最前面。变量声明会被提升,赋值不会被提升。变量提升后在初始化变量后返回的是undefined。
let、const不存在变量提升。let和const声明的变量会形成暂时性死区,为赋初始值直接访问会报错。

22、说说hashrouter和historyrouter的区别?

两者都是前端路由,hashrouter是监听location的hash值进行实现,特点是地址中含有#,historyrouter是浏览记录的api实现。
相同的url,哈希路由不会添加进历史记录中,history路由会被添加到历史记录中。
historyrouter需要配合后端不然会出现跳转页面返回出现404问题,hash路由不会。
通过window.onhashchange获取url中hash值。
通过history.pushState使用它做页面跳转不会触发页面刷新,使用window.onpopstate监听浏览器的前进和后退

23、map和foreach区别?

map会改变原数组,返回一个长度和原来一样的新数组。
foreach不会改变原数组,返回undefined。
map处理速度更快,而且可以链式调用。如:arr.map(v=>v*v).filter(v=>v>10)

24、说一说事件循环Event loop,宏任务与微任务?

在js代码执行过程中,遇到同步任务,直接推到调用栈中执行,遇到异步任务,将其挂起,等到有返回值将它推到任务队列中。
当调用栈中所有的同步任务执行完成后,再将任务队列中的异步任务一个一个的推入并执行。
异步任务分为宏任务和微任务,每个宏任务都包含一个微任务队列。
常见的宏任务有:script标签中的代码,定时器,ajax,setImmediate,I/O。
常见的微任务有:promise,Object.observe。

25、什么是diff算法?

diff算法主要是在虚拟dom树发生改变后,生成dom树的一种更新方式。
通过对比他们之间的差异,将更新补丁直接作用在真实dom树上。以最小成本完成视图更新。
框架会将所有节点转化成vnode,在发生更改后将vnode和更改前oldnode比较,然后以vnode为基准,在oldnode上进行更改。
原本没有新版有则添加,反之,则删除。

26、什么是CSRF跨站请求伪造?

用户在访问并登录网站A后会产生一个cookie,用户在没退出网站A继续访问网站B,
网站B收到用户请求返回代码获取用户浏览器上登录网站A的cookie,浏览器直接以用户的权限将cookie返回网站B

27、什么是XSS跨站脚本攻击?

攻击者将脚本放在网站A上,在用户访问完网站A的时候运行脚本并获取用户的cookie。

28、Vue中计算属性computed和监听watch有什么区别?

computed有缓存,依赖于其他属性值,只有其他属性值发生改变才会重新计算
watch无缓存,可以进行异步操作,每当坚挺的值发生变化后就会立即回调进行后续操作

29、Vue中$nextTick的作用?

为了立刻获取更新后DOM。
Vue中更新DOM是异步的,当数据发生变化后,vue会开启一个异步更新队列,队列中的所有数据更新完才会更新视图。
\$nextTick就解决了这个问题。原理是返回一个promise

30、浏览器渲染页面原理?

浏览器将html文件解析成DOM树,将css解析成stylesheet,然后将DOM树和style结合形成Render树,之后浏览器通过Render树计算出每个元素节点的位置并将其绘制出来

31、说说defer和async的区别?

两者都是异步的,区别在于何时执行script脚本。
defer是在加载完JS并且HTMl解析完成后再执行JS脚本
async是在加载完JS就执行JS脚本,会阻塞HTML的渲染

32、浏览器输入url后会发生什么?

判断是搜索内容还是url
查找本地缓存,如果本地存在直接访问页面(304),反之,进入网络请求阶段
DNS域名解析
三次握手建立TCP连接
合成请求头信息,发送http请求
处理响应数据
四次挥手释放TCP连接
浏览器渲染页面(将html解析成dom树,将css解析成stylesheet,并生成render,通过render计算各个节点位置并绘制到页面上)

33、说说对盒模型的理解?

盒模型包括标准盒模型和怪异盒模型(IE盒模型)
标准盒模型:box-sizing: content-box(盒子的宽度=width+margin+padding+border)
怪异盒模型:box-sizing: border-box(盒子的宽度=width+margin)

34、伪数组转数组方式?

  • 使用for循坏
  • 使用扩展运算符([...nodelist])
  • Array.from(nodelist)
  • Array.prototype.slice.call(nodelist)
  • Array.prototype.concat.apply([], nodelist)

35、说说Vue中的keep-alive?

keep-alive是vue的内置组件。通常将数据量多且不易变动的组件放在keep-alive中,起到缓存的作用。
防止因为dom重复渲染,减少资源消耗。
keep-alive有3个属性:
include 需要保存的组件
exclude 不需要保存的组件
max 最多能保存的组件数

36、说说new操作会发生什么?

1、创建一个新的对象。
2、为该对象添加_proto_属性,该属性链接到构造函数的原型对象。
3、为新创建的对象作为this。
4、如果函数有返回值则返回,没有返回值则返回this。

37、说一下token 能放在cookie中吗?

token可以放在cookie中。token一般是用来判断用户是否登录的,它内部包含的信息有:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串) token可以存放在Cookie中,token是否过期,应该由后端来判断,不该前端来判断,所以token存储在cookie中只要不设置cookie的过期时间就行了,如果 token失效,就让后端在接口中返回固定的状态表示token失效,需要重新登录,再重新登录的时候,重新设置cookie中的token就行了。

token验证流程:
客户端使用账号密码请求登录,服务端收到请求会验证账户名和密码,验证成功后服务端会签发一个token,并发送给客户端。客户端收到token后会把他存储下来。每次客户端发送请求都会携带token,服务端会对它进行验证,验证通过则返回数据。

38、说一说Vue中 v-if 和 v-show区别?

作用:都是控制元素隐藏和显示的指令
区别: v-show: 控制的元素无论是true还是false,都被渲染出来了,通过display:none控制元素隐藏
v-if: 控制的元素是true,进行渲染,如果是false不渲染,根本在dom树结构中不显示
应用: v-show: 适合使用在切换频繁显示/隐藏的元素上 v-if: 适合使用在切换不频繁,且元素内容很多,渲染一次性能消耗很大的元素上

39、简单的说明下react的生命周期

constructor:完成了数据的初始化。
render:render()函数会将jsx生成的dom插入到目标节点中。在每次组件更新时,react通过diff算法比较更新前和更新之后的dom节点,找到最小的有差异的dom位置并更新,花费最小的开销。
componentDidMount:组件第一次渲染完成,此时dom节点已经生成,在这里调用接口请求,返回数据后使用setState()更新数据后重新渲染。
componentDidUpdate:组件更新完成。每次react重新渲染之后都会进入这个生命周期,可以拿到更新之前的props和state。
componentWillUnmount:在这个生命周期完成组件的数据销毁和卸载,移除所有的定时器和监听。

getDerivedStateFromProps(nextProps,prevState): 代替老版的componentWillReceiveProps()。官方将更新state与触发回调重新分配到了componentWillReceiveProps()中,让组件整体的更新逻辑更加清晰,并且在当前生命周期中,禁止使用this.props,强制让开发者们通过比较nextProps和PrevState去保证数据的正确行为。
shouldComponentUpdate(): return true可以渲染,return false不重新渲染。
getSnapshotBeforeUpdate(prevProps,prevState): 代替componentWillUpdate(),核心区别在于getSnapshotBeforeUpdate()中读取到的dom元素状态是可以保证和componentDidUpdate()中的一致。

40、如何实现可过期的localstorage数据?

一般来说,localstorage定义的数据是默认永久保存在浏览器中的,知道手动删除。
要实现数据的过期(有效时间),可以使用惰性删除和定时删除,原理相差不大。
惰性删除: 惰性删除是指某个键值过期后,该键值不会被马上删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。实现方法是,获取数据的时候,从存储的数据对象中拿到存储的时间和当前时间做对比,如果超过过期时间就清除Cookie。
定时删除:每隔一段时间执行一次删除操作,并通过限制删除操作执行的次数和频率,来减少删除操作对CPU的长期占用。另一方面定时删除也有效的减少了因惰性删除带来的对localStorage空间的浪费。实现过程,获取所有设置过期时间的key判断是否过期,过期就存储到数组中,遍历数组,每隔1S(固定时间)删除5个(固定个数),直到把数组中的key从localstorage中全部删除。
LocalStorage清空应用场景:token存储在LocalStorage中,要清空。

41、HTML中重排和重绘的区别,解决方案?

重排:当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排
重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来,这个过程叫做重绘。
重排一定重绘,重绘不一定重排。
可以使用GPU加速(transform)、脱离文档流(定位)、样式集中改变来避免。

42、Vue 列表为什么加 key?

为了性能优化 因为vue是虚拟DOM,更新DOM时用diff算法对节点进行一一比对,比如有很多li元素,要在某个位置插入一个li元素,但没有给li上加key,那么在进行运算的时候,就会将所有li元素重新渲染一遍,但是如果有key,那么它就会按照key一一比对li元素,只需要创建新的li元素,插入即可,不需要对其他元素进行修改和重新渲染。 加分回答 key也不能是li元素的index,因为假设我们给数组前插入一个新元素,它的下标是0,那么和原来的第一个元素重复了,整个数组的key都发生了改变,这样就跟没有key的情况一样了。

43、react的antd中的表格数据怎么根据数字字母进行排序?

数组对象中:
字母/文字按照A-Z(a-z)顺序: sorter: (a, b) => a.introduction.localeCompare(b.introduction),
数字按照大小排序:sorter: (a, b) => a.number - b.number,

在javascript中也可以使用这类方法。

43、react中怎么使用三元表达式实现多个条件判断?

import styles from './index.less'

let tbody = data.map(item,index) => {
    <tr key={index}>
        <td className={index === 6 && item.number < 0 ? styles.orange : (index === 7 && item.number <= 0 ? styles.red : (index === 6 || index === 7 || index === 8 ? styles.black : ''))}>{item.number}
        </td>
    </tr>
}

这里表示第7行的值如果小于0为橙色,第8行的值如果小于等于0为红色,其他的值为黑色


活的像诗一样