1.请你谈一谈对js运行机制的理解。
js就是单线程,同一个时间只能做一件事情,作为浏览器的脚本语言,js的主要用途是与用户互动,以及操作DOM,这决定了它只能是单线程,否则会带来很复杂的同步问题, 所以为避免复杂性,js从一诞生就是单线程,为了利用多核CPU的计算能力,HTML5提出了Web Worker标准,允许Js脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM,所以这个新标准也没有改变js是单线程的本质。
但是这样CPU的利用率又不是很高,所以就出现了任务队列,一种是同步任务,一种是异步任务。同步任务指的是:在主线程上排队执行,只有前一个任务执行完毕,才能执行后一个任务。异步任务指的是:不进入主线程、而进入任务队列的任务,只有当主线程执行完毕,才会让任务队列中的异步任务执行。
异步任务的执行机制如下:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
3.闭包
概念:闭包是指有权访问另一个函数作用域中的变量的函数。
闭包的优点:可以重复使用变量并且不会不会造成变量污染。
闭包的缺点:容易造成内存泄漏,
4.继承
①原型链继承
重点:让新实例的原型等于父类的实例
缺点:1)新实例无法向父类构造函数传参
2)继承单一
3)所有的新实例都会共享父类实例的属性
②借用构造函数继承
重点:用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的只执行)
特点:1)只继承了父类构造函数的属性,没有继承父类原型的属性
2)解决了原型链的缺点
3)在子实例中可以向父实例传参
4)可以继承多个构造函数属性(call多个)
缺点:1)只能继承父类构造函数的属性
2)无法实现构造函数的复用
3)每个新实例都有父类构造函数的副本,臃肿
③ 组合继承
重点:结合了两种模式的优点,传参和复用
特点:1 可以继承父类原型上的属性,可以传参,可复用。
2 每个新实例引入的构造函数属性都是私有的
缺点:调用了两次父类构造函数(耗内存),子类的构造函数会替代原型上的那个父类构造函数
④ ES6的class和extend
5 数据类型 存储位置
五种基本数据类型
undefined、null、boolean、Number、string
存储位置:栈
引用数据类型(对象、数组、函数)
存储位置:堆
引用数据类型在栈中存储量指针,该指针指向堆中该实体的起始位置,当解释器寻找该值时,会首先检索其在栈中的地址,取得地址后,从堆中获得实体。
6 ES6、7、8特性
① let,const
1)let和const存在块级作用域
2) 不存在变量提升
3)不允许重复声明
4)声明必须赋值否则报错
5)暂时性死区:一个块级作用域如果定义了局部变量,就不会使用
② 结构赋值
③ 字符串函数的扩展,在String.prototype添加了一些方法
1)includes() 返回布尔值,表示是否找到参数字符串
2)startsWith() 返回布尔值,表示参数字符串是否在原字符串的头部
3)endsWith() 返回布尔值,表示参数字符串是否在原字符串的尾部
4)repeat()方法返回了一个新字符串,表示将原字符串重复n次
includes和indexOf十分相似,但是includes支持NaN等
④ 模板字符串
⑤ 箭头函数
箭头函数没有作用域,this指向外部作用域,箭头函数是函数的简化
⑥ Array.from方法用于将类似数组的对象或可遍历的对象转为真正的数组
⑦ 参数默认值
⑧ 引入了rest参数(形式为"...变量名")用于获取函数的多余参数,这样就不需要arguments对象了,rest参数中的变量代表是一个数组,所以数组特有的方法都可用于这个变量
⑨ Symbol类型,表示独一无二的值,它是Js的第七种数据类型
⑩ ES6提供了新的数据结构Set,它类似于数组,但是成员的值都是唯一的,没有重复的值
7 去重
① set 去重
let s1 = new Set([...arr1,...arr2])
② var arr=['12','32','89','12','12','78','12','32'];
// 最简单数组去重法
function unique1(array){
var n = []; //一个新的临时数组
for(var i = 0; i < array.length; i++){ //遍历当前数组
if (n.indexOf(array[i]) == -1)
n.push(array[i]);
}
return n;
}
arr=unique1(arr);
③ // 速度最快, 占空间最多(空间换时间)
function unique2(array){
var n = {}, r = [], type;
for (var i = 0; i < array.length; i++) {
type = typeof array[i];
if (!n[array[i]]) {
n[array[i]] = [type];
r.push(array[i]);
} else if (n[array[i]].indexOf(type) < 0) {
n[array[i]].push(type);
r.push(array[i]);
}
}
return r;
}
④ //数组下标判断法
function unique3(array){
var n = [array[0]]; //结果数组
for(var i = 1; i < array.length; i++) { //从第二项开始遍历
if (array.indexOf(array[i]) == i)
n.push(array[i]);
}
return n;
}
⑤ Array.from与set去重
Array.from方法可以将Set结构转换为数组结果,而我们知道set结果是不重复的数据集,因此能够达到去重的目的
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
return Array.from(new Set(arr))
}
⑥ set与解构赋值去重
ES6中新增了数据类型set,set的一个最大的特点就是数据不重复。Set函数可以接受一个数组(或类数组对象)作为参数来初始化,利用该特性也能做到给数组去重
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
return [...new Set(arr)]
}
⑦ 利用对象属性去重
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
let res = [],
obj = {}
for (let i = 0; i < arr.length; i++) {
if (!obj[arr[i]]) {
res.push(arr[i])
obj[arr[i]] = 1
} else {
obj[arr[i]]++
}
}
return res
}
⑧ 相邻元素去重
这种方法首先调用了数组的排序方法sort(),然后根据排序后的结果进行遍历及相邻元素比对,如果相等则跳过改元素,直到遍历结束
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
arr = arr.sort()
let res = []
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
res.push(arr[i])
}
}
return res
}
8 Map和Set的区别
① Set是类似数组的一种数据结构,不同点在于Set中没有重复的值
1)数组的并集
let arr1 = [1,2,3];
let arr2 = [3,4,5];
let s1 = new Set([...arr1,...arr2]) //这样就把重复的3去掉了
console.log([...s1]);//这就是并集的结果了复制代码
2)数组的交集
let arr1 = [1,2,3,1];
let arr2 = [3,4,5,4];
let s1 = new Set(arr1);//先去除arr1数组自身的重复项
let s2 = new Set(arr2);//去除arr2数组自身的重复项
//先简单介结一个数组的filter方法,它是es5的方法,它的参数是一个函数,如果这个函数返回的是true,
//表示把数组的这一项留下,如果是false的话,会把数组的这一项删除掉
let arr = [...s1].filter((item)=>{
return s2.has(item) //has方法看s2里有没有item这一项
})
console.log(arr)//[3]
② Map是类似Object的一种键值对集合,区别在于Map的键不仅限于是字符串,其他各种类型的值包括对象都可以成为Map的键
9 事件委托
指的是把原本需要绑定在子元素上的响应事件委托给父元素,当点击子元素的时候通过冒泡触发父元素的事件
e.target 触发事件的元素
e.currentTarget 绑定事件的元素
10 冒泡和捕获
① 冒泡:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。
② 捕获: 事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定)。
11 position的值
① absolute:绝对定位,相对于static定位以外的第一个父元素定位。
② fixed: 相对于浏览器窗口定位
③ relative:相对定位,相对去其自身位置定位
12 什么情况下要跨域
① 因为浏览器有同源策略限制,不同源会造成跨域,协议不同,域名不同,端口号不同都会造成跨域
13 跨域解决办法
① jsonp
原理:利用了标签具有可跨域的特性,由服务端返回预先定义好的javascript函数的调用,并且将服务端数据以该函数的参数的形式发送过来
步骤:1)创建一个script标签
2)script的src属性设置接口地址
3)接口参数必须带一个自定义函数名,要不然后台无法返回数据
4)通过定义函数名去接受后台返回的数据
只能解决get跨域请求
② cors跨域资源共享
原理服务器设置Access-Control-Allow-OriginHTTP响应头之后,浏览器将会允许跨域
限制:浏览器需要支持html5,可以支持post、put等方法兼容IE9以上
14 TCP和UDP协议的区别
① TCP是面向连接的协议,在收发数据之前,必须和对方建立可靠的连接,一个TCP连接必须要经过三次“对话“才能建立起来
1)主机A向主机B发出连接请求数据包:'我想给你发数据,可以吗?' 这是第一次会话
2)主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接受,协调工作)的数据包:‘可以,你什么时候发?’,这是第二次对话
3)主机A再发出一个数据包确认主机B的要求同步:‘我想在就发,你接着把’,这是第三次对话
三次对话的目的是使数据包的发送和接受tongue,进过三次对话之后主机A才向主机B正式发送信息
TCP三次握手过程
第一次握手:主机A通过向主机B发送一个含有与tongue序列号的标志位的数据段给主机B,向主机B请求建立连接,通过这个数据段,主机A告诉主机B两件事:我想要和你通信,你可以用哪个序列号作为起始数据来回应我。
第二次握手:主机B收到主机A的请求后,用一个带有确认应答(ACK)和同步序列号(SYN)标志位的数据段响应主机A,也告诉A两件事情,我已经收到你的请求了你可以传输数据了,你要用哪个序列号作为起始数据段来回应我。
第三次握手:主机A收到收到这个数据段之后,在发送一个确认应答,确认已经收到主机B的数据段“我已经收到回复,我现在要开始传输实际数据了“这样三次握手就完成了,主机A和主机B就可以传输数据了。
三次握手的特点
没有应用层的数据,SYN这个标志位只有在TCP建立连接时才会被重置为1,握手完成后SYN标志位被重围为0
TCP简历连接要进行三次握手,而断开连接要进行四次
第一次:当主机A完成数据传输后,将控制为FIN置1,提出停止TCP连接的请求
第二次:当主机B收到FIN后对其作出相应,确认这一方向上的TCP连接将关闭,将ACK置1
第三次:由B端在提出反方向的关闭请求,将FIN置1
第四次:主机A对主机B的请求进行确认,将ACK置1,双方向的关闭结束
由TCP的三次握手和四次断开可以看出,TCP使用面向连接的通信方式, 大大提高了数据通信的可靠性,使发送数据端和接收端在数据正式传输前就有了交互, 为数据正式传输打下了可靠的基础。
名词解释
1、ACK 是TCP报头的控制位之一,对数据进行确认。确认由目的端发出, 用它来告诉发送端这个序列号之前的数据段都收到了。 比如确认号为X,则表示前X-1个数据段都收到了,只有当ACK=1时,确认号才有效,当ACK=0时,确认号无效,这时会要求重传数据,保证数据的完整性。
2、SYN 同步序列号,TCP建立连接时将这个位置1。
3、FIN 发送端完成发送任务位,当TCP完成数据传输需要断开时,,提出断开连接的一方将这位置1。
UDP(User Data Protocol,用户数据报协议)
1、UDP是一个非连接的协议,传输数据之前源端和终端不建立连接, 当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。 在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、 计算机的能力和传输带宽的限制; 在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。
2、 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等, 因此一台服务机可同时向多个客户机传输相同的消息。
3、UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。
4、吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、 源端和终端主机性能的限制。
5、UDP使用尽最大努力交付,即不保证可靠交付, 因此主机不需要维持复杂的链接状态表(这里面有许多参数)。
6、UDP是面向报文的。发送方的UDP对应用程序交下来的报文, 在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界, 因此,应用程序需要选择合适的报文大小。
我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常, 其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包, 如果数据包是否到达的消息及时反馈回来,那么网络就是通的。
ping命令是用来探测主机到主机之间是否可通信,如果不能ping到某台主机,表明不能和这台主机建立连接。ping命令是使用 IP 和网络控制信息协议 (ICMP),因而没有涉及到任何传输协议(UDP/TCP) 和应用程序。它发送icmp回送请求消息给目的主机。
ICMP协议规定:目的主机必须返回ICMP回送应答消息给源主机。如果源主机在一定时间内收到应答,则认为主机可达。
区别:
1、基于连接与无连接;
2、对系统资源的要求(TCP较多,UDP少);
3、UDP程序结构较简单;
4、流模式与数据报模式 ;
5、TCP保证数据正确性,UDP可能丢包;
6、TCP保证数据顺序,UDP不保证。
16 谈一谈你对MVC和MVVM的理解
MVC是一种设计模式,全称是Model(模型)、View(视图)和Controller(控制)
Model(数据层):储存和管理数据
View(视图层): 展示UI、响应用户的交互
Controller(控制层):监听数据的改变、控制视图行为和处理用户交互
他们所有的通讯都是单向的,这三层紧密联系,又相互独立,每一层内部的变化不影响其它层。每一层对外提供接口,供上一层调用,实现 程序的模块化。
优点:① 清晰的架构以代码的复杂性为代价,小项目可能反而降低开发效率
② Controller层代码难以复用,到后面会变得臃肿
③ 运行效率相对较低,太过复杂反而不太适合中小型项目
MVVM是对MVC的增强版
将MVC中的Controller的数据和逻辑处理部分抽离出来,放在了ViewModel中,这样只需组好View与ViewModel的数据绑定即可,MVVM显著特点就是双向绑定,view的变化会自动更新到Model中
(项目复杂程度越来越高之后,MVC中的Controller会变得越来越臃肿,难以维护,所以将其中的数据和逻辑处理抽离出来的MVVM更佳)
17 webstorage和cookid的区别
① 存储空间不同
1)webstorage能提供5M的存储空(不同浏览器不同),cookie提供4k的空间
2)webstroage每个域(包括子域)都有独立的存储空间,各个存储空间是完全独立的,因此不会造成数据混淆
② 服务端交互
1)webstorage中的数据仅仅本地存储,不会和服务器发生任何交互
2)cookie的内容会随着请求一并发送到服务器(每请求一个新的页面时,cookie都会被发送过去,无形中造成带宽的浪费)
③ 接口
1)webstorag提供了许多丰富易用的接口,拥有setItem,removeItem,getItem,clear等方法,操作数据更简单
2)cookie需要自己封装setCookie,getCookie等
④ sessionStorage和localStorage区别
1)sessionStorage:仅在当前浏览器窗口关闭前有效,localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据
2)作用域不同:sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面,localStorage在所有同源窗口都是共享的
3)webStorage支持事件监听机制,可以将数据更新通知发送给监听者
说明:cookie也是必不可少的,cookie的作用是与服务器交互,作为http规范的一部分而存在,webstorage仅仅是为了本地存储护数据而生。
18 请你谈谈cookie的弊端
① cookie数量和长度的限制,每个域最多有20条cookie,每个cookie的长度不能超过4KB,否则就会被截掉
② 安全性问题,如果cookie被人拦截,就可以取得多哦呦的session信息,即使加密也没用,因为拦截者并不需要知道cookie的意义,他只要原样转发cookie就可以达到目的了。
③ 有些状态不可能保存在客户端,例如为了防止重复提交表单,我们需要在服务器端保存一个计数器,如果我们把这个计数器保存在客户端,那么它就起不到任何作用。
19 请你谈一谈性能优化
① 减少 HTTP请求
② 使用服务器端渲染
③ 使用字体图标iconfont代替图片图标
④ 善于使用缓存,不重复加载相同的资源
⑤ 压缩文件
⑥ 图片延迟加载
⑦ 减少重绘重排
20 服务器端渲染
① 客户端渲染过程:获取HTML文件,根据需要下载Javascript文件,运行文件,生成DOM,在渲染。
客户端渲染过程:
1)访问客户端渲染的网站
2)服务器返回了一个包含引入资源语句和<div id="app"></div>的HTML文件
3)客户端通过HTTP向服务器请求资源,当必要的资源都加载完毕后,执行new Vue()开始实例化并渲染页面
② 服务器渲染:服务器返回HTML文件,客户端只需要解析HTML。
服务器端渲染过程:
1)访问服务器端渲染的网页
2)服务器会查看当前路由组件需要哪些资源文件,然后将这些文件内容填充到HTML文件,如果有ajax请求,就会执行它进行数据预取并填充到HTML文件里,最后返回这个HTML页面
3)当客户端接收到这个HTML页面时,可以马上就开始渲染页面,与此同时页面也会加载资源,当必要的资源都加载完毕后,开始执行new Vue()开始实例化并接管页面
优点:首屏渲染快,SEO好
缺点:配置麻烦,增加了服务器的计算压力
区别在第二步,客户端渲染的网站会直接返回HTML文件,而服务器端渲染的页面则会渲染完页面在返回这个HTML文件。