Skip to content

Latest commit

 

History

History
622 lines (419 loc) · 22 KB

面试总结.md

File metadata and controls

622 lines (419 loc) · 22 KB

面试总结

css

  1. 如何减少重排和重绘

    1. 读写分离
    2. 样式集中改变
    3. dom离线
      1. 父元素先设置display:none, 在子元素上进行css修改,然后再把display设置回来
      2. documentFrament创建一个碎片,在上面进行完成操作后在加入到dom中
    4. 使用absolute或者fixed使得脱离文档流,成为body元素的另一个子元素
  2. BFC,如何触发BFC?

    BFC是块级元素布局的区域,也是浮动元素和块级元素交互的区域。

    触发BFC的条件:

    1. body根元素
    2. float设置成none以外的
    3. display为inline-block、flex、flow-root(没有任何副作用的方式)
    4. overfloat设置为visible以外的值
    5. 绝对定位的元素
  3. 伪类和伪元素

    1. 伪类是元素在某个特定状态的时候,添加他的样式

      link hover active visited checked focus first-child first-of-type last-child last-of-type nth-child nth-of-type

    2. 伪元素是添加不在文档树的元素,添加他的样式

      after before first-letter first-line selection

  4. position

    1. static
    2. absolute
    3. fixed
    4. relative
    5. sticky
  5. display

    1. none
    2. inline
    3. inline-block
    4. block
    5. flex
    6. grid
    7. flow-root
  6. display:none 和 visible: hidden的区别

    1. render树上没有display:none的元素;二有
    2. 会引发回流,进行重新渲染;只会引发重绘
    3. 不是继承属性,子元素会消失;是继承属性,子元素可以设置显示
  7. 选择器的优先级和权重值

    !important 无限大 内联1000 id选择器100 class选择器、属性选择器、伪类10 标签选择器、伪元素1 通配符0

  8. 介绍flex布局

    flex布局分为主轴和副轴,flex容器的子元素在主轴上依次布局,默认是主轴方向是row、不换行。

    flex容器:

    flex-warp:是否换行

    flex-direction:flex主轴方向

    flex-flow:两者的结合

    justify-content:主轴的对齐方式(flex-start, flex-end,center,space-between,space-around)

    align-items: 副轴的对齐方式(flex-start,flext-end,center,stretch)

    align-content:多个轴线的对齐方式。(flex-start,flex-end,center)

    flex具体项目:

    order:顺序

    flex-grow:多余空间如何分配

    flex-shrink:缩小的比例

    flex-basis:主轴方向初始大小

    flex:flex-grow flex-shrink flex-basis

    align-self:更改自己的对齐方式

  9. @import 和 link的区别

    1. 归属问题:link属于html,@import属于CSS
    2. 加载问题:link可以和html同时加载,@import需要等页面全部加载完毕之后,再被加载
  10. async和defer的区别和联系

    联系:都是异步加载js文件的

    区别:

    1. async可能在任何时刻运行,defer在页面加载完后,
    2. async修饰的多个js文件执行顺序不确定,defer的执行顺序是确定的
  11. CSS常用的优化策略

    1. 文件压缩

    2. 除去多余的css

    3. 内联关键的css

    4. 异步加载非关键的css

      link中ref设置为preload,as设置为style

    5. 减少使用@import,因为会阻止css异步加载

    6. 减少重绘和重排

  12. Css3新特性

    1. 圆角,阴影
    2. 文字特效,渐变
    3. 旋转,缩放,定位,倾斜
    4. rgba, ::selection
    5. 媒体查询
  13. css预处理器的作用

    提供css的复用机制,减少冗余代码,提高css的可维护性

  14. link rel和as

    rel:

    1. preload文加载完之后去加载,有较高的优先级
    2. prefetch 缓存数据,优先级较低,竟可能的多使用,但是会费用户的流量
    3. preconnect 预先建立tcp连接,(tls连接)浏览器不一定执行
    4. dns-prefetch预先解析一个域名,浏览器不一定执行

    as:

    1. style
    2. script
    3. font
    4. fetch
  15. CSS动画的优化

    1. 尽可能多的使用硬件能力,比如3d变形
    2. 尽可能让元素脱离文档流
    3. 减少重排和重绘
  16. 移动端设配的方案

    1. @media
    2. rem
    3. px+viewport
    4. vh/vw

