前端面试题总结-补01

23 minute read

前端面试题第二波 第一波

实习相关

主要记录一下当面试官问道暑期实习的相关问题时,该怎么回答。

介绍一下实习期间的工作

(2018年美团实习)实习期间主要负责的是B端的各类工作,包括给统计每天各城市、各商家销售情况的统计图表类项目,主要的工作内容是页面和前端接口的编写;以及还有实习最后阶段开发用于内部负责数据分析工作同事使用的实现类似自定义页面的系统。

实习期间遇到的主要问题

主要问题是新框架的适应问题,实习期间用到了一种接口描述语言“thrift”(Thrift包含一套完整的栈来创建客户端和服务端程序。顶层部分是由Thrift定义生成的代码。而服务则由这个文件客户端和处理器代码生成。在生成的代码里会创建不同于内建类型的数据结构,并将其作为结果发送。协议和传输层是运行时库的一部分。有了Thrift,就可以定义一个服务或改变通讯和传输协议,而无需重新编译代码。除了客户端部分之外,Thrift还包括服务器基础设施来集成协议和传输,如阻塞、非阻塞及多线程服务器。栈中作为I/O基础的部分对于不同的语言则有不同的实现),还有就是开发过程中会用到一些小组内部重构或整合的UI库,随着react版本的迭代,很多库都变得老化臃肿,所以开发过程中还需要不断地踩坑填坑。(待补充,一两个react组建的坑)

实习期间最大的收获是什么

实习期间最大的收获首先是认识了很多不错的同事,同事会进行code view,所以经过实习之后,会更注意代码规范。

Javascript

原生js解析cookie

function getCookie(name)
{
    var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
    if(arr=document.cookie.match(reg))
        return unescape(arr[2]);
    else
        return null;
}

正则解释:^| name表示匹配开始或者匹配空格,=([^;]*)匹配‘=’加上后面的一直的非‘;’,最后 (;|$)匹配最后的‘;’或最后结束

