前端面试一般问些什么(前端面试常问的题目(持续更新中))
1 、闭包问题
什么是闭包?答:“闭包就是能够读取其他函数内部变量的函数
造成闭包的原因?答:
解决方法?答:可以使用let 、立即执行函数
闭包可以实现什么?可以访问局部变量 、可以保护变量不受污染2 、ajax 、axios 前后端通信的过程(原生的ajax怎么写)
原生ajax请求步骤get:
1.创建核心的对象(new xhrhttprequest对象)
2.编写回调函数(判断是否请求成功 状态码等于4或者等于200)
3.调用open方法 设置请求方式和请求路径
4.调用send方法 发送
post:要多设置一个请求头setrequestheader(在发送的前面)3 、跨域问题(具体实现)
答:跨域解决方式:
1.jsonp(script这个标签本来就有跨域的能力 ,然后将ajax中datatype中类型改为jsonp)缺点只支持get
2.利用cors设置头 允许跨域(头有哪些)优点:支持get和post两种
使用普通的XMLHttpRequest发起请求和获得数据 ,比起JSONP有更好的错误处理
3.利用proxy代理(具体实现不管是否跨域 ,在自己服务器上中转一次 ,让它允许跨域 ,类似于一个中转站的意思)4 、浏览器的渲染流程
这个问题应该就是输入url到页面呈现问题的变种 ,只不过此时的侧重点是获取完数据之后进行的渲染流程 。
根据李兵老师的浏览器工作原理一节做如下回答:
第一步 ,HTML转换成DOM
第二步 ,CSS转换成浏览器可理解的styleSheets ,然后计算DOM节点的样式
第三步 ,创建布居树 ,计算元素的布局信息
第四步 ,对布居树进行分层,构建分层树
第五步 ,为每个图层生产绘制列表 ,并将其提交到合成线程
第六步,合成线程将图层转化为图块 ,进而将图块转化成位图
第七步 ,合成线程发送绘制命令给浏览器
第八步 ,浏览器根据绘制命令生成页面 ,并显示到显示器上 。
5 、deffer和async的区别
浏览器脚本 ,在普通的情况下 ,是会依次执行 。但是我们可以用deffer和async关键字来让脚本异步执行 。
但是 ,deffer是按照加载顺序执行DOMContentLoaded之前执行 ,但是async则是脚本加载完毕之后立即执行(不考虑依赖以及DOM的加载状态) ,一般来说 ,deffer要比async好一点 。
6 、map和forEach的区别
forEach返回undefined ,map会返回新的数组 。
forEach没办法中止循环 ,但是map可以通过返回false或者出错来中止 。
7 、call 、apply以及bind的区别 。
三者都是改变this执行,不同的是 ,call和apply是直接生成了函数调用 ,而bind则是返回了一个函数,你需要再次执行才会达到相同的效果 。
call和apply又是因为参数的传递方式不一样 ,apply传递的是数组 ,call传递的单个参数的陈列 。
bind则是以函数调用参数的方式传递参数 。
8 、事件循环 。
JavaScript是单线程执行模型 ,执行的时候将会区分为主线程和任务队列。主线程执行完毕 ,会从任务队列中读取新的任务放入主线程进行执行 ,这个读取过程是循环读取 ,所以也叫事件循环 。
任务队列分为宏任务和微任务 ,同层次 ,先执行微任务 ,再执行宏任务 。
微任务:promise.then()、process.nextTick()
宏任务:setTimeOut() 、setInterval()
9 、get和post请求区别
GET在浏览器回退时是无害的 ,而POST会再次提交请求。GET产生的URL地址可以被Bookmark ,而POST不可以 。
GET请求会被浏览器主动cache ,而POST不会,除非手动设置 。
GET请求只能进行url编码 ,而POST支持多种编码方式 。
GET请求参数会被完整保留在浏览器历史记录里 ,而POST中的参数不会被保留 。
GET请求在URL中传送的参数是有长度限制的,而POST么有 。
对参数的数据类型 ,GET只接受ASCII字符 ,而POST没有限制 。
GET比POST更不安全 ,因为参数直接暴露在URL上 ,所以不能用来传递敏感信息 。
GET参数通过URL传递 ,POST放在Request body中 。
GET产生一个TCP数据包;POST产生两个TCP数据包 。
GET与POST都有自己的语义 ,不能随便混用 。
10、双向绑定原理
vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的我们已经知道实现数据的双向绑定 ,首先要对数据进行劫持监听 ,所以我们需要设置一个监听器Observer ,用来监听所有属性 。如果属性发上变化了 ,就需要告诉订阅者Watcher看是否需要更新 。因为订阅者是有很多个 ,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者 ,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile ,对每个节点元素进行扫描和解析 ,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher ,并替换模板数据或者绑定相应的函数 ,此时当订阅者Watcher接收到相应属性的变化 ,就会执行对应的更新函数 ,从而更新视图 。
11 、Async和Await原理
async 、await从字面上理解 ,async就是异步的意思 ,await就是等待的意思 ,而两者的用法上也是这样的 ,async用于申明一个function是异步的 ,而await用于等待一个异步方法执行完成
过程就是:
1 ,async就是一个async函数 ,而await只能在这个函数中使用
2 ,await表示这里等待await后面的操作执行完毕,再执行下一句代码
3 ,await后面紧跟的最好是一个定时操作或者一个异步操作
async和await的优点1 ,解决了回调地狱的问题
2,支持并发执行
3 ,可以添加返回值 return xxx
4 ,可以在代码中添加try/catch捕获错误12 、webpack工作原理
1 、核心概念(1)entry:一个可执行模块或者库的入口 。
(2)chunk:多个文件组成一个代码块。可以将可执行的模块和他所依赖的模块组合成一个chunk ,这是打包 。
(3)loader:文件转换器 。例如把es6转为es5 ,scss转为css等
(4)plugin:扩展webpack功能的插件 。在webpack构建的生命周期节点上加入扩展hook ,添加功能
2 、webpack构建流程(原理)
从启动构建到输出结果一系列过程:
(1)初始化参数:解析webpack配置参数 ,合并shell传入和webpack.config.js文件配置的参数 ,形成最后的配置结果 。
(2)开始编译:上一步得到的参数初始化compiler对象 ,注册所有配置的插件 ,插件监听webpack构建生命周期的事件节点 ,做出相应的反应 ,执行对象的 run 方法开始执行编译 。
(3)确定入口:从配置的entry入口 ,开始解析文件构建AST语法树,找出依赖 ,递归下去 。
(4)编译模块:递归中根据文件类型和loader配置 ,调用所有配置的loader对文件进行转换,再找出该模块依赖的模块 ,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理 。
(5)完成模块编译并输出:递归完事后 ,得到每个文件结果 ,包含每个模块以及他们之间的依赖关系 ,根据entry配置生成代码块chunk 。
(6)输出完成:输出所有的chunk到文件系统 。
注意:在构建生命周期中有一系列插件在做合适的时机做合适事情 ,比如UglifyPlugin会在loader转换递归完对结果使用UglifyJs压缩覆盖之前的结果
13 、webpack的loader和plugin区别
【Loader】:用于对模块源码的转换 ,loader描述了webpack如何处理非javascript模块 ,并且在buld中引入这些依赖 。loader可以将文件从不同的语言(如TypeScript)转换为JavaScript ,或者将内联图像转换为data URL 。比如说:CSS-Loader ,Style-Loader等 。loader的使用很简单:
在webpack.config.js中指定loader。module.rules可以指定多个loader ,对项目中的各个loader有个全局概览 。
loader是运行在NodeJS中 ,可以用options对象进行配置 。plugin可以为loader带来更多特性。loader可以进行压缩 ,打包,语言翻译等等 。
loader从模板路径解析 ,npm install node_modules 。也可以自定义loader ,命名XXX-loader 。
语言类的处理器loader:CoffeeScript,TypeScript ,ESNext(Bable),Sass,Less,Stylus 。任何开发技术栈都可以使用webpack 。
【Plugin】:目的在于解决loader无法实现的其他事 ,从打包优化和压缩 ,到重新定义环境变量 ,功能强大到可以用来处理各种各样的任务 。webpack提供了很多开箱即用的插件:CommonChunkPlugin主要用于提取第三方库和公共模块 ,避免首屏加载的bundle文件 ,或者按需加载的bundle文件体积过大 ,导致加载时间过长 ,是一把优化的利器 。而在多页面应用中 ,更是能够为每个页面间的应用程序共享代码创建bundle 。
14 、promise 与 async await
async/await是写异步代码的新方式 ,以前的方法有回调函数和Promise 。
async/await是基于Promise实现的 ,它不能用于普通的回调函数 。
async/await与Promise一样 ,是非阻塞的 。
async/await使得异步代码看起来像同步代码,这正是它的魔力所在函数前面多了一个aync关键字 。await关键字只能用在aync定义的函数内。async函数会隐式地返回一个promise ,该promise的reosolve值就是函数return的值 。(示例中reosolve值就是字符串 ”done ”)
为什么Async/Await更好?
1)使用async函数可以让代码简洁很多 ,不需要像Promise一样需要些then,不需要写匿名函数处理Promise的resolve值 ,也不需要定义多余的data变量 ,还避免了嵌套代码 。
2) 错误处理:
Async/Await 让 try/catch 可以同时处理同步和异步错误。在下面的promise示例中 ,try/catch 不能处理 JSON.parse 的错误 ,因为它在Promise中 。我们需要使用 .catch ,这样错误处理代码非常冗余 。并且 ,在我们的实际生产代码会更加复杂 3) 使用async/await的话 ,代码会变得异常简单和直观 。
4)错误栈
如果 Promise 连续调用 ,对于错误的处理是很麻烦的 。你无法知道错误出在哪里 。async/await中的错误栈会指向错误所在的函数 。在开发环境中 ,这一点优势并不大 。但是 ,当你分析生产环境的错误日志时 ,它将非常有用 。这时 ,知道错误发生在makeRequest比知道错误发生在then链中要好 。
5)调试
async/await能够使得代码调试更简单 。2个理由使得调试Promise变得非常痛苦:《1》不能在返回表达式的箭头函数中设置断点
《2》如果你在.then代码块中设置断点,使用Step Over快捷键 ,调试器不会跳到下一个.then ,因为它只会跳过异步代码 。使用await/async时,你不再需要那么多箭头函数 ,这样你就可以像调试同步代码一样跳过await语句 。
15 、重绘和汇流有什么区别
什么是回流 ,什么是重绘 ,有什么区别?
html 加载时发生了什么:
在页面加载时 ,浏览器把获取到的HTML代码解析成1个DOM树 ,DOM树里包含了所有HTML标签 ,包括display:none隐藏 ,还有用JS动态添加的元素等。
浏览器把所有样式(用户定义的CSS和用户代理)解析成样式结构体
DOM Tree 和样式结构体组合后构建render tree, render tree类似于DOM tree ,但区别很大 ,因为render tree能识别样式 ,render tree中每个NODE都有自己的style ,而且render tree不包含隐藏的节点(比如display:none的节点 ,还有head节点),因为这些节点不会用于呈现 ,而且不会影响呈现的 ,所以就不会包含到 render tree中 。我自己简单的理解就是DOM Tree和我们写的CSS结合在一起之后,渲染出了render tree 。
什么是回流:
当render tree中的一部分(或全部)因为元素的规模尺寸 ,布局 ,隐藏等改变而需要重新构建。这就称为回流(reflow) 。每个页面至少需要一次回流 ,就是在页面第一次加载的时候 ,这时候是一定会发生回流的 ,因为要构建render tree 。在回流的时候 ,浏览器会使渲染树中受到影响的部分失效 ,并重新构造这部分渲染树 ,完成回流后 ,浏览器会重新绘制受影响的部分到屏幕中 ,该过程成为重绘 。什么是重绘:
当render tree中的一些元素需要更新属性 ,而这些属性只是影响元素的外观 ,风格,而不会影响布局的 ,比如background-color 。则就叫称为重绘 。区别:
他们的区别很大:
回流必将引起重绘 ,而重绘不一定会引起回流 。比如:只有颜色改变的时候就只会发生重绘而不会引起回流
当页面布局和几何属性改变时就需要回流
比如:添加或者删除可见的DOM元素,元素位置改变 ,元素尺寸改变——边距 、填充 、边框 、宽度和高度 ,内容改变扩展:
浏览器的帮忙
所以我们能得知回流比重绘的代价要更高 ,回流的花销跟render tree有多少节点需要重新构建有关系
因为这些机制的存在 ,所以浏览器会帮助我们优化这些操作 ,浏览器会维护1个队列 ,把所有会引起回流 、重绘的操作放入这个队列 ,等队列中的操作到了一定的数量或者到了一定的时间间隔 ,浏览器就会flush队列 ,进行一个批处理 。这样就会让多次的回流、重绘变成一次回流重绘 。自己的优化
靠浏览器不如靠自己 ,我们可以改变一些写法减少回流和重绘
比如改变样式的时候 ,不去改变他们每个的样式 ,而是直接改变className 就要用到cssText 但是要注意有一个问题,会把原有的cssText清掉 ,比如原来的style中有’display:none;’ ,那么执行完上面的JS后,display就被删掉了 。
为了解决这个问题 ,可以采用cssText累加的方法 ,但是IE不支持累加 ,前面添一个分号可以解决 。
还有添加节点的时候比如要添加一个div里面有三个子元素p ,如果添加div再在里面添加三次p ,这样就触发很多次回流和重绘 ,我们可以用cloneNode(true or false) 来避免 ,一次把要添加的都克隆好再appened就好了 ,还有其他很多的方法就不依依说了创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!