年前滴滴面试总结

650 阅读10分钟

时间匆匆,不知不觉大三就过了一半了,至今还没有面试过的小萌新就有想法了,趁着临近过年,在年前面一次试试水,为明年开学的春招做准备,所以做了一份简历,找了学长学姐聊了聊,试着内推了一下。整场面试(1小时)下来,暴露了自己的一些问题,文末有个人总结。

快乐面具.jpg

面试开始

面试是视频面,以下为个人知识总结,欢迎各位大佬指正。

经典自我介绍

我叫xxx,来自xxx,目前就读xxx大学,是一名大三学生。我将从以下三点来介绍自己:

  1. 校园经历:xxx...
  2. 项目经历:xxx...
  3. 学习习惯:xxx...

一、 你说有自己的理解你会输出一些文章,那你讲一讲你输出了那些文章

这问题一出,我愣了2-3秒,想了一下很快就概况讲了一下我少的可怜的三篇文章。主要是对文章的出现的场景和内容讲解。

二、你说axios中使用了Promise链式调用,那你讲一下Promise吧

一听Promise,呦呵,这不是面试常问吗?

  • promise有三种状态:pending表示初始状态,fulfiled表示成功状态,它对应的方法是resolve,当你在Promise回调中使用了resolve()则会发生状态改变,变为成功状态,rejected表示失败状态,对应的是reject(),使用后状态变为失败。状态一旦改变就不会再变。
  • Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,可以将异步操作以同步操作的流程表达出来,避免出现回调地狱——层层嵌套的回调函数。
  • Promise一旦new了就会立即执行,而且无法取消Promise,如果不设置回调函数的话,Promise内部出现的错误不会反映到外面。

三、Promise有哪些方法?

比较常见的有: resolve, reject,all, race, any

  • resolve: 将传入的参数转为状态为fulfiledPromise对象,参数会立即执行。如果参数本身就是一个Promise,那么就会返回该Promise本身。方法的参数,会原封不动地作为fulfiled的理由,变成后续方法的参数。resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。
  • reject:和resolve类似,状态为reject
  • all: 参数为一个Promise数组,数组每个元素都是Promise实例。如果不是Promise,会调用Promise.resolve将其转化为Promise。只有数组中所有的Promise状态都为fulfiledPromise的状态才会变为fulfiled,而且会将所有元素的返回值组成一个数组作为参数传递给调用allPromise的回调函数。若数组中有一个状态变为rejectd,会将第一个rejectd的返回值作为参数传递给回调函数。
  • race: 同all一样参数为一个Promise数组,数组哪一个元素率先改变状态,那么就将他返回的结果和状态传递给回调函数。
  • any: 和race很像,但不同的是any不会随着一个rejectd而结束Promise,必须等所有都变为rejectd才会结束。 当然还有其他方法,我这就不一一介绍了。

四、换个话题,css的position属性有哪些值?

一共五个值:inherit, static,relative, absolute, fixed, sticky,开启position后,可以通过top,right,bottom,left四个属性来定位移动。

  • inherit: 继承父元素的position的值,如果父元素没有设置position属性,则这个属性无效。

  • static: 元素为正常布局行为,不会脱离文档流,top等属性作用无效,效果相当于没有设置这个属性。

  • relative: 相对定位,基于他原来位置来定位移动。原来的位置会留白

  • absolute: 绝对定位,元素会脱离文档流,不会占据文档流的位置,基于离它最近的非static定位祖先元素来定位移动,绝对定位可也设置margin,且不会产生边距重合问题。

  • fixed:固定定位,元素会脱离文档流,也不会占据文档流的位置,它是基于屏幕视口来进行定位移动。元素的位置在屏幕滚动时不变,打印时,会出现在每页的固定位置。fixed会创建新的层叠上下文,当祖先元素的transformperspectivefilter属性非none时,容器会改为该祖先。

  • sticky:粘性定位,在屏幕范围内该元素没有达到偏移阈值(设置的top等属性),此时该元素设置的(top,left等属性)无效,而且没有脱离文档流,会产生留白效果,类似relative的效果。如果该元素达到其设置的阈值,该元素则会变为固定定位的效果,会固定在阈值的位置,而且还是会有留白效果。

    总结一下:sticky没达到阈值就像relative,达到或超过就会变为fixed,且留白效果还存在。

五、display属性的block, inline, inline-block的区别

  • block: 设置后元素变为块级元素
    • 块级元素会独占一行,多个相邻的块级元素会各自新开一行,默认情况下,元素宽度自动占满其父元素宽度。
    • 块级元素可以设置widthheight,即使设置width,元素还是会独占一行,但多余的部分不在是width,而是使用margin-right填充满。
    • block元素可以设置marginpadding属性
  • inline:设置后元素变为行内元素
    • inline元素不会独占一行,多个相邻的行内元素会排列在同一行里,直到一行排列不下,才会新换一行,其宽度随元素的内容而变化。
    • inline元素设置width,height属性无效。
    • inline元素的marginpadding属性,水平方向的padding-left, padding-right, margin-left, margin-right都产生边距效果;但竖直方向的padding-top, padding-bottom, margin-top, margin-bottom不会产生边距效果。
  • inline-block: 设置后元素会变为行内块元素
    • 结合了inline与block的一些特点,结合了上述inline的第1个特点和block的第2,3个特点。
    • 通俗讲就是不独占一行的块级元素。 默认下inline元素与其他元素之间会出现空白间隙,所以inline-blick使用后同样也有。那么这些空白间隙是什么呢,都是空白符。

在浏览器中,空白符是不会被浏览器忽略的,多个空白符会合并为一个。而我们在编写代码的时候,使用的空格换行都会产生空格符。如何解决呢

常用的方法就是给其父元素设置font-size:0,本质上空白符也是一个字符,也受字体大小控制。

