当前位置: 首页 > news >正文

前端面试总结

1.引言

    最近参加了大量的招聘会,投递了大量的简历,整整体会了从“随便找个厂上一下”——“还是的找个大厂”——“没人要”——“急了急了,海投一波”——“工资有点尬”——“海投中…”。简单说一下自己的一些感受吧,现在的前端属实有点尴尬:
在这里插入图片描述
前端的基础教程特别多,最开始本来是觉得自己有这断断续续3年的编程经历还算有优势,可惜很多公司需要的是那种把面试玩明白的,知道后来我才发现原来前端是有着一个30w字的简历总结的,那里面涵盖了大量的前端面试题,甚至基本上我参加的面试或者笔试都有相关的题目。我面过多益网络(这厂背后总有一股不是很让人向往的知乎风评),多益网络的面试虽然可能繁杂一下,但是多益问的基础还是比较广泛的,更加适合那些从前端还没有炸就开始学的,面过一些类似“冲业绩的”(懂的都懂),还有一些直接拿着双飞本科说事的。

2.一点点体会

    总结一下就是前端教程很多,都偏向于应用上,实际上公司需要的是那一种能够深挖原理的(比如可以看看《你不知道的JavaScript》,技术群别人推荐的),如果现在还不懂原理的话找到理想工资的前端工作还是很有难度的。
    大概说一下我个人的情况吧,本科双飞,强调这个可能学历高的没有体会到这个的劣势,一谈工资面试官就会拿学历压薪资,然后大学生创新创业和同学组建过公司,这大概算得上自己最耀眼的经历吧,然后过了六级分不高,专业差不多前十(当然不保研这个实际作用应该不大),然后动手能力可能强一些,在公司的时候一起接过外包,自己也接过学妹学弟的单子(虽然钱不多),差不多几天一个全栈小demo的那种。实际上,面试官可能比较烦这种写demo,影响编码规范也是有可能的。主要是可能因为自己去年四月份选择了考研,然后在经验上或者是技术变化的跟随上有一些不足,这里我觉得更为核心的还是实习经验上,自己创新创业的一年好像不是很能够被认可,但是在我个人看来其实面试我的公司的开发部还没有我们当时的那个人数多。
    我这里不是要批斗什么,也不是报一种就业消极的心态,面试其实也有它的好处。比如给我影响深刻的是,能够提高我们的技术基础,长期写代码的人可能更能体会到,学的东西很快会忘,导致问什么可能当时想不起来,但是给个机会,几分钟搜一下就能明白(比较经典的就是某个公司问我的axios拦截重复请求问题)。还有一些公司的面试官比较友好,他们会暗示性的让你回答他想要的答案,比如:真的吗?这个时候往往就是你错了,你得记下来回去试试、搜一搜、再积累积累。更好一下的面试官直接给你讲解少量的知识,里面很多都是你理解错了的或者理解不充分的,比如多益的JSON.parse()拷贝的浅拷贝的理解,一零跃动promise的then方法其实有着第二个参数err触发到catch。还有就是面试的碰壁能够激励自己去学历更多的理论知识,总不能一家不知道直接下一家也不知道吧,想想都觉得尬,面试官还能够提示一些关键词,比如快狗打车的ast抽查,diff,模板渲染等,在外界的刺激下确确实实能够学习更多理论。从想——搜——敲/复制——调试——经验积累(什么可以实现,需要借助什么),转变为想——涉及原理——封装——优化——编码决策。

3.建议

    如果找工作的话,建议还是多看看理论,当然编码能够加强记忆和理解。如果你已经有很好的编码经验,建议还是多看看原理,那才是提高薪资的敲门砖。如果你觉得不能够很好的表达,多看看原理相关的文章,比如去大佬的公众号或者是掘金,csdn更偏向于代码编写和框架依赖的使用(至少热度上是这么表现的)。

4.面试难题总结

    今天就花点时间把这些原理给学了,不再下次一定。主要包括如下几点:
(1)原型和原型链;
(2)koa原理;
(3)promise原理;
(4)nodejs模块化原理;
(5)node events;
如果有不对的欢迎在评论区指点指点带我一手,非常感谢。

