• 前端工程师面试题总结附加分项及基础复习


    有兴趣的朋友可以查看博主自己的前端面试笔试学习知识笔记总结

            基础自省:

            更多基础请直接前往前端学习专栏进行查看;

            牛客网前端工程师面试题学习,不断学习更新,希望有大佬来指点提携一波,望共勉,一起进步~~


    目录

    1. 说一说cookie、sessionStorage、localStorage区别?

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

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

    4. 说一说promise是什么与使用方法?

    5. 说一说跨域是什么?如何解决跨域问题?

    6. 说一说BFC

    7. 说一说Vuex是什么,每个属性是干嘛的,如何使用 ?

    8. 说一说JavaScript有几种方法判断变量的类型?

    9. 说一说样式优先级的规则是什么?

    10. 说一说JS实现异步的方法 

    11.  说一说Vue2.0 双向绑定的原理与缺陷?


    1. 说一说cookie、sessionStorage、localStorage区别?

            首先,cookie、sessionStorage、localStorage都是浏览器存储,都存储在浏览器本地。区别在于:

    • Cookie是由服务端写入的,而SessionStorage、LocalStorage都是由前端写入的;
    • Cookie的生命周期是由服务器端在写入的时候就设置好的,LocalStorage是写入就一直存在,除非手动清除,SessionStorage是在页面关闭的时候就会自动清除;
    • Cookie的存储空间比较小,大概4KB,SessionStorage、LocalStorage存储空间比较大,大概5M;

            而且Cookie、SessionStorage、LocalStorage数据共享都遵循同源原则,SessionStorage还限制必须是同一个页面,在前端给后端发送请求的时候会自动携带Cookie中的数据,但是SessionStorage、LocalStorage不会。

            加分项:

            由于他们的以上区别,所以他们的应用场景也不同,Cookie一般用于存储登录验证信息如SessionID或者token,LocalStorage常用语存储不一变动的数据,减轻服务器的压力,SessionStorage可以用来检测用户是否是属性进入页面,如音乐播放器恢复进度条的功能。

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

            JS数据类型分为两类:

    • 一类是基本数据类型,也叫简单数据类型,包含7种类型,分别是Number、String、Boolean、BigInt、Symbol、Null、Undefinrd。
    • 另一类是引用数据类型,也叫复杂数据类型,通常用Object代表,普通对象,数组,正则,日期,Math数学函数都属于Object。

            数据分为两大类的本质区别:基本数据类型和引用数据类型他们在内存的存储方式不同。

    • 基本数据类型是直接存储在栈中的简单数据段,占据空间小,属于被频繁使用的数据;
    • 引用数据类型时存储在堆内存中,占据空间打。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。

            加分项:

    • Symbol是ES6新出的一种数据类型,这种数据类型的特点就是没有重复的数据,可以作为object的key。
    1. 数据的创建方法Symbol(),因为它的构造函数不够完整,所以不能使用new Symbol()创建数据。
    2. 由于Symbol()创建数据具有唯一性,所以Symbol()!==Symbol(),同时使用Symbol数据作为key不能使用for获得到这个key,需要使用Object.getOwnPropertySymbols(obj)获得这个obj对象中key类型时Symbol的可以值。
      1. let key = Symbol('key');
      2. let obj = { [key]: 'symbol'};
      3. let keyArray = Object.getOwnPropertySymbols(obj); // 返回一个数组[Symbol('key')]
      4. obj[keyArray[0]] // 'symbol'
    • BigInt也是ES6新出的一种数据类型,这种数据类型的特点就是数据涵盖的范围大,能够解决超出普通数据类型范围报错的问题。
    1. 使用方法:整数末尾直接+n:1n;或者调用BigInt()构造函数:BigInt("1")
    2. 注意BigInt和Number之间不能进行混合操作;

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

            闭包一个函数和词法环境的引用捆绑在一起,这样的组合就是闭包(closure)。一般就是一个函数A,如return其内部的函数B,被return出去的B函数能够在外部访问A函数内部的变量,这时候就形成了一个B函数的变量背包,A函数执行结束后这个变量背包也就不会被销毁,并且这个变量背包在A函数外部只能通过B函数访问。

    • 闭包形成原理:作用域链,当前作用域可以访问上级作用域中的变量;
    • 闭包解决的问题:能够让函数作用域中的变量在函数执行结束后不被销毁,同时也能在函数外部可以访问函数内部的局部变量。
    • 闭包带来的问题:由于垃圾回收器不会将比保重变量销毁,于是就造成了北村泄露,内存泄露积累多了就容易导致内存溢出。

            加分项:

    • 闭包的应用:模块化的导出引入、防抖节流,能够模仿块级作用域,在构造函数中定义特权方法,Vue中数据响应式Observer中使用闭包等。

    4. 说一说promise是什么与使用方法?

    • Promise的概念:异步编程的一种解决方案,解决了地狱回调的问题;
    • Promise的使用场景:异步请求、可读性高的场景、处理多并发 。
    • Promise的使用方法:用来进行异步编程,通过.then进行链式调用,总共三个状态:pendding:等待响应:,resolved:成功的放回,rejected:失败的返回,书写错误时的返回。无论什么情况都会返回个状态要么成功要么失败。
    • Promise的作用:Promise是异步微任务,解决了异步多层嵌套回调的问题,让代码的可读性更高,更容易维护;
    • Promise的使用:Promise是ES6提供的一个构造函数,可以使用Promise构造函数new一个实例,Promise构造函数接受一个函数作为参数,这个函数有两个参数,分别是两个函数“resolve”和“reject”,“resolve”将Promise的状态有等待变为成功,将异步操作的结果作为参数传递过去。“reject”则将状态有等待转变为失败,在异步操作失败时调用,将异步操作报出的错误作为参数传递过去。实例穿件完成后,可以使用“then”方法分别指定成功或失败的回调函数,也可以使用catch捕获失败,then和catch最终返回的也是一个Promise,所以可以链式使用;
    • Promise的特点:
    1. 对象的状态不受外界影响(Promise对象代表一个异步操作,有三种状态):-pending(执行中)-Resolved(成功,又称Fulfilled)-rejected(拒绝),其中pending为初始状态,fildilled和rejected为结束状态(结束状态标识promise的生命周期已结束);
    2. 一旦状态改变,就不会在变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能(状态凝固了,就不会在变了,会一直保持这个结果):从Pending变为Resolved,从Pending变为Rejected;
    3. resolve方法的参数是then中回调函数的参数,reject方法中的参数是catch中的参数;
    4. then方法和catch方法只有不报错,返回的都是一个fullfilled状态的promise;
    • 加分项:Promise的其他方法:
    1. Promise.resolve():返回的Promise对象状态为fullfilled,并且将改value传递给对应的then方法;
    2. Promise.reject():返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法;
    3. Promise.all():返回一个新的promise对象,该promise对象在参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发改promise对象的失败;
    4. Promise.any():接受一个Promise对象的集合,当其中的一个promise成功,则返回那个成功的promise的值;
    5. Promise.race():当参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参考调用父promise绑定的相应句柄,并返回该promise对象;

    5. 说一说跨域是什么?如何解决跨域问题?

    • 跨域:当前页面中的某个接口请求的地址和当前页面的地址如果协议、域名、端口其中有一项不同,就说改接口跨域了;
    • 跨域限制的原因:浏览器为了保证网页的安全,出的同源同源协议策略;
    • 跨域报错信息:
    •  跨域解决方案: 跨域一般的解决方法时Jsonp,script标签不受同源策略影响;前端还可以设置代理proxy;后端也可以设置CROS,Access-Control-Allow-Origin;此外还有nginx的反向代理。
    1. cors:目前最常用的一种解决方法,通过设置后端允许跨域实现。res.setHeader('Access-Control-Allow-Origin','*');res.setHeader("Access-Control-Allow-Methods","GET,PUT,OPTIONS,POST");
    2. node中间件、nginx反向代理:跨域限制的时候浏览器不能跨域访问服务器,node中间件和nginx反向代理,都是让请求发给代理服务器,静态页面和代理服务器是同源的,然后代理服务器再向后端服务器发请求,服务器和服务器之间不存在同源限制;
    3. JSONP:利用的原理是script标签跨域跨域请求资源,将回调函数作为参数拼接在url中。后端收到请求,调用该回调函数,并将数据作为参数返回去,注意设置响应头返回文件类型,应该设置成javascript;
    4. postmessage:H5新增API,通过发送和接受API实现跨域通信;

    6. 说一说BFC

            BFC(Block Formatting Context)块级格式化上下文,是Web页面一块独立的渲染区域,内部元素的渲染不会影响便捷意外的元素。

            BFC布局规则:

    • 内部盒子会在垂直方向,一个接一个地放置;
    • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠;
    • 每个盒子(块盒与行盒)的margin box的左边,与包含块border box的左边相接触(对于从左到右的格式化,否则相反)。即使存在浮动也是如此;
    • BFC的区域不会与float box重叠;
    • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也是如此;
    • 计算BFC的高度时,浮动元素也参与计算;

            BFC形成条件:

    • “float”设置成“left”或者“right”,不为none;
    • “position”是“absolute”或者“fixed”;
    • “overflow”不是“visible”,为“auto”,“scroll”,“hidden”;
    • “display”是“flex”或者“inline-block”等;

           BFC主要用途:

    • 清除内部浮动,富元素设置为BFC可以清除子元素的浮动(最常用overflow:hidden,IE6需加上*zoom:1):计算BFC高度时会检测浮动子盒高度;
    • 解决外边距合并问题;
    • 右侧盒子自适应:BFC区域不会与浮动盒子产生交集,而是紧贴浮动边缘;

            加分项:

    • BFC的方式都能清除浮动,但我们常用的清除浮动的BFC方式只有“overflow:hidden”,原因是使用float或者position方式清除浮动,虽然父级盒子内部浮动被清除了,但是父级本身又脱离文档流了,会对父级后面的兄弟盒子的布局造成影响。如果 设置父级为“display:flex”,内部的浮动就会失效。所以通常知识用“overflow:hidden”清除浮动;
    • IFC(Inline formatting contexts):内联格式上下文。IFC的高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响),IFC的line box一般左右都贴紧整个IFC,但是会因为float元素二扰乱;
    • GFC(GrideLayout formatting contexts):网格布局格式化上下文。当为一个元素设置display值为grid的时候,此元素将会获得一个独立的渲染区域;
    • FFC(Flex formatting contexts):自适应上下文。display值为flex或者inline-flex的元素将会生成自适应容器;

    7. 说一说Vuex是什么,每个属性是干嘛的,如何使用 ?

            Vuex是集中管理项目公共数据的。Vuex有state、mutations、getters、actions、module属性。

    • state属性用于存储公共管理的数据;
    • mutations属性定义改变state中数据的方法,注意:不要在mutation中的方法中写异步方法Ajax,那样数据就不可跟踪了;
    • getters属性可以认为是定义store的计算属性的。就像计算属性一样,getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算;
    • action属性类似与mutation,不同在于:Action提交的是mutation,而不是直接变更状态。Action可以包含任意异步操作;
    • moudle属性是将store分割成模块。每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块,从上至下进行同样方式的分割;

            使用方法:

    • state:直接以对象方式添加属性;
    • mutations:用过`store.commit`调用;
    • action:通过`store.dispatch`方法触发;
    • getters:直接通过store.getters调用;

            加分项:

    • 可以使用mapState、mapMutations、mapAction、mapGetters一次性获取每个属性下对应的多个方法。
    • VueX在大型项目中比较常见,非关系组件传递数据比较方便;

            总结:VueX是全局状态管理仓库,相当于window对象挂载了一个全局变量,state是数据源、actions处理异步事件、mutations处理同步事件、getters是过滤数据的、modules是模块。

    8. 说一说JavaScript有几种方法判断变量的类型?

            JavaScript有4中方法判断变量的类型,分别是typeof、instanceof、Object.prototype.toString.call()(对象原型链判断方法)、constuctor(用于引用数据类型)。

    • typrof:根据二进制判断,常用于判断基本数据类型,对于引用数据类型处理duncyion返回‘function’,其余全部返回‘object’,不能判断Object和null;
    • instanceof:根据原型链判断,主要用于区分引用数据类型,检测方法是检测的类型在当前实例的原型链上,用其检测出来的结果都是true,不太适合用于简单数据类型的检测,检测过程繁琐且对于简单数据类型中的undefined,null,symbol检测不出来;
    • construction:根据构造器判断,用于检测(监听)引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型类的干扰;
    • Object.protptype.toString.call():用Object的toString方法判断,适用于所有类型的判断检测,检测方法是Object.prototype.toString.call(数据)返回的是该数据类型的字符串;

            这四种判断数据类型的方法中,各种数据类型都能检测且检测精准的就是Object.prototypr.toString.call()这种方法。

            加分项:

    • instanceof的实现原理,验证当前类的原型prototype是否会出现在实例的原型链_proto_上,只要在它的原型链,则结果都为true。因此,`instanceof`在查找的过程中会遍历左边变量的原型链,直到找到右边变量的`prototype`,找到返回true,未找到返回false;
    • Object.prototype.toString.call()原理:Object.prototype.toString表示一个返回对象类型的字符串,call()方法可以改变this的指向,那么把Object.prototype.toString()方法指向不同的数据类型上面,返回不同的结果;

    9. 说一说样式优先级的规则是什么?

            CSS样式的优先级应该分为四大类。

    • 第一类`!important`:无论引进方式是什么,选择器是什么,它的优先级都是最高的;
    • 第二类引进方式,行内样式的优先级要高于嵌入和外链,嵌入和外链如果使用的选择器相同就看他们在页面中插入的顺序,在后面插入的会覆盖前面的;
    • 第三类选择器,选择器优先级:id选择器>(类选择器|伪类选择器|属性选择器)>(后代选择器|伪元素选择器)>(子类选择器|相邻选择器)>通配选择器;
    • 第四类继承样式,是所有样式中优先级比较低的;
    • 第五类浏览器,默认样式优先级最低;

            加分项:

    • 使用!important要谨慎:
    1. 一定要优先考虑使用样式规则的优先级来解决问题而不是`!important`;
    2. 只有在需要覆盖到全站或者外部CSS的特定页面中使用`!important`;
    3. 永远不要在你的插件中使用`!important`;
    4. 永远不要在全站范围的CSS代码中使用`!important`;
    • 优先级的比较指的是相同的样式属性,不同样式属性优先级比较失效;
    1. 在设置`max-width`时注意,已经给元素`max-width`设置了`!important`但是还是不生效,很有可能就是被width覆盖了;
    2. `div`最终的宽度还是`200px`:div{max-width:400px !important; height:200px; backgroud-color:tomato; width:200px;}

    10. 说一说JS实现异步的方法 

            所有的异步任务都是在同步任务执行结束之后,从任务列中一次取出执行。

    • 回调函数式异步操作最基本方法,比如AJAX回调,回调函数的优点是简单、容易理解和实现,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,使得程序结构混乱、流程难以追踪(尤其是多个回调函数嵌套的情况),而且每个任务只能指定一个回调函数。此外它不能使用try catch不火错误,不能直接return;
    • Promise包装了一个异步调用并产生一个Promise实例,当异步调用返回的时候根据调用的结果分别调用实例化时传入的resolve和reject方法,then接收到对应的数据,做出相应的处理。Promise不仅能够捕获错误,而且也很好地解决了回调地狱的问题,缺点是无法取消Promise,错误需要通过回调函数捕获;
    • Generate函数式ES6提供的一种异步编程解决方案,Generator函数是一个状态机,封装了多个内部状态,可暂停函数,yield可暂停,next方法可启动,每次返回的是yield后的表达式结果。优点是异步语义清晰,缺点是手动迭代`Generator`很熟很麻烦,实现逻辑优点绕;
    • async/awt是基于Promise实现的,async/awt使得异步代码看起来像同步代码,所以优点是:使用方法清晰明了,缺点是awt将异步代码改造成了同步代码,如果多个异步代码没有依赖性缺使用了awt会导致性能上的降低,代码没有依赖性的话,完全可以使用Promise.all的方式;

            加分项:

    • 异步编程进化史:callback->promise->generator/yield->async/awt。
    • async/awt函数对Generator函数的改进,体现在以下三点:
    1. 内置执行器:Generator函数的执行必须靠执行器,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
    2. 更广的适用性::yield命令后面只能是Thunk函数或Promise对象,而async函数的awt命令,可以跟Promise对象和原始类型的值(数值、字符串和布尔值,但此时等同于同步操作)。
    3. 更好的语义:async和awt,比起星号和yield,语义更清楚了。async表示函数里有异步操作,awt表示紧跟在后面的表达式需要等待结果。
    • 目前使用很广泛的就是promise和async/awt;
    • 异步微任务:回调函数,promise,async/await;
    • 异步宏任务:setTimeout,setIntervial;

    11.  说一说Vue2.0 双向绑定的原理与缺陷?

    • Vue响应式指的是:组件的data发生变化,立刻触发试图的更新;
    • 原理:Vue采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理。通过原生js提供的监听数据的API,当数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理。通过原生js提供的监听数据的API,当数据发生变化的时候,在回调函数中修改dom;
    • 核心API:Object.defineProperty,Object.defineProperty;
    • API的使用的作用:用来定义对象属性;
    • API的使用的特点:
    1. 默认情况下定义的数据的属性不能修改;
    2. 描述属性和存取属性不能同时使用,使用会报错;
    • 响应式原理:
    1. 获取属性值会触发getter方法;
    2. 设置属性值会触发setter方法;
    3. 在setter方法中调用修改dom的方法;
    • 加分项:Object.defineProperty的缺点:
    1. 一次性递归到底开销很大,如果数据很大,大量的递归导致调用栈溢出;
    2. 不能监听对象的新增属性和删除属性,需要通过Vue.set();
    3. 无法正确的监听数组的方法,不能监听通过下标改变数组对应数据;
    • 总结:
    1. 原理:通过初始化最后一步observe遍历对象 ,对每个属性进行defineproperty劫持数据的setter和getter,结合发布-订阅模式,getter收集依赖(发布),setter派发更新(订阅)。
    2. 缺陷:无法对新添加或删除的属性进行监听、无法监听数组的变化。
  • 相关阅读:
    生成蛇形矩阵——python
    索引的弊端和查找sql慢的原因(面试)
    java毕业生设计园艺生活网站计算机源码+系统+mysql+调试部署+lw
    软件测试面试真题 | Selenium 的工作原理是什么?
    @Validated指定校验顺序
    基于真理和逻辑之后的行动:年少轻狂、中年压力、老年迟疑
    idea 中配置 maven
    Python学习之CSDN21天学习挑战赛计划之16
    【六】sql 语言 -- 复杂查询与视图
    解决了个bug,想说点啥但又难以启齿
  • 原文地址:https://blog.csdn.net/weixin_53919192/article/details/126696842