一: 什么是防抖和节流?有什么区别?
防抖: 触发高频事件n秒后函数只会执行一次,但在n秒内高频事件如果再次触发,则重新计时(一般在input获取输入值之类的会使用到)
节流:高频事件触发在n秒内,无论触发多少次,都只执行一次(一般用在用户获取验证码,滚动请求数据之类会使用到, 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每n秒发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。)
二:JS 异步解决方案的发展历程以及优缺点?
- 回调函数(callback)
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)
缺点:回调地狱,不能用 try catch 捕获错误,不能 return
setTimeout(() => {
// callback 函数体
}, 1000)
2.Promise
优点:解决了回调地狱的问题
缺点:无法取消 Promise ,错误需要通过回调函数来捕获
Generator
优点:可以控制函数的执行Async/await
优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。
三:http2 的多路复用?
1.HTTP2采用二进制格式传输,取代了HTTP1.x的文本格式,二进制格式解析更高效。同域名下所有通信都在单个连接上完成,消除了因多个 TCP 连接而带来的延时和内存消耗。
2.在 HTTP/2 中,有两个非常重要的概念,分别是帧(frame)和流(stream)。
帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。
多路复用,就是在一个 TCP 连接中可以存在多条流。换句话说,也就是可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。
四: TCP 三次握手和四次挥手的理解?
TCP三次握手:
1、客户端发送syn(在连接建立时用来同步序号)包到服务器,等待服务器确认接收。
2、服务器确认接收syn包并确认客户的syn,并发送回来一个syn+ack(TCP协议规定,只有ACK=1时有效)的包给客户端。
3、客户端确认接收服务器的syn+ack包,并向服务器发送确认包ack,二者相互建立联系后,完成tcp三次握手。
三次握手之所以是三次是保证client(客户端)和server(服务器)均让对方知道自己的接收和发送能力没问题而保证的最小次数。
四次挥手:
TCP连接是双向传输的对等的模式,就是说双方都可以同时向对方发送或接收数据。当有一方要关闭连接时,会发送指令告知对方,我要关闭连接了。这时对方会回一个ACK,此时一个方向的连接关闭。但是另一个方向仍然可以继续传输数据,等到发送完了所有的数据后,会发送一个FIN段来关闭此方向上的连接。接收方发送ACK确认关闭连接。。所以关闭双通道的时候就是这样:
客户端:我要关闭输入通道了。
服务端:稍等,还有一些数据给你
服务端:好了,我也要关闭输入通道了。
客户端:好的你关闭吧,我也把这个通道关闭。
五: HTTP和 HTTPS有什么区别?
1.HTTP(超文本传输协议):
是一个应用层协议,由请求和响应构成,基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等),HTTP默认的端口号为80,明文传输,连接简单,但是不安全,容易造成信息泄露
2.HTTPS(超文本传输安全协议):
HTTPS经由HTTP进行通信,但利用SSL/TLS来加密数据包。HTTPS开发的主要目的,是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。默认端口号为443,需要到CA申请证书,不免费的
详情:https://juejin.cn/post/6844903599303032845
六:浏览器输入URL发生了什么?
1.当用户按下回车键,浏览器开始检查URL是否符合规范,然后组装协议,构成完整的URL
2.浏览器进程通过进程间通信(IPC)把url请求发送给网络进程,检查是否有本地缓存该请求资源,如果有,则返回给浏览器进程,
3.没有则向web服务器发送HTTP请求,过程:
-进行DNS解析,获取服务器IP地址和端口号;
-通过IP地址和服务器建立TCP连接;
-构建请求头,发送请求头;
-服务器响应后,网络进程接收响应头和响应信息,并解析响应内容;
4.网络进程解析响应流程:
-检查状态码,-200状态码:检查响应类型Content-Type,如果是字节流类型,则将该请求提交给下载管理器,该导航流程结束,不再进行,后续的渲染,如果是html则通知浏览器进程准备渲染进程准备进行渲染。
常见状态码
6.渲染进程准备好后,接收完整数据,向浏览器发送“确认提交”,浏览器进程接收到确认消息后更新浏览器界面状态:安全、地址栏url、前进后退的历史状态、更新web页面。
详情:https://time.geekbang.org/column/article/117637
七:GET与POST的区别?
1.GET:传参方式是通过URL地址栏传参,请求数据放在URL?后面,通过&符号进行参数分割,传递的参数是可见的,对传递数据大小有限制,最大2048字符.回退不会影响,请求可以被缓存,请求记录会被保存在历史记录中.
2.POST:传参方式URL不可见,数据存放在HTTP包体内,数据大小是没有限制的,回退会重新提交,数据不能被缓存,请求也不会有历史记录.
八:浏览器请求跨域?
https://blog.csdn.net/ashleyjun/article/details/98849400
url构成: 协议://服务 域名( IP:端口)/URI?key1=value1&key2=value2#xxxx;
协议:http、https、ftp…
服务:万维网(World Wide Web )
域名 = 标识串(baidu 、google、sina…)+网站类型(com、gov、edu…)
端口号:以数字形式表示,若协议为HTTP,默认端口号为80,可省略不写,https为443;
查询:以“?”为起点,每个参数以“&”隔开,以“=”分开参数名称与数据
片段:以“#”字符开头
跨域: 就是相对同源策略而言,域名,端口号,协议都相同就是同源,否则就是跨域;
解决一: JSONP, 让网页从跨域地址那获取资料,及跨越数据;
原理: 一个HTML中可以有多个script标签,数据间可以互相访问,src不仅能导入本地资源,也能导入远程资源,src没有同源限制,所以可以跨域请求数据;
解决二: 服务器代理,浏览器有跨域限制,但是服务器不存在跨域问题,所以可以由服务器请求所要域的资源再返回给客户端。
https://zhuanlan.zhihu.com/p/81809258
九:js单线程运行的宏任务,微任务,eventLoop?
eventLoop:是解决JS单线程运行阻塞的一种机制,在JS的异步运行机制中,我们需要知道:
1.所有的「同步任务」都在主线程进行,
2.「异步任务」进入任务队列,任务队列会通知主线程,哪个异步任务可以执行,这个异步任务就会进入主线程。异步任务必须指定回调函数,当主线程开始执行异步任务,其实就是在执行对应的回调函数。
3.如果主线程的所有同步任务都执行完,系统就会去读取「任务队列」上的异步任务,如果有可以执行的,就会结束等待状态,进入主线程,开始执行。
4.主线程不断的执行第3步
这就是JS的运行机制,也称为「Event Loop」事件循环。
1.宏任务(macrotask):创建主文档对象,解析HTML,执行主线或全局js代码,及更改url各种事件,属于宏任务的事件有:script(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI rendering.
2.微任务(microtasks):更新程序状态,但是必须在浏览器任务继续执行其他任务前执行的任务,简言之就是代码自上而下执行,在开始新一轮宏任务前,必须执行完微任务才能开始新一轮的宏任务,微任务事件:Promise.then,catch,finally,object.observe等.
3.Promise是立即执行函数,是个同步函数也就是在宏任务里执行,Promise后面的.then是微任务(.then里面有Promise也是立即执行,比此轮宏任务的setTimeout函数还先执行的那种立即执行,因为代码执行到setTimeout时只是将事件放入任务列表,得下轮任务开始才执行; .then后面跟着的.then是放进微任务栈中,立即执行,因为本质返回的是个Promise)
console.log('1')
setTimeout(() => {
console.log('2')
}, 0)
Promise.resolve().then(() => {
console.log('3')
}).then(() => {
console.log('4')
})
console.log('5')
//运行结果
//1
//5
//3
//4
//2
过程分析:
1、任务队列中只有script,则将script中所有函数放进函数执行栈按顺序执行
2、接下来遇到setTimeout,这里的setTimeout只是将回调函数在0毫秒后放入任务队列(其实是4ms,html5标准中规定中要求setTimeout中低于4ms的时间间隔算为4ms),也就是说回调函数在下一个事件循环执行,setTimeout在这里将回调函数放至任务列表后就结束了(ps:下一轮宏任务开始才执行)。
3、遇到Promise,.then属于「microtasks」,所以将第一个then的回调放到microtasks队列 4、执行完所有script代码后,检查microtasks队列,发现队列不为空,所以执行第一个回调函数输出'3',由于then的返回仍然是Promise,因此将第二个then的回调放至microtasks队列并执行输出'4'。
5、此时microtasks队列为空,开始执行下一个事件循环。检查task队列发现setTimeout的回调函数,因此执行输出'setTimeout'
十: js深拷贝与浅拷贝?
基本数据类型: String,Number,Null,Undefined,Symbol,Boolean;
1.它们保存在栈内存内,因为栈比堆速度快,基本数据类型比较稳定相对占用内存小;
2.可以使用typeof可以检测基本数据类型是什么,但null返回的是Object,因此null表示的是空对象的指针,undefined是null的衍生,所以 undefined==null // true;
引用数据类型:Object(包括Array,Date,RegExp,Function);
1.引用类型是动态的,保存在堆内存中(只是在栈内存了一个对象的引用或者说是指针),因为堆比栈更大,放栈内存中会降低查找速度;
2.堆内存是无序的,可根据引用(指针)直接获取;
3.引用类型检查数据类型用 instanceof ,使用typeof都是返回object
深浅拷贝只针对引用类型(ps:其实基本数据类型改变值算深拷贝,因为改变不会影响原来的值)
- 浅拷贝: 只复制指向对象的指针,不复制本身,新旧对象共享同一内存,所以修改新对象会改变旧对象的值;
2.深拷贝:创建一个一模一样的对象,不共享内存,所以修改新对象不会改变旧对象的值;
js自带深拷贝的方法:
array: slice()、concat、Array.from()、... 操作符:只能实现一维数组的深拷贝;
Object: Object.assign():只能实现一维对象的深拷贝;JSON.parse(JSON.stringify(obj)):可实现多维对象的深拷贝,但会忽略undefined、任意的函数、symbol 值;
FUnction: bind(),eval, new Function(ary1,ary2...,function-body) // 需将参数与函数体提取出来;
(JS 提供的自有方法并不能彻底解决Array、Object的深拷贝,只能自己实现)
十一: 原型链?
(--proto--中划线其实为两条下划线,markDown无法转义只能这样写)
原型: 构造函数的 prototype 指向原型对象(只有函数有prototype,对象没有),原型对象有一个 constructor 属性指回构造函数,每个构造函数生成的实例对象都有一个 --proto-- 属性,这个属性指向创建那个构造函数的原型对象。isprototypeof()方法可以确定对象间是否有关系.
原型链: 简单理解就是原型组成的链,当需读取某个对象的属性时,首先会从对象实例本身开始,如果实例中找到该属性则返回值,没有则继续通过--proto--向上搜索指向的构造函数,没有则通过prototype搜索指向的原型对象,层层递进(原型对象的proto属性指向null); 假如我们让原型对象(A)等于另一个类型的实例呢?此时原型对象将包含另一个原型(B)的指针,相应的B中也包含着指向自己构造函数的指针(constructor),可以向下访问,这就构成了实例与原型的链条.
function pig(name) {this.name=name} //pig的构造函数
let sonPig=new pig('lucy') //pig的实例
pig.prototype===sonPig.__proto__ // true
pig.__proto__===Function.prototype// true
pig.prototype.constructor===pig // true
pig.prototype.isPrototypeOf(sonPig) //true
https://blog.csdn.net/u012468376/article/details/53121081;
https://zhuanlan.zhihu.com/p/62903507
十二: new 一个对象的过程?
1.创建一个空对象;
2.让构造函数的this指向新对象;
3.设置新对象的--proto--属性指向构造函数的原型对象;
4.判断返回数据类型,如果是值类型,返回新对象,是引用类型则返回这个引用类型的对象.
十三: 组件,模块化编程?
组件: 将js,HTML,css包装在一起,提供方法和效果;
模块化: 将相同功能抽取出来,存放在一个位置进行编程;
十四: let 与const?
- 不存在变量提升,须在声明后使用,否则会报错;
- 存在暂时性死区,区块作用域内存在let,const命令,这个区块对这些命令声明的变量是形成封闭作用域,凡在声明前使用就会报错,无论作用域外是否有同名变量;
- 不允许相同作用域内重复声明一个变量,会报错;
//{ }->这叫块级作用域,ES6允许块级作用域任意嵌套;
{{
let a="hello"
console.log(a) //hello
}}
{
{let a="hello"}
console.log(a) //Uncaught ReferenceError: a is not defined (因为没有变量提升,外层作用域无法读取内层变量)
}
十五: 下面的代码打印什么内容,为什么?
let b = 10;
(function b(){
b = 20;
console.log(b);
})();
打印结果内容如下:
ƒ b() {
b = 20;
console.log(b)
}
原因:
作用域:执行上下文中包含作用域链:
在理解作用域链之前,先介绍一下作用域,作用域可以理解为执行上下文中申明的变量和作用的范围;包括块级作用域/函数作用域;
特性:声明提前:一个声明在函数体内都是可见的,函数声明优先于变量声明;
在非匿名自执行函数中,函数变量为只读状态无法修改;js作用域中先从最近的开始向上寻找,b=20没有被声明,所以在非严格模式下打印出函数体.
十六: 实现一个sleep函数,从Promis,Async/Await 等角度实现?
//Promise:
function sleep(time) {
return new Promise((resolve => setTimeout(resolve, time)))
}
sleep(1000).then(() => {
console.log("睡了一觉啦~")
})
//Async/Await :
function sleep(time) {
return new Promise(resolve => setTimeout(resolve, time))
}
async function res() {
await sleep(1000)
console.log("我睡了一觉啦~")
}
res()
十七: Cookie、SessionStorage、LocalStorage区别?
Cookie: 主要用来判断用户是否登录,默认是关闭浏览器后失效, 但是也可以设置过期时间,每次请求都会携带在HTTP请求头中,如果使用cookie保存过多数据会带来性能问题;
localStorage: 主要用于本地储存,长期存储数据,比如:购物车,浏览器关闭后数据不丢失;
sessionStorage: 主要用于会话存储,数据在浏览器关闭后自动删除。
Cookie容量限制: 大小(4KB左右)和个数(20~50);
SessionStorage和LocalStorage容量限制: 大小(5M左右)
十八: js 中强制转型?
在js中两个参数的内置类型间的转换被称为强制转型;
显示强制转换: parseInt,parseFloat,Number;
隐式强制转换: == ===
//显式强制转换:
let a = "123"
let b = Number(a)
console.log(a, b, typeof a, typeof b) // 123 123 string number
//隐式强制转换:
let a="123"
let b=a*1 //这样a的值"123"会被隐式转换成123
console.log(a, b, typeof a, typeof b) //123 123 string number
十九: undefined 和 not defined的区别?
let a; //声明了一个变量a,但未被赋值
console.log(a) //undefined
console.log(c) // 试图使用一个不存在且尚未声明的变量,js抛出错误“c is not defined”
二十: undefined 和 null 的区别?
undefiend: 已经声明,尚未被初始化;
null: 空值,表示 "什么都没有",是一个只有一个值的特殊类型,表示一个空对象引用;
null 和 undefined 的值相等,但类型不等:
typeof undefined // undefined (typeof 一个没有值的变量会返回 undefined)
typeof null // object
null === undefined // false
null == undefined // true
二十一: Vue 的SPA单页面的优缺点?
优点: 用户体验好,快,切换页面不用重新加载整个页面,避免了不必要的跳转和渲染;相对服务器压力较小,前后端分工明确,架构清晰,前端负责逻辑交互,后端负责数据处理;
缺点:
- 初次加载耗时多: 因为需要将html,js,css统一进行加载,只有部分页面按需加载;
- 前进后退路由需自己建立堆栈管理,因为不能使用浏览器自带的前进后退功能;
- SEO难度较大: 因为所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
如何解决:
1.首屏加载时间过长: 加载过多的问题可以通过webpack优化 ,尽可能减少加载项,或者预渲染 prerendering(适用于静态站点),但终极方案还是ssr(适合动态渲染,但配置繁琐,Nuxt可以帮助简化配置),
路由用vue-router进行管理
如何SEO(搜索引擎优化): 1. 内容建设:是不是当前广大用户需求的内容?2. 竞争对手:对手都是怎么在做的?我们如何差异化3. 协调资源:我们该协调那些资源来促成这次seo修改?4. 站内优化:把你的seo细节做到极致5. 站外优化:网站上线,如何推广、链接建设促进收录和排名6. 迭代优化:数据分析促进页面体验的不断完善修改
https://www.zhihu.com/question/19760381/answer/21614135
二十二: 加法操作?
let a = true
console.log(true==1,false==0) // true true
console.log(a + true) // 2
console.log(a + false) // 1
console.log(a + 1) // 2
console.log(a + "bcd") // truebcd
所以不同类型执行加法操作是这样的:
Number + Number 执行加法; (例: 2 +2 = 4)
Boolean + Number 执行加法; (例: true + 2 = 3)
Boolean + Boolean 执行加法; (例: true + true = 2)
Number + String 执行连接; (例: 2 + "abc" = "2abc" ; 2 + "2" = "22")
String + Boolean 执行连接; (例:"2" + true = "2true")
String + String 执行连接; (例: "2" + "2" = "22")