六、换一下js的吧,instanceOf了解吗

instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。

function myInstanceof(left, right){
    const baseType = ["number", "string", "boolean", "undefined", "symbol"]
    if(left == null || baseType.includes(typeof left)) return false
    let proto = left.__proto
    while(true){
        if(proto == null) return false
        if(proto == right.prototype) return true
        proto == proto.__proto
    }
}

七、 [2,3,4] instanceOf Object的结果是什么

答案是true

[2,3,4].__proto__ == Array.prototype
[2,3,4].__proto__.__proto__ == Object.prototype

八、 讲一下原型链

说起原型链,不禁让我想到一张图: (图来源于冴羽大佬的文章)

原型链.webp

每一个构造函数实例都有一个__proto__属性,它指向其构造函数的原型对象,当构造函数的原型对象指向另一个实例或者原型时,就形成了原型链。

__proto__在绝大数浏览器都支持这个方法访问实例的原型,但在prototype上并没用这个属性,可以理解使用了Object.getPrototypeOf(obj)

九、 出了一道题目

Object.prototype.a = "a"
Function.prototype.a = "a1"
function Person() {}
let yideng = new Person()
console.log(Person.a)
console.log(yideng.a)
console.log(yideng.__proto__.__proto__.constructor.constructor.constructor.constructor)

问上面代码中三个console.log分别打印了什么?

答案为: 1."a1" 2."a" 3."[Function: Function]"

  • 第一个console.log: 这里使用Person.a,表示此时的Person为一个实例,该实例上没有找到a属性,所以会去他的原型对象上找,相当于Person.__proto__.aPersonFunction构造函数的一个实例,所以Person.__proto__ == Function.prototype,此时Function.prototype上有a属性,就将其值"a1"返回。
  • 第二个console.log:这里的yidengPerson构造函数的一个实例,该实例上没有a属性,去它原型Person.prototype上找,也没有找到,则会去Person.prototype.__proto__上找,也就是Object.prototype,找到了a属性并返回结果。
    yideng.__proto__ === Person.prototype
    yideng.__proto__.__proto__ == Person.prototype.__proto__ 
    Person.prototype.__proto__ == Object.prototype
  • 第三个console.log: 这里在第二个延伸了一下,相当于Object.prototype点一连串的constructor
Object.prototype.constructor //ƒ Object() { [native code] }
Object.prototype.constructor.constructor //ƒ Function() { [native code] }
Object.prototype.constructor.constructor.constructor // ƒ Function() { [native code] }
...

在多的.constructor都是ƒ Function() { [native code] }

十、 也是一道题

$("test").click(function (){
	console.log("2")
})
setTimeout(()=>{
	console.log("1")
},0)
while(true){
	console.log(Math.random())
}

上面clicksetTimeout会执行打印吗? 答案: 不会

因为click是一个事件、setTimeout是一个定时器,他们都属于异步任务,在当前所有同步代码执行完后,才会执行异步任务。而while(true)这段同步任务会一直打印一个随机数,不会结束,导致阻塞了主进程,就算异步任务满足条件也不会执行。

讲一讲Event Loop

同步和异步的执行顺序

  1. JavaScript将任务分为同步任务和异步任务,首先同步任务进入主线程,碰到异步任务后,将其放入Event Table进行回调函数注册
  2. 当异步任务的触发条件满足条件后,会将其回调函数从Event Table压入Event Queue中。
  3. 当主线程里的同步任务执行完毕后,会从Event Queue中读取异步的回调函数。
  • 只要主线程没有任务可以执行了,就会去Event Queue中读取异步的回调函数,这个过程叫做Event Loop

这样会存在很多问题,举个例子:(例子来自Jiasm的文章)

在柜台办理业务,你前边的一位老大爷可能在存款,在存款这个业务办理完以后,

柜员会问老大爷还有没有其他需要办理的业务,这时老大爷想了一下:“最近P2P爆雷有点儿多,是不是要选择稳一些的理财呢”,

然后告诉柜员说,要办一些理财的业务,这时候柜员肯定不能告诉老大爷说:“您再上后边取个号去,重新排队”。 所以本来快轮到你来办理业务,会因为老大爷临时添加的“理财业务”而往后推。 也许老大爷在办完理财以后还想 再办一个信用卡?或者 再买点儿纪念币

相当于在setTimeout中在定义了很多别的异步任务,按之前的说法,那么他只会在外层的setTimeout满足条件后执行后才会将其内部的异步回调注册进入Event Table中,这就不符合实际情况。

所以引入了宏任务微任务异步任务概念。setTimeout就是作为宏任务来存在的,而Promise.then则是具有代表性的微任务,

这里就需要对同步、异步、宏任务、微任务的执行顺序有一个清楚的认知。

  1. 首先执行同步代码,每一个script都属于一个宏任务。
  2. 所有同步代码执行完毕,去查看是否有异步代码需要执行。
  3. 如果有的话执行异步代码中的微任务
  4. 所以微任务执行后,至此第一轮Event Loop就结束了。(之后就是渲染)
  5. 开始新的一轮Event Loop,开始执行异步任务中的宏任务

并且在浏览器中引入了多线程的使用,事件触发线程定时器触发线程js引擎线程异步Http请求线程GUI渲染线程

面试个人总结

  • 面试的时候要沉稳一些,不要急躁。
  • 回答问题前要思考思考,组织一下回答的语言
  • 回答问题的时候最好想好一个流程,引导面试官提问准备好的。
  • 回答问题时,建议回答清晰,举例一个情境,问题出现的原因和解决方案。

如果面试前紧张,最好在面试前睡一会,静下心回顾一下基础知识。临近面试如果还是紧张,建议什么都不想,听一首音乐,轻轻哼唱。