面试题总结
1、var、let、const区别?
答:
- 1、var声明的变量为方法作用域,let、const是块级作用域即 {} 之内可见;
- 2、在变量声明之前就访问的话,let、const会报错(ReferenceError: xxx is not defined),而var使用默认值undefined;
- 3、let、const作用域相同,但是const定义的是常量,一旦被赋值不可更改,let值和类型都可以改变,没有限制。
- 4、const一旦声明变量,就必须立即初始化,不能留到以后赋值。
2、vue生命周期都有什么?vue-router路由守卫都有什么?路由跳转方式?传参方式都有什么以及区别是什么?
答:
- vue生命周期:beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed;
- vue-router路由守卫:
- 导航守卫按照维度分三个:(参数都是to, from, next)
- 全局的:进入任何一个路由都会执行
beforeEach:进入路由前执行
beforeResolve:在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
afterEach:导航确认执行时执行,可理解为导航完成时执行 - 路由的:进入某个路由才会执行
beforeEnter: 进入该路由前 - 组件的:进入某个组件才会执行组件复用时
beforeRouteEnter: 进入组件时
beforeRouteUpdate: 组件被复用时调用
beforeRouteLeave: 离开组件前
- 路由跳转:
- 一、this.$router.push({path:url}),或者replace(用法同push,但是这个方法不会向history里面添加新的记录)
- 二、<router-link to="/xxx/xx">点这里跳转</router-link>
- 传参方式:
- 一、<router-link :to="{name:'home', params: {id:1}}"> ,<router-link :to="{name:'home', query: {id:1}}">
- 二、
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name
- query和params区别:
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params,刷新页面id还在
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
3、vuex属性都有什么?用法?
答:Vuex有五个核心属性: state, getters, mutations, actions, modules。
- state:vuex的基本数据,用来存储变量
- geeter:从基本数据(state)派生的数据,相当于state的计算属性
- mutation:提交更新数据的方法,必须是同步的(如果需要异步使用action)。接受 state 作为第一个参数,提交载荷作为第二个参数。
- action:和mutation的功能大致相同,不同之处在于 ==》1. Action 提交的是 mutation,而不是直接变更状态。 2. Action 可以包含任意异步操作。
- modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
使用下面这两种方法存储数据:
- modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
- dispatch:异步操作,写法:
this.$store.dispatch('action方法名',值)
- commit:同步操作,写法:
this.$store.commit('mutations方法名',值)
4、vue中父组件调用子组件中的方法以及子组件调用父组件方法分别怎么做?
答:父调子:父组件通过$ref获取到子组件的实例对象并调用子组件的 sonFun 方法,this.$refs.sonRef.sonFun();
子调父:一、直接在子组件中通过this.$parent.fatherFun()来调用父组件的方法
二、this.$emit('fatherFun');
三、父组件把方法传入子组件中,在子组件里直接调用这个方法;eg: <child :fatherFun="fatherFun"></child>,子组件:this.fatherFun();
5、vue中 provide 和 inject 用法
答:provide 是在父组件中定义,然后所有子组件都是可以通过 inject 注入该变量或方法进行操作。
6、computed和watch区别,以及computed和methods中方法的区别
答:computed:
- 支持缓存,只有依赖数据发生改变,才会重新进行计算
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
watch:
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- 不支持缓存,数据变,直接会触发相应的操作;
- watch支持异步;
computed和methods中方法的区别:
- watch支持异步;
- 1、methods使用时,一般情况需要加括号,而computed则不需要。
- 2、methods每次调用时会重新执行函数,而computed在其内部变量不变或其返回值不变的情况下多次调用只会执行一次,后续执行时直接从缓存中获取该computed的结果。
7、VUE中key的意义和作用?
答:key的作用主要是为了高效的更新虚拟DOM,提高Diff算法性能。
- 1、key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。
- 2、若不设置key还可能在列表更新时引发一些隐蔽的bug
- 3、vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
8、写一个JS函数,实现JS变量深度克隆。(不准使用JSON.parse、JSON.stringify)
第一种方法:
function extendDeep(parent, child) {
child = child || {};
for (let i in parent) {
if (parent.hasOwnProperty(i)) {
// 检测当前属性是否为对象
if (typeof parent[i] === "object") {
// 如果当前属性为对象,还要检测它是否为数组
// 这是因为数组的字面量表示和对象的字面量表示不同
// 前者时[],后者时{}
child[i] = Object.prototype.toString.call(parent[i] === "[object Array]") ? [] : {};
// 递归调用 extendDeep
extendDeep(parent[i], child[i]);
} else {
child[i] = parent[i];
}
}
}
return child;
}
第二种方法:
用ES6的Array.isArray方法,使用此方法判断变量是否为数组,则非常简单。
Array.isArray([]); // => true
Array.isArray({0: 'a', length: 1}); // => false
// 实际上,通过Object.prototype.toString去判断一个值的类型,也是各大主流库的标准。因此Array.isArray的polyfill通常长这样:
if (!Array.isArray){
Array.isArray = function(arg){
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
function deepCopy(obj) {
if (typeof obj != 'object' || obj === null) {
return obj;
}
var newObj = {};
if (Array.isArray(obj)) {
newObj = [];
}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const el = obj[key];
newObj[key] = deepCopy(obj[key]);
}
}
return newObj;
}
9、解释下margin塌陷以 及 margin合并
margin塌陷,也叫 margin重叠
- 标准文档流中,两个盒子,分别有上下外边距,竖直方向的margin不叠加,只取较大的值作为margin(水平方向的margin是可以叠加的)。
<style>
body{
background-color:#000;
}
.wrapper{
width:200px;
height:200px;
background-color:red;
margin-top:100px;
}
.box{
width:50px;
height:50px;
background-color:#eee;
opacity:0.8;
}
</style>
</head>
<body >
<div class="wrapper">
<div class="box"></div>
</div>
</body>

