JS前端面试题

209 阅读13分钟

@HTTP协议相关

URL回车后发生什么?

  • URL解析: 协议段 + 域名/IP段 + 端口段(HTTP协议默认端口80/HTTPS协议默认端口443)+ 路径段 + 查询参数段 + 哈希段;浏览器执行URL解析;
  • DNS查询:域名映射为IP+端口;IP=宇宙中的唯一主机(host);端口=该主机上的唯一进程(应用程序);
  • TCP连接:宗旨稳定连接+可靠传输;三次握手实现稳定连接;四次挥手实现可靠传输;稳定连接=服客双方确保彼此能发能收;可靠传输=服客双方确保对方数据发送完毕才能断开连接;三次挥手图形=客户端出发的N字形;四次挥手=客户端出发的V字形+服务端出发的V字形;
  • HTTP请求发送:请求报文的构成=请求行+请求头+空行+请求体;
  • HTTP响应返回:响应报文的构成=状态行+响应头+响应体;
  • TCP连接断开:要经历四次挥手;
  • 浏览器渲染页面:要会画渲染示意图;

简述TCP原理

  • 宗旨稳定连接+可靠传输;
  • 三次握手实现稳定连接;
  • 四次挥手实现可靠传输;
  • 稳定连接=服客双方确保彼此能发能收;
  • 可靠传输=服客双方确保对方数据发送完毕才能断开连接;
  • 三次挥手图形=客户端出发的N字形;
  • 四次挥手=客户端出发的V字形+服务端出发的V字形;
  • PS:一蹴而就原则:三次握手和四次挥手都必须一次性完成,否则作废重来;
  • PS:UDP=没有握手加挥手=连接+收发+断开=注重性能;
  • PS:TCP/UDP工作在OSI七层网络模型的传输层;
  • PS:OSI七层网络模型=物数网(IP)传(TCP/UDP)会表应(HTTP/FTP/SMTP)

GET请求与POST请求异同

  • 数据携带方式:GET只能是查询参数;POST/PUT/PATCH还可以请求体携带数据;
  • 数据量:查询参数K级别;请求体携带数据量理论上无限;
  • 安全性:查询参数(完全暴露)不能携带敏感数据;请求体中的数据相对安全;
  • 安全性:HTTPS协议接近绝对安全;HTTPS协议的通信数据全部使用加密;
  • PS: HTTPS协议使用非对称加密; 服务器持有公钥(网站证书),客户端给服务器发送的数据使用该公钥加密;服务端用自己配对的私钥解密;

HTTP常见的状态码有哪些?

  • 100+=请求继续;
  • 200+=请求成功;200=请求成功
  • 300+=重定向;301=永久重定向;302=临时重定向;304=资源未更改(滚回去用缓存);
  • 400=客户端错误;401=未授权;403=禁止访问;404=路径未找到(域名存在但路径不存在);
  • 500=服务端错误;500=服务端错误;
  • 追问1:具体解释一下304的含义;
  • 追问2:具体解释一下404;
  • 追问3: 301与302具体有何区别;
  • 追问4:你平时是如何调试网络通信错误的;

JWT(JsonWebToken)鉴权原理

  • 单点登录:单点登录,多点开花;A服务器上登录并发放token + B服务器上可以效验 + AB服务器必须持有相同的秘钥
  • 荷载加密:token构成 = 头 + 荷载 + 签名;签名由【头 + 荷载 + 秘钥】三合一计算而来;截获并篡改token中的任意片段都会导致token效验失败
  • VS cookie:cookie无法实现单点登录;cookie需要自行加密

Ajax如何实现跨域

  • 服务端支持跨域:配置响应头Access-Control-Allow-Origin = 允许跨域访问的前端
  • JSONP:用script/src得到服务端响应的callback(jsonData)在前端执行
  • 使用代理:在具有代理功能的服务器上(Nginx)配置代理,访问代理地址 = 访问目标地址
location /a {
    proxy_pass 需要跨域访问的b地址
}

@JS基础

什么是事件委托

  • 概念:子元素委托事件给父元素去处理;
  • 好处:节约内存+方便管理+防内存泄漏;
  • 节约内存:事件监听器的数量少;
  • 方便管理:无需考虑子元素动态增减时事件监听器的动态增减问题;
  • 防内存泄露:子元素被删除时无需删除其对应的事件监听器;
  • PS:追问=哪些情形会造成内存泄露?

ES6有哪些新特性?

变量层面

  • let:var弊病=变量提升+重复声明+作用域穿透;
  • const:通常将不希望修改地址的对象声明为常量;

数据结构层面

  • 数组:解构+展开;
  • 对象:解构+展开+简写;
  • 字符串:模板字符串;
  • Map:方便增删改产的键值对存储器;
  • Set: 有序且不重复的元素集;

函数层面

  • 箭头函数:this从父级作用域继承;
  • PS:this的可能情形;

类层面

  • class系列语法:class(构造函数+原型属性) + constructor(构造函数) + static静态成员 + super调用父类方法;super(name)调用父类构造器; super.xxx()调用父类实例方法;

请说出10个请求头

  • Accept:客户端想要的数据类型
  • Accept-Encoding:客户端想要的压缩编码
  • Cookie:客户端携带的用户信息
  • If-None-Match:客户端携带上次的数据指纹
  • If-Modified-Since:携带上次数据的最后的更新时间
  • User-Agent:客户端浏览器信息
  • Origin:客户端地址
  • Content-Type:请求体的数据类型
  • Content-Length:请求体的数据字节长度
  • Accept-Language:客户端想要的语言
  • Host:服务器地址

请说出10个响应头

  • Content-Type:服务端返回的数据类型
  • Content-Encoding:服务端响应的压缩编码
  • Set-Cookie:服务端给客户端写的Cookie
  • Last-Modified:数据的最后更新时间
  • E-tag:数据指纹
  • Server:服务器的信息
  • Access-Control-Allow-Origin:服务端允许谁跨域访问它
  • Content-Type:响应体的数据类型
  • Content-Length:响应体的数据字节长度
  • Content-Language:响应体的语言
  • Data:服务器响应时间

什么是深拷贝/浅拷贝

  • 浅拷贝:引用数据类型赋值给别人(包括调用函数时实参给形参赋值)= 地址被拷贝 = 修改任何副本都会影响其他副本
  • 浅拷贝示例const obj2 = objfn(obj)
  • 深拷贝:将对象中的所有key-value递归拷贝给另外一个对象,递归到每一个叶子结点(即value是基本数据类型或函数),深拷贝的副本修改互相不影响

什么是值传递/引用传递

  • 值传递:基本数据类型赋值给比人,包括调用函数时作为入参传递
  • 应用传递:引用数据类型赋值给别人,包括调用函数时作为入参传递
  • 值传递示例let num2 = numfn(num)
  • 引用传递示例let obj2 = objfn(obj)

this的所有可能情形

  • 普通函数的this就是调用者;(显式主语+无主语时为window+DOM事件监听器的主语恒为事件源e.currentTarget)
  • 箭头函数的this从父级作用域继承;
  • 普通函数可以通过 fn.call/apply/bind 绑定this
  • 构造器里的this为正在构建的实例
  • 实例方法中的this为当前实例
  • 静态方法中的this为当前类

谈谈对事件传播/派发机制的理解

  • 解决事件发生时具体由谁处理的问题
  • 具体解决方案可从三方面入手,即:指定传播方向 + 按需设置一级到多级监听器 + 设置传播断点
  • 指定传播方向:dom.addEventListener(type, handler, useCapture)最后一个如蝉true即使用捕获,否则使用冒泡,默认冒泡
  • 设置断点:e.stopPropagation()

内存泄露的可能情形有哪些?

  • 用完的事件监听器:DOM元素删除时忘记删除其事件监听器;
  • 用完的事件监听器:离开页面时没有释放各种事件监听器;(div.onclick=null,div.removeEventListener...)
  • 用完的定时器:定时器用完没有clear;
  • 用完的全局变量:用完没释放的全局变量(obj=null)
  • 用完的闭包:用完未释放的闭包(innerFn = null)
  • 总结:【用毕未释放】的【事件监听器】+【定时器】+【全局变量】+【闭包】;

谈谈对闭包的理解

  • 广义闭包:凡返回引用数据的函数,在其返回值被引用期间,期执行空间无法被释放,客观上形成一个零食在封闭作用域,即函数闭包
  • 狭义闭包:即返回函数的函数,外层函数通常作为内层函数的状态缓存区
  • 函数式编程:闭包是函数式编程的基础,可以派生出诸多玩法
  • 释放闭包:闭包会导致算法的时间和空间开销增大,用完后一定要释放,否则会形成内存泄漏
  • 释放方式:let ret = enclosure()用完后ret = null,无人引用闭包的返回值闭包就能释放了

深拷贝 VS 浅拷贝

  • 浅拷贝:复制地址 + 相互影响const bojCopy = obj
  • 摄拷贝:递归拷贝对象的所有key-value + 直到叶子节点 + 互不影响const objCopy = deepCopy(obj)

值传递 VS 引用传递

  • 值传递:基本数据类型(number, string, boolean, null, undefined)+ 赋值给别人 + 值拷贝 + 互不影响const numCopy = numfn(num)基本数据类型的实参传递给形参走的是值传递/值拷贝
  • 引用传递:引用数据类型 + 赋值给别人(包括实参给形参赋值) + 地址的浅拷贝 + 相互影响const objCopy = obj/fn(obj)

前端有哪些数据存储方式

  • cookie:4K级别 + 键值存储 + 主要用于前后端共享用户信息
  • localStorage/sessionStorage:50M级别 + 键值存储 + 前端独享 + 缓存业务数据(以减少网络请求次数)

@面向对象

typeof 与 instanceof 区别

  • typeof主要用于判断基本数据类型,对于引用数据类型只能识别为function或object
  • instanceof判断某个对象是否是某类的实例或后代实例
  • 张三 instanceof Person或(Animal,Object)结果都是true

JS如何实现继承

  • 以父类的实例挂载到子类的原型身上

字符串有哪些常用API?

  • slice:截取字符串(含头不含尾)
  • split:根据传入的切割符切割字符串,返回数组
  • indexOf:指定字符串中寻找字符串所在位置的下标
  • concat:拼接字符串
  • charAt:根据下标获取字符的位置
  • trim:去掉字符串头尾的空格
  • toUpperCase:将字符串转成大写
  • toLowerCase:将字符串转成小写
  • replace:替换字符串obj.replace('AAA','***')
  • search:查找第一个匹配的字符串下标
  • substring:截取字符串(含头含尾)obj.substring(0,3)
  • endsWith:字符串必须以特定的值结尾(返回boolean值)obj.endsWith('taobao.com')

数组常用API

  • push:数组末尾追加一个元素
  • pop:删除数组末尾的一个元素
  • unshift:在数组前面追加一个元素
  • shift:删除数组最前面的一个元素
  • splice:根据下标删除特定下标位置的内容(可删除,可追加)arr.splice(0, 1, 90)
  • reverse:反转数组
  • sort:数组排序降序:arr.sort((a, b) => b - a)``升序:arr.sort((a, b) => a - b)
  • concat:拼接数组
  • join:将数组每一项转换成字符串
  • indexOf:根据下标查找数组中对应的元素
  • forEach:遍历数组arr.forEach(item => console.log(item))
  • map:映射数组,对数组中的每一项进行操作,返回新数组`arr.map(item => Math.pow(item, 2))
  • filter:过滤数组`arr.filter(item => item % 3 == 0)
  • slice:截取数组(含头不含尾)
  • every:判断数组中所有元素是否都符合条件arr.every(item => item % 3 == 0)
  • some:判断是否有一项或者多项都满足条件arr.some(item => item % 3 == 0)
  • rednce:累加数组中的数据arr.rednce((pv,cv) => pv + cv)
  • includes:判断数组中是否有某个值

正则API

  • 普通创建正则(常用)const reg = /hello/ig
  • new 创建正则const reg = new RegExp('hello', 'ig')
  • exec() 每次捕获一次,加g的时候是捕获全部的内容,不加g的时候只捕获第一个
let str = 'abc';
const reg = /(a)(b)(c)/;
console.log( reg.exec(str));
// ["abc", "a", "b", "c", index: 0, input: "abc"]
// 其中 "a" "b" "c" 都是第一次匹配到的"abc" 拆分的小分组
  • test() 判断正则是否成立,返回值为true或者false
let str="aass1d"
const reg=/\d/g
const r=reg.test(str)
console.log(r) //true
//返回值为true或者false
  • match()
const  reg = /\d+/g;
let str = 'abc123de45fgh6789qqq111';
str.match(reg)
// ["123", "45", "6789", "111"]
//受小分组的影响 且也受g的影响
  • search() 返回第一条自己的索引位置
let str="aass1zd" 
const reg=/\d/g 
const r= str.search(reg); 
console.log(r) // 4
  • replace() 替换方法,一代新词换旧词
let str3="a1ass11zd" 
const reg=/\d+/g 
const r3= str3.replace(reg,"1111") 
console.log(r3,"replace") //a1111ass1111zd

@Git

Git常用命令

  • 日常三件套:添加受控、提交、推远程 git add . git commit -m "日志" git push -u origin master
  • stash 暂存(不能达到提交要求的)
  • resset / resvert 版本回退
  • mege / rebase 合并其他分支
  • fetch / pull 拉去远程仓库内容 image.png

Git怎么回退版本

  • git reset --hard 版本ID = 暴力回退到指定版本,其后的历史记录被销毁
  • git revert 版本ID = 否定特定版本的空闲 = 将这个版本以前的版本合并到HEAD中来,形成一个最新的快照
  • 暴力回退在有充分把握的情况下简单粗暴高效
  • revert不销毁任何一次提交的历史记录,更加稳妥

pull与fetch的区别

  • pull将远程仓库的所有更新(分支 + 快照)同步到笨地仓库,并直接将origin / xxx合并到工作区里面的xxx分支,有覆盖未提交的风险
  • fetch仅同步远程仓库的所有更新(分支 + 快照)同步到本地仓库,而不更新工作区内容
  • git pull = git fetch + git merge origin / xxx

merge与rebase的区别

  • merge xxx 立足当前,将目标分支xxx的代码兼容并合并进来
  • rebase xxx 以xxx的最新提交为底,与当前分支的每次提交一一形成合并版本
  • merge 是一步到位的暴力合并,没有过程信息
  • rebase 是逐快的慢慢合并,过程记录完整
  • merg 简单厨宝,rebase精细一些

那些命令可能形成冲突

  • git merge xxx 当前分支兼容并合并其他分支
  • git stash pop 暂存区顶部代码与工作区分支合并
  • git revert xxx 目标快照的上一快照与HEAD节点合并
  • git rebase xx 道理和merge
  • git pull 远程分支origin / xxx与工作区中的xxx合并

@Vue八股文

说说对Vue的理解

  • MVVM:VM层相当于MVC中的C层中的一部分特定职能,实时监控和读取数据层的修改并将其映射到视图层,所以VM层专干数据驱动视图的活
  • 响应式:与VM本质相同提法有区别,还是数据驱动视图,声明响应式的数据,当数据变化时自动驱动视图更新
  • 组件化开发
  • 虚拟DOM与diff算法

Vue3的响应式与Vue2有什么区别

  • Vue2监听数据变化使用数据劫持

前端核心面试题(持续更新) 具体看这里