乐趣区

关于javascript:前端面试比较好的回答

介绍一下 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,就返回第一个fulfilledPromise实例的返回值。

实现

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 start
async2
start
async1 end

代码的执行过程如下:

  1. 首先执行函数中的同步代码 async1 start,之后遇到了await,它会阻塞async1 前面代码的执行,因而会先去执行 async2 中的同步代码async2,而后跳出async1
  2. 跳出 async1 函数后,执行同步代码start
  3. 在一轮宏工作全副执行完之后,再来执行 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)
  })

输入后果如下:

1
finally2
finally
finally2 前面的 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 的状态的惟一办法就是提交 mutation

action: 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,6
console.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')
    })
})

输入后果如下:

1
7
6
8
2
4
3
5
9
11
10
12

(1)第一轮事件循环流程剖析如下:

  • 整体 script 作为第一个宏工作进入主线程,遇到console.log,输入 1。
  • 遇到setTimeout,其回调函数被散发到宏工作 Event Queue 中。暂且记为setTimeout1
  • 遇到process.nextTick(),其回调函数被散发到微工作 Event Queue 中。记为process1
  • 遇到 Promisenew Promise 间接执行,输入 7。then被散发到微工作 Event Queue 中。记为then1
  • 又遇到了setTimeout,其回调函数被散发到宏工作 Event Queue 中,记为setTimeout2
宏工作 Event Queue 微工作 Event Queue
setTimeout1 process1
setTimeout2 then1

上表是第一轮事件循环宏工作完结时各 Event Queue 的状况,此时曾经输入了 1 和 7。发现了 process1then1两个微工作:

  • 执行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

第二轮事件循环宏工作完结,发现有 process2then2两个微工作能够执行:

  • 输入 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

第三轮事件循环宏工作执行完结,执行两个微工作 process3then3

  • 输入 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

解析:

  1. Foo.a() 这个是调用 Foo 函数的静态方法 a,尽管 Foo 中有优先级更高的属性办法 a,但 Foo 此时没有被调用,所以此时输入 Foo 的静态方法 a 的后果:4
  2. let obj = new Foo(); 应用了 new 办法调用了函数,返回了函数实例对象,此时 Foo 函数外部的属性办法初始化,原型链建设。
  3. obj.a() ; 调用 obj 实例上的办法 a,该实例上目前有两个 a 办法:一个是外部属性办法,另一个是原型上的办法。当这两者都存在时,首先查找 ownProperty,如果没有才去原型链上找,所以调用实例上的 a 输入:2
  4. 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 事件触发执行之前。
退出移动版