4.1原型和原型链

    可能你在layui的部分源码中看到过,知道它是用来挂载属性和函数的,但这过面试还不够。原型和原型链其实还牵扯了JS内部很多的概念问题:constructor、函数、对象、prototype、_proto_、原型对象、原型、原型链。
    object.constructor也就是拿到一个对象的构造函数,函数和对象有一种说话是:“函数即对象”,Function 函数和Object函数都是JS内置对象(内部类),函数其实就是Function这个内置对象的实例对象,因此说函数即对象。函数与对象的区别:功能上,函数强调对一种操作的封装,对象则更强调实例,更偏向于是一个功能被丰富的变量。
    prototype出现的原因:当我们使用一个对象构造出两个不同的实例对象,然后给实例对象分别挂载上相同的方法,由于被挂载的方法分属于不同的对象,那么对出现student1.getName() != student2.getName();同时带来的问题是直接占用了两块内存空间,这么玩的话也有点尬,明显封装度不是很够,还有比如我们利用Array引用对象构造了一个arr的实例对象,这个实例就可以使用到pop、push、splice等一系列方法,其实就是prototype的作用,也就是常理解的挂载属性和方法;
    原型对象即创建自己的构造函数所在的类中的prototype,接下来分析一下constructor的属性的位置,既然constructor在每个实例对象都可以获取到,那刚好可以把这个constructor挂载到prototype里面,实际上,恰好也是这么处理的。然而问题来了,是否可以中途修改constructor达到修改所有的?(答案是不能)
在这里插入图片描述
这个时候__proto__就来了,那就能够指向原型对象的prototype(也就是原型对象):
在这里插入图片描述
接下来再来理解原型链就轻而易举啦,student1的 __proto__可以指向其构造函数内部的prototype属性(原型对象),这玩意本身它就是一个对象,并且是一个Function类构造的实例,那么Function本质又是Object类构造的,因此就能够一步步向上查找原型对象,这就是所谓的原型链,student1.__proto__可以找到Student.prototype,
student1.proto.__proto__可以找到new Student函数的原型,也就是Object.prototype,为了里面原型链的循环,取对象的原型为null:
在这里插入图片描述

4.2 koa原理

    这个题目确实有些难,经过一些尝试,我把我的理解分享出来,官网上主要是介绍一些基本信息:
在这里插入图片描述
差不多说的是koa是一个开发express的团队设计的新的web框架,以更小更快更健壮为目标来搭建web应用或者是api,可以摆脱回调的使用增加错误处理,核心部分没有捆绑任何中间件,能够更优雅的写服务端应用。像没说一样,需要关注的还是原理,经过一些文章的学习,明白了koa的核心文件:application.js、context.js、request.js以及response.js,分别针对应用搭建、上下文(ctx),请求体和响应体。结构图如下:
在这里插入图片描述
aplication继承nodejs里面的events模块(用于事件统一管理的,对应第五个,先不细说),简单说一下程序执行流程,由application里面封装的listen方法结合调用new koa().listen 传入的参数来开启一个nodejs服务,接着处理middleware中间件,然后将洋葱模型串联起来并执行,返回响应。这里先挖一下洋葱模型的实现原理,为什么要是用洋葱模型?其实我并没有找到我能接受的答案,网上说的是能够保证中间件的顺序执行,为什么会顾及中间件的这个执行呢,因为中间件之间可能会存在相互引用,使用洋葱模型结合koa-compser里面的promise串联机制能够保证函数的顺序执行,可能也是为了抽离出思想或者是借鉴了某些非计算机领域的思想来这么称谓的吧,我觉得这个概念有点强制话主要是因为在koa里面的middleware只有一个next能够调用下一个中间件,并没有说能够自由调用内部中间件,因为自己经验还是比较有限,可能对于代码思想上的概念还缺乏相当程度的理解:
在这里插入图片描述
于是找来了一个项目看看(尝试理解):
在这里插入图片描述
是否觉得还可以挣扎一下?对,将middleWare强行用一个函数套在一起返回,当然实测发现不行,因为已经脱离了koa框架的执行机制和对外暴露的接口了。这样就能够举例证明middleware之间顺序执行的必要性,但诚然无法说明洋葱的重要性,不过相比于顺序执行而言,next的参与能够像nextTick()一眼获得到下一个middleware的数据,然后next的调用者内部能够进一步利用更新后的ctx进行后续处理。还是拿着这段经典代码来解释洋葱模型的顺序化处理吧(并没有在实际开发中真正使用到,说真的理解起来不是很顺畅):