javascript

  1. 基本类型和引用类型的区别

    1. 基本类型存储在栈中,引用类型存储在堆中
    2. 基本类型按值访问,引用类型按地址访问
  2. Map 和WeakMap的区别

    weakmap的键是对象,map可以是任何类型的数据,weakmap是弱引用,只要对对象的引用删除,就会被垃圾回收机制随时清理,weakmap没有clear(),也不能遍历

  3. 深拷贝和浅拷贝

    1. 浅拷贝

      function isArray(val){
      	return Object.prototype.toString.call(val).includes("Array");
      }
      
      function shallowCopy(src){
          let target = isArray(src) ? []: {};
          
          for(let key in src){
              if(src.hasOwnProperty(key)){
                  target[key] = src[key];
              }
          }
          return target;
      }
    2. 深拷贝

      function deepClone(src){
          return JSON.parse(JSON.stringify(src));
      }
  4. 拥有symbol.iterator()数据类型有哪些

    1. String
    2. Array
    3. Map
    4. Set
    5. arguments
  5. typeof可以判断哪些类型

    1. Number、String、Boolean、Bigint、Undefined、Object、Function,Symbol
  6. 判断的时候的类型转换

    1. boolean转换成数字
    2. 只有一个string,另一个为数字,那么string转换为数字
    3. 只有一个是对象,调用valueof的方法,如果不是基本类型,调用toString()转换成字符串,都是对象,比较地址
    4. 同时定义null == undefined,null和undefined和其他类型比较总是false,NaN也总是false
  7. 运算时候的类型转换

    1. 有string,那么转换成string
    2. 有数字
      1. 另一个是基本类型,转数字
      2. 引用类型,转string
  8. 有符号整数的最大数

    Math.pow(2, 53)-1

  9. new的具体过程

    1. 创建一个对象
    2. 绑定this
    3. 执行构造函数
    4. 返回对象
  10. 继承的方法

    1. 原型链继承,

    2. 借用构造函数,无法继承函数和父类原型

    3. 组合式继承,两次调用构造函数

    4. 原型继承

    5. 寄生式继承,函数无法公用

    6. 寄生组合式继承

      function Parent(name){
      	this.name = name;
      }
      
      function Child(age,name){
      	Parent.call(this,name);
      	this.age = age;
      }
      
      let temp = Object.create(Parent.prototype);
      temp.constructor = Child;
      Child.prototype = temp;
    7. class extends

  11. 闭包

    函数可以记住并访问所在的词法作用域,在此法作用域外执行的时候,产生了闭包

    优点:

    1. 避免污染全局变量
    2. 模块化
    3. 防止内存泄漏
  12. js的垃圾回收机制

    1. 对于栈来说,通过改变esp来进行销毁内存的
    2. 对于堆来说,V8把内存分为新生代和老生代,对于新生代,采用副垃圾回收器,将内存分为空闲区和对象区,每次遍历对象区,如果有被引用,就将其复制到空闲区,接着翻转两个区域就可以了,元素经历两次赋值,就将其放到新生代,对于老生代,采用从根元素标记,将能到达的元素称为活动元素,不能到达的就标记为垃圾元素,为了避免内存碎片,将存活的对象都向一端移动
  13. throttle debounce

    function throttle(fn, delay){
    	let time;
        return function(){
            if(time) return;
            time = setTimeout(()=>{fn.apply(this, arguments)}, delay);
        }
    }
    
    function debounce(fn, delay){
        let time;
        return function(){
            if(time){
                clearTimeout(time);
            }
            time = setTimeout(()=>{
                fn.apply(this, arguments);
            }, delay)
        }
    }
  14. 事件循环

    js是单线程的,先执行同步任务,在执行异步队列中的任务,异步任务分为宏任务和微任务。所有异步任务有了结果之后就在自己的相应的队列后面添加事件,当同步任务执行完之后,会检查当前还有没有微任务,有的话执行微任务,没有的话执行宏任务,一直循环即可。

    常见的宏任务: setTimeout、setIntervel,UI渲染、script代码、IO操作

    微任务:promise、mutationObserver

  15. 并发限制下载

    function LimitedTask(){
    	this.task = [];
        this.maxSize = 10;
        setTimeout(run,0);
    }
    
    LimitedTask.prototype.run(){
        if(this.task.length === 0){
            return;
        }
        
        let num = Math.min(this.maxSize, this.task.length);
        
        for(let i = 0 ; i < num; ++i){
            this.maxSize--;
            let task = this.task.shift();
            
            task().then(res=>{
                console.log(res);
            }).catch(err=>{
                console.log(err);
            }).finally(()=>{
                this.maxSize++;
                this.run();
            })
        }
    }
    
    LimitedTask.prototype.push(task){
        this.task.push(task);
    }
  16. 函数柯里化

    function curry(fn){
        return function curryFun(...args1){
            if(args1.length >= fn.length){
                fn.apply(this, args1);
            }else{
                return function(...args2){
                    return curryFun.apply(this,args1.concat(args2));
                }
            }
        }
    }
  17. 浏览器不同页面的通信

    1. BroadcastChannel,onmessage函数获得传递的值,通过postMessage函数传递参数
    2. indexDB
    3. localStorage
  18. import 和require的区别

    require:

    1. 所有代码在模块作用域,不会污染全局
    2. 模块可以加载多次,但是结果被缓存,要重新加载需要删除缓存
    3. 加载顺序就是代码出现的顺序

    import:

    1. import是静态加载的
    2. import读入的变量会提升且是只读的
    3. import是静态执行的,singleton的模式

    区别:

    1. commonjs的模块输出的是值得拷贝,es6输出的是值得引用

    2. commonjs是运行是加载,es6是编译时输出接口

