JS面试题
in Projects with 0 comment

JS面试题

in Projects with 0 comment

1.如何中断ajax请求?

⼀种是设置超时时间让ajax⾃动断开,另⼀种是⼿动停⽌ajax请求,其核⼼是调⽤XML对象的abort⽅法。

2…说⼀说页面渲染html的过程

1.解析HTML文件,创建DOM树
	浏览器解析html源码,然后创建一个DOM树。css/image/js在DOM树中,每一个HTML标签都有一个对应的节点,并且每一个文本也都会看一个对应的文本节点。DOM树的根节点就是documentElement对应的是html标签。
2.解析CSS,形成CSS对象模型
	浏览器解析CSS代码,计算出最终的样式数据。构建CSSOM树。对CSS代码中非法的语法它会直接忽略掉。解析CSS的时候会按照如下顺序来定义优先级:浏览器默认设置<用户设置<外链样式<内联样式<html中的style
3.将CSS与DOM合并,构建渲染树(renderingtree)
	渲染树和DOM树有点像,但是是有区别的。DOM树完全和html标签一一对应,但是渲染树会忽略掉不需要渲染的元素,比如headdisplay:none的元素等。而目一大段文本中的每一个行在渲染树中都是独立的一个节点。渲染树中的每一个节点都存储有对应的css属性
4.布局和绘制
	一旦渲染树创建好了,浏览器就可以根据渲染树直接把页面绘制到屏幕上
	
	以上四个步骤并不是一次性顺序完成的。如果DOM或者CSSOM被修改,以上过程会被重复执行。实际上,CSS和JavaScript往往会多次修改DOM或者CSSOM

3.es6的常用功能

1. let / const
2.多行字符串 / 模板变量 / 模板字符串  注意:${ } 要和 `` 一起使用,不然不会被解析
3.解构赋值
4.函数默认参数
5.箭头函数
6.ES6模块化
7.promise
8.数组新增方法(forEach、map、filter、every、some、indexOf)

4.关于cookie、sessionStorage、localStorage三者的区别

存储大小:
cookie数据大小不能超过4k,sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
有效时间:
localStorage存储持久数据,浏览器关闭后数据不丢失除非主动删除数据; sessionStorage数据在当前浏览器窗口关闭后自动删除;cookie设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
交互方式:
数据与服务器之间的交互方式,cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端; sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存

5.数组去重的几种方法

1.利用Set()+Array.from()
对NaN和undefined类型去重也是有效的,是因为NaN和undefined都可以被存储在Set中, NaN之间被视为相同的值
2.利用两层循环+数组的splice方法
通过两层循环对数组元素进行逐一比较,然后通过splice方法来删除重复的元素。此方法对NaN是无法进行去重的,因为进行比较时NaN !== NaN。
3.利用数组的indexOf方法
新建一个空数组,遍历需要去重的数组,存放前用indexof方法判断数组中是否已经含有当前元素,没有则用push方法存入。此方法也无法对NaN去重。
4.利用数组的includes方法
类似indexOf方法,但能够检测到数组中包含NaN
5.利用数组的filter()+indexOf()
6.利用Map()构造函数创建的实例对象,使用has和set的方法
7.利用对象,利用了对象的属性名不可重复这一特性。

6.前端解决跨域的几种方法

同源策略:
如果url中:协议、子域名、主域名、端口号任意一个不同,就算是不同的域,浏览器基于安全的考虑,不同的域之间是不能相互访问资源,这就是同源策略。
跨域:
越过浏览器的同源策略,拿到不同域的资源,这个过程就叫跨域。

1.JSONP:
其核心思想:网页通过添加一个<script>元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。
客户端利用 script 标签的 src 属性,去请求一个接口,因为 src 属性不受跨域影响,
服务端响应一个字符串,
客户端接收到字符串,然后把它当做 JS 代码运行。
2.vue-cli代理转发
在vue.config.js配置文件配置deServer配置项,填入需要跨域的IP地址。
3.利用http-server反向代理

