前端面试题准备完毕

目录

1、箭头函数和普通函数的区别

2、一道JS综合面试题

3、CSS盒子模型和IE盒子模型

4、DOM事件

5、HTTP协议

6、原型和原型链

7、JS面向对象

8、通信类


1、箭头函数和普通函数的区别

  1. 箭头函数没有prototype(原型),所以箭头函数本身没有this
  2. 箭头函数的this在定义的时候继承自外层第一个普通函数的this
  3. 如果箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window(全局对象)
  4. 箭头函数本身的this指向不能改变,但可以修改它要继承的对象的this
  5. 箭头函数的this指向全局,使用arguments会报未声明的错误
  6. 箭头函数的this指向普通函数时,它的argumens继承于该普通函数
  7. 使用new调用箭头函数会报错,因为箭头函数没有constructor
  8. 箭头函数不支持new.target
  9. 箭头函数不支持重命名函数参数,普通函数的函数参数支持重命名
  10. 箭头函数相对于普通函数语法更简洁优雅

2、一道JS综合面试题

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>JS综合面试题</title>
	</head>
	<body>
		<script type="text/javascript">
			// function Foo() {
			// 	getName = function(){alert(1);};
			// 	return this;
			// }
			// Foo.getName = function(){ alert(2);};
			// Foo.prototype.getName = function(){ alert(3);};
			// var getName = function(){ alert(4);};
			// function getName(){ alert(5);};
			
			function Foo() {
				getName = function(){alert(1);};
				return this;
			}
			var getName;
			function getName(){ alert(5);};
			// 上面是变量提升和函数提升,变量只是声明提升,函数是整个函数提升,当变量和函数的声明有冲突时,默认保留函数的定义
			Foo.getName = function(){ alert(2);};
			Foo.prototype.getName = function(){ alert(3);};
			getName = function(){ alert(4);};
			
			
			//请写出下列的输出结果
			Foo.getName(); //2
			getName();		 //4
			Foo().getName();	// (Foo()).getName() --> window.getName() 1
			getName();				//1
			new Foo.getName();					//new (Foo.getName)() --> new (function(){ alert(2);})() 2
			new Foo().getName();				//(new Foo()).getName() --> foo.getName() 3
			new new Foo().getName();		//new ((new Foo()).getName)() --> new (foo.getName)() --> new (function(){ alert(3);})() 3
		</script>
	</body>
</html>

3、CSS盒子模型和IE盒子模型

标准模型和IE模型的区别就在于宽和高的计算方式不同,标准模型的宽度和高度指的就是content的宽度和高度,它不包含padding和border,IE模型的宽度和高度是计算padding和border的,如果宽度都设置为200px,对于IE模型来说,200px包含了padding和border,如果IE模型的padding是10px,无border,那么对应的content只能是180px,高度亦是如此。

CSS如何设置这两种模型

通过CSS3中的属性box-sizing:content-box;设置成标准模型;box-sizing:border-box;设置成IE模型。浏览器默认的方式是content-box

JS如何设置获取盒子模型对应的宽和高

  • DOM.style.width/height(并不能拿到所有的宽和高,只能拿到内联样式的宽和高,也就是在div标签内写style的写法,其他两种CSS写法是取不到宽高的)
  • DOM.currentStyle.width/height(拿到浏览器渲染后的宽高,但这个属性只有IE支持)
  • window.getComputedStyle(DOM).width/height(与上一种原理相似,但这种支持大多数浏览器,兼容性更好)
  • DOM.getBoundingClientRect().width/height(经常适用的场景是计算一个元素的绝对位置,这个绝对位置是根据视窗的绝对位置,getBoundingClientRect()这个方法拿到的有4个元素:left,top,width,height)

BFC(边距重叠解决方案)

BFC的基本概念

块级格式化上下文

BFC的原理

  1. 在BFC这个元素的垂直方向的边距会发生重叠
  2. BFC的区域不会与浮动元素的box重叠(用来清除浮动和布局的)
  3. BFC在页面上是一个独立的容器,它外边的元素不会影响它里边的元素,里边的元素也不会影响它外边的元素
  4. 计算BFC高度的时候,浮动元素也不参与计算

如何创建BFC

  • 浮动元素:float除none以外的值
  • 定位元素:position除static、relative以外的值(absolute、fixed)
  • display为inline-block、table-cells(跟table相关的那几个)、flex
  • overflow除visible以外的值(hidden、auto、scroll)

4、DOM事件

DOM事件的级别

准确来说是DOM标准定义的级别。element.addEventListener('click', function(){}, false)其中function是响应函数,后面可以是false也可以是true,它其实是指定了冒泡还是捕获。DOM3其实还是DOM2这种事件的定义方式,只不过事件类型增加了很多,比如说鼠标事件、键盘事件等等。

DOM事件模型

事件模型是捕获和冒泡,捕获是从上往下,冒泡是从当前元素也就是目标元素往上,这是两个过程

DOM事件流

浏览器在当前页面与用户做交互的过程中,比如说点击鼠标左键,这个左键是怎么传到这个页面上,这就是事件流,它又是怎么响应的,一个完整的事件流分3个阶段,第一阶段是捕获,第二阶段也就是目标阶段,比如说我点了当前这个按钮,这个按钮就是目标阶段,也就是说事件通过捕获到达目标元素,这个时候就是目标阶段,第三阶段就是从目标元素再上传到window对象,也就是冒泡的过程

描述DOM事件捕获/冒泡的具体流程

第一个接收到事件的对象是window,window接收完以后给document,JS中关于BOM对象,两个最常用的,一个是window,一个是document,这两个是非常重要的对象,在事件捕获中也是它两优先得到的,第三个才是html标签(怎么用JS表示当前这个HTML节点?document.documentElement专门表示节点的对象),html完了以后就是body了,body完了以后就是按照普通的html结构一层一层往下传,比如说父级元素,然后是子级元素,最后到达目标元素。冒泡的具体流程,就是从目标元素,一层一层往上传,最后到window,完成了一次冒泡的流程。

Event对象的常见应用

自定义事件

5、HTTP协议

HTTP协议的主要特点

  • 无连接(我连接一次,它就会断掉,不会保持连接)
  • 无状态(客户端和服务端是两种身份,客户端向服务端请求一个图片,HTTP协议帮你建立连接,帮你中间传输,任务完成以后,连接就要断开了,下次客户端再过来,服务端是没法区分上次和这次连接是不是同一个人同一个身份,服务端单从HTTP协议上是不能区分两次连接者的身份的,这就是无状态)
  • 简单快速(每个资源url,一个图片或者页面地址要统一资源符,这是固定的,所以在HTTP协议处理起来也是非常简单的,想访问某个资源只需输入这个url就可以了)
  • 灵活(在每一个HTTP协议中有一个头部分会有一个数据类型,通过一个HTTP协议就可以完成不同数据类型的传输,所以是比较灵活的)

HTTP报文的组成部分

请求行包含什么

HTTP方法、页面地址、HTTP协议以及版本

请求头是什么

就是一些key、value值,来告诉服务端我要哪些内容,要注意什么类型

空行是什么

空行是告诉服务端往下就是按照请求体的内容解析了,而不是请求头了

请求头下面是空行和请求体,没显示出来

响应头下面是空行和响应体(文档部分),没显示出来

HTTP方法

GET:请求指定的页面信息,并返回实体主体。POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和已有资源的修改。HEAD:类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头。PUT:从客户端向服务器传送的数据取代指定的文档的内容。DELETE:请求服务器删除指定的页面。 

GET和POST的区别

红色的必须记住

HTTP状态码

状态码分类:

  • 1XX- 信息型,服务器收到请求,需要请求者继续操作。
  • 2XX- 成功型,请求成功收到,理解并处理。
  • 3XX - 重定向,需要进一步的操作以完成请求。
  • 4XX - 客户端错误,请求包含语法错误或无法完成请求。
  • 5XX - 服务器错误,服务器在处理请求的过程中发生了错误。

常见状态码:

  • 200 OK - 客户端请求成功301 - 资源(网页等)被永久转移到其它URL
  • 302 - 临时跳转
  • 400 Bad Request - 客户端请求有语法错误,不能被服务器所理解
  • 401 Unauthorized - 请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
  • 404 - 请求资源不存在,可能是输入了错误的URL
  • 500 - 服务器内部发生了不可预期的错误
  • 503 Server Unavailable - 服务器当前不能处理客户端的请求,一段时间后可能恢复正常