html

  1. doctype
    1. 严格模式,采用w3c规定的标准解析文档
    2. 混杂模式,用浏览器自己的方式解析文档
  2. html5的新特性
    1. 语义化标签
    2. svg canvas
    3. video、audio
    4. web worker:创建一个线程来运行计算密集型或者高延迟的js代码,主要脚本需同源,不能获取window,document对象,与主线程可以通过postMessage来通信
    5. web storage
    6. webSocket,通过事件驱动来接收相应,无需要轮询
  3. 如何优化seo
    1. 设置好title description keyword标签
    2. 图片设置alt标签
    3. 提高服务器相应速度
    4. 减少iframe的使用
    5. 对于SPA,采用服务器渲染的方式

浏览器

  1. web worker

    1. js是单线程的,这意味着同一时间只能执行一个代码,但是html5在浏览器的层面上,提供了运行js的线程,使得脚本可以脱离主线程单独运行。
  2. 用户登录的几种方式

    1. cookie和session的方式

      缺点: 存放sessionid,导致服务器压力过大,sessionid存放在cookie中,无法避免csrf攻击

    2. token

      1. 服务器端不用存放token
      2. token可以放在任何地方,提高了安全性

      常用的token是JWT

      JWT由三个部分组成:header、payload、signature,header保存加密算法,payload保存时间,signature是签名,由header中的加密算法对header和payload进行base64的转义再和服务器私钥加密组成的。当服务器要验证的时候,可以通过header、payload和自己的私钥加密,查看签名是否一致,来验证token是否被修改,以及通过payload来判断是否登录失效

  3. 判断用户机型

    navigator.userAgent

  4. 禁用了cookie可以访问session嘛

    1. 追加到url中
    2. 在数据中包含sessionid
    3. 在http请求头中包含sessionid的字段
  5. get和post的区别

    1. get将参数放在url中,并且有数量的限制,参数必须是ASCII字符,会被浏览器主动cache
    2. post将参数放在request的body中,没有长度的限制,可以是任何类型,不会主动cache
  6. chrome浏览器的进程

    1. 浏览器进程
    2. 渲染进程
    3. GPU进程
    4. 网络进程
    5. 插件进程

vue

  1. Vue的双向绑定

    采用数据劫持和订阅发布者模式,整体来说有4个核心类,observer、dep、watcher、compiler,在vue2中,observer采用Object.defineProperty,对data中的数据绑定getter和setter,进行数据劫持,这个绑定需要判断数据的类型,如果是object那么需要递归绑定,如果是数组,那么需要劫持push等多个函数。同时每一个属性都有一个dep来保存所有watcher订阅,在每一次get,通过判断dep.target来向dep中添加watcher,set的时候调用dep的notify实现所有watcher的更新。在compiler中,对模板进行数据的绑定,同时在绑定每个指令或者{{}}这样的数据的时候,创建一个watcher,设置dep.target为自己,调用get来添加到相应的dep中,这样就可以实现数据的双向绑定了。vue2采用object.defineProperty有个缺点就是无法绑定新添加的数据,或者通过下标访问数据。所以在vue3中,将其替换成了proxy。

  2. Vue computed 和 watch的原理

    1. computed数据通过对data中的数据监听,来修改自己的dirty位,当自己的dirty位为true,就调用自己的getter函数,更新value属性,否则就直接从value中取值
    2. watched只是在属性的dep后面增加了一个watcher,当数据更新的时候调用这个更新函数
  3. Vue nextTick的原理

    先判断浏览器是否支持promise,当支持promise的时候,在异步队列中添加执行nextTickHandle的promise,这样就可以实现在更新dom之后执行自己的nextTickhandle了,当promise不被支持的时候,就采用setTimeout,但是这样会有不必要的渲染,造成性能一定的浪费。

  4. 子组件为何不能修改父组件的props,警告如何发布的?

    1. 在vue中,数据是单向流动的,这样有利于数据观测,在definereactive中,set函数中,判断是否在生产环境,如果在生产环境中,那么判断数据源是不是updatingChildren,是的话,就发出警告。
  5. Vue diff算法

    1. 只进行同层比较

    2. 在旧的和新的vnode的两侧各有一个指针,每一次判断这四个指针指向的vnode是不是同一个vnode,这里就有5种情况,执行相应的操作,指导新的或者旧的指针起始指针越过末尾指针,那么就结束。结束后判断新旧vnode的长度,不一样长,就做相应的删除和插入即可。

      5种情况的详细描述:

      1. 新的vnode和旧的vnode起始指针一样,那么直接patch,起始指针都加加
      2. 新的和就得vnode的末尾指针一样,那么在新末尾节点处patch,指针都减减
      3. 新的vnode的起始指针和旧的vnode的末尾指针一样,那么创建的元素放在旧vnode的起始指针处,vnode起始指针加加,旧vnode末尾指针减减
      4. 旧vnode的起始指针和新vnode的末尾指针一样,那么在新vnode末尾指针处添加vnode,
      5. 根据旧vnode的key值,以index为value的hash表中去寻找当前新vnode的下标,找到就patch同时将这个创建的dom移动到旧vnodeindex对饮dom的前面,否则就创建添加,新vnode加加
  6. spa的理解和优缺点?

    spa就是在web初始化页面的时候加载html、css、js,在用户交互的过程中不会引发页面的跳转或者重新加载,通过路由的方式,实现html的替换,从而和用户交互

    优点:

    1. 不需要对页面重新加载或者跳转,速度快
    2. 减少了服务器的压力
    3. 实现前后端分离

    缺点:

    1. 实现前后跳转,需要自己进行堆栈管理
    2. 首页加载慢
    3. 不利于seo
  7. vue的生命周期

    1. beforeCreate:创建了vue实例,但是不能访问data method computed、watch
    2. created: 创建了vue实例,可以访问data methods computed watch,并且这个时候要访问dom,需要放在nexttick中
    3. beforemount: 编译template成render函数
    4. mounted:已经挂载到dom和渲染
    5. beforeUpdate: 在更新之前
    6. updated:完成dom的重新渲染和打补丁
    7. beforeDestroy: 在组件销毁之前,可以访问所有属性
    8. Destroyed: 组件销毁之后
    9. actived: keep-alive激活的时候调用
    10. deactived: keep-live 停用的时候调用
  8. vue keep-alive

    keep-alive是抽象组件,他并不会作为节点渲染在界面上。他用来缓存组件,有3个属性,include设置白名单,exclude设置黑名单,max设置最大存储的个数,当缓存的个数超过最大个数,那么就采取LRU的机制,替换组件

  9. 路由模式

    1. hash:location上的hash会返回USVString,包括url、#后面的标识符。标识符有以下特点,#后面的标识符不会传送给服务器,标识符改变,浏览器只会跳转到相应的位置,页面不会重新加载,修改标识符也会在浏览器中增加一个记录,点击回退可以返回上个记录。
    2. history:通过pushState,replaceState来修改url,触发popState事件来加载页面的,需要服务器配合
    3. abstract模式
  10. axios

    1. 支持浏览器和node.js
    2. 支持promise风格的封装,
    3. 支持拦截请求和响应
    4. 取消请求
    5. 防止CSRF
    6. 自动转换json数据
  11. vue-router中router view如何渲染

    通过Vue.observable在router实例上创建一个保存当前路由的监控对象current,当地址变化的时候,修改监控对象,router-view中的组件监听current对象的变化后,获取用户注册相应的component,通过h函数渲染成vnode,实现视图更新

  12. v-if 和v-show的区别

    1. v-if通过控制dom节点是否存在来元素的显隐的,v-show通过控制display是否是none来控制元素显隐的
    2. v-if默认是惰性的,只有当v-if是真的时候,才会进行编译,v-show在任何时候都会编译缓存的
    3. v-if切换消耗高,v-show首次渲染消耗高
  13. vue-router路由解析的过程

    1. 导航激活
    2. 在失活的组件中调用beforeRouteLeave
    3. 在全局调用beforeEach
    4. 在更新的组件中,调用beforeRouteUpdate
    5. 在路由配置中,调用beforeEnter
    6. 解析组件
    7. 在激活的组件中调用beforeRouterEnter
    8. 调用全局的beforeResolve
    9. 导航被确认
    10. 调用全局的afterEach
    11. 更新dom
    12. 调用激活组件中beforeRouteEnter中的next,创建的组件作为他的参数
  14. template转化成render函数的过程

    1. template将模板字符串转化成element AST
    2. 将AST转化成静态节点标记,用于虚拟dom的渲染优化
    3. 将AST转化成render函数的字符串