const koa = require("koa");
const app = new koa();
app.use(async (ctx, next) => {console.log(1);next();console.log(5);
});
app.use(async (ctx, next) => {console.log(2);next();console.log(4);
})
app.use(async (ctx) => {console.log(3);ctx.body = "hello world";
})
app.listen(3000, () => {console.log("服务启动:localhost:3000");
})
//输出12345

    其实就是利用了promise的异步执行特点,将next封装成一个promise对象,然后利用递归来实现(还是看看源码吧):
在这里插入图片描述
在这里插入图片描述
现在将核心放在递归链上,上面这个递归的理解也需要花点时间,我们先易后难,将函数简化成:

const koa = require("koa");
const app = new koa();
app.use(async (ctx, next) => {console.log(1);
});
app.listen(3000, () => {console.log("服务启动:localhost:3000");
})

在只有一个middleware的情况下,函数步入,肯定会到达上图代码的42行,但由于函数在next处没有执行过,因此直接退出,下面恢复next的使用,函数步入,由于这次next执行,函数递归,就形成了嵌套,相当于将下一个中间件塞到了前一个中间件内部,这里则不是由于i===middleware.length跳出的,而是函数正常执行而结束的,所以这个函数其实算是一种伪递归。接下来分析,为什么函数能够检测出来两次next(),两次next的限制可能是为了防止嵌套深度过大,假设在上面最简单的代码中加上两次next(那个只打印1的代码块),那么还是一样,模仿函数执行,函数步入之后,也就是会重复调用两次depatch(1),也就是会出现第二次的时候i==index的情况。
    回到面试上来,如果再次遇到了这个问题该怎么回答呢?
    答:koa主要是利用promise异步编程抛弃回调函数,不再内置中间件,其中主要包含四大模块,分别是application,context、request、response,在application中封装了一个构造函数,在继承了nodejs的events模块的同时,对外暴露了listen、use、toJSON等接口,listen内部又基于nodejs的原生http模块封装了nodeServer函数,利用use来装载外部自定义的中间件,在中间件机制上,koa使用洋葱模型的思想,先将多个middleware中间件顺序存到一个数组中,然后再利用koa-compose里面的compose方法来保证middleware能够顺序执行,在compose方法中其实利用的是递归的思想,对middleware函数进行遍历同时将函数的next参数修改为执行下一个middleware的函数,当然存在一定的爆栈风险,可以改用倒序遍历中间件的方式重新封装compose方法。对于request和response上采用的是类似于Vue的getter、setter来设置和修改request和response上面的属性,同时结合delegate将request和response代理到context上面,方便获取和函数调用。

4.3 promise原理

    面试官特别喜欢问这个,但我总觉得的不是很好回答(甚至直接被误解为只是使用过,一般听到这儿那你的promise基本就算是没答上来),手写promise也搞过,但不怕被笑话我是背的源码,下面再好好总结总结。首先总结一下promise的特点:
(1)promise一共有三种状态:pending、Fulfilled、Rejected;
(2)promise接受一个函数作为参数,该函数又具有resolve和reject两个函数类型参数,resolve函数能够将pending状态转变成Fulfilled,reject函数能够将peding状态转化成Rejected,在状态转变之后立即触发相对应的then和catch函数(也就是说代码中同时有resolve和reject调用的话,谁在前面调用就谁说了算),最后如果有finally的话,只要转变了都触发;
(3)注意一点(某次被面试官刷新认知),promise的then方法里面是有两个参数的,包括成功的处理方法还有失败的处理方法,如果在成功中调用抛出异常那么函数可以进入catch,这个本质是promise内部做的异常处理,如果是reject并且在then里面写了第二个函数即错误处理函数,那么catch不会触发,并且如果是在then的错误处理函数抛出错误也不会触发catch,其实这是因为,catch本质是一个语法糖,还是通过调用then里面的方法来实现的,也就是(这样也就很好理解了,建议还是不要使用then的第二个参数,否则这个语法糖的意义也就不大了,代码清晰它不香吗):

Promise.prototype.catch = function(fn){return this.then(null,fn);
}

(4)promise在执行的过程中支持链式调用,promise.then可以一直向下调用,就算是中间某处的then或者catch函数有问题也能将状态向下传递,就简单举个例子吧:
在这里插入图片描述
面试官问的话,把这些答出来基本就差不多了,友好一点的面试官可能会问你:“聊聊promise吧”,不友好的:“promise的原理是什么”(这么问有点尬,异步编程确实就是这样的,有点无从下口,但是你就把你的理解说出来应该问题不大,或者面试官没有听到想要的答案也会转入你喜欢的问答模式)。接下来再来说说promise的手写问题,了解以上就比较好理解了,特别是关于为什么进行回调函数的数组封装(我怕自己写的不够完美,借鉴了掘金的大佬的代码,然后微调了一下):

const isFunction = variable => typeof variable === 'function'const PENDING = 'PENDING'const FULFILLED = 'FULFILLED'const REJECTED = 'REJECTED'class MyPromise {constructor(handle) {if (!isFunction(handle)) {throw new Error('MyPromise must accept a function as a parameter')}this._status = PENDINGthis._value = undefinedthis._fulfilledQueues = []this._rejectedQueues = []try {handle(this._resolve.bind(this), this._reject.bind(this))} catch (err) {this._reject(err)}}// 添加resovle时执行的函数_resolve(val) {const run = () => {if (this._status !== PENDING) returnconst runFulfilled = (value) => {while (this._fulfilledQueues.length) {this._fulfilledQueues.shift()(value);}}const runRejected = (error) => {while (this._rejectedQueues.length) {this._rejectedQueues.shift()(error);}}//判断是否是resolve的参数为Promise对象if (val instanceof MyPromise) {val.then(value => {this._value = valuethis._status = FULFILLEDrunFulfilled(value)}, err => {this._value = errthis._status = REJECTEDrunRejected(err)})} else {this._value = valthis._status = FULFILLEDrunFulfilled(val)}}// 为了支持同步的Promise,这里采用异步调用setTimeout(run, 0)}// 添加reject时执行的函数_reject(err) {if (this._status !== PENDING) returnconst run = () => {this._status = REJECTEDthis._value = errwhile (this._rejectedQueues.length) {this._rejectedQueues.shift()(err)}}// 为了支持同步的Promise,这里采用异步调用setTimeout(run, 0)}then(onFulfilled, onRejected) {const { _value, _status } = this// 返回一个新的Promise对象return new MyPromise((onFulfilledNext, onRejectedNext) => {// 封装一个成功时执行的函数let fulfilled = value => {try {if (!isFunction(onFulfilled)) {onFulfilledNext(value)} else {let res = onFulfilled(value);if (res instanceof MyPromise) {// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后再执行下一个回调res.then(onFulfilledNext, onRejectedNext)} else {//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数onFulfilledNext(res)}}} catch (err) {// 如果函数执行出错,新的Promise对象的状态为失败onRejectedNext(err)}}// 封装一个失败时执行的函数let rejected = error => {try {if (!isFunction(onRejected)) {onRejectedNext(error)} else {let res = onRejected(error);if (res instanceof MyPromise) {// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后再执行下一个回调res.then(onFulfilledNext, onRejectedNext)} else {//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数onFulfilledNext(res)}}} catch (err) {// 如果函数执行出错,新的Promise对象的状态为失败onRejectedNext(err)}}switch (_status) {// 当状态为pending时,将then方法回调函数加入执行队列等待执行case PENDING:this._fulfilledQueues.push(fulfilled)this._rejectedQueues.push(rejected)break// 当状态已经改变时,立即执行对应的回调函数case FULFILLED:fulfilled(_value)breakcase REJECTED:rejected(_value)break}})}// 添加catch方法catch(onRejected) {return this.then(undefined, onRejected)}// 添加静态resolve方法static resolve(value) {// 如果参数是MyPromise实例,直接返回这个实例if (value instanceof MyPromise) return valuereturn new MyPromise(resolve => resolve(value))}// 添加静态reject方法static reject(value) {return new MyPromise((resolve, reject) => reject(value))}// 添加静态all方法static all(list) {return new MyPromise((resolve, reject) => {let values = []let count = 0for (let [i, p] of list.entries()) {// 数组参数如果不是MyPromise实例,先调用MyPromise.resolvethis.resolve(p).then(res => {values[i] = rescount++// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilledif (count === list.length) resolve(values)}, err => {// 有一个被rejected时返回的MyPromise状态就变成rejectedreject(err)})}})}// 添加静态race方法static race(list) {return new MyPromise((resolve, reject) => {for (let p of list) {// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变this.resolve(p).then(res => {resolve(res)}, err => {reject(err)})}})}finally(cb) {return this.then(value => MyPromise.resolve(cb()).then(() => value),reason => MyPromise.resolve(cb()).then(() => { throw reason }));}}

4.4 nodejs模块化原理

    node的应用是模块组成的,Node遵循commonjs的模块规范,用来隔离每一个模块的做用域,使每个模块在自身的命名空间中执行。这也是面试遇到的一大原因,光会用一些内置模块和第三方依赖远远不够,提到模块,基本每个语言都有,在js中还有一种es6模块,了解二者的区别也是必要的:
(1)导入导出语法上不同,CommonJS 使用的是 module.exports = {} 导出一个模块对象,require(‘file_path’) 引入模块对象;ES6使用的是 export 导出指定数据, import 引入具体数据。由于前端一般是es6后端一般是CommonJS,仔细回想一下编程时候使用的导入导出不难理解,需要注意的是后端也是可以通过package.json文件配置不同的编规范的;开发规范其实有很多,不过这两种算是比较常用的,es6的模块化其实还依赖于babel,babel能够将还未被宿主环境兼容的es6模块编译成es5(CommonJS),webpack中就用到了babel-loader处理兼容性问题。
(2)模块输出上的区别:CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用,也就是CommonJS输出之后不再受到模块内部变化的干预,而ES6则会改变;
(3)模块加载上,CommonJS 模块是运行时加载,ES6 模块是编译时加载,CommonJS先加载整个模块并生成对象然后读取对象上的方法,而ES6则不需要加载整个模块,利用import加载指定值;
    还是回到问题上来,如果面试官问什么是nodejs的模块化(其实是考察是否看过官方文档,是否了解框架底层工具的原理).
    你可以这么回答:在nodejs中,每一个文件都被视为一个模块,然后利用nodejs的模块包装器函数,保证每一个模块的变量的作用域范围只限制在模块内部,在导出时,nodejs会利用module.exports函数类挂载函数或者变量等,模块除了exports属性外,还有模块id(通常就是模块解析后的文件名)、被调用的子模块children和调用者parent、模块搜索路径path、模块完全解析路径filename以及标识模块是否加载完成的loaded。nodejs中的模块一共被分为三类:系统内核模块、用户自定义模块以及第三方模块。在一个模块调用另一个模块中的函数时,会使用require函数来引入,具体流程是:首先查看文件模块中是否有缓存(模块在第一次加载之后进行缓存),如果有缓存直接使用,如果没有则判断是否是内核模块,如果是内核模块加载、缓存并使用,如果不是需要查找文件模块的位置,在这个过程中,需要分情况来加载,如果是有相对路劲标识,那么直接定位相对路径指向的位置加载,如果是第三方模块,那么会在package.json的main属性下指定文件(通常是app.js)的同级目录的node_modules下找,没有找到再一步一步再上一级的node_modules中找,没有找到则返回查找失败报错。相对路劲下如果没有文件后缀会利用.js、.json、.node依次补充查找,如果判定为第三方包,还是会找package.json中的main属性指定的文件名,不成功则找index.js,index.node,index.json(类似于php的默认查找)。
    这个可能还是不够,面试官可能追问,如果模块之间相互依赖构成了依赖环怎么办?——其实nodejs自己处理了,出现循环依赖主要是模块之间的相互引入,nodejs会返回其中一个模块加载未完成的副本,当然还有可能问到一个问题,既然是缓存,那这个缓存怎么刷新,什么时候失效呢?有多种缓存策略可以了解一下,这里不再详细描述,强制刷新缓存的话可以利用这句delete require.cache[require.resolve("module")];

4.5 node events

    感觉这个问的我挺惭愧的,基本用的都是常用模块,对框架工具的探索或者说是官方文档的阅读明显不足。其实这个也是让你讲讲events的用途。前面提到了koa中直接继承了events模块,从侧面也可以看出events的完善和强大。
    其实,nodejs是事件驱动非阻塞io构建的,所谓的事件驱动指的是,通过有效的方法来监听状态,状态变化了触发响应事件,什么是非阻塞io,这里然后我想起了有一次面试题里面问到了阻塞io与非阻塞io的区别,阻塞io指的是需要内核IO操作彻底完成后,才返回到用户空间执行用户的操作。而非阻塞指的是用户空间的程序不需要等待内核IO操作彻底完成,可以立即返回用户空间执行用户操作,即处于非阻塞的状态,与此同时内核会立即返回给用户一个状态值,就比如nodejs一大优点为处理io密集型工作。
    事件模型使用的是发布订阅模式,在vue的响应式原理中也经常被提到,在实时消息系统中也经常用到,另外websocket和mqtt协议在使用时也会经常被提到。先给出一个使用例子吧:
在这里插入图片描述
    一下子就能想到第一个用途,增强系统的维护能力,在每次有错误的时候能够打印错误日志,甚至还能够借助邮件系统相关的第三方依赖直接发送邮件反馈系统的错误信息,及时对系统进行维护。另外,可以联想到vue里面的子组件像父组件传参,跨模块倒是不如回调函数来的直接(当然把监听器导出去也可以),但是一个模块内部进行传参监听还是可以的,但也需要注意另外一些问题,传来传去太多了系统容易像goto一样,另外监听器还是容易产生一些性能和内存泄漏上的顾虑,不同的时候及时剔除,或者参考下面的常用api最贴切的使用:

emitter.on(eventName, listener)//添加指定事件的监听
emitter.emit(eventName[, ...args])//触发事件
emitter.once(eventName, listener)//绑定一次性监听,类似于vue的v-once
emitter.eventNames()//获取监听器监听的事件的名称
emitter.getMaxListeners()//获取能够设置的监听器上限
emitter.setMaxListeners(n)//设置监听器
emitter.listenerCount(eventName)//获取当前监听器的数量
emitter.listeners(eventName)//获取监听某事件的监听器副本
emitter.addListener(eventName, listener)//追加监听器
emitter.prependListener(eventName, listener)//头插监听器
emitter.prependOnceListener(eventName, listener)//头插一次性监听器
emitter.removeAllListeners([eventName])//不再监听某事件
emitter.removeListener(eventName, listener)//一处指定监听器对指定事件的监听

    这个其实和dom事件比较类似,该有的方法都有,自定义能力强大,甚至有了数组的功能,在面试的时候如果没有使用过也要挣扎一下,比如我虽然没有在项目中真正使用过,但是我知道…可以怎么样,有哪些方法等,就和打游戏一样,“射手被人抓总不能一直逃吧,你说两句还能在薪资上回个血”。

5.求职反问阶段的问题

    我很幸运,考完研之后,有很多好兄弟都入职了大厂,他们给我了很多指点,下面给出一些反问面试官的话语:
(1)表示出你很希望加入公司,问一些关于项目的团队情况,培训方式、入职大概的工作等;
(2)如果你很在意薪资,不要有什么顾虑(虽然有些面经觉得这个有些敏感了,因为背后会有人根据面试情况给你定薪资,比如B站的模拟面试),咱找工作这个都不问有点傻乎乎的,至少自己得有个薪资的线,你可能怕问了别人不给offer,不给就不给下一家;
(3)五险一金中公积金的比例,根据根据好兄弟介绍这里面大概有个1w左右的钱;
(4)住宿和就餐问题,毕竟这也关系到能够攒下多少钱;
注:每次别人给了面试机会,最好把相关的网络信息搜集一下,比如知乎风评、薪资、工作氛围等都初步了解一下,不要套路式的反问,反而可能让别人觉得你有点憨,等下给你来句我们校招简章里面有说明的,你可以去看看,嘿嘿,什么意思应该懂了吧。

相关文章:

前端面试总结

1.引言 最近参加了大量的招聘会,投递了大量的简历,整整体会了从“随便找个厂上一下”——“还是的找个大厂”——“没人要”——“急了急了,海投一波”——“工资有点尬”——“海投中…”。简单说一下自己的一些感受吧,现在的前端属实有点尴…...

Geospatial Data Science (6): Spatial clustering

Geospatial Data Science (6): Spatial clustering 1.Clustering, spatial clustering, and geodemographics 本节涉及空间观测的统计聚类。许多问题和主题都是复杂的现象,涉及多个维度,难以归纳为一个单一的变量。在统计学术语中,我们把这一类问题称为多变量,而不是在…...

蚁群算法优化问题

%%%%%%%%%%%%蚁群算法解决 TSP 问题%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%初始化%%%%%%%%%%%%%%%%%%% clear all; %清除所有变量 close all; %清图 clc; %清屏 m 50; %蚂蚁个数 Alpha 1; %信息素重要程度参数 Beta 5; %启发式因子重要程度参数 Rho 0.1; %信息素蒸发系数 G 20…...

为啥一个 main 方法就能启动项目

在 Spring Boot 出现之前,我们要运行一个 Java Web 应用,首先需要有一个 Web 容器(例如 Tomcat 或 Jetty),然后将我们的 Web 应用打包后放到容器的相应目录下,最后再启动容器。 在 IDE 中也需要对 Web 容器…...

洛谷:P1554 梦中的统计 JAVA

思路:定义一个长度为10的数组,数组下标代表数组元素的数字,比如arr[0]代表数字0.用一个for循环,对每个数先取余再取整,知道取整得到的数为0,说明该数字已经被拆解完了。今天又学了一个输入,原来…...

C++初学笔记整理

目录 1. C关键字 2. 命名空间 1)命名空间的引入和概述 2)命名空间的定义 3)std与命名空间的使用 4).相关特性 3. C输入&输出 4. 缺省参数 1 )缺省参数概念 2)使用及分类 a.全缺省 b.部分缺省 5. 函数…...

