美团实习前端一二面面经

一面

1、介绍一下webpack和Node.js

Webpack是一个现代的JavaScript应用程序的静态模块打包器(module bundler)。

四个核心概念:entry、output、loader、plugins

Node.js:Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎。

2、v-if和v-show的区别

两者都是在视图层进行条件判断视图展示。

v-if是根据判断条件来动态的渲染标签,增删DOM元素。

v-show是根据判断条件来动态的显示和隐藏元素。

频繁的DOM操作会影响页面加载速度和性能,因此v-show的性能比v-if好

3、简单的理解Vue diff算法和Virtual Dom的原理

DOM操作非常昂贵,为了减少对DOM操作,Vue.js将DOM抽象成一个以JavaScript对象为节点的虚拟DOM树,以VNode节点模拟真实DOM节点,可以对虚拟DOM节点进行创建、删除以及修改等操作,这个过程不需要操作真实DOM。当数据发生改变时,Vue.js会使用diff算法比对OldVNode和VNode,得出最小修改单位,对真实DOM打补丁。

当数据进行更改时,会触发update函数,该函数会接收一个VNode对象,利用__patch__方法比对VNode和OldVNode,并对真实DOM打补丁,__patch__的核心就是diff算法。diff算法是通过同层的树节点进行比较而非对树进行逐层搜索,时间复杂度只有log(n)。

diff算法:

1、使用sameVnode判断是否为相同节点(tag、key、isComment是否相同)

2、不是相同节点则在真实DOM上创建新的DOM,移除旧的DOM

3、是相同节点则调用patchVnode函数:

  • 找到Vnode和OldVnode对应的真实DOM,即el
  • 判断是Vnode和oldVnode是否指向同一节点,是则返回
  • 两者都有文本节点切不相等,将el的文本节点设置为Vnode的文本节点
  • oldVnode有子节点,Vnode没有,则删除el的子节点
  • oldVnode没有子节点,Vnode有,将Vnode子节点真实化后添加到el
  • 都存在子节点,调用updataChildren函数比较子节点

4、对子节点调用updataChildren函数:

  • 将Vnode的子节点Vch和oldVnode的子节点oldCh提取出来
  • Vch和oldCh都有StartIdx和EndIdx变量,2个变量相互比较。
  • 若4种比较都未匹配,如果设置了key,利用key进行比较
  • 比较过程中变量会向中间移,出现StartIdx>EndIdx则表示至少存在一个遍历完成,结束比较

https://github.com/answershuto/learnVue/blob/master/docs/VirtualDOM%E4%B8%8Ediff(Vue%E5%AE%9E%E7%8E%B0).MarkDown

4、手写:ajax请求,解释状态码含义

var xhr = new XMLHttpRequest();
xhr.open('get',url);
xhr.setRequestHeader(header);
xhr.send(data);
xhr.onreadystatechange = function(){
    if(xhr.status === 200 && xhr.readyState === 4){
        console.log(xhr.responseTest)
    }
}

ajax的5种状态(readyState):

  • 0 : 未初始化,还未调用open方法
  • 1 :载入中,已调用sned方法发送请求
  • 2 :载入已完成,send方法执行完成,已收到全部响应信息
  • 3 :交互,正在解析响应内容
  • 4 :完成,响应内容解析完成

5、项目有用过Promise吗?说说Promise

项目中用了axios,是基于promise封装的ajax。

promise主要用于异步状态,可以将异步状态操作队列化,按照希望的顺序执行,解决回调地狱

promise有有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)

.then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。

.catch方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

6、为什么使用Vuex,作用是什么

项目中的登录是使用vuex实现的,vuex存储的后台返回的token以及权限控制所需要的用户组信息

Vuex是一个专门为Vue.js应用程序开发的状态管理模式,解决了不同组件之间的数据共享和数据持久化。

Vuex的五个核心概念:

  • State:保存着store中的变量
  • Getter:对store的数据派生出状态,可以理解为store中数据的计算属性
  • Mutation:提供更改store中数据的方法,是同步函数
  • Action:异步方法,提交Mutation,通过Mutation更改状态
  • Module:状态树过大时将store分割成module,每个module都有自己的state、mutation、action、getter

7、display的常用属性

none,block,inline,table,inherit,flex,grid

display:none和visibility:hidden的区别

visibility:hidden可以隐藏元素,但隐藏的元素仍需要占用与未隐藏之前一样的空间

display:none可以隐藏元素,隐藏的元素不会占用任何空间

8、手写:实现垂直居中的方法

<div class="a">
   <div class="b">
   </div>
</div>

1、父元素相对定位,子元素绝对定位,利用margin:auto

.a{
    width:100px;
    height:100px;
    background-color:green;
    position:relative;
}
.b{
    width:50px;
    height:50px;
    background-color:gray;
    position:absolute;
    top:0; left:0; right:0; bottom:0;
    margin:auto;
}

2、父元素设置flex布局,设置flex-direction和justify-content属性,子元素设置align-self属性

.a{
    width:100px;
    height:100px;
    background-color:green;
    display:flex;
    flex-direction:column;
    justify-content:center;
}
.b{
    width:50px;
    height:50px;
    background-color:gray;
    align-self:center;
}

3、父元素设置相对定位,子元素绝对定位使用transform和translate

.a{
    width:100px;
    height:100px;
    background-color:green;
    position:relative;
}
.b{
    width:50px;
    height:50px;
    background-color:gray;
    position:absolute;
    top:50%; left:50%;
    transform: translate(-50%,-50%)
}

4、父元素设置table属性,子元素设置table-cell然后设置vertical-align属性

.a{
    width:300px;
    padding:100px;
    background-color:green;
    display:table;
}
.b{
    height:100px;
    background-color:gray;
    display:table-cell;
    vertical-align:center;
}

9、手写:CSS 实现一个三角形

利用边框均分原理

div{
    width:0;
    height:0;
    border:100px solid;
    border-color:red transparent transparent transparent;
} 

10、JS的基本数据类型

值放置在栈中,有Number、String、Undefined、Null、Symbol、Boolean

引用数据类型:值放置在堆内存中,栈中保存指向该数据的指针 Object,Array,Function

11、判断变量类型

判断是不是number:使用typeof和Object.prototype.toString.call()

判断是不是Array:使用instanceof和Object.prototype.toString.call()

12、let、const和var的区别

变量提升和块级作用域,let会绑定当前作用域,const常量不可更改

13、git常用命令

git config      //配置详细
git init        //初始化仓库
git remote        //管理远程仓库 添加、删除和修改远程分支的关联
git status        //显示文件状态,红色表示已修改未提交暂存区,绿色表示已提交暂存区
git add            //将文件提交到暂存区
git commit         //将暂存区修改提交到本地仓库
git push        //将本地更新推送远程主机
git pull        //获取远程主机某个分支的更新
git branch        //进行分支管理,查看、创建和删除分支
git checkout    //切换分支
git merge        //分支合并
git diff         //比较修改
git reset        //将本地仓库的修改撤回暂存区

14、linux常用命令

ls、cd、pwd、mkdir、rm、rmdir、mv、cp、cat、more、grep、tail、chmod、kill、free、ps
ps aux|grep xxx

反问:问了一下登录权限控制是怎么实现的

让我去了解一下jwt、token和session,权限控制一般都是后台实现的

后面回顾录音感觉没几个问题顺利答完都结结巴巴的,ajax请求码完全说错了,可能我答的比较自信,面试官也没追究,diff面试完才知道有这个东西,对虚拟dom的理解在看完vue diff这块源码后发现其实是错误的,查漏补缺吧_(:з」∠)_,我居然能过一面也是挺意外的

二面

二面一上来自我介绍,然后面试官就开始出题,做了一个小时的题(:з」∠)
除了第一题我后面都写错了或者概念糊涂,面试官给我讲了40分钟的题,好温柔啊
我同学说他问题没答上来向问问答案,腾讯面试官非常冷漠的让他自己去查

EventLoop

for(var i = 0; i < 3; i++) {
    setTimeout(function() {
        //问:console输出结果
        console.log(i);
    }, 1000);
}
// 输出3 3 3
for(var i = 0; i < 3; i++) {
    setTimeout(function() {
        //问:console输出结果
        console.log(i);
    }, 0);
}
//输出3 3 3
for(let i = 0; i < 3; i++) {
    setTimeout(function() {
        //问:console输出结果
        console.log(i);
    }, 0);
}
//输出0 1 2

我答了前两题答了222和222,面试官居然没发现我的错误

然后解释了一下宏任务和微任务,js的事件循环机制

宏任务:script(整体代码)、setTimeOut、setInterval、postMessage、I/O、UI交互等

微任务:promise.then、process.nextTick等

运行机制:

  • 主线程在执行栈中执行同步代码(宏任务)
  • 主线程中之外,事件触发线程管理着任务队列(包括宏任务队列和微任务队列)
  • 异步执行有了运行结果,就在任务队列中放置一个事件,宏任务事件结果放到宏任务队列,微任务事件结果放到微任务队列
  • 执行栈中的同步代码执行完毕后,主线程就会读取任务队列将可运行的任务添加到可执行栈,开始执行
  • 执行完一个宏任务后,会先添加微任务队列中所有微任务到执行栈中,全部执行完之后,在添加宏任务队列中的宏任务

第一题和第二题setTimeOut是个异步宏任务,因此js会将for循环执行完成后再执行conso.log(i),此时的i已经完成了三次++操作,所以输出3 3 3

第三题i使用let声明,let命令会绑定作用域,因此输出的是0 1 2

this指向

var obj = {
    xo: 'obj',
    method: function() {
        var xo = 'method';
        //问1:console输出结果
        console.log(this.xo);

        function test() {
            var xo = 'test';
            //问2:console输出结果
            console.log(this.xo);
        }
        test();
    }
};

function call1(method) {
    method();
}

call1(obj.method);
// undefined
// undefined
obj.method();
// obj
// undefined

this指向:

  • 一般函数this指向全局,严格模式下是undefined
  • 箭头函数的this继承自定义该箭头函数的宿主对象
  • 函数内的this指向调用该函数的对象
  • 构造函数里的this指向创建出来的实例
  • 使用bind、call、apply绑定的this指向绑定对象

上述call1函数中method是独自调用,因此指向全局,全局中无xo变量,所以打印出undefined

obj.method() 调用method方法的是obj对象,所以this指向obj对象,this.xo的值为 'obj' ;test()是独自运行的,因此this指向全局,输出undefined

变量提升&this&原型链

function Foo() {
    getName = function () { console.log(1); };
    return this;
}
Foo.getName = function () { console.log(2); };
Foo.prototype.getName = function () { console.log(3); };
var getName = function () { console.log(4); };
function getName() { console.log(5); }

//问:出以下输出结果
Foo.getName();      // 2
getName();            // 4
Foo().getName();     // 1
getName();            // 1

这道题被我蒙对了3个=- =

Foo.getName();

运行的是function () { console.log(2); };,会先查找实例本身属性,若未找到再原型链上查找,因此输出2

getName();

var getName = function () { console.log(4); };
function getName() { console.log(5); }
// 存在函数提升和变量提升,函数提升在变量提升之上
// 上述两句代码真正的执行顺序如下
function getName() { console.log(5); }
var getName;
getName =  function () { console.log(4); };

所以最终getName被function () { console.log(4); };覆盖了,输出4

Foo().getName();

首先Foo()执行的返回值是this,所以这个可以看做是this.getName(),此时this指向全局

全局范围内的this.getName就是getName = function () { console.log(4); };

但是Foo()中的getName = function () { console.log(1); };没有对getName使用let声明,所以此时全局的getName被Foo中的function () { console.log(1); };覆盖了,所以输出1

getName();

这个getName和this.getName是同一个变量,因此输出和Foo().getName();一样,都为1

手写代码

浏览器打开一个页面,在控制台执行脚本,获取页面TagName种类

刚刚开始写了

var list = document.querySelectorAll('*')
var res = new Set()
list.forEach(node=>res.add(node))
console.log(res)

发现打印出来的是所有node,而不是tagName,虽然用了set但是由于每个node都不唯一,没有起到过滤的作用。后来查了查怎么获取node的tagName属性

var list = document.querySelectorAll('*')
var res = new Set()
list.forEach(node=>res.add(node.tagName))
console.log(res)

// 面试官让我简化代码,弄成一句话代码,= =,不知道咋简化了
var r = new Set()
console.log(document.querySelectorAll('*').forEach(n=>r.add(n.tagName)))

后来问了我对工作公司、工作地点、实习时间,然后介绍了美团点评上海的研发中心,面试官在的到店部门。到店的一些主要部门什么的和技术栈。

反问了要是不通过还有机会面试美团吗?面试关口头答复说这面是通过了,还有一轮面试,那轮面试不会问很细,他是倾向于我能过是最好的,要是不通过会推荐给其他部门。

#前端##美团##前端工程师##实习##面经#
全部评论
楼主,请问你是面的哪个部门啊
1 回复 分享
发布于 2020-04-21 18:00
console.log(new Set([...document.querySelectorAll('*')].map(item=>item.tagName)));
1 回复 分享
发布于 2020-04-15 18:44
console.log(new Set(Array.prototype.slice.call(document.querySelectorAll('*'), 0).map((item)=>item.tagName)))
1 回复 分享
发布于 2020-04-15 10:45
楼主三面了吗
点赞 回复 分享
发布于 2020-04-15 22:21
兄弟几号面的
点赞 回复 分享
发布于 2020-04-12 23:10
前端也好难,我面的美团后台,只有两面,在等消息中
点赞 回复 分享
发布于 2020-04-12 22:03

相关推荐

评论
13
65
分享

创作者周榜

更多
牛客网
牛客企业服务