面试题(包括网络上的面经,进行了回答)
实现一个三列布局,使用flex、position、float实现,屏幕共享手写代码
-
- flex .box { display: flex; height: 200px; flex-direction: row; flex-wrap: nowrap; justify-self: start; } .left { flex: 0 0 800px; background-color:cadetblue; } .middle { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; background-color: yellowgreen; } .right { flex: 0 0 600px; background-color: bisque; } -position .left { height: 1500px; width: 200px; position: absolute; background-color: red; } .middle { overflow: hidden; margin: 0 200px; height: 300px; background-color: blue; } .right { position: relative; top: -200px; left: calc(100% - 200px); height: 200px; width: 200px; background-color: green; } -float .box { overflow: hidden; } .left { float: left; width: 200px; height: 200px; background-color: red; } .middle { overflow: hidden; height: 300px; background-color: blue; margin:0px 200px; } .right { float: right; width: 200px; height: 200px; background-color: green; margin-top: -200px; }
position
- 属性:
- static(默认)
- 标准流,无定位
- relative (相对定位)
- 相对于当前标签未移动前的位置定位,不会脱离文档流,可通过z-index定义层叠关系
- absolute (绝对定位)
- 相对于非static的已定位元素为参照进行定位,若没有,则以document为参照进行定位,脱离文档流,可通过z-index定义层叠关系
- fixed (固定定位)
- 相对于浏览器窗口定位,脱离文档流,可通过z-index定义层叠关系
- sticky (粘性定位)
- 相对定位relative和固定定位fixed的结合,简单说在滑动过程中,某个元素的距离其父元素的距离达到 sticky 粘性定位 要求时;position:sticky 这时的效果就相对于 fixed 定位,固定到适当的位置
javascript基本数据类型
- 基本数据类型
- null
- undefined
- boolean
- number
- string
- symbol
- bigint
- 引用数据类型
- object(function array object regexp date ...)
JavaScript中Symbol数据类型的作用
- symbol作用
- 变量私有化
- 作为对象属性
- 定义不重复的常量
- 避免魔术字符串(多次出现,与代码形成强耦合的字符或数字)
- 避免命名冲突
ES6的let、const与var的区别
-
let,const
- 没有变量提升
- 存在暂时性死区
- 同一作用域内不能被重复声明
- 具有块级作用域
- let定义变量
- const定义常量,不能被改变
- const定义时必须赋值
-
var
- 在函数内部定义属于函数作用域
- 存在变量提升值为undefined
- 避免使用var
Promise对象以及async与await的作用
- Promise对象作用
- 异步编程
- 解决回调地狱
- async与await的作用
- promise的语法糖,简洁干净
- 让异步代码具有同步代码风格
- 错误处理,结合trycatch,可以同时处理同步和异步错误
- 方便调试,可以直接跨过await调用,promise不能再返回表达式的箭头函数中设置断点,不会跳到下一个then,而是会跳过异步代码
ES5与ES6实现继承的方式,手写代码
- 原型链继承
- 子类所有实例共享一个原型对象
- 无法通过构造函数传参给父类
- 构造函数继承
- 每次创建实例都会创建方法,无法复用
- 组合式继承
- 每次创建子类实力都会创建两份父类实例
- 原型继承
- 所有子类实例共享一个原型对象
- 无法复用
- 寄生式继承
- 无法复用
- 寄生式组合继承
- 修复了组合继承的问题
- ES6继承
- extends super()
JavaScript的作用域、原型链以及闭包
- 作用域
- 定义变量的区域,有一套访问变量的规则,用来管理浏览器引擎如何在当前作用域以及嵌套的作用域中根据变量进行变量查找
- 作用域链
- 作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,访问变量时,在本作用域中查找,如果没有找到会去外层作用域查找,直至全局作用域,这样查找关系称为作用域链
- 闭包
- 可以访问其他函数内部变量的函数,本质是当前环境中存在对父级作用域的引用
JavaScript中判断数据类型的方法以及分别有什么区别
- typeof
- 基本数据类型除了null会被误判为object外都可以判断
- 引用数据类型只可以判断出function
- instanceof
- 判断当前对象的实例是否由构造函数生成
- 适合判断复杂引用类型
- 返回布尔值
- 不能跨iframe
- isArray()
- 判断数组
- isNaN()
- 会先进性Number()转换判断NaN
- Number.isNaN()
- 严格判断传入的值是否直接等于NaN
- constructor
- 查看对象对应的构造函数
- 构造函数名.prototype.constructor = 构造函数名
- null和undefined没有构造函数
- 不能跨iframe
- Object.prototype.toString
- 可以判断所有数据的类型
HTTP2.0协议的特点
- HTTP1.0
- 短连接,每次发送数据都建立连接
- HTTP1.1
- 长连接,可以被多个请求复用
- 同一个TCP连接可以发送多个请求,按次序通信请求,会阻塞后面的通信
- 新增请求方法/请求头/响应头
- HTTP2
- 多路复用
- 同时发送多个,避免阻塞
- 首部压缩
- 根据首部表存储和更新之前发送过的键值对,相同的在下次请求时就不会发送,减少冗余数据,减小开销
- 二进制分帧
- 二进制格式传输数据,不是文本格式,解析更高效
- 将请求和响应数据分帧,并且采用二进制编码
- 同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流
- 每个数据流都以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装,这也是多路复用同时发送数据的实现条件
- 服务器推送
- 允许服务端推送资源给客户端
- 多路复用
HTTP请求中options的用途,简单请求与复杂请求,跨域问题
- HTTP请求中options的用途
- 获取服务器能接受哪些请求方法,测试服务器性能,通知客户端是否携带凭证
- 简单请求
- 请求方法:get post head
- 请求头: Accept Accept-Language Content-Type DPR Downlink Save-Data Viewport-Width Width
- Content-Type: application/x-www-form-urlencoded multipart/form-data text/plain
- XMLHttpRequestUpload均没有注册任何事件监听器
- 请求中没有使用ReadableStream(可读流媒体)对象
- 复杂请求
- 在实际进行请求前,需要发起预检请求的请求
- 跨域
- 简单请求,只需设置Access-Control-Allow-Origin: *
- 复杂请求,设置相应的请求头
- Access-Control-Request-Method: POST
- Access-Control-Request-Headers: X-CUSTOMER-HEADER, Content-Type
浏览器的缓存机制,分别用什么请求头控制,Cache-Control属性的值,强缓存与协商缓存
- 浏览器的缓存机制
- 强缓存
- Cache-control (优先级高于expries)
- max-age=300(秒) 相对时间 距离请求发起的秒数
- no-store 不得存储
- no-cache 会缓存,但是每次请求前都要向服务端评估缓存的有效性,协商缓存是否可用根据响应时304还是200决定是否使用
- public || private(默认)是否可以被中间代理(被CDN缓存)
- must-revalidate 让缓存在过期后的任何情况下都必须验证
- expires: 具体时间(受限于本地时间)
- 时间内会读取缓存,不请求
- Cache-control (优先级高于expries)
- 协商缓存 (未命中强缓存,命中协商缓存 返回304)
- last-modified(文件最后修改日期) if-modified-since带上上次修改时间,判断资源是否有更新(本地打开也会修改,所以出现Etag)(秒级别的)
- Etag(指纹,资源变化都会导致Etag变化) If-None-Match会将山上次返回的Etag发送给服务端,有变化就会发送新的资源回来,优先级高于last-modified
- 某些文件修改次数过多,一秒内修改很多次,if-modified无法监测到
- 某些文件周期更改,但是不改变内容,值改变修改时间
- 某些服务器不能精确得到文件的最后修改时间
- 强缓存
XSS与CSRF攻击的原理以及避免
-
XSS 跨站脚本攻击
- 原理
- 攻击者嵌入恶意脚本代码到正常页面中,正常用户访问页面时可能会执行恶意代码 script img代码等
- 反射型 点击链接,反射到浏览器执行 跨域
- 存储型 在文档或博客加入代码,被存储到服务器,用户访问会执行(也称持久型,大量盗窃cookie)数据过滤,转义 设置cookie访问为http-only
- DOM型 修改DOM树,类似反射型 转义 不用innerHTML v-html 避免不可信的字符串拼接到 eval setTimeout ...API中,对于不信任的输入限制输入长度
- 原理
-
CSRF 跨站请求伪造
- 浏览器会对同一域名下的所有请求携带cookies,利用网站对客户的信任,
- 对于敏感操作加入验证码,强制用户与网站交互
- 避免使用get
- token验证,双重token
对于MVVM模式的理解
- MVC
- 视图从model层获取数据渲染,用户输入时,controller更新模型,并且更新视图
- MVVM
- view model view-model
- viewmodel只关心数据和业务处理,view和view通过双向绑定自动更新,通过viewmodel将视图中的状态和用户的行为分离出一个抽象
Vue的生命周期,分别在何时触发
- Vue组件从创建到销毁
- beforecreate
- 实例初始化之后,数据观测和事件配置前调用
- created
- 实例创建完成后,可以访问data
- beforemount
- 挂载前调用,相关的render函数首次被调用
- mounted
- el被vm.el替换,挂载到实例上之后调用
- beforeupdate
- 数据更新时,虚拟DOM打补丁之前调用,在更新前可以访问DOM,
- updated
- 虚拟DOM重新渲染和打补丁之后调用
- beforedestory
- 组件销毁前调用,可以移除一些定时器
- destoryed
- 组件销毁后调用
- actived
- keep-alive组件激活时调用
- deactived
- keep-alive组件停用时调用
- errerCaptured
- (错误对象,错误组件,错误信息) => ? boolean 返回false阻止错误继续向上传播
- beforecreate
Vue中循环的key的作用
- 每个虚拟节点的唯一标识,是优化diff算法的一种策略,更准确,更快速;
- diff算法时,会先进行首尾交叉对比,无法匹配的时候会用新节点的key与旧节点对比
- 如果不设置key,当数据更新后,默认使用就地复用策略,尽可能复用原有的数据,会产` 生bug
- key的唯一性可以被map保存,查找起来很快O(n)=>O(1)
Vue的diff算法,层次遍历还是深度遍历
- 只比较同级节点,层次遍历,再比较子节点
- 旧节点没有子节点,新节点有,则添加新节点
- 旧节点有子节点,新节点没有,则移除旧节点
- 递归比较子节点
- 新旧节点都有子节点,双端比较,借助key找到可复用的节点,在进行相关操作
Vue中父子组件传值通信的办法
- 父传子 props
- 子传父 $emit
- 兄弟 eventbus
- 祖孙 provide inject
- vuex
- 父子 ref parent $root
- $listeners .sync
Vue如何实现的数据双向绑定,在2.0与3.0有什么区别
- 数据劫持和发布订阅模式结合
- Object.defineProperty
- Proxy
Vue-Router如何实现的路由,Hash模式和History模式的区别
- 监听url的变化,展示不同的页面
- hash模式 #后面的hash值,监听hash的改变,不同hash对应不同页面
- history h5新出的api pushState repalceState popState 改变浏览器历史记录,需要后端配置跳转至index.html
编写程序将数组扁平化并去除其中重复部分数据,最终得到一个升序且不重复的数组
-
function myFlat(arr) { return arr.reduce((p, c) => { return p.concat(Array.isArray(c) ? myFlat(c) : c) }, []) } let arr = [ [10, 3], [4, 5], [2, [12, 5]], [4, 1] ] let unqiueArr = [...new Set(myFlat(arr))].sort((a,b) => a-b)
编程,实现plus(1)(2)(3)(4)等于8
-
function add(x){ var sum = x; var tmp = function(y){ sum = sum + y; return tmp; }; tmp.toString = function(){ return sum; }; return tmp; } console.log(add(1)(2)(3)(4).toString());