2019年9月面试记录
# 2019-9-9
# 常见的6种数据类型
- string 字符串
- number 数值
- object 对象
- array 数组
- null 空
- undefined 未定义
# ajax和promise的区别
- ajax的数据是在回调函数里面返回的
- promise是在then函数里面返回
- promise是为了解决ajax回调地狱的问题
- promise是链式写法
- es7新增async await函数也可以解决ajax回调地狱和promise重复嵌套的问题
# 怎么解决0.1+0.2=0.33333的问题,只想要小数部分
- 利用(0.1+0.2).toFixed(1)
- 可以先分别乘以10,然后再除以10
# promise有几种状态
- pending:进行中
- fulfilled:已成功
- rejected:已失败
# 为什么不建议在原型上拓展方法
- 容易造成全局污染,和其他库冲突
- 出了Bug不太好定位问题
- 有可能出现代码向上不兼容的情况
# array的some、every、filter、find有什么区别
- some只要数组中有一个满足条件,就返回true
- every只有数组中所有的项满足条件,才返回true
- filter返回一个满足条件的新数组
- find返回数组中满足条件的第一个元素的值
# es6都用过哪些新增的特性
- let和const声明变量
- 对象解构、数组解构、参数解构
- 拓展运算符...
- async await 函数
- Promise 对象
- Class类
- Set和Map数据结构
- for of 循环迭代 String、Array、Map、Set、
- 字符串的拓展
- 字符串模板 ``
- string.includes()
- 数值的拓展
- Number.isNaN()
- Number.isFinite()
- Number.isInteger()
- Math.trunc() 去除一个数的小数部分,返回整数部分
- 指数运算符 2 ** 2 = 4
- 函数的拓展
- 函数参数的默认值
- 箭头函数
- rest参数...rest
- 函数的name属性,返回该函数的函数名
- 数组的拓展
- Array.from转换伪数组或 Set 和 Map 数据结构为真数组
- Array.of方法用于将一组值,转换为数组
- copyWithin() 将指定位置的成员复制到其他位置
- find()与findIndex(),find返回符合条件的成员,findIndex返回符合条件的索引值
- fill()方法使用给定值,填充一个数组
- 数组实例的 entries(),keys() 和 values()
- 数组实例的 includes()
- 数组实例的 flat() 将二维数组转换成一维数组,返回一个新数组
- 对象的拓展
- Object.keys(),Object.values(),Object.entries()
- Object.is()比较两个值是否相等,与===基本一致
- Object.assign方法用于对象的合并,只是一级属性复制,比浅拷贝多深拷贝了一层而已
# reduce的常见用法
- 数组求和
- 数组求最大值
// 数组求和
const arr = [12, 34, 23];
const sum = arr.reduce((total, num) => total + num);
// 数组最大值
const a = [23,123,342,12];
const max = a.reduce(function(pre,cur,index,arr){return pre>cur?pre:cur;});
// 将二维数组转为一维数组
const arr = [[1, 2, 8], [3, 4, 9], [5, 6, 10]];
const res = arr.reduce((x, y) => x.concat(y), []);
# Map和Set数据结构有什么区别
- Set是没有重复元素的集合
- Map是键值对的形式
# 箭头函数和普通函数有什么区别
- 箭头函数的this指向当前的执行环境,而普通函数指向自己
- 箭头函数不可以当作构造函数,不能使用new命令
- 箭头函数可以使用rest获取参数对象,普通函数可以用arguments获取参数对象
- 箭头函数没有prototype(原型),所以箭头函数本身没有this
- 不能直接修改箭头函数的this指向
# 命令式编程与声明式编程有什么区别
- 命令式编程:详细的告诉计算怎么一步一步的达到结果
- 声明式编程:只告诉计算机需要做什么,计算机自己执行达到结果
// 命令式编程
let result;
for (var i = 0; i < user.length; i++) {
if (user[i] === "Ben") {
result = user[i];
break;
}
}
// 声明式编程
const result = user.find(item => item === "Ben");
# 为什么{} + [] === 0为true
{} 认定是语法块,这个放在前面,只有混淆作用,并不参与运算。
+[] 类型转换 0 ,0===0
# 什么是变量提升
参考:深入理解 js 变量提升 (opens new window)
JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)
# 2019-9-11
# socket怎么检测客户端长时间没有连接
这里面会有一个心跳机制,服务端定时向客户端发送一个数据包,然后启动一个低级别的线程,该线程中不断检测客户端的回应, 如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线,同样,如果客户端在一定时间内没 有收到服务器的心跳包,则认为连接不可用。
# 路由的hash模式和history模式有什么区别
参考:hash 和 history的区别 (opens new window)
- 原理不一样
- hash模式是通过在 window 对象 onhashchange 事件监听的地址栏的hash部分实现路由跳转
- history模式则是通过在window对象上监听popState()事件,history api 提供 pushState、replaceState、go、back、forward,等方法实现路由跳转
- 路由跳转表现不一样
- pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL
- pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中
- 发送 http 请求的时表现不一样
- hash对地址栏的变化不会发送到后端,准确说只会发送#前半部分到后端,例如:https://yixiu.com
- history则会将地址发送到后端,一旦后端没有对应的路由处理,会返回404错误,所以需要后台配置:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面
# 谈谈高阶组件在实际项目的运用
参考:如何理解 React 高阶组件(HOC)? (opens new window)
- 高阶组件就是接受组件为参数返回一个加强组件的函数
- withRouter、connect这些都是高阶组件的应用
- 用高阶组件做增加loading效果
- 复用组件逻辑
# 深拷贝和浅拷贝
- 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存
- 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改变原对象
/ | 和原数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据包含子对象 |
---|---|---|---|
赋值 | 是 | 改变会使原数据一同改变 | 改变会使原数据一同改变 |
浅拷贝 | 否 | 改变不会使原数据一同改变 | 改变会使原数据一同改变 |
深拷贝 | 否 | 改变不会使原数据一同改变 | 改变不会使原数据一同改变 |
浅拷贝的实现方式
- 使用
Object.assign()
- 使用es6的扩展运算符
...
- Array.prototype.concat()
- Array.prototype.slice()
深拷贝的实现方式
- JSON.parse(JSON.stringify()),这种方法虽然可以实现数组或对象深拷贝,但不能处理函数
- 手写递归方法
- 函数库lodash提供_.cloneDeep用来做 Deep Copy
//定义检测数据类型的功能函数
function checkedType(target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
//实现深度克隆---对象/数组
function clone(target) {
//判断拷贝的数据类型
//初始化变量result 成为最终克隆的数据
let result, targetType = checkedType(target)
if (targetType === 'Object') {
result = {}
} else if (targetType === 'Array') {
result = []
} else {
return target
}
//遍历目标数据
for (let i in target) {
//获取遍历数据结构的每一项值。
let value = target[i]
//判断目标结构里的每一值是否存在对象/数组
if (checkedType(value) === 'Object' ||
checkedType(value) === 'Array') { //对象/数组里嵌套了对象/数组
//继续遍历获取到value值
result[i] = clone(value)
} else { //获取到value值是基本的数据类型或者是函数。
result[i] = value;
}
}
return result
}
# 为什么react和vue里面循环产生html时要绑定key
key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度
# 怎么控制video视频的宽高
设置width和height
# 为什么要发送一个option请求
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错
这和浏览器的 CORS 跨域机制有关,客户端将请求分为了两种:简单请求和非简单请求;当请求为非简单请求时,就会触发浏览器发送预检请求。
预检请求一般包括 request header
# 在项目中做过哪些优化
# 前端部署
# 2019-9-12
# 为何要在componentDidMount里面发送请求?
参考:为何要在componentDidMount里面发送请求? (opens new window)
- componentDidmount 是在组件完全挂载后才会执行,在此方法中调用setState 会触发重新渲染,最重要的是,这是官方推荐的!
- componentWillMount、constructor 里进行网络请求会阻碍组件的渲染,会阻碍组件的实例化
- 从React 16.3(2018年3月)开始componentWillMount会被弃用
# 你用过哪些react相关的UI框架
- Zent有赞
- ant-design和ant-mobile
- element-react
- React Suite
# 怎么用css写出三角形
.trangle {
width: 0;
height: 0;
border: 40px solid;
border-color: transparent transparent transparent red;
}
# react和vue有什么区别
区别:https://juejin.im/post/5b8b56e3f265da434c1f5f76 数据流动原理:https://recallhyx.github.io/2018/03/18/%E5%89%8D%E7%AB%AF%E4%B8%89%E5%A4%A7%E6%A1%86%E6%9E%B6%E6%95%B0%E6%8D%AE%E6%B5%81%E5%8A%A8%E5%92%8C%E5%8E%9F%E7%90%86/
- 数据流向不一样:vue支持双向数据绑定,react是单向数据流
- 监听数据变化的原理不一样:vue使用Object.defineProperty()实现数据劫持,组件能自动响应数据变化,react通过onChange/setState的方式
# MVVM的原理
参考:
MVC,MVP 和 MVVM 的图示 (opens new window)
浅析 web 前端 MVVM (opens new window)
- View:视图
- ViewModel:视图模型
- Model:数据模型
它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然
Vue 的 MVVM
View:单文件里 <template>
标签的内容,展现给用户的内容,与 ViewModel 双向绑定,可以在其中插入 ViewModel 提供的数据。
ViewModel:Vue 实例整个都是 ViewModel,与 View 双向绑定,用户在 View 修改数据或发出 ajax 等指令时, ViewModel 会及时相应,接着向下修改 Model——至此可以看出 Model 和 View 是没有直接关系的。
Model:这一层或者有歧义。为了更好理解 Model 需要引入 Vuex,在有 Vuex 的情况下,Vuex 提供的数据就是 Model,这符合后端架构中 Model 包含业务逻辑的情况。但是在无 Vuex 的情况下,Model 应该就是 Vue 实例的 data 属性,也就是 JavaScript 数据对象本身。
# 实现垂直居中的方式有哪些
# 在项目中怎么做移动端适配
# react组件通信
# react导入组件的方式有哪些
- 通过传统import方式导入
- 通过require引入
# HTTP请求头
- General 一般头
- Request URL 请求地址
- Request Method 请求方式
- Status Code 请求状态
- Remote Address 远端地址
- Request Header 请求头
- Accept 用户代理期望的 MIME(媒体类型) 类型列表
- Origin 远端信息
- User-Agent 用户代理信息
- Content-Type 示服务器文档的MIME 类型
- Host 域名
- Authorization 用户凭证
- Response Header 响应头
- Access-Control-Allow-Headers 允许请求头
- Access-Control-Allow-Methods 允许的请求方式
- Access-Control-Allow-Origin 是否允许跨域
- Content-Type 示服务器文档的MIME 类型
- Cache-Control 是否开启缓存
- Expires 过期时间
- Date 时间
# 按需引入
参考:
const OtherComponent = React.lazy(() => import('./OtherComponent'));
# 对于redux的理解
用来做全局状态管理,组件可以共享到状态
- state:是一个js对象,存放应用状态
- action:是一个包含
type
和payload
的普通obj,我们通常使用一个函数(action creator)返回action - reducer:是一个纯函数,描述state是如何改变的,它有两个参数state和action,我们在函数体内使用switch语句匹配到action的type,拿到payload然后对状态作出更改
数据流向
数据流向是这样的,组件dispath一个action,reducer匹配到actions的type对state作出修改,view接收到数据变化,视图响应变化。视图是怎么接收到变化到呢,当我们没有使用react-redux时,我们是通过store.subscrib()
订阅了state到变化,从而响应到视图上,而在使用react-redux
时,我们借助connect方法,它自动订阅了state到变化,并映射到视图上,组件可以使用connect到前提是,我们必须在根组件外层包裹Provider
组件。
异步操作
异步操作需要借助redux-thunk
这个中间件,它可以使我们可以在action中拿到dispatch对象,我们使用action creator函数返回一个promise对象,在then函数内再dispath一个action通知reducer对state做出改变,也就是说异步操作发起了2次dispatch。
# 同事对你的评价
热情、乐于助人、自律、做事认真、有潜力