A和B都是距离上边100px;
现在给B设置margin-top:100px;发现两个方块位置没动;
而当给B设置margin-top:150px;小方块B带着大方块A往下移动了50px;
原理:父子嵌套元素在垂直方向的margin,父子元素是结合在一起的,他们两个的margin会取其中最大的值.
正常情况下,父级元素应该相对浏览器进行定位,子级相对父级定位.
但由于margin的塌陷,父级相对浏览器定位.而子级没有相对父级定位,子级相对父级,就像坍塌了一样.
margin塌陷解决方法
1.给父级设置边框或内边距(不建议使用)
.wrapper{
width:200px;
height:200px;
background-color:red;
margin-top:100px;
border-top:1px solid black;
}
2.触发bfc(块级格式上下文),改变父级的渲染规则
方法:
改变父级的渲染规则有以下四种方法,给父级盒子添加
(1)position:absolute/fixed
(2)display:inline-block;
(3)float:left/right
(4)overflow:hidden
这四种方法都能触发bfc,但是使用的时候都会带来不同的麻烦,具体使用中还需根据具体情况选择没有影响的来解决margin塌陷
margin合并现象:
原理:两个兄弟结构的元素在垂直方向上的margin是合并的
原理图:
margin合并问题也可以用bfc解决,
1.给box2加上一层父级元素并加上overflow:hidden;
<style>
.wrapper{
overflow:hidden;
}
</style>
<div class="box1"></div>
<div class="wrapper">
<div class="box2"></div>
</div>
2.给两个都加一层父级再加bfc
<div class="wrapper">
<div class="box1"></div>
</div>
<div class="wrapper">
<div class="box2"></div>
</div>
但是这两种方法都改变了HTML结构,在开发中一般不采用的;
所以在实际应用时,在margin合并这个问题上,我们一般不用bfc,而是通过只设置上面的元素的margin-bottom来解决距离的问题。
10、解释下CSS-BFC
一、何为BFC
BFC(Block Formatting Context)格式化上下文,是Web页面中盒模型布局的CSS渲染模式,指一个独立的渲染区域或者说是一个隔离的独立容器。
二、形成BFC的条件
1、浮动元素,float 除 none 以外的值;
2、定位元素,position(absolute,fixed);
3、display 为以下其中之一的值 inline-block,table-cell,table-caption;
4、overflow 除了 visible 以外的值(hidden,auto,scroll);
参考:
https://www.cnblogs.com/chen-cong/p/7862832.html
https://www.jianshu.com/p/828023418450
11、看下这行数字的规律,写个方法返回第30位是什么?1,1,2,3,5,8...
function fn(len) {
if (len < 3) {
return 1;
}
var first = 1, sec = 1, third = 0;
for (let i = 2; i < len; i++) {
third = first + sec;
first = sec; sec = third;
}
return third;
}
fn(1); // 1
fn(2); // 1
fn(5); // 5
fn(30); // 832040
12、 javascript的typeof返回哪些数据类型.
答案:string,boolean,number,undefined,function,object
13、例举3种强制类型转换和2种隐式类型转换?
答案:强制(parseInt,parseFloat,number)
隐式(== ===)
14、split()、join() 的区别
答案:前者是将字符串切割成数组的形式,后者是将数组转换成字符串
15、数组方法pop()、push() 、unshift()、 shift()
答案:push()尾部添加 pop()尾部删除
unshift()头部添加 shift()头部删除
16、IE和标准下有哪些兼容性的写法
答案:
var ev = ev || window.event;
document.documentElement.clientWidth || document.body.clientWidth;
Var target = ev.srcElement || ev.target;
17、innerHTML和outerHTML的区别
答案:
innerHTML(元素内包含的内容)
outerHTML(自己以及元素内的内容)
18、offsetWidth offsetHeight和clientWidth clientHeight的区别
答案:
(1)offsetWidth (content宽度+padding宽度+border宽度)
(2)offsetHeight(content高度+padding高度+border高度)
(3)clientWidth(content宽度+padding宽度)
(4)clientHeight(content高度+padding高度)
19、闭包的好处
答案:
(1)希望一个变量长期驻扎在内存当中(不被垃圾回收机制回收)
(2)避免全局变量的污染
(3)私有成员的存在
(4)安全性提高
20、冒泡排序算法
var array = [5, 4, 3, 2, 1];
var temp = 0;
for (var i = 0; i <array.length; i++){
for (var j = 0; j <array.length - i; j++){
if (array[j] > array[j + 1]){
temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
}
}
21、请写出一个打乱JS数组【1,2,3,4,5,6】内元素排列顺序的方法。
function randomsort(a, b) {
return Math.random() > .5 ? -1 : 1;
}
var arr = [1, 2, 3, 4, 5, 6];
arr.sort(randomsort);
22、不用循环(包括ES5的forEach、map等方法),创建一个长度位100的数组,并且每个元素值等于的下标索引【0,1,2...99】。
// 方法一
Object.keys(Array.from({length:100}));
// 方法二
Object.keys(Array.apply(null,{length:100}));
// 方法三
Object.keys([...Array(100)]);
// 方法四
Object.keys(Array(100).keys());
// 方法五
[...Array(100).keys()];
22、Object.assign用法、特点、模拟实现
答: