前端面试的笔记整理

188 阅读10分钟

1、浅拷贝和深拷贝

数据分为基本数据类型和引用数据类型。

基本数据类型:数据直接存储在栈中;

引用数据类型:存储在栈中的是对象的引用地址,真实的对象数据存放在堆内存中。

浅拷贝:

对于基本数据类型:直接复制数据值;

对于引用数据类型:只是复制了对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值也随之改变。

深拷贝:

对于基本数据类型:直接复制数据值;

对于引用数据类型:开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象。

深拷贝相比于浅拷贝速度较慢并且花销较大

2. substr和substring的区别

它们两者都是截取字符串的作用

相同点:如果只是写一个参数,两者的作用都一样:都是是截取字符串从当前下标以后直到字符串最后的字符串片段。

var str = '123456789abcd'; 

console.log(str.substr(2)); // "3456789" 

console.log(str.substring(2)) ;// "3456789"

不同点:它们的第二个参数

substr(startIndex,lenth): 它的第二个参数是截取字符串的长度,从第一个参数的后面一位开始截取,截取第二个参数的长度后停止

substring(startIndex, endIndex):它的第二个参数是截取的是另外一个索引,也就是第一个参数和第二个参数之间的截取,但截取尾部,不截取头部

console.log("123456789".substr(2,5)); // "34567" 
console.log("123456789".substring(2,5)) ;// "345"

3. slice和splice的区别

slice方法是用来截取数组的元素,返回一个新的数组

slice方法有两个参数,第一个参数为截取的开始位置,为数组的索引;第二个参数为截取的结束位置。但不包含结束位置的元素。

let array = [1, 2, 3, 4, 5];
let array1 = array.slice(0,2); //输出: [1,2]
let array2 = array.slice(2,3); //输出: [3]
let array3 = array.slice(4); 	 //输出: [5]

splice方法用来向数组添加元素或者删除数组某个元素,然后删除的元素。

第一个参数为插入元素或者删除元素的位置,第二个参数为要删除的元素数量。后面的每个参数都会依次添加到数组中(从删除的位置开始)。

let array1 = [1, 2, 3, 4, 5];
let array2 = [1, 2, 3, 4, 5];
let array3 = [1, 2, 3, 4, 5];

const newArray1 = array1.splice(0,2);
//输出: [1, 2]; 原数组: [3, 4, 5]
const newArray2 = array2.splice(3);
//输出: [4, 5]; 原数组: [1, 2, 3]
const newArray3 = array3.splice(3, 1, "a", "b", "c");
//输出: [4]; 原数组: [1, 2, 3, "a", "b", "c", 5]

4.事件循环—宏任务和微任务

js是单线程的,只有一个调用栈,调用栈按照先入后出的规则进行,一次调用一个,在执行调用栈的时候,会先执行同步任务(指马上就能执行的任务),调用栈在发现一部任务的时候会把异步任务放入到异步任务的集合队列里面,异步任务里面又分为宏任务微任务两种

宏任务有:新程序或子程序会被直接执行(script里面写的代码)事件的回调函数,定时器

微任务有Promise.then() .catch() .finally(), Object.observe

每次宏任务结束后事件循环就会先执行微任务,知道微任务里面的任务清空了才会执行下一轮宏任务

5.watch和计算属性

既能实现computed又能够实现watch的推荐使用computed,重点在于computed有缓存功能。

computed是用来声明式的描述一个值依赖其它的值,当所有依赖的值或者变量改变的时候,计算属性也会随着该变;所以计算属性取值返回的是最新的结果 ,但里面不能异步的返回结果以及异步的逻辑

watch主要是监听data里面的定义的量,当该变量变化的时候,触发watch的监听事件,可以进行一些异步操作逻辑

6.js数据类型和数据类型检测

数据类型:

简单数据类型6种:字符串(String),数字(Number),布尔值(Boolean),空(Null),Undefined,Symbol

复杂数据类型:对象(Object)是唯一的复杂数据类型。数组(Array)和函数(Function)这些引用类型最终也归类与Object复杂数据类型

数据类型的检测的方法

typeof:

基本类型里面除了Null(返回object)以外,均可以返回正确的值类型。

复杂类型里面除了function(返回function)以外,都会返回object

7、生命周期,父子组件生命周期

生命周期

  1. 什么是生命周期 组件实例从创建到销毁的过程就是生命周期

  2. vue有四个阶段 创建 挂载 更新 销毁

  3. 创建 有二个钩子 beforeCreate created 在工作一般用的是created 拿ajax数据

  4. 挂载 有二个钩子 beforeMount mounted 在工作中可以在mounted 里面去操作DOM

  5. 更新阶段 beforeUpdate updated 在工作的时候可以在updated里面 拿到最新的DOM效果

  6. 销毁阶段 beforeDestroy destroyed 我们可以在beforeDestroy中 去清理当前组件中有的定时器和DOM监听的事件

如果面试官继续问,就需要答keep-alive相关的钩子二个 activated,deactivated

父子组件的生命周期执行顺序

创建执行顺序

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

更新阶段顺序

父beforeUpdate->子beforeUpdate->子updated->父updated

销毁阶段顺序

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

8、new的过程

  1. 内存中创建一个空对象
  2. 我们将这个空对象的**--proto--**成员指向了构造函数对象prototype成员对象
  3. 我们将构造函数对象的this指针替换成空对象,然后再调用构造函数,于是构造的this就指向了新的空对象,并且会往实例化对象身上挂载方法
  4. 返回实例化对象

9.原型链

一个对象有原型对象,它的原型对象也有自己的原型对象,一直往上找,找到Object对象,Object对象的的原型对象是null,在往上就没了。这个像链式一样的结构称为原型链。其本质描述的是对象的一种继承关系。

10.如何实现三角形

原理:利用border是梯形的形状来进行绘制,所以将元素的width和height设置为0,将其他的边框颜色设置为transparent透明,就能得到一个三角形

试例代码:

<div class="triggle"></div>

.triggle{
width: 0px; 
height: 0px; 
border-top: 50px solid transparent; 
border-left: 50px solid transparent; 
border-bottom: 50px solid red; 
border-right: 50px solid transparent;

11.高度为0.5像素px

正常想法 height:0.5px 这个想法是不对的,谷歌浏览器会自动将他变为1px

正常操作:我们可以使用css的transform属性,把Y轴缩小为0.5倍,利用transform-origin的方法,第一个值50%意思是x轴移动到居中的位置,第二个值为100%意思是将Y轴移动到底部

实列:

.line{
    height:1px;
    transform:scaleY(0.5);
    transform-origin:50%  100%;
}

12.重绘与回流

回流:当我们render tree中的一些元素的结构或者尺寸发生变化,浏览器重新渲染部分或者全部文档的过程就是回流

会导致回流的操作: 页面首页渲染 浏览器窗口大小发生变化 内容变换 添加或者删除节点 激活 css伪类 clinentWidth

重绘:当页面中元素样式的改变不影响它在文档流中的位置,浏览器会将新的样式赋予给元素。

会导致重绘的操作: background visibility

总结:回流一定会引起重绘,重绘不一定引起回流

13.v-model底层原理

数据发生了变化,会同步视图,视图发生了变化,同步数据

v-model是一个特殊的指令,一般用于表单控件,可以实现双向数据绑定

<input v-model="msg"/>
<input v-bind:value="msg"v-on:input="msg=$event.target.value"/>
<input value="msg"@input="msg=$event.target.value"/>

13.let const var区别

var

    • var只有函数作用域,没有块作用域,可以声明全局/局部变量(在一共函数内声明的变量,只在该函数有效)
    • var定义的变量不能跨函数访问,但是可以跨块访问
    • var 定义的变量如果不初始化会输出undefined,但不会报错
    • 可以重复定义,后定义的会覆盖先定义的。

let

  1. . let是块级作用域,函数内部使用let定义后,对函数外部无影响
  2. . let定义的变量只能在块作用域中访问,不能跨块访问,更不能跨函数访问
  3. . 不能变量声明提前,否则会报错
  4. . 不能重复定义,否则会报错

const

    • 块级作用域内有效;
    • const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
    • 对于复合类型的变量,如数组和对象,变量名不指向数据,而是指向数据所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须非常小心。

14.computed和 watch哪个可以异步

watch

因为计算属性(computed)是通过return返回值传递参数 异步操作的时候return是没有意义的

15.观察者模式

观察者模式定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新,属于行为型模式。

举个例子:

image.png

两大主体

目标对象:subject 观察者列表 observerlist

当subject(海王)自身心情发生变化,,通过自己的notify(发朋友圈)的方法依次通知每个观察者的自己身上的update(置顶关心)的方法

观察者observer(舔狗)需要开通朋友圈权限update方法,能接收海王的信息,update方法中可以执行自定义逻辑

16.flex1

  1. flex是一个复合属性 是 flex-grow,flex-shrink,flex-basis简写
  2. flex:1 -> flex-grow:1;flex-shrink:1;flex-basis:0%;
  3. flex-grow:1; 当父盒子宽度比所有子盒子宽度宽的时候,剩余空间 放大比例 flex-shrink 缩小比例
  4. flex-basis不加的话,width就是减掉,加上flex-basis:0%;原来width相当于没有写(flex 和 width)

17.img标签 alt和title的区别

alt:当加载图片失败时显示的内容 title:当光标放在图片上时出现的内容

18.link和@import到底有什么区别?

  1. link属于html标签,而@import是css提供的。
  2. 页面被加载时,link会同时被加载,而@import引用的css会等到页面被加载完再加载的。
  3. 兼容性问题:@import只在IE5以上才能识别,而link是html标签,无兼容性问题。 4.权重问题:@import的权重要高于link。 5.DOM操作:DOM可以操作link中的样式,而不可以操作@import中的样式。

19.防抖节流

防抖 原理:当事件被触发时,设定一个周期延迟执行动作,若期间又被触发,则重新设定周期,直到周期结束,执行动作。

节流 原理:固定周期内,只执行一次动作,若有新事件触发,不执行。周期结束后,又有事件触发,开始新的周期。

20.数组去重方法

定义一个数组和一个新的空数组

var arr = [1,'true','dpn',1,2,'3',3,'false','dpn',1,'leave'] var arr1 = []

1、indexof

arr.forEach((element) => {
if(arr1.indexOf(element) == -1) {
arr1.push(element) 
} 
}); 
console.log(arr1)

2、双重for循环

for (let i = 0; i < arr.length; i++) {
	for (let j = i+1; j < arr.length; j++) {
		if (arr[i] === arr[j]) {
			 arr.splice(j,1)
		}
	} 
}
console.log(arr)

3、includes

arr.forEach((element) => {
   if(!arr1.includes(element)) {
   	  arr1.push(element)
    }
});
console.log(arr1)

4、利用ES6 Set去重 (有兼容性)

console.log(Array.from(new Set(arr)))

21.router和route的区别

route

通过 $route.params 获取动态路由参数

在动态路由渲染出来的组件中,可以使用 this.route.params 对象访问到动态匹配的参数值。this.route.params 对象访问到动态匹配的参数值。this.route 里面存放的是当前页面路由的相关信息。

router

vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是:

this.$router.push('hash 地址')

 跳转到指定 hash 地址,并增加一条历史记录