JavaScript相关面试题
JS数据类型有哪些,区别是什么?
JS数据类型分为两类: 基本数据类型,分别是Number 、String、Boolean、BigInt、Symbol、Null、Undefined。基本数据类型是直接存储在栈中的简单数据段,占据空间小 引用数据类型,通常用Object代表,普通对象,数组,正则,日期,Math数学函数都属于Object。 引用数据类型是存储在堆内存中,占据空间大。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
JS变量提升?
- var声明会存在变量提示,函数声明也会提升。
- let,const不会存在变量提升问题 变量提升。
- 函数声明提示,都是在js编译阶段提升的,将当前变量、函数提升至当前作用域的最顶端 并且由于编译阶段,并没有开始执行代码,所以都会被赋初始值undefined,由于函数不同,函数会在堆内存中开辟一个空间用于来保存函数的执行体,会将当前内存地址赋值给函数变量,所以可以在函数声明上面,来调用该函数 。
- 并且let,const声明变量会形成暂时性死区,就是在该作用域当中,无法在变量声明之前去访问,即使全局存在一个同名变量,也无法访问。
原型与原型链
- 原型是函数特有的属性。
- 对象和数组都是没有原型这个属性的。
- 函数的原型可以挂载属性和方法上去。
- proto 这就是原型链。浏览器最新的表达: [[prototype]]
- 任何的数据都是有原型链的。对象obj、数组arr、number等。
- 原型: 原型分为隐式原型和显式原型,每个对象都有一个隐式原型,它指向自己的构造函数的显式原型。
- 原型链: 多个proto组成的集合成为原型链。
- 所有实例的proto都指向他们构造函数的prototype。
- 所有的prototype都是对象,自然它的proto指向的是Object()的prototype。
- 所有的构造函数的隐式原型指向的都是Function()的显示原型。
- Object的隐式原型是null。
通用规则
- 对象有proto属性,函数有prototype属性;
- 对象由函数生成;
- 生成对象时,对象的proto属性指向函数的prototype属性。
闭包的理解?
1.内层函数引用外层函数中变量,这些变量的集合就是闭包 2.通过作用域链,当前作用域可以访问上级作用域中的变量 3.解决的问题:保存变量,保护变量 4.带来的问题:内存泄露 5.块级作用域
new会发生什么?
- 创建一个新对象
- 将新对象的proto(原型)指向构造函数的prototype(原型对象)
- 构造函数绑定新对象的this并执行返回结果
- 判断返回结果是否为null,如果为null,返回新对象,否则直接返回执行结果。
事件循环Event loop,宏任务与微任务?
- js是单线程的,会循环往复的先执行同步任务,当同步任务执行完毕后去执行异步队列中的异步任务。
- 事件循环中是执行栈中的内容,栈中内容执行后执行微任务,微任务清空后执行宏任务,所以微任务的优先级高于宏任务。
- js中常见的微任务:promise.then,MutationObserver,process.nextTick;
- js中常见的宏任务:setTimeout、setInterval定时器、DOM事件、AJSX事件
promise是什么与使用方法?
- 概念:异步编程的一种解决方案,解决了地狱回调的问题
- 使用方法:new Promise((resolve,reject) => { resolve(); reject(); })里面有多个resovle或者reject只执行第一个。如果第一个是resolve的话后面可以接.then查看成功消息。如果第一个是reject的话,.catch查看错误消息。
- Promise是宏任务(同步执行),只有Promise的回调是异步微任务。
JavaScript有几种方法判断变量的类型?
- typeof(根据二进制判断),不能判断数据类型:null和object
- intanceof(根据原型链判断),原生数据类型不能判断
- constructor.name(根据构造器判断),不能判断null数据类型
- Object.prototype.toString.call()(用Object的toString方法判断)所有类型数据都能判断,记住判断结果打印为:'[object Xxx]'
JS实现异步的方法?
- 异步微任务:promise, async/await
- 异步宏任务:setTimeout setIntervial dom事件
map 和 forEach 的区别?
- map一般不修改原数组,forEach能改变原数组。
- map有返回值,创建新数组,而forEach无返回值,返回结果undefined。
- map和forEach用只能用try...catch...中断抛出错误中断,而return不能,用其效果相当于for循环的continue会跳过此次循环进入下一次循环。
事件扩展符用过吗(...)?
- 数组克隆 let a = [1,2,3];let b = [...a] 。
- 数组合并 let a = [1,2,3];let b = [4,5,6];let c = [...a,...b] 。
- 类数组转成真正的数组 let a = new Set([1,2,3]); let b = [...a]。
数组去重都有哪些方法?
- new Set
- filter + 对象属性不重复
- filter + indexOf
[] == []/[] === []?
每次使用 [] 都是新建一个数组对象。当数组比较的时候其实比较的是他们的引用。[] == []/[] === []的时候,从值上尽管两边都是[]但是从引用上两边是不相等的。
伪数组和数组的区别?
- 伪数组的特点:类型是object、不能使用数组方法、可以获取长度、可以使用for in遍历。
- 伪数组可以转换为数组的方法:
- Array.prototype.slice.call()
- Array.from()
- [...伪数组]
- 有哪些是伪数组:
- 函数的参数arguments
- Map和Set的keys()、values()和entires()
null 和 undefined 的区别,如何让一个属性变为null
- null是定义并赋值null
- undefined是定义未赋值
箭头函数
- 写法简洁
- 无自己的this,继承上一个作用域的this(全局或上一个函数)
- 内部this无法被改变
- arguments的特殊性(window下保存,this指向上一个函数则arguments表示上一个函数的参数) 没有arguments
- 不能作为构造函数(无自己的this、constructor)
- 没有自己的prototype(原型)
- 不能作为generator函数,不能使用yield命令
this指向(普通函数、箭头函数)?
普通函数: 1、普通函数的this指向window(默认情况,严格模式指向undefined,否则指向全局对象)。 2、事件处理函数中,this指向触发事件的对象,点谁谁就是this 3、对象方法中的this指向该对象(由new调用的话,this指向新创建的对象;) 4、由call或apply调用,this指向指定的对象; 箭头函数: 1、箭头函数没有this,不能作为构造函数,所以也没有原型和new 2、箭头函数的this取决于它的外部函数 3、没有arguments 普通函数指向调用处,箭头函数指向定义处
js继承的方法和优缺点?
原型链继承。
缺点: 1.引用类型的属性被所有实例共享 2.在创建 Child 的实例时,不能向 Parent 传参
借用构造函数(经典继承)
优点: 1.避免了引用类型的属性被所有实例共享 2.可以在 Child 中向 Parent 传参 缺点: 1.方法都在构造函数中定义,每次创建实例都会创建一遍方法。
- 组合继承
优点: 1.融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。
- 原型式继承
缺点: 1.包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。
- 寄生式继承
缺点: 1.跟借用构造函数模式一样,每次创建对象都会创建一遍方法。
- 寄生组合式继承
优点: 1.这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。 2.与此同时,原型链还能保持不变; - 3.因此,还能够正常使用 instanceof 和 isPrototypeOf。
call apply bind的作用和区别?
相同点:call apply bind三个方法都可以用来改变函数的this指向。 不同点: 1、fn.call (newThis,params) call函数的第一个参数是this的新指向,后面依次传入函数fn要用到的参数。会立即执行fn函数。 2、fn.apply (newThis,paramsArr) apply函数的第一个参数是this的新指向,第二个参数是fn要用到的参数数组,会立即执行fn函数。 3、fn.bind (newThis,params) bind函数的第一个参数是this的新指向,后面依次传入函数fn要用到的参数。 不会立即执行fn函数,且只能改变一次fn函数的指向,后续再用bind更改无效。
cookie sessionStorage localStorage 区别?
相同点:Cookie、SessionStorage、 LocalStorage都是浏览器的本地存储。都是存储在浏览器本地的。 不同点:
- cookie是由服务器端写入的,而SessionStorage、 LocalStorage都是由前端写入的。
- cookie的生命周期是由服务器端在写入的时候就设置好的,LocalStorage是写入就一直存在,除非手动清除,SessionStorage是页面关闭的时候就会自动清除。
- cookie的存储空间比较小大概4KB,SessionStorage、 LocalStorage存储空间比较大,大概5M。
如何实现可过期的localstorage数据?
- 惰性删除:惰性删除是指获取数据的时候,拿到存储的时间和当前时间做对比,如果超过过期时间就清除Cookie。
- 定时删除:每隔一段时间执行一次删除操作,并通过限制删除操作执行的次数和频率,来减少删除操作对CPU的长期占用。 LocalStorage清空应用场景:token存储在LocalStorage中,要清空token
创建ajax过程?
- 创建
XMLHttpRequest对象,创建一个异步调用对象. - 创建一个新的
HTTP请求,并指定该HTTP请求的方法、URL及验证信息. - 设置响应
HTTP请求状态变化的函数. - 发送
HTTP请求
fetch 请求方式?
- fetch是一种HTTP数据请求的方式。
- fetch()方法返回一个Promise解析Response来自Request显示状态(成功与否)的方法。
跨域是什么?如何解决跨域问题?
- 跨域:当前页面中的某个接口请求的地址和当前页面的地址如果协议、域名、端口其中有一项不同,就说该接口跨域了。
- 跨域限制的原因:浏览器为了保证网页的安全,出的同源协议策略。
- 跨域解决方案: - jsonp - 前端proxy后端cors - 线上Nginx - websockt
浏览器垃圾回收机制?
有两种机制: 1、标记清除:对所有活动对象进行标记,清除阶段会将没有标记的对象清除;标记整理算法:标记结束后,算法将活动对象压入内存一端,则需要清理的对象在边界,直接被清理掉就行。(效率低) 。 2、引用计数:将对象是否不再需要简化定义为有没有其他对象引用它,如果没有引用指向这个对象,则会被垃圾回收机制回收。(内存空间不连续)。
defer和async区别?
- html文件都是按顺序执行的,script标签中没有加defer和async时,浏览器在解析文档时遇到script标签就会停止解析阻塞文档解析,先加载JS文件,加载完之后立即执行,执行完毕后才能继续解析文档。
- script标签中写入defer或者async时,就会使JS文件异步加载,即html执行到script标签时,JS加载和文档解析同时进行。
- async是在JS加载完成后立即执行JS脚本,阻塞文档解析。
- defer则是JS加载完成后,在文档解析完成后执行JS脚本。
有什么方法可以保持前后端实时通信?
- 轮询:轮询是客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。
- 长轮询:长轮询是对轮询的改进版,客户端发送HTTP给服务器之后,如果没有新消息,就一直等待。有新消息,才会返回给客户端。在某种程度上减小了网络带宽和CPU利用率等问题。
- iframe流:iframe流方式是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间创建一条长连接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。
- WebSocket:WebSocket是类似Socket的TCP长连接的通讯模式,一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。
- SSE:SSE 是单向通道,只能服务器向浏览器发送,因为 streaming 本质上就是下载。
浏览器如何渲染页面的?
- HTML被HTML解析器解析成DOM树。
- CSS被CSS解析器解析成CSS规则树。
- 浏览器会将CSS规则树附着在DOM树上,并结合两者生成渲染树Render Tree。
- 生成布局(flow),浏览器通过解析计算出每一个渲染树节点的位置和大小,在屏幕上画出渲染树的所有节点。
- 将布局绘制(paint)在屏幕上,显示出整个页面
浏览器输入URL发生了什么?
- url解析:判断是搜索内容还是请求URL。
- 查找本地缓存:如果有缓存直接返回给页面,没有缓存则进入网络请求阶段。
- DNS解析。
- 通过三次握手建立TCP连接。
- 合成请求头信息,发送http请求。
- 处理响应信息。
- 通过四次挥手断开TCP连接。
- 如果响应状态码301,则重定向。
- 浏览器进行页面渲染:1)解析html,生成DOM树;2)根据css计算节点样式,生成stylesheet;3)生成布局树;4)为特定的元素生成独立图层;5)布局渲染
axios的拦截器原理及应用?
axios拦截器分为响应和请求拦截器。
- 请求拦截器 在请求发送前进行必要操作处理,例如添加统一cookie、请求体加验证、设置请求头等,相当于是对每个接口里相同操作的一个封装。
- 响应拦截器 同理,响应拦截器也是如此功能,只是在请求得到响应之后,对响应体的一些处理,通常是数据统一处理等,也常来判断登录失效等。
重绘、重排区别如何避免?
- 重排(回流):当dom的变化影响元素的几何信息(元素的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面的正确的位置。这个过程叫重排。
- 重绘:一个元素外观发生改变,但没有改变布局,重新把元素绘制出来的过程。
- 重排(回流)一定会发生重绘,重绘就不一定要重排(回流)。
- 重排对性能的消耗要更多一些。
- 触发重排的方法:改变元素的位置;改变元素的尺寸,比如边距,填充,边框,宽度,高度等;页面初始渲染,这是开销最大的一次重排;改变元素的字体大小;改变浏览器窗口大小,比如resize事件发生时;改变元素内容,字体数量,图片大小;激活css伪类;设置style属性的值,因为设置style属性会改变结点样式,就会触发重排。查询某些属性、或者调用某些计算方法:offsetwidth,offheight。
- 避免重排的方法是样式集中改变;使用absolute或fixed脱离文档流;使用gpu加速,transform。