阅读 191

前端面试汇总

一、 css

1. 手写三角形

     .a {
           width: 100px;
           height: 100px;
           border-left: 100px solid transparent;  
           border-right: 100px solid transparent;  
           border-top: 100px solid transparent;  
           border-bottom: 100px solid pink;   //尖尖朝上
           box-sizing: border-box;
       }
       .b {
           width: 0px;
           height: 0px;
           border-width:100px;
           border-style:solid;
           border-color: transparent  transparent transparent red ; // 尖尖朝右
       }

2. 重置居中

##### 1.flex 
  父
  display: flex;
  justify-content: center;
  align-items: center;##### 2.父 display: flex; 子 margin:auto
##### 3.绝对定位
父:position:relative  
子:position:absolute top:50% left:50% transform:translate(-50%,-50%)父:position:relative  
子:position:absolute top:0 left:0 right:0 bottom:0

3. 元素消失

display:none 
visibilty:hidden
opcacity:0height:0,width:0

4. 左侧固定,右侧自适应两栏布局的方法

##### 1.左边浮动,右边margin-left左侧宽度.父{overflow:hidden;}.左边{float:left;width:100px}.右边{margin-left:100px}##### 2.左边绝对定位,右边margin-left左侧宽度
父{position:relative}.左边{position: absolute;left: 0;top:0;width:200px;}.右边{margin-left:100px}##### 3.双float + calc()计算属性 
.父{overflow:hidden;}.左边{float: left;width:200px;}.右边{float:left;width:calc(100%-200px);}##### 4.flex.父{display: flex;}.左边{flex:0 0 200px;}.右边{flex: 1;}

5. 两侧固定,中间自适应

<div class='box'>
        <div class='left'></div>
        <div class='middle'></div>      
        <div class='right'></div></div>
1.浮动+布局,将middle放最下面,left左边浮动,right右边浮动

   //只写了关键的代码
    .left{ float:left,width:100px}
    .right{float:right,width:100px}
    .middle {margin:0px 100px;}
2.浮动+calc

    //只写了关键的代码
     .left,.right,.middle {float:left}
    .left,.right{ width:100px}
    .middle {width:calc(100% - 200px)}
3.定位

    //只写了关键的代码
    .box {position:relative;}
    .left,.right{ width:100px;position:absolute;top:0px}
    .left {left:0px}
    .right{right:0px}
    .middle {margin:0px 100px}
4.flex

    .box {display:flex}
    .left,.right{flex:0 0 100px}
    .right {flex:2}

6.flex布局

定义:flex是弹性布局,用来给盒模型提交最大的灵活性。设置为flex布局以后,子元素的 float,clear,vertical-aglin属性失效

1. 容器的属性

1.flex-direction: row | row-reverse | column | column-reverse //方向2.flex-wrap:nowrap | wrap | wrap-reverse //换行3.flex-flow : row | nowrap // 方向和换行的合写4.justify-content:flex-start | flex-end | center | space-between | space-around 
5.align-item: flex-start | flex-end | center | stretch | baseline6.align-content:flex-start | flex-end | center | space-between | space-around | stretch
2. 项目的属性(自身)

1.order // 排序 从小到大 默认02.flex-grow //放大 默认0 不放大3.flex-shrink //缩小 默认1 缩小4.flex-basis //分配多余空间之前,项目占据的空间 默认auto 5 flex: flex-grow flex-shrink basis //3个属性的合写 默认 0 1 auto6.align-self: auto | flex-start | flex-end | center | basline | stretch // 单个项目与其他项目不一样的对齐方式 默认auto表示继承父元素的align-items

7.文本溢出展示。。。

1.单行文本溢出

overflow:hidden;text-overflow:ellipsis; // 文本超出的处理方法,属性clip=>裁剪 ellipsis=>点点点white-space:nowrap;//不换行
2.多行文本溢出

line-height:30px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2; // 2表示2行-webkit-box-orient:vertical; //属性规定框的子元素应该被水平或垂直排列。

8.white-space 与 word-wrap的区别

white-space:nowrap //不换行
word-wrap: break-word; // 换行,但不会拆分英文单词
word-break: break-all  // 换行,会拆分单词

二 、js

1.this指向的几种类型(核心:谁调用指向谁)

1.普通函数 => window
2.箭头函数 => window

箭头函数能保存函数创建时候的this,而不是调用的

3.对象里的函数 => 调用的对象
4.构造函数 => new 出的新的函数

为何 React事件要自己绑定 this,几种方式的优缺点

2.如何判断元素类型

typeof

typeof '';// stringtypeof 1;// number

intanceof

[] instanceof Array;// true{} instanceof Object;// true

Object.prototype.toString.call('') ;  // [object String]

3.JS 异步解决方案的发展历程以及优缺点

回调-promise-generator-aync/await

4.Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?

promise构造函数是同步执行的,then方法是异步执行的

5.js的事件执行机制

宏任务 微任务

6.http

状态码 301 302 和307的区别    常见的请求方式 get post put delete put
http请求头有哪些 Request的Header信息
content-type有哪些常见类型
前端缓存机制
浏览器和服务器设置cache-control的区别
1.cache-control是由服务器端设置的,服务器没设置不会走缓存。
2.当服务器端设置了强缓存,浏览器设置no-store,no-cache,max-age=0可以跳过强缓存命中协商换存。
3.强缓存不会向服务器发送请求
参考:HTTP请求头和响应头中cache-control的区别
url输入到展示的过程、tcp三次握手,4次挥手

7. 节流 防抖

节流,执行多次  防抖,执行一次

函数节流:让一个函数不要执行得太频繁,减少一些过快的调用来节流。也就是在一段固定的时间内只触发一次回调函数,即便在这段时间内某个事件多次被触发也只触发回调一次。原理是通过判断是否有延迟调用函数未执行。高频触发事件,定时执行事件,过渡平滑。

//节流function throttle(fn,wait){
    let previos = 0 
    return function(){
        const _this = this,arg = argment        const now = Date.now()
        if(now - previos > wait){
             fn.apply(_this,arg)
             previos = now        }
    }}

函数防抖:将多次操作合并为一次操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。

//防抖function debounce(fn,delay){
     let timer = null
     return function(){
        clearTimeout(timer)
        let _this = this,arg = argment
        timer = setTimeout(()=>{
              fn.apply(_this,arg)
        },delay)
    }}

区别:
函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,函数防抖只是在最后一次事件后才触发一次函数。

比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。

三、react问题

1.react的diff算法原理

传统diff算法:通过循环递归对节点进行依次比较,计算两棵树差异的时间复杂度为0(n^3)。
react的diff算法:
三大策略
1、tree diff => DOM节点垮层级的移动操作特别少,可以忽略

1.react 通过updateDepth对虚拟dom进行层级控制
2.对数分层比较,两棵树只比较同一层节点比较,若不存在,直接删除
3.只需遍历一次,就可以完成整棵树的比较

若出现了跨级操作,则只会删除和新建节点

2、component diff => 拥有相同类的两个组件,生成相同的树形结构;拥有不同类的连个组件,生成不同的树形结构

1.同一类型的两个组件,按同一层级节点进行比较
2.同一类型的两个组件,若是组件A变成组件B,可能虚拟DOM没有任何变化,只需要用户使用shouldComponentUpdate()来判断是否需要计算
3.不同类型组件,会删除一个,重新创建

3、element diff => 对于同一层级的子节点,通过唯一的id进行区分

节点处于同一层级时候,diff提供了三种方式:删除,插入,移动

2. setState何时同步何时异步?

由React控制的事件处理程序,以及生命周期函数调用setState不会同步更新state 。

React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval等。

大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等,这些事件处理程序中的setState都是异步处理的。

React是怎样控制异步和同步的呢?

在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到队列中延时更新,而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state;但是,有一个函数 batchedUpdates,该函数会把 isBatchingUpdates 修改为 true,而当 React 在调用事件处理函数之前就会先调用这个 batchedUpdates将isBatchingUpdates修改为true,这样由 React 控制的事件处理过程 setState 不会同步更新 this.state。

在hanldeClick处理程序中调用了两次setState,但是render只执行了一次。因为React会将多个this.setState产生的修改放在一个队列里进行批延时处理。所以不应该依靠它们的值来计算下一个状态

3.请简述react的事件机制?

react利用了事件委托机制实现事件机制,事件并没有绑定在真实的dom节点上面,是绑定在最外层的docment上。使用一个统一的监听器,所有的事件都由这个监听器统一分发。

