胡哥面试视频手录

本文详细介绍了前端面试中常见的知识点,包括如何提高工作效率、面试技巧、项目流程、接口安全、加密算法、前端框架、性能优化等方面。强调了与同事沟通、代码管理和职业规划的重要性,还探讨了Vue.js、Webpack的工作原理以及防抖节流等技术。
摘要由CSDN通过智能技术生成

胡哥面试视频手录

胡哥面试手录

1. 如何提高工作效率?

我是做前端工作的,我会把工作分为三个部分。

比如说:项目前、项目中期和项目后期。

  1. 在项目前期,我们作为前端一定要和 UI 和产品经理进行深度的沟通,争取把每一个需求都弄明白。这样做的好处是避免了咱们后边分工。分工其实是最大的成本的浪费。
  2. 项目中期,在项目开始的时候,不仅仅是个人编写的现有的代码,我们需要把以前的工具包、插件、组件等等跟工作相关的一系列常用的东西都收集好,这样在工作中效率就会提高很多。在工作中,先做重要的工作,再做次要的工作,先实现功能,再进行优化,可以避免期限到了功能还没有实现的局面。
  3. 在项目后期,最重要的是项目的维护,以及项目 bug 的修改。在这个过程当中,如果咱们前期和中期弄得不错,沟通得好、解决得好,那么其实到了项目后期,bug 的出现会明显的降低。即使产生了,解决速度也会变快。后期一定要和后台人员进行充分的沟通,把项目争取用最短的时间部署到咱们的服务器上。

这样的话,我们在前、中、后期三个节点上面的效率就会得到最大的提升。

2. 面试的时候怎么自我介绍比较好?

注:你做自我介绍的时候,兴趣爱好、学历啥的简历上都有,不用拿来介绍。

我叫xxx,我掌握的技能有哪些?如:H5、C3、js、element-plus、node、vue等等。

在工作中我的打包工具用的是什么?如:webpack、vite。

我的版本控制工具(分布式)用的是什么?如:git

说完这些之后,最好加一些,在以前的工作当中,我们前端经常遇到的是什么问题?比如说接口安全的问题、常见的一些攻击的问题、适配的问题等等。

3. 面试官问:“你还有什么想问的吗?”,这个时候该怎么回答?

在这个时候,对于成熟的前端人员来说的话,想问的相对来说多一些:

  1. 咱们公司除了工资以外,还有没有什么补助?
  2. 公司节假日是怎么安排的?
  3. 考勤是怎么计算的?
  4. 五险一金是怎么来计算的?
  5. 咱们公司大概有多少技术部的同事在一起工作?

4. 面试的简历,应该从这五大方面去写:

  1. 个人信息:以简洁明了为主。姓名、学历、毕业院校、电话、邮箱,不要超过6项。

  2. 专业技能:以充实为主。把会的技能多写上一些。

  3. 过往的经历:过往的公司和时间要搭配,不要有空窗。

  4. 项目介绍:一到两年(包装2-3个项目)、三年(3-4个项目)。你工作3年就只有这3个项目吗?回答:我放到简历上的项目,是我认为比较好的,其实其他的一些小的项目,有一些雷同的地方,我没有往上写,所以从中挑选了基本较好的项目。

    注意点:

    1. 一定要有前缀,比如说:京东商城项目。
    2. 针对人群,客户群是什么?
    3. 开发项目的周期?(四个月?五个月?)
    4. 项目中用到的技术栈有哪些?(vue?三个项目可以都用vue,不是三个项目一定要选择三个不同的技术栈)
    5. 写完以上四点内容之后,底下要写150字-200字的项目总述。
    6. 最后,再把你项目中的各个模块,或者你做了哪些模块,你的岗位职责是什么什么样的,你做的模块实现了什么功能?等等这些东西你要写进去。
  5. 自我评价:本人比较善于沟通,团队协作能力比较强,能够完成领导交待前端相关的工作任务。

5. 你的近期和长期的职业规划是怎样的?

注意:聊的时候,一定不要跟人家谈年限,最近两年要干什么。。。

回答:我作为一个前端来说,我近期的职业规划是把咱们公司的项目做好,另外呢,学无止境,把我的技术得以提升。多学一些组件、插件,多学一到两款框架。

对于中长期的规划来说的话,我打算呢将来我再研究一门后台语言,比如说像Java、PHP、Python这些我研究一门,争取研究一门之后,可以更加方便咱们前端跟后端去沟通,为咱们公司更好的去服务。另外呢,我也相信,咱们公司有合理的晋升机制和制度。所以呢,如果将来公司有这样的机会,这种机会放在我跟前,我也是会毛遂自荐,服务好公司。

这就是我近期、中期和长期的一个职业规划。

6. 前端工作中该如何与同事相处、沟通?

  1. 如果工作中遇到问题了,需要和 UI、后台人员去沟通,那么注意了,如果人间帮助你解决了问题了,那你无论如何下班要请人家吃顿饭,而且马上就得兑现,俗话说:“报恩不隔夜”。
  2. 如果是在平时,UI 那小伙子跟我关系不错,后台某个小帅哥跟你关系也不错,平时怎么和人家沟通呢?尽量组织或者参与一些这样同事之间的社交活动,以加深同事之间的友谊。

7. 面试官问,入职之后将如何开展工作,应该怎么回答?

  1. 如果公司录用我了,我将会带着我以前的技术和经验,把这些东西带到咱们的项目中,让咱们的项目更好,更人性化。

  2. 我会快速的掌握咱们现在所写的项目。包括它的路由是怎么架构的,接口的请求是怎么来处理的,还有它里面的一些难点重点的技术,我以前有没有处理过。
    这些都是我们前端人员在刚入职的时候要做的事情。以避免后边在开展工作中遇到这些问题,再去处理就比较难处理了。

  3. 要考虑我是不是需要后台或者其他的技术人员去配合,这个也要有一个正确的评估。为啥要评估这个呢?如果问题到了跟前再去跟后台沟通,这样的话,很可能会打乱后台的工作计划。

进入公司以后,我打算基于以上三个方面开展工作。

8. 面试官:对于加班你怎么看?

注意点:

  1. 咱们互联网 IT 公司加班是一种常态,包括有些公司执行的996或者大小周。
  2. 咱们国家在 IT 行业,一般来说所有人加班基本上都是免费的。可能在一些少数的一些大公司中加班是有加班费的。

回答:

我可以接受加班,不管是自己的原因还是其他的一些什么原因,都是可以免费接受的。

9. 请描述一下,你以前公司的开发流程是怎么样的?

在我们公司以前技术部分这么几个角色:

产品经理、UI、前端人员、后台(后台是一个统称:Java、PHP、Android、iOS都是在写后台的业务逻辑),以上这些角色去进行项目开发。

前端在项目中的地位和工作流程:

产品经理: 当一个项目进来,最先跟这个项目客户去接触的一般是产品经理,产品经理的作用是:和客户进行聊天,聊天的过程中,他会做一个产品需求文档(说白了,就是客户想要什么?),在这里面有一个重要的职责,有时候客户很盲目,他不知道自己的需求到底是什么(比如说他想做一个卖菜的网站,他可能不知道什么是APP,但是他们会使用),这个时候产品经理要做的就是正确引导客户需求。然后,产品经理需要把这些需求跟UI、前端等技术人员去讲明白。

UI: 出了需求文档之后,产品经理会把文档给到UI、前端、后台各一份。UI 把需求文档做成原型图(黑白的,像黑白照片)和设计图,UI在整个工期里面大概要占到三分之一。当设计稿出来后,同时交给前端和后台,因为基本上前端和后端干活是并行的。

前端: 开始根据页面去制作各种各样的效果。或者是DIA(diagram 图表)或者是小程序。在这个过程中后台也在干活。

后台: 后台就要开发它对应的数据管理部分。除此之外,后台还需要去做 API 接口的东西,这个接口是为前端去开发的。

等到前端的页面布局完了,后台的接口也写完了,剩下的就是前端去渲染数据,调用接口里面相对应的数据就可以了。这些完了之后,就是一个测试阶段。

测试阶段: 测试阶段是 UI、前端、后台都需要参与的,如果有专业的测试,他们的工作量就稍微会少一点。都参与可以保证出现问题可以及时得到处理。

部署: 测试完成之后,再把这个项目部署到客户的服务器上去。

基本上整个工作流程就是这样子的。

10. Web 前端岗位工资的基本构成和发放。

如:10k为例,我上家公司是这样发放的:

  1. 基本工资:5000

  2. 岗位工资:如前端 3000

  3. 绩效工资:2000左右(总工资的15%-20% )

    绩效工资是一个考核标准,干得好才有,你负责的部分出了问题会扣一部分或者扣除全部。

  4. 补贴补助:1000以内(话补、餐补、车补等等)。

  5. 社保公积金:一般情况下公积金+社保占 10%左右。

  6. 扣税:5000以上需要交税

真正到手的大概是九千左右的一个范围

11. 公司面临一些问题,我们该何去何从?

  1. 如果这个时候我走了,我会得到什么,我会失去什么?

    我得到了:我得到的肯定是一份新工作,需要吃饭穿衣嘛这个很正常。

    我失去了:我会失去老板和领导对我的信任,俗话说:“患难见真情”,如果我跟他们共渡难关,在工作中共同努力克服了困难,挺过这一关,那我在这个公司的价值会大大提升。而且我的口碑也会好很多。

  2. 每一个员工应该各司其职,尽职尽责把自己的工作做好,做好之后,才能够让公司踏踏实实,放心去解决问题。当然在自己的事情完成得很好的情况下,在力所能及的范围之内去帮助其他的同事。如果一个团队、一个公司的人都有这样的想法,我觉得老板和领导一定会想尽办法去解决公司的困难。

12. 前端找工作,被公司工资压价怎么办?

注:我说15000,HR压榨成9000

做两手准备:

这个时候跟 HR 去较劲,很大概率是会谈崩的。

  1. 回复HR:行,咱们公司既然觉得我是值这个价,那肯定是没有问题的,我也相信我进入公司之后,我会用技术去把咱们的项目做得更好,这个时候我相信公司也会给我涨工资的。
  2. 如果入职了,继续观望市场,如果有更好的机会摆在面前,立马跳槽。

13. async 和 await 的区别

1. 区别

Async:

async 用来定义函数的,异步函数,打印函数名会得到一个promise对象。可以使用函数名称来调用.then方法,如:函数名称.then

async 内部实现,有返回值。其实是内部调用了 promise.resolve(),然后把它转换成一个promise对象。如果出错的话,调用的是promise.reject()。它用catch捕获了一下

Await:

await 理论上来说后面可以跟任意的表达式。但是一般是跟promise的表达式

async function add() {
	const res = await 123
	console.log(res) // 123
	return res
}
console.log(add()) // 成功的promise,值是123

await 等待后边的promise对象执行完毕,拿到了promise.resolve()的返回值之后,在往后继续去执行后边的代码,所以await有一个等待的意思。

await关键字只能写在async的函数里。

await 后边的表达式可能是失败的,await 只能拿到成功的结果,如果失败会向浏览器控制台抛出错误。所以建议把await的代码放到try…catch语句中捕获错误会比较好。

async function test() {
  const res = await Promise.reject(123)
  return res
}
console.log(test()) // Uncaught (in promise) 123
async function test() {
  try {
    const res = await Promise.reject(123)
  } catch (error) {
    console.log('error', error) // 2. error 123
  }
}
console.log(test())
console.log(111) // 1. 111
2. 优点
  • async和await属于ES2016(ES7)的语法。
  • async和await是异步执行的,但是写代码的时候是同步的
  • async和await方式更容易让开发人员维护
3. 补充

promise 和 async await 区别:

  • promise 属于es6的语法,promise是链式调用的
  • promise中包含了catch,而async和await是在函数内自己定义catch
  • promise 提供方法要多一些。如:all, race, any, finally(原型方法), then(原型方法), catch(原型方法)

14. 打包之后 dist 目录过大,该如何解决

命令:npm run build

需要在vue.config.js中配置 entry, output

处理方案:

  1. 打包后,会在dist下生成.map文件,那么这个文件其实是不必要的

    解决:在vue.config.js中配置文件中:productionSourceMap: false。这样就不会生成.map文件了。

  2. 使用组件和路由懒加载

    组件和路由懒加载是按需加载的,这样会降低一些文件的大小。

  3. 对于文件和图片,最好压缩一下。可以使用这个组件(此组件需要安装):compression-webpack-plugin

    1. 导入组件:compression-webpack-plugin

    2. 配置:

      • 最小化代码配置 minisize(true)
      • 分割代码 splitChunks
      • 超过限定值的文件进行压缩 threshold: 文件大小(字节为单位)

15. 接口安全该如何回答

安全问题是一个很宽泛的问题,如:xss和csrf攻击等等。

这里主要谈论的是接口的安全问题。

接口安全:防止传输过程中,数据被抓包。我们会在数据传输的时候(前台传给后台,后台传给前台)进行加密和解密。

常用的加密算法:

aes rsa SHA1 SHA256 md5 … 有很多的算法

胡哥用过的有 aes rsa

aes加密工具:crypto.js 对称加密

aes加密工具的使用:

  1. 安装:npm i crypto-js –save -dev

  2. 在utils工具文件夹下创建crypto.js文件

  3. crypto.js中写入

    import cryptojs from 'crypto-js'
    export default {
        encrypto(str) { // 加密
            // 生成秘钥
            // 秘钥是前端和后端都知道的,但是网络上是不知道的,所以他们截取去了也没有用。比如说我们前端生成秘钥是11111,那么后端也要用11111去解密
            let key = crytojs.enc.utf8.parse('11111') 
            // 生成偏移量 --- 一般都是传进来的字符串
            // 生成偏移量作用:如果字符串内容长度不够,会自动补齐
            let srcs = cryptojs.enc.utf8.parse(str)
            // 加密 --- 参数:偏移量,秘钥,配置加密的参数
            let encrypted = cryptojs.AES.encrypt(str,key,{
                mode: cryptojs.mode.ECB, // 加密模式
                // ECB 表示等量加密,就是说长度跟你的str相同
                padding: crytojs.pad.pkcs7 // 加密、解密的标准
            })
            return encrypted.toString()
        },
        decrypto(str) { // 解密
            let key = crytojs.enc.utf8.parse('11111') 
            // 解密不需要偏移量
            // 加密 --- 参数:偏移量,秘钥,配置加密的参数
            let decrypted = cryptojs.AES.decrypt(str,key,{
                mode: cryptojs.mode.ECB, // 加密模式
                // ECB 表示等量加密,就是说长度跟你的str相同
                padding: crytojs.pad.pkcs7 // 加密、解密的标准
            })
            return decrypted.toString()
        }
    }
    

    注意:

    1. 前端不论加密还是解密都是padding: crytojs.pad.pkcs7,而后端是padding: crytojs.pad.pkcs5
    2. 秘钥长度是8的整数倍(8 的倍数)
    3. 秘钥前后端统一
    4. 详细使用步骤看这个文档,步骤不多
rsa加密工具:jsencrypt 算法

jsencrypt 算法是一种非对称加密算法,它得生成一对秘钥,即公钥和私钥。前后端一样的,加密的时候使用公钥,解密的时候使用私钥

长度可以能是1024也可能是2048。已知破解的密码长度为:768位。

rsa加密工具的使用:

  1. 安装:npm i jsencrypt

  2. 在utils工具文件夹下创建jsencrypt.js文件

  3. jsencrypt.js文件中写入:

    这里是生成公钥 - 加密

    function enjsencrypt(str) {
        var jsObj = new jsencrypt()
        var pubkey = ""
        jsObj.setPublicKey(pubkey)
        return jsObj.encrypt(str)
    }
    

    这里是生成私钥 - 解密

    function denjsencrypt(str) {
        var jsObj = new jsencrypt()
        var prikey = ""
        jsObj.setPublicKey(prikey)
        return jsObj.dencrypt(str)
    }
    
  4. 详解博客地址

对称加密与非对称加密的区别是什么
  1. 对称加密中加密和解密使用的秘钥是同一个;非对称加密中采用两个密钥,一般使用公钥进行加密,私钥进行解密。
  2. 对称加密解密的速度比较快,非对称加密和解密花费的时间长、速度相对较慢。
  3. 对称加密的安全性相对较低,非对称加密的安全性较高。

16. keep-alive

keep-alive 组件缓存,刷新的时候保持状态。

keep-alive 缓存组件,避免组件内的数据重复渲染,直接可以在页面中调用。

注意看描述:我有一个导航栏,一级导航栏新闻、房产两个。二级导航栏,新闻下有体育、财经。我给整个一级导航栏新闻做了组件缓存。如:<keep-alive includes="News"></keep-alive>

问题:我先点击新闻,然后点击体育,我再点击房产。当我点击新闻的时候,我希望它能回到上一次的状态体育。但是它不会。直接停留到新闻页面了。

需求:点击新闻–》点击体育–》点击社会–》回到新闻下的体育页面

解决:

当点击新闻的时候,有两个方法会触发:activated 和 deactivated

在新闻组件中写:

let url;
beforeRouteLeave() {
    url = this.$route.path
}
activated() {
    this.$router.push(url)
}    

胡哥当前视频地址

17. vuex 如何回答?

vuex存放数据的状态

需要安装:npm i vuex

state 公共数据 组件中this.$store.state.变量名

mutations 同步方法 更新state中的数据

actions 异步方法,组件通过this.$store.dispatch(‘方法名’, 参数)

getters 读取state中的数据

commit 状态提交,对mutation进行提交

vuex页面刷新,数据会重置。这里涉及到持久化的问题

解决:

  1. 把vuex中的数据放到localStorage中

  2. 插件vuex-persistedstate 这个插件需要安装,然后在vuex中添加配置。

    plugins: [createPersistedState({
      storage: window.sessionStorage
    })]
    

    persistedstate 本质是使用了localStorage

18. 防抖节流

返回顶部:到达一定距离才会出现。