7.get、post的区别

1.Get请求的参数会显示在地址栏,而Post不会,所以Post比Get更加安全。
2.Post请求的参数存放到了请求实体中,而Get没有请求实体,Get是存储在请求行中。
3.数据传输Post有优势:Get方式请求的数据不能超过2k,而Post 没有上限。
4.浏览缓存Get有优势:Get具有数据缓存,而Post没有。
5.GET请求在浏览器刷新或者回退的时候不会有影响,POST的话数据会被重新提交。
从优势角度看,数据传输使用Post,数据浏览查询使用Get。即查询时使用Get,其他时候使用Post,表单全部使用Post提交。

8.说⼀说闭包

正常情况下函数外部是访问不到函数内部的局部变量。闭包就是函数里面嵌套的函数,通过这个子函数在外部就可以拿到父函数的局部变量,但是这样做会把父函数的局部变量放到了全局作用域下,内存不被回收,滥用闭包可能会造成内存溢出。

9.Var、 let 、const 区别

变量提升:
var声明的变量存在变量提升,即变量可以在声明之前调⽤,值为undefined。let和const不存在变量提升,即它们所声明的变量⼀定要在声明后使⽤,否则报错。
暂时性死区:
var不存在暂时性死区,let和const存在暂时性死区,只有等到声明变量的那⼀⾏代码出现,才可以获取和使⽤该变量。
块级作⽤域
var不存在块级作⽤域,let和const存在块级作⽤域
重复声明
var允许重复声明变量,let和const在同⼀作⽤域不允许重复声明变量
修改声明的变量
var和let可以,const声明⼀个只读的常量。⼀旦声明,常量的值就不能改变。对对象和数组中对象的元素的修改,不算对常量的修改。
能⽤const的情况尽量使⽤const,其他情况下⼤多数使⽤let,避免使⽤var。

10.bind、call、apply 区别

一、call,apply,bind的相同点:
都是改变this指向的
第一个参数都是this要指向的对象
都可以利用后续参数传参
二、call,apply,bind的区别:
call和bind的参数是依次传参,一一对应的
但apply只有两个参数,第二个参数为数组
call和apply都是对函数进行直接调用,而bind方法返回的仍是一个函数,需要手动去调用

11.深拷贝与浅拷贝

浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制这个对象本身,新旧对象共享一块内存。
深拷贝(deep copy):复制并创建一个一模一样的对象,不共享内存,修改新对象旧对象不会变。
如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝;如果B没变,那就是深拷贝。
浅拷贝的实现:Object.assign(目标对象,源对象),可以把源对象自身的任意多个的可枚举属性拷贝给目标对象,然后返回目标对象,但是Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
深拷贝的实现:用JSON.stringify()将对象转成字符串,再用JSON.parse()把字符串解析成对象。这种方法可以实现数组和对象类型的深拷贝,但不能处理函数。

12.节流与防抖

解决我们频繁性的触发事件的操作,限制函数的执行次数,提高页面性能。
防抖原理(debounce):通过setTimeout的方式,在一定的时间间隔内,将多次触发变成一次触发。
                  应用场景:          
                            1.搜索框输入查询
                            2.鼠标的拖拽
                            3.浏览器窗口缩放,resize事件
                            4.scroll事件滚动触发
                            5.表单验证
                            6.按钮提交事件
                        * 这些实时监听的,会影响数据的改变,数据的改变就会影响页面视图的改变
节流原理(throttle):如果持续触发事件,每隔一段时间,只执行一次事件。减少一段时间的触发频率。
使用第三方库Underscore的_.debounce方法实现防抖
使用第三方库Underscore的_.throttle方法实现节流

13.原型,原型链 ? 有什么特点?

1.每一个构造函数下都有一个原型对象(Array.prototype)
2.原型对象下面有一个属性constructor,该属性指向构造函数
3.每一个实例对象和他的原型对象有一条链接(arr.__proto__),就叫做原型链
4.原型对象外还有原型对象,一直指向Object原型对象为止

