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

前端面试高级面试题合集

时间:2023-03-28 11:12:22 HTML

说的是输入网址回车后的流程1.读取缓存:搜索自己的DNS缓存。(如果在DNS缓存中找到IP地址,则跳过下一步查找IP地址,直接访问IP地址。)2.DNS解析:将域名解析成IP地址3.TCP连接:TCP三-方式握手,简述三次握手Client:你在服务器端吗?服务器:我在客户端,你要连接我吗?客户端:是的服务器,我要链接。连接建立,可以开始请求4.发送HTTP请求5.服务器处理请求并返回HTTP报文6.浏览器解析并渲染页面7.断开连接:TCP挥手四次关于第6步浏览器再解析渲染页面再说如果返回的html页面是根据HTML解析出DOM树,根据CSS解析生成CSS规则树,将DOM树和CSS规则树结合,生成渲染树,计算根据渲染树获取每个节点的信息,并根据计算出的信息绘制页面。它不会粘在袋子上吗?TCP协议是面向流的协议,UDP是面向消息的协议。UDP报文段就是一条消息,应用程序必须以消息为单位提取数据,不能一次提取一个字节的数据。UDP有报文边界保护,有报文头(报文源地址、端口等信息),接收端很容易区分和处理。传输协议将数据作为一个独立的消息在互联网上传输,接收端只能接收独立的消息。接收端一次只能接收到发送端发送的一个数据包。如果一次接收到的数据大小小于发送端发送的数据大小,就会丢失一部分数据。即使丢了,接收端也不会分两批接收。网络劫持有哪些类型以及如何预防?网络劫持分为两种:(1)DNS劫持:(进入京东强制跳转到淘宝,即dns劫持)DNS强制解析:通过修改运营商本地DNS记录,将用户流量引导至缓存服务器302跳转方式:通过监控网络出口流量,分析判断哪些内容可以被劫持,然后向被劫持的内存发起302跳转回复,引导用户获取内容(2)HTTP劫持:(访问谷歌但有总是打蓝月的广告)。由于http明文传输,运营商会修改你的http响应内容(即添加广告)。DNS劫持因为涉嫌违法,所以受到监管。现在DNS劫持已经很少见了,http劫持还是很普遍的。最有效的方法是全站使用HTTPS对HTTP进行加密,让运营商无法获取明文,劫持你的响应内容。JavaScript为什么要进行变量提升,会导致什么问题?变量提升的表现是无论变量在函数中的什么地方声明,都好像被提升到函数头部,可以在变量声明之前访问而不会报错。变量声明提升的本质原因是js引擎在代码执行之前有一个解析过程,创建一个执行上下文,初始化一些代码执行过程中需要用到的对象。当访问一个变量时,会去当前执行上下文中的作用域链中查找,作用域链的头部指向当前执行上下文的变量对象。这个变量对象是执行上下文的一个属性,它包含函数参数,所有的函数和变量声明,这个对象是在解析代码时创建的。首先你要知道,JS在获取一个变量或者函数的时候,都会有两个步骤,分别是解析和执行。在解析阶段,JS检查语法并预编译函数。解析时,会先创建一个全局的执行上下文,先取出代码中即将执行的变量和函数声明。变量首先被赋值为undefined,函数首先被声明并准备好使用。在一个函数执行之前,也会创建一个函数执行上下文,它和全局执行上下文类似,但是函数执行上下文会多出this、arguments和函数参数。全局上下文:变量定义、函数声明函数上下文:变量定义、函数声明、this、arguments在执行阶段,它们按照代码的顺序执行。那么为什么要进行变量提升呢?主要有两个原因:性能提升和容错性更好(1)性能提升在JS代码执行之前,会进行语法检查和预编译,这个操作只会执行一次。这样做是为了提高性能。如果没有这一步,每次执行代码之前都要重新解析变量(函数),这是没有必要的,因为变量(函数)的代码是不会变的,解析一遍就够了。在解析过程中,还会为函数生成预编译代码。预编译时,会统计声明了哪些变量,创建了哪些函数,并对函数的代码进行压缩,去除注释、不必要的空白等。这样做的好处是每次执行函数时,栈空间可以直接为函数分配(无需再次解析以获得代码中声明了哪些变量以及创建了哪些函数),并且由于代码压缩,代码执行也更快。(2)更好的容错性变量提升可以在一定程度上提高JS的容错性,见如下代码:a=1;变种;控制台日志(一);如果没有变量提升,这两行代码会报错,但是因为有变量提升,这段代码可以正常执行。虽然,在开发过程中,完全可以避免这样写,但是有时候代码会很复杂。可能由于疏忽,先使用后定义,不影响正常使用。由于变量吊装的存在,它会正常工作。总结:解析和预编译期间的声明提升可以提高性能,允许函数在执行期间为变量预分配堆栈空间。声明提升也可以提高JS代码的容错性,让一些不规范的代码也可以正常进行变量提升。虽然有一些好处,但也会带来一定的问题。在ES6中,let和const被提出来定义变量,它们没有变量提升的机制。我们来看看变量提升可能带来的问题:vartmp=newDate();functionfn(){console.log(tmp);如果(假){vartmp='你好世界';}}fn();//undefined在这个函数中,本来打算打印外层的tmp变量,但是因为变量提升的问题,把内层定义的tmp提到到函数的最上面,相当于覆盖了外层的tmp,所以打印出来的结果是undefined。vartmp='helloworld';for(vari=0;i{console.log(this.a)}}obj.say()varanotherObj={a:30}obj.say.apply(anotherObj)输出结果:1010我怎么知道箭头函数没有绑定this,它的this来自其parent所在的上下文,所以会先打印全局10中a的值。虽然后面让say方法指向了另一个对象,但是箭头函数的特性是无法改变的。它的this仍然指向全局,所以10仍然会输出。但是,如果是普通函数,那么就会有完全不同的结果:vara=10varobj={a:20,say(){console.log(this.a)}}obj.say()varanotherObj={a:30}obj.say.apply(anotherObj)outputresult:2030这时候say方法中的this会指向它所在的对象,输出其中的a的值。代码输出varobj={say:function(){varf1=()=>{console.log("1111",this);}f1();},pro:{getPro:()=>{console.log(this);}}}varo=obj.say;o();obj.say();obj.pro.getPro();输出结果:1111windowobject1111objobjectwindow对象解析:o(),o全局执行,而f1是一个箭头函数,它没有绑定this,它的this指向它父级的this,它父级的say方法的this指向全局作用域,所以它会打印出window;obj.say(),谁调用say,say的this指向谁,所以此时this指向obj对象;obj.pro.getPro(),我们知道箭头函数没有绑定this,getPro是在pro中,对象不构成单独作用域,所以箭头函数的this指向全局作用域window.事件是如何实现的?基于发布-订阅模型,事件相关的代码会在浏览器加载时被读取,但只有在特定事件真正被触发时才会执行。例如,点击一个按钮就是一个事件(Event),负责处理该事件的代码段通常称为事件处理器(EventHandler),也就是“开始显示对话框”的动作。在web端,我们常见的DOM事件:DOM0级事件,直接给html元素绑定on-events,比如onclick,如果取消,dom.onclick=null,同一个事件只能有一个handler,如下将覆盖前一个。DOM2级事件,通过addEventListener注册事件,通过removeEventListener删除事件,一个事件可以有多个事件处理器,按顺序执行,捕获事件和冒泡事件DOM3级事件,增加事件类型,比如UI事件,焦点事件,什么创建鼠标事件对象的方法有哪些?一般都是直接以字面量的形式创建对象,但是这种创建方式在创建大量相似的对象时会产生大量的重复代码。但是js不同于一般的面向对象语言,它在ES6之前没有类的概念。但是,函数可用于模拟以产生可重用的对象创建方法。常见的有以下几种:(1)第一种是工厂模式。工厂模式的主要工作原理是用函数来封装对象的创建。细节,从而通过调用函数达到复用的目的。但是它有一个很大的问题,就是创建的对象不能与某个类型相关联。它只是简单地封装了重用代码,而没有建立对象和类型之间的关系。(2)第二种是构造函数模式。js中的每一个函数都可以作为构造函数,只要是通过new调用的函数,都可以称为构造函数。执行构造函数会先创建一个对象,然后将对象的原型指向构造函数的prototype属性,再将执行上下文中的this指向这个对象,最后执行整个函数。如果返回值不是对象,则返回新创建的对象。因为this的值指向了新创建的对象,所以可以用this给对象赋值。构造器模式相对于工厂模式的优势在于创建的对象与构造器相关联,因此可以通过原型来识别对象的类型。但是构造函数的缺点是会创建不必要的函数对象,因为函数在js中也是对象,所以如果object属性中包含函数,每次都会创建一个新的函数对象,浪费不必要的内存空间,因为该功能对所有实例都是通用的。(3)第三种模式是原型模式,因为每个函数都有一个原型属性,原型属性是一个包含属性和方法的对象,所有通过构造函数创建的实例都可以共享这些属性和方法。因此,可以利用原型对象添加公共属性和方法,实现代码重用。与构造函数模式相比,这种方式解决了函数对象的复用问题。但是这种模式也存在一些问题,一是没有办法通过传入参数来初始化值,二是如果存在Array等引用类型的值,那么所有的实例都会共享一个对象,并且实例具有引用类型值更改将影响所有实例。(4)第四种模式是构造函数模式和原型模式的结合,是最常见的创建自定义类型的方式。因为构造函数模式和原型模式分开使用存在一些问题,所以可以将这两种模式结合起来,通过构造函数来初始化对象的属性,通过原型对象实现函数方法的复用。这种方式解决了两种模式单独使用时的不足,但是缺点是因为使用了两种不同的模式,所以代码的封装性不够好。(5)第五种模式是动态原型模式。该模式将原型方法赋值的创建过程移至构造函数内部。通过判断属性是否存在,只有在函数第一次被调用时才能实现原型。分配一个对象一次的效果。这个方法很好的封装了上面的混合模式。(6)第六种模式为寄生构造函数模式。这种模式的实现与工厂模式基本相同。我对这种模式的理解是,它主要是基于一个已有的类型。要扩展的对象。这样,在不修改原有构造函数的情况下,达到了扩展对象的目的。它的缺点之一就是和工厂模式一样,不能实现对象识别。forEach和map方法有什么区别?该方法用于遍历数组。两者的区别在于:forEach()方法会对每个元素执行提供的函数,对数据的操作会改变原来的数组。该方法没有返回值;map()方法不会改变原数组的值,而是返回一个新数组。新数组中的值为原数组调用函数后的值;浏览器资源缓存的位置是什么?资源缓存有3个位置,优先级从高到低:ServiceWorker:ServiceWorker运行在JavaScript主线程之外。虽然因为脱离了浏览器窗口而不能直接访问DOM,但是可以离线完成。缓存、消息推送、网络代理等功能。它可以让我们自由控制缓存哪些文件,如何匹配缓存,如何读取缓存,缓存是持久化的。当ServiceWorker没有命中缓存时,需要调用fetch函数获取数据。也就是说如果serviceworker没有命中缓存,就会按照缓存查找优先级来查找数据。但是不管数据是从MemoryCache中获取的,还是从网络请求中获取的,浏览器都会显示从ServiceWorker中获取的内容。MemoryCache:MemoryCache是??内存缓存,效率最快。但是内存缓存虽然读取效率高,但是缓存持续时间很短,会随着进程的释放而释放。一旦我们关闭Tab页面,内存中的缓存就被释放了。DiskCache:DiskCache也是一种存储在硬盘上的缓存。读取速度较慢,但??一切都可以存储在磁盘上。与MemoryCache相比,在容量和存储时效性上更胜一筹。在所有的浏览器缓存中,DiskCache的覆盖范围基本上是最大的。它会根据HTTPHerder中的字段判断哪些资源需要缓存,哪些资源可以不用请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站的情况下,一旦相同地址的资源被硬盘缓存,就不会再请求数据了。DiskCache:PushCache是??HTTP/2中的内容。当上面三个缓存都没有命中时才会使用。而且缓存的时间也很短,只存在于会话(Session)中,一旦会话结束就会释放。它有以下特点:可以推送所有资源,但Edge和Safari浏览器兼容性不是很好。可以推送无缓存和无存储资源。一旦连接关闭,PushCache就会被释放。可以使用多个页面。相同的HTTP/2连接意味着可以使用相同的缓存。PushCache中的缓存只能使用一次。浏览器可以拒绝接受已有的资源推送,可以将资源推送到其他域名。