当前位置: 首页 > Web前端 > HTML

面试题总结_0

时间:2023-04-02 17:30:07 HTML

1. vue的双向绑定原理:vue的双向绑定原理是通过Object.definedProperty的getter和setter来对属性进行数据劫持的。因为Object.definedProperty最低是支持到浏览器IE9的,所以如果想要兼容IE8,只能去做Object.definedProperty的兼容,那最终使用的是做的兼容,而不是Object.definedProperty.找了一张图片来表示响应原理。首先Object.definedProperty会对data的每个属性进行数据的劫持,当我们把data属性的值改了,就会触发它的setter,然后通知到watcher,watcher再更新指令所绑定的属性的值。通过Observer对data做监听,并提供了订阅某个数据项变化的能力把template编译成一段document fragment,然后解析其中的Directive,得到每一个Directive所依赖的数据项和update方法。通过Watcher把上述2部分结合起来,即把Directive中的数据依赖通过Watcher订阅在对应数据的Observer的Dep上,当数据变化时,就会触发Observer的Dep上的notify方法通知对应的Watcher的update,进而触发Directive的update方法来更新dom视图,最后达到模型和视图关联起来。2. vue的钩子函数:依次为:beforeCreate ==> created ==> beforeMount ==> mounted ==> beforeUpdate ==> updated ==> beforeDestroy ==> destroyed 初始化的时候只有一次,只有当数据更改的时候才会去触发update钩子3. vue的method,computed,watch的区别:computed是对结果进行缓存的,只要依赖的值没有变化,结果就不会变化。method就是普通的方法。computed减少执行的getter减去了重复计算,节省内存。watch是一直在监听。比如this.num + new Date(),虽然new Date的值一直在变,但只要this.num没变,结果还是一样的。4. flex做骰子的3点:html:<div class="box"> <div class="item"></div> <div class="item"></div> <div class="item"></div></div>style:.box{ display:flex;}.item:nth-child(2){ align-self:center;}.item:nth-child(3){ align-self:right;}5. css的伪类: :first-child/:last-children //选择第一个孩子,:nth-of-type(n) :checked/:disabled //选择checkbox已选中的 :afeter/:before //标签的伪类 :not(selecter) //非元素的其它元素 6. 三行文本垂直居中:1.加上下一样的padding值,达到上下居中的目的。2.利用table<div class="wrapper"> <div class="cell"> <p></p> <p></p> <p></p> </div></div>.wapper{ displaty:table; }.cell{ display:table-cell; vertical-align:center;}3.{position:relative;top:50%;transform:translateY(-50%)}4.通过box:<div class="center"> <div> <p></p> <p></p> <p></p> </div></div>.center{ display:box; box-orient:horizontal; box-pack:center; box-align:center;}5.flex:<div class="flex"> <div> <p></p> <p></p> <p></p> </div></div>.flex{ display:flex; align-items:center;}7. 跨域的方法:浏览器为了安全机制,采用同源策略,域名,协议,端口号一致的才可以进行访问,否则容易受到XSS、CSRF等攻击;跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了1,jsonp:是通过script标签的src属性来实现跨域的。通过src传过去一个函数,把数据放在函数的实参调用就可以拿到数据。由于是用src的链接,所以jsonp只支持get方式。content-type:javascript2,cors:改变请求头信息。客户端加:Origin:地址。服务器:Access-Control-Allow-Origin:地址.支持IE10以上。3,webpack:devServer里配置proxy:{api:'地址'};实际是http-proxy-middleware这个中间件,同源策略是浏览器遵循的标准,如果是服务器向服务器请求就不需要.接受客户端请示-->将请求转发给服务器-->拿到服务器响应数据-->将响应转发给客户端浏览器 <--转发响应 请求--> 代理服务器 <--响应请求 转发请求--> 服务器// server1.js 代理服务器(http://localhost:3000)const http = require('http')// 第一步:接受客户端请求const server = http.createServer((request, response) => { // 代理服务器,直接和浏览器直接交互,需要设置CORS 的首部字段 response.writeHead(200, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': '*', 'Access-Control-Allow-Headers': 'Content-Type' }) // 第二步:将请求转发给服务器 const proxyRequest = http .request( { host: '127.0.0.1', port: 4000, url: '/', method: request.method, headers: request.headers }, serverResponse => { // 第三步:收到服务器的响应 var body = '' serverResponse.on('data', chunk => { body += chunk }) serverResponse.on('end', () => { console.log('The data is ' + body) // 第四步:将响应结果转发给浏览器 response.end(body) }) } ) .end()})server.listen(3000, () => { console.log('The proxyServer is running at http://localhost:3000')})4,nginx反向代理:类似于Node中间件代理,只需要搭载一个中转nginx服务器,支持所有浏览器,不会影响性能,不用改代码。通过nginx配置一个代理服务器(域名和aaa相同,端口不同)做跳板机,反向代理访问bbb接口aaa-->bbbnginx.confupstream tomcatserver{ server 192.168.72.49:8081//3.找到代理服务器的ip地址进行请求}server{ listen 81; server_name www.aaa.com;//1.客户端调用名 location / { proxy_pass http://www.bbb.com:8080;//2.到代理服务器 index index.html index.html; }}8. webpack:loader和plugins的区别:loader是负责解析代码的,而且plugins是去做优化,代码整合之类的。new ExtractTextPlugin('styles.css'):分隔出css单独打包;new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: function (module) { // 该配置假定你引入的 vendor 存在于 node_modules 目录中 return module.context && module.context.indexOf('node_modules') !== -1; }})//依赖项不重复打包,分隔模块<LazilyLoad modules={{ TodoHandler: () => importLazy(import('./components/TodoHandler')), TodoMenuHandler: () => importLazy(import('./components/TodoMenuHandler')), TodoMenu: () => importLazy(import('./components/TodoMenu')),}}>//懒加载解析es7用到的babel:babel-core,babel-loader,babel-preset-es2015,babel-preset-stage-2,babe-plugin-transform-runtime,babel-runtime,babel-register.9. es6的声明方法,es5:var,function:var:会存在变量提升,如果在声明之前用到会报undefined.let:不存在变量提升,如果在声明之前用到会报错。暂时性死区。块级作用域。const:声明之后就不能改变。同上,如果是对象的话,只是指向引用的地址,所以对象里面的值改变了,是没有任何反应的。function:声明属于window.functionclass:声明一个类import:声明一个引用过来的模块10. 性能优化:压缩css,js:体积更小,加载速度更快。css在前,js在后:css在前可以和dom树一起合成render树,js在后不阻塞dom渲染。减少http请求:http请求需要时间。而且要等到它请求完才能执行。请求是异步的,不知道什么时候才能请求完。webpack配置:按需加载。分离css。分隔依赖,把相同的依赖只打包到一起,不必重复加载。11. 异步管理:promise:promise等到执行完成后返回2种状态,resolve代表成功,reject代表失败。如果有多个异步可以用promise.all([]).async await:async声明一个函数返回一个promise。await等到promise异步执行结束拿到的一个结果12. angularJS双向绑定实现原理:脏值检测:angular在scope模型上设置了一个监听队列,用来监听数据变化并更新view,每次绑定一个东西到view上时angular就会往$watch队列插入一条$watch,用来检测它监视的model里是否有变化的东西(一个数据一个$watcher,对象会有一个,里面的值还会有,数组中每个对象也都会有一个)。这些$watch列表会在$digest循环中通过一个叫做‘脏值检测’的程序解析。angular对大部分能产生数据变动的事件进行封装(如click,mouse-enter,timeout),在每次事件发生后,比如更改了scope中的一条数据,angular会自动调用$digest来触发一轮$digest循环,它会触发每个watcher,检查scope中的当前model值是否和上一次计算得到的Model值是否一样,如不同,对应的回调函数会被执行,调用该函数的结果,就是view中的表达式内容会被更新。如果执行了非angular的方法,如setTimeout需要调用$apply()应用到angular上,它会调用$rootScope.$digest()。因此,一轮$digest循环在$rootScope开始,随后会访问到所有的children scope中的watchers。$apply()里面可以加参数,而且会触发作用域树上所有的监控。$digest()只作用在当前作用域和它的子作用域上。angular服务:sevicer对象的实例化this.xx=。factory返回一个对象return{a:xx}13. 在arr=[1,2,4],4之前插入3arr.splice(2,0,3)14. json字符串与json对象的转换:在数据传输过程中,json是以文本,即字符串的形式传递的。而js操作的是json对象。转对象:str.parseJSON(),JSON.parse(str),eval('('+str+')')转字符串:obj.toJSONString(),JSON.stringify(obj)15. requestAnimationFrame和setTimeout/setInterval:因为js是单线程的,setTimeout/setInterval是在定时器线程,要等主线程走完了,才会执行事件队列。如果主线程的计算执行时间过长,那么定时器就要一直等着不能执行,就导致了,动画卡,或者一下堆在一起执行重叠过快。requestAnimationFrame不需要设置时间间隔。IE9以下不支持。cancelAnimationFrame()用来取消。16. 原型链:每一个构造函数都有自己的原型对象,用prototype属性来表示。每个原型对象都有一个隐式的_proto_属性指向它父级的原型对象。如:let a= new A() a._proto_=A.prototypea._proto_._proto_=A.prototype._proto_=Object.prototypea._proto_._proto_._proto_=A.prototype._proto_._proto_=Object.prototype._proto_=null17. 闭包:闭包简单来说就是函数套函数。我们在函数内声明的变量叫局部变量,局部变量只能在里面访问,外面是访问不到的。如果我们想访问局部变量就可以在函数的内部在写一个函数,根据作用域链的原则就可以往上找父级的变量。闭包会导致内存泄露的问题,解决办法是用过之后把这个变量清空还原。18. arguments:arguments是一个对应于传递给函数的参数的类数组对象。它是一个类似于数组的对象,但还不是数组。arguments.length:实参长度。arguments.callee.length:形参长度可以用[].slice.call(arguments);或Array.from(arguments);[...arguments]19. get和post:get一般表示从服务器获取数据,post是向服务器传递数据。get的方式是url,因为在地址栏所以长度有限,而且地址栏的内容可以看到,所以保密性较差。而post是放在requst body。对长度没有限制,保密性也较好。get一般用于搜索。post一般用于表单提交。20.String+Number :3 + '3' = 33undefined + 3 = NaN //undefined为NaN,null + 3 = 3 //null为空。'null' + 3 = 'null3'当有+的时候会检查是否有类型为string的,如果有就会把其它值也转化为string格式进行字符连接。如果没有string,有number,那么会把其它值转化为number类型的值执行加法运算。21. 循环for(vari=0,j=0;i<5,j<10;i++,j++){ }console.log(i+j) //2022. 给数组加一个数组去重的方法:Array.prototype.q=function(arr){ let obj={},a=[]; for(let i=0;i<arr.length;i++;){ if(!obj[arr[i]]){ obj[arr[i]]=true; a.push(arr[i]) } } return a;}let arr=[3,4,4,5,6,7,72,4,3];console.log(Array.q(a));23. 类的继承:es6:extendses5:1.构造函数绑定:function Cat(name,color){ Animal.apply(this,arguments); this.name=name; this.color=color;}2.利用prototype:function Cat(name,color){ this.name=name; this.color=color;}Cat.prototype=new Animal();Cat.prototype.constructor=Cat;3.组合继承:function Cat(){ Animal.call(this)}Cat.prototype=new Animal();4.通过空对象:function extend(Child,Parent){ var F=function(){}; F.prototype=Parent.prototype; Child.prototype=new F(); Child.prototype.constructor=Child;}function Animal(){}Animal.prototype.species='动物';function Cat(name){ this.name=name;}extend(Cat,Animal)5.拷贝继承:function extend2(Child,Parent){ var p=Parent.prototype; var c=Child.prototype; for(var i in p){ //浅拷贝 c[i]=p[i] }}extend2(Cat,Animal)24. map和forEach的区别:forEach不能breack,也不能return.map循环每一个的时候就相当于在内存中新建了一个数据,比较占内存25. 获取url地址:window.location.href26. async的好处:最开始的异步都是用回调函数解决的,比如ajax,setTimeout,addEventListener.但是如果回调函数过多就会形成回调地狱。那么promise就是为解决这一问题的。promise可以用链式写法等到异步有结果再进行下一步。但如果promise写多了也会变的不直观,async的await等到promise的返回。可以让代码看起来更加简洁高效。27. 解构出a.b.c的值:let obj={a:{b:{c:1}}}let {a:{b:{c}}}=objconsole.log(c) //128.react的生命周期:29. react与vue的对比:共同点:数据驱动视图,组件化,虚拟DOM;数据驱动视图:不用再操作DOM的方式,只用关注依赖的数据变化即可。组件化:都遵循组件化思想,主要在UI层分细块的组件,组件嵌套都有父子组件传递。都有数据状态管理,前端路由,插槽等。虚拟DOM:都使用了Virtual DOM+diff算法,不管是vue的template+options api写法还是react的class或function写法,最后都是生成render函数,而render函数执行返回VNode。每一次UI更新都会根据render重新生成最新的VNode,然后跟以前缓存起来的老VNode对比,再使用diff算法去更新真实的DOM(虚拟DOM是js对象存在js引擎中。而真实DOM在浏览器渲染引擎中,所以操作虚拟DOM比操作真实DOM开销要小的多)不同点:核心思想不同,组件写法不同,diff算法不同,响应式原理不同,其它不同核心思想不同: vue的定位是降低前端开发门槛,容易上手,所以vue的主要特点:灵活易用的渐进式框架,对数据进行劫持/代理,侦测数据变化更敏感精确。 react背靠Fackbook,定位是UI开发新思想,用更好的方式颠覆前端开发方式。所以推崇函数式编程(纯组件),数据不可变及单向数据流。组件写法不同:react推荐JSX+inline style,就是把html和css都写进javascript,就是all in js。vue就是传统的html,css,js,用template的单文件组件格式。diff算法不同:传统diff算法是循环递归每一个节点,合起来diff2棵树的复杂度就是O(n3)。vue和react有相同的diff优化原则将算法复杂度降到了O(n)。不同的组件产生不同的DOM结构,当type不同时,对应DOM操作就是直接销毁老的DOM,创建新DOM同一层的一级子节点,可以通过唯一key区分react的核心算法实现:react首先对新集合遍历,for(name in nextChildren)通过唯一key来判断老集合中是否存在的节点,没有就创建有的话:if(preChild===nextChild)将节点在新集合的位置和在老集合中lastIndex进行比较。如果if(child._mountIndex<lastIndex)进行移动操作,否则不移动。如果遍历中,发现新集合中没有,老集合中有的节点,会删除掉vue的核心算法实现:updateChildren是vue diff的核心:旧children和新children各有2个头尾的变量startIdx和endIdx,它们的2个变量相互比较,共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key比较,比较过程中变量会往中间走,一旦startIdx>endIdx说明旧children和新children至少有一个已经遍历完了,会结束比较。vue的diff采用了双端比较的算法,同时从新旧children的2端开始进行比较,借助key找到可以复用的节点,再进行相关操作。对比react可以减少节点移动次数,减少性能损耗,更加优雅。响应式原理不同:vue:依赖收集,自动优化数据可变。递归监听data的所有属性,可以直接=改。当数据改变时,自动找到引用组件重新渲染。react:基于状态机,手动优化,数据不可变,需要setState驱动新的state替换老的state。当数据改变时,以组件为根目录,默认全部重新渲染,所以react中会需要shouldComponentUpdate生命周期函数进行优化控制。比如api的差异也挺大的,vue为了更加简单易用加了指令,filter,以及watch,computed等概念。react的api就比较少了,相对来说react没有上限也没有下限,但如果js基础不够好,代码也会写的一蹋糊涂。react源码编译过程vue源码编译过程https://www.cnblogs.com/vicky...30. 计算数组中元素出现的次数,并最多次数的值:function getMost(arr) { let max=null; //出现次数最多的元素 let num=1; //该元素出现次数 let sum=arr.reduce((p,k)=>{//出现次数 p[k]?p[k]++:p[k]=1; //k代表当前正在遍历的元素。应用到p[k]里,k表示p对象里的一个键,p[k]表示该键对应的值。 if(p[k]>num){//最大值 num=p[k] max=k } return p;//出现次数的对象 },{}); return {max:max,num:num} }let arr= ["lujj","lujj2","lujj2","lujj","lujj"];getMost(arr)31. Hooks为什么不能写在条件语句下:react官网hook限制:不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。总的来说:函数式组件第一次渲染时,像多个useState,useEffect它们没有各自的key,所以会把带use的hooks缓存到一个单向链表中,react根据顺序去区分找对应的state。如果在带有条件中写,那么当重新渲染时use的顺序和之前缓存的值顺序会对不上,可能会导致执行错误的hooks。