前端面试题大全

HTML/CSS基础

1、盒模型

每个HTML元素都可看作盒模型,有标准盒模型(W3C)和IE盒模型。
标准盒模型:width=content的width,可以通过box-sizing:content-box设置
IE盒模型:width=content+padding+border,可以通过box-sizing:border-box设置


标准盒模型.png

ie盒模型.jpg

2、BFC

就是“块级格式化上下文”,是一个独立容器,容器里边和外边元素互不影响。
如何让所属box产生BFC:
-overflow:auto/hidden;
-position:定位如absolute/fixed;
-float:浮动元素如left/right;
-display:inline-block/table-cell/flex;
BFC使用场景
-float实现两栏布局,左侧float:left,右侧overflow:auto,原理是BFC不和float元素重叠;
-overflow:auto在父元素上可以清除子元素的浮动;
-overflow:hidden在父元素可以解决垂直子元素上下margin重叠的问题;

3、元素居中

1)、 translate


center.png
center
.box { position: relative; border: 1px solid red; height: 60px; } .container { width: 100px; height: 20px; border: 1px solid red; } .box-center { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }

2)、 flex


center01.png
flex
.box-flex { display: flex; align-items: center; justify-content: center; height: 80px; border: 1px solid gray; } .container { width: 100px; height: 20px; border: 1px solid red; }

4、实现3行3列布局

1)flex

原理:容器flex布局,flex-wrap:wrap换行显示。
flex:合并属性,flex-basic,flex-grow,flex-shrink
flex-basic:定义元素初始宽或高,根据flex-direction确定是设置宽还是高,默认值main-size | width;
flex-grow:定义每一个子元素在盒子内的弹性,元素扩展剩余空间的能力,默认值:0
剩余空间的分配规则 : flex-basis + flow-grow/sum(flow-grow)remain remain 表示多余的空间
flex-shrink:定义元素收缩的能力(空间不足时),默认: 1
不足空间收缩的规则 : flex-basis + flow-grow/sum(flow-grow)
remain remain 表示不足的空间 (负值)
可参考:https://www.ruanyifeng.com/blog/2015/07/flex-grammar.html

flex3row3col.png
111
111
111
111
111
.flex-multi-col { display: flex; justify-content: space-between; flex-wrap: wrap; } .flex-multi-col .cont { flex: 0 0 30%; //3列,所以是30%,4列为25%或以下等 height: 30px; margin-right: calc(10% / 2); margin-bottom: calc(10% / 2); background-color: aqua; box-sizing: border-box; } /* last column remove margin right */ .flex-multi-col .cont:nth-child(3n) { margin-right: 0; } /* last child margin right occupy the remaining space */ .flex-multi-col .cont:last-child { margin-right: auto; } /* last line remove the margin bottom */ .flex-multi-col .cont:nth-last-child(-n+3) { margin-bottom: 0; }

2)grid

原理:容器grid布局,grid-template-columns: 1fr 1fr 1fr等比例分配空间;
可参考:https://www.ruanyifeng.com/blog/2019/03/grid-layout-tutorial.html

grid.png
111
111
111
111
111
.grid-multi-col { display: grid; justify-content: space-between; grid-template-columns: 1fr 1fr 1fr; gap: 10px; } .grid-multi-col .cont { background-color: aquamarine; }

Javascript

1、9*9乘法表

