2021年初面试总结

125 阅读11分钟

css篇:

1. 盒模型

box-sizing: content-box(W3C盒模型,又名标准盒模型):元素的宽高大小表现为内容的大小。
box-sizing: border-box(IE盒模型,又名怪异盒模型):元素的宽高表现为内容 + 内边距 + 边框的大小。背景会延伸到边框的外沿。

2. css3新特性

word-wrap 文字换行
text-overflow 超过指定容器的边界时如何显示
text-decoration 文字渲染
text-shadow文字阴影
gradient渐变效果
transition过渡效果 transition-duration:过渡的持续时间
transform拉伸,压缩,旋转,偏移等变换 只能是from...to...
animation动画 可以一帧一帧的动画

3. BFC

BFC(Block Formatting Context)格式化上下文,是Web页面中盒模型布局的CSS渲染模式,指一个独立的渲染区域或者说是一个隔离的独立容器。 

  • 触发条件
    • 根元素
    • float的值不为none
    • overflow的值不为visible
    • display的值为inline-block、table-cell、table-caption
    • position的值为absolute、fixed
  • 特性
    • 内部的Box会在垂直方向上一个接一个的放置。
    • 垂直方向上的距离由margin决定
    • bfc的区域不会与float的元素区域重叠。
    • 计算bfc的高度时,浮动元素也参与计算
    • bfc就是页面上的一个独立容器,容器里面的子元素不会影响外面元素。
  • 应用
    • 防止margin重叠
    • 清除内部浮动
    • 自适应两(多)栏布局
    • 防止字体环绕

4. 实现水平垂直居中方案

  • flex
.outer{
    display: flex;
    justify-content: center;
    align-items: center
}
  • position + transform, inner宽高未知
.outer{
    position:relative;
}
.inner{
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
}
  • position + 负margin, inner宽高已知
.outer{
    position: relative;
}
.inner{
    width: 100px;
    height: 100px;
    position: absolute;
    left: 50%;
    top: 50%;
    margin-left: -50px;
    margin-top: -50px;
}
  • 设置各个方向的距离都是0,再将margin设为auto,也可以实现,前提是inner宽高已知
.outer {
    position: relative;
}
.inner {
    width: 100px;
    height: 100px;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    margin: auto;
}

5. 权重计算方式

  • !important > 行内样式 > ID选择器 > 类选择器 > 标签选择器 > 通配符选择器。

6. position属性

absolute 绝对定位,相对于 static 定位以外的第一个父元素进行定位。
relative 相对定位,相对于其自身正常位置进行定位。
fixed 固定定位,相对于浏览器窗口进行定位。
static 默认值。没有定位,元素出现在正常的流中。
inherit 规定应该从父元素继承 position 属性的值。

7. 浏览器字体小于12px的方法

.small-font {
   -webkit-transform-origin-x: 0;
   -webkit-transform: scale(0.5);
}

8. em和rem区别

  • em是相对长度单位,em是相对于父元素来设计字体大小的。
  • rem是CSS3新增的一个相对单位,rem是相对单位,是相对HTML根元素。

js篇

js数据类型

  • JS基本有5种简单数据类型:String,Number,Boolean,Null,Undefined。
  • 引用数据类型:Object,Array,Function。

判断类型

  • typeof、instanceof、Object.prototype.toString.call()

闭包

  • 在函数内部和函数外部搭建起一座桥梁,使得子函数可以访问父函数中所有的局部变量

数组方法

push、pop、shift、unshift、sort、map、forEach、filter、reduce、some、every等

set和weakSet、map和weakMap区别

  • Set
    • 1.成员不能重复
    • 2.只有健值,没有健名,有点类似数组。
      1. 可以遍历,方法有add, delete,has
  • weakSet
    • 成员都是对象
    • 成员都是弱引用,随时可以消失。 可以用来保存DOM节点,不容易造成内存泄漏
    • 不能遍历,方法有add, delete,has
  • Map
    • 本质上是健值对的集合,类似集合
    • 可以遍历,方法很多,可以干跟各种数据格式转换
  • weakMap
    • 直接受对象作为健名(null除外),不接受其他类型的值作为健名
    • 健名所指向的对象,不计入垃圾回收机制
    • 不能遍历,方法同get,set,has,delete

手写题的实现

call

Function.prototype.call = function (context) {
  let fn = Symbol();
  context = context || window;
  context[fn] = this;
  const args = [...arguments].slice(1);
  const result = context[fn](...args);
  delete context[fn];
  return result;
}

apply