持久连接

只有HTTP1.1版本才支持,HTTP1.0版本不支持,面试要答出这个和持久连接与非持久连接之间的区别

HTTP协议的缺点

  • 请求信息明文传输,容易被窃听截取。数据的完整性未校验,容易被篡改没有验证对方身份,存在冒充危险
  • HTTP协议不适合传输一些敏感信息,比如:各种账号、密码等信息,使用http协议传输隐私信息非常不安全

什么是HTTPS

HTTPS 协议(HyperText Transfer Protocol over Secure Socket Layer):一般理解为HTTP+SSL/TLS,通过 SSL证书来验证服务器的身份,并为浏览器和服务器之间的通信进行加密

HTTPS协议的优点

相比于http,https可以提供更加优质保密的信息,保证了用户数据的安全性,此外https同时也一定程度上保护了服务端,使用恶意攻击和伪装数据的成本大大提高。

HTTPS协议的缺点

HTTPS协议多次握手,导致页面的加载时间延长近50%;HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗;申请SSL证书需要钱,功能越强大的证书费用越高。SSL涉及到的安全算法会消耗 CPU 资源,对服务器资源消耗较大。

HTTP和HTTPS的区别

  • https协议需要到CA申请证书,一般免费证书较少,因而需要一定费用。
  • http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl/tls加密传输协议。
  • http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  • http的连接很简单,是无状态的;HTTPS协议是由SSL/TLS+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

CA证书是什么

CA证书就是CA机构颁发的数字证书,CA机构就是电子商务认证授权机构,是负责发放和管理数字证书的权威机构,是具有权威性和公正性的第三方信任机构,承担公钥体系中公钥的合法性检验的责任。

HTTP1.0 和 HTTP1.1 区别

  1. 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
  2. 带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
  3. 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
  4. Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
  5. 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点

HTTP/1.x现在存在的一些问题

  • HTTP1.x 在传输数据时,每次都需要重新建立连接,增加了大量的延迟时间
  • HTTP1.x 在传输数据时,所有的传输内容都是明文的,客户端和服务端无法验证对方的身份,在一定程度上无法保证数据的安全性
  • HTTP1.x 在使用时,header里携带的内容过大,增加了传输的成本,在移动端增加用户流量
  • HTTP1.x 虽然支持了keep-alive, 来减少多次创建连接产生的延迟,但是keep-alive 使用多了也会给服务端带来大量的性能压力,并且对于单个文件被不断请求的服务,因为文件被请求之后还保持了不必要的连接时间,keep-alive可能会极大的影响服务器的性能
  • 虽然HTTP1.x可以让客户端向服务器并行发送多个请求,而且服务器也可以并行处理多个请求,但是HTTP/1.x 有严格的串行返回响应机制,通过 TCP 连接返回响应时,必须一个接一个,前一个响应没有完成,下一个响应就不能返回,如果第一个响应时间很长,那么后面的响应处理完了也无法发送,只能被缓存起来,占用服务器内存

HTTP2.0新特性

  1. 二进制分帧层
  2. 多向请求与响应
  3. 优先级和依赖性
  4. 首部压缩
  5. 服务器推送

HTTP2.0优点

  1. 多路复用(MultiPlexing):一次TCP握手,多个同域并行请求,请求和响应同时发送接受,然后再拼装组合,不阻塞;一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面

  2. header压缩 : HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小;

  3. 优先级和依赖性(Priority):可以请求的时候告知服务器端,资源分配权重,优先加载重要资源;

  4. 服务器推送(Server Push):根据客户端需求,服务端主动推送资源,减少请求耗时;

Http2和Http1.X的区别

(1)HTTP2使用的是二进制传送,HTTP1.X是文本(字符串)传送。

二进制传送的单位是帧和流。帧组成了流,同时流还有流ID标示

(2)HTTP2支持多路复用

因为有流ID,所以通过同一个http请求实现多个http请求传输变成了可能,可以通过流ID来标示究竟是哪个流从而定位到是哪个http请求

(3)HTTP2头部压缩

HTTP2通过gzip和compress压缩头部然后再发送,同时客户端和服务器端同时维护一张头信息表,所有字段都记录在这张表中,这样后面每次传输只需要传输表里面的索引Id就行,通过索引ID查询表头的值

(4)HTTP2支持服务器推送

HTTP2支持在未经客户端许可的情况下,主动向客户端推送内容

HTTPS什么时候用的对称加密,什么时候用的非对称加密

HTTPS在内容传输的加密上使用的是对称加密,非对称加密只作用在证书验证阶段。

6、原型和原型链

创建对象有几种方法

原型、构造函数、实例、原型链

构造函数M,它的实例o3,这些都为true,M.prototype.constructor === M;o3.__proto__ === M.prototype;

从一个实例对象往上找构造这个实例的相关联的对象,这个关联的对象再往上找,它又有创造它的上一级的原型对象,以此类推,一直到Object.prototype终止,这个链条就断了,也就是说Object.prototype属性是整个原型链的顶端,原型链是通过prototype这个原型和__proto__属性完成原型链的查找;

当我有多个实例的时候,有相同方法的时候,可以存到共同的东西上,共同的东西就是原型对象,通过原型链的方式找到原型对象,原型对象的方法是被不同的实例所共有的,这是原型链的一个工作原理;

按照JS引擎的分析方式,在访问一个实例的时候,实例有什么方法,在这个实例上没有找到这个方法和属性的话,它就会往它的上一级原型对象上找,如果在它的上一级原型对象还没有找到这个属性和方法,它会在它原型对象的基础上再通过原型对象这个__proto__再往上一级查找,以此类推,直到找到Object.prototype,如果到那个地方还没有找到,原路返回,告诉它这个方法或属性没有找到没有定义,如果在中间找到了,它就停止向上查找,直接返回这个方法的用处;

函数才有prototype,对象是没有prototype的;只有实例对象才有__proto__,函数也有__proto__,因为函数既是函数,也是一个对象(函数.__proto__ === Function.prototype返回true),说明了函数M的构造函数是Function(把M看成一个实例),可以理解为普通函数M是Function这个构造函数的一个实例;

实例明明是被构造函数生成的,那它是怎么跟这个原型对象产生关联的,也就是构造函数的prototype这个属性,也就是说你修改这个属性,也就修改了实例上一级的原型对象;

当我们创建一个函数的时候,每个函数会自动生成一个原型(prototype)属性;在函数中就只有这一个原型属性,而这个属性是一个指针,指向一个对象,称为原型对象,原型对象中含有一个constructor属性,通过这个属性又可指回函数。当我们向函数中添加属性时,实际上添加到了原型对象之中,当我们用new操作符创建新实例时,这个新实例是可以共享原型对象中的属性的。当我们向新实例中添加属性时,属性被保存到了新实例中,当向新实例中添加和原型中一模一样的属性时,这个属性会覆写原型中的属性

instanceof原理

7、JS面向对象

类的声明

类的声明有2种方式,一种是传统的用构造函数来模拟一个类的方式,第二种就是ES6中对class的声明。ES6中有增加了对类的声明,就是class这个语法,后面就是类名,constructor就是构造函数,构造函数里面就可以写它这个属性了,跟ES5中的写法是相同的

实例化类的对象

通过new就可以实例化一个类,虽然声明方式不一样,但是实例化的方式是一样的,如果构造函数没有参数的话,new Animal()的括号是可以不要的

如何实现继承

继承的本质就是原型链,第一种方式是借助构造函数实现继承,第二种方式是借助原型链实现继承,第三种方式是前两种的组合方式

第一种方式:

主要原理就在Parent1.call(this);这句话上(没有这句话实现不了继承):将父级构造函数那个类指向子构造函数的实例上去,导致你看到的父级构造函数的所有属性在子级中也有就是这个原理

缺点:Parent1的原型链上的东西并没有被Child1继承,这种方式只是实现了部分继承,如果父类的属性都在构造函数里面,那么完全可以实现继承,但如果父类的原型对象上还有方法的话,那么子类是拿不到这些方法的

第二种方式:

每个函数都有一个prototype属性,这个属性它是一个对象,这个对象是可以任意赋值的,现在赋值给父类一个实例对象,而上面两个红框内容是等同的,所以new Child2().__proto__引用的就是父类Parent2的一个实例对象。按照类似于作用域的寻找方式,先找new Child2()上面的属性,比如找name,通过Child2实例化的属性上没有name,然后这个作用域开始找__proto__属性,这个属性是一个对象,这个属性又被赋值为父类的一个实例,所以这个属性也就找到了父类的一个对象,父类的对象上已经有name了,所以这个对象也就能拿到这个name,也就实现了继承。这种方式完全就是原型链。new Child2().__proto__.name返回"parent2"

