体验极佳的一次面试!面试官大大贼温柔,循循善诱地帮助我把问题捋出来;
另外问题也都对我这个初学者很友好,超级简单哈哈哈
面试完半个小时二面电话就来了,约到了明天上午十一点
后来才知道,面的这个部门其中一个业务就是为政府做智慧城市相关的业务~真的巧!我最近在忙活的外包项目也是这个主题的说!
无。面试官大大表示这个是加分项 其实实际开发也用不太上
这里我还写过3篇文章嘞~(但是面试时候没想起来XD)
【青训营】做面试题般回顾前端基础知识CSS篇 - 2 CSS盒模型那些有趣的知识点 - 掘金 (juejin.cn)
【青训营】做面试题般回顾前端基础知识CSS篇 - 3 标准盒模型中块元素宽度&总宽度的问题 - 掘金 (juejin.cn)
由box-sizing属性引出的对盒模型的思考 - 掘金 (juejin.cn)
CSS3 中的盒模型有以下两种:标准盒子模型、IE 盒子模型
盒模型都是由四个部分组成的,分别是 margin、border、padding 和 content。
另外我提到了——
标准盒模型和 IE 盒模型的区别在于设置 width 和 height 时,所对应的范围不同:
可以通过修改元素的 box-sizing 属性来改变元素的盒模型:
box-sizeing: content-box
表示标准盒模型(默认值)box-sizeing: border-box
表示 IE 盒模型(怪异盒模型)【1】使用特殊的布局单位(追问:rem em的区别?)
em 和 rem:它们相对于 px 更具灵活性,它们都是相对长度单位,它们之间的区别:em 相对于父元素,rem 相对于根元素。
二者的详细区别:
抛出之前写得一篇文章 我觉得很好理解&很全面了~
【青训营】做面试题般回顾前端基础知识CSS篇 - 4 弹性布局与经典面试题CSS实现垂直居中 - 掘金 (juejin.cn)
flex布局的概念:
flex 布局的基本概念 - CSS(层叠样式表) | MDN (mozilla.org)
总结几个常用方法:
更改flex方向 flex-direction
用flex-wrap实现多行Flex容器
以上两个属性的简写:简写属性 flex-flow
flex-grow - CSS(层叠样式表) | MDN (mozilla.org)
flex-shrink - CSS(层叠样式表) | MDN (mozilla.org)
flex-basis - CSS(层叠样式表) | MDN (mozilla.org)
当然!这些如果不常用 根!本!记!不!住!
所以就有了下面这个小练习
推荐一个5.2Kstars的仓库(截至21/10),在线尝试地址戳这里
然而!在我之前都做了一遍的前提下 在面试时候除了最基础的居中的属性,其他flex相关的属性都没答上来这里就很难受了 所以要时常用啊!
overflow:hidden;
这样父盒子就是一个BFC了(挖坑。)高度塌陷消除~这个当时没太说清楚 只是举了个自适应双栏布局的例子(自己挖坑自己跳啊!),查阅下资料——
先来看两个相关的概念:
- Box: Box 是 CSS 布局的对象和基本单位,⼀个⻚⾯是由很多个 Box 组成的,这个 Box 就是我们所说的盒模型。
- Formatting context:块级上下⽂格式化,它是⻚⾯中的⼀块渲染区域,并且有⼀套渲染规则,它决定了其⼦元素将如何定位,以及和其他元素的关系和相互作⽤。
块格式化上下文(Block Formatting Context,BFC)是 Web 页面的可视化 CSS 渲染的一部分,是布局过程中生成块级盒子的区域,也是浮动元素与其他元素的交互限定区域。
通俗来讲:BFC 是一个独立的布局环境,可以理解为一个容器,在这个容器中按照一定规则进行物品摆放,并且不会影响其它环境中的物品。如果一个元素符合触发 BFC 的条件,则 BFC 中的元素布局不受外部影响。
- 解决 margin 的重叠问题:由于 BFC 是一个独立的区域,内部的元素和外部的元素互不影响,将两个元素变为两个 BFC,就解决了 margin 重叠的问题。
- 解决高度塌陷的问题:在对子元素设置浮动后,父元素会发生高度塌陷,也就是父元素的高度变为 0。解决这个问题,只需要把父元素变成一个 BFC。常用的办法是给父元素设置
overflow:hidden
。(这个就是上面提到的内容)- 创建自适应两栏布局:可以用来创建自适应两栏布局:左边的宽度固定,右边的宽度自适应。
(2)表示迄今为止在日常实习中遇到了两次
Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。
这个问题太有趣了!真的是要多(带着思考地)写(优雅的)代码,才能流利地回答啊!
面试官大大当时提醒了我
reduce concat sort
这几个方法(居然没想起来!用的太少了啊还是),其他说得都还算全,这里我根据《JS数据结构与算法(第三版)》再捋一遍!
map forEach
(问了这两个的区别 答:一个返回改变后的数组 一个只是单纯遍历)concat
sort
还提到了sort中函数的内容reduce
这个是面试官大大提醒的 讲了下用reduce算数组之和indexOf(某一个数) find(某一个条件) includes((是否有这个)数)
splice
与切片(节选一段)slice
(这个当时居然忘记了!经常用的明明)toString
join('分隔符')
reverse
filter(输出的元素需要满足的条件)
some
是否全为某个值every
这个当时也忘了!fill
Object.values() - JavaScript | MDN (mozilla.org)
这个API很有趣!返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in
循环的顺序相同
【1】通过原型链
通过原型链实现子类对父类的继承
Child.prototype = new Father();
别忘了修正Child类显示原型的
constructor
Child.prototype.constructor = Child
这部分之前写过一篇小结,放到这里,能说出来肯定是加分的!
继承模式
原型链继承 : 得到方法
- 通过
Child.prototype = new Parent()
继承父函数 从而继承了属性与方法- 缺点:通过Child构造的对象 的原型对象 的
constructor
属性指向Parent!这不好!
- 我们需要让子类型的原型的
constructor
指向子类型 才对!- 这个问题可以通过
Child.prototype.constructor = Child;
来修正constructor属性function Parent(){} Parent.prototype.test = function(){}; function Child(){} Child.prototype = new Parent(); // 子类型的原型指向父类型实例 Child.prototype.constructor = Child;// 修正constructor属性 var child = new Child(); //有test()
借用构造函数 : 得到属性
- 通过
Parent.call(this,name,age)
继承(调用父类型构造函数)- 缺点:获得父类型的方法很麻烦!还得借助call方法一个个地弄
Parent.func.call(this,参数)
function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){}; function Child(xxx,yyy){ Parent.call(this, xxx);//借用构造函数 this.Parent(xxx) // 相当于 this.Parent(xxx) } var child = new Child('a', 'b'); //child.xxx为'a', 但child没有test()
组合
function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){}; function Child(xxx,yyy){ Parent.call(this, xxx);//借用构造函数 this.Parent(xxx) } Child.prototype = new Parent(); //得到test() var child = new Child(); //child.xxx为'a', 也有test()
JS实现继承的最佳实践
使用 原型链结合构造函数
在父类型属性有很多条时 使用
Parent.call(this,父函数属性)
让子类型继承function Parent(attribute,other){ this.attribute = attribute; this.other = other; } Parent.prototype.output = function(){ console.log("此方法位于Parent的显式原型中 Child构造函数继承了我 所以Child的实例对象可以顺着隐式原型连找到这个对象(在Object对象上)"); } function Child(attribute, other){ Parent.call(this,attribute,other);// 继承父类型的属性 } Child.prototype = new Parent();// 原型链继承!让子类型的显式原型指向父类型的实例! Child.prototype.constructor = Child;// 出于严谨 这里修正constructor属性 要不然Child的显式原型的constructor就是Parent了 这就很奇怪! var child = new Child('a', 'b');// ['a', 'b'] child.output();
上面的属性的输出有些问题
JS继承最佳实践完整版输出如下:
好吧上面的还是不全 有点小问题——
Child.prototype.constructor = Child;// 出于严谨 这里修正constructor属性 要不然Child的显式原型的constructor就是Parent了 这就很奇怪! // 修正完 constructor指向Child构造函数
【2】通过ES6中的Class类
这个没太说清楚~
月影大大的课程之前做得截图,很基础哈~
【3】通过构造函数继承
这个其实才是面试官想考察的内容(顺带着引出了下一个问题的内容)
let son = new Father()
艾玛太经典了,我还写过一篇文章说这里哩(写得老清楚了,看就完事!)
说了一下 在红宝书看到过bulabulabula
另外拓展了一下——判断构造函数的返回值类型,如果res为对象类型,new Person的最终结果为res 而非我们想要的那个实例对象!
指向这个新创建的对象(红宝书提到了)
拓展:可以看看之前写的那篇文章中的例子 Fn即为构造函数 很明显它是指向这个新创建的Object空对象的
我能说我上个月刚看完相关文章么emm 其实我也说了(
老实话痨孩子)
前段时间总结的笔记——
JS引擎的执行机制——事件循环 Event Loop
看了下这篇文章 简单入门10分钟理解JS引擎的执行机制
注意本文中所有执行流程是基于浏览器环境,而不是node环境
node轮询有phase(阶段)的概念
浏览器和NodeJS中不同的Event Loop事件循环的核心机制是:宏任务、微任务及其相关队列的执行流程图
单线程的JS通过事件循环 Event Loop 实现异步
JS的执行机制是
- 首先判断JS是同步还是异步,同步就进入主线程,异步就进入event table
- 异步任务在event table中注册函数,当满足触发条件后,被推入event queue
- 同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主线程中
以上三步循环执行,这就是event loop
举个例子
console.log(1) setTimeout(function(){ console.log(2) },0) console.log(3)
1.console.log(1) 是同步任务,放入主线程里 2.setTimeout() 是异步任务,被放入event table, 0秒之后被推入event queue里 3.console.log(3) 是同步任务,放到主线程里 // 当 1、 3在控制条被打印后,主线程去event queue(事件队列)里查看是否有可执行的函数,执行setTimeout里的函数
但这还不够!
微任务、宏任务的概念
如果有多个任务在event queue里呆着呢?谁先?谁后?上新概念!
上道题
setTimeout(function(){ console.log('定时器开始啦') }); new Promise(function(resolve){ console.log('马上执行for循环啦'); for(var i = 0; i < 10000; i++){ i == 99 && resolve(); } }).then(function(){ console.log('执行then函数啦') }); console.log('代码执行结束');
尝试按照,上文我们刚学到的JS执行机制去分析
【1】setTimeout 是异步任务,被放到event table 【2】new Promise 是同步任务,被放到主线程里,直接执行打印 console.log('马上执行for循环啦') 【3】.then里的函数是 异步任务,被放到event table 【4】 console.log('代码执行结束')是同步代码,被放到主线程里,直接执行
所以,结果是 【马上执行for循环啦 — 代码执行结束 — 定时器开始啦 — 执行then函数啦】吗?
亲自执行后,结果居然不是这样,而是【马上执行for循环啦 — 代码执行结束 — 执行then函数啦 — 定时器开始啦】
欸?不是setTimeout这个任务先进的event table麽?
并不是!上述根据异步同步一股脑划分的方法不对!
而准确的划分方式是:
- macro-task(宏任务):包括
整体代码script
,setTimeout
,setInterval
- micro-task(微任务):
Promise
,process.nextTick
(Node独有)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5oeipqxB-1640179150890)(https://segmentfault.com/img/bV1TKz?w=879&h=723)]
按照这种分类方式:JS的执行机制是
- 【1】执行一个宏任务(JS脚本中的内容都是宏任务~),过程中如果【2】遇到微任务,就将其【3】放到微任务的【事件队列】里
- 当前【4】宏任务执行完成后,会查看微任务的【事件队列】,并【5】将里面全部的微任务依次执行完
重复以上2步骤,结合event loop(1) event loop(2) ,就是更为准确的JS执行机制了。
尝试按照刚学的执行机制,去分析例2:
1.首先执行script下的宏任务,遇到setTimeout,将其放到宏任务的【队列】里 2.遇到 new Promise直接执行,打印"马上执行for循环啦" 3.遇到then方法,是微任务,将其放到微任务的【队列里】 4.打印 "代码执行结束" 5.本轮宏任务执行完毕,查看本轮的微任务,发现有一个then方法里的函数, 打印"执行then函数啦" 6.到此,本轮的event loop 全部完成。 7.下一轮的循环里,先执行一个宏任务,发现宏任务的【队列】里有一个 setTimeout里的函数,执行打印"定时器开始啦"
所以最后的执行顺序是【马上执行for循环啦 — 代码执行结束 — 执行then函数啦 — 定时器开始啦】
谈谈setTimeout
这段setTimeout代码什么意思? 我们一般说: 3秒后,会执行setTimeout里的那个函数
setTimeout(function(){ console.log('执行了') },3000)
但是这种说并不严谨,准确的解释是: 3秒后,setTimeout里的函数会被推入event queue,而event queue(事件队列)里的任务,只有在主线程空闲时才会执行。
所以只有满足 (1)3秒后 (2)主线程空闲,同时满足时,才会3秒后执行该函数
如果主线程执行内容很多,执行时间超过3秒,比如执行了10秒,那么这个函数只能10秒后执行了
图解Event Loop
- 同步任务直接进入主执行栈(call stack)中执行
- 等待主执行栈中任务执行完毕,由EL将异步任务推入主执行栈中执行
说完概念 简单说了下例子,面试官大大下狠手了——
答:简单了解 刚准备描述一个输出题的例子…
补一下async await的概念
面试官大大说你别费劲了 我给你来一道 说输出吧
async function async1(){
console.log("async1 start");
await async2();
console.log("async1 end");
return 'async return';
}
async function async2(){
console.log("async2");
}
console.log("script start");
setTimeout(function() {
console.log("setTimeout");
}, 0);
async1().then(function (message) { console.log(message)});
new Promise(function(resolve){
console.log("promise1");
resolve();
}).then(function() {
console.log("promise2");
});
console.log("script end");
我自己的理解如下:
第二版理解:(但还是不透彻!回头需要看下红宝书&一些文章继续学习)
另外面试官大大建议我在babel中转换下ES6的async await代码(额我试了一下 不太会啊 这不是一样的么orz
) 自行理解下~
这道算法题太友善了趴 感谢一面大大!
经典递归问题,输入n 输出斐波那契数
function fb(n) {
if(n === 1 || n === 2) {
return 1
}
return fb(n - 2) + fb(n - 1)
}
激动的心 颤抖的手 终于拿到offer了!
感谢和善的面试官大大!一定努力学习回报大大的信任!
总而言之这次面试还是更注重“我在学校做了什么”
这部分面试官大大问了我几个有趣的问题——
大学这三年多的学习历程?
为什么放弃人工智能科研,选择前端开发?
大学期间上过感触最深的课程是哪个?
大学期间做过(计算机相关)最有成就感的一件事是什么?
‘a.b.c’ -> {a: {b: {c: null }}}
用了个笨办法 面试官也没让优化 直接就到反问环节了
function main(str) {
let obj = {}
let data = str.split(".").reverse()
temp = JSON.parse(JSON.stringify(`{ ${data[0]} : null }`))
for(let i = 1; i < data.length; i++) {
obj = JSON.parse(JSON.stringify(`{ ${data[i]} : ${temp} }`))
}
return obj
}
console.log(main('a.b.c.d'));