组件挂载更新时,会将事件分门别类放进事件池,事件触发根据event找到对应的组件,再组件标识和事件类型找到对应的事件进行监听回调,然后,执行回调函数。

事件绑定中丢失的this
class 组件中,给元素添加事件时,class 的方法默认不会绑定 this,当调用这个方法的时候,this 的值为 undefined。

class Demo extends React.Component {
  fun1(){}
  render (
    <div onClick={this.fun1} />
  )}

4. 为什么 React组件首字母必须大写

如果传递的是一个字符串,那么在创建虚拟DOM对象时,React会认为这是一个简单的HTML标签,但是这显然不是一个简单的HTML标签,因此去创建一个不存在的标签肯定是会报错的。
如果首字母大写,那么就会当成一个变量传递进去,这个时候React会知道这是一个自定义组件,因此他就不会报错了

5. Redux

  1. 整个应用的状态管理
    2.三个原则

2.1.单一事实来源
Redux 使用 “Store” 将程序的整个状态存储在同一个地方。因此所有组件的状态都存储在 Store 中,并且它们从 Store 本身接收更新。单一状态树可以更容易地跟踪随时间的变化,并调试或检查程序

2.2.状态是只读的
改变状态的唯一方法是去触发一个动作。动作是描述变化的普通 JS 对象。就像 state 是数据的最小表示一样,该操作是对数据更改的最小表示

2.3.使用纯函数进行更改
为了指定状态树如何通过操作进行转换,你需要纯函数。纯函数是那些返回值仅取决于其参数值的函数。

3.列出 Redux 的组成

Action – 这是一个用来描述发生了什么事情的对象。
Reducer – 这是一个确定状态将如何变化的地方。
Store – 整个程序的状态/对象树保存在Store中。
View – 只显示 Store 提供的数据。

  1. Redux 有哪些优点

结果的可预测性 - 由于总是存在一个真实来源,即 store ,因此不存在如何将当前状态与动作和应用的其他部分同步的问题。

可维护性 - 代码变得更容易维护,具有可预测的结果和严格的结构。

服务器端渲染 - 你只需将服务器上创建的 store 传到客户端即可。这对初始渲染非常有用,并且可以优化应用性能,从而提供更好的用户体验。

开发人员工具 - 从操作到状态更改,开发人员可以实时跟踪应用中发生的所有事情。

社区和生态系统

三、vue

1.computed 与watch

computed 计算属性 存在缓存,只有它依赖的data数据发生改变它才会改变,应用场景,购物车

watch 属性监听 监听一个值的变化,执行对应的回调,没有缓存,它会影响多个数据,应用场景,搜索框
1.watch观察的名称与data属性名称一致,属性变化,watch函数执行
2.传入两个参数,一个newVal,一个oldVal
3.不需要调用
4.只监听数据值是否 变化,不会监听值的引用地址的改变,要监听引用类型,就需要深度监听了(handler+deep)

区别:

1.功能不同,computed,计算属性,watch监听属性变化,执行回调2.缓存,computed有缓存,data没变化,调用改属性直接从缓存中取值,watch每次监听都要重新执行3.return,computed必须return ,watch不是必须

2.vue的响应式原理

vue组件在渲染时候会把data里的所有数据遍历,用Object.definePrototype()把所有的protype给打上getter/setter。每个vue组件实例都对应着一个watcher实例,它会把组件渲染过程中的protype作为依赖,依赖项的setter更新时候就会通知watcher,从而触发组件的更新。


视图改变数据

3.手写一个数据双向绑定

<input id='input'/><span id='span'></span>

const data = {}const input = document.getElementById('input')const span = document.getElementById('span')Object.defineProperty(data,'text',{
   set(value){
      input.value = value      this.value = value   }
  get(){
    return value  }})input.addEventListen('input',function(e){
   data.text = e.target.value})obj.text = '111' //

4.双向绑定

Vue的模式是m-v-vm模式,即(model-view-modelView),通过modelView作为中间层(即vm的实例),进行双向数据的绑定与变化。

1.通过建立虚拟dom树document.createDocumentFragment(),方法创建虚拟dom树。
2.一旦被监测的数据改变,会通过Object.defineProperty定义的数据拦截,截取到数据的变化。
3.截取到的数据变化,从而通过订阅——发布者模式,触发Watcher(观察者),从而改变虚拟dom的中的具体数据。
4.最后,通过更新虚拟dom的元素值,从而改变最后渲染dom树的值,完成双向绑定
https://segmentfault.com/a/1190000006599500

观察者-订阅者:
Observer 数据监听器,把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用Object.defineProperty()方法把这些属性全部转成setter、getter方法。当data中的某个属性被访问时,则会调用getter方法,当data中的属性被改变时,则会调用setter方法。

Compile指令解析器,它的作用对每个元素节点的指令进行解析,替换模板数据,并绑定对应的更新函数,初始化相应的订阅。

Watcher 订阅者,作为连接 Observer 和 Compile 的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数。Dep 消息订阅器,内部维护了一个数组,用来收集订阅者(Watcher),数据变动触发notify 函数,再调用订阅者的 update 方法。执行流程如下:

流程

从图中可以看出,当执行 new Vue() 时,Vue 就进入了初始化阶段,一方面Vue 会遍历 data 选项中的属性,并用 Object.defineProperty 将它们转为 getter/setter,实现数据变化监听功能;另一方面,Vue 的指令编译器Compile 对元素节点的指令进行解析,初始化视图,并订阅Watcher 来更新视图, 此时Wather 会将自己添加到消息订阅器中(Dep),初始化完毕。当数据发生变化时,Observer 中的 setter 方法被触发,setter 会立即调用Dep.notify(),Dep 开始遍历所有的订阅者,并调用订阅者的 update 方法,订阅者收到通知后对视图进行相应的更新。因为VUE使用Object.defineProperty方法来做数据绑定,而这个方法又无法通过兼容性处理,所以Vue 不支持 IE8 以及更低版本浏览器。

5.mvvm

Model:数据层,通过ajax/fetch等api完成客户端和服务器端的Model数据的同步。
View:视图层,是动态模板,展示ViewModel层的数据和状态
把Model用纯JavaScript对象表示,View负责显示,两者做到了最大限度的分离。

ViewModel:把Model和View关联起来的就是ViewModel,ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。

它有两个方向:一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定


image.png

优点:
1.分离视图和模型,降低代码耦合,提高视图或逻辑的重用性。
2.提高可测试性
3.自动更新dom
缺点:
1.bug难调试
2.model过大占用性能
3.维护成本高

6.keep-alive

作用:保存组件的渲染状态,是一种抽象组件,会缓存不活动的组件实例而不是销毁。
过程:
1.获取keep-alive包裹的子组件的name和组件实例
2.查看keep-alive的黑白名单,看看该子组件是否需要缓存,不需要直接返回组件实例,缓存继续往下
3.根据组件的ID和tag生成缓存key,并在缓存对象中查找是否已经缓存了该组件。若存在,则直接从缓存获取,而且要更新该key在缓存this.keys中的位置。
4.查看keep-alive缓存设置最大值,若超过,则删除下标为0的数组
5.最后,将组件的keeAlive设置为true

7.vue不能检测哪些属性变化

数组
使用下标更新数组元素
使用赋值方式改变数组长度
使用下标增删数组元素
解决办法:

Vue.set( target, key, value )vm.items.splice(indexOfItem, 1, newValue)

对象
增删元素
解决办法:

Vue.set(target, propertyName, value);Vue.delete( target, propertyName/index )

8.vue的diff算法

vue的diff

vue与react diff算法的比较
相同点:原始diff算法o(n^3)时间复杂度,循环递归比较每一个节点。现在优化到o(n)时间复杂度,采用先序深度优先遍历。忽略跨级比较,只做同级比较。如果出现跨级操作,则直接删除重新创建一个新的元素。
不同点
1.如果元素className不同,vue认为是不同的元素,会删除重建。react会认为是个同类型节点,只修改节点的属性。
2.同级元素比较,react从左到右比较,vue从两端到中间比较。vue比较更高效。

四、算法

1.去重

[...new Set([1,1,2])] Array.form(new Set([1,1,2]))array.filter((item,index,arr)=>arr.indexOf(item) == index)const obj = {}array.forEach(item=>{
  obj[item] = item})Object.keys(obj)

2.回文

str.split('').reverse().join('')

3.最大公共前缀

查找字符串数组中的最长公共前缀

var longestCommonPrefix = function(strs) {
    if(strs.length ===0){
        return ''
    }else if(strs.length ===1){
        return strs[0]
    }
    let index = ''//假设index是公共字符串
     for(let i = 0;i<strs[0].length;i++){
         let comStr = strs[0].charAt(i)
        for(let j = 1; j<strs.length;j++){
            if(strs[j].charAt(i)!==comStr){
                return index            }
        }
        index += comStr    }
    return index};

4.[1,[2],[3,[4,7,5],4,3]] 扁平化+排序 + 去重

//方法一:flat(Infinity)无限去扁平化 sort排序 new Set()去重[...new Set(arr.flat(Infinity).sort((a,b)=>a-b))]//方法二:toString去扁平化Array.from(new Set(arr.toString().split(',').sort((a,b)=>a-b)))

5.快排

思路:随便获取一个基准,把它从数组中去除(splice)遍历数组,小于基准的元素放左边数组,大于放右边,循环遍历,最终数组长度为1停止

function quickSort(arr){
    if(arr.length <= 1) return arr    const pre = arr.splice(0,1)
    let left = [],right = []
    arr.forEach(item=>{
        if(item < pre){
            left.push(item)
        }else {
          right.push(item)
    })
   return quickSort(left).concat(pre).concat(quickSort(right))}

6.冒泡

7.深浅拷贝

1.浅拷贝

1.Object.assign()
Object.assign() 拷贝只是第一层是深拷贝,后面的都是浅拷贝了,所以只能算浅拷贝

const obj = {
        a:1,
        b:{c:3},
        d:4}const cloneB = Object.assign({},obj) // 这里改变 cloneB.a的值对obj.a的值没影响,改变 cloneB.b.c的值会影响obj.b.c

2.slice concat 这两个方法都不会改变原数组(还有join也不会改变原数组)

const ary = [1,3,5]const cloneAry1 = ary.slice() // ary.slice(0)const cloneAry2 = ary.concat(ary)
2.深拷贝

1.JSON.stringify() JSON.parse()

const obj = { name:"source", child:{ name:"childnm" } } const str = JSON.stringify(obj)const cloneDeepObj = JSON.parse(str)

缺点 1.性能低
2.一些类型无法拷贝,如函数,正则等
3.循环引用无法正确解析

2.递归循环

function deepClone(obj){
  const cloneObj = Array.isArray(obj) ?  [] : {}
  // 如果obj为空直接返回 [] 或者 {}
  if(obj && typeof obj === 'object'){
     for(let key in obj){
        if(obj[key]  && typeof obj[key] === 'object'){
            cloneObj[key] = deepClone[obj[key]]
        }else {
            cloneObj[key] = obj[key]
        }
     }
  }
  return cloneObj}
  1. lodash库 lodash.cloneDeep()

五、TS

核心原则之一:给值的结构进行类型检查

1.ts的基本数据类型

1.boolean  // let a:boolean = true
2.number // let b:number = 123
3.string // let c:string = '123'
4.数组

let d1: number[]=[1,2,3] // 数字数组写法一let d2: Array<number> = [1,2,3]  // 数字数组写法二

5.元祖tuple

let x:[number,string]x = [1,'qw'] // okx = ['qw',1] // Error

6.枚举enum

enum color = {blue,red,yellow}
 let g:color = color['blue']

7.any,任意类型 // let f:any = 2
8.void,与any相反,不是任何类型

函数没有返回值就是voideg: function fn():void{
    console.log(11)}申明void只能赋值undefined和null,没啥意义 const a :void = undefined

9.Null/undefined
默认情况下是任何类型的子类
申明了--strictNullChecks后,只能赋值给自己和void

10.never
3中类型:总是异常、没有返回值的函数,箭头函数
任何类型的子类,可以赋值给任何类型

11.object

declare function create(o:obejct | null) :voidcreate({p:a})// OKcreate(null); // OKcreate(42); // Error

2.类型断言

1.<string>some

const someStr:any = 'hello world'const strleg:number = (<string>someStr).length

2.some as string
react里面的jsx语法只能使用as这种语法

const someStr:any = 'hello world'const strleg:number = (someStr as string).length



作者:lemonzoey
链接:https://www.jianshu.com/p/6fd606f0de05

文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