缺点:在一个类上我们实例了两个对象,修改第一个对象的属性play,第二个对象也跟着改变了,这个不是想要的效果。这个缺点造成的原因就是因为原型链中的原型对象它俩是共用的,因为s1.__proto__和s2.__proto__它俩引用的是同一个对象,也就是父类的实例对象,而那个父类方法是在它的实例对象上,而且是引用类型,所以,当你改变s1这个原型链对象的时候,改的就是s2原型对象的值,所以它俩会同时跟着变,同理,你改了s2,s1也会跟着变

第三种方式-组合方式:

通过组合方式轻松地实现了继承,把构造函数和原型它们的优点都结合起来,把它们的不足也弥补了,这是实现继承最通用的方式,但也有不足

缺点:我们在写子类的原型链的时候,就是实例化了这个父类,我们在实例化子类的时候,父类的构造函数在上面两处地方都执行了一次,也就是父类的构造函数执行了两次,然而这两次没有必要,因为构造函数函数体内的内容会自动执行,所以没有必要再把父类的实例对象再放到原型对象上去,因为在子类的构造函数里面已经有了这块内容

组合继承优化1:

通过两个构造函数组合能拿到所有的构造函数体里面的属性和方法,既然是想继承父类的原型对象,所以直接赋值给我当前这个原型对象就行了,这样的话就可以把父级的构造函数体内的属性和方法拿到,还能把它原型对象拿到,完成继承。这种方式父类的构造函数只执行了一次,也就是在实例化子类的时候执行一次,原型对象上只是简单的引用,Parent4.prototype是引用类型,所以它不会再执行父级的构造函数。

缺点:s5.constructor指向了父类,这不是想要的效果,prototype里面有一个属性constructor,而子类的原型对象和父类的原型对象是一个对象,这个对象的constructor就是父类的constructor,指向的当然是Parent4自己了,所以这就是问题的根源所在。将子类的原型对象和父类的原型对象划等号,也就是引用同一个对象,说白了,它俩是一个对象,它俩的构造函数指向的是一个,也就是constructor是一个,所以无法区分这个实例是由父类创造的还是由子类创造的

组合继承优化2:

这是组合最完美的方式。通过创建中间对象的方法,就把两个原型对象区分开,中间对象还具备的一个特性就是,它的原型对象是父类的原型对象,这样的话在原型链上又连起来了通过给Child5的原型对象的constructor做修改,就能正常地区分父类和子类的构造函数了。Object.create创建的对象原型对象就是参数Parent5.prototype。Child5的实例的原型对象等于Child5.prototype,它又等于Object.create这个方法创建的对象,这个中间对象原型对象又是父类的原型对象,一级的上一级的上一级,通过这样找就实现了继承,而且还达到了父类和子类原型对象的隔离,这就是原理。这时候Child5实例的constructor还是没有自己的constructor,它要找的话肯定也到原型对象找,到原型对象找的话contructor还是指向Parent5,作用域通过原型链向上找的特性,所以要覆盖,要给Child5的原型对象写一个自己的constructor,Child5.prototype.constructor = Child5

8、通信类

什么是同源策略及限制

比如http是协议,www.imooc.com是域名,默认的端口是80(如果没指定端口的话,默认的端口是80),这三个构成的叫一个源,三个中有一个不一样,就是源不一样,就是跨域了。

限制:不是一个源的文档,你没有权利去操作另一个源的文档,主要限制的上面几个方面,无法获取和操作另一个资源的DOM,任何DOM都拿不到,有限制。Ajax只适合同源的通信,跨域就不行了。

前后端如何通信

  • Ajax
  • WebSocket
  • CORS

Ajax是同源的通信方式,跨域就不行了。WebSocket不受同源策略的限制。CORS既支持跨域通信也支持同源通信

如何创建Ajax

跨域通信的几种方式

JSONP

JSONP的原理是什么,怎么实现的

通过script标签的异步加载来实现的

猜你喜欢

转载自blog.csdn.net/weixin_43804496/article/details/112130628
今日推荐