14.说⼀下http和https

http:超⽂本传输协议,是⼀个客户端和服务器端请求和应答的标准(TCP)
https:是以安全为⽬标的HTTP通道,即HPPT下加⼊SSL层,⽐http更安全
区别:
http传输的数据都是未加密的(明⽂),https协议是由https和SSL协议构建的可进⾏加密传输和身份认证的⽹络协议,需要ca证书,费⽤较⾼。

15.说⼀说promise

1.Promise:是一个构造函数,所谓的Promise对象,就是通个new Promise()实例化得到的对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的API,可供进一步处理。
2.Promise有三种状态:
Pending(未完成)可以理解为Promise对象实例创建时候的初始状态
Resolved(成功)可以理解为成功的状态
Rejected(失败)可以理解为失败的状态
3.Promise的状态是否可变?
不可变
4.Promise如何解决地狱回调
then里面可以return Promise,来防止地狱回调
5.Promise有哪些方法?他们的应用场景是什么?
all:多个Promise实例,包装成一个新的Promise实例,所有参数中的promise状态都为resolved时,新的promise的状态才为resolved,只要其中有一个参数被rejected,新的promise的状态就变成rejected
race:有一个参数成功么就直接返回成功
6.如何让Promise顺序执行?(async/await)
async function execute() {
	await new Promise((resolve,reject) => {
		resolve(666);
	} ).then( res => {
		...
	})
}

解决异步代码的顺序问题,避免回调地狱;多个请求都到达才能做功能

在处理一些需要花费比较长时间的任务时,使用Promise就可以进行异步的处理,防止阻塞。

16.浏览器缓存机制

简单来说,浏览器缓存其实就是浏览器保存通过HTTP获取的所有资源,是浏览器将网络资源存储在本地的⼀种⾏为。
缓存的资源在内存和磁盘中
⼆者区别:内存退出进程时数据会被清除,⼀般存脚本、字体、图⽚;磁盘退出进程时数据不会被清除,⼀般存⾮脚本会存,
如css等。

三级缓存原理(访问缓存优先级)
1. 先在内存中查找,如果有,直接加载。
2. 如果内存中不存在,则在硬盘中查找,如果有直接加载。
3. 如果硬盘中也没有,那么就进⾏⽹络请求。
4. 请求获取的资源缓存到硬盘和内存。

浏览器缓存的分类
1. 强缓存
2. 协商缓存
浏览器再向服务器请求资源时,⾸先判断是否命中强缓存,再判断是否命中协商缓存。

强缓存
浏览器在加载资源时,会先根据本地缓存资源的 header 中的信息判断是否命中强缓存,如果命中则直接使⽤缓存中的资源不会再向服务器发送请求。
这⾥的header中的信息指的是 expires 和 cahe-control.
http1.0 --> expires
http1.1 --> Cache-Control
expires、Cache-Control同时使⽤,Cache-Control优先级最⾼

协商缓存
当强缓存没有命中的时候,浏览器会发送⼀个请求到服务器,服务器根据header中的部分信息来判断是否命中缓存。如果命中,则返回304,告诉浏览器资源未更新,可使⽤本地的缓存。
这⾥的header中的信息指的是Last-Modify/If-Modify-Since和ETag/IfNone-Match.
Last-Modify/If-Modify-Since --> 修改时间
ETag/If-None-Match --> 校验码
Last-Modified 与 ETag 是可以⼀起使⽤的,服务器会优先验证 ETag,⼀
致的情况下,才会继续⽐对 Last-Modified,最后才决定是否返回 304

17.延迟加载JS有哪些⽅式

defer属性、async属性、动态创建DOM方式(用的最多)、按需异步载入JS

defer属性:(页面load后执行)
script标签定义了defer属性
用途:表明脚本在执行时不会影响页面的构造。也就是所,脚本会被延迟到整个页面解析完毕之后再执行。
<script  src=”XXX.js” defer=“defer”></script>