jS实现兼容的div拖放功能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>tmp.html</title>
    <style>
        .content {
            background-color: #bfbfbf;
            height: 500px;
        }
        .obj {
            position: absolute;
            top: 0;
            left: 0;
            width: 100px;
            height: 100px;
            border: 1px solid green;
            background-color: orange;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="content" id="obja"></div>
        <div class="obj" id="objb"></div>
        <p id="para"></p>
    </div>
    <script>
        var flag = false;
        var sx, sy, cx, cy, dx, dy;
        var obj = document.getElementById("objb");
        if(obj.addEventListener) { // 除IE8已下都可以
            obj.addEventListener('mousedown', function(event) {dragStart(event);}, false);
            obj.addEventListener('mousemove', function(event) {dragMove(event);}, false);
            document.body.addEventListener('mouseup', function(event) {dragEnd(event);}, false);
        }
        else if(obj.attachEvent){
            obj.attachEvent('onmousedown', function(event) {dragStart(event);});
            obj.attachEvent('onmousemove', function(event) {dragMove(event);});
            document.body.attachEvent('onmouseup', function(event) {dragEnd(event);});
        } else {
            obj['onmousedown'] = function(event) {dragStart(event);}
            obj['onmousemove'] = function(event) {dragMove(event);}
            obj['onmouseup'] = function(event) {dragEnd(event);}
        }
        function dragStart(event) {
            flag =true;
            sx = event.clientX;
            sy = event.clientY; // client是moseevent,所以sx,sy为鼠标的位置,要判断鼠标在框框内
            dx = obj.offsetLeft;
            dy = obj.offsetTop;
        }
        function dragMove(event) {
            if(flag) {
                cx = event.clientX;
                cy = event.clientY;
                obj.style.left = cx - sx + dx + 'px';
                obj.style.top = cy - sy + dy + 'px';
                if(event.preventDefault) { // 可加可不加
                    document.addEventListener("mousemove", function(){
                        event.preventDefault(); // 默认动作不要做                 
                    }, false); // false代表事件冒泡,true代表事件捕获
                } else {
                    document.attachEvent("onmousemove", function(){
                        window.event.returnValue = false;
                    });
                }
            }
        }
        function dragEnd(event) {
            flag = false;
        }
    </script>
</body>
</html>

css实现tab切换

关键代码是label的for标签,这样相当于可以和for绑定的radio有一个联动

<div class="tabs">
  <div class="tab-pane">
    <input type="radio" name="tab" id="tab01" checked />
    <label class="tab-item" for="tab01">tab01</label>
    <div class="tab-content">111</div>
  </div>
  <div class="tab-pane">
    <input type="radio" name="tab" id="tab02"/>
    <label class="tab-item" for="tab02">tab02</label>
    <div class="tab-content">222</div>
  </div>
  <div class="tab-pane">
    <input type="radio" name="tab" id="tab03"/>
    <label class="tab-item" for="tab03">tab03</label>
    <div class="tab-content">333</div>
  </div>
</div>

各种选择器,注意+是选择紧接在xx元素之后的所有xx元素。

.tabs{
  position:relative;
  width:400px;
  height:300px;
}
.tab-pane{
  display:inline-block;
}
.tabs input[type='radio']{
  position:absolute;
  clip:rect(0,0,0,0)
}
.tab-item{
  display:block;
  height:34px;
  line-height:34px;
  cursor:pointer;
  padding:0 10px
}

.tabs input[type='radio']:checked+.tab-item{
  background:orangered;
  color:#fff
}

.tab-content{
  position:absolute;
  border:1px solid #eee;
  padding:20px;
  left:0;
  top:36px;
  bottom:0;
  right:0;
  background:#fff;
}

.tabs input[type='radio']:checked+.tab-item+.tab-content{
  z-index:1
}

原生实现autocomple

 <div id="div1">
    <input type="text" id="input" placeholder="有autocomplete的输入框"/>
    <ul id="ul">
    </ul>
</div>
*{
    margin: 0;
    padding: 0;
    box-sizing:border-box;
}
ul{
    list-style: none;
    margin: 0 auto;
    background-color: #ededed;
    color: #3b200c;
    width: 400px;
    border: none;
}
li{
    cursor: pointer;
}
input{
    display: block;
    margin: 0 auto;
    line-height: 40px;
    height: 40px;
    width: 400px;
    font-size: 20px;
}
var arr = ['a','apple','abandon','bilibili','beep','before','become','being','highmaintains','by','bye','banana']
input.addEventListener('input', function(event){
    var _value = event.target.value.trim()
    if(_value){
        autoComplete(_value, arr)
    }
    else{
        ul.innerHTML = ''
    }
})
function autoComplete(str, arr){
    var lis = []
    arr.forEach((word)=>{
        if(word.startsWith(str)){
            lis.push('<li>' + word + '</li>')
        }
    })
    ul.innerHTML = lis.join('')
}
function addToInput(li){
    var _txt = li.innerText
    input.value = _txt
}
ul.addEventListener('click', function(event){
    if(event.target.tagName.toLowerCase() === 'li'){
        addToInput(event.target)
    }
})

原生实现event类

  • on(event,fn):监听event事件,事件触发时调用fn函数;

  • once(event,fn):为指定事件注册一个单次监听器,单次监听器最多只触发一次,触发后立即解除监听器;

  • emit(event,arg1,arg2,arg3…):触发event事件,并把参数arg1,arg2,arg3….传给事件处理函数;

  • off(event,fn):停止监听某个事件。

class EventEmitter{
    constructor(){
        this._events={}
    }
    on(event,callback){
        let callbacks = this._events[event] || []
        callbacks.push(callback)
        this._events[event] = callbacks
        return this
    }
    off(event,callback){
        let callbacks = this._events[event]
        this._events[event] = callbacks && callbacks.filter(fn => fn !== callback)
        return this
    }
    emit(...args){
        const event = args[0]
        const params = [].slice.call(args,1)
        const callbacks = this._events[event]
        callbacks.forEach(fn => fn.apply(this, params))
        return this
    }
    once(event,callback){
        let wrapFunc = (...args) => {
            callback.apply(this,args)
            this.off(event,wrapFunc)
        }
        this.on(event,wrapFunc)
        return this
    }
}

React

react生命周期

  1. componentWillMount()
  2. render()

在componentWillMount()方法之后 在componentWillReceive(nextProps, nextState)方法之后

  1. componentDidMount()
  2. componentWillReceiveProps(nextProps)
  3. shouldComponentUpdate(nextProps, nextState)

shouldComponentUpdate() 返回 false,这就意味着 componentWillUpdate()render()componentDidUpdate() 将不再执行

  1. componentWillUpdate(nextProps, nextState)

不可以调用setState()

  1. componentDidUpdate(prevProps, prevState)
  2. componentWillUnmount()

react context

context被翻译为上下文,当在组件树中不想通过props或者state的方式来传递数据时,可以使用context来实现跨层级组件传递,有很多react组件都是通过context来完成自己的功能,比如react-redux的<Provider />,就是通过context提供一个全局态的store

当使用propsstate时,数据是自顶向下流,也就是一层一层的传递,而context则可以跨越组件进行数据传递(其实也就导致了不稳定,所以官方并不推荐在稳定版本的react中使用)

如果要Context发挥作用,需要用到两种组件,一个是Context生产者(Provider),通常是一个父节点,另外是一个Context的消费者(Consumer),通常是一个或者多个子节点。所以Context的使用基于生产者消费者模式(想想redux怎么用的)

react/redux设计模式

react/redux构架中观察到的最重要的一种结构模式是中介者模式,他允许我们公开一个统一的接口,系统不同部分可以通过该接口进行通讯。

react/redux这个组合中严格贯彻了中介者模式的思想,redux为单一state管理提供了解决方法。单一对象在处理复杂数据变化的时候可以协调指挥,不至于交叉变化引用。中介者可以处理多个委托人的请求,处理一个委托人的请求也不在话下。而且在增加委托者的时候对系统中其他委托者是没有影响的,他们都是独立存在的,只和中介者发生通讯联系。如果用单元测试的方法来测试也很容易。

mvvm设计模式

最常见的客户端架构有三种:

  • MVC: Model-View-Controller

View通过Controller来和Model联系,Controller是View和Model的协调者,View和Model不直接联系,基本联系都是单向的。

用户User通过控制器Controller来操作模板Model从而达到视图View的变化。

  • MVP: Model-View-Presenter

是从MVC模式演变而来的,都是通过Controller/Presenter负责逻辑的处理+Model提供数据+View负责显示。在MVP中,Presenter完全把View和Model进行了分离,主要的程序逻辑在Presenter里实现。并且,Presenter和View是没有直接关联的,是通过定义好的接口进行交互,从而使得在变更View的时候可以保持Presenter不变。

  • MVVM: Model-View-ViewModel

MVVM是把MVC里的Controller和MVP里的Presenter改成了ViewModel。Model+View+ViewModel。View的变化会自动更新到ViewModel,ViewModel的变化也会自动同步到View上显示。这种自动同步是因为ViewModel中的属性实现了Observer,当属性变更时都能触发对应的操作。

ES6

阮一峰,只记录一下难点。

Proxy & Reflect

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与处理器对象的方法相同。

Promise原理

Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.