面试题 原型链和es5继承 闭包 DOCTYPE的作用 获取浏览器地址参数 柯里化函数 图片的懒加载和预加载 什么是虚拟dom webpack 二叉树 react和vue的区别 vue2和vue3的区别 #flex布局 #new之前做了什么 #动画 #http #bfc #递归 uri和url的区别 浏览器存储 (cookie、localStorage、sessionStorage) h5新特性 keep-alive组件缓存组件问题 和生命周期 VUE单页应用首屏加载速度优化方案 vue3.0中为何使用proxy? nextTick源码分析 动态路由匹配 vue中key的作用和工作原理 如何解决vuex页面刷新数据丢失问题?
------------恢复内容开始------------
// 语法1: 原型继承
// 所谓的原型继承,就是通过 原型链 将父类构造函数 和 子类 构造函数 串联起来
// 所谓的原型链,本质就是通过 __proto__ 将实例化对象,构造函数 串联起来,可以调用数据
// 实例化对象,通过 __proto__ 访问 构造函数 prototype 中存储的函数方法
// 父类构造函数 <--- 实例化对象 <--- 子类构造函数
// 语法2: 借用构造函数继承
// 主要继承 属性
// 语法3: 组合继承
// 同时使用 原型继承和借用构造函数继承
// 原型继承 可以继承父类的方法 但是不能定义继承父类的属性
// 借用构造函数继承 可以定义继承父类的属性,但是不能继承父类的方法
// let 声明 变量i 可以完成需要的效果
// 原因: 每次执行循环,都会生成一个独立的,存储独立数据的循环变量 i
// 点击时,调用i 不同的标签,触发的点击事件,执行时,i对应的是不同数值
闭包
闭包的特点 优点,同时也是缺点
1, 函数的执行空间不会被销毁
优点 : 空间中的内容,会一直存在
缺点 : 会占用内存空间,降低执行效率
2, 可以从函数外部调用函数内部的数据
优点 : 数据使用便捷
缺点 : 容易造成数据泄露,不安全
3, 保护局部作用域变量不被销毁
优点 : 局部作用域变量不被销毁
缺点 : 占用存储空间,容易内存泄露
闭包的重要性:
闭包看似是一个为了调用函数内,局部作用域变量的方法
实际上,是为了 防止 全局变量污染 的一个手段
全局变量污染
定义变量分为全局作用域变量和局部作用域变量
全局作用域变量特点:任意一个函数都可以调用修改这个变量存储的数据数值
如果一个全局作用域变量,被不应该操作的函数,修改了存储的数据,就称为全局变量濡染
为了防止全局变量污染,可以使用一个函数,将变量定义为局部作用域变量
其他函数的操作,就不会影响到这个变量存储的数据了
但是又产生了新的问题,定义在函数内部的局部作用域变量,无法在函数外部被直接使用
可以使用闭包,让,局部作用域变量,可以在函数外部被调用,被使用
问:浏览器页面有哪三层构成,分别是什么,作用是什么?
构成:结构层、表示层、行为层
分别是:HTML、CSS、JavajScript
作用:HTML实现页面结构、CSS完成页面的表现与风格、JavaScript实现客户端的一些功能和业务
DOCTYPE的作用
DOCTYPE是document type (文档类型) 的缩写。<!DOCTYPE >声明位于文档的最前面,处于标签之前,它不是html标签。主要作用是告诉浏览器的解析器使用哪种HTML规范或者XHTML规范来解析页面
# 清除浮动
(1)、父级div定义 height 原理:父级div手动定义height,就解决了父级div无法自动获取到高度的问题. 缺点:只适合高度固定的布局,要给出精确的高度,
(2) 、结尾处加空div标签 clear:both 原理:添加一个空div,利用css提高的clear:both清除浮动,让父级div能自动获取到高度 如果页面浮动布局多,就要增加很多空div,让人感觉很不好
(3)、父级div定义 伪类:after 和 zoom 原理:IE8以上和非IE浏览器才支持:after,原理和方法2有点类似,zoom(IE转有属性)可解决ie6,ie7浮动问题
(4)、父级div定义 overflow:hidden 原理:必须定义width或zoom:1,同时不能定义height,使用overflow:hidden时,浏览器会自动检查浮动区域的高度 建议:只推荐没有使用position或对overflow:hidden理解比较深的朋友使用
(5) 、父级div定义 overflow:auto 原理:必须定义width或zoom:1,同时不能定义height,使用overflow:auto时,浏览器会自动检查浮动区域的高度 缺点:内部宽高超过父级div时,会出现滚动条
# 元素居中
1,块元素,且宽和高已知
父元素relative; 子元素absolute left和top是50%,margin-left和top是宽高的一半就行
2,块元素,且宽和高未知
父元素relative; 子元素absolute left和top是50%,transform: translateX(-50%) translateY(-50%);
3,flex布局
父级display: flex; justify-content: center; align-items: center;
4,margin:auto
父元素relative; 子元素absolute top: 0; right: 0; bottom: 0; left: 0; margin: auto;
获取浏览器地址参数
function getQueryVariable(variable) {
var query = window.location.search.substring(1)
var vars = query.split("&")
for(let i =0 ; i <= vars.length ; i++){
var pre = vars[i].split("#")
if(pre[0] == variable){
return pre[1]
}
}
}
柯里化函数
柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
function curry (fn, currArgs) {
return function() {
let args = [].slice.call(arguments);
// 首次调用时,若未提供最后一个参数currArgs,则不用进行args的拼接
if (currArgs !== undefined) {
args = args.concat(currArgs);
}
// 递归调用
if (args.length < fn.length) {
return curry(fn, args);
}
// 递归出口
return fn.apply(null, args);
}
}
函数的柯里化,是 Javascript 中函数式编程的一个重要概念。它返回的,是一个函数的函数。其实现方式,需要依赖参数以及递归,通过拆分参数的方式,来调用一个多参数的函数方法,以达到减少代码冗余,增加可读性的目的。
call 和 apply 的共同点
它们的共同点是,都能够改变函数执行时的上下文,将一个对象的方法交给另一个对象来执行,并且是立即执行的
call 和 apply 的区别
它们的区别,主要体现在参数的写法上。先来看一下它们各自的具体写法。
不同点
- call和apply会调用函数,并且改变函数内部的this指向
- call和apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递
- bind不会调用函数,需要定义新函数调用
bind 方法 与 apply 和 call 比较类似,也能改变函数体内的 this 指向。不同的是,bind 方法的返回值是函数,并且需要稍后调用,才会执行。而 apply 和 call 则是立即调用。
#防抖和节流
1、防抖(debounce):触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间
举例:就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才会触发。
节流(thorttle):高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率
举例:预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,可是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快导致系统出现bug。
2、区别:防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
图片的懒加载和预加载
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。
数据双向绑定作为 Vue 核心功能之一,其实现原理主要分为两部分:
- 数据劫持
- 发布订阅模式
getter是收集依赖 setter 是通知变更
什么是虚拟dom
angular,react,vue 等框架的出现就是为了解决这个问题的。
他们的思想是每次更新 dom 都尽量避免刷新整个页面,而是有针对性的去刷新那被更改的一部分,来释放掉被无效渲染占用的 gpu,cup 性能。
angular
angular 采用的机制是 脏值检测查机制 所有使用了 ng 指令的 scope data 和 {{}} 语法的 scope data 都会被加入脏检测的队列
vue
vue 采用的是虚拟 dom 通过重写 setter , getter 实现观察者监听 data 属性的变化生成新的虚拟 dom 通过 h 函数创建真实 dom 替换掉dom树上对应的旧 dom。
react
react 也是通过虚拟 dom 和 setState 更改 data 生成新的虚拟 dom 以及 diff 算法来计算和生成需要替换的 dom 做到局部更新的
webpack
- 入口(entry)
- 输出(output)
- loader
- 插件(plugins)
入口(entry)
入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
出口(output)
output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output 字段,来配置这些处理过程:
loader
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理
插件(plugins)
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
webpack中提供了require.ensure()来实现按需加载。以前引入路由是通过import 这样的方式引入,改为const定义的方式进行引入。
二叉树
二叉树是每个节点最多有两个子树的有序树。通常子树的根被称作“左子树”和“右子树”。二叉树常被用做二叉查找树和二叉堆或是二叉排序树。二叉树的每个节点至多只有两颗子树,二叉树有左右之分,次序不能颠倒。 四叉树是一个节点四个子节点
react和vue的区别
react整体是函数式的思想,把组件设计成纯组件,状态和逻辑通过参数传入,所以在react中,是单向数据流,推崇结合immutable来实现数据不可变。react在setState之后会重新走渲染的流程,如果shouldComponentUpdate返回的是true,就继续渲染,如果返回了false,就不会重新渲染,PureComponent就是重写了shouldComponentUpdate,然后在里面作了props和state的浅层对比。
而vue的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立Watcher来监听,当属性变化的时候,响应式的更新对应的虚拟dom。
react可以通过高阶组件(Higher Order Components--HOC)来扩展,而vue需要通过mixins来扩展
vue2和vue3的区别
vue2的主程序入口是main.js
vue3d暴露出vreateApp方法
vue3 beforeDestroy --> beforeUnmount destroyed --> unmounted
setup 中接受的props是响应式的, 当传入新的 props 时,会及时被更新。
由于是响应式的, 所以不可以使用 ES6 解构,解构会消除它的响应式。
reactive用于处理对象的双向绑定,ref处理 js 基础类型或者处理对象的双向绑定。 注意refs 它接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property.value数据双向绑定:
关于数据双向绑定的实现,vue2 采用了defineProperty,而vue3则采用了proxy。
优点:
1. 使用proxy不污染源对象,会返回一个新对象,defineProperty是注入型的,会破坏源对象
2. 使用proxy只需要监听整个源对象的属性,不需要循环使用Object.defineProperty监听对象的属性
3. 使用proxy可以获取到对象属性的更多参数,使用defineProperty只能获取到监听属性的新值newvalue
#flex布局
作用:弹性盒子控制子元素的布局方法(不包括孙子辈)
特点:
a,弹性盒子默认情况下,子元素沿着主轴排列,默认情况下x轴为主轴
b,弹性盒子的子元素,称作‘灵活元素’所有的子元素都能设置宽高
c,如果一个子元素在弹性盒里面左右上下居中只需要给子元素添加margin: 0 auto;
1、触发弹性盒:
display:flex;
2、改变主轴的方向(加在父元素上面)
flex-direction:;
属性值: row x轴为主轴
column y轴为主轴
row-revers x轴主轴反向排列
column-revers y轴主轴反向排列
3、控制主轴对齐方式:
justify-content:;
属性值:
flex-start 弹性盒子开始位置
flex-end 弹性盒子结束位置对齐
center 居中对齐
space-between 二端对齐
space-around 自动分配间距
4、侧轴对齐方式(父元素上面)
align-items:;
属性值:
flex-start 侧轴开始
flex-end 结束
center 居中
baseline flex-stare等效
stretch(默认值)拉伸
5、弹性盒子元素不会换行(在主轴挤压)是否让子元素超出的时候换行
flex-wrap:
属性值:
nowrap 不换行
wrap 换行
wrap-revers 底部对齐换行
6、控制行与行之间的间距
align-content:;
属性值:
flex-start 开始
flex-end 结束
center 居中
space-between 二端对齐
space-around 自动分配
7、flex-flow:;
flex-direction和flex-wrap
flex-flow:column wrap;
添加子元素上面的元素:
8、控制某一个子元素在侧轴对齐方式
align-self:
auto 默认值
stretch 拉伸
center 居中
flex-start 元素位于容器开头
flex-end 结尾
9、控制子元素的排列顺序:
order 属性值是个数字,数字越大越往后排列,能接受负数
10、剩余空间分配:
flex:1; 把剩余空间全部分配给当前元素
#new之前做了什么
(1) 创建一个新对象;我们将这个空对象的__proto__成员指向了函数prototype成员对象 在普通函数里面的this是window对象
所以得改变this指向来给空对象添加属性和方法 函数.call(空对象); 把函数里面的this改变成空对象
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
(3) 执行构造函数中的代码(为这个新对象添加属性) ;
(4) 返回新对象
相当于干了四件事情
1.var obj={};
2.obj.__proto__=test.prototype;
3.test.call(obj);
4.把obj的地址赋值给等式左边的变量
#动画
#http
一般完整的 网络地址 应该由 这三部分组成
协议 地址 端口
http 规范的 网络传输协议的一部分
要与服务器建立连接 : 需要 三次握手 过程
与服务器断开连接 : 需要 四次挥手 过程
#bfc
BFC::纯概念-》布局逻辑规定
直译:块状格式化上下文(就是一个独立的渲染区域)
注:只有块状元素参与BFC布局逻辑
一,BFC的布局规则
A,内部的box会垂直方向一个接一个的放置
B,box垂直方向的距离由margin决定属于同BFC的两个相邻的盒子上下margin会重叠
C,每个元素的margin左侧与包含块border,box的左边接触
D,BFC的区域不会与float box重叠
E,BFC就是页面上一个隔离独立渲染的容器,容器里面的子元素不会影响到盒子外面的元素
F,计算BFC高度时,浮动元素也参与计算
二,BFC触发条件
A,根元素(html)
B,Float属性不为none
C,Position为absolute或fixed
D,Display为inline-block,table-cell,table-caption,flex,inline-flex
E,Overflow为visibiel(默认值)
三,BFC的应用
1,解决高度坍塌
2,放置margin上下重叠
3,完成自适应二栏布局(左侧宽度固定 右侧宽度自适应)
width:calc(100%—宽度)
四,解决margin重叠问题
对于上下相邻的两个元素,只要把其中一个设置为display:inline-block
给父元素设置border或者padding,
给父元素设置 overflow: hidden或者display: inline-block或者float: left或者position: absolute,子元素的margin就不会与父元素重叠。
#递归
uri和url的区别
uri可以认为是一个编号,类似一个身份证号,用来标识其唯一性
,而url既可以标识其具有唯一性,而且可以根据url找到资源的位置,这就是区别
浏览器存储
(cookie、localStorage、sessionStorage)
h5新特性
语义化标签 新元素是canvas,新多媒体元素video,audio 页面缓存指的还是有网络状态下,而离线web应用指的是在没有网络状态可以运行应用(applicationCache
)css3选择器伪类选择器 before、after、first-child、nth-child 提供的效果包括box-shadow、text-shadow、background-size。
keep-alive组件缓存组件问题 和生命周期
Keep动态包裹组件时,会缓存不活动的组件实例,第一次加载这个组件的时候,会执行组件的所有周期函数,但是第二次打开的时候就不会触发这些生命周期钩子函数,但是会触发activated和deactivated这二个钩子函数
路由守卫
全局路由守卫有个两个:一个是全局前置守卫,一个是全局后置守卫
beforeEach和beforeRouterEnter afterEach:
和beforeEach不同的是afterEach不接收第三个参数 next 函数,也不会改变导航本身,一般beforeEach用的最多,afterEach用的少.
to是到哪个页面去 form是从哪个页面来 next就是回调就是可以往下走
微任务和宏任务
其实真正会造成微任务优先级大于宏任务,是因为script本来就是一个宏任务,所以会先执行script这个宏任务,这个宏任务执行完又添加了一个宏任务(计时器),一个微任务(promise),但是执行完一个宏任务,该执行当前所有的微任务,所以先输出2,执行完微任务,再去循环调用,在执行一个宏任务,也就是定时器,执行完,发现微任务为空,就暂停,等待任务的到来,这就是事件循环的原理eventloop。
setTimeout、Promise、Async/Await 的区别
事件循环中分为宏任务队列和微任务队列
其中setTimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行async函数表示函数里面可能会有异步方法,await后面跟一个表达式
async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行
promise有几种状态,什么时候会进入catch?
三个状态:
pending、fulfilled、reject
两个过程:
padding -> fulfilled、padding -> rejected当pending为rejectd时,会进入catch
事件流:分为捕获阶段、处于目标阶段、冒泡阶段三个阶段
1. 事件冒泡:从触发的对象开始,事件不断往上传递。
2. 事件捕获:从dom树一直向下传递事件直到捕获为止
先捕获再冒泡
VUE单页应用首屏加载速度优化方案
1. 使用CDN资源,减小服务器带宽压力
如果不想把第三方库打包到bundle中,这就有了externals。官方的使用externals比较简单,只需三步——
1.在HTML中引入第三方库的cdn
2.在webpack中配置externals
externals: {jquery: "jQuery",}
3.在js中引用const $ = require("jquery");$("#content").html("<h1>hello world</h1>");
好,现在我们可以随心所欲的使用jquery插件并保证不会打包到bundle中。
2. 路由懒加载 import方式
3. 将一些静态js css放到其他地方(如OSS),减小服务器压力
4. 按需加载三方资源,如iview,建议按需引入iview中的组件
5. 使用nginx开启gzip减小网络传输的流量大小
6. 若首屏为登录页,可以做成多入口,登录页单独分离为一个入口
7. 使用uglifyjs-webpack-plugin插件代替webpack自带UglifyJsPlugin插件
防止冒泡和捕获
w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true
取消默认事件
w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
父组件和子组件的生命周期顺序
执行的先后顺序为父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted。
然后我们通过控制台打印的结果进一步证实了这个顺序。
vue3.0中为何使用proxy?
vue的双向数据绑定是由数据劫持结合发布-订阅者模式实现的,利用Object.definePropery()来劫持对象属性的setter和getter操作,在数据变动时做你想做的事情。vue中使用v-model实现,主要原理是绑定了DOM对象的value属性,当它初次绑定的时候,就会触发getter,watcher就会触发, watcher通知Vue生成新的VDOM树。再通过render函数进行渲染,生成真实DOM。Object.defineProperty的缺点:(1)在Vue中,Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。(2)Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue里,是通过递归以及遍历data对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象,不管是对操作性还是性能都会有一个很大的提升。proxy的优点:
可以劫持整个对象,并返回一个新对象
有13种劫持操作。
Proxy是 ES6 中新增的一个特性,翻译过来意思是"代理",用在这里表示由它来“代理”某些操作。 Proxy 让我们能够以简洁易懂的方式控制外部对对象的访问。其功能非常类似于设计模式中的代理模式。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
使用 Proxy 的核心优点是可以交由它来处理一些非核心逻辑(如:读取或设置对象的某些属性前记录日志;设置对象的某些属性值前,需要验证;某些属性的访问控制等)。 从而可以让对象只需关注于核心逻辑,达到关注点分离,降低对象复杂度等目的。
nextTick源码分析
动态路由匹配
插槽内容
vue中key的作用和工作原理
如何解决vuex页面刷新数据丢失问题?
1、问题描述:
一般在登录成功的时候需要把用户信息,菜单信息放置vuex中,作为全局的共享数据。但是在页面刷新的时候vuex里的数据会重新初始化,导致数据丢失。因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被重新赋值。
2、解决思路:
办法一:将vuex中的数据直接保存到浏览器缓存中(sessionStorage、localStorage、cookie)
办法二:在页面刷新的时候再次请求远程数据,使之动态更新vuex数据
办法三:在父页面向后台请求远程数据,并且在页面刷新前将vuex的数据先保存至sessionStorage(以防请求数据量过大页面加载时拿不到返回的数据)
分析:
办法一的缺点是不安全,不适用大数据量的存储;
办法二适用于少量的数据,并且不会出现网络延迟;
办法三是办法二和办法一配合使用。
3、解决过程:
3.1、选择合适的浏览器存储
localStorage -- 是永久存储在本地,除非你主动去删除;
sessionStorage -- 是存储到当前页面关闭为止,和其他tab页没关联;
cookie -- 则根据你设置的有效时间来存储,但缺点是不能储存大数据且不易读取,会和后台进行交互。
本方法选择的是sessionStorage,选择的原因是由于vue是单页面应用,操作都是在一个页面跳转路由,另一个原因是sessionStorage可以保证打开页面时sessionStorage的数据为空,而如果是localStorage则会读取上一次打开页面的数据。
3.2、解决方案
由于state里的数据是响应式,所以sessionStorage存储也要跟随变化,而且只能通过mutations来改变state中的值。首先在用户登录成功之后,然后把用户信息,菜单信息通过actions分发保存至vuex中。然后在菜单页面计算vuex中state的菜单数据,将数据解析组装成前端组件所需的格式,然后渲染组件,生成菜单树。如果页面没有刷新,则一切正常。
登录成功后将用户和菜单数据同步至vuex
CSS 盒子模型(Box Model)
-
box-sizing: content-box
是W3C盒子模型 在标准的盒子模型中,width指content部分的宽度 -
box-sizing: border-box
是IE盒子模型 在IE盒子模型中,width表示content+padding+border这三个部分的宽度
------------恢复内容结束------------