async属性:(页面load前执行)
script标签定义了async属性。与defer属性类似,都用于改变处理脚本的行为。同样,只适用于外部脚本文件
目的:不让页面等待脚本下载和执行,从而异步加载页面其他内容。异步脚本一定会在页面load事件前执行。不能保证脚本会按顺序执行
<script src=”XXX.js” async></script>

动态创建DOM方式

18.null和undefined的区别

null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在对象
undefined主要指定义了变量,但是并未赋值
ECMAScript认为undefined是从null派生出来的,他们的值是一样的,但是类型却不一样。
所以
null == undefined     //true
null === undefined   //false

19.new关键词到底做了什么

(1) 创建⼀个新对象;
(2) 将构造函数的作⽤域赋给新对象(因此 this 就指向了这个新对象) ;
(3) 执⾏构造函数中的代码(为这个新对象添加属性) ;
(4) 返回新对象

20.影响页面性能的因素

1.DOM树操作频繁
2.事件绑定次数过多
3.script节点过多、过大
4.link节点过多、过大
5.页面小图过多:可使用精灵图、雪碧图
6.⼀个⻚⾯内过多的http请求

21.this的指向问题

A.在全局环境中,this指向window 
B.在作为对象的⽅法调⽤时,this指向这个对象
C.构造函数调⽤时,this指向new后的对象
D.在通过call、apply、bind⽅法调⽤时,this可以指向我们想让它指向的事物
E.在箭头函数中,this指向外⾯的函数

22.三次握手和四次挥手

1、客户端向服务端发送TCP报⽂,表示“请求建⽴新连接
2、服务端收到客户端TCP报⽂后,若同意建⽴连接,则发送确认TCP报⽂,确认客户端的报⽂Seq序号有效,服务器能正常接
收客户端发送的数据,并同意创建新连接
3、客户端收到TCP确认报⽂后,再向服务端发送⼀次确认报⽂,表示“确认收到服务器端同意连接的信号”,此时双⽅TCP连
接正式建⽴。

所谓的四次挥⼿即TCP连接的释放(解除)。连接的释放必须是⼀⽅主动释放,另⼀⽅被动释放
1、客户端向服务端发送TCP报⽂,表示"请求释放连接"
2、服务端收到客户端TCP报⽂后,确认了客户端想要释放连接,返回⼀段TCP报⽂,表示“接收到客户端发送的释放连接的
请求” 
3、服务器端做好了释放服务器端到客户端⽅向上的连接准备,再次向客户端发出⼀段TCP报⽂,表示“已经准备好释放连接了” 
4、客户端收到从服务器端发出的TCP报⽂,确认了服务器端已做好释放连接的准备,并向服务器端发送⼀段报⽂,表示“接收
到服务器准备好释放连接的信号”,此时双⽅TCP连接正式解除

23.JS事件循环机制

JS引擎执行代码是从上往下执行的,执行的代码放入调用栈中,然后执行代码,执行完毕后便将其从调用栈中移出去,遇到定时器(延迟1秒),进入调用栈会调用Web API,1秒后进入回调队列,此时JS引擎会将这个定时器移出调用栈,继续执行后面的代码。此时进入事件循环(EventLoop),他会不断循环的访问回调队列,等待1秒后Web API会将要执行的定时器放入回调队列;事件循环(EventLoop)将回调队列中的内容放入调用栈,开始执行,然后得到定时器的结果。

事件循环的执行过程:
1.同步代码:调用栈执行后直接出栈
2.异步代码:放到Web API中,等待合适的时机放入回调队列,等到栈空时事件循环(EventLoop)开始工作,进行轮询。
3.微任务比宏任务执行时机要早
4.微任务在DOM渲染前触发,宏任务在DOM渲染后触发

JS是单线程的,浏览器在执行JS代码时先执行同步代码,再执行异步代码。
同步代码:调用栈执行后直接出栈
异步代码:放到Web API中,等待合适的时机放入回调队列,等到栈空时 事件循环(EventLoop)开始工作,进行轮询。
主要过程是:
先清空调用栈(call stack)中的同步代码,执行微任务队列中的微任务,尝试DOM渲染,触发事件循环(Event Loop)反复询问回调队列中是否有要执行的语句,有则放入调用栈继续执行。

24.判断对象是否为空

1.采用for…in…进行遍历,通过return的值返回是否位空对象
2.通过JSON.stringify()方法返回的值来判断是否全等于空对象
3.ES6中新增的Object.keys()方法取出所有的key得到的数组,判断长度是否为0,如果是则为空对象

25.promise对象

1.Promise:是一个构造函数,所谓的Promise对象,就是通个new Promise()实例化得到的对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的API,可供进一步处理。
2.Promise有三种状态:
Pending(未完成)可以理解为Promise对象实例创建时候的初始状态
Resolved(成功)可以理解为成功的状态
Rejected(失败)可以理解为失败的状态
3.Promise的状态是否可变?
不可变
4.Promise如何解决地狱回调
then里面可以return Promise,来防止地狱回调
5.Promise有哪些方法?他们的应用场景是什么?
all:多个Promise实例,包装成一个新的Promise实例,所有参数中的promise状态都为resolved时,新的promise的状态才为resolved,只要其中有一个参数被rejected,新的promise的状态就变成rejected
race:有一个参数成功么就直接返回成功
6.如何让Promise顺序执行?(async/await)
async function execute() {
	await new Promise((resolve,reject) => {
		resolve(666);
	} ).then( res => {
		...
	})
}

解决异步代码的顺序问题,避免回调地狱;多个请求都到达才能做功能

在处理一些需要花费比较长时间的任务时,使用Promise就可以进行异步的处理,防止阻塞。

26.浏览器的缓存有哪些

Cookie
Sessionstorage
localstorage

区别看上述第4条

27.说一说ES6语法

1. let / const
2.多行字符串 / 模板变量 / 模板字符串  注意:${ } 要和 `` 一起使用,不然不会被解析
3.解构赋值
4.函数默认参数
5.箭头函数
6.ES6模块化
7.promise

28.判断数据类型的方法

1.typeof,但不能判断null和引用类型,返回的结果都是Object
2.instanceof只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪种类型
3.根据constructor判断,因为constructor是原型对象的属性,指向构造函数
4.Object.prototype.toString()方法,需要借助apply/call/bind方法,因为数组原型对象上的toString方法重写了判断不了

29.说一说const

变量提升:
var声明的变量存在变量提升,即变量可以在声明之前调⽤,值为undefined。let和const不存在变量提升,即它们所声明的变量⼀定要在声明后使⽤,否则报错。
暂时性死区:
var不存在暂时性死区,let和const存在暂时性死区,只有等到声明变量的那⼀⾏代码出现,才可以获取和使⽤该变量。
块级作⽤域
var不存在块级作⽤域,let和const存在块级作⽤域
重复声明
var允许重复声明变量,let和const在同⼀作⽤域不允许重复声明变量
修改声明的变量
var和let可以,const声明⼀个只读的常量。⼀旦声明,常量的值就不能改变。对对象和数组中对象的元素的修改,不算对常量的修改。
能⽤const的情况尽量使⽤const,其他情况下⼤多数使⽤let,避免使⽤var。

30.说一说浅拷贝和深拷贝

浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制这个对象本身,新旧对象共享一块内存。
深拷贝(deep copy):复制并创建一个一模一样的对象,不共享内存,修改新对象旧对象不会变。
如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝;如果B没变,那就是深拷贝。
浅拷贝的实现:Object.assign(目标对象,源对象),可以把源对象自身的任意多个的可枚举属性拷贝给目标对象,然后返回目标对象,但是Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
深拷贝的实现:用JSON.stringify()将对象转成字符串,再用JSON.parse()把字符串解析成对象。这种方法可以实现数组和对象类型的深拷贝,但不能处理函数。