Function.prototype.apply = function (context, arr) {
  let fn = Symbol();
  context = context || window;
  context[fn] = this;
  var result;
  if (!arr) {
    result = context[fn]();
  } else {
    context[fn](...arr);
  }
  delete context[fn];
  return result;
}

bind

Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  const _this = this
  const args = [...arguments].slice(1)
  return function F() {
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

快排 (滴滴、百度)

/*
在左边找大数,在右边找小数
交换
 */
function QuickSort(arr, low, high) {
    let left = low
    let right = high
    let basic = arr[low]
    while (left < right) {
        while (left < right && arr[right] > basic) {
            right--
        }
        while (left < right && arr[left] <= basic) {
            left++
        }

        if (left < right) {
            const temp = arr[left]
            arr[left] = arr[right]
            arr[right] = temp
        } else {
            const temp = arr[low]
            arr[low] = arr[left]
            arr[left] = temp

            QuickSort(arr, low, left - 1)
            QuickSort(arr, right + 1, high)
        }
    }

    return arr
}

防抖节流 (滴滴、字节、百度)

防抖是任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行,一般用于输入框实时搜索;节流是规定函数在指定的时间间隔内只执行一次,一般用于scroll事件。

// 防抖
function debounce(fn,time){
    let timer = null;
    return function(){
        if(timer){
            clearTimeout(timer)
        }
        timer = setTimeout(()=>{
            fn.apply(this,arguments)
        },time)
    }
}
// 节流
function throttle(fn,time){
    let canRun = true;
    return function(){
        if(!canRun){
            return
        }
        canRun = false;
        setTimeout(() => {
            fn.apply(this,arguments);
            canRun = true;
        },time)
    }
}

深拷贝

function deepClone(obj) {
    var result = Array.isArray(obj) ? [] : {};
    for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (typeof obj[key] === 'object' && obj[key]!==null) {
          result[key] = deepClone(obj[key]); 
        } else {
          result[key] = obj[key];
        }
      }
    }
    return result;
  }

数组flat

可以看看神三元大佬的文章

 function flattenByDeep(array,deep){
      var result = [];
      for(var i = 0 ; i < array.length; i++){
          if(Array.isArray(array[i]) && deep >= 1){
                result = result.concat(flattenByDeep(array[i],deep -1))
          }else{
                result.push(array[i])
          }
      }
      return result;
  }

数组filter

Array.prototype.filter = function(fn,context){
    if(typeof fn != 'function'){
        throw new TypeError(`${fn} is not a function`)
    }
    let arr = this;
    let reuslt = []
    for(var i = 0;i < arr.length; i++){
        let temp= fn.call(context,arr[i],i,arr);
        if(temp){
            result.push(arr[i]);
        }
    }
    return result
}

实现eventEmitter (百度)

class EventEmitter {
    constructor(){
        this.events = {}
    }
    on(name,cb){
        if(!this.events[name]){
            this.events[name] = [cb];
        }else{
            this.events[name].push(cb)
        }
    }
    emit(name,...arg){
        if(this.events[name]){
            this.events[name].forEach(fn => {
                fn.call(this,...arg)
            })
        }
    }
    off(name,cb){
        if(this.events[name]){
            this.events[name] = this.events[name].filter(fn => {
                return fn != cb
            })
        }
    }
    once(name,fn){
        var onlyOnce = () => {
            fn.apply(this,arguments);
            this.off(name,onlyOnce)
        }
        this.on(name,onlyOnce);
        return this;
    }
}

new

function newParent(){
    var obj = {}; // 首先创建一个对象
    obj.__proto__ = Parent.prototype; // 然后将该对象的__proto__属性指向构造函数的protoType
    var result = Parent.call(obj) // 执行构造函数的方法,将obj作为this传入
    return typeof(result) == 'object' ?  result : obj
}

函数柯里化

function currying(fn,...args){
    if(fn.length <= args.length){
        return fn(...args)
    }
    return function(...args1){
        return currying(fn,...args,...args1)
    }
}
function add(a,b,c){
    return a + b + c
}
add(1,2,3) // 6
var curryingAdd = currying(add);
curryingAdd(1)(2)(3) // 6

promise.all (美团)

Promise.all = function(arr){
    return new Promise((resolve,reject) => {
        if(!Array.isArray(arr)){
            throw new TypeError(`argument must be a array`)
        }
        var length = arr.length;
        var resolveNum = 0;
        var resolveResult = [];
        for(let i = 0; i < length; i++){
            arr[i].then(data => {
                resolveNum++;
                resolveResult.push(data)
                if(resolveNum == length){
                    return resolve(resolveResult)
                }
            }).catch(data => {
                return reject(data)
            })
        }
    })
    
}

promise.retry(字节)

promise.retry 的作用是执行一个函数,如果不成功最多可以尝试 times 次。传参需要三个变量,所要执行的函数,尝试的次数以及延迟的时间。

Promise.retry = function(fn, times, delay) {
    return new Promise(function(resolve, reject){
        var error;
        var attempt = function() {
            if (times == 0) {
                reject(error);
            } else {
                fn().then(resolve)
                    .catch(function(e){
                        times--;
                        error = e;
                        setTimeout(function(){attempt()}, delay);
                    });
            }
        };
        attempt();
    });
};

写一个函数,可以控制最大并发数 (字节)

mapLimit = (list, limit, asyncHandle) => {
    let recursion = (arr) => {
        return asyncHandle(arr.shift()).then(()=>{
                // 数组还未迭代完,递归继续进行迭代
                if (arr.length!==0) return recursion(arr)   
                else return 'finish';
            })
    };
    
    let listCopy = [].concat(list);
    let asyncList = []; // 正在进行的所有并发异步操作
    while(limit--) {
        asyncList.push( recursion(listCopy) ); 
    }
    // 所有并发异步操作都完成后,本次并发控制迭代完成
    return Promise.all(asyncList);  
}

判断A、B数组的包含关系(值和数量),A属于B返回1,B属于A返回2,两者相等返回0,其他返回-1 (字节)

    const a = arrA.sort((a,b) => a- b);
    const b = arrB.sort((a, b) => a -b);
    
    
    const n = a.length >= b.length ? b.length: a.length;
    let isBC;
    if( a.length < b.length ) {
        isBC = 1;
    } else if(a.length === b.length){
        isBC = 0;
    } else {
        isBC = 2;
    }
    for(let i = 0;i < n; i++) {
        if(a[i] !== b[i]) {
            isBC = -1;
        }
    }
    return isBC;
}

var a1 = [4,2,3,1,4]
var a2 = [4,2,3,1,4,5]
console.log(arrayInclude(a1, a2)) // 1

var a3 = [4,2,3,1,4]
var a4 = [4,2,3,1]
console.log(arrayInclude(a3, a4)) // 2

var a5 = [4,2,3,1,4]
var a6 = [4,2,3,1,4]
console.log(arrayInclude(a5, a6)) // 0

var a7 = [4,2,3,1,4]
var a8 = [3,2,3,1,4]
console.log(arrayInclude(a7, a8)) // -1

1234567890 --> 1,234,567,890 (字节)

function format(str) {
    let len = str.length;
    if(len <= 3) {
        return str;
    }
    const arr = str.split('').reverse();
    
    let newArr = [];
    for(let i = 0; i< len; i++) {
        if(i % 3 === 0 && i !== 0 ) {
            newArr.push(',')
        }
        newArr.push(arr[i]);
    }
    
    return newArr.reverse().join('');
}

console.log(format('1234566789'));

编码实现:求二叉树是否存在和值为N的路径 (字节)

给一棵二叉树 和 一个值,检查二叉树中的是否存在一条路径,这条路径上所有节点的值加起来等于给的那个初始值。例如,对于下面的二叉树,如果初始值是 22,那么存在一条路径 5->4->11->2 5 /
4 8 / /
11 13 4 / \
7 2 1 请实现如下这个函数 function hasPathSum(root, sum) { // 请输入答案 };

function hasPathSum(root, sum) {
    if(root.value !== sum) {
        if(root.left) {
          return hasPathSum(root.left, sum - root.value);
        } else if(root.right) {
           return hasPathSum(root.right, sum - root.value)
        } else {
           return false;
        }
    } else {
        return true;
    }
    
}

二叉树的遍历(百度)

// 先序遍历可用于打印树的结构
// 先序遍历先访问根节点,然后访问左节点,最后访问右节点。
preTraversal() {
  this._pre(this.root)
}
_pre(node) {
  if (node) {
    console.log(node.value)
    this._pre(node.left)
    this._pre(node.right)
  }
}

// 中序遍历可用于排序
// 对于 BST 来说,中序遍历可以实现一次遍历就
// 得到有序的值
// 中序遍历表示先访问左节点,然后访问根节点,最后访问右节点。
midTraversal() {
  this._mid(this.root)
}
_mid(node) {
  if (node) {
    this._mid(node.left)
    console.log(node.value)
    this._mid(node.right)
  }
}

// 后序遍历可用于先操作子节点
// 再操作父节点的场景
// 后序遍历表示先访问左节点,然后访问右节点,最后访问根节点。
backTraversal() {
  this._back(this.root)
}
_back(node) {
  if (node) {
    this._back(node.left)
    this._back(node.right)
    console.log(node.value)
  }
}

匹配括号 (伴鱼)

var isValid = function (s) {
  let map = {
    '(': -1,
    ')': 1,
    '[': -2,
    ']': 2,
    '{': -3,
    '}': 3
  }
  let stack = []
  for (let i = 0; i < s.length; i++) {
    if (map[s[i]] < 0) {
      stack.push(s[i])
    } else {
      let last = stack.pop()
      if (map[last] + map[s[i]] != 0) return false
    }
  }
  if (stack.length > 0) return false
  return true
};

浏览器相关

defer和async区别(滴滴、百度、字节)

  • defer:延迟执行。载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后。
  • async:异步执行引入的 JavaScript,加载完成后就执行 JS,阻塞DOM

跨域

  • CORS : Access-Control-Allow-Origin: www.nczonline.net
  • jsonp
  • 代理
  • iframe
    • window.postMessage(ie8)
    • Cross Frame(aba)
    • window.name
  • child与parent
不受同源策略的限制

给接收数据的一方添加事件绑定:addEventListener('message', receiveMessage);

发送数据的一方拿到接收数据一方的window:targetWindow.postMessage("Welcome to unixera.com", "http://iframe1.unixera.com");
  • child与child
    • 设置document.domain为同一级域名
  • window.name
    • window对象的name属性是一个很特殊的属性,在设定了window.name之后,执行location.href跳转,window.name属性仍然不会发生变化,可以通过这种方式实现变量的传递。

重绘和回流 优化

  • 现代浏览器大多都是通过队列机制来批量更新布局,浏览器会把修改操作放在队列中,至少一个浏览器刷新(即16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值。
  • 减少重绘和回流
    • 使用 transform 替代 top
    • 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局
    • 避免使用table布局,可能很小的一个小改动会造成整个 table 的重新布局。
    • 尽可能在DOM树的最末端改变class,回流是不可避免的,但可以减少其影响。尽可能在DOM树的最末端改变class,可以限制了回流的范围,使其影响尽可能少的节点。
    • 避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。
    • 将动画效果应用到position属性为absolute或fixed的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择 requestAnimationFrame
    • 避免使用CSS表达式,可能会引发回流
    • 将频繁重绘或者回流的节点设置为图层
    • CSS3 硬件加速(GPU加速)
    • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
    • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
    • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
    • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

计算机基础

TCP 三次握手

  • 第一次握手:起初两端都处于CLOSED关闭状态,Client将标志位SYN置为1,随机产生一个值seq=x,并将该数据包发送给Server,Client进入SYN-SENT状态,等待Server确认;
  • 第二次握手:Server收到数据包后由标志位SYN=1得知Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给Client以确认连接请求,Server进入SYN-RCVD状态,此时操作系统为该TCP连接分配TCP缓存和变量;
  • 第三次握手:Client收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,并且此时操作系统为该TCP连接分配TCP缓存和变量,并将该数据包发送给Server,Server检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client和Server就可以开始传输数据。

cdn原理

CDN的全称是Content Delivery Network,即内容分发网络。CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应

OSI 七层模型

  • 应用层:文件传输,常用协议HTTP,snmp,FTP ,
  • 表示层:数据格式化,代码转换,数据加密,
  • 会话层:建立,解除会话
  • 传输层:提供端对端的接口,tcp,udp
  • 网络层:为数据包选择路由,IP,icmp
  • 数据链路层:传输有地址的帧
  • 物理层:二进制的数据形式在物理媒体上传输数据

tcp udp 区别

  • UDP
    • 1.无连接
    • 2.面向报文,只是报文的搬运工
    • 3.不可靠,没有拥塞控制
    • 4.高效,头部开销只有8字节
    • 5.支持一对一、一对多、多对多、多对一
    • 6.适合直播、视频、语音、会议等实时性要求高的
  • TCP
    • 1.面向连接:传输前需要先连接
    • 2.可靠的传输
    • 3.流量控制:发送方不会发送速度过快,超过接收方的处理能力
    • 4.拥塞控制:当网络负载过多时能限制发送方的发送速率
    • 5.不提供时延保障
    • 6.不提供最小带宽保障

性能优化

  • 降低请求量:合并资源,减少HTTP 请求数,minify / gzip 压缩,webP,图片lazyLoad。

  • 加快请求速度:预解析DNS,减少域名数,并行加载,CDN 分发。

  • 缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存localStorage。

  • 渲染:JS/CSS优化(避免使用CSS表达式),加载顺序(将CSS样式表放在顶部,把javascript放在底部),服务端渲染,pipeline。

  • 使用http 2.0