1. http协议POST和GET的区别
1.GET在浏览器回退时是无害的,而POST会再次提交请求
2.GET产生的URL地址可以被收藏,而POST则不可以
3.GET请求会被浏览器主动缓存, 而POST不会,除非手动设置
4.GET请求只能进行url编码, 而POST支持多种编码方式
5.GET请求参数会被完整保留在浏览器历史记录里, 而POST中的参数不会保留
6.GET请求在URL中传送的参数是有长度限制的, 而POST没有限制
7.对参数的数据类型, GET只接受ASCII字符, 而POST没有限制
8. GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息
9. GET参数通过URL传递,POST放在Request body中
2. 从输入url到显示页面,都经历了什么
1、首先,在浏览器地址栏中输入url。
2、浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。
3、在发送http请求前,需要域名解析(DNS解析)(DNS(域名系统,Domain Name System)是互联网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网,而不用去记住IP地址。),解析获取相应的IP地址。
4、浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手。(TCP即传输控制协议。TCP连接是互联网连接协议集的一种。)
5、握手成功后,浏览器向服务器发送http请求,请求数据包。
6、服务器处理收到的请求,将数据返回至浏览器。
7、浏览器收到HTTP响应。
8、读取页面内容,浏览器渲染,解析html源码。
9、生成Dom树、解析css样式、js交互。
10、客户端和服务器交互。
11、ajax查询
3. 写一个方法从数组中随机抽取N个不重复的元素
const nCArr = (arr, n) => {
let newArr = [];
while (newArr.length < n) {
let num = Math.floor(Math.random() * (arr.length))
if (newArr.indexOf(arr[num]) === -1) {
newArr.push(arr[num])
}
}
console.log(newArr);
return newArr
}
nCArr([1, 2, 3, 4, 5, 6, 7, 89, 1, 2, 4, 5], 3);
4. 计算students中大于等于10的grade总分数。使用map,filter和reduce方法
const students = [{
name: "Nick",
grade: 10
},
{
name: "John",
grade: 15
},
{
name: "Julia",
grade: 19
},
{
name: "Nathalie",
grade: 9
},
];
let res = students.filter(item => item.grade >= 10).map(item => item.grade).reduce((prev, next) => prev + next,
0);
console.log(res, 'res'); // 44 "res"
5. (携程)算法手写题
已知如下数组:
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组。
var arr = [
[1, 2, 2],
[3, 4, 5, 5],
[6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10, 12345
];
// 方法一
console.log(Array.from(new Set(arr.flat(Infinity))).sort((a, b) => a - b))
//方法二
console.log(Array.from(new Set(arr.toString().split(','))).map(Number).sort((a, b) => a - b))
// 方法三
// 第一步:扁平化
let newArr = [];
function flat(originArr) {
if ({}.toString.call(originArr) === '[object Array]') {
for (let i of originArr) {
if ({}.toString.call(i) === '[object Array]') {
flat(i)
} else {
newArr.push(i)
}
}
}
return newArr;
}
console.log(flat(arr))
// 第二步:去重
var newArr1 = [];
for (let i of newArr) {
if (!newArr1.includes(i)) newArr1.push(i);
}
// 第三步:排序 可以采用相关算法处理
console.log(newArr1.sort((a, b) => a - b))
6. 请把俩个数组 [A1, A2, B1, B2, C1, C2, D1, D2] 和 [A, B, C, D],合并为 [A1, A2, A, B1, B2, B, C1, C2, C, D1, D2, D]
// 方法1
const arr1 = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2']
const arr2 = ['A', 'B', 'C', 'D']
const ret = []
let tmp = arr2[0]
let j = 0
for (let i=0;i<arr1.length;i++) {
if (tmp === arr1[i].charAt(0)){
ret.push(arr1[i])
}else {
ret.push(tmp)
ret.push(arr1[i])
tmp=arr2[++j]
}
if(i===arr1.length-1){
ret.push(tmp)
}
}
console.log(ret)
// 方法2
const a1 = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2']
let a2 = ['A', 'B', 'C', 'D'].map((item) => {
return item + 3
})
let a3 = [...a1, ...a2].sort().map((item) => {
if(item.includes('3')){
return item.split('')[0]
}
return item
})
console.log(a3)
// 放法3
var a1 = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'];
var a2 = ['A', 'B', 'C', 'D'];
var j = 0;
var arr = []
for (let i = 0; i < a1.length; i++) {
if (i % 2 === 0) {
arr = arr.concat((a1.slice(i, i + 2)).concat(a2[j]))
j++
}
}
console.log(arr)
7. 下面代码中 a 在什么情况下会打印 1?
// 方法1
let a = [1, 2, 3];
a.toString = a.shift;
if (a == 1 && a == 2 && a == 3) {
console.log(1);
}
// 方法2
// 因为==会进行隐式类型转换 所以我们重写toString方法就可以了
var a = {
i: 1,
toString() {
return a.i++;
}
}
if (a == 1 && a == 2 && a == 3) {
console.log(1);
}
8. call, apply和bind的区别
三者相似之处:
- 都是用来改变函数的this对象的指向的
- 第一个参数都是this要指向的对象
- 都可以利用后续参数传参 不同之处 call传递的是字符串, apply传递的是数组。 且call和apply是立即执行的。 bind不会被立即调用, 传递的也是字符串, 可以在bind的同时传参, 也可以在函数调用的时候当做参数传递。
call使用: Array.prototype.call(null) , Array.prototype.call(null, 'param1', 'param2');
apply使用: Array.prototype.apply(null), Array.prototype.apply(null, ['param1', 'param2']);
bind的使用: fn.bind(null, 'param1', 'param2')(), fn.bind(null)('param1', 'param2')
应用:
- 将伪数组转化为数组(含有length属性的对象,dom节点, 函数的参数arguments)
js中的伪数组(例如通过document.getElementsByTagName获取的元素、含有length属性的对象)具有length属性,并且可以通过0、1、2…下标来访问其中的元素,但是没有Array中的push、pop等方法。就可以利用call,apply来转化成真正的数组,就可以使用数组的方法了
case1: dom节点:
<div class="div1">1</div>
<div class="div1">2</div>
<div class="div1">3</div>
let div = document.getElementsByTagName('div');
console.log(div); // HTMLCollection(3) [div.div1, div.div1, div.div1] 里面包含length属性
let arr2 = Array.prototype.slice.call(div);
console.log(arr2); // 数组 [div.div1, div.div1, div.div1]
9. 函数科里化
10. new到底做了些什么
要创建一个对象的新实例, 必须使用new操作符。 以这种方式调用构造函数实际上会经历以下4个步骤
- 实例化一个新对象
- 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
11. 为什么在vue的组件中, data要用function返回对象呢?
防止组件重用的时候导致数据相互影响。
官方解释:
当一个组件被定义, data必须声明为返回一个初始数据对象的函数, 因为组件可能被用来创建多个实例。 如果data仍然是一个纯粹的对象, 则所有的实例将共享引用同一个数据对象。 通过提供data函数, 每次创建一个新实例后, 我们能够调用data函数, 从而返回初始数据的一个全新副本数据对象。
12. 介绍一下css盒子模型
① 盒模型的组成: 由里向外content,padding,border,margin
② 盒模型有两种类型: 一个是标准模型,一个是IE模型
③ 两种盒模型的主要区别是: 标准盒模型的宽高是指内容宽高(content), 而IE盒模型的宽高是指content + padding + border
④ 设置盒模型的方式是: 设置box-sizing
box-sizing: content-box (默认) 标准盒模型
box-sizing: border-box IE盒模型
13. 如何实现自己的v-model
14. this.$nextTick的原理是什么
15. 如何实现跨域的请求
16. webpack的 插件和loader的区别是什么
17. vue3和vue2的区别, vue3有哪些新特性
18. http2和http的区别, 介绍下http
19. computed 和 watch的区别
20. 说下vue的原理, diff算法, 虚拟dom树的原理
21. diff算法原理, 解释一下什么叫虚拟dom
22. bfc是什么
(block formatting context) 块级格式上下文。 是Web页面的可视化CSS渲染的一部分,是布局过程中生成块级盒子的区域,也是浮动元素与其他元素的交互限定区域
-
通俗理解
- BFC是一个独立的布局环境,可以理解为一个容器,在这个容器中按照一定规则进行物品摆放,并且不会影响其它环境中的物品。
- 如果一个元素符合触发BFC的条件,则BFC中的元素布局不受外部影响。
- 浮动元素会创建BFC,则浮动元素内部子元素主要受该浮动元素影响,所以两个浮动元素之间是互不影响的。
-
触发bfc的条件
- float: left/right。 也就是float不为None
- position: absolute/fixed。 也就是position不为static或者relative中的任何一个
- overflow: auto/scroll/hidden。 也就是overflow不为visible
- 根元素
- display: inline-block/table-caption/table-cell
-
目前利用BFC特性最多的地方: margin-top的合并问题, 清除浮动问题。 当出现bfc的时候这些问题就自然而然的解决了
-
规则
- 浮动的元素会被父级计算高度(父级触发了BFC)
- 非浮动元素不会覆盖浮动元素位置(非浮动元素触发了BFC)
- margin不会传递给父级(父级触发了BFC), 两个相邻元素上下margin回重叠(给其中一个元素增加父级, 然后让他的父级触发BFC)
23. 说一下常用算法和时间复杂度
24. 都用webpack做过哪些优化
25. 用什么方法能提升页面性能
26. 回流和重绘的区别
27. 如何判断一个数据的类型, 都有哪些数据。
28. 解释一下proxy
29. await报错, 怎么处理
当一个异步函数正在等待Promise返回值的时候,当Promise方法报了错误的时候,它会抛出异常,这个异常可以在catch方法里面捕获到。所以可以使用try/catch进行异常捕获
但是这样每个await 都需要进行try/catch处理, 就非常不优雅。 可以制作一个小的通用函数来捕获这些错误。
// 抽离成公共方法
const awaitWrap = (promise) => {
return promise .then(data => [null, data]).catch(err => [err, null])
}
const [err, data] = await awaitWrap(fetchData()) console.log('err', err) console.log('data', data) // err null // data fetch data is me
30. ajax和promise的区别
31. 如何实现arr[-1]的访问
const proxyArray = arr => {
const length = arr.length;
return new Proxy(arr, {
get(target, key) {
key = +key;
while (key < 0) {
key += length;
}
return target[key];
}
})
};
var a = proxyArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
console.log(a[1]); // 2
console.log(a[-10]); // 9
console.log(a[-20]); // 8
console.log(a['-20']); // 8
32. 实现一个sleep函数
function sleep(time = 1000){
return new Promise((resolve)=>{
setTimeout(resolve, time);
})
}
33. 实现 (5).add(3).minus(2)输出4, 也就是实现 5+2-3
简化版, 在Number的原型上添加方法。
// Number.prototype.add = function (number) {
// if (typeof number !== 'number') {
// throw new Error('请输入数字~');
// }
// return this.valueOf() + number;
// };
// Number.prototype.minus = function (number) {
// if (typeof number !== 'number') {
// throw new Error('请输入数字~');
// }
// return this.valueOf() - number;
// };
// console.log((5).add(3).minus(2));
function addmin(){
function add(n){
return this+n
}
function minus(n){
return this-n
}
Number.prototype.add = add
Number.prototype.minus = minus
}
addmin()
console.log((5).add(3).minus(2))
加减操作精度问题是躲不过去的一个bug, 所以这块要重点处理一下。 还有安全数的问题
Number.MAX_SAFE_DIGITS = Number.MAX_SAFE_INTEGER.toString().length-2
Number.prototype.digits = function(){
let result = (this.valueOf().toString().split('.')[1] || '').length
return result > Number.MAX_SAFE_DIGITS ? Number.MAX_SAFE_DIGITS : result
}
Number.prototype.add = function(i=0){
if (typeof i !== 'number') {
throw new Error('请输入正确的数字');
}
const v = this.valueOf();
const thisDigits = this.digits();
const iDigits = i.digits();
const baseNum = Math.pow(10, Math.max(thisDigits, iDigits));
const result = (v * baseNum + i * baseNum) / baseNum;
if(result>0){ return result > Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : result }
else{ return result < Number.MIN_SAFE_INTEGER ? Number.MIN_SAFE_INTEGER : result }
}
Number.prototype.minus = function(i=0){
if (typeof i !== 'number') {
throw new Error('请输入正确的数字');
}
const v = this.valueOf();
const thisDigits = this.digits();
const iDigits = i.digits();
const baseNum = Math.pow(10, Math.max(thisDigits, iDigits));
const result = (v * baseNum - i * baseNum) / baseNum;
if(result>0){ return result > Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : result }
else{ return result < Number.MIN_SAFE_INTEGER ? Number.MIN_SAFE_INTEGER : result }
}
Number.prototype.add = function(n){
return this + n
}
Number.prototype.minus = function(n){
return this - n
}