一名菜鸡前端的实习+秋招面经
罗里吧嗦的自我介绍:楼主前端方向,在毕业之前有幸稍微稍微地接触了一下,那个时候觉得前端不就写写页面能有多难,当开始找实习找工作的时候真是脸疼~整个过程还是慢慢成长的,从2020年3月初开始系统的学习,作为一名科研狗也是得益于疫情才能稍微自由地在家从头学习。经过漫长的游击战最后加入了字节这个大家庭,这里真的要感谢字节温柔的hr小姐姐还有牛客网小伙伴们分享的经验贴。刚好有空整理下实习+秋招阶段自己的学习笔记,希望能够帮助到其他前端小伙伴。
前端必须掌握的知识结构
- HTML+CSS:这方面建议多动手练习,了解其中原理,这里推荐一些还不错的网站可以在线练习,根据个人认为的训练难易程度排序
A | 链接 |
---|---|
Flex布局 | http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html |
W3cschool | https://www.w3cschool.cn/codecamp |
在线网页编辑器 | https://jsbin.com/?html,output |
学习CSS布局 | https://zh.learnlayout.com/ |
CSS Battle | https://cssbattle.dev/ |
- JavaScript:这个是重中之重,推荐红宝书-《JavaScript高级程序设计》,书中自有黄金屋,看的时候你会发现有很多东西是看网页或者视频会漏掉的!同样推荐一些同学链接,我的建议是都可以看着学习一遍,一个是巩固然后就是查漏补缺了~
B | 链接 |
---|---|
阮一峰 | https://es6.ruanyifeng.com/ |
廖雪峰 | https://www.liaoxuefeng.com/wiki/1022910821149312 |
- 前端框架:Vue和React,如果是前端初学者并且时间有限可以阅读一下官方文档,熟悉其中原理(好应付面试),时间充裕的朋友可以先看官方文档然后跟着视频一起动手试试(仅个人建议,因为我就是看了官方文档以后没多久就忘了~血的教训,可能还是我太菜了)
C | 链接 |
---|---|
Vue | https://cn.vuejs.org/ |
React | https://react.docschina.org/docs/getting-started.html |
想看相关视频的话,可以去学习(B)之站,多得是选择~
4. 计算机基础:计算机网络是每次必问的,也是我每次跪下的地方,大家要引起重视。然后还有基础的操作系统问题
5. 然后就是NodeJs,数据可视化方法等等
6. 还有重中之重的算法,记得每天都刷题多刷题,去LeetCode每天坚持就好了
个人认为,学到这里并且都能掌握,已经可以找一个蛮8错测工作了~
总结的面经
这里提供一下我从各个渠道总结的面试可能会问的,还有我认为比较重要的一些知识点(可能会有错误的地方,看到的朋友可以评论区指正嘻嘻嘻)
HTML+CSS
- 页面渲染流程
- 为什么CSS要放在头部,js要放在body底部
- js设置为 async 和 defer 的区别
async:立即下载
defer:等文档解析并显示完后再下载 - 为什么js是单线程的
- 因为多线程的复杂性,多线程操作需要加锁,编码的复杂性会增高。
- 如果同时操作 DOM ,在多线程不加锁的情况下,最终会导致 DOM 渲染的结果不可预期。
- 自适应布局:使网页自适应的显示在不同大小终端设备上的新网页设计方式及技术,它需要开发多套界面来适应不同的终端。
- 两栏布局左边固定,右侧自适应,两列等高
- 两栏布局,左右均自适应,两列等高
- 三栏布局多种写法:圣杯、双飞翼
- 水平居中和垂直居中的方式,越多越好
- BFC块级上下文
- 块级格式化上下文,是一个独立的容器,子元素不会影响到外面
- 计算高度时,浮动元素也会被计算
- 产生条件:float不为none;position为fixed和absolute;display为inline-block,table-caption,flex,inline-flex;overflow不为visible
- 可用于清除浮动,解决margin重叠
- margin重叠
- 外边距塌陷:上面盒子margin-bottom和下面盒子margin-top
- 父元素没有border以及padding-top,则父元素的margin-top会和子元素的margin-top重叠
- 解决办法:给父元素设置border或者padding-top;给父元素添加overflow:hidden
- html5和CSS3的新特性
- 响应式布局:一个网站能够兼容多个终端,而不是为了每一个终端做一个特定的版本,需要了解设备像素比dpr以及vm/vh
- 设置文字大小为6px
font-size: 12px; transform: scale(0.5);
- CSS优先级计算
- !importent: 无穷
- 行间样式:1000
- id: 100
- class/属性/伪类:10
- 标签/伪元素:1
对盒模型的理解:box-sizing: border-box|content-box; 要会利用该属性画三角形、菱形、平行四边形等
清除浮动的几种方式
display: none, visibility: hidden, opacity: 0 的区别
块级元素、行内元素、行内块级元素的区别
JS
- DOM绑定事件的几种方式及区别
- 标签上绑定:onClick="change"
- btn.onClick = function(){} //会覆盖
- btn.addEventListener(type, handle, true) //true表示捕获,可以注册多个listener,不会覆盖
- 基本数据类型和引用类型 及 其几种判别方式
- 基本数据类型:null, undefined, number, boolean, string, symbol;存储在栈中
- 引用数据类型:object, array, function, Boolean, Number, String;存储在堆中
- JS的字符串是不能更改的,最大长度2^53 - 1
1. typeof 可判断undefined, number, boolean, string, symbol, function且返回值为对应字符串;null, Array, object返回值为Object 2. new Number(2) instanceOf Number; 不能判断null和undefined;返回值为bool类型 3. Object.prototype.toString.call(obj) === '[object Object]' 都可判别
- const、let和var的区别
- null和undefined的区别
- JS闭包应用及其场景
- 概念:内部函数引用外部函数的参数和局部变量
- 应用场景:节流;防抖;封装私有变量;将多参数函数变为单参数
- 缺点:内存泄漏,根据垃圾回收机制被另一个作用与引用的变量不会被回收,所以在退出函数之前将不使用的局部变量全部删除
- 原型链原理
- 继承的几种方式:对象冒充、原型链继承、组合式继承、寄生组合继承、ES6 需要会写代码
//组合式继承 function ClassW(sColor){ this.color = sColor; } ClassW.prototype.sayColor = function(){ alert(this.color); } function ClassJ(sColor, sName){ ClassW.call(this, sColor); this.name = sName; } ClassJ.prototype = new ClassW(); //校正构造函数 ClassJ.prototype.constructor = ClassJ; ClassJ.prototype.sayName = function () { alert(this.name); }; //寄生组合式 //ES6 class Parent{ constructor(name){ this.name = name; } getName(){ return this.name; } } class Child extends Parent{ constructor(name, age){ super(name); this.age = age; } getAge(){ return this.age; } }
- 事件循环的概念:任务执行顺序
- 事件流:事件捕获,处于目标,事件冒泡
- 事件代理/事件委托:指不在事件的触发目标上设置监听,而是在其父元素设置,通过冒泡机制,父元素可以监听到子元素上事件的触发。
- 箭头函数的作用
- 异步实现方式:Ajax请求、回调、Promise等等,及其各自优缺点
- 防抖节流原理及实现
- 跨域的几种方式:JSONP、WebSocket、CORS、代理
- forEach、for..in、for..of的区别
- 几种常见的设计模式
- 模块加载方案比较:CommonJS 与 ES6
Vue
- 对渐进式的理解:看官网首页
- 对MVVM的理解
- 响应式原理
- Proxy和Object.defineProperty的区别
- proxy可以直接监听对象而非属性
- proxy拦截方法较多,如:has,apply,construct,ownkeys
- proxy返回值为对象实例,而object.property只能遍历对象属性直接修改
- 为什么要有虚拟DOM
- 讲解生命周期:官网超级详细
- Vue-router原理
- 父子组件通信
- 使用组件标签时,在组件内定义props属性
- vue自定义事件:子组件绑定监听事件,父组件通过$emit接收
- 发布订阅库
- slot插槽
- Vuex
- Vue组件中的data为什么需要是函数
- data不能为对象,因为对象是引用类型,组件是可复用的可能会被多个实例同时引用;所以其中一个组件改变data值其他组件也会被影响
- data为函数,返回为对象的靠背,这样每个组件实例都有自己独立的对象,实例之间互不影响。
- Vue2.0和Vue3.0的区别
- Computed和watch的区别
- v-if和v-show的区别
- 使用v-if的时候,如果值为false,则页面将不会有这个html标签生成,切换时会触发组件的销毁和挂载
- v-show值为false时,html标签存在,只是隐藏而已,在dispaly:none和display:block之间切换,适合频繁切换的场景
- Vue+Webpack构建具体过程
- webpack优化
计算机网络
- 讲解浏览器输入url到显示过程
- DNS解析
- TCP三次握手原理及为什么是三次
- 握手失败如何处理
- TCP四次挥手原理及为什么是四次
- http和https的区别
- TCP和UDP的区别
- 对称加密和非对称加密
- 常用状态码
- CSRF和XSS攻击
- Cookie、localstorage、sessionStorage
- http请求方法
- GET: 获取资源
- POST: 传输实体的主体
- PUT: 更新资源,PUT通常指定了资源的存放位置,而POST则没有,POST的数据存放位置由服务器自己决定。
- DELETE: 删除文件
- HEAD: 获取报文首部,HEAD和GET本质是一样的,区别在于HEAD不含有呈现数据,而仅仅是HTTP头部信息,例如url的有效性以及更新的日期时间。
- OPTIONs: 请求web服务器告知其支持的各种功能。可以询问服务器通常支持哪些方法,或者对某些特殊资源支持哪些方法。
- TRACE: 追踪路径,将之前的请求通信返回给客户端
- CONNECT: 要求用隧道协议连接代理,一般为 TLS和SSL
- Get和Post的区别
操作系统
- 进程和线程的区别
- 进程是系统进行资源分配和调度的一个独立单位
- 线程是CPU调度的基本单位
- 区别:
a. 进程有独立的地址空间,一个进程崩溃后不会对其他进程产生影响。
b. 线程有自己的堆栈和局部变量,但没有独立的地址空间。
c. 一个程序至少有一个进程,一个进程至少有一个线程
d. 两者都可以并发执行
- 进程的状态
- 进程状态有就绪,运行,阻塞这三种
- 就绪即一个进程获得了所需资源,一旦得到处理机便可运行
- 阻塞即一个进程正在等待某一事件发生而暂时停止运行
- 运行转为就绪,比如时间片到;运行转为阻塞,比如等待IO请求的返回;阻塞转为就绪,比如IO结束
- 进程调度算法的特点
- 短作业优先,运行时间最短的优先
- 先来先服务,即先来先运行
- 时间片轮转,即每次一个进程运行一个时间片,不断切换
- 优先级,即按优先级运行
- 高响应比,即优先级随着等待时间的增长而提高,抢占式
- 死锁:死锁是指一组争用系统资源或相互通信的进程被阻塞的现象。
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
代码题
代码仅供参考,大家知道哪些是常问的自己准备就好啦~
- 原型和原型链的理解
function Foo() { getName = function () { console.log(1); }; return this; } Foo.getName = function () { console.log(2);}; Foo.prototype.getName = function () { console.log(3);}; var getName = function () { console.log(4);}; function getName() { console.log(5);} //请写出以下输出结果: Foo.getName(); //输出2 getName(); //函数提升大于变量提升所以输出4· console.log(Foo()); //window Foo().getName(); //运行Foo()时,window下的getName已经被覆盖 1 getName(); // 1 new Foo().getName(); //3
- this指向
let obj = { a() { return () => { return () => { console.log(this) } } } } obj.a()()() //obj对象 let obj1 = { a() { console.log(this) }, name: 'jack', showName: this.name //初始化时指向window即为'' } obj1.a() //obj1对象 console.log(obj1.showName) //'' let a = obj1.a; a(); //window
- ** 解构赋值**
//请写出以下返回的结果: function move({x, y} = { x: 0, y: 0 }) { return [x, y]; } move({x: 3, y: 8}); //[3, 8] move({x: 3}); //[3, undefined] move({}); //[undefined, undefined] move(); //[0, 0]
- 事件循环
let promise = new Promise((resolve, reject)=>{ console.log(1); resolve(); console.log(2); reject(); //promise状态变为resolve以后不会再改变,所以不会被catch }); setTimeOut(()=>{ console.log(3); }, 1000); promise.then(()=>{console.log(4);}).then(()=>{ console.log(5); }).catch(()=>{console.log(6)}); console.log(7); //1 2 7 4 5 3
- let、const、var
let x = 1; const y = 2; var z= 3 console.log(window.x); //undefined console.log(window.y); //undefined console.log(window.z); //3
- 变量提升
var bar = 456; function bar() { console.log(123); } console.log(bar); bar = 789; console.log(bar); console.log(bar()); //变量提升以后 var bar = function(){ console.log(123); } var bar; //不会覆盖函数提升 console.log(bar); //输出函数bar的定义 console.log(bar()); // 先运行bar函数输出123,然后输出undefined(因为没有返回值) bar = 456; //会覆盖函数 console.log(bar); //456 bar = 789; console.log(bar); //789 console.log(bar()); //is not a function
- 数组扁平化的方法
function flatten1(arr){ return arr.reduce((re, item)=>{ return re.concat(Array.isArray(item)? flatten1(item):item); }, []); } function flatten2(arr){ //[1,[2,3]].toString() => 1,2,3 return arr.toString().split(',').map(item=>parseInt(item)); } function flatten3(arr){ return arr.join(',').split(',').map(item=>parseInt(item)); } function flatten4(arr){ var res = []; arr.map(item=>{ if(Array.isArray(item)){ res = res.concat(flatten4(item)); }else{ res.push(item); } }); return res; } function flatten5(arr){ while(arr.some(item=>Array.isArray(item))){ arr = [].concat(...arr); } return arr; }
- 数组乱序
function shuffle1(arr){ let len = arr.length-1; while(len){ //相当于向下取证整 let t = (Math.random()*len--) >>> 0; [arr[len], arr[t]] = [arr[t], arr[len]] } return arr; } function shuffle2(arr){ return arr.sort(Math.random()-0.5); }
- promise实现sleep
function sleep(ms){ var temple=new Promise( (resolve)=>{ console.log(111);setTimeout(resolve,ms) }); return temple } sleep(500).then(function(){ //console.log(222) })
- Promise各种输出
Promise.all([]).then(res=>console.log('all')) // all Promise.race([]).then(res=>console.log('race')) // pending
// 输出结果:1 2 error1 error2 console.log('1'); new Promise((resolve, reject)=>{ console.log('2'); throw new Error('error1'); resolve(); }).then(res=>{console.log('3');}) .catch(err=>{console.log(err); throw new Error('error2');}) .then(res=>{console.log('4');}) .catch(err=>{console.log(err);})
//1 2 error1 undefined console.log('1'); new Promise((resolve, reject)=>{ console.log('2'); throw new Error('error1'); resolve(); }).then(res=>{console.log('3');}) .catch(err=>{console.log(err);}) .then(res=>{console.log(res);}) //res为上一个catch的resolve返回 .catch(err=>{console.log(err);})
//输出1 2 3 4 console.log('1'); new Promise((resolve, reject)=>{ console.log('2'); resolve(); throw new Error('error1'); }).then(res=>{console.log('3');}) .catch(err=>{console.log(err); throw new Error('error2');}) .then(res=>{console.log('4');}) .catch(err=>{console.log(err);})
- new、apply、cal、bind实现
function create(){ console.log(arguments) //1. 创建一个新的对象 let obj=new Object(); //获得构造函数 let con = [].shift.call(arguments); //[]为Array构造函数的实例 将类数组转化为真正的数组 //2.链接到原型 obj.__proto__ = con.prototype; //3.绑定this 执行构造函数 let result = con.apply(obj, arguments) //4.返回新对象 确保new出来的是个对象 return typeof result == 'Object' ? result:obj; } var test_create = create(Car, 'a', 'b', 'c'); console.log(test_create) Function.prototype.Mycall = function(){ let context = arguments[0] || window; context.fn = this; let args = [...arguments].slice(1); let result = context.fn(...args); delete context.fn; return result; } //第二个参数为数组 Function.prototype.Myapply = function(){ let context = arguments[0] || window; context.fn = this; let result; //判断是否有第二个参数 if(arguments[1]){ result = context.fn(...arguments[1]); }else{ result = context.fn(); } delete context.fn; return result; } Function.prototype.Mybind = function (context, ...args) { if(typeof this !== 'function'){ throw Error(''); } var self = this; var fBound = function () { var bindArgs = Array.from(arguments); return self.apply(this instance of fBound ? this: context, args.concat(bindArgs)); } fBound.prototype = Object.create(this.prototype); return fBound; }
- 实现instanceOf
- 数组去重
function unique1(arr) { return arr.filter((cur, index, arr)=>{ return arr.indexOf(cur, 0) === index; }) } function unique2(arr) { //Set数据结构,它类似于数组,其成员的值都是唯一的 return Array.from(new Set(arr)); // 利用Array.from将Set结构转换成数组 } function unique3(arr) { return [...new Set(arr)]; } //ES5:借用object,问题:字符和数字,key添加前缀比如 (typeof cur)+cur
- 深拷贝和浅拷贝
//浅拷贝 //1.使用Object.assign({}, a) //2. {...a} function deepcopy(ori, target){ target = target || {}; for(let p in ori){ if(ori.hasOwnProperty(p)){ //值是对象 if(typeof(ori[p]) === 'object' && ori[p]){ //如果是数组 if(ori[p] instanceof Array){ target[p] = []; deepcopy(ori[p], target[p]); }else{ target[p] = {}; deepcopy(ori[p], target[p]); } }else{ target[p] = ori[p]; } } } return target; }
- setTimeout实现setInterval
function setInterval(fn, interval){ var timeOut = function(){ setTimeout(timeOut, interval); fn.apply(null); } timeOut(); }
- 实现map
function map (arr, mapCallback) { let result = [] for (let i = 0, len = arr.length; i < len; i++) { result.push(mapCallback(arr[i], i, arr)) } return result }
- 实现filter
function filter (arr, filterCallback) { let result = [] for (let i = 0, len = arr.length; i < len; i++) { if (filterCallback(arr[i], i, arr)) { result.push(arr[i]) } } return result }
- 实现reduce
function reduce (arr, reduceCallback, initialValue) { let hasInitialValue = initialValue !== undefined let value = hasInitialValue ? initialValue : arr[0] for (let i = hasInitialValue ? 0 : 1, len = arr.length; i < len; i++) { value = reduceCallback(value, arr[i], i, arr) } return value }
本文实在是菜鸡一枚,在找工作阶段只粗略学习了Vue框架,这边提一句,自己会什么就只在简历里面写什么,面试官会看你简历里面的内容来问你·千万别给自己挖坑!!!我觉得最最最重要的还是基础+算法,这篇文章可是掏空了我的积蓄,希望对前端的朋友有帮助,这样我也就很欣慰哒~
最后有不对的地方还望指正,我也是在慢慢学习中,有空大家可以一起分享讨论哈。也希望正在奋力找工作的牛友们都能进想去的公司!
一个小小的学习建议,可以参考下
如果有同学觉得入门困难,也可以报名一些课程来督促自己。牛客自己也有做前端秋招集训营,参加校招的同学可以考虑一下。这种课相对比较系统,能让我们少走弯路,在更短时间内达到一个通过笔试和面试的水平。苦于没有项目的同学也能通过这些课程做出来一个自己的项目丰富自己的简历,还是很不错的。
我自己也报名了牛客的课程,里面有基础知识和项目实战讲解,有兴趣的可以看看。这是我报的那个课程,还有其他课程大家可以根据自己的情况来选择哈~
https://www.nowcoder.com/courses/cover/live/662?coupon=ABvoHDH
全部评论
(26) 回帖