【初级】 前端面试题1.0 (重难点)
0. 前言
初衷:网上有很多非常优秀的面试题汇总,对我有很大的启发与帮助。本来没有必要再写一份汇总,但看到网上很多文档还是有一些不合适自己的地方(如答案繁琐、每篇文档只有部分需要的答案、等等),于是便萌生了自己写文档的想法。
作用:汇总适合自己的,以及自认为重要的面试题。
备注:答案有纰漏之处,还望读者见谅。如能留言指出错误,更是感谢。
一、Vue基础
1. 说一下vue的基本原理
当一个Vue实力创建时,Vue会遍历data中的属性,用Object.defineProperty将它们转为getter/setter,并且在内部追踪相关依赖,在属性被访问和修改时通知变化。
每个组件实例都有相应的watcher实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,更新组件。
2. Vue双向绑定原理是什么?
Vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。通过Object.defineProperty实现数据劫持。
实现一个解析器compile,可以扫描和解析每个节点的相关指令,初始化模板数据和相应的订阅器。
实现一个监听器observer,用来劫持并监听所有属性,如有变动,就通知订阅者。
实现一个订阅者watcher,它是observer和compile之间通信的桥梁,主要做的事是:
①在自身实例化时往属性订阅器(dep)里添加自己;
②自身必须有一个update方法;
③当属性变动时,调用update方法,触发compile中绑定的回调。
watcher简单来说,就是收到属性变化的通知,就执行update更新视图。
3. 使用Object.defineProperty来进行数据劫持有什么缺点?
对于数组而言,大部分操作都是拦截不到的,只是Vue内部通过重写函数的方式,解决了这个问题。
4. computed、methods、watch有什么区别?
computed:计算属性。当一个或多个值发生变化时,会影响某个值跟着变化。
methods:编写事件方法。与computed的区别是:computed有缓存,多次调用只会执行一次,提高性能;methods没有缓存。
watch:监听数据变化,执行回调。当一个值变化时,会影响多个值跟着一起变化。也适合监听路由改变。
5. data为什么是一个函数而不是一个对象?
JS中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例改变了对象的属性值,其他实例中的数据也会随之改变。
组件中的data写成一个函数,数据以函数返回值的形式定义,这样每复用一次组件,就会返回一个新的data。类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。
6. 谈谈你对Vue生命周期的理解。
vue包含8个基本生命周期: beforeCreate、created、beforeMount、mounted、beforeUpdate、update、beforeDestroy、destroyed。
一旦进入页面或组件,会先后执行beforeCreate、created、beforeMount、mounted;
beforeCreate:实例初始化后,data和event还未配置;
created:实例已创建。有data和event,但没有el,不能操作dom;
beforeMount:挂载之前。render函数被调用。
mounted:挂载阶段。有data,有el,可以操作dom,数据完成了双向绑定。
当加入了keep-alive后,新增两个生命周期:actived、deactived;
主要作用:当重复访问某个组件时,可以缓存该组件,避免组件内的数据重复渲染,提高性能。
actived:组件被激活时调用;
deactived:组件被销毁时调用;
7. Vue子组件和父组件执行顺序是什么?
1. 加载渲染阶段:
父组件:beforeCreate
父组件:created
父组件:beforeMount
子组件:beforeCreate
子组件:created
子组件:beforeMount
子组件:mounted
父组件:mounted
2. 更新阶段:
父组件:beforeUpdate
子组件:beforeUpdate
子组件:updated
父组件:updated
3. 销毁阶段:
父组件:boforeDestroy
子组件:boforeDestroy
子组件:destroyed
父组件:destroyed
8. created和mounted的区别是什么?
created:模板渲染成html前调用。只有data,没有el,不能操作dom;
mounted:模板渲染成html后调用。有data,有el,可以操作dom。
9. 一般在哪个生命周期内请求异步数据,为什么?
可请求异步数据的生命周期:created、beforeMounte、mounted;
原因:这三个钩子函数中,data已经创建,可以将服务器端返回的数据进行赋值;
10. 路由有几种模式?区别是什么?
路由有两种模式:hash和history;
hash:hash值会出现在url里,但不会出现在HTTP请求中,对后端完全没有影响,所以改变hash值,也不会重新加载页面。这种模式的浏览器支持度很好,低版本的IE浏览器也支持这种模式。
history:history使用传统的路由分发模式,history在跳转时,会发送请求,服务接受到这个请求后,会解析这个url,并做出相应的逻辑处理。history需要后台配置支持,如果没有后台配置支持,访问时则会返回404。
11. 什么是跨域问题?如何解决跨域问题?
做项目有开发环境和生产环境。我们要请求接口,但后端的端口号可能和我们的端口号不同,这时就会遇到跨域的问题。跨域的问题可以由后端解决,也可以由我们前端解决。前端可以通过设置代理的方式来解决跨域的问题。
新建vue.config.js配置文件;然后再设置devServer.proxy属性,将未知的请求代理到这个属性中,就可以解决跨域的问题。
12. 如何打包上线?遇到什么问题?怎么解决的?
方式:可以通过npm run build 命令来执行打包操作;
问题:会出现空白页的情况;
解决:
1. 在vue.config.js配置文件中,添加默认路径:publicPath: './';
2. 测试的时候用hash模式;上线的时候用history模式,让后端做一个重定向。
13. Vuex是什么?有哪些属性?
概念:vuex 是专门为 vue 提供的全局状态管理系统,用于多个组件中数据共享、数据缓存等。(无法持久化、内部核心原理是通过创造一个全局实例 new Vue)
属性:
0. vuex有5个基本属性:state、getters、mutations、actions、modules;
1. state:用于存放数据,类似于data;
2. getters:允许组件从store中获取数据,类似于computed;
3. mutations:可以更改store中的状态,类似于methods;
4. actions:可以是异步操作,但不能直接改变状态,只能通过提交mutation;
5. modules:允许将单一的store拆分为多个store。
14. 你做过哪些vue性能优化?
1. 图片懒加载;
2. 路由懒加载;
3. 第三方插件按需导入;
4. keep-alive缓存组件;
5. 防抖、节流运用;
二、ES6相关
1. var、let、const有什么区别?
1. 变量提升:var有;let、const无。
2. 多次声明同一变量:var可以;let、const不能。
3. 作用域:var没有自身作用域;let、const有自身作用域。
4. 修改赋值:var、let可以;const不能。
2. 箭头函数和普通函数有什么区别?
1. 有无this:箭头函数没有prototype(原型),所以没有this;
2. 继承①:箭头函数的this继承自外层第一个普通函数的this;
3. 继承②:如果外层没有普通函数,this指向window(全局对象);
4. 修改继承:箭头函数本身的this不能改变,但可以修改它要继承的对象的this;
5. arguments①:箭头函数的this指向全局时,arguments会报未声明的错误;
6. arguments②:箭头函数的this指向普通函数时,它的arguments会指向该普通函数;
7. new:箭头函数不可以new实例化,因为箭头函数里没有constructor;
3. for...in 和 for...of 的区别是什么?
简述:for...of是ES6新增的遍历方式,允许遍历一个含有iterator接口的数据结构(数组、对象等)并且返回各项的值。
区别:
1. 遍历什么:for...of遍历对象的键值;for...in遍历对象的键名;
2. 是否遍历原型链:for...of不会;for...in会;
3. 遍历数组的返回内容:for...of(属性值);for...in(属性)
总结:for...in是为了遍历对象而生的,不适用于遍历数组;for...of可以遍历所有含iterator的数据;
其他
1. 块级作用域;
2. 解构赋值;
3. symbol;
4. Set、Map;
5. proxy;
6. Iterator和for...of;
7. generator;
8. class
三、Promise相关
1. 什么是Promise?
Promise是异步编程的一种解决方案。它是一个对象,可以获取异步操作的消息。
简单说就是一个容器,里面保存着某个未来才会结束的事件的结果。
Promise的构造函数接受一个参数,这个参数是一个函数,这个函数里面有两个参数:
1. resolve:异步操作执行成功后的回调函数;
2. reject:异步操作执行失败后的回调函数;
2. promise有哪几种状态?
promise有三种状态:Pending(进行中)、Fulfilled(已完成)、Rejected(已拒绝)
3. Promise有什么特点?
1. promise的状态不受外界影响。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态;
2. 一旦状态改变就不会再变,且任何时候都可以得到这个结果。promise对象的状态改变,只有两种可能:从pending变为fulfilled,或从pending变为rejected。
4. promise的缺点有哪些?
1. 无法取消promise,一旦新建就会立即执行,无法中途取消;
2. 需要设置回调函数,否则promise内部抛出的错误,无法反应到外部;
3. 链式调用then依然不够方便;
5. 如何创建Promise对象?
创建Promise对象有三种方式:new Promise()、promise.resolve、promise.reject
6. promise有哪些方法?具体是做什么的?
0. promise有5个基本方法:then、catch、all、race、finally;
1. then:
1.1 then方法接受两个回调函数作为参数。第一个回调是promise对象状态变为resolved时调用;第二个回调是promise对象状态变为rejected时调用(可以省略)。
1.2 then方法返回的是一个新的promise实例,因此可以采取链式调用的方法。
2. catch:
2.1 相当于then方法的第二个参数,指向reject的回调函数。
2.2 不同点在于,在执行resolve回调函数时,如果出现错误,抛出异常,不会停止运行,而是进入catch方法中。
3. all:
3.1 all方法可以完成并行任务,它接收一个数组,数组的每一项都是一个promise。
3.2 当数组中所有的promise状态都达到resolved的时候,all的方法才会变为resolved;如果有一个promise状态变为rejected,那么all的状态就会变成rejected;
4. race:
4.1 race和all接收的参数一样,都是一个包含promise的数组。
4.2 但与all不同的是,当最先执行的事件执行完之后,就直接返回该promise对象的值。
4.3 如果第一个promise对象状态变为resolved,那自身的状态就会变为resolved;反之,第一个promise对象状态变为rejected,那自身的状态就会变为rejected。
5. finally:
5.1 不管promise对象最后的状态如何,都会执行的操作。
5.2 finally本质上是then方法的特例;
5.3 finally方法不接受任何参数,所以不会知道前面promise状态时fulfilled还是rejected
7. async、await是什么?
async:表示函数里有异步操作。返回的是一个promise对象。如果成功,状态为resolved,结果为return的值;如果失败,状态为rejected。
await:await的含义为等待,也就是async函数需要等待await后面函数执行完并有了返回结果之后,才能继续执行下面的代码。await通过返回一个promise对象来实现同步的效果,简化了then链式调用的操作。
8. 使用async/await需要注意什么?
1. await必须在async包裹的函数里使用;
2. await后面如果是一个promise对象,需等待promise对象resolve,得到resolve的值,作为await表达式的运算结果。之后,才执行后面的代码;
9. async/await相比于promise有什么优势?
单一的promise链和async/await没有什么区别;但是如果需要处理由多个promise组成的then链的时候,优势就体现出来了。
10. async/await如何捕获异常?
通过设置:try{} catch(err){}
四、JS相关
1. 谈谈你对js微任务和宏任务的理解
1. js是单线程的语言;
2. js代码的执行流程是:先执行同步任务,再执行事件循环(异步请求、定时器、事件)
3. 事件循环包含微任务和宏任务;
4. 微任务:promise.then、宏任务:setTimeout等;
5. 清空了所有的微任务,才能执行宏任务;
2. 谈谈你对js作用域的理解
1. 作用域分为:全局作用域、函数作用域、块级作用域;
2. 作用域链:如果在自己作用域中找不到该变量,就去父级作用域查找,依次向上级作用域查找,知道全局作用域结束,这一层层的关系就是作用域链。内部可以访问外部变量,但是外部不能访问内部的变量;
3. 优先级:声明变量 > 声明的普通函数 > 参数 > 变量提升;
3. new操作符具体做了什么?
1. 在内存中创建了一个空对象;
2. 将构造函数的作用域赋给新对象(对象的__proto__属性指向构造函数的prototype属性)
3. 改变this指向,让this指向这个空对象(执行构造函数内部代码,为空对象添加属性)
4. 返回新的对象。
4. 什么是闭包?
含义:函数嵌套函数,内部函数就是闭包;
作用:保护私有变量不收外部干扰,形成不易销毁的栈内存;
缺点:变量会留在内存中,造成内存损耗的问题。
5. 谈谈你对原型链的理解
详述:在JS中使用构造函数来新建一个对象时,每一个构造函数内部有一个prototype原型对象,包含了可以由该构造函数所有实例共享的属性和方法。新建的对象中,有一个__proto__,指向构造函数的prototype。
简述:原型链就是把原型串联起来;原型链的最顶端是null;
归属:函数的原型是prototype;对象的原型是__proto__;
作用:实现对象属性和方法的共享;
对象查找属性方法的顺序:1. 对象本身;2. 构造函数;3. 对象的原型;4. 构造函数的原型;
6. 谈谈你对this的理解
含义:this是执行上下文的一个属性,它指向最后一次调用这个方法的对象。
this的四种指向:
1. 指向全局对象:在全局定义的函数、setTimeout;
2. 指向某个对象:函数作为一个对象的方法被调用;
3. 指向实例对象:new一个构造函数时,this指向实例化的对象;
4. 改变this指向:call、apply、bind;
7. 异步编程有哪几种方式?
1. 回调函数;2. promise;3. generator函数;4. async/await。
8. 哪些情况会造成内存泄漏问题?
含义:变量使用完毕,系统应当收回被占用的内存空间,否则就会造成内存泄漏。
简述:一般有四种情况会导致内存泄漏的问题;
1. 意外的全局变量:由于使用了未声明的变量,而意外创建了一个全局变量,使这个变量一直留在内存中无法被回收;
2. 计时器或回调函数:比如设置了setInterval定时器,但忘记取消它。如果这个定时器里有对外部变量的引用,那么这个变量会一直留在内存中,无法被回收;
3. 闭包:函数执行结束后,函数外部的变量依然指向函数内部的局部变量,此时局部变量依然被使用,所以不会被回收。
9. 什么是垃圾回收?
含义:运行JS代码时,需要分配内存空间来存储变量和值。当变量不再参与运行时,需要系统收回被占用的内存空间,这就是垃圾回收。
垃圾回收方式:
1.标记清除:当变量进入执行环境时,就标记这个变量“进入环境”。当变量离开环境时,就标记“离开环境”。标记为“进入环境”的变量,表示正在被使用,是不能被回收的。标记为“离开环境”的变量,会被删除,垃圾收集器回收被占用的空间。
2. 引用计数:引用计数会记录每个值被引用的次数。
10.为什么要减少垃圾回收?如何减少垃圾回收?
原因:浏览器可以进行垃圾自动回收,但当代码比较复杂的时候,垃圾回收所带来的的代价比较大,所以应该尽量减少垃圾回收。
方式:
1. 对数组进行优化:在清空数组时,最简单的方式就是给数组赋值为[],但是与此同时会创建一个新的空对象。可以将数组的长度设置为0,以此来达到清空数组的目的。
2. 对对象进行优化:对象尽量复用,对于不再使用的对象,将其设置为null,尽快回收。
3. 对函数进行优化:在循环中的函数表达式,如果可以复用,尽量放在函数的外面。
五、前后端相关
1. 谈谈你对ajax的理解,如何实现一个ajax请求?
含义:ajax指的是通过JS的异步通信,从服务器获取XML文档,从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。
创建AJAX请求的步骤:
1. 创建一个XMLHttpRequest对象;
2. 在这个对象上,使用open方法创建一个HTTP请求。open的参数为:请求的方式、请求的地址、是否异步和用户的认证信息;
3. XMLHttpRequest有5种状态,通过onreadystatechange事件,监听状态变化;当readyState变为4的时候,代表服务器返回的数据接收完成;再判断请求的状态,如果状态是2xx或者304,代表返回正常,可以通过response中的数据来更新页面;
4. 最后,调用send方法向服务器发起HTTP请求;
简化的步骤:
1. 创建XMLHttpRequest对象:new XMLHttpRequest();
2. 创建HTTP请求:xhr.open('GET', url, true);
3. 设置状态监听函数:xhr.onreadystatechange = function() {};
3.1 判断xhr的状态:if( this.readyState === 4 ) {};
3.1.1 判断请求的状态:if (this.status === 200 ) {};
4. 发送HTTP请求:xhr.send(null);
2. 如何用promise封装一个ajax请求?
promise封装ajax请求的步骤:
A. 创建一个封装函数;
a. 创建一个promise对象;
0. 创建ajax请求(步骤如上题)
1. 创建XMLHttpRequest对象(new);
2. 创建HTTP请求(open);
3. 设置状态监听函数(onreadystatechange);
3.1 判断xhr状态(readyState);
3.1a 请求成功时(status === 200),修改promise状态,调用resovle函数;
3.1b 请求失败时(status !=== 200),修改promise状态,调用reject函数;
4. 发送HTTP请求(send)。
b. 返回这个promise;
3. ajax、fetch、axios有什么区别?
1. ajax:
简述:一种创建交互式网页应用的网页开发工具。无需加载整个网页,通过在后台与服务器进行少量数据交换,使网页实现异步更新。
缺点:
1. 针对MVC编程,不适用于MVVM;
2. 针对原生XHR开发,XHR本身的架构不清晰;
2. fetch:
简述:基于promise实现,使用原生js,脱离了XHR,代码更加清晰。(要看其他文档)
3. axios:
简述:基于promise封装的HTTP客户端。(要看其他文档)
五、CSS相关
1. 谈谈你对盒模型的理解
简述:CSS3中的盒模型有两种:标准盒模型、IE盒模型;盒模型由四个部分组成:margin、padding、border、content。两种盒模型的区别在于设置width和heigth时,所对应的范围不同;
区别:
1. 标准盒模型:width和heigth只包含了content;
2. IE盒模型:width和heigth包含了padding、border和content。
修改:可以修改元素的box-sizing属性来改变元素的盒模型:
1. 标准盒模型(默认):box-sizing: content-box;
2. IE盒模型: box-sizing: border-box;
2. CSS3中有哪些新特性?
1. 圆角(border-radius: 2px);
2. 旋转(transform);
3. flex布局;
3. 为什么要清除浮动?清除浮动的方式有哪些?
浮动含义:非IE浏览器下,容器不设高度且子元素浮动时,容器高度不能被内容撑开。此时,内容会溢出到容器外面而影响布局,这种现象被称为浮动。
清除浮动的方式:
1. 给父级div定义一个高度;
2. 最后一个浮动元素之后添加一个空的div标签,并添加clear:both样式;
3. 包含浮动元素的父级标签添加overflow:hidden或者overflow:auto;
4. 什么是margin重叠问题?如何解决?
含义:两个块级元素的上外边距和下外边距可能会合并为一个外边距,其大小会取决于外边距值最大的那个。
解决方式:看是“兄弟之间重叠”,还是“父子之间重叠”
1. 兄弟之间重叠:
1.1 底部元素变为行内盒子:display:inlin-block;
1.2 底部元素设置浮动:float;
1.3 底部元素的position的值为:absolute/fixed;
2. 父子之间重叠:
2.1 父元素加入:overflow:hidden;
2.2 父元素添加透明边框:border:1px solid transparent;
2.3 子元素变为行内盒子:display:inlin-block;
2.4 子元素加入浮动属性或定位
5. 什么是flex布局?
含义:即“弹性布局”,为盒模型提供最大的灵活性。采用flex布局的元素,称为flex容器;它的子元素称为容器成员,称为flex项目。容器默认两条轴:主轴和交叉轴;
容器有5个属性:
1. 主轴方向:flex-direction;
2. 是否换行:flex-wrap;
3. 方向换行简写:flex-flow;
4. 主轴对齐方式:justify-content;
5. 交叉轴对齐方式:align-items;
项目有6个属性:
1. 排序:order;
2. 放大比例:flex-grow;
3. 缩小比例:flex-shrink;
4. 主轴剩余空间:flex-basis;
5. 简写:flex;
待更新内容:
待添加问题(js)
- js继承;
待添加问题(Vue)
- vue路由相关;
待添加问题(其他)
- 重绘与回流;
- localStorage、sessionStorage、cookie
- 跨域
待手写: js:
- 手写防抖、节流;
- 实现ajax请求;
- 使用promise封装ajax请求;
- 实现浅拷贝、深拷贝; 数据处理:
- 实现日期格式化函数;
- 交换a、b的值,不能用临时变量;
- 实现数组的乱序输出;
- 实现数组元素求和;
- 实现数组的扁平化;
- 实现数组去重;
- 实现字符串的repeat方法;
- 实现字符串翻转;
- 将数字每千分位用逗号隔开;
- 实现非负整数相加;
- 实现add(1)(2)(3);
- 实现类数组转化为数组;
- 使用reduce求和;
- 将js对象转化为树状结构;
- 使用ES5和ES6求参数的和;
- 解析url为params为对象; 场景应用:
- 循环打印红黄绿(callback、promise、async/await);
- 实现每隔一秒打印1,2,3,4;
- 小孩报数问题;
- 用promise实现图片的异步加载;
- 实现发布-订阅者模式;
- 查找文章中出现频率最高的单词;
- 封装异步的fetch,使用async/await
- 实现prototype继承;
- 实现双向绑定数据;
- 实现简单路由;
- 实现斐波那契数列;
- 字符串出现的不重复最长长度;
- 使用setTimeout实现setInterval;
- 实现jsonp;
- 判断对象是否存在循环引用;
其他手写需求: js:
- for...of的使用;
- 用promise封装一个ajax;
- fetch、axios的使用;
- 修改和重写原型; css:
- 实现一个三角形;
- 实现一个扇形;
- 实现一个宽高自适应的正方形;
- 画一条0.5px的线;
重点小题:
- 遍历数组的方法;
- forEach和map的区别;
引用: