前端面试题1

1.css的居中方式

1.1 水平居中
1.1.1 内联元素水平居中 text-align: center(常用)
text-align: center 可以实现在块级元素内部的内联元素水平居中。
此方法对内联元素(inline), 内联块(inline-block), 内联表(inline-table), inline-flex元素水平居中都有效。

1.1.2块级元素水平居中 margin : 0 auto;
给出固定的宽度之后设置margin : 0 auto;,若是没有宽度,此方法是没有作用的,只有块元素使用这个方法,行内块元素要先设置display:block;之后并且设置margin:0 auto;才可以用这种方法

1.1.3 多块级元素水平居中
inline-block
如果一行中有两个或两个以上的块级元素,通过设置块级元素的显示类型为inline-block和父容器的text-align属性从而使多块级元素水平居中。

1.1.4 display: flex
利用弹性布局(flex),实现水平居中,其中justify-content 用于设置弹性盒子元素在主轴(横轴)方向上的对齐方式。

1.2垂直居中
1.2.1单行内联(inline-)元素垂直居中
通过设置内联元素的高度(height)和行高(line-height)相等,从而使元素垂直居中。

1.2.2块级元素垂直居中
(1)固定高度的块级元素
我们知道居中元素的高度和宽度,垂直居中问题就很简单。通过绝对定位元素距离顶部50%,并设置margin-top向上偏移元素高度的一半,就可以实现垂直居中了。

(2)未知高度的块级元素
当垂直居中的元素的高度和宽度未知时,我们可以借助CSS3中的transform属性向Y轴反向偏移50%的方法实现垂直居中。但是部分浏览器存在兼容性的问题。

1.3水平垂直居中
1.3.1 固定宽高元素水平垂直居中
通过margin平移元素整体宽度的一半,使元素水平垂直居中。

1.3.2 未知宽高元素水平垂直居中
利用2D变换,在水平和垂直两个方向都向反向平移宽高的一半,从而使元素水平垂直居中。

1.3.3 利用flex布局
利用flex布局,其中justify-content 用于设置或检索弹性盒子元素在主轴(横轴)方向上的对齐方式;而align-items属性定义flex子项在flex容器的当前行的侧轴(纵轴)方向上的对齐方式。

1.3.4 屏幕上水平垂直居中
屏幕上水平垂直居中十分常用,常规的登录及注册页面都需要用到。要保证较好的兼容性,还需要用到表布局。

2.px,em,rem,%区别

2.1px
px就是像素,也是我们现在经常使用的基本单位,比如常常听到的电脑像素是1024x768的,表示的是水平方向是1024个像素点,垂直方向是768个像素点。
2.2em
em参考物是父元素的font-size,默认字体大小是16px,所以1em不是固定值,因为它会继承父元素的字体大小
2.3rem
rem参考物是相对于根元素,我们在使用时可以在根元素设置一个参考值即可,相对于em使用,减少很大运算工作量,例:html大小为10px,12rem就是120px
2.4%
% 是相对于父元素的大小设定的比率,position:absolute;的元素是相对于已经定位的父元素,position:fixed;的元素是相对可视窗口

3.display的值和作用

display的值有很多种,这里简要概括一下display的值以及他们的作用
display:block: 指定对象为块元素。
display:inline(默认值):表示指定对象为内联元素
display:inline-block:指定对象为内联块元素。
display:inline-table:指定对象作为内联元素级的表格。类同于html标签table
display:list-item:指定对象为列表项目
display:none:表示隐藏对象,与visibility属性的hidden值不同,display:none不为被隐藏的对象保留物理空间 ,然visibility:hidden就保留
display:run-in:根据上下文决定对象是内联对象还是块级对象。
display:table: 指定对象作为块元素级的表格。类同于html标签table
display:table-caption:指定对象作为表格标题。类同于html标签caption
display:table-cell:指定对象作为表格单元格。类同于html标签td
display:table-column:指定对象作为表格列。类同于html标签col
display:table-column-group:指定对象作为表格列组显示。类同于html标签colgroup
display:table-footer-group:指定对象作为表格脚注组。类同于html标签tfoot
display:table-header-group: 指定对象作为表格标题组。类同于html标签thead
display:table-row:指定对象作为表格行。类同于html标签tr
display:table-row-group: 指定对象作为表格行组。类同于html标签tbody
display:box: 将对象作为弹性伸缩盒显示。(伸缩盒的最老版本中属性
display:inline-box: 将对象作为内联块级弹性伸缩盒显示。(伸缩盒的最老版本中属性)
display:flexbox: 将对象作为弹性伸缩盒显示。(伸缩盒的最老版本中属性)
display:inline-flexbox: 将对象作为内联块级弹性伸缩盒显示。(伸缩盒的最老版本中属性)
display:flex: 将对象作为弹性伸缩盒显示。(伸缩盒的最老版本中属性)
display:inline-flex: 将对象作为内联块级弹性伸缩盒显示。(伸缩盒的最老版本中属性

4.路由跳转的方式

1、router-link 【实现跳转最简单的方法】
<router-link to='需要跳转到的页面的路径>
浏览器在解析时,将它解析成一个类似于<a></a> 的标签。

    <li >
        <router-link to="keyframes">点击验证动画效果 </router-link>   
     </li>
1. 不带参数
 
<router-link :to="{name:'home'}"> 
 
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name  
 
// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
 
 
2.带参数
 
<router-link :to="{name:'home', params: {id:1}}">  
 
 
// params传参数 (类似post)
 
// 路由配置 path: "/home/:id" 或者 path: "/home:id" 
 
// 不配置path ,第一次可请求,刷新页面id会消失
 
// 配置path,刷新页面id会保留
 
 
// html 取参  $route.params.id
 
// script 取参  this.$route.params.id
 
 
 
<router-link :to="{name:'home', query: {id:1}}"> 
 
// query传参数 (类似get,url后面会显示参数)
 
// 路由可不配置
 
// html 取参  $route.query.id
 
// script 取参  this.$route.query.id

2、this.$router.push() (函数里面调用)
params只能用name来引入路由
而query要用path引入

1.  不带参数
 
this.$router.push('/home')
 
this.$router.push({name:'home'})
 
this.$router.push({path:'/home'})
 
2. query传参 
 
this.$router.push({name:'home',query: {id:'1'}})
 
this.$router.push({path:'/home',query: {id:'1'}})
 
// html 取参  $route.query.id
// script 取参  this.$route.query.id
 
 3. params传参
 
this.$router.push({name:'home',params: {id:'1'}})  // 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
 
// 不配置path ,第一次可请求,刷新页面id会消失
 
// 配置path,刷新页面id会保留
 
// html 取参  $route.params.id
 
// script 取参  this.$route.params.id
 
 
4. query和params区别
 
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
 
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失

在helloworld.vue文件中

<template>
.....
<li @click="change">验证路由传参</li>
</template>
 
<script>
export default {
  data () {
    return {
      id:43,  //需要传递的参数
    }
  },
  methods:{
    change(){
      this.$router.push({  //核心语句
        path:'/select',   //跳转的路径
        query:{           //路由传参时push和query搭配使用 ,作用时传递参数
          id:this.id ,  
        }
      })
    }
  }
}
</script>

在select.vue文件中

<template>
  <select>
          <option value="1" selected="selected">成都</option>
          <option value="2">北京</option>
      </select>
</template>
 
<script>
    export default{
        data(){
            return{
                id:'',
            }
        },
        created(){  //生命周期里接收参数
            this.id = this.$route.query.id,  //接受参数关键代码
            console.log(this.id)   
        }
    }
</script>

3、this.$router.replace() (用法同上,push)

4、this.$router.go(n) ()

this.$router.go(n)
 
向前或者向后跳转n个页面,n可为正整数或负整数
ps : 区别
 
this.$router.push
	跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面
	
this.$router.replace
	跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
	
	
this.$router.go(n)
	向前或者向后跳转n个页面,n可为正整数或负整数

5.vue生命周期的理解

首先我们来看一下官网的生命周期图:
在这里插入图片描述
从最开始的流程往下分析整体

1. init events $ lifecycle(说明:初始化事件和生命周期)
什么是事件?什么是生命周期?vue api文档中事件有(on、once、off、emit四个方法),生命周期有(mount、forceUpdate、nextTick、destroy四个方法,其他那么多生命周期钩子应该也在这初始化的,要不然下一步的beforeCreate是哪里来的)

2. beforeCreate(组件刚被创建,组建属性计算之前,如data属性等 执行的钩子函数)
说白了这里给我们用户一个加入操作的入口,在这之前做了初始化事件和生命周期的事情,所以el现在还没初始化、data没有初始化,这也是有些人在这个方法里打印el、data都是undefined。

3.init injections & reactivity(通过依赖注入导入依赖项)
理解这句首先需要知道什么是“依赖注入”,vue中有provide和inject,可以将父组件provide中的对象注入到子孙的属性中。这么说这个过程reactive,就是将数据包装成一种可观测的类型,当数据产生变更的时候,我们能够感知到。这个操作后data数据应该就可以是响应的了。

4. created
在inject和reactive后再次给我们一次hook的机会;组件实例创建完成,属性已绑定,此时DOM还未生成 执行的钩子函数。

5. 检查vue配置,即new Vue{}里面的el项是否存在,有就继续检查template项。没有则等到手动绑定调用vm.$mount()

6. 检查配置中的template项
如果没有template进行填充被绑定区域,则被绑定区域的el对象的outerHTML(即整个#app DOM对象,包括

标签)都作为被填充对象替换掉填充区域。

7. beforeMount
模板编译、挂载之前执行的钩子函数;这里我有个疑问,这个钩子与create有什么区别,区别在于el属性已经创建完成,只是还没挂载到dom上。

8. create vm.$el and replace “el” with it
编译,并替换了被绑定元素;说白了就是我们用的模板语法被真实的数据替换了比如html中的{{msg}}被替换了真实的数据。

9. mounted
编译、挂载后执行的钩子函数

10. beforeUpdate
组件更新之前执行的钩子函数;当vue发现data中的数据发生了改变,会触发对应组件的重新渲染,先后调用beforeUpdate和updated钩子函数。

11. update
组件重新渲染后调用
最后还有两个destory

6.作用域链

什么是作用域链:当代码在一个环境中执行时,会创建变量对象的一个作用域链。
作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端始终是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象。作用域链的下一个变量对象来自包含环境,而在下一个变量对象则来自下一个包含环境。这样一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。

当代码在一个环境中执行时,都会创建一个作用域链。 作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。整个作用域链的本质是一个指向变量对象的指针列表。作用域链的最前端,始终是当前正在执行的代码所在环境的变量对象。
  如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,就是函数内部的arguments对象。作用域链中的下一个变量对象来自该函数的包含环境,而再下一个变量对象来自再下一个包含环境。这样,一直延续到全局执行环境,全局执行环境的变量对象始终是作用域链中的最后一个对象。

7.数组去重

1.将数组的每一个元素依次与其他元素做比较,发现重复元素,利用数组方法splice()删除重复元素

var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5,5,5,5];
function norepeat(arr) {
   for(var i = 0; i < arr.length-1; i++){
       for(var j = i+1; j < arr.length; j++){
           if(arr[i]==arr[j]){
               arr.splice(j,1); 
               j--;
           }
       }
   }
   return arr;
 }
var arr2 = norepeat(arr);
console.log(arr2);    //[1, 23, 3, 5, 6, 7, 9, 8]

2使用双层循环改变原数组

var arr = [1,1,2,2,3,3,4,4,5,5,4,3,1,2,6,6,6,6];
 	console.log(arr); 
	function norepeat(arr){
		for(var i=0;i<arr.length;i++){
			for(var j=0;j<arr.length;j++){
			    if(arr[i] == arr[j] && i !=j){
			        arr.splice(j,1);
		        }
		    }
		 }
		 return arr;
	}   
    var arr2=norepeat(arr);
    console.log(arr2);  //[1, 2, 3, 4, 5, 6]

3.借助新数组,判断新数组中是否存在该元素如果不存在则将此元素添加到新数组中(原数组长度不变但被按字符串顺序排序)

var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5];
console.log(arr); 
function norepeat(arr){
	var temp=[];
    var end;       //临时变量用于对比重复元素
    arr.sort();
    end=arr[0];
    temp.push(arr[0]);
    for(var i=1;i<arr.length;i++){
    	if(arr[i] !=end){   //当前元素如果和临时元素不等则将此元素添加到新数组中
	       temp.push(arr[i])
           end=arr[i]
         }
    }
    return temp;
}
var arr2=norepeat(arr);
console.log(arr2);   //[1, 23, 3, 5, 6, 7, 8, 9]

4.创建一个新数组,判断新数组中是否存在该元素如果不存在则将此元素添加到新数组中

  var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5];
        console.log(arr); 
        function norepeat(arr){
            var temp =[];
            for(var i=0;i<arr.length;i++){
                if(temp.indexOf(arr[i]) == -1){
                    temp.push(arr[i]);
                }
            }
            return temp;
        }
    var arr2=norepeat(arr);
    console.log(arr2);//[1, 23, 3, 5, 6, 7, 9, 8]

5.借助indexOf()方法判断此元素在该数组中首次出现的位置下标与循环的下标是否相等

var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5,5,5];
console.log(arr);    
function norepeat(arr) {
	for (var i = 0; i < arr.length; i++) {
		if (arr.indexOf(arr[i]) != i) {
			arr.splice(i,1);//删除数组元素后数组长度减1后面的元素前移
			i--;//数组下标回退
		}
	}
    return arr;
}
var arr2 = norepeat(arr);
console.log(arr2);   //[1, 23, 3, 5, 6, 7, 9, 8]

6.利用数组中的filter方法

var arr = ["apple","banana","pear","apple","orange","orange"];
console.log(arr);
var arr2 =arr.filter(function(value,index,self){
	return self.indexOf(value) ===index;
});
console.log(arr2);   //["apple", "banana", "pear", "orange"]

8.统计字符串中出现最多的字符

判断一个字符串中出现次数最多的字符,统计这个字符出现的次数:如字符串"abcdefgaddda",d出现次数最多,次数为4.
代码演示:

var str = 'abcdefgaddda';
var arr = str.split('');   //将字符串转为数组
var newArr = [];      //声明一个数组保存去重后的字符
var numArr = [];      //声明一个数组保存字符对应的个数
arr.forEach(function(element,index,array){
	var index1 = newArr.indexOf(element);    //获取当前元素在去重数组中的索引,如果存在则大于等于0,不存在则为-1
	if(index1==-1){
		newArr.push(element);   //判断去重数组里没有当前元素,所以往数组里面追加
		numArr.push(1);          //同步更新个数组对应的字符个数,刚追加进去都为1
	}else{
		numArr[index1]++;        //如果当前元素已存在,则更新个数数组对应的字符个数自增1
     }
})
console.log(arr,newArr,numArr);
//["a", "b", "c", "d", "e", "f", "g", "a", "d", "d", "d", "a"] 原字符数组
//["a", "b", "c", "d", "e", "f", "g"]  去重后的字符的数组
//[3, 1, 1, 4, 1, 1, 1]   去重后的字符数组对应的个数数组
//得到去重后的字符数组及对应的字符个数后,找个数最大的数及对应的字符
function sortNumber(a,b){
	return b-a;   //规定排序规则
}
var numArr1 = [].concat(numArr);   
//创建一个新数组并连接原数组,这样改变原数组才不会影响复制后的数组
numArr1.sort(sortNumber);  //将每个字符的个数从大到小排序
var maxNum = numArr1[0];   //获取最大个数
var index = numArr.indexOf(maxNum);   //最大个数对应原数组的位置
var maxStr = newArr[index];           //根据最大个数的位置找到出现次数最多的字符
console.log('字符串"'+str+'",'+maxStr+'出现次数最多,次数为'+maxNum);   

9.js垃圾回收机制

1垃圾收集

  • 什么是垃圾:
    • 一般来说,没有被引用的对象就是垃圾,就要被清除。
    • 有个例外,如果几个对象引用形成一个环,它们互相引用,但是根访问不到它们,这几个对象也是垃圾,也要被清除。
  • 垃圾回收GC的全拼是 Garbage Collection 其在维基百科的定义是:在计算机科学中,垃圾回收是一种自动的内存管理机制。当一个电脑上的动态内存不再需要时,就应该予以释放,以让出内存,这种内存资源管理,称为垃圾回收
  • JavaScript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。
  • 垃圾收集机制的原理:
    • 找出那些不再继续使用的变量,然后释放其占用的内存。
    • 为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作。

2.算法

2.1标记清除(Mark-Sweep GC)

  • 标记阶段:从根集合出发,将所有活动对象及其子对象打上标记

  • 清除阶段:遍历堆,将非活动对象(未打上标记)的连接到空闲链表上

  • 优点

    • 实现简单,容易和其他算法组合
  • 缺点

    • 碎片化,会导致无数小分块散落在堆的各处
    • 分配速度不理想,每次分配都需要遍历空闲列表找到足够大的分块
    • 与写时复制技术不兼容,因为每次都会在活动对象上打上标记

2.2引用计数(Reference Counting)

  • 引用计数,就是记录每个对象被引用的次数,每次新建对象、赋值引用和删除引用的同时更新计数器,如果计数器值为0则直接回收内存。很明显,引用计数最大的优势是暂停时间短
  • 优点
    • 可即刻回收垃圾
    • 最大暂停时间短
    • 没有必要沿指针查找,不用和标记清除算法一样沿着根集合开始查找
  • 缺点
    • 计数器的增减处理繁重
    • 计数器需要占用很多位
    • 实现繁琐复杂,每个赋值操作都得替换成引用更新操作
    • 循环引用无法回收

2.3解除引用

  • 一旦数据不再有用,最好通过将其值设置为null来释放其引用,这个做法叫做解除引用(dereferencing)。
  • 这一做法适用于大多数全局变量和全局对象的属性。
  • 局部变量会在它们离开执行环境时自动被解除引用。

10.原型原型链

原型是js中实现继承的过程中产生的一个概念
继承:指在一个对象的基础上创建新对象的过程,原型指在这过程中作为基础的对象。
prototype对象
使用new对象会生成一个Student对象的实例
缺点:无法共享属性和方法
比如:

function Student(name){
    this.name=name;
    this.species='理科生';
    return this;
}
var stu1 = new Student('张三');
var stu2 = new Student('李四');
sru1.species = '文科生';
console.log(stu1);//Student {name: "张三", species: "文科生"}
console.log(stu2);//Student {name: "李四", species: "理科生"}

我们改变了stu1的species,不会影响stu2中的species.
每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。
prototype属性
这个属性包含一个对象,所有实例对象需要共享的属性和方法,都放在这个对象里面;哪些不需要共享的属性和方法, 就放在构造函数里面。
实例一旦创建,将自动引入prototype对象的属性和方法,也就是说,实例对象的属性和方法,分为两种,一种是本地的,另一种是引用的。

function Student(name){
    this.name=name;
    return this;
}
Student.prototype = {species:'理科生'};
var stu1 = new Student('张三');
var stu2 = new Student('李四');
console.log(stu1.species);//理科生
console.log(stu2.species);//理科生prototype

现在,species属性是放在prototype对象中,是两个实例对象共享的。只要修改了prototype,就会同时影响到两个实例对象。

Student.prototype.species = '文科生';       
console.log(stu1.species);//文科生
console.log(stu2.species);//文科生生prototype

原型链
讲原型一个不可避免的概念就是原型链,原型链是通过前面两种创建原型Object.create()或dog.prototype的方式生成的一个_prtoto_指针来实现的。

function foo(){
    foo.prototype.z=3;
}
var obj = new foo();
obj.x = 1;
obj.y = 2;
console.log(obj);//foo {x: 1, y: 2}
console.log(obj.z);//3

对象obj上是没有z的,但是他的原型链上有z,所以查找的时候如果对象本身不存在的话就会沿着原型链往上查找

obj.z=5;
console.log(obj.z);//5
console.log(foo.prototype.z);//3

如果对象上有的话则不会向原型链上查找,而且并不会改变原型链上的值

总结一下原型链作用:对象属性的访问修改和删除。

访问。优先在对象本身查找,没有则顺着原型链向上查找
修改。只能修改跟删除自身属性,不会影响到原型链上的其他对象。
总结
由于所有的实例对象共享同一个 prototype 对象,那么从外界看起来,prototype 对象就好像是实例对象的原型,而实例对象则好像”继承”了 prototype 对象一样。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值