记录--在Vue3这样子写页面更快更高效

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 在开发管理后台过程中,一定会遇到不少了增删改查页面,而这些页面的逻辑大多都是相同的,如获取列表数据,分页,筛选功能这些基本功能。而…...

【程序设计与算法(三)】测验和作业题部分答案汇总(面向对象篇)

题目来源:程序设计与算法(三)测验和作业题汇总 文章目录001:简单的swap002:难一点的swap003:好怪异的返回值004:神秘的数组初始化005:编程填空:学生信息处理程序006:奇怪的类复制007:返回什么才好呢008:超简单的复数类009:哪来的输…...

LeetCode 349. 两个数组的交集和 692. 前K个高频单词

两个数组的交集 难度 简单 题目链接 这道题的难度不大,我们可以把数组里的数据存到set里面。这样就完成了排序和去重,然后我们再把一个set里面的数据和另外一个set数据进行比较。如果相同就插入到数组里。 代码如下: 但是这个算法的时间复…...

SpringCloud的五大组件功能

SpringCloud的五大组件 EurekaRibbonHystrixZuulConfig 一、Eureka 作用是实现服务治理,即服务注册与发现。 Eureka服务器相当于一个中介,负责管理、记录服务提供者的信息。服务调用者不需要自己寻找服务 ,而是把需求告诉Eureka &#x…...

剑指 Offer II 016. 不含重复字符的最长子字符串

题目链接 剑指 Offer II 016. 不含重复字符的最长子字符串 mid 题目描述 给定一个字符串 s,请你找出其中不含有重复字符的 最长连续子字符串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子字符串是 “abc”,所以其长度…...

HBase读取流程详解

读流程从头到尾可以分为如下4个步骤:Client-Server读取交互逻辑,Server端Scan框架体系,过滤淘汰不符合查询条件的HFile,从HFile中读取待查找Key。其中Client-Server交互逻辑主要介绍HBase客户端在整个scan请求的过程中是如何与服务…...

Redis学习(一):NoSQL概述

为什么要使用Nosql 现在是大数据时代,过大的数据一般的数据库无法进行分析处理了。 单机MySQL的年代 90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够! 那个时候,更多的去使用静态网站,服务器…...

ESP32设备驱动-MCP23017并行IO扩展驱动

MCP23017并行IO扩展驱动 1、MCP23017介绍 MCP23017是一个用于 I2C 总线应用的 16 位通用并行 I/O 端口扩展器。 16 位 I/O 端口在功能上由两个 8 位端口(PORTA 和 PORTB)组成。 MCP23017 可配置为在 8 位或 16 位模式下工作。 其引脚排列如下: MCP23017 在 3.3v 下工作正常…...

RabbitMQ简介

0. 学习目标 能够说出什么是消息中间件能够安装RabbitMQ能够编写RabbitMQ的入门程序能够说出RabbitMQ的5种模式特征能够使用Spring整合RabbitMQ 1. 消息中间件概述 1.1. 什么是消息中间件 MQ全称为Message Queue,消息队列是应用程序和应用程序之间的通信方法。是…...

【项目设计】高并发内存池(五)[释放内存流程及调通]

🎇C学习历程:入门 博客主页:一起去看日落吗持续分享博主的C学习历程博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记&…...

Git标签与版本发布

1. 什么是git标签 标签,就类似我们阅读时的书签,可以很轻易找到自己阅读到了哪里。 对于git来说,在使用git对项目进行版本管理的时候,当我们的项目开发到一定的阶段,需要发布一个版本。这时,我们就可以对…...

Python面向对象编程

文章目录1 作用域1.1 局部作用域2 类成员权限3 是否继承新式类4 多重继承5 虚拟子类6 内省【在运行时确定对象类型的能力】7 函数参数8 生成器1 作用域 1.1 局部作用域 1,当局部变量遮盖全局变量,使用globals()[变量名]来使用全局变量;2&am…...

【什么情况会导致 MySQL 索引失效?】

MySQL索引失效可能有多种原因,下面列举一些常见的情况: 数据库表数据量太小: 如果表的数据量非常小,则MySQL可能不会使用索引,因为它认为全表扫描的代价更小。 索引列上进行了函数操作: 如果在索引列上…...

Java核心知识点整理之小碎片--每天一点点(坚持呀)--自问自答系列版本

1.int和Integer Integer是int的包装类;int是基本数据类型。 Integer变量必须实例化后才能使用;int变量不需要。 Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值…...

js中new Map()的使用方法

1.map的方法及属性Map对象存有键值对,其中的键可以是任何数据类型。Map对象记得键的原始插入顺序。Map对象具有表示映射大小的属性。1.1 基本的Map() 方法MethodDescriptionnew Map()创建新的 Map 对象。set()为 Map 对象中的键设置值。get()获取 Map 对象中键的值。…...

synchronized从入门到踹门

synchronized是什么synchronized是Java关键字,为了维护高并发是出现的原子性问题。技术是把双刃剑,多线程并发给我带来了前所未有的速率,然而在享受快速编程的过程,也给我们带来了原子性问题。如下:public class Main …...

ubuntu-8-安装nfs服务共享目录

Ubuntu最新版本(Ubuntu22.04LTS)安装nfs服务器及使用教程 ubuntu16.04挂载_如何在Ubuntu 20.04上设置NFS挂载 Ubuntu 20.04 设置时区、配置NTP同步 timesyncd 代替 ntpd 服务器 10.0.2.11 客户端 10.0.2.121 NFS简介 (1)什么是NFS NFS就是Network File System的缩写&#xf…...

算法练习(特辑)设计算法的常用思想

1、递推法 递推的思想是把一个复杂的庞大的计算过程转换为简单过程的多次重复,每一次推导的结果作为下一次推导的开始。 2、递归法 递归算法实际上是把问题转化成规模更小的同类子问题,先解决子问题,再通过相同的求解过程逐步解决更高层次…...

哈希->模拟实现+位图应用

致前行路上的人: 要努力,但不要着急,繁花锦簇,硕果累累都需要过程! 目录 1. unordered系列关联式容器 1.1 unordered_map 1.1.1概念介绍: 1.1.2 unordered_map的接口说明 1.2unordered_set 1.3常见面试题oj…...

苹果手机想要传输数据到电脑怎么传输呢?

苹果手机想要传输数据到电脑怎么传输呢?尤其是传输数据到Windows系统,可能需要使用一些传输软件,那么常用的传输软件有哪些呢?下文将为大家推荐几款常用的苹果手机数据传输常用工具。近期苹果发布了iPhone14系列手机,如…...

Linux 练习四 (目录操作 + 文件操作)

文章目录1 基于文件指针的文件操作1.1 文件的创建,打开和关闭1.2 文件读写操作2 基于文件描述符的文件操作2.1 打开、创建和关闭文件2.2 文件读写2.3 改变文件大小2.4 文件映射2.5 文件定位2.6 获取文件信息2.7 复制文件描述符2.8 文件描述符和文件指针2.9 标准输入…...

自学大数据第四天~hadoop集群的搭建

Hadoop集群安装配置 当hadoop采用分布式模式部署和运行时,存储采用分布式文件系统HDFS,此时HDFS名称节点和数据节点位于不同的机器上; 数据就可以分布到多个节点,不同的数据节点上的数据计算可以并行执行了,这时候MR才能发挥其本该有的作用; 没那么多机器怎么办~~~~多几个虚拟…...

ULID和UUID

ULID:Universally Unique Lexicographically Sortable Identifier(通用唯一词典分类标识符)UUID:Universally Unique Identifier(通用唯一标识符)为什么不选择UUIDUUID 目前有 5 个版本:版本1&a…...

java基础面试10题

1.JVM、JRE 和 JDK 的关系 Jvm:java虚拟机,类似于一个小型的计算机,它能够将java程序编译后的.class 文件解释给相应平台的本地系统执行,从而实现跨平台。 jre:是运行java程序所需要的环境的集合,它包含了…...