前端面试复习总结-为学弟学妹秋招护驾
1,实现居中
水平居中:
- ext-align:center;
- margin: 0 auto;
- display:flex; justify-content: center;
- position:absolute;left: 50%; transform: translateX(-50%);
垂直居中:
- line-height等于高度
- position:absolute; top: 50%; transform: translateY(-50%);
- display:flex; align-items:center;
- 父元素display:table; 子元素display:table-cell;vertical-align:middle;
2,闭包的概念
- 概念:闭包就是能读取其他函数内部变量的函数
- 优点:1、避免全局变量的污染;2、希望一个变量长期存储在内存(缓存变量);
- 缺点:1、容易造成内存泄露 内存泄露例子:
let getMoney = function() {
let money = 10
return function() {
return money
}
}
let f = getMoney()
// f函数存在的话,money会一直得不到释放
3,深拷贝和浅拷贝
浅拷贝:
Object.assign()
Array.prototype.slice()
深拷贝:
JSON.parse(JSON.stringify()) // 无法序列化函数
递归函数:
function cloneObject(obj) {
var newObj = {} //如果不是引用类型,直接返回
if (typeof obj !== 'object') {
return obj
}
//如果是引用类型,遍历属性
else {
for (var attr in obj) {
//如果某个属性还是引用类型,递归调用
newObj[attr] = cloneObject(obj[attr])
}
}
return newObj
}
// 无法解决循环引用问题
4,数组去重
ES6中的set:
let arr = [1,2,3,1,2,3]
let arr2 = [...new Set(arr)]
新数组去重:
let newArr = []
let arr = [1,2,3,1,2,3]
arr.forEach(item => {
if (newArr.indexOf(item) === -1) {
newArr.push(item)
}
})
5,JS执行机制、事件循环
- 事件循环:同步任务进入主线程,异步任务进入EventTable中并注册回调函数中。当所有同步任务执行完毕时,主线程从事件队列中读取回调函数并执行。上述过程不断重复,称为事件循环
6, 宏任务和微任务
- 异步任务会分成宏任务与微任务
- 微任务是在宏任务执行完后立即执行的任务
- 宏任务:主代码块,setTimeout,setInterval等
- 微任务:Promise,process.nextTick等
- 当宏任务执行完时,如果有可执行的微任务,则执行所有微任务后再执行新的宏任务。如果没有可执行的微任务,那么直接开始执行新的宏任务
7,函数提升与变量提升
在作用域中,函数声明与变量声明都会提升到顶部。首先声明变量整体,然后再声明函数整体。
function v() {
console.log(a)
var a = 1;
function a() {}
}
v()
// 输出函数a
// 上面函数相当于:
function v() {
var a;
function a() {}
console.log(a)
a = 1;
}
v()
注意:函数表达式不会被提升
8,常见内存泄露
- 1、全局变量滥用,当不用var声明变量的时候,比如b=1,相当于挂载到全局变量。解决方法:使用严格模式
- 2、没有清理DOM元素的引用
- 3、闭包
- 4、定时器被遗忘
9,性能优化
- 1、减少请求数量
- 2、事件委托:减少DOM操作,每个新增的元素都能拥有该事件
- 3、压缩资源大小
- 4、减少重绘回流
- 5、innerHTML代替dom操作
- 6、图片预加载
10,Vue性能优化
- 1、路由懒加载
- 2、v-if与v-show正确使用
- 3、v-for中设置唯一key
- 4、图片懒加载
11,实现EventBus
function EventEmitter() {
this.events = new Map()
this.addListener = function (name, fn) {
this.events.set(name, fn)
}
this.emit = function (name) {
let fn = this.events.get(name)
fn(...[...arguments].slice(1))
}
}
var bus = new EventEmitter()
bus.addListener('age', (age, time) => {
console.log(age, time)
})
bus.emit('age', 18, '2019')
12,引用类型有哪几种
- Object
- Array
- Date
- RegExp
- Function
12,CSS盒模型
- Margin、Border、Padding、Content
- IE定义的盒模型中元素的宽高包括了padding和border
- W3C盒模型元素宽高为Content
- box-sizing: content-box; // W3C盒模型
- box-sizing: border-box; // IE盒模型
13,伪类和伪元素的区别
- 它们之间的根本区别在于是否创造了新的元素
- 伪类不会创造新的元素,比如:hover :active
- 伪元素会创造新的元素,比如:after :before
- 伪类:表示已存在的某个元素处于某种状态
- 伪元素:用于将特殊的效果添加到某元素
14,实现快速排序
function quickSort(arr) {
if (arr.length < 1) {
return arr
}
var left = []
var right = []
var pivot = arr[0]
for (var i = 1; i < arr.length; i++) {
if (arr[i] > pivot) {
right.push(arr[i])
} else {
left.push(arr[i])
}
}
return [...quickSort(left), pivot, ...quickSort(right)]
}
var arr = [0, 3, 4, 2, 6, 1, -1, 123]
console.log(quickSort(arr))
// ----------------------
console.log('------------')
// ----------------------
// 单指针循环法
function quickSort2(arr) {
if (arr.length < 1) {
return arr
}
var pivot = arr[0]
var mark = 0
for (var i = 0; i < arr.length; i++) {
if (arr[i] < pivot) {
mark++
let temp = arr[i]
arr[i] = arr[mark]
arr[mark] = temp
}
}
let temp = arr[0]
arr[0] = arr[mark]
arr[mark] = temp
return [...quickSort2(arr.slice(0, mark)), arr[mark], ...quickSort2(arr.slice(mark+1, arr.length))]
}
var arr = [0, 3, 4, 2, 6, 1, -1, 3]
console.log(quickSort2(arr))
15,堆排序
// 最小堆,父节点小于两个子节点
function downAjust(arr, parentIndex, length) {
let childIndex = 2 * parentIndex + 1
while (childIndex < length) {
if (childIndex + 1 < length && arr[childIndex + 1] < arr[childIndex]) {
childIndex ++
}
if (arr[parentIndex] < arr[childIndex]) {
return
}
let temp = arr[parentIndex]
arr[parentIndex] = arr[childIndex]
arr[childIndex] = temp
parentIndex = childIndex
childIndex = 2 * parentIndex + 1
}
}
function buildHeap(arr) {
for (let i = parseInt((arr.length -1) / 2); i >= 0; i--) {
downAjust(arr, i, arr.length)
}
console.log(arr)
}
var arr = [7, 1, 3, 10, 5, 2, 8, 9, 6]
buildHeap(arr)
heapSort(arr)
function heapSort(arr) {
for (let i = arr.length - 1; i >= 0; i--) {
let temp = arr[i]
arr[i] = arr[0]
arr[0] = temp
downAjust(arr, 0, i)
}
console.log(arr)
}
16,归并排序
function merge(left, right) {
let arr = []
while (left.length && right.length) {
if (left[0] < right[0]) {
arr.push(left.shift())
} else {
arr.push(right.shift())
}
}
arr.push(...left, ...right)
return arr
}
function mergeSort(arr) {
if (arr.length > 1) {
let mid = parseInt(arr.length / 2)
let left = arr.slice(0, mid)
let right = arr.slice(mid)
return merge(mergeSort(left), mergeSort(right))
}
return arr
}
var arr = [8, 2, 4, 1, 3, 9, 0, -1, -2]
console.log(mergeSort(arr))
17,实现二分查找
function binarySearch(arr, target) {
let low = 0
let high = arr.length - 1
while (low <= high) {
let mid = parseInt((low + high) / 2)
if (arr[mid] > target) {
high = mid - 1
} else if (arr[mid] < target) {
low = mid + 1
} else {
return mid
}
}
return -1
}
var arr = [0, 1, 2, 3, 3, 4, 5]
console.log(binarySearch(arr, 5))
18,数组和链表的区别
数组:
- 在内存中连续存储
- 优点是具有高效的随机访问能力,常量时间内可以找到对应元素
- 缺点是在插入和删除元素时,可能会导致大量元素被迫移动,影响效率
- 数组适合在读操作多,写操作少的场景
链表:
- 在内存中是随机存储
- 优点是能够灵活地进行插入删除操作
- 缺点是查找元素的性能较差
19,URI和URL
- URI就是统一资源标识符,URL就是统一资源定位符
- URI有两种形式,URL和URN
- URL由协议、主机、端口、路径四个部分组成,唯一标识资源位置
- URN是统一资源名,它使得资源与位置无关,只使用资源名就能够获取资源,这种形式处于试验阶段,尚未被大范围使用
20,TCP与UDP
- TCP是一种可靠的传输方式,能够将数据按序无差错传输。HTTP使用TCP来传输其报文数据。
- TCP的建立需要经过三次握手
- UDP是一种不可靠的传输方式,它不保证数据能准确传输。在允许丢包的情况下使用UDP传输效率会更高,比如视频、音频,个别丢包不影响整体画面
21,访问一个网址时浏览器经历的步骤
- 1、浏览器从URL中解析主机名和端口
- 2、DNS解析出IP地址
- 3、建立TCP连接
- 4、浏览器发送HTTP请求报文
- 5、服务器返回HTTP响应报文
- 6、关闭连接,浏览器开始解析文档
- 7、浏览器首先解析出DOM树
- 8、再次解析出CSSOM树
- 9、根据DOM树和CSSOM树来构造render tree渲染树
- 10、浏览器重排、重绘,绘制每个节点
22,重排、重绘
- 浏览器默认采用流式布局模型
- 重排也叫回流
- 重排:根据渲染树中每个对象的信息,计算渲染对象各自的几何信息(位置、尺寸大小),并将其安置在界面正确位置。
- 因此呢,如果发生改变了位置、尺寸大小,会引发回流,重新计算全局布局(从根节点重新布局)或者局部布局。例如,改变窗口大小会引发全局布局的重新计算;
- 引起重排的操作:
- 页面首次渲染
- 窗口大小改变
- 元素尺寸或位置改变
- 字体大小改变
- 添加或删除DOM元素
- 获取offsetTop、offsetLeft等属性触发重排,应缓存起来
- 重绘:当页面某元素样式的改变并不影响其在文档流中的位置
- 例如改变背景颜色、字体颜色等操作
23,强缓存与协商缓存
- 强缓存:服务器通过设置response
header中的cache-control字段来控制浏览器对资源缓存,max-age用于设置缓存的过期时间。如果是cache-control:no-cache,代表跳过设置强缓存,会走协商缓存。 - 协商缓存:当强缓存过期后,客户端每次访问资源时会先看下缓存有无过期,如果过期了,则询问服务器资源是否已更改,如果已更改,服务器会返回资源以及对应的etag和last-modified;如果没有更改,则服务器返回一样的etag和last-modified,状态码为304(资源无变更),这时客户端之后访问缓存都要走一次协商缓存。
- etag:每个文件的hash
- last-modified:资源最后更改时间
- 强制缓存是我们在第一次请求资源时在 http 响应头设置一个过期时间,在时效内都将直接从浏览器进行获取,常见的 http 响应头字段如
Cache-Control 和 Expires 协商缓存是我们通过 http 响应头字段 etag 或者 Last-Modified等判断服务器上资源是否修改,如果修改则从服务器重新获取,如果未修改则 304 指向浏览器缓存中进行获取
24,call、apply、bind
- 这三个函数都是改变this的指向
- call、apply第一个参数都是this所指向的对象,它们之间的区别是传参的方式不同。
- apply的传参方式是以数组的方式传参,而call是以逐个传参的方式
- bind函数的第一个参数也是this所指向的对象,它和上面call、apply不一样,它返回一个新的函数。
25,完全二叉树与满二叉树
- 满二叉树:深度为k,并且有2^k-1个节点的二叉树
- 完全二叉树:完全二叉树的n个节点的编号都能对应相同深度满二叉树的1~n个节点一一对应
26,Promise对象
Promise有三个状态:
- 1、进行中pending;2、成功态fulfilled;3、拒绝rejected;
- Promise构造函数接受一个参数为resolve和reject的函数
- resolve、reject能改变promise的状态。
- 通过then方法为promise注册完成时的处理程序
27,Vue生命周期
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestory、destoryed
- created周期在模板渲染成HTML前调用,能够获取到实例的data,通常初始化某些属性值
- mounted周期在模板渲染成HTML后调用,通常是初始化页面完成后对DOM进行一些操作。
28,web worker
JavaScript是单线程语言,WebWorker是为了创造多线程环境,允许主线程创建Worker线程,这样worker线程完成任务后会自动返回结果给主线程。
需要注意以下几点:
- 1、主线程与worker线程必须同源
- 2、worker无法读取主线程的Dom对象
- 3、不能直接通信,需要通过消息postMessage来完成
29,封装ajax
function ajax(url, type, data, success) {
let aj = new XMLHttpRequest()
if (type === 'get') {
if (data) {
url += '?'
let arr = []
Object.keys(data).forEach(key => {
arr.push(key + '=' + data[key])
})
url += arr.join('&')
}
aj.open(type, url)
aj.send(data)
} else if (type === 'post') {
aj.open(type, url)
aj.setRequestHeader('Content-type', 'x-www-form-urlencoded')
if (data) {
aj.send(data)
} else {
aj.send()
}
}
aj.onreadystatechange = function() {
if (aj.readyState === 4 && aj.status === 200) {
success(aj.responseText)
console.log(aj.responseText)
return aj.responseText
}
}
}
30,for…in与for…of
- for…in循环用于获取键名
- for…of循环用于获取键值
代码如下
var arr = ['a','b','c']
for (var i in arr) {
console.log(i)
}
// 0, 1, 2 //遍历数组就是获取下标
for (var i of arr) {
console.log(i)
}
// a b c //遍历数组就是获取值
var obj = {
a: 1,
b: 2,
c: 3
}
for (var i in obj) {
console.log(i)
}
// a b c //遍历对象就是获取键名
for (var i of arr) {
console.log(i)
}
// 用在对象会报错
31,防篡改对象
- 主要使用三个方法Object.preventExtensions、Object.seal、Object.freeze
- 分别对应为不可扩展、密封、冻结
- 通过 Object.isExtensible([Object]) 可以确定对象是否可以扩展。 true 可以扩展, false不可以扩展。
- 通过 Object.isSealed([Object]) 可以确定对象是否被密封了。
- 通过 Object.isFrozen([Object]) 来检测对象是否被冻结。
32,数组扁平化
var arr = [1, 2, [3,4], [1, [2, 3, [3, 4]]]]
function fn(arr) {
let newArr = []
for (var i of arr) {
if (i instanceof Array) {
newArr.push(...fn(i))
} else {
newArr.push(i)
}
}
return newArr
}
console.log(fn(arr))
33,判断数据类型
- 判断数组:Array.isArray()
- typeof:能判断的数据类型有String、number、objcet、function、boolean、undefined。不能判断null和array
typeof Symbol(); // symbol 有效
typeof ''; // string 有效
typeof 1; // number 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof new Function(); // function 有效
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效
- instanceof:判断是否某个原型的实例,不能检测null和undefined
[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
new RegExp() instanceof RegExp//true
null instanceof Null//报错
undefined instanceof undefined//报错
- Object.prototype.toString.call():这个较为准确,不能检测自定义类型
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
34,BFC
块格式化上下文,BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素
如何创建BFC:
- 1、display为inline-block table-cell flex
- 2、浮动元素float不是none
- 3、定位元素:absolute、fixed
- 4、overflow除了visible以外的值
BFC的作用: - 1、避免margin重叠(放在不同的BFC)
- 2、避免高度塌陷
- 3、包裹浮动元素
BFC布局规则: - 1、在BFC中,盒子(块级元素)会在垂直方向排列
- 2、box垂直方向的距离有margin决定,同一个BFC的margin会重叠(以最大值为准)
35,清除浮动
- 1、clear:both(通过after伪元素清除浮动)
- 2、父级div设置overflow:hidden(触发BFC)
36,async和await
- async函数返回一个Promise对象
- await用于等待一个异步函数执行结果
- 这两个配合使用能使得代码看起来更像同步代码
37,await处理错误
把await的代码放在try…catch块中或者使用catch注册函数
async function MyAsync() {
try{
await doSomething()
} catch(err) {
console.log(err)
}
await doAnotherThing().catch((err) => {
console.log(err)
})
}
// 以上两种方法皆可以
37,Cookie的弊端
- 1、每个特定的域名下最多生成20个cookie
- 2、最大存储空间为4K
- 3、安全性问题。被恶意获取到时他人只需原样转发cookie便可以达到目的
38,CSS中 link 和@import 的区别是?
- link是HTML标签,无兼容性问题
- @import是CSS提供的,需要IE5以上才能识别
- 页面加载时link会同时被加载,而import会在页面被加载后再加载
39,CSS 选择符有哪些?哪些属性可以继承?优先级算法如何计算?
css选择器:
- id选择器#id
- 类选择器.class
- 属性选择器a[href=“bb”]
- 后代选择器h2 p
- 子代选择器div > p
- 相邻选择器div + h1
- 伪类选择器a:hover
- 标签选择器div,p
- 可继承样式:
- font-size font-family color 不可继承样式:
- margin padding border width height 优先级:
- !important > 内联 > id > class > tag
- !important比内联优先级高,内联优先级比id高
40,HTML语义化
- 1、当样式丢失时,页面能够呈现出清晰 的结构
- 2、有利于SEO搜索爬取有效信息
- 3、方便其他设备解析(屏幕阅读器、盲人阅读器)
41,new操作符的作用
- 1、创建空对象,并将this指向该对象
- 2、属性和方法被加入到this指向的对象中
- 3、最后隐式返回this
42,相等操作符的比较
- ==操作符会将操作数进行数据类型的强制转换
- 1、如果有操作数为布尔值,则布尔值转为数字,false转0,true转1
- 2、如果有操作数为字符串,则字符串转为数字
- 3、如果有操作数为对象,则调用valueOf, 没有valueOf就有toString,都没有就返回NaN
- 反正就是都转为数字来进行比较
43,对象转字符串或数字
var a = {
toString: () => '1',
valueOf: () => 0
}
a == '1' // false
a == 0 // true
var b = {
toString: () => '1'
}
b == '1' // true
对象转字符串:
- 1、判断对象是否有toString,有则调用
- 2、没有toString,判断有无valueOf,有则调用
- 3、都没有,报错
对象转数字:
- 1、判断有无valueOf,有则调用
- 2、没有valueOf,判断有无toString,有则调用
- 3、都没有。报错
44,虚拟DOM
- 虚拟DOM其实就是用JavaScript去模拟真实的DOM,当DOM变化的时候,先在虚拟DOM上进行新旧对比,然后将差异部分更新到真实的DOM上。
45,Vue中key值的作用
- key值主要出现在使用v-for的时候,需要为每个渲染元素指定key值。当v-for更新列表时,默认采用就地复用的策略。如果顺序被打乱,vue不会移动DOM元素来匹配,而是用就地复用每个元素。key的作用是为了更高效的更新虚拟DOM
- 为每个元素指定key值,以便vue能跟踪节点的身份
46,Vue组件通信
- 1、props和emit:props是父组件向子组件单向传值,子组件不能改变父组件的属性。因此子组件只能通过emit:props是父组件向子组件单向传值,子组件不能改变父组件的属性。因此子组件只能通过emit向父组件派发事件来进行传值。
- 2、vuex:这是vue配套的状态管理插件,通过抽离部分状态为全局状态,使整个数据流变得清晰可追踪。
47,Vue的prop单向数据流
- 父级prop的更新将向下流动到子组件中,反过来不行。但是如果传入的prop是引用类型比如数组或者对象,那么子组件对prop的改动会影响父组件的状态
48,vuex有哪几种属性
- state、getter、mutation、action、module
49,vue中的v-model
- v-model实际上是默认利用了名为value的prop属性和名为input的事件,通过model选项来指定prop和事件的名称
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
50,vue的插槽
使用插槽可以将组件内的slot替换为任何模板代码
<navigation-link url="/profile">
Your Profile
</navigation-link>
然后你在 的模板中可能会写为:
<a
v-bind:href="url"
class="nav-link"
>
- 注意:如果组件内没有slot元素,那么组件起始标签和结束标签之间的任何内容都会被抛弃
- 当含有多个插槽时,为每个插槽指定一个name,这是就可以指定希望往哪个具名插槽内添加模板代码了
51,箭头函数
- 箭头函数没有this、arguments、new.target等属性
- 他只能通过作用域查找来寻找this
- 如果被包含在非箭头函数时,自动绑定到最近一层非箭头函数。否则自动绑定到全局对象
52,class和构造函数的区别
- 1、class不能被提升
- 2、class中所有方法默认是不可枚举的
- 3、class必须使用new调用否则会报错
53,this指向问题
- 情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。
- 情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
- 情况3:如果一个函数中有this,这个函数被多个对象包含,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
- this永远指向的是最后调用它的对象
- 一句话:如果一个函数没有被一个对象所调用,那么它指向window。只有被对象所调用,才会指向对象
54,HTTP常见状态码
- 302:临时重定向。展示最新的网页,但网址仍然是旧网址
- 301:永久重定向。网页将永久转移到另一个网址上
- 304:未修改
- 401:未验证登录
- 403:拒绝访问
- 500:服务器错误