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

前端高频面试题(三)(附答案)

时间:2023-03-26 23:05:39 JavaScript

告诉我JSON.stringify有什么缺点?1.如果obj中有时间对象,那么JSON.stringify后JSON.parse的结果,时间只会是字符串的形式,不会是对象的形式。2、如果有RegExp(正则表达式的缩写),Errorinobj对象,序列化的结果只会得到一个空对象;3.如果obj中有一个函数,undefined,序列化的结果会丢失这个函数或者undefined;4.如果obj中存在NaN、Infinity和-Infinity,则序列转换的结果会变成null5。JSON.stringify()只能序列化对象的可枚举自身属性。例如obj中的对象是由构造函数生成的,使用JSON.parse(JSON.stringify(obj))深拷贝后,对象的构造函数会被丢弃;6.如果对象中存在循环引用,则无法正确实现深拷贝;newoperator题目描述:手写newoperator的实现代码如下:functionmyNew(fn,...args){letobj=Object.create(fn.prototype);让res=fn.call(obj,...args);if(res&&(typeofres==="object"||typeofres==="function")){returnres;}returnobj;}用法如下:////functionPerson(name,age){////this.name=name;////this.age=age;////}////Person.prototype.say=function(){////console.log(this.age);////};////letp1=myNew(Person,"lihua",18);////console.log(p1.name);////console.log(p1);////p1.say();前端高级面试题详解eventbus(发布-订阅模式)classEventEmitter{constructor(){this.cache={}}on(name,fn){if(this.cache[name]){this.cache[name].push(fn)}else{this.cache[name]=[fn]}}off(name,fn){lettasks=this.cache[name]if(tasks){constindex=tasks.findIndex(f=>f===fn||f.callback===fn)if(index>=0){tasks.splice(index,1)}}}emit(name,once=false,...args){if(this.cache[name]){//创建副本,如果回调函数继续注册同一个事件,会造成死循环lettasks=this.cache[name].slice()for(letfnoftasks){fn(...args)}if(once){deletethis.cache[name]}}}}//testleteventBus=newEventEmitter()letfn1=function(name,age){console.log(`${name}${age}`)}letfn2=function(name,age){console.日志(`你好,${name}${age}`)}eventBus.on('aaa',fn1)eventBus.on('aaa',fn2)eventBus.emit('aaa',false,'Bran',12)//'Bran12'//'hello,Bran12'Arrayflattening数组扁平化就是将多层数组如[1,[2,[3]]]扁平化为一层[1,2,3]使用Array.prototype.flat可以直接将多层数组扁平化为一层:[1,[2,[3]]].flat(2)//[1,2,3]现在是实现扁平化的效果。ES5实现:递归。functionflatten(arr){var结果=[];对于(vari=0,len=arr.length;iArray.isArray(项目))){arr=[].concat(...arr);}returnarr;}正向代理和反向代理的区别获取。于是客户端设置代理服务器并指定目标服务器,然后代理服务器将请求转发给目标服务器,并将获取到的内容发送给客户端。这本质上是为了将真实客户端从真实服务器中隐藏起来。实现正向代理需要修改客户端,比如修改浏览器配置。反向代理:为了提高网站性能(负载均衡),通过将工作负载分到多个服务器上,服务器在收到请求时会先根据转发规则判断将请求转发到哪个服务器,然后转发请求到对应的真实服务器。这本质上是为了向客户端隐藏真实服务器。一般使用反向代理后,需要修改DNS,将域名解析到代理服务器IP。此时浏览器无法检测到真实服务器的存在,当然也不需要修改配置。正向代理和反向代理的结构相同,都是客户端-代理-服务器结构。它们之间的主要区别在于哪一方在中间设置代理。在正向代理中,代理由客户端设置,隐藏客户端;而在反向代理中,代理由服务器设置以隐藏服务器。同步和异步的区别Synchronization是指当一个进程在执行请求时,如果请求需要等待一段时间返回,那么进程会一直等到消息返回后再继续执行。异步是指当一个进程在执行请求时,如果请求需要等待一段时间才能返回,此时进程会继续执行,不会阻塞等待消息的返回。当消息返回时,系统会通知进程继续进行。处理。代码输出问题functionA(){}functionB(a){  this.a=a;}functionC(a){  if(a){this.a=a;  }}A.prototype.a=1;B.prototype.a=1;C.prototype.a=1;console.log(newA().a);console.log(newB().a);控制台。日志(新C(2).a);输出结果:1undefined2分析:console.log(newA().a),newA()是构造函数创建的对象,它本身没有属性,所以在里面添加找到prototype的prototype,发现原型的a属性的属性值为1,所以输出值为1;console.log(newB().a),ewB()是构造函数创建的对象,构造函数有参数a,但是对象没有传递参数,所以输出值是undefined;console.log(newC(2).a),newC()是构造函数创建的一个对象,它有一个参数a,而其实参为2,执行里面的函数,发现if为true,执行this.a=2,所以属性a的值为2类数组对象的理解,如何将其转为数组一个对象有一个length属性,还有几个index属性,就可以称为类数组对象.类数组对象类似于数组,但它不能调用数组方法。常见的类数组对象包括参数和DOM方法的返回结果。函数参数也可以看作类数组对象,因为它包含一个length属性值,表示可接受参数的个数。将类数组数组转换为数组有几种常见的方法:调用数组的slice方法实现转换Array.prototype.slice.call(arrayLike);调用数组的splice方法实现转换Array.prototype.splice.call(arrayLike,0);通过apply调用数组的concat方法实现转换Array.prototype.concat.apply([],arrayLike);通过Array.from方法Array.from(arrayLike)实现转换;代码输出结果Promise.resolve().then(()=>{returnnewError('error!!!')}).then(res=>{console.log("then:",res)}).catch(err=>{console.log("catch:",err)})输出结果如下:"then:""Error:error!!!"返回任何非promise值都会被包装成一个promise对象,所以这里的returnnewError('error!!!')也被包装成returnPromise.resolve(newError('error!!!')),所以届时它将被捕获而不是捕获。原型链指向p.__proto__//Person.prototypePerson.prototype.__proto__//Object.prototypep.__proto__.__proto__//Object.prototypep.__proto__.constructor.prototype.__proto__//Object.prototypePerson.prototype.constructor.prototype.__proto__//Object.prototypep1.__proto__.constructor//PersonPerson.prototype.constructor//人代码输出问题window.number=2;varobj={number:3,db1:(function(){console.log(this);this.number*=4;returnfunction(){console.log(this);this.number*=5;}})()}vardb1=obj.db1;db1();obj.db1();控制台.log(obj.number);//15console.log(window.number);//40本题看起来有点乱,其实考察this指向什么:执行db1()时,this指向全局函数域,所以window.number4=8,然后执行匿名函数,所以window.nu??mber5=40;执行obj.db1();时,this指向obj对象,执行匿名函数,所以obj.numer*5=15迭代查询和递归查询其实DNS解析是一个过程,包括迭代查询和递归询问。递归查询是指查询请求发出后,由域名服务器代为向下一级域名服务器发送请求,最后将查询的最终结果返回给用户。使用递归查询,用户只需要发出一次查询请求。迭代查询是指在一次查询请求后,域名服务器返回单次查询的结果。下一级查询由用户自己请求。使用迭代查询,用户需要发出多次查询请求。一般我们向本地DNS服务器发送请求的方式都是递归查询,因为我们只需要发送一次请求,然后本地DNS服务器返回给我们最终的请求结果。本地DNS服务器请求其他域名服务器的过程是一个迭代查询的过程,因为每个域名服务器只返回单次查询的结果,下一级查询由本地DNS服务器自己进行。写代码:实现可以深度克隆基本类型的函数浅克隆:functionshallowClone(obj){letcloneObj={};for(letiinobj){cloneObj[i]=obj[i];}returncloneObj;}deepclone:考虑引用类型的基本类型RegExp、Date和非JSON安全的函数。构造函数将丢失。所有构造函数都指向Object以打破循环引用functiondeepCopy(obj){if(typeofobj==='object'){varresult=obj.constructor===Array?[]:{};for(variinobj){结果[i]=typeofobj[i]==='对象'?deepCopy(obj[i]):obj[i];}}else{varresult=obj;}returnresult;}VirtualDom的优势是什么?AdvantagesofVirtualDom》这个问题其实面试官更想听到的答案并不是说“直接操作/频繁操作DOM性能差”。如果DOM操作性能这么差,那么jQuery不会活到今天,所以面试官想听听VDOM想解决的问题,为什么频繁的DOM操作会导致性能不佳。首先,我们需要知道:DOM引擎和JS引擎是相互独立的,但是它们工作在同一个线程(主线程)中。JS代码调用DOMAPI时,必须挂起JS引擎,转换传入的参数数据,激活DOM引擎,转换前重绘DOM可能会有一些返回值,最后激活JS引擎继续执行。如果DOMAPI调用频繁,浏览器厂商又不优化“批处理”,引擎间切换的单位成本会快速累积。如果DOMAPI调用有强制重绘,重新计算布局,重绘图片会造成较大的性能消耗。二是VDOM与真实DOM的区别和优化:虚拟DOM不会立即进行排版和重绘操作,虚拟DOM会被频繁修改,然后在真实DOM中对比修改需要改动的部分一次,最后在真实DOM中进行排版和重绘重绘,减少DOM节点过多的布局和重绘损失虚拟DOM可以有效减少大面积真实DOM的重绘和布局,因为它最终不同于真正的DOM,并且只能渲染本地JavaScript其中内置对象(全局对象(globalobjects))或标准内置对象,不要与“全局对象”混淆。这里所说的全局对象是指全局范围内的对象。全局范围内的其他对象可以由用户脚本创建或由宿主程序提供。标准内置对象的分类:(1)值属性,这些全局属性返回一个简单的值,没有自己的属性和方法。例如Infinity、NaN、undefined、null字面量(2)函数属性,全局函数可以直接调用,调用时无需指定对象,执行后直接返回结果给调用者。例如eval()、parseFloat()、parseInt()等。(3)基础对象,是定义或使用其他对象的基础。基本对象包括一般对象、函数对象和错误对象。如Object、Function、Boolean、Symbol、Error等。(4)数字和日期对象,用来表示数字、日期和进行数学计算的对象。例如,Number、Math、Date(5)字符串,用于表示和操作字符串的对象。例如String、RegExp(6)可索引集合对象,这些对象表示按索引值排序的数据集合,包括数组和类型数组,类数组结构的对象。例如,Array(7)使用键控集合对象。这些集合对象在存储数据时会使用key,支持按插入顺序迭代元素。例如Map,Set,WeakMap,WeakSet(8)向量集合,SIMD向量集合中的数据会被组织成一个数据序列。如SIMD等(9)结构化数据,这些对象用于表示和操作结构化缓冲区数据,或使用JSON编码的数据。例如JSON等。(10)控制Promise、Generator等抽象对象。(11)Reflection。例如Reflect、Proxy(12)国际化,为了支持多语言处理而加入ECMAScript的对象。例如Intl、Intl.Collat??or等。(13)WebAssembly(14)其他。比如参数总结:js中的内置对象主要是指js定义的一些全局值属性、函数和构造函数对象,在程序执行前就存在于全局范围内,用来实例化其他对象。常用的如全局变量值NaN、undefined、全局函数如parseInt()、parseFloat()用于实例化对象构造函数如Date、Object等,以及提供数学计算的单个内置对象如数学对象。对AJAX的理解,实现一个AJAX请求AJAX是AsynchronousJavaScriptandXML的缩写,指的是通过JavaScript进行异步通信,从服务器获取XML文档提取数据,然后更新当前网页相应部分而不用刷新整个网页。创建AJAX请求的步骤:创建一个XMLHttpRequest对象。对此对象使用open方法来创建HTTP请求。open方法需要的参数是请求的方法,请求的地址,是否异步,用户的认证信息。在发出请求之前,您可以向该对象添加一些信息和侦听器功能。例如,您可以通过setRequestHeader方法为请求添加头信息。您还可以向该对象添加一个状态侦听器函数。一个XMLHttpRequest对象共有5种状态。当它的状态改变时,onreadystatechange事件将被触发。您可以设置监听函数来处理请求成功的结果。当对象的readyState变为4时,表示已经接收到服务器返回的数据。这时候就可以判断请求的状态了。如果status为2xx或304,则表示返回正常。这时可以通过响应中的数据来更新页面。设置好对象的属性和监听函数后,最后调用sent方法向服务端发起请求,传入参数即可作为发送的数据体。constSERVER_URL="/server";letxhr=newXMLHttpRequest();//创建Http请求xhr.open("GET",url,true);//设置状态监听函数xhr.onreadystatechange=function(){if(this.readyState!==4)返回;//当请求成功时if(this.status===200){handle(this.response);}else{console.error(this.statusText);}};//设置请求失败时的监听函数xhr.onerror=function(){console.error(this.statusText);};//设置请求头信息xhr.responseType="json";xhr.setRequestHeader("Accept","application/json");//发送Http请求xhr.send(null);使用Promise封装AJAX://Promise封装实现:functiongetJSON(url){//创建一个promise对象letpromise=newPromise(function(resolve,reject){letxhr=newXMLHttpRequest();//创建一个新的httprequestxhr.open("GET",url,true);//设置状态监听函数xhr.onreadystatechange=function(){if(this.readyState!==4)return;//当请求成功或失败时,改变承诺的状态if(this.status===200){resolve(this.response);}else{reject(newError(this.statusText));}};//设置错误监控函数xhr.onerror=function(){reject(newError(this.statusText));};//设置响应数据类型xhr.responseType="json";//设置请求头信息xhr.setRequestHeader("Accept","application/json");//发送http请求xhr.send(null);});returnpromise;}说说说你用过的css布局grid布局,layout布局,flex布局,双飞翼,圣杯布局等等。再来说说HTTP3.0HTTP/3是基于UDP协议实现类似TCP的数据流复用,传输可靠性等功能,这套功能我们称之为QUIC协议流控,传输可靠性功能:QUIC在上面加了一层UDP在保证数据传输可靠性的基础上,提供了数据包重传、拥塞控制等TCP特性。集成TLS加密功能:目前QUIC使用TLS1.3,减少了握手时花费的RTT次数。多路复用:同一个物理连接上可以有多个独立的逻辑数据流,实现了数据流的分离传输,解决了TCP的队头阻塞问题。快速握手:因为是基于UDP的,所以可以使用0~1个RTT建立连接。