监听浏览器的滚动时间,返回当前滚动条与顶部的距离。

<div>里面的内容足够多,让其有滚动条出现</div>
<script>
function showTop() {
// 兼容获取滚动条位置
    var scrollTop = document.body.scrollTop || document.documentElement.scrollTop
    console.log('滚动条位置', scrollTop)
}

window.onscroll = showTop
<script>

问题:滚动条滚动时,方法触发太频繁

  1. 防抖

    第一次触发事件,不要立即执行,给出一个事件间隔,比如200ms

    <= 200ms 大于等于200毫秒,没有触发,那就执行函数

    <= 200ms 大于等于200毫秒,再次触发滚动,当前的计时取消,重新计时

    思路:如果短时间内,大量执行事件,那就执行一次

    实现:setTimeout + 闭包

    <div>里面的内容足够多,让其有滚动条出现</div>
    <script>
      function showTop() {
      // 兼容获取滚动条位置
          var scrollTop = document.body.scrollTop || document.documentElement.scrollTop
          console.log('滚动条位置', scrollTop)
      }
    
    function debounce(fnName, delay) { // 实现间隔调用showTop
        let timer = null
        return function() {
            // 1000毫秒以内,这个timer存在了,那么就清除timer
            if(timer) {
                clearTimeout(timer)
            }
            // 1000毫秒以内,这个timer不存在。setTimeout 返回的是一个ID号,从1开始
            timer = setTimeout(fnName, delay) // setTimeout(showTop, 1000)
        }
    }
    
    window.onscroll = debounce(showTop, 1000)
    </script>
    
  2. 节流

    个人感觉这里用节流会更好(沛华的代码)

    原理:在一定时间内只执行一次,降低触发频率

    实现:闭包

    <div>里面的内容足够多,让其有滚动条出现</div>
    <script>
        function showTop() {
        // 兼容获取滚动条位置
            var scrollTop = document.body.scrollTop || document.documentElement.scrollTop
            console.log('滚动条位置', scrollTop)
        }
    function throttle(fnName, delay) { // 实现间隔调用showTop
        let lastTime = 0
        return function() {
            // 获取当前时间
            let nowTime = Date.now()
            // 如果当前时间 - 上一次的时间 < 传进来的时间 
            // 如:100 - 0 < 1000 吗?如果小于1000,那就直接return了
            if(nowTime - lastTime < time) {
                return
            }
            
            // 大于传进来的时间就会执行下面的代码
            fnName.call(this)
            lastTime = nowTime
    
        }
    }
    
    window.onscroll = throttle(showTop, 1000)
    <script>
    

19. 时间复杂度和空间复杂度(非胡哥)

定义:

  1. 时间复杂度:指一个算法从开始执行到结束所消耗的时间。
  2. 空间复杂度:指对一个算法在运行过程中临时占用存储空间大小的一个量度。

20. webpack的工作原理(打包原理)

  1. 从入口文件开始出发,根据入口文件的依赖关系找到所有的模块

  2. 调用各种loader进行处理,如果遇到处理不了的调用babel进行语法转换。(babel-loader core preset)

  3. 如果一些功能性的,如压缩等这个时候就要调用plugins中相应的插件来进行处理

  4. 编辑阶段 —》根据出口的路径,打包多个chunk,如果把css和html等都摘出去了,那么就会打包多个chunk,最后统一生成一个bundle

  5. loader plugin babel 的区别

    • loader用于处理各种类型文件的
    • plugin是用于loader处理不了的,一些功能性的插件
    • babel 是一个 JavaScript 编译器。将es6+的语法转换成浏览器所认识的js代码。babel是一个独立的工具,可以不在webpack中使用。
  6. 工程化:

    狭义上的理解: 将开发阶段的代码发布到生产环境,包含:构建,分支管理,自动化测试,部署

    广义上理解: 前端工程化应该包含从编码开始到发布,运行和维护阶段

webpack的核心概念(天禹+熊键)
  1. 什么是 webpack(熊键)

    • Webpack 是一个模块打包器(bundler)。
    • 在 Webpack 看来, 前端的所有资源文件(js/json/css/img/less/…)都会作为模块处理。
    • 它将根据模块的依赖关系进行静态分析,生成对应的静态资源。
  2. 五大“护法”(熊键)

    • Entry:入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。
    • Output: output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。
    • Loader: loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只能解析 JavaScript)。
    • Plugins:插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
    • Mode:模式,有生产模式 production 和开发模式 development。
  3. 理解 Loader

    • Webpack 本身只能加载 JS/JSON 模块,如果要加载其他类型的文件(模块),就需要使用对应的 loader 进行转换/加载。
    • Loader 本身也是运行在 node.js 环境中的 JavaScript 模块。
    • 它本身是一个函数,接受源文件作为参数,返回转换的结果。
    • loader 一般以 xxx-loader 的方式命名,xxx 代表了这个 loader 要做的转换功能,比如 json-loader。

    loader常处理的文件类型(天禹)

    • 【file-loader】:提取源代码图片资源,到指定位置,可修改文件名等操作。
    • 【url-loader】:与file-loader功能几乎一致,优势是可以对图片进行动态转换base64编码(控制limit属性值可以控制阈值)。
      备注:上述两个loader中url-loader应用比file-loader广泛。
    • 【eslint-loader】:对项目中的js语法进行检查。
    • 【babel-loader】:将es6语法转换为es5语法
      备注1:直接使用只能处理简单的语法,Promise等无法处理。
      备注2:借助polyfill完成高级es6语法的转换,缺点:所有都转换,无法按需转换,生成的js体积大。
      备注3:使用core-js配合polyfill完成按需转换。

    常用的 loader 有(熊键):

    • eslint-loader:审查代码是否存在语法错误。
    • babel-loader:将 es6 以上的语法编译成 es5 及以下的语法。
    • style-loader:是将 css-loader 打包好的 css 代码以<style>标签的形式插入到 html 文件中。
    • css-loader:用于解释 @import 和 url(),并通过 import 后进行解析,通常和 style-loader 结合使用。
    • less-loader:将 less 语法编译成 css 语法。

    问题:

    eslint-loader 配置:“0” “1” "2"代表什么?

    • eslint 要想工作,必须定义配置文件,配置文件有两种写法
      • .eslintrc.*
      • package.json 文件中 eslintConfig
    • 我们以第一种为例,项目根目录新建.eslintrc.js
  4. plugin常用插件

    • 【extract-text-webpack-plugin】:用于提取项目中的css, 最终合并为一个单独的文件。

      备注:上述插件需要配合:css-loader、less-loader两个loader使用,css-loader、less-loader处理之后,交给extract-text-webpack-plugin处理。

    • 【html-webpack-plugin】:自动创建html文件,且自动引入外部资源。配置项如下:

title:"webpack",
filename:"index.html",
template:"./src/index.html"
//用于压缩html
minify: {
	removeComments: true,  //移除注释
	collapseWhitespace: true
} //移除换行
  • 【clean-webpack-plugin】:清空webpack的输出目录,防止其他文件“乱入”。

  • 【MiniCssExtractPlugin 】:用于提取css为单独文件

21. 数组扁平化算法

如:let arr = [1, [2], [3], [4]]

多维数组转成一维数组,递归:逐层遍历数组,数组的每个元素取出,拼接成新数组

// 数组扁平化
let arr = [1, [2, [3, [4]]]]
// 方法一:使用es6的flat()
// console.log(arr.flat(Infinity))

// 方法二:使用递归
function flat(arr) {
let result = []
// 判断是否传进来是数组
if (!Array.isArray(arr)) {
  // 不是数组,就把数据压入result中
  result.push(arr)
} else {
  for (let i = 0; i < arr.length; i++) {
    // console.log(arr[i])
    // 第一次进来这样写:result = result.concat(arr[0])
    // 第二次进来,又是一个数组,那又得调用flat(),重新从上往下执行函数:result = result.concat(arr[1])
    result = result.concat(flat(arr[i]))
  }
}
return result
}
console.log(flat(arr))

视频链接

22. setTimeout时间为0_,程序发生了什么?

// setTimeout(fun, 时间)
setTimeout(() => {
	console.log(11)
}, 0)
console.log(22)

分析:

  • 代码从上往下执行,当执行到setTimeout的时候,setTimeout是同步代码,setTimeout里面的回调是异步代码。
  • 此时会把setTimeout里面的回到函数放入到计时器管理模块中。
  • 同步代码执行完成后,当过了0秒(马上),会把当前的回调放入到宏队列中,如果微队列的代码执行完成后,会执行宏队列中的代码,然后依次执行宏队列中的定时器回调。

事件轮询中有个重要模块:郎金杰

  • 事件管理模块
    1. 定时器管理模块。
    2. dom 时间管理模块。
    3. ajax 管理模块。
    4. MutationsObserver 管理模块。
  • 回调对列:
    • 宏任务:
      • setTimeout,setInterval,ajax,dom事件监听等。
    • 微任务
      • promise中的.then函数,async/await,mutationsobserver等。

23. setTimeout时间误差的解决办法

setTimeout(fun, 1000) 间隔1000毫秒执行

代码的执行:

  1. 同步代码执行时间:会先等待同步代码执行完成,开始执行异步代码。

  2. 异步代码执行时间:在执行异步代码的时候,会有等待的时间,因为要等前面队列中的代码都执行完成之后,才会执行这个setTimeout中的回调。

  3. 响应时间:比如你在美国访问了京东setTimeout的倒计时,那么美国看到的京东倒计时一定有问题,响应过去都得3秒至少,setTimeout没法1秒就执行。

  4. 在现实中setTimeout在执行的时候:等待时间 + 执行时间 + 响应时间。所以在现实中执行基本是大于1000毫秒的。

统计误差时间:

/* 
    获取时间戳的四种方式
      let start = new Date().valueOf()
      let start = new Date().getTime()
      let start = Date.now()
      let start = +new Date()
*/
setInterval(() => {
    let n = 0
    while (n++ <= 1000000) {}
}, 1000)
// 统计误差
// 开始时间
let start = new Date().getTime()
// 程序执行的秒数
let count = 0
// 间隔时间
let inits = 1000
let timer = setTimeout(fun, inits)

function fun() {
    count++
    // 误差时间 = 现在的时间 - (开始时间 + count * 1000)
    // 如果 误差时间 = 计算出来的时间,那就没有误差。
    let errorTime = new Date().getTime() - (start + count * 1000) + 'ms'
    console.log(errorTime)
    // 解决思路:误差时间累加(当误差达到1000ms之后,不要count++,而是+2)。
}

24. js面向对象的相关总结

面向对象(Object Oriented Programming, OOP),是一种编程思想

es5:

创建对象:

  1. function 函数名(){} ==》new 函数名 ==》得到对象(类的实例对象)

  2. 字面量 {}

  3. new Object()

面型对象的三大特征:

  1. 封装:将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据

  2. 继承:子类可以继承父类的属性和方法

  3. 多态:同一个方法,在子类和父类中都可以定义,只是内容不同。

    由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat

OOP的优势:

  1. 维护方便:采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护也只是在局部模块
  2. 代码质量高
  3. 模块化:封装可以定义对象的属性和方法的访问级别,通过不同的访问修饰符对外暴露安全的接口,防止内部数据在不安全的情况下被修改
  4. 易扩展:通过继承,我们可以大幅减少多余的代码,并扩展现有代码的用途

**遍历对象:**for … in

对象转化成字符串:

  • {} ==> string 使用 JSON.stringify()

  • string ==> {} 使用 JSON.parse()

继承:

  1. 构造函数继承(属性继承)

    /* 
    	属性继承:在子类调用父类函数,用call改变this指向 
    */
    
    function Father(uname, age){
      this.uname = uname;
      this.age = age;
      console.log('Father-this',this);
    }
    
    // var o = new Father('张三丰', 22); // 这里的this是Father
    // console.log('o', o);
    
    // 继承
    function Son(uname, age, score){
      // Father(uname, age);这样写的this指向不同,此时Father里面的this指向的是window
      // Father(uname, age);
      // 如果调用Father函数,并且能够把Father里面的this指向儿子的实例对象,此时Father中的this指向的是Son
      Father.call(this, uname, age);
      this.score = score;
    }
    
    var obj = new Son('erzi', 19, 66);
    console.log('obj', obj);
    
  2. 原型链继承(方法继承)

    /*
    	原型链:Son.prototype -> Father.prototype -> Object.prorotype -> null
    	ES6中的extends关键字实现继承,底层仍然用的是原型
    */
    
    // 方法继承 -- 就是原型继承
    function Father() {
      Father.prototype.say = function () {
        console.log('say方法');
      }
    }
    
    // 继承
    function Son() { }
    Son.prototype = new Father();
    Son.prototype.eat = function () {
      console.log('吃饭');
    }
    
    // 如果继承下来父类的方法,那么要把父类实例对象赋值给子类的原型对象,指回构造函数本身
    Son.prototype.constructor = Son;
    
    console.log(Son.prototype);
    console.log(Father.prototype);
    
    var obj = new Son();
    obj.say();
    obj.eat();
    
  3. 组合继承

    /*
        1. 原型继承:继承属性和方法,属性值会重复
        2. 借用构造函数继承:继承属性,解决属性值重复的问题,但是父类的方法不会被继承
    		3. 组合继承:原型继承 + 借用构造函数继承
    */
    
    function Person(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    };
    Person.prototype.sayHi = function () {
      console.log('啊捏哈赛有');
    };
    function Student(name, age, sex, score) {
      // 借用构造函数
      Person.call(this, name, age, sex);
      this.score = score;
    };
    // 改变原型指向 --- 实现继承
    Student.prototype = new Person(); // 不传值
    Student.prototype.eat = function () {
      console.log('吃东西');
    };
    
    // 如果继承下来父类的方法,那么要把父类实例对象赋值给子类的原型对象,指回构造函数本身
    Student.prototype.constructor = Student;
    
    var stu = new Student('小黑', 20, '男', 100);
    console.log(stu.name, stu.age, stu.sex, stu.score);
    stu.sayHi();
    stu.eat();
    
    var stu2 = new Student('小黑2', 20, '女', 1010);
    console.log(stu2.name, stu2.age, stu2.sex, stu2.score);
    stu.sayHi();
    stu.eat();
    
    // 这样就做到了属性和方法都被继承了
    

设计模式:

沿用了java中的设计模式,一共有23种。

  1. 单例模式:一个类只能有一个实例,假如这个实例存在,那么就会沿用当前实例,如果不存在那就创建。
  2. 工厂模式:批量创建对象。
  3. 观察者模式:设立一个观察员或者哨兵,观察是否有值更新。通过Object.defineProperty方法来修改其它的属性。
  4. 订阅模式:发布者通过中间层通知那些订阅者,订阅者收到通知,然后更新相应的数据变化。
es6:

用class作为类名称。主要是js向后台语言靠近。

static 定义静态属性

constructor 构造函数

super 父类构造函数,super必须写在子类的属性前面

extends 继承关键字

25. 高阶函数

定义:指一个函数的参数是函数或者返回值是函数,满足其中一种情况就算是高阶函数。

funA可以接受funB为参数,那么funA就是一个高阶函数。

// add() 就是一个高阶函数
function add(x, y, fun) {
	return fun(x) + fun(y)
}

function fun(x) {
	return Math.abs(x)
}

console.log(add(-2, 3, fun))

常见的高阶函数:map, reduce, filter, sort

还有一个函数柯里化的问题:

定义:柯里化是把接收多个参数的函数转换成多个只接收一个参数的函数。

作用:参数复用

function add(a, b) {
    return a + b;
}
add(1, 2) // 3  普通做法 一次传入两个参数

// 假设有一个 curring 函数可以做到柯里化
function curring(){}
curring(1)(2) // 我们通过这样的方式来接受参数,这样就实现了柯里化
function curring(x) {
    return y => x + y // 返回一个函数,然后函数的x+y作为最终的返回值
}
curring(1)(2)  // => 3

参数复用:

// 我们平时的写法 --- 非参数复用的写法
function myInfo(inf, name, age) {
    return `${inf}${name}${age}`
}
const myInfo1 = myInfo('个人信息', 'ljc', '19')
console.log(myInfo1); // 个人信息:ljc19
// 函数柯里化的写法 --- 参数复用的写法 复用了inf
function myInfoCurry(inf) {
    return (name, age) => {
        return `${inf}${name}${age}`
    }
}
let myInfoName = myInfoCurry('个人信息')
const myInfo1 = myInfoName('ljc', '19')
const myInfo2 = myInfoName('ljcc','19')
console.log(myInfo2); // 个人信息:ljcc119
console.log(myInfo1); // 个人信息:ljc19

博客地址

26. 浏览器引擎Chrome(v8)

v8引擎对内存有限制:

  1. v8引擎是对浏览器设计的,不太可能大规模使用内存
  2. v8垃圾回收也限制了内存

nodejs中对v8引擎的内存限制

  • 64位的操作系统是1.4G
  • 32位操作系统是0.7G

拿官方的一个案例为例,官方是以1.5G内存来进行参考的,1.5G如果要进行垃圾回收,大概是时间是50ms。

如果node要进行一些大内存对象的操作,可以使用buffer来缓冲对象的内存。因为buffer不受限制,因为buffer基于的逻辑是c++的,c++的内存是不受v8引擎限制的。

v8 如何来划分内存和垃圾回收的?

分代回收机制,新生代和老生代

新生代老生代
32位内存大小限制是16M内存大小限制是700M
64位内存大小限制是32M内存大小限制是1.4G
适合周期短的对象(如:操作某个DOM对象)。一般短期对象较多,占用80%以上周期长的对象(如:new Vue() , document, window等,它就一直存在)
垃圾回收采用了Scavenge算法进行操作的。在算法实现时主要采用Cheney算法。Scavenge的缺点:可用内存只有一半,另一半永远是等待的。Scavenge的优点:只保留活跃的对象,不活跃的就消灭了。就是部分清空。先用Mark-Sweep(标记清除,就是现在不清楚,等下一次进行扫描的时候发现还在,就会被清除掉,内容清除掉后,会留下一个空间),后用Mark-Compact(标记整理,就是把标记清楚后的空间合并起来。)

27. 浏览器是如何渲染页面的

cssom: Css Object Model 采用css代码,并将每个选择器呈现为树状结构,是对css样式表的对象化表示。同时还提供了api来操作css。

w3c标准中:

cssom 包括两个部分

  1. Model:描述样式表和规则的模型。如style标签、link标签都定义了css Model
  2. View:和元素视图相关的api

下面是熊键的笔记

  1. DNS 查询/解析
  • 作用:将域名地址解析为 ip 地址
  • 过程:
    • 读取 4 个缓存
      • 浏览器 DNS 缓存
      • 计算机 DNS 缓存
      • 路由器 DNS 缓存
      • 网络运营商 DNS 缓存
    • 1 个递归查询
      • 13 个根域名服务器
  1. TCP 连接:TCP 三次握手
  • 第一次握手:客户端发起,我将要发送请求了,你准备一下

  • 第二次握手:服务端发起,我知道了,准备好了,放马过来

  • 第三次握手:客户端发起,我也知道了,等着

  • 为什么要三次握手?

    • 目的:确保客户端和服务端收、发能力都是正常的
    • 第一次握手:服务端知道客户端发送能力是正常的
    • 第二次握手:客户端知道服务端发送和接受能力是正常的
    • 第三次握手:服务端知道客户端接受能力是正常的
  1. 发送请求
  • 客户端发送请求给服务器

  • 请求报文

    • 请求首行
      • 请求地址
      • 请求方式
      • 查询字符串参数
    • 请求头
      • 携带 cookie(cookie)
      • 携带 token(authorization)
    • 空行
    • 请求主体
      • 请求体参数(POST)
  1. 接收响应
  • 服务端返回响应给客户端

  • 响应报文

    • 响应首行
      • 响应状态码
        • 1xx
        • 2xx
        • 3xx
        • 4xx
        • 5xx
    • 响应头
      • set-cookie 设置 cookie
      • 缓存 cache-control
    • 空行
    • 响应主体
      • 响应数据
  1. 渲染页面
  • 最开始返回 index.html 页面,浏览器解析 html 文件
  • 遇到 DOM 标记,调用 HTML 解析器,将元素解析 DOM 树
  • 遇到 css 标记,调用 css 解析器,解析成 CSSOM 树
    • style 不阻塞
    • link 阻塞(防止闪屏)
  • 遇到 js 标记(script 阻塞),调用 js 引擎,解析 js 代码
    • 可能会操作 DOM,重新调用 HTML 解析器,将元素解析 DOM 树
    • 可能会操作 CSS,重新调用 css 解析器,解析成 CSSOM 树
    • 这个过程就会导致页面重排重绘
  • 将 DOM 树和 CSSOM 树组合生成 render 树
  • 布局 layout
  • 渲染 render
  1. TCP 断开连接:TCP 四次挥手
  • 断开 TCP 连接需要 4 次:客户端发送请求断开两次,服务端返回响应断开两次
  • 第一次挥手:客户端发起,客户端数据发送完毕了,准备断开了
  • 第二次挥手:服务端发起,服务端数据接收完毕了,可以断开了
  • 第三次挥手:服务端发起,服务端数据发送完毕了,准备断开了
  • 第四次挥手: 客户端发起,客户端数据接收完毕了,可以断开了

28. js程序的执行顺序是怎样的?

外部js文件先加载还是window.onload先加载?

外部js文件与body的位置有关

  • 如果js是放在body里,是按照body的先后顺序进行加载的。

  • 如果js在body外(一般指head中),是在body加载完毕后,才加载js。

外部js文件不论是在head中还是在body中,都要优先于window.onload = function(){}执行。如果外部js有定时器等异步代码,就会滞后执行。

29. vue-loader的作用

vue-loader的用法:用来解析和转换vue文件,提取出其中的script、style、template,把他们交给对应的loader去处理。

vue-loader的特性:

  1. 允许为vue的组件的每个部分去使用其他loader。
  2. 允许在vue文件中使用自定义模块和自定义的loader。
  3. 将style和template引用的资源当做模块依赖处理。为每个这样的组件,都模拟出一个scope。
  4. 在开发过程中允许使用热重载。

热重载:"热重载"不是当你修改文件的时候简单重新加载页面。启用热重载后,当你修改 .vue 文件时,所有该组件的实例会被替换,并且不需要刷新页面。它甚至保持应用程序和被替换组件的当前状态!当你调整模版或者修改样式时,这极大的提高了开发体验。

使用场景:

  1. 编译template将编译用作vue的template选项,url转换成webpack的模块请求。
  2. 可以用loader进行预处理(babel scss less等)
  3. 可以允许热重载

30. node可以通过哪些方法可以防止程序奔溃

node 速度比较快,node是单线程的,不稳定,不适合处理复杂业务。

单线程,产生未处理异常,会捯饬程序奔溃。

问题:

调用了req.params

let http = require('http')
let server = http.creawteServer(function(req, res){
    
    let ok = req.params.ok; // params 是undefined。如果是未定义就会导致程序奔溃
    
    res.writeHead(200,{'content-type': 'text/plain'})
    res.end('hello world')
}).listen(8080, '127.0.0.1')

解决方案1:

uncaughtException 来全局捕获Error,捕获之后,可以防止node奔溃
process.on(‘uncaughtException’, function(err){
	打印错误
})

解决方案2:加上try-catch

let http = require('http')
let server = http.creawteServer(function(req, res){
    
    // let ok = req.params.ok; // 把这个未定义的代码拿到外面去
    try	{
        handler(req, res)
    } catch(e) {
        console.log(e)
    }
    res.writeHead(200,{'content-type': 'text/plain'})
    res.end('hello world')
}).listen(8080, '127.0.0.1')

let handler = function(req, res) {
    let ok = req.params.ok; // 同样是未定义,会报错
}

31. http 和 https的区别

http:

http(Hyper Text Transfer Protocol) 超文本传输协议,用于在浏览器和服务器之间传递信息的,http协议以明文的形式传输内容的,数据是不加密的。不适合传输敏感信息。比如:身份证、账号、密码等。

https:

https:(Hyper Text Transfer Protocol over Secure Socket Layer) 即基于SSL的HTTP协议,简单地说就是HTTP的安全版。https是在http的基础上加上了SSL协议。SSL是依靠证书来验证服务器的身份的,它对浏览器和服务器之间通信是加密的。

https协议是由SSL+ http构成的,是可加密的,身份认证的网络安全,比http更安全。

区别:

  1. https协议需要Ca(Certificate Authority 数字认证机构)申请证书,一般情况下免费的证书很少,证书是需要费用的。
  2. http是明文传输的,https是加密传输的。
  3. 端口不同,http是80,https是443。

32. XSS攻击是什么?如何避免?

定义:

XSS攻击又叫CSS(Cross Site Script)跨站脚本攻击。恶意攻击者往web页面中插入html代码,用户请求该页面,嵌入其中的该代码将会被执行,从而达到攻击用户的目的。

一般有三种攻击类型:

  1. 存储型(这个攻击危害很严重)
  2. 反射型
  3. DOM型

解决:

  1. 对用户输入的内容进行过滤,过滤所有的非法字符。
  2. html转义(html转义是将特殊字符或html标签转换为与之对应的字符),内容太多的话要转义的内容会过多,所以有局限性。
  3. 对于链接跳转,要进行内容检测。如:<a href="xxx"/>
  4. 限制输入长度。
  5. 数据进行加密。
  6. http only 只允许http请求的方式。

博客地址

33. csrf 攻击(非胡哥)

CSRF概念:CSRF跨站点请求伪造(Cross—Site Request Forgery),跟XSS攻击一样,存在巨大的危害性,你可以这样来理解:

攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。如下:其中Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户。

解决:

为了防止CSRF攻击,Token要求不能重复,需要含有时间戳信息、签名信息。

如:token = base64(msg)格式化…base64(sha256(“密锁”, msg))

token产生:

  1. token构成。Token要求不能重复,需要含有时间戳信息、签名信息。
  2. token加密:当用户向服务提出访问请求时,产生Token再提交给服务器的时候,服务器需要判断token的有效性(是否过期,签名有效)
  3. token校验
    • token解包
    • 比对签名
    • 判断时间是否过期

博客地址

34. css hack

条件Hack:用来调整页面的兼容性的。css hack 这个兼容性主要是针对IE来说的,因为IE一开始没有遵循W3C的标准。

// 条件Hack的写法
// if条件共包含6种选择方式:是否、大于、大于或等于、小于、小于或等于、非指定版本
<!--[if <keywords>? IE <version>?]>
HTML代码块
<![endif]-->

兼容性:浏览器品牌不同,在解释css的时候,解释方式不一样,所以样式不兼容的问题。

注意:尽可能减少对CSS Hack的使用。Hack有风险,使用需谨慎

博客地址

35. src和href的区别

请求资源类型不同

  1. href是Hypertext Reference的缩写,表示超文本引用。用来建立当前元素和文档之间的链接。常用的有:link、a。<link href="reset.css" rel=”stylesheet“/>
  2. src是source的缩写,在请求src资源时会将其指向的资源下载并应用到文档中,常用的有script、img、iframe。

作用结果不同

  1. href 用于文档和资源之间确立联系。
  2. src 用于替换当前内容。

浏览器解析方式不同

  1. 用href,浏览器会识别该文档为css文件,就会并行下载资源并且不会停止对当前文档的处理。
  2. 用src,会暂停其他资源的下载和处理,知道将该资源加载、编译、执行完毕,将所指向资源应用到当前内容。这也是为什么建议把js脚本放在底部而不是头部的原因。

36. link和@import的区别?(非胡哥)

  1. link属于html标签,而@import是css提供的。
  2. 页面被加载时,link会同时被加载,而@import引用的css会等到页面被加载完再加载的。
  3. 兼容性问题:@import只在IE5以上才能识别,而link是html标签,无兼容性问题。
  4. 权重问题: @import的权重要高于link。
  5. DOM操作: DOM可以操作link中的样式,而不可以操作@import中的样式。

37. 胡哥快排

function quickSort(arr) {
  if (arr.length < 1) {
      return arr
  }
// 基准值(得到一个整数) --- 使用Math.floor是为了处理除不尽的情况
let provotIndex = Math.floor(arr.length / 2)
// 求得基准值对应的元素 --- 从基准下标开始截取,截取一个
var provot = arr.splice(provotIndex, 1)[0]

// 遍历数组和基准数进行对比
let left = []
let right = []
for (let i = 0; i < arr.length; i++) {
  if (arr[i] < provot) {
    left.push(arr[i])
  } else {
    right.push(arr[i])
  }
}
// console.log(left, right)
  return quickSort(left).concat([provot], quickSort(right))
}
let arr = [2, 3, 4, 5, 3, 2, 1, 5]
const result = quickSort(arr)
console.log(result)

38. 边界塌陷(外边距重合)

同时给兄弟/父子设置上下边距,理论上是两者之和,实际上只有一半,这种现象叫作边界塌陷。

如:给box1的margin-bottom: 20px,给box2的margin-top: 20px; 理论上应该是40的边距,实际上只有20px的边距。

<head>
    <style>
      .box1 {
        width: 100px;
        height: 100px;
        border: 1px solid red;
        margin-bottom: 20px;
      }

      .box2 {
        width: 100px;
        height: 100px;
        border: 1px solid green;
        margin-top: 20px;
      }
    </style>
</head>
<body>
	<div class="box1"></div>
	<div class="box2"></div>
</body>

注意:

  1. 浮动元素和定位元素是不会发生边界塌陷的。

  2. 这种现象一般发生在块级元素的垂直方向

解决方案:

  • margin 同为正数或者同为负数的时候,取的是绝对值大的数。

  • 如果一正一负的时候,求的是和,如给box1的margin-bottom: 20px,给box2的margin-top: -20px; 那么最终边界值为0。

2022年8月24日10: 17: 30

未完待续。。。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值