9multiply9.png
for (let i = 0; i <= 9; i++) { document.write(""); for (let j = 1; j <= i; j++) { document.write(" "); document.write("" + i + "*" + j + "=" + i * j + " "); document.write(""); } document.write("
"); }

2、根据对象的一个属性将对象排序

// 当 a>b 时,
// a - b > 0 ,排序结果 ===> b,a (升序)
// b - a < 0 ,排序结果 ===> a,b (降序)
// 当 b>a 时,
// a - b < 0 ,排序结果 ===> a,b (升序)
// b - a > 0 ,排序结果 ===> b,a (降序)
// 当 a=b 时,
// a - b = b - a =0 , 排序结果 ===> 保持不变
// 由此看出,
// 无论a>b还是b>a,return a-b 总能得到升序的结果,而 return b-a 总能得到降序的结果。

let arr = [{id: 1, name: "aaa" },{ id: 3},{id: 2},{id: 6}];
function compare(property) {
    return function (a, b) {
      var value1 = a[property];
      var value2 = b[property];
      console.log(value1)
      return value1 - value2; //升序
    };
 }
console.log(arr.sort(compare("id"))); //[0: {id: 1, name: "aaa"},1: {id: 2},2: {id: 3},3: {id: 6}]
//根据上述原理,可实现数组排序
var arrSort = [1, 22, 15, 32, 4, 5, "11"]
    arrSort.sort((a, b) => {
      if (a > b) return 1;
      else if (a < b) return -1;
      else return 0;
    });
    console.log(arrSort)
    // [1,4,5,15,22,32]  //升序

3、this,js中 call()、apply()、bind() 的用法

var name = "this.window";
    var obj = {
      name: "my object",
      getNameFunc: function () {
        console.log(this); //{name: "my object", getNameFunc: ƒ}
        return function () {
          console.log(this); //Window {window: Window, self: Window, document:  …}
          console.log(this.name); //this.window
        };
      },
    };
obj.getNameFunc()();
-call()、apply()、bind() 都是用来重定义 this 这个对象的
-bind 返回的是一个新的函数,你必须调用它才会被执行
区别:
call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:
call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,'成都', ... ,'string' )。
apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,['成都', ..., 'string' ])。
bind 除了返回是函数以外,它 的参数和 call 一样。

可参考:https://www.runoob.com/w3cnote/js-call-apply-bind.html

4、闭包

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常用方式:在一个函数内部创建另一个函数
使用场景:创建私有变量、延长变量的生命周期、计数器、延迟调用(这里会考查到防抖和节流)、回调

for (var i = 0; i < 3; i++) {
    setTimeout(() => { console.log(i) }, 1)
 }
    // 3 3 3
 for (let i = 0; i < 3; i++) {
      setTimeout(() => { console.log(i) }, 1)
 }
 // 0 1 2
考察:必包;js事件循环机制;var定义的i为全局变量,let定义的i为块变量

可参考:https://segmentfault.com/a/1190000023356598

5、js实现树形菜单

let source = [
      { id: 1, pid: 0, name: 'body' },
      { id: 2, pid: 1, name: 'title' },
      { id: 3, pid: 2, name: 'div' },
    ]
    console.log(source)
    //将每项的id作为对象的key,每一项作为对象的value,保存到新对象map
    let map = []  //所有的父节点
    source.map(item => {
      return map[item.id] = item
    })
    let val = []
    source.map(item => {
      let parent = map[item.pid] //查找是否存在该项的父节点
      if (parent) {
      //该项的父节点存在,将该项push到父节点的children中
        parent.children = [];
        parent.children.push(item)
      } else {
      //该项的父节点不存在,那么该项是根节点,其他项目都是该项的子节点,val为根节点
        val.push(item)
      }
      return val
    })
  console.log(val)

6、手写一个通用方法实现下边的转换

“23214237835782937532500.00”转为:"232#1423#7835#7829#3753#2500.00"

let str = "23214237835782937532500.00"
    let conStr = str.split("").reverse().join("")
    conStr = conStr.split(".")[1]
    let res = ''
    for (let i = 0; i < conStr.length; i++) {
      if (i % 4 === 0) {
        res += '#' + conStr[i]
      }
      else {
        res += conStr[i]
      }
    }
    res = res.split("")
    res.splice(0, 1, '');
    res = res.reverse().join("")
    res = res + '.00'
    console.log(res);

7、数组去重,方法有很多

1)利用Set不重复
2)利用对象key不重复的特点
3)利用新数组中是否包含数组项indexOf()
...

let arr = [1, 2, 3, 5, 6, 3, 1, 7, 9, 10, "2"]
    // 对纯数字数组有效
    console.log("Set去重:" + [...new Set(arr)]) //Set去重:1,2,3,5,6,7,9,10,2
    //对有字符串数组也有效
    let obj = {}
    arr.map((item, i) => {
      obj[arr[i]] = 1
    })
    let newArr = Object.keys(obj).map(o => Number(o))
    console.log("利用对象key不重复:" + newArr) //利用对象key不能重复来去重数组:1,2,3,5,6,7,9,10

    //indexOf()
    let resArr = []
    arr.forEach((item, i) => {
      let flag = resArr.indexOf(item) > -1
      if (!flag) {
        resArr.push(item)
      }
    })
    console.log(resArr) //[1, 2, 3, 5, 6, 7, 9, 10, "2"]

可参考:https://segmentfault.com/a/1190000016418021

8、for in 遍历对象

//for in 遍历对象,且会遍历对象原型上的属性
   Object.prototype.z = 1
    let obj = { x: 1, y: 2 }
    for (let k in obj) {
      console.log(k) //x,y,z
      if (obj.hasOwnProperty(k)) {
        console.log("obj的属性:" + k) //x,y
      }
    }

9、for of 遍历数组

//for of 遍历数组,对象不能使用
    // let arr = ['1','2','3']
    let arr = [{ name: 'zcc', age: 18 }, { name: 'z', sex: 'nv' }]
    // let obj = {'name':'zcc','age':18}
    for (let k of arr) {
      console.log("for of arr :" + k['name']) //for of arr :zcc ,for of arr :z
    }

10、forEach 和 map

forEach()方法不会返回执行结果,而是undefined。也就是说,forEach()会修改原来的数组。而map()方法会得到一个新的数组并返回。

let arr = [1, 3, 5, 6];
let map = arr.map(item => item * item)
console.log(map) //map不修改原始数组  //[1, 9, 25, 36]
let res = []
arr.forEach((item) => {
    res.push(item + 2)
});
arr.forEach((item,index) => {
    return arr[index] = item+2
});
console.log(arr) //[1, 3, 5, 6] 
console.log(res) //[3, 5, 7, 8]

11、includes(),数组和字符串都适用

let arr = [1, 2, 5]
arr.includes(2)  //true
let str = 'fdfdfd';
str.includes('fd') // true

12、深拷贝数组或对象

//slice()不改变原始数组,分割数组,可以深拷贝
//concat 方法实现数组的深拷贝
//es6中...展开运算符实现深拷贝数组 [...arr]
//for 循环实现数组的深拷贝

//深拷贝数组
let arr = [1, 3, 5, 6]
let arr2 = [...arr]
let res = arr.slice() //类似于arr.concat(),一维数组

res.push(9)
let slice = res.slice(0, 3)
console.log(arr) //[1, 3, 5, 6]
console.log(res) //[1, 3, 5, 6, 9]
console.log(slice)  //[1, 3, 5]
//多维数组,for循环结合js递归实现

//万能的for循环实现对象的深拷贝
//JSON.parse(JSON.stringify(obj))
// 扩展运算符实现对象的深拷贝

//将数组转换为对象  {...arr} 
let arr = [1, 3, 5, 6]
const obj = { ...arr }
console.log(obj) //{0: 1, 1: 3, 2: 5, 3: 6}

13、数组扁平化,第二种可以手动删除undefined

function flatArr(arr, d = 1) {
  return d > 0 ? arr.reduce((prev, next) => prev.concat(Array.isArray(next)
   ? flatArr(next, d - 1) : next), []) : arr.slice()
}
let arrFlat = [1, 4, 5, [5, 6, undefined, , [3, 4, [4]], 8], 6];
console.log(flatArr(arrFlat, Infinity)) 
//[1, 4, 5, 5, 6, undefined, 3, 4, 4, 8, 6]
const flatArrForOfFunc = (arr = [], d = 1) => {
    let result = [];
    (function flat(arr, d) {
      for (let item of arr) {
        if (Array.isArray(item) && d > 0) {
          flat(item, d - 1)
        } else {
          item !== void 0 && result.push(item) 
        }
      }
    })(arr, d)
    return result
  }
let arrFlat = [1, 4, 5, [5, 6, undefined, , [3, 4, [4]], 8], 6];
console.log(flatArrForOfFunc(arrFlat, Infinity))
//[1, 4, 5, 5, 6, 3, 4, 4, 8, 6]

14、splice()

//splice 接受3个参数,要插入元素的位置,从该位置删除的元素个数,插入的元素
let arr = [1, 3, 5, 6, 7]
// arr.splice(2,0,9) //0表示不删除元素个数,从第二位添加一个9
arr.splice(2, 1, 9) //从第二位删除一个元素,再添加一个9
console.log(arr)

Vue

1、vue组件传值

父传子:
父组件通过属性的方式向子组件传值,子组件通过 props 来接收

子传父:
子组件绑定一个事件,通过 this.$emit() 来触发
通过 $parent / $children 或 $refs 访问组件实例
$attrs/$listeners

兄弟组件:
子传父,父再传子,通过props,this.$emit()
EventBus,$emit和$on,发布订阅事件
$attrs/$listeners
vuex

多层级组件:
vue 提供的更高阶的方法:provide/inject
vuex

可参考:https://segmentfault.com/a/1190000022700216

2、vue keep-alive

可以使被包含的组件实现缓存,避免重新渲染。不会再次初始化,因此不会走mounted,如果希望重新渲染组件,activated 当 keepalive 包含的组件再次渲染的时候触发。
deactivated 当 keepalive 包含的组件销毁的时候触发
include: 字符串或正则表达式。只有匹配的组件会被缓存。
exclude: 字符串或正则表达式。任何匹配的组件都不会被缓存。

可参考:https://juejin.cn/post/6844903919273918477

3、vue双向数据绑定/响应式原理

数据变化更新视图,视图变化自动更新数据。
实现原理:
利用数据劫持和发布订阅者模式。主要涉及 Observer, Watcher , Dep这3个类,数据劫持利用ES5的Object.defineProperty(obj,key,val)为每个属性设置getter/setter,在数据set变动时,发布消息给订阅者,更新视图。
数据更改->通知Watcher->更新DOM

可参考:https://segmentfault.com/a/1190000023824423
https://cn.vuejs.org/v2/guide/reactivity.html

4、vue强制刷新组件

强制刷新也就是重新渲染组件,this.$forceUpdate();
v-if控制子组件移除后,nextTick可实现dom状态变化后,执行传入的方法,重新渲染组件

5、vue nextTick实现原理

异步任务队列里的macrotask,以事件循环的形式进入执行栈;每次事件循环包含一个微任务队列,在循环结束后依次执行队列中的微任务并移除,然后开始下一次事件循环;
调用nextTick时,vue就在更新DOM的那个微任务后追加我们自己的回调函数,确保代码在DOM更新后执行
常见微任务:promise、mutationObserver、node的process.nextTick
总结:vue先用异步队列方式控制DOM更新,nextTick后执行;
microtask优先级高特性,确保队列中的微任务在下一次事件循环开始前执行完毕。

6、修改elementUI样式

1)通过:style,:class 来局部修改样式,提高样式权重;
2)多个组件用到同一个组件需要不同的样式,比如el-table,可以在组件前加入不同的id选择器如#home .el-table; #order .el-table
3)这种 >>> 方式只能用在原生 CSS 语法中,不能在 css 预处理器如 less scss 等直接使用;
4)/deep/
/deep/ .el-cascader {
width: 100%;
}

7、Vue.js 性能优化技巧

https://juejin.cn/post/6922641008106668045

8、vue-router

可参考:https://juejin.cn/post/6844903945530245133

Http

1、webpack打包静态资源使用CDN

静态资源是指在不同请求中访问到的数据都相同的静态文件。例如:图片、视频、网站中的文件(html、css、js)、软件安装包、apk文件、压缩包文件等。
内容分发网络(Content Delivery Network),由分布在不同区域的边缘节点服务器群组成的分布式网络,现有的互联网基础之上的一层智能虚拟网络。

CDN加速的本质是缓存加速。将服务器上存储的静态内容缓存在CDN节点上,当访问这些静态内容时,无需访问服务器源站,就近访问CDN节点即可获取相同内容,从而达到加速的效果,同时减轻服务器源站的压力。

可参考:https://zhuanlan.zhihu.com/p/28940451

2、http缓存

强制缓存,首次向服务器请求资源,从服务器返回的响应头获取过期时间,在过期时间内不会向服务器发送请求,直接读取本地缓存。
协商缓存,会向服务器发送请求,服务器会根据request header标识来判断是否命中协商缓存,是就返回304,浏览器就读取本地缓存中的数据。如果没命中,服务器返回更新的资源和新的标识。

可参考:https://segmentfault.com/a/1190000016199807

3、http状态码

304 Not Modified,请求资源未修改时,服务器返回304,应用本地缓存的资源
4xx表示一般是客户端发生了错误
  -400 Bad Request,客户端请求错误。
  -401 Unauthorized,用户未经过认证,可以再次请求认证。
  -403 Forbidden,禁止访问也就是无权限访问,用户没有权限。
  -404 Not Found,资源访问路径错误等,服务器不存在该资源。
5xx表明服务端发生了错误
  -500 Internal Server Error
  -502 Bad Gateway
  -504 Gateway Timeout

可参考:https://segmentfault.com/a/1190000018264501

安全

1、CSRF攻击
https://juejin.cn/post/6844903689702866952

你可能感兴趣的:(前端面试题大全)