webpack

  1. 有哪些loader

    css-loader、style-loader、eslint-loader、image-loader、file-loader、url-loader、vue-loader

  2. 有哪些plugin

    html-webpack-plugin、servicework-webpack-plugin、minicssExtractPlugin

  3. loader和plugin的区别

    loader:本质是一个函数,webpack只认识js、json文件,通过loader可以将各种格式的文件转换成webpack可以理解的文件。

    plugin:本质上,他是一个js的对象,apply方法会被webpack compiler调用,webpack在不同的生命周期中会广播出许多事件,plugin通过监听这些事件,改变最终的输出结果

  4. webpack的构建流程

    1. 初始化参数。从配置文件和shell命令中获取合并参数
    2. 开始编译。通过上步获得的compiler对象,加载所有插件,执行对象的run函数开始编译
    3. 确定入口。通过entry确定所有的入口文件
    4. 编译模块。从入口文件出发,用loader对所有的模块进行编译,并且对模块中的模块进行编译,直到所有模块都编译结束
    5. 完成模块编译。所有模块都已经编译成最终文件和依赖关系,
    6. 输出资源。根据入口文件和模块的依赖关系,组成一个个由多个模块组成的chunk,再把chunk组成最终的单独文件加入到输出列表中,这步是最后修改文件的机会
    7. 输出完成,将文件输出到文件系统中
  5. webpack开发中,用过哪些可以提高效率的插件

    1. webpack-dashboard展示相关的打包信息
    2. SMP,分析loader和plugin在打包过程中的耗时。
  6. source map怎么用

    webpack 最终生成的代码人是无法读的,通过source map将最终的代码映射会源代码。

    通过nginx为白名单设置.map文件

  7. 文件监听的原理

    可以通过后面加上--watch或者在配置文件中添加watch:true的方式启动文件监听

    原理:

    通过文件轮询的方式访问文件的最后修改时间,如果发生了变化,在aggregateTimeout时间后告诉监听者,进行文件更新

  8. webpack HMR的原理

    webpack在编译期间为每一个需要热更新的entry添加了热更新代码,实际上是与服务器端通信的eventSource,在第一次打开页面的时候,页面和服务器端建立websocket,并且服务器向客服端推送hash值,当监听到文件变化的时候,服务器推送hash值,客服端收到hash值后,对比变化,通过ajax向服务器请求文件列表和hash值,之后通过jsonp请求获得chunk的增量更新,之后通过HotModulePlugin提供的api实现浏览器的无刷新更新。像vue-loader也是通过这个api的。

  9. 文件指纹

    hash:根据整个文档来设置hash的,只要有一个文件被改动了,那么所有文件都会被重命名

    chunkhash:他根据不同entry进行依赖文件更新,生成的chunk来生成对应hash值。

    Contenthash:文件内容不变,hash值不变。