介绍一下Connection:keep-alive
什么是keep-alive
咱们晓得HTTP协定采纳“申请-应答”模式,当应用一般模式,即非KeepAlive模式时,每个申请/应答客户和服务器都要新建一个连贯,实现 之后立刻断开连接(HTTP协定为无连贯的协定);
当应用Keep-Alive模式(又称长久连贯、连贯重用)时,Keep-Alive性能使客户端到服 务器端的连贯继续无效,当呈现对服务器的后继申请时,Keep-Alive性能防止了建设或者从新建设连贯。
为什么要应用keep-alive
keep-alive技术的创立目标,能在屡次HTTP之前重用同一个TCP连贯,从而缩小创立/敞开多个 TCP 连贯的开销(包含响应工夫、CPU 资源、缩小拥挤等),参考如下示意图
客户端如何开启
在HTTP/1.0协定中,默认是敞开的,须要在http头退出"Connection: Keep-Alive”,能力启用Keep-Alive;
Connection: keep-alive
http 1.1中默认启用Keep-Alive,如果退出"Connection: close “,才敞开。
Connection: close
目前大部分浏览器都是用http1.1协定,也就是说默认都会发动Keep-Alive的连贯申请了,所以是否能实现一个残缺的Keep- Alive连贯就看服务器设置状况。
Promise.any
形容:只有 promises
中有一个fulfilled
,就返回第一个fulfilled
的Promise
实例的返回值。
实现
Promise.any = function(promises) { return new Promise((resolve, reject) => { if(Array.isArray(promises)) { if(promises.length === 0) return reject(new AggregateError("All promises were rejected")); let count = 0; promises.forEach((item, index) => { Promise.resolve(item).then( value => resolve(value), reason => { count++; if(count === promises.length) { reject(new AggregateError("All promises were rejected")); }; } ); }) } else return reject(new TypeError("Argument is not iterable")); });}
代码输入后果
async function async1() { console.log("async1 start"); await async2(); console.log("async1 end");}async function async2() { console.log("async2");}async1();console.log('start')
输入后果如下:
async1 startasync2startasync1 end
代码的执行过程如下:
- 首先执行函数中的同步代码
async1 start
,之后遇到了await
,它会阻塞async1
前面代码的执行,因而会先去执行async2
中的同步代码async2
,而后跳出async1
; - 跳出
async1
函数后,执行同步代码start
; - 在一轮宏工作全副执行完之后,再来执行
await
前面的内容async1 end
。
这里能够了解为await前面的语句相当于放到了new Promise中,下一行及之后的语句相当于放在Promise.then中。
闭包产生的实质
以后环境中存在指向父级作用域的援用
代码输入后果
var a=3; function c(){ alert(a); } (function(){ var a=4; c(); })();
js中变量的作用域链与定义时的环境无关,与执行时无关。执行环境只会扭转this、传递的参数、全局变量等
代码输入后果
Promise.resolve('1') .then(res => { console.log(res) }) .finally(() => { console.log('finally') })Promise.resolve('2') .finally(() => { console.log('finally2') return '我是finally2返回的值' }) .then(res => { console.log('finally2前面的then函数', res) })
输入后果如下:
1finally2finallyfinally2前面的then函数 2
.finally()
个别用的很少,只有记住以下几点就能够了:
.finally()
办法不论Promise对象最初的状态如何都会执行.finally()
办法的回调函数不承受任何的参数,也就是说你在.finally()
函数中是无奈晓得Promise最终的状态是resolved
还是rejected
的- 它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出的是一个异样则返回异样的Promise对象。
- finally实质上是then办法的特例
.finally()
的谬误捕捉:
Promise.resolve('1') .finally(() => { console.log('finally1') throw new Error('我是finally中抛出的异样') }) .then(res => { console.log('finally前面的then函数', res) }) .catch(err => { console.log('捕捉谬误', err) })
输入后果为:
'finally1''捕捉谬误' Error: 我是finally中抛出的异样
参考 前端进阶面试题具体解答
Promise.resolve
Promise.resolve = function(value) { // 1.如果 value 参数是一个 Promise 对象,则一成不变返回该对象 if(value instanceof Promise) return value; // 2.如果 value 参数是一个具备 then 办法的对象,则将这个对象转为 Promise 对象,并立刻执行它的then办法 if(typeof value === "object" && 'then' in value) { return new Promise((resolve, reject) => { value.then(resolve, reject); }); } // 3.否则返回一个新的 Promise 对象,状态为 fulfilled return new Promise(resolve => resolve(value));}
JS 整数是怎么示意的?
- 通过 Number 类型来示意,遵循 IEEE754 规范,通过 64 位来示意一个数字,(1 + 11 + 52),最大平安数字是 Math.pow(2, 53) - 1,对于 16 位十进制。(符号位 + 指数位 + 小数局部无效位)
vuex
vuex是一个专为vue.js利用程序开发的状态管理器,它采纳集中式存储管理利用的所有组件的状态,并且以相应的规定保障状态以一种能够预测的形式发生变化。state: vuex应用繁多状态树,用一个对象就蕴含来全副的利用层级状态mutation: 更改vuex中state的状态的惟一办法就是提交mutationaction: action提交的是mutation,而不是间接变更状态,action能够蕴含任意异步操作getter: 相当于vue中的computed计算属性
代码输入后果
var a, b(function () { console.log(a); console.log(b); var a = (b = 3); console.log(a); console.log(b); })()console.log(a);console.log(b);
输入后果:
undefined undefined 3 3 undefined 3
这个题目和下面题目考查的知识点相似,b赋值为3,b此时是一个全局变量,而将3赋值给a,a是一个局部变量,所以最初打印的时候,a仍旧是undefined。
JS 隐式转换,显示转换
个别非根底类型进行转换时会先调用 valueOf,如果 valueOf 无奈返回根本类型值,就会调用 toString
字符串和数字
- "+" 操作符,如果有一个为字符串,那么都转化到字符串而后执行字符串拼接
- "-" 操作符,转换为数字,相减 (-a, a * 1 a/1) 都能进行隐式强制类型转换
[] + {} 和 {} + []
布尔值到数字
- 1 + true = 2
- 1 + false = 1
转换为布尔值
- for 中第二个
- while
- if
- 三元表达式
- || (逻辑或) && (逻辑与)右边的操作数
符号
- 不能被转换为数字
- 能被转换为布尔值(都是 true)
- 能够被转换成字符串 "Symbol(cool)"
宽松相等和严格相等
宽松相等容许进行强制类型转换,而严格相等不容许
字符串与数字
转换为数字而后比拟
其余类型与布尔类型
- 先把布尔类型转换为数字,而后持续进行比拟
对象与非对象
- 执行对象的 ToPrimitive(对象)而后持续进行比拟
假值列表
- undefined
- null
- false
- +0, -0, NaN
- ""
setInterval 模仿 setTimeout
形容:应用setInterval
模仿实现setTimeout
的性能。
思路:setTimeout
的个性是在指定的工夫内只执行一次,咱们只有在setInterval
外部执行 callback
之后,把定时器关掉即可。
实现:
const mySetTimeout = (fn, time) => { let timer = null; timer = setInterval(() => { // 敞开定时器,保障只执行一次fn,也就达到了setTimeout的成果了 clearInterval(timer); fn(); }, time); // 返回用于敞开定时器的办法 return () => clearInterval(timer);}// 测试const cancel = mySetTimeout(() => { console.log(1);}, 1000); // 一秒后打印 1
js脚本加载问题,async、defer问题
- 如果依赖其余脚本和 DOM 后果,应用 defer
- 如果与 DOM 和其余脚本依赖不强时,应用 async
什么是作用域?
ES5 中只存在两种作用域:全局作用域和函数作用域。在 JavaScript 中,咱们将作用域定义为一套规定,这套规定用来治理引擎如何在以后作用域以及嵌套子作用域中依据标识符名称进行变量(变量名或者函数名)查找
原型
JavaScript中的对象都有一个非凡的 prototype 内置属性,其实就是对其余对象的援用简直所有的对象在创立时 prototype 属性都会被赋予一个非空的值,咱们能够把这个属性当作一个备用的仓库当试图援用对象的属性时会登程get操作,第一步时查看对象自身是否有这个属性,如果有就应用它,没有就去原型中查找。一层层向上直到Object.prototype顶层基于原型扩大形容一下原型链,什么是原型链,原型的继承,ES5和ES6继承与不同点。
说一下slice splice split 的区别?
// slice(start,[end])// slice(start,[end])办法:该办法是对数组进行局部截取,该办法返回一个新数组// 参数start是截取的开始数组索引,end参数等于你要取的最初一个字符的地位值加上1(可选)。// 蕴含了源函数从start到 end 所指定的元素,然而不包含end元素,比方a.slice(0,3);// 如果呈现正数就把正数与长度相加后再划分。// slice中的正数的绝对值若大于数组长度就会显示所有数组// 若参数只有一个,并且参数大于length,则为空。// 如果完结地位小于起始地位,则返回空数组// 返回的个数是end-start的个数// 不会扭转原数组var arr = [1,2,3,4,5,6]/*console.log(arr.slice(3))//[4,5,6] 从下标为0的到3,截取3之后的数console.log(arr.slice(0,3))//[1,2,3] 从下标为0的中央截取到下标为3之前的数console.log(arr.slice(0,-2))//[1,2,3,4]console.log(arr.slice(-4,4))//[3,4]console.log(arr.slice(-7))//[1,2,3,4,5,6]console.log(arr.slice(-3,-3))// []console.log(arr.slice(8))//[]*/// 集体总结:slice的参数如果是负数就从左往右数,如果是正数的话就从右往左边数,// 截取的数组与数的方向统一,如果是2个参数则截取的是数的交加,没有交加则返回空数组 // ps:slice也能够切割字符串,用法和数组一样,但要留神空格也算字符// splice(start,deletecount,item)// start:起始地位// deletecount:删除位数// item:替换的item// 返回值为被删除的字符串// 如果有额定的参数,那么item会插入到被移除元素的地位上。// splice:移除,splice办法从array中移除一个或多个数组,并用新的item替换它们。//举一个简略的例子 var a=['a','b','c']; var b=a.splice(1,1,'e','f'); console.log(a) //['a', 'e', 'f', 'c'] console.log(b) //['b'] var a = [1, 2, 3, 4, 5, 6];//console.log("被删除的为:",a.splice(1, 1, 8, 9)); //被删除的为:2// console.log("a数组元素:",a); //1,8,9,3,4,5,6// console.log("被删除的为:", a.splice(0, 2)); //被删除的为:1,2// console.log("a数组元素:", a) //3,4,5,6console.log("被删除的为:", a.splice(1, 0, 2, 2)) //插入 第二个数为0,示意删除0个 console.log("a数组元素:", a) //1,2,2,2,3,4,5,6// split(字符串)// string.split(separator,limit):split办法把这个string宰割成片段来创立一个字符串数组。// 可选参数limit能够限度被宰割的片段数量。// separator参数能够是一个字符串或一个正则表达式。// 如果separator是一个空字符,会返回一个单字符的数组,不会扭转原数组。var a="0123456"; var b=a.split("",3); console.log(b);//b=["0","1","2"]// 留神:String.split() 执行的操作与 Array.join 执行的操作是相同的。
代码输入后果
console.log('1');setTimeout(function() { console.log('2'); process.nextTick(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') })})process.nextTick(function() { console.log('6');})new Promise(function(resolve) { console.log('7'); resolve();}).then(function() { console.log('8')})setTimeout(function() { console.log('9'); process.nextTick(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') })})
输入后果如下:
176824359111012
(1)第一轮事件循环流程剖析如下:
- 整体script作为第一个宏工作进入主线程,遇到
console.log
,输入1。 - 遇到
setTimeout
,其回调函数被散发到宏工作Event Queue中。暂且记为setTimeout1
。 - 遇到
process.nextTick()
,其回调函数被散发到微工作Event Queue中。记为process1
。 - 遇到
Promise
,new Promise
间接执行,输入7。then
被散发到微工作Event Queue中。记为then1
。 - 又遇到了
setTimeout
,其回调函数被散发到宏工作Event Queue中,记为setTimeout2
。
宏工作Event Queue | 微工作Event Queue |
---|---|
setTimeout1 | process1 |
setTimeout2 | then1 |
上表是第一轮事件循环宏工作完结时各Event Queue的状况,此时曾经输入了1和7。发现了process1
和then1
两个微工作:
- 执行
process1
,输入6。 - 执行
then1
,输入8。
第一轮事件循环正式完结,这一轮的后果是输入1,7,6,8。
(2)第二轮工夫循环从**setTimeout1**
宏工作开始:
- 首先输入2。接下来遇到了
process.nextTick()
,同样将其散发到微工作Event Queue中,记为process2
。 new Promise
立刻执行输入4,then
也散发到微工作Event Queue中,记为then2
。
宏工作Event Queue | 微工作Event Queue |
---|---|
setTimeout2 | process2 |
then2 |
第二轮事件循环宏工作完结,发现有process2
和then2
两个微工作能够执行:
- 输入3。
- 输入5。
第二轮事件循环完结,第二轮输入2,4,3,5。
(3)第三轮事件循环开始,此时只剩setTimeout2了,执行。
- 间接输入9。
- 将
process.nextTick()
散发到微工作Event Queue中。记为process3
。 - 间接执行
new Promise
,输入11。 - 将
then
散发到微工作Event Queue中,记为then3
。
宏工作Event Queue | 微工作Event Queue |
---|---|
process3 | |
then3 |
第三轮事件循环宏工作执行完结,执行两个微工作process3
和then3
:
- 输入10。
- 输入12。
第三轮事件循环完结,第三轮输入9,11,10,12。
整段代码,共进行了三次事件循环,残缺的输入为1,7,6,8,2,4,3,5,9,11,10,12。
代码输入后果
function Foo(){ Foo.a = function(){ console.log(1); } this.a = function(){ console.log(2) }}Foo.prototype.a = function(){ console.log(3);}Foo.a = function(){ console.log(4);}Foo.a();let obj = new Foo();obj.a();Foo.a();
输入后果:4 2 1
解析:
- Foo.a() 这个是调用 Foo 函数的静态方法 a,尽管 Foo 中有优先级更高的属性办法 a,但 Foo 此时没有被调用,所以此时输入 Foo 的静态方法 a 的后果:4
- let obj = new Foo(); 应用了 new 办法调用了函数,返回了函数实例对象,此时 Foo 函数外部的属性办法初始化,原型链建设。
- obj.a() ; 调用 obj 实例上的办法 a,该实例上目前有两个 a 办法:一个是外部属性办法,另一个是原型上的办法。当这两者都存在时,首先查找 ownProperty ,如果没有才去原型链上找,所以调用实例上的 a 输入:2
- Foo.a() ; 依据第2步可知 Foo 函数外部的属性办法已初始化,笼罩了同名的静态方法,所以输入:1
说一下HTTP和HTTPS协定的区别?
1、HTTPS协定须要CA证书,费用较高;而HTTP协定不须要2、HTTP协定是超文本传输协定,信息是明文传输的,HTTPS则是具备安全性的SSL加密传输协定;3、应用不同的连贯形式,端口也不同,HTTP协定端口是80,HTTPS协定端口是443;4、HTTP协定连贯很简略,是无状态的;HTTPS协定是具备SSL和HTTP协定构建的可进行加密传输、身份认证的网络协议,比HTTP更加平安
script标签中defer和async的区别
如果没有defer或async属性,浏览器会立刻加载并执行相应的脚本。它不会期待后续加载的文档元素,读取到就会开始加载和执行,这样就阻塞了后续文档的加载。
defer 和 async属性都是去异步加载内部的JS脚本文件,它们都不会阻塞页面的解析,其区别如下:
- 执行程序: 多个带async属性的标签,不能保障加载的程序;多个带defer属性的标签,依照加载程序执行;
- 脚本是否并行执行:async属性,示意后续文档的加载和执行与js脚本的加载和执行是并行进行的,即异步执行;defer属性,加载后续文档的过程和js脚本的加载(此时仅加载不执行)是并行进行的(异步),js脚本须要等到文档所有元素解析实现之后才执行,DOMContentLoaded事件触发执行之前。