在Promise内部,有一个状态管理器的存在,有三种状态: pending、fulfilled、rejectedjavascript
(1) promise初始化状态为pendinghtml
(2) 当前调用resolve(成功), 会由pending => fulfilledvue
(3) 当调用reject(失败), 会由pending => rejectedjava
协议、端口和域名不一致致使的跨域 跨域是由于浏览器须要遵照同源策略,发出的请求即便相应成功,也被浏览器拦截下来node
同源策略限制了从同一个源加载的文档或脚本如何与来自另外一个源的资源进行交互、这是一个用于隔离潜在恶意文件的重要安全机制、nginx
若是缺乏了同源策略,浏览器很容易受到XSS、CSFR等攻击。面试
一、 防护 XSS 攻击vuex
二、防护 CSRF 攻击数据库
一、经过jsonp跨域 二、document.domain + iframe跨域 三、location.hash + iframe 四、window.name + iframe跨域 五、postMessage跨域 六、跨域资源共享(CORS) 七、nginx代理跨域 八、nodejs中间代理跨域 九、WebSocket协议跨域json
jsonp的核心则是动态添加 script 标签调用服务器提供的js脚本,容许用户传递一个callback参数给服务器,而后服务器返回数据时会将这个callback参数做为函数名老包裹JSON数据,这样客户端就能够随意定制本身的函数来自动处理返回数据了
一、Content方面
二、Server方面
三、Cookie方面
四、CSS方面
五、JavaScript
六、图片方面
七、移动方面
大概流程
一、在浏览器数地址栏输入URL
二、浏览器查看缓存,若是请求资源在缓存中而且新鲜,跳转到转码步骤
Expires
和 Cache-Control
三、浏览器解析URL获取协议,主机,端口,path
四、浏览器组装一个HTTP(GET)请求报文
五、浏览器获取主机ip地址,过程以下:
六、打开一个sokcet与目标地址,端口创建TCP连接, 三次握手以下:
七、TCP连接创建后发送HTTP请求
八、服务器接受请求并解析,将请求转发到服务程序,如虚拟机使用HTTP Host头部判断请求的服务程序
九、服务器检查HTTP请求头是否包含缓存验证信息若是验证缓存新鲜,返回304等对应状态码
十、处理程序读取完整请求并准备HTTP响应,可能须要查询数据库等操做
十一、服务器将响应报文经过TCP连接发送回浏览器
十二、浏览器接受HTTP响应,而后根据状况选择关闭TCP链接或者保留重用,关闭TCP链接的四次握手以下:
1三、浏览器检查响应状态码:是否为1XX、3XX、4XX、5XX,这些状况处理与2XX不一样
1四、若是资源可缓存,进行缓存
1五、对响应进行解码(例如gzip压缩)
1六、根据资源类型决定如何处理(假设资源为HTML文档)
1七、解析HTML文档、构件DOM树,下载资源,构造CSSOM树,执行js脚本,这些操做没有严格的前后顺序,如下分别解释
1八、构建DOM树:
1九、解析过程当中遇到图片、样式表、js文件,启动下载
20、构建CSSOM树
2一、根据DOM树和CSSOM树构建渲染树:
script,meta
这样自己不可见的标签。2)被CSS隐藏的节点,入display:none
2二、js解析以下
2三、显示页面(HTML解析过程当中会逐步显示页面)
一、经过meta标签设置viewport,移动端的理想适口。
<meta name="viewport" content="width=width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
二、设置rem单位来进行适配、加上Flex布局、百分比布局
三、其它方案,响应式适配、vw+rem
JavaScript 函数中的 this 指向并非在函数定义的时候肯定的,而是在调用的时候肯定的。换句话说,函数的调用方式决定了 this 指向。 函数调用的方式
直接调用,就是经过 函数名(...) 这种方式调用
方法调用是指经过对象来调用其方法函数,它是 **对象.方法函数(...)** 这样的调用形式
官方解释:箭头函数表达式的语法比函数表达式更简洁,而且没有本身的this,arguments,super或 new.target。
引用箭头函数有两个方面的做用:更简短函数和而且不绑定this
箭头函数不会建立this,它只会从本身的做用域链上一层继承this。
简而言之,箭头函数,永远指向当前调用的对象
BFC就是"块级格式化上下文"的意思,建立了BFC的元素就是一个独立的盒子,不过只有Block-level Box 能够参与建立BFC,它规定了内部的Block-level Box如何布局,而且与这个独立盒子里的布局不受外部影响,固然它不会影响到外面的元素。
利用发布/订阅模式,发布/订阅模式由一个发布者、多一个订阅者以及一个调度中心所组成。订阅者们先在调度中心订阅某一事件并注册相应的回调函数,当某一个时刻发布者发布了一个事件,调度中心取出订阅了该事件的订阅者们所注册的回调函数来执行。
在发布/订阅模式中,订阅者和发布者并不须要关心对方的状态,订阅者只管订阅事件并注册回调、发布者只管发布事件,其他一切交给调度中心来调度,从而实现解耦。
Vue是采用数据劫持结合发布者-订阅者模式的方式,经过Object.defineProperty()来劫持各个属性的setter、getter,在数据变更时发布消息给订阅者,触发响应的监听回调。
具体步骤:
第一步:须要 Observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到数据变化。
第二步:Compile 解析模板指令,将模板中的变量替换成数据,而后初始化渲染页面视图,并将每一个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变更,收到通知,更新视图
第三步:Watcher 订阅者是 Observe 和 Compile 之间通讯的桥梁,主要的事情是:
一、在自身实例化时往属性订阅器(dep)里面添加本身
二、自身必须有一个update()
三、待属性变更dep.notify()通知时,能调用自身的 update() 方法,并触发 Compile 中绑定回调,则功成身退。
第四步:MVVM做为数据绑定的入口,整合 Observe、Compile 和 Watcher 三者,经过 Observe 来监听本身的 Model 数据变化。 经过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observe 和 Compile 之间的通讯桥梁; 达到数据变化 -> 视图更新; 视图交互(input) -> 数据 Model 变动的双向绑定效果。
第一步:建立一个computedWathcers 空对象, 对 computed 对象遍历,获取计算属性每个 userDef(自定义的函数或对象),而后尝试获取 userDef 的getter,而且为每个 getter 添加一个watcher
第二步:判断遍历 computed 对象的key,是否已经存在 data 和 props 所占用,存在则发出警告,不存在就调用 defineComputed 函数,给对应的key添加getter 和 setter
第三步:在调用 defineComputed 函数,会进行依赖收集 computedWatcher ,经过computedWatcher来进行派发通知,更新视图
第四步:缓存就是在获取 getter 数据的,判断是否值相等,相等的话就直接返回,再也不进行更新视图
MVVM分为Model、View、ViewModel三者
Model 和 View 并没有直接关联,而是经过 ViewModel 来进行联系的, Model 和 ViewModel 之间有着双向数据绑定的联系。所以当 Model 中的数据改变时会触发 View 层的刷新,View 中因为用户交互操做而改变的数据也会在 Model 中同步
区别:这种模式实现了 Model 和 View的数据自动同步,所以开发时这须要要专一对数据的维护操做便可,而不须要本身操做dom 场景:数据操做比较多的场景,更加便捷
JS,是单线程的,利用JS的事件循环
事件循环大体分为如下几个步骤:
(1) 全部同步任务都在主线程上执行,造成一个执行栈(execution context stack)
(2) 主线程以外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3) 一旦"执行栈"中的全部同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。哪些对应的异步任务,因而结束等待状态,进入执行栈,开始执行。
(4) 主线程不断重复上面的第三步
先执行宏观任务,再执行微观
for (macroTask of macroTaskQueue) {
// 1. Handle current MACRO-TASK
handleMacroTask();
// 2. Handle all MICRO-TASK
for (microTask of microTaskQueue) {
handleMicroTask(microTask);
}
}
复制代码
nextTick原理:
会有一个callbacks数组,接受nextTick的回调函数,push进去
首先判断是否支持Promise,支持则利用的Promise.then进行调用遍历调用callbacks数组
判断是否支持 MutationObserver,支持则利用 MutationObserver 遍历调用callbacks数组
判断是否支持 setImmediate,支持则利用 setImmediate 遍历调用callbacks数组
都不支持,则利用setTimeout进行遍历调用 callbacks数组
面试回答 : 它的逻辑也很简单,把传入的回调函数 cb 压入 callbacks 数组,最后一次性地根据 useMacroTask 条件执行 macroTimerFunc 或者是 microTimerFunc,而它们都会在下一个 tick 执行 flushCallbacks,flushCallbacks 的逻辑很是简单,对 callbacks 遍历,而后执行相应的回调函数。
VNode是对真实 DOM 的一种抽象描述,它的核心定义无非就几个关键属性,标签名、数据、子节点、键值等,其它属性都是用来扩展VNode的灵活性以及实现一些特殊 feature的。
Virtual DOM 除了它的数据结构的定义,映射到真实的 DOM 实际上要经历 VNode的 create、diff、 patch等过程。
父子组件通讯,props、emit、ref调用函数
兄弟组件通讯,vuex、eventBus
vuex具备五种属性: state、getter、mutation、action、module
vuex就是一个仓库,仓库里面放不少对象。state就是数据存放地,对应于通常vue对象里面的data
state里面存放的数据是响应式的
getters能够对state进行计算操做
能够在多组件之间复用
action相似于mutation
action提价的是mutation,而是否是直接变动状态
action能够包含任何异步操做
可维护性会降低,你要想修改数据,你得维护三个地方
可读性降低,由于一个组件里的数据,你根本看不出来是从哪来的
增长耦合,大量的上传派发,会让耦合性大大的增长,原本Vue用Component就是为了减小耦合,如今这么用,和组件化的初衷相背。
总共分为8个阶段建立前/后,载入前/后,更新前/后,销毁前/后
建立前/后: 在beforeCreated阶段,vue实例的挂载元素el尚未。
载入前/后: 在beforeMount阶段,vue实例的$el和data都初始化了,但仍是挂载以前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后: 当data变化时,会触发beforeUpdate和updated方法。
销毁前/后: 在执行destroy方法后,对data的改变不会触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,可是dom结构依然存在
首先,组件能够提高整个项目的开发效率。可以把页面抽象成多个相对独立的模快,解决了咱们传统项目开发:效率低、难维护、复用性等问题。
而后,使用Vue.extend方法建立一个组件,而后使用Vue.component方法注册组件。子组件须要数据,能够在props中接受定义。而子组件修改好数据后,想把数据递给父组件。能够采用emit方法。
Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等是 **Object.defineProperty()**不具有的
Proxy返回的是一个新对象,咱们能够只操做新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改
Proxy做为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利
固然,Proxy的劣势就是兼容性问题,并且没法用polyfill磨平,所以Vue的做者才声明须要等到下个大版本(3.0)才能用Proxy重写。
数据存储方案:
大概说一下Cookie和localStorage、sessionStorage的功能特性。问到的话,Cookie的缺点就是,存储量少、数据大影响性能、只能储存字符串、安全性问题、须要检查Cookie可否使用
/** * @desc 深拷贝,支持常见类型 * @param {Any} values * @return {Any} */
function deepClone(values) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == values || "object" != typeof values) return values;
// Handle Date
if (values instanceof Date) {
copy = new Date();
copy.setTime(values.getTime());
return copy;
}
// Handle Array
if (values instanceof Array) {
copy = [];
for (var i = 0, len = values.length; i < len; i++) {
copy[i] = deepClone(values[i]);
}
return copy;
}
// Handle Object
if (values instanceof Object) {
copy = {};
for (var attr in values) {
if (values.hasOwnProperty(attr)) copy[attr] = deepClone(values[attr]);
}
return copy;
}
throw new Error("Unable to copy values! Its type isn't supported.");
}
复制代码
若是是一个数组,就声明一个数据组,而后循环遍历,递归赋值。 若是是一个对象,就声明一个对象,而后判断是否子元素,递归赋值
除了递归,咱们还能够借用JSON对象的parse和stringify
function deepClone(obj){
let _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone
}
复制代码