前端面试填(cai)坑

1 篇文章 0 订阅
1 篇文章 0 订阅

从上一家公司离职一个多月,不管愿不愿意都滚出来找工作了。整理了一些面试题填填坑,知识有限,下面答案仅代表个人观点。如有错误欢迎指正。

写运行结果

1. 引用类型考察

const array1 = [1,2,3,4]
const array2 = array1
array2.push(5)
console.log(array1) // [1,2,3,4,5]
console.log(array2) // [1,2,3,4,5]

这里考察引用类型的修改,Array属于引用类型,因此在复制后两个变量共同使用同一块地址。并且push可以改变原数组,因此结果都是[1,2,3,4,5]


2. 变量作用域(var 变量提前)

var name = 'monika'
(function () {
   if (name === undefined) {
     var name = 'jeck' // 变量提升,这里会吧name提升到最上面
     console.log('goodbye' + name)
   } else {
     console.log('holle' + name)
   }
 })()
 // 输出结果是: goodbye jack

结果意不意外?一开始我以为是考察闭包的,因为自执行的匿名函数本来就是一个闭包。但是就算是闭包,name最开始是在全局作用域定义的。包多少层都能拿到。其实解题点是 var name = ‘jack’ 这里用的 var ,它很神奇,相比let 和const 赋值点即定义点,var 定义的变量浏览器会默认的在当前定义作用域的最上方加一行var name = undefined 所以就走了第一个分支, 如果去掉var 或者替换成 let就会走第二个分支。


3. 块级作用域

var str = 'hello', i = 1
for (let i = 0; i < str.length; i++) {}
console.log(i) // 1 这里是1哦

咋看一下很简单嘛,for循环最后一轮 i=4i++ 还是会执行,要我采坑填4?我偏要写个5. 我以为这就对了,但是。。。考察点不是这个[滑稽],这里的考点是块级作用域,注意循环中用了let 这个家伙只在for循环中有效,外面是拿不到的。所以不管i++几次。输出哪里拿到的变量一直是1


4. 异步

for (var j = 0; j < 3; ++j) {
 setTimeout(() => {
    console.log(j) // 1s 后连续输出3个3
  }, 1000)
}

这里的考察点其实有两个,第一个不用说异步,第二个跟上面有点像,就是到达循环条件的终止点后还会一轮计算。这里最困扰我的其实是i++++i 一个是自增后下一步调用才能拿到增加后的值,一个是自增执行后立马生效,打个比方:

// i++ 模式的自增
vari = 0
console.log(i++) // 0
console.log(i) // 1 

// ++i 模式的自增
var i = 0 // 这里没有错,var声明的可以重复声明哦
console.log(++i) // 1
console.log(i) // 1 

后来想了一想,其实这里i++和++i结果都一样,因为判断i值已经是下一步运算的事情了。

写代码

1. 水平垂直居中
已知父容器类名container,子容器类名child,用css代码实现水平垂直居中

/* flex box ie11以下不兼容*/
.container{
    width: 100vw;
    height: 100vh;
	display: flex;
    align-items: center;
    justify-content: center;
}
/* 通用方法 */
.container{
	width: 100vw;
    height: 100vh;
    position: relative;
}
.child{
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%)
}

2. 数组排序
手写一个排序方法,不能使用原有的api,实现降序排序。鉴于本人比较垃圾,所以只写了个选择排序(选择排序不是冒泡哦,效率会比冒泡好一点)。双重循环都会,看看就行了。

Array.prototype._sort = (arr) => {
  if (!Array.isArray(arr)) throw new Error('param error!')
  for (let i = 0, len = arr.length; i < len - 1; i++) {
    for (let j = i + 1; j < len; j++) {
      if (arr[i] < arr[j]) {
        [arr[i], arr[j]] = [arr[j], arr[i]] // 这里相当于交换变量
      }
    }
  }
}

3. url解析
虽然知道用正则比较高效,但是菜鸡不会用。等填好正则的坑以后再来补上正则的版本吧。。。然后就写成了刀耕火种的版本:

const parseUrl = (url) => {
  if (typeof url !== 'string') throw new Error('params error')
  const paramsStr = url.includes('?') ? url.substr(url.indexOf('?') + 1) : ''
  if (!paramsStr) return {}
  const paramsArr = paramsStr.split('&')
  const params = {}
  paramsArr.forEach((item) => {
    const temp = item.split('=')
    params[temp[0]] = temp[1]
  })
  return params
}

4. 怎么验证一个邮箱
这个一眼就能看出来是用正则,当然是不会的。这个题目再用刀耕火种的方法有点不好意思写上去。人还要脸皮所以直接空着了,下面是网上薅的版本:

const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/

5. 手写一个ajax
果然,以前写过但忘得差不多了,最后乱写一通。虽然基本没人用原生的ajax了,但是把你考倒了,就足以说明人家题目出的有水平。OK 回来以后修修改改用promise包装了一下。没有考虑到通用性。jquery,axios不香吗?

const ajax = (method = 'get', url, dataStr = null) => {
 return new Promise((resolve, reject) => {
    if (method !== 'get' && method !== 'post') reject(new Error('params Error'))
    const xhr = new XMLHttpRequest()
    xhr.open(method, url)
    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
    xhr.onreadystatechange = function () {
      try {
        if (this.status === 200 && this.readyState === 4) {
          resolve(this.responseText)
        }
        if (this.status !== 200) {
          reject(new Error('status Error'))
        }
      } catch {
        reject(new Error('500 Error'))
      }
    }
    // 这里post如果传对象的话一定要用get传参的方法: name=sam&age=18,不然node+body-parser拿不到正确数据
    method === 'get' ? xhr.send(null) : xhr.send(dataStr)
  })
}
// promise处理异步
ajax('post', 'http://localhost:3000', 'name=sam&age=18').then((res) => {
    console.log('res', res)
  }).catch((err) => {
    console.log('err', err)
})
// async awai 处理异步
(async () => {
   try {
     const res = await ajax('post', 'http://localhost:3000', 'name=sam&age=18')
     console.log('await ', res)
   } catch (err) {
     console.log('await err', err)
   }
 })()

