原型
每个对象都有一个属性__proto__指向构造函数的原型对象,对象可以继承原型对象上的属性和方法,原型对象又有原型,可以继承原型对象上的属性和方法,这样一层一层,以此类推,这种关系称为原型链
每个函数都有一个属性prototype指向原型对象,创建一个函数的时候就创建了这个属性
new 构造函数创建的实例,内部的__proto__属性指向构造函数的原型对象,普通对象的__proto__属性指向Object.prototype,函数对象的__proto__指向Function.prototype,每个对象都有__proto__属性,除了Object,create(null)创建的对象没有原型
原型对象上有一个constructor的属性指向构造函数,因为实例对象继承了原型对象的方法,所以实例对象也可以通过constructor属性找到构造函数
new一个对象
- 创建一个新的对象
- 将this指向这个对象
- 执行构造函数中的代码,添加属性和方法
- 返回这个新对象
闭包
有权访问函数内部变量的函数叫做闭包,创建的方式就是一个函数内部创建一个函数
闭包的两个用处:
- 可以访问函数内部变量,可以设计私有变量和方法
- 可以将变量始终保存在内存中
好处:实现封装和缓存,减少全局变量的污染
缺点:闭包会引用函数的作用域,增加内存用量,容易造成内存泄漏
解决方法:在退出函数之前,将不用的局部变量删除,将变量设为Null
作用域链的理解:
确保执行环境中对有权访问的变量和函数是有序的,只能向上访问,访问到window为止,不能向下访问
创建函数会创建一个作用域链(包含全局变量以及包裹他的函数活动对象),作用域链保存在内部的[[scope]]属性中
JS继承
- 构造函数继承:在子构造函数中加上Super.apply(this) 继承实例属性
- 原型链继承:Sub.prototype= Object.create(Super.prototype) 继承原型方法
- 组合继承 上面两个都应用
对this的理解
this 执行上下文对象,在函数调用时确定this的指向
- 默认绑定:直接使用函数引用调用的,指向window,严格模式是undefined
- 隐式绑定:对象.方法 对象调用函数指向对象
- 显式绑定:bind call apply 绑定this
- new创建的新对象 this指向新对象
陈述输入URL回车后的过程
- 解析URL
- DNS解析得到IP地址
- 与服务器进行TCP连接
- 发送http请求,传输数据
- 客户端得到服务器端响应的html文件
解析HTML页面
- 根据HTML解析出DOM树
- 根据css解析生成css规则树
- 结合DOM树和css规则树生成 渲染树
- 根据渲染树计算每个节点的信息
- 根据计算好的信息绘制页面
DOM树中包含所有的标签包括dispaly:none的元素
render树中只包含所有可见的元素以及visible:hidden
回流和重绘
回流(Reflow):渲染树中的元素尺寸、形状、为止改变时,影响了周围元素或内部元素时,浏览器会重新渲染部分或全部文档
操作:
- 页面首次渲染
- 添加或删除可见元素
- 元素尺寸内容位置字号图片大小改变
- 浏览器窗口resize
- 激活css伪类(:hover)
重绘(Repaint):改变颜色以及visible
性能影响:回流的代价更高
浏览器优化回流,将所有引起回流重绘的操作放入队列中,如果任务数量或时间间隔达到某一个阈值,浏览器会清空队列,进行一次批处理
但是某些方法或属性,会立即处理:
clientWidth/offsetWidth/scrollWidth/width/height/getComputedStyle(elem,null).getPropertyValue(‘height’)
优化:引起少量回流或少量元素回流
- 避免使用table布局,因为table在变成渲染树时会经过多次计算
- 将动画效果应用在position为absolute或fixed元素上,,使其脱离文档流,否则会引起父元素以及后续元素的回流
- 避免使用css表达式(会增大计算量)
javascript中避免回流的方法
- 避免频繁操作样式 style一次重写,用class2
- . documentFragment 文档片段 不会回流
- 必须用到的属性 offsetHeight 缓存
- 先display:none;操作之后再display:block;
- 对动画使用绝对定位
defer和async
defer: 延迟执行引用的JS(执行之后,执行DOMContentLoaded)
载入JS文件不会影响HTML的解析,执行阶段会放到HTML解析完成之后
async: 异步引入的js
如果已经加载好,就开始执行(此时可能是HTML解析阶段,可能是在DOMContentLoaded之后);一定在load之前触发;执行会阻塞HTML解析,如果是在HTML解析阶段
src和href的区别
src: 是下载src指向的资源应用到当前文档中,会暂停其他资源的下载和解析
href是指向网络资源所在位置,并行下载资源,不会停止对当前文档的处理
BFC块级格式化上下文
常规流(标准流):
块盒一行一行竖着排列;行内盒横着排列;float为none;position为static和relative在常规流中排列
position为relative相对定位,位置偏移,但是原有位置会在常规流中保留
BFC的效果:
- 创建一个独立的空间
- 内部有个标准流
- 处于同一个bfc的元素,可能会发生margin合并
- 计算BFC高度是,考虑BFC的所有子元素,包括浮动元素
BFC的创建
- 根元素
- float不为none,position为fixed和absolute
- 行内块(display:inline-block)
- 表格单元格(display:table-cell)
- 弹性盒(display:flex/inline-flex)
- overflow不为 visible
外边距合并: 处于同一个BFC中的块盒垂直方向上会发生margin合并
解决:给其中一个盒子加上overflow:hidden;让其不在同一个BFC中
事件代理(委托)
把需要绑定的事件委托给父元素,让父元素监听事件
原理:事件冒泡 冒泡的事件可以委托 不冒泡的事件不可以委托
好处:提高性能,节约内存占用,减少事件注册
event.target是触发的对象
event.currentTarget 是绑定事件的对象
事件模型
事件3个阶段:捕获(父到子)目标 冒泡(子到父)
默认触发事件的阶段是冒泡阶段
如果把addEventListener的第三个参数改为true 就是在捕获阶段触发
阻止冒泡:stopPropagation IE是 cancelBubble= true
取消默认事件: e.preventDefault IE是 e.returnValue= false
说说对promise 的了解
- Promise是异步抽象处理对象,处理异步请求返回的响应数据的使用
- 常见异步编程方案:
链接: https://blog.csdn.net/qq_32442973/article/details/89322763.
(1)回调
(2)事件监听
(3)发布订阅
(4)Promise
(5)async await - 一共有三种状态 pending resolved rejected
pending 变为resloved
pending 变为rejected
只有这两种变化,并且一个promise对象只能改变一次,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般为value,失败的结果数据一般为reason - then 注册成功或失败的回调 一定返回新的promise对象,返回的promise的结果是执行回调函数(onResolved、onRejected)的结果
async 和 await
- await必须放在async函数里面
- await后面是一个promise对象,如果不是就转为一个立即resolve的promise对象
- 只要await后面的promise变成reject,那么整个async函数会被中断执行,如果不希望中断后续请求 使用try …catch
- async 函数返回一个promise对象,函数中的返回值就是这个promise对象resolve中的参数,如果出错了throw new Error(‘出错了’)或者 await Promise.reject(‘出错了’),就是reject中的参数
commonJS
- node.js是commonJS的主要实践者
- 一个文件一个模块,每个模块都有独立的作用域,在一个模块内定义的变量在其他模块中无法读取,除非是global的属性和方法
- 模块只有一个出口,module.exports是一个对象,将想要暴露的方法或者属性放到该对象上;用require(url)加载一个模块
特点:同步加载模块,服务端,模块文件都在本地磁盘上,读取起来很快,所以没问题,但浏览器上限于网络原因,更合理应使用异步加载
AMD和require.js
- 用require.js实现AMD的模块化
- require.config() 指定引用路径
- define() 定义模块
- require()加载模块
特点:异步加载模块,模块的加载不影响后面语句的执行。所有依赖这个模块的语句都被定义在一个回调中,等待加载完成执行
requireJS主要解决的问题:
(1)多个JS可能存在依赖关系,被依赖的文件要早于依赖他的文件加载
(2)JS加载,浏览器暂停页面渲染,加载文件越多,页面失去响应的时间越长
CMD通用模块定义
一个模块一个文件
define(function(require, exports,module){
var $= require(‘jquery.js’)
$(‘div’).addClass(‘active’)
})
在文件中引入加载模块,使用时加载
AMD与CMD的区别
AMD:依赖前置,定义模块时就下载并执行依赖模块,所有模块执行完了,才开始执行回调中的主逻辑
CMD:就近依赖,定义模块时需要把模块变成字符串遍历一遍,知道依赖了哪些模块,然后下载但不执行,只有主逻辑遇到require了才去执行依赖
AMD中的依赖放在数组中,依赖执行没有先后顺序
CMD中遇到require才去执行,执行顺序与页面书写的顺序一致
下载都是异步下载,AMD体验好,因为没有延迟,依赖模块提前执行;CMD性能好,只有用户用到才去执行
ES6模块
ES6 的模块不是对象,import命令会被JavaScript引擎静态分析,在编译时就引入模块代码,而不是运行时,因此不能实现条件加载
CommonJS与ES6的区别
- commonJS模块输出值的拷贝(浅拷贝)
ES6输出值的引用,js引擎对脚本静态分析时,遇到import命令会生成一个只读引用,当代码真正执行时,根据这个引用到被加载的模块中取值;因此ES6是动态绑定,不会缓存值,import的变量绑定其所在模块
export default math
import math form ‘./math’
math中存放的是指向math.js 中对象的指针 - CommonJS是运行时加载,ES6是编译时输出接口
CommonJS运行时加载整个模块,生成一个对象,在对象上调用方法
ES6模块不是对象,而是通过export命令显示指定输出的代码,import时采用静态命令形式,即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载成为编译时加载
export let bar= 1
setTimeout(()=> { bar= 2; }, 500)
import { bar } from ‘utils.js’
console.log(bar) // 1
setTimeout(() => { console.log(bar) }, 500) //
vue和react的区别
- react是函数式的思想,并且是单向数据流;vue是响应式的,视图的改变基于数据,并且对于表单属性来说,vue是双向绑定的,当视图变化,data中的数据也会变化
- react通过JS来生成HTML,所以设计了jsx;vue是把html,css,js组合到一起,用各自的处理方式,vue有单文件组件,可以吧html/css/js写到一个文件中,HTML提供了模板引擎来处理
- react是类的写法;vue是声明式的写法
- react可以通过高阶组件来扩展,而vue通过mixins来扩展
对于MVVM的理解
MVVM是model-view-viewmodel的缩写
model代表数据层 ,view代表是视图层,
viewmodel监听数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步view和model的对象,连接model和view
在MVVM架构中,view和model之间没有直接的联系,而是通过viewmodel进行交互,model和viewmodel之间的交互式双向的,因此view数据的变化会同步到model中,而model数据的变化也会立即反映到view上
vue响应式系统
- 每个Vue组件都有一个watcher实例与之对应
- 每个属性都有一个与之对应的dep实例
- vue的data属性都被observer数据劫持添加上setter和getter
- 当组件render执行,会访问属性,触发属性的getter,此时vue的watcher会记录渲染视图依赖的属性,与属性对应的dep实例也会记录当前的watcher
- 当属性改变时,会触发属性的setter,dep就会通知所有依赖此属性的watcher,执行watcher的update方法,如果是渲染watcher就是重新渲染视图,如果是user、computed watcher就执行回调
vue组件通信
- 父->子: 传props
- 子->父:
(1)父组件传值的时候 :title.sync=“doc.title” 子组件使用 this. e m i t ( " u p d a t e : t i t l e " , n e w T i t l e ) 触 发 事 件 更 新 父 组 件 的 值 ( 2 ) 方 法 名 作 为 属 性 名 父 组 件 : : g e t T i t l e = " g e t T i t l e " 子 组 件 : t h i s . g e t T i t l e ( ) ( 3 ) 方 法 作 为 事 件 父 组 件 : @ g e t T i t l e = " g e t T i t l e " 子 组 件 : t h i s . emit("update:title", newTitle) 触发事件更新父组件的值 (2)方法名作为属性名 父组件::getTitle="getTitle" 子组件:this.getTitle() (3)方法作为事件 父组件:@getTitle="getTitle" 子组件:this. emit("update:title",newTitle)触发事件更新父组件的值(2)方法名作为属性名父组件::getTitle="getTitle"子组件:this.getTitle()(3)方法作为事件父组件:@getTitle="getTitle"子组件:this.emit(‘getTitle’) - eventBus事件总线
- vuex全局数据管理
state mutation 更改store中状态的唯一方式 提交mutation(mutation必须是同步函数) action提交的是mutation 不直接更改状态 可以是异步函数 - provide/inject 允许一个祖先组件向其子孙后代注入一个依赖
vue路由的钩子函数
beforeRouteEnter进入路由之前
beforeRouteUpdate路由改变 但该组件被复用
beforeRouteLeave 离开路由之前
afterEach 进入每个路由之后
beforeEach 进入每个路由之后
vue的diff算法
相同类型的节点:
- key相同(如果没有 就是undefined == undefined)
- tag 标签名相同且isComment(是否为注释节点)且data是否都存在且是否都为input或不为input
- 是异步组件 工厂函数asyncFactory相同
vue的diff算法
- 对比新旧开始结束节点是否为相同类型的节点,是的话就原地复用,新旧开始指针向后挪一个
- 对比新旧开始与结束或结束与开始节点是否为相同节点;是的话就用insertBefore移动节点
- 如果新有旧没有,添加节点
这样简单的移动添加得到快速处理,待处理节点减少,缩小处理范围,性能得到提升
vue中key值的作用
就是在上面提到的判断节点是否为相同类型节点时,如果key相同的话就会就地复用,key的作用主要是为了高效的更新虚拟DOM