前端JavaScript面试题(持续更新)

686 阅读18分钟

最近也跟很多小伙伴一样在投简历面试中,总结自己面试中提问到的问题,同时也会看些视频博客补充八股文。废话不多说,直接上干货!(持续更新)

1. JS由哪三部分组成?

  • ECMAScript:JS的核心内容,描述了语言的基础语法,比如var,for,数据类型(数组、字符串等)
  • 文档对象模型DOM:DOM把整个HTML页面规划为元素构成的文档
  • 浏览器对象模型BOM:对浏览器窗口进行访问和操作

2. JS有哪些内置对象?

  • String Boolean Number Array Object Function Math Data RegExp(正则)...
  • Math 常用: abs()算绝对值  sqrt()算开平方  max()  min()
  • Data 常用:new Data()实例化  getYear()
  • String 常用:concat()  lenght slice() split()

3. 操作数组的方法有哪些?

push()  pop()  sort()  splice()  unshift()  shift() reverse()  concat()  join() map()  filter()  every()  some()  reduce()  isArray()  findIndex() ...
其中,push()  pop()  sort()  splice()  unshift()  shift() reverse()会改变原数组

4. JS对数据类型的检测方式有哪些?

  • typeof() -- 只适用于基本数据类型,遇到引用数据类型就不管用
  • instanceof() -- 只能判断引用数据类型,不能判断基本数据类型
  • constructor -- (有局限性)几乎可以判断基本数据类型和引用数据类型
  • Object.prototype.toString.call() -- 完美解决方案

5. 说一下闭包,特点?

什么是闭包? 函数嵌套函数,内部函数被外部函数返回并保存下来时,就会产生闭包。

特点:可以重复利用变量,并且这个变量不会污染全局的一种机制;这个变量是一直保存在内存中,不会被垃圾回收机制回收的。

缺点:闭包较多时,会消耗内存,导致页面性能下降,在IE浏览器中才会导致内存泄漏。
使用场景:防抖、节流,函数嵌套函数避免全局污染的时候。

6. 前端的内存泄漏怎么理解?

JS里已经分配内存地址的对象,但是由于长时间没有释放或者没办法清除,造成长期占用内存的现象,会让内存资源大幅浪费,最终导致运行速度变慢,甚至崩溃的情况。

导致内存泄漏的因素: 一些为声明直接赋值的变量、未清空的定时器、过度的闭包、一些引用元素没有被清除。

7. 什么是事件委托(事件代理)?

原理就是利用事件冒泡的机制来实现,也就是说把子元素的事件绑定到了父元素的身上。如果子元素阻止了事件冒泡,那么委托也就不成立了。

阻止事件冒泡:event.stopPropagation()  addEventListener(‘click’, 函数名, true/false) 默认是false(事件冒泡), true(事件捕获)

好处:提高性能,减少事件的绑定,也就减少了内存的占用。

8. 基本数据类型和引用数据类型的区别?

  • 基本数据类型:String Number Boolean Undefined null
    -- 保存在栈内存当中,保存的就是一个具体的值。
  • 引用数据类型(复杂数据类型):Object Function Array
    -- 保存在堆内存中,声明一个引用类型的变量时,它保存的是引用类型数据的地址。

9. 说一下原型链

先说下原型:原型就是一个普通对象,它是为构造函数的实例共享属性和方法,所有实例中引用的原型都是同一个对象。

使用prototype可以把方法挂在原型上, 内存值保存一份

_proto_可以理解为指针,实例对象中的属性,指向了构造函数的原型(prototype)

原型链:一个实例对象在调用属性和方法的时候,会依次从实例本身、构造函数原型、原型的原型去找。

10. new操作符具体做了什么?

  • 先创建一个空对象
  • 把空对象和构造函数通过原型链进行连接
  • 把构造函数的this绑定到新的空对象身上
  • 根据构建的函数返回的数据类型判断,如果是值类型,则返回对象;如果是引用类型,就返回这个引用类型。 image.png

11. JS是如何实现继承的?

  • 原型链继承:让一个构造函数的原型是另一个类型的实例,那么这个构造函数new出来的实例就具有该实例的属性。
    优点:写法方便简洁,容易理解;
    缺点:对象实例共享所有继承的属性和方法,无法向父类构造函数传参。
  • 借用构造函数继承:在子类型构造函数的内部调用父类型构造函数;使用apply() 或 call() 方法将父对象的构造函数绑定在子对象上。
    优点:解决了原型链实现继承的不能传参的问题和父类的原型共享的问题;
    缺点:在父类型的原型中定义的方法,对子类型而言是不可见的,结果所有类型都只能使用构造函数模式。
  • 组合式继承:将原型链和借用构造函数组合到一块。使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过原型上定义方法实现了函数复用,又能够保证每个实例都有自己的属性。
    优点:解决了原型链继承和借用构造函数继承造成的影响;
    缺点:无论在什么情况下,都会调用两次父级构造函数:一次是在创建子类原型的时候,另一次是在子类构造函数内部。
  • ES6的class类继承:Class通过extends关键字实现继承,其实质是先创造出父类的this对象,然后用子类的构造函数修改this,子类的构造方法中必须调用super方法,且只有在调用了super()之后才能使用this,因为子类的this对象是继承父类的this对象然后对其进行加工,而super方法表示的是父类的构造函数,用来新建父类的this对象。
    优点:语法简单易懂,操作更方便;
    缺点:并不是所有的浏览器都支持class关键字。

12. JS的设计原理是什么?

联想到:JS引擎 运行上下文 调用栈 事件循环 回调

  • 既然js是一门高级语言,它被计算机CPU执行前需要通过某种程序将JS转换成低级的计算机语言并执行。这种程序就被称为js引擎。和其他语言相比js有许多执行引擎。
  • 调用栈是JS引擎追踪函数执行流程的一种机制,当执行环境中调用了多个函数时,通过这种机制,我们能够追踪到哪个函数正在执行,执行的函数体又调用了哪个函数。它采用了先进后出的机制来管理函数的执行。
    javaScript的执行环境是一个单线程,这就意味着JS环境只有一个调用栈。
  • JS是单线程的,但是浏览器是多线程的,这使得JS同时具有异步的操作,即定时器,请求,事件监听等,而这个时候就需要一套事件的处理机制去决定这些事件的顺序,即EventLoop(事件循环)。
  • 前端发出的请求,一般都是会进入浏览器的http请求线程,等到收到响应的时候会通过回调函数推入异步队列,等处理完主线程的任务会读取异步队列中任务,执行回调

13. JS中的this指向的问题

  • 全局对象中的this指向:指向的是window
  • 全局作用域或者普通函数中的this:指向的是window
  • this永远指向最后调用它的那个对象:在不是箭头函数的情况下
  • new关键词改变了this的指向
  • apply,call,bind方法可以改变this的指向,前提:不是箭头函数
  • 箭头函数中的this:它的指向在定义的时候就已经确定了。箭头函数没有this,看外层是否有函数,有函数则就是外层函数的this,没有就是window
  • 匿名函数中的this:永远指向window,匿名函数的执行环境具有全局性,故this指向window

14. scirpt标签里的async和defer有什么区别?

  • 当没有async和defer这两个属性时,浏览器会立刻加载并执行指定的脚本。
  • 有async:加载和渲染后面元素的过程将和script的加载和执行并行进行(异步)
  • 有defer:加载和渲染后面元素的过程将和script的加载并行进行(异步),但是它的执行事件要等所有元素解析完成之后才会执行
    总的说,defer会保证脚本的一个顺序,而async不会,谁先下载好就先加载谁。

15. setTimeout最小执行时间是多少?

HTML5规定的内容:

  • setTimeout最小执行时间是4ms
  • setInterval最小执行时间是10ms(如果小于10ms就会调整成为10ms)

16. ES6和ES5有什么区别?

JS的组成:ECMAScript BOM DOM

  • ES5: ECMAScript5 2009年修订
  • ES6: ECMAScript6 2015年修订 是JS的下一个版本标准

17. ES6的新特性?

  • 新增块级作用域(let const)
    -- let const同var的区别:
        1. let const不存在变量提升
        2. 存在暂时性死区的问题
        3. 块级作用域的内容
        4. 不能再同一个作用域内重复声明
  • 新增了定义类的语法糖(class)
  • 新增了一种基本数据类型(symbol)
  • 新增了箭头函数
    -- 和普通函数的区别:
        1. 不能作为构造函数使用,不能用new,箭头函数就没有原型
        2. 箭头函数没有arguments
        3. 箭头函数不能用call apply bind去改变this的指向
        4. this指向外层第一个函数的this
  • 新增了解构赋值:从数组或者对象中取值,然后给变量赋值。
  • 新增了函数参数的默认值
  • 给数组新增了API
  • 对象和数组新增了扩展运算符
  • Promise
    -- 解决回调地狱的问题
        1. 自身有all reject resolve race方法
        2. 原型上有then catch
        3. 是把异步操作队列化
        4. 有三种状态:pending初始状态  fulfilled操作成功  rejected操作失败
        5. 状态改变:pending -> fulfilled / pending -> rejected 一旦发生,状态就会凝固,不再改变
    -- async await:
        1. 同步代码做异步操作,两者必须搭配使用
        2. async表明函数内有异步操作,调用函数会返回promise对象
        3. await是组成async的表达式,结果是取决于它所等待的内容:如果是promise,那就是promise的结果;如果是普通函数,就进行链式调用
        4. await后的promise如果是reject状态,那么async函数都会中断,后面的代码不执行
  • 新增了模块化(import export)
  • 新增了set和map数据结构
    -- set就是不重复,map的key的类型不受限制
  • 新增了generator

18. call apply bind 三者有什么区别?

  • 都是改变this指向和函数的调用,call和apply的功能类似,只是传参的方法不同
  • call方法传的是一个参数列表
  • apply传递的是一个数组
  • bind传参后不会立刻执行,会返回一个改变了this指向的函数,这个函数可传参,bind()()
  • call方法的性能要比apply好一些,所以call用的更多

19. 用递归的时候有没有遇到什么问题?

  • 如果一个函数内可以调用函数自身,那么这个就是递归函数 -- 函数内部调用自己
  • 特别注意:写递归必须要有退出条件return,否则会一直将进行,容易造成浏览器卡顿

20. 如何实现一个深拷贝?

深拷贝:就是完全拷贝一份新的对象。会在堆内存中开辟新的空间,拷贝的对象被修改后,原对象不受影响。主要针对的是引用数据类型。

方法:

  • 扩展运算符
    -- 缺点:这个方法只能实现第一层,当有多层的时候还是浅拷贝
  • JSON.parse(JSON.stringify())
    -- 缺点:该方法并不会拷贝内部函数
  • 利用递归函数实现(优)

21. 什么是事件循环?

JS是一个单线程的脚本语言。
首先要了解:主线程 执行栈 任务队列 宏任务 微任务

  • 主线程先执行同步任务,然后才去执行任务队列里的任务。如果在执行宏任务之前有微任务,那么先执行微任务。全部执行完之后等待主线程的调用,调用完之后再去任务队列中查看是否有异步任务,这样一个循环往复的过程就是事件循环。

22. ajax是什么?怎么实现的?

创建交互式网页应用的网页开发技术。
在不重新加载整个网页的前提下,与服务器交换数据并更新部分内容。

原理:通过XmlHttpRequest对象向服务器发送异步请求,然后从服务器拿到数据,最后通过JS操作DOM更新页面。

实现步骤:

  • 创建XmlHttpRequest对象 xhr
  • 通过xhr对象里的open()方法和服务器建立连接
  • 构建请求所需的数据,并通过xhr对象的send()方法发送给服务器
  • 通过xhr对象的onreadystatechange事件监听服务器和你的通信状态
  • 接收并处理服务器响应的数据结果
  • 把处理的数据更新到HTML页面上

23. get和post的区别

  • get一般是获取数据,post一般是提交数据
  • get参数会放在url上,安全性会比较差;post是放在body中,数据长度不受限制
  • get请求刷新服务器或退回是没有影响的,post请求退回时会重新提交数据
  • get请求会被缓存,post请求不会被缓存
  • get请求会被保存在浏览器历史记录中,post不会
  • get请求只能进行url编码,post请求支持很多种

24. Promise的内部原理是什么?它的优缺点是什么?

Promise对象,封装了一个异步操作并且还可以获取成功或者失败的结果

Promise主要就是解决回调地狱的问题。之前如果异步任务比较多,同时他们之间有相互依赖的关系,就只能使用回调函数处理,这样容易形成回调地狱,代码可读性差,可维护性也差

有三种状态:pending初始状态 fulfilled成功状态 rejected失败状态
状态改变:pending -> fulfilled / pending -> rejected 一旦发生,状态就会凝固,不再改变

原理:

  • 首字母大写,是一个构造函数。构造一个Promise实例,实例需要传递函数的参数,这个函数有两个形参,分别都是函数类型,一个是resolve,一个是reject
  • Promise上还有then方法,这个方法就是来指定状态改变时的确定操作,resolve是执行第一个函数,reject是执行第二个函数

缺点:

  • 我们无法取消promise,一旦创建它就会立即执行,不能中途取消
  • 如果不设置回调,promise内部抛出的问题就无法反馈到外面
  • 若当前处于pending状态时,无法得知目前在哪个阶段

25. promise和async await的区别是什么?

  • 都是处理异步请求的方式
  • promise是ES6, async await是ES7的语法
  • async await是基于promise实现的,它和promise都是非阻塞性的

优缺点:

  • promise是返回对象,我们要用then,catch方法去处理和捕获异常,并且书写方式是链式,容易造成代码重叠,不好维护; async await是通过try catch进行捕获异常
  • async await最大的优点就是能够让代码看起来像同步一样,只要遇到await就会立刻返回结果,然后再执行后面的操作; promise.then()的方式返回会出现请求还没返回,就执行了后面的操作。

26. 浏览器的存储方式有哪些?

  • cookies
        1. H5标准前的本地存储方式
        2. 兼容性好,请求头自带cookie
        3. 存储量小,资源浪费,使用麻烦(需要封装)
  • localstorage
        1. H5加入的以键值对为标准的方式
        2. 操作方便,永久存储,兼容性较好
        3. 保存值的类型被限定,浏览器在隐私模式下不可读取,不能被爬虫
  • sessionstorage
        与localstorage类似,不同的是:当前页面关闭后会立刻清理,不能在所有同源窗口进行共享,只能会话级别的存储方式
  • indexedDB
        H5标准的存储方式,是键值对进行存储,可以快速读取,适合web场景

27. token存在sessionstorage还是localstorage?

什么是token?
        验证身份的令牌,一般用户通过账号密码登录后,服务端把这些凭证通过加密等一系列操作后得到的字符串。

  • token存在localstorage时:后期每次请求接口都需要把它当做一个字段传给后台
  • token存在cookie时:会自动发送,缺点就是不能跨域
  • 一般情况下会存在localstorage或sessionstorage中比存在cookie要好
  • 如果存在localstorage中,容易被xss攻击,但是如果做好了对应的措施,则利大于弊;如果存在cookie中,会有CSSRF攻击

28. token的登录流程

  • 客户端用账号密码请求登录
  • 服务端收到请求后,需要去验证账号密码
  • 验证成功之后,服务端会签发一个token,把这个token发送给客户端
  • 客户端收到token后保存起来,可以放在cookie也可以是localstorage
  • 客户端每次向服务端发送请求资源的时候,都会携带这个token
  • 服务端收到请求,接着去验证客户端里的token,验证成功才会返回客户端请求的数据

29. 页面渲染的过程是怎么样的?

  • DNS解析
  • 建立TCP连接
  • 发送HTTP请求
  • 服务器处理请求
  • 渲染页面
            浏览器会获取HTML和CSS的资源,然后把HTML解析成DOM树,再把CSS解析成CSSOM,然后把DOM和CSSOM合并为渲染树 -> 布局 -> 把渲染树的每个节点渲染到屏幕上(绘制)
  • 断开TCP连接

30. DOM树和渲染树的区别

  • DOM树是和HTML标签一一对应的,包括head和隐藏元素
  • 渲染树是不包含head和隐藏元素
  • 同时要注意二者形成的时间节点即可

31. 精灵图和base64的区别

  • 精灵图:把多张小图整合到一张大图上,利用定位的一些属性把小图显示到页面上,访问页面时可以减少请求,提高加载速度
  • base64:传递8bit字节代码的编码方式,把原本的二进制形式转化为64字符的单位,最后组成字符串
  • base64是会和html和css一起下载到浏览器中,减少请求,减少跨域问题,但是一些低版本不支持,若base64体积比原图片大,不利于css的加载

32. svg格式了解多少?

svg格式是基于XML语法格式的图像格式,可缩放矢量图。其他图像是基于像素的,svg是属于对图像形状的描述,本质是文本文件,体积小,并且不管放大多少倍都不会失真。

  • svg可直接插入页面中,成为DOM的一部分,然后用JS或CSS进行操作 <svg></svg>
  • svg可作为文件被引入 <img src="pic.svg" />
  • svg可以转为base64引入页面

33. 了解过JWT吗?

JSON Web Token 通过JSON形式作为在web应用中的令牌,可以在各方之间安全地把信息作为JSON对象传输

JWT的认证流程:

  • 前端把账号密码发送给后端的端口
  • 后端核对账号密码成功后,把用户id等其他信息作为JWT负载,把它和头部分别进行base64编码拼接后签名,形成一个JWT(token)
  • 前端每次请求时都会把JWT放在HTTP请求头的Authorization字段内
  • 后端检查是否存在,若存在就验证JWT的有效性(签名是否正确,token是否过期)
  • 验证通过后,后端使用JWT中包含的用户信息进行其他操作,并返回对应结果

34. npm的底层环境是什么?

npm全称是node package manager,node的包管理和分发工具,已经成为分发node模块的标准,是JS的运行环境。

npm的组成:网站、注册表、命令行工具