主观题

1. 前端如何优化性能
这个范围可以说是很广了,我只答了我知道的

1: 减少前端请求
	- 代码合并,多个文件可合并一个文件
	- 服务端渲染,不用每次渲染都像后端请求数据
	- 数据可以考虑存localstorage
	- 懒加载
	- 雪碧图
	- 使用节流和防抖函数包装一些高频操作
2: 减少请求数据的体积
	- 代码压缩
3: 减少dom操作
	- 多个dom更新使用缓存document.createDocumentFragment()
	- 用js代替dom操作
4: script标签放在body最下面(浏览器遇到js会暂停往下渲染,知道执行完当前的js代码再往下执行)
5: 多使用内存
6: 使用CDN

2. 你知道哪些前端攻击?怎么预防?
之前只对XSS有印象,GG

1:跨站点伪造请求(CSRF):在用户登录站点A后,利用脚本向站点B发送请求。此时站点A拿到用户信息后,在站点B执行一些危险操作。关键词: 跨站点, 伪造用户信息, 发送危险请求
	- 解决方案: 
		a. 使用post请求(CSRF可以生成form表单发送)
		b. 验证refer字段
		c. 在请求中添加token
2: 跨站点脚本攻击(XSS):简而言之就是在页面中执行恶意的html,css,js。
	- 解决方案:
		a. 前端不信任原则,对特殊字符进行转义
		b. cookie设置httpOnly,防止客服端通过document.cookie获取cookie

3: SQL注入:在SQL语句中加入条件,改变原有SQL执行的时机。
	- 解决方案: 
		a. SQL预编译
		b. 特殊字符转义

3. 我们隐藏元素时一般用display: none设置,你还知道哪些方法有相同的隐藏元素效果?这些方法有什么特点?
这个暂时只想到了下面几点

1. height 或 width设为0:视觉上不可见,实际上还是占位的,且Chrome工具可见
2. opacity设置为0:同上
3. transform: scale(0,0): 同上
4. visibility: hidden: 同上
5. js移除dom: dom操作耗费性能,如果需显示还需dom操作

面试官提问

1. 技术选型
Q: 你了解现在前端的一些主流技术吗?
A: 了解
Q: 如果现在让你开发一个CRM系统,你会选择哪些技术?
A: Vue + webpack
Q: 没别的了吗
A: 没有
AWSL~, 后来对面的面试官小姐姐提示我才知道要vuex,vue-router,scss,eslint…这样,想要回答的时候对面的主面试官已经不想听我说直接往下问了。我TM,现在觉得自己蠢已经太晚了。如果之前这样答应该会好一点吧:

我会选vue+webpack, 此外需要一些插件并在webpack.config.js配置
	- eslint规范项目代码
	- webpack-dev-server、html-webpack-plugin让调试更方便
	- 使用css预处理语言scss
	- vue中使用VueX 和 Vue-router用于状态管理和前端路由跳转
	- 如果没有特殊的UI要求可以使用Element-ui提高开发效率
当然,如果不需要SSR服务端渲染并且项目体积适中的话我觉得完全可以用vue官方提供的脚手架初始化工具初始化项目
这样的好处是节省了一些webpack的配置时间,同时如果需要自定义webpack配置也可以在配置文件中修改

2. 对象设计的solid原则
Q: 说一说对象设计的SLOID?
A: 单一职责、开闭原则
然后下面的不记得了,设计模式真的让人头秃,GG

1. 单一职责原则:
一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。

2. 开闭原则:
软件实体应当对扩展开放,对修改关闭。

3. 里氏替换原则:
所有引用基类的地方必须能透明地使用其子类的对象。

4. 接口隔离原则
定义:
客户端不应该依赖那些它不需要的接口。

5. 依赖倒转原则
高层模块不应该依赖底层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

3. 你常用的es6新特性?
这里只说了一部分自己常用的

- const,let, 块级作用域
- 箭头函数
- class
- Array新增api、Object新增api
- 扩展和解构
- promise
- proxy、reflex(其实这两个只是听说没用过)

Q: 那你知道map和set这两种类型吗?
A: 嗯,set是不允许重复的集合,map是类似Object的键值对集合,但map的可以可以是任意类型,Object的key只能是string


4. 你知道闭包吗
该来的还是来了,说实话我一直没有想好该怎么描述闭包。。。
Q: 你来说一说闭包吧
A: 我理解的闭包就是跨作用于获取变量的现象。一般来说当函数作为参数或者函数作为返回值得时候就可能出现闭包。闭包最常见的作用是变量私有化和变量存储。
Q: 那你说一说变量私有化
A: 每个函数都有函数自身的作用域,外界是无法访问的,借助闭包可以在函数里面定义变量,这个变量只能通过闭包让外界访问。这样的好处就是避免污染全局作用域。在ES6规范出来之前经常使用自执行的匿名函数形成闭包来避免各种变量、函数的全局调用。


暂时就填这么多吧,接下来的面试遇到坑再来填。最后,希望早点结束这波面试,找到一份自己喜欢的工作,然后努力搬砖

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值