interview

349 阅读8分钟

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的区别

三者相似之处:

  1. 都是用来改变函数的this对象的指向的
  2. 第一个参数都是this要指向的对象
  3. 都可以利用后续参数传参 不同之处 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个步骤

  1. 实例化一个新对象
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
  3. 执行构造函数中的代码(为这个新对象添加属性)
  4. 返回新对象

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的条件

    1. float: left/right。 也就是float不为None
    2. position: absolute/fixed。 也就是position不为static或者relative中的任何一个
    3. overflow: auto/scroll/hidden。 也就是overflow不为visible
    4. 根元素
    5. display: inline-block/table-caption/table-cell
  • 目前利用BFC特性最多的地方: margin-top的合并问题, 清除浮动问题。 当出现bfc的时候这些问题就自然而然的解决了

  • 规则

    1. 浮动的元素会被父级计算高度(父级触发了BFC)
    2. 非浮动元素不会覆盖浮动元素位置(非浮动元素触发了BFC)
    3. 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
}

34. 强缓存和协商缓存的区别

www.jianshu.com/p/9c95db596…

35. 说一下 loader和plugins的区别

36. 打包的步骤