Css:
1、盒子水平垂直居中的五大方案
1)定位+margin(需要子元素宽度)
.father {
width: 500px;
height: 400px;
border: 2px solid red;
position: relative;
}
.son {
width: 200px;
height: 200px;
background-color: aqua;
position: absolute;
top: 50%;
left: 50%;
margin-left: -100px;
margin-top: -100px;
}
2)定位+transform: translate(-50%, -50%)
.father {
width: 500px;
height: 400px;
border: 2px solid red;
position: relative;
}
.son {
width: 200px;
height: 200px;
background-color: aqua;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
3) 定位+top、bottom、left、right:0,margin:auto
.father {
position: relative;
width: 500px;
height: 400px;
border: 10px solid darkorange;
}
.son {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
4)flex
.father {
width: 500px;
height: 400px;
border: 2px solid red;
display: flex;
justify-content: center;
align-items: center;
}
.son {
width: 200px;
height: 200px;
background-color: aqua;
}
5)父:display: table-cell;vertical-align: center;text-align: center;
.father {
width: 500px;
height: 400px;
border: 2px solid red;
display: table-cell;
vertical-align: middle;
text-align: center;
}
.son {
display: inline-block;
width: 200px;
height: 200px;
background-color: aqua;
}
2、css盒模型
标准盒模型: width = content + padding + border +margin box-sizing: content-box
怪异盒模型: width = content + padding box-sizing: border-box
3、BFC详解
具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素, 并且 BFC 具有普通容器所没有的一些特性。
触发BFC:
body 根元素
浮动元素:float 除 none 以外的值
绝对定位元素:position (absolute、fixed)
display 为 inline-block、table-cells、flex
overflow 除了 visible 以外的值 (hidden、auto、scroll)
实践:
1)解决双margin合并问题,给一个div外部加上一个
,并设置wrapper:overflow: hidden;2)左边固定宽度,右边不设宽,因此右边的宽度自适应,随浏览器窗口大小的变化而变化。
4、flex:1
flex:1 => flex: 1 1 0
flex - flex-shrink flex-grow flex-basic
flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
flex-shrink 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
flex-basis给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为 auto, 即项目本身的大小
5、两边固定,中间自适应布局
1)flex
.container {
display: flex;
flex-direction: row;
width: 100%;
height: 500px;
}
.container .left {
width: 200px;
background-color: red;
}
.container .right {
width: 200px;
background-color: bisque;
}
.container .center {
flex: 1;
background-color: aqua;
}
<div class="container">
<div class="left"></div>
<div class="center"></div>
<div class="right"></div>
</div>
2)利用浮动实现,左边盒子左浮动,右边盒子右浮动,中间自适应
.container {
width: 100%;
height: 500px;
}
.container .left {
width: 200px;
height: 100%;
float: left;
background-color: red;
}
.container .right {
width: 200px;
height: 100%;
float: right;
background-color: bisque;
}
.container .center {
height: 100%;
background-color: aqua;
}
<div class="container">
<div class="left"></div>
<div class="right"></div>
<div class="center"></div>
</div>
3)使用相对定位
.container {
width: 100%;
height: 500px;
position: relative;
}
.container .left {
width: 200px;
height: 100%;
background-color: red;
position: absolute;
top: 0;
left: 0;
}
.container .right {
width: 200px;
height: 100%;
background-color: bisque;
position: absolute;
top: 0;
right: 0;
}
.container .center {
height: 100%;
background-color: aqua;
}
<div class="container">
<div class="left"></div>
<div class="right"></div>
<div class="center"></div>
</div>
4)圣杯布局
.container {
height: 500px;
padding-left: 200px;
padding-right: 200px;
overflow: hidden;
}
.container .col {
float: left;
position: relative;
}
.container .left {
width: 200px;
height: 500px;
background-color: red;
margin-left: -100%;
left: -200px;
}
.container .right {
width: 200px;
height: 100%;
background-color: bisque;
margin-left: -200px;
right: -200px;
}
.container .center {
width: 100%;
height: 100%;
background-color: aqua;
}
<div class="container">
<div class="col center"></div>
<div class="col left"></div>
<div class="col right"></div>
</div>
JS
1、数据类型
1) 基本数据类型:number, string, boolean, null, undefined,symbol存在栈中
引用数据类型:object, array 存在堆中,对象的地址(指针)存在堆中
2) 引用数据类型出了object和array之外,还有 Date, RepExp, function
3) null和undefined有什么区别?
相同点: ①、都是原始类型的值,都保存在栈中;②、进行条件判断时,都是false
不同点: ①、null是js的关键字,表示空值;undefined不是关键字,是一个全局变量
②、 null是object的一个特殊值,如果object为null,则表示这个对象不是一个有效对象,而undefined是未定义,是一个属性
2、如何判断基本数据类型和引用数据类型?如何判断对象、数组、Date、function、RepExp?
1) 判断基本数据类型还是引用数据类型?
typeof, 可以判断基本数据类型还是引用数据类型(typeof null === 'object',特殊)
2) instanceof 用来测试一个对象在原型链中是否存在构造函数的prototype属性(就是判断对象是否是某个数据类型的实例(如Array)),基本数据类型不能用instanceof判断
3)Object.prototype.toString.call(),万能方法,但不能判断某个person 是 Person的实例,判断是否是某个对象的实例,用instanceof
3、闭包
定于:
1)函数执行完毕,并且内部创建的东西被外部引用了,形成了不销毁的栈内存
2)在代码中引用了自由变量
自由变量:是指在函数中使用的变量即不是函数参数arguments
也不是函数内部声明的变量,那这个变量就是自由变量。
闭包有3个特性:
①函数嵌套函数
②函数内部可以引用函数外部的参数和变量
③参数和变量不会被垃圾回收机制回收
4、如何解决内存泄漏
在退出函数之前,将不使用的局部变量全部删除。可以使变量赋值为null
function foo () {
var name = 'foo'
var age = 20
function bar () {
console.log(name)
console.log(age)
}
return bar
} // 第二行至第八行为闭包函数 name 和 age 上升为自有变量
var fn = foo()
fn()
// 解决
fn = null
4、js如何实现链式调用
1) 定义一个对象
2)对象中定义一些方法(this.run, this.sing, this.stopSing),方法中return this
//创建一个bird类
function Bird(name) { this.name = name; this.run = function () {
document.write(name+" "+"start run;");
return this;// return this返回当前调用方法的对象。
}
this.stopRun = function () {
document.write(name+" "+"start run;");
return this;
}
this.sing = function () {
document.write(name+" "+"start sing;");
return this;
}
this.stopSing = function () {
document.write(name+" "+"start stopSing;");
return this;
}
}
var bird=new Bird("测试");
bird.run().sing().stopSing().stopRun();//结果为;测试 start run;测试 start sing;测试 start stopSing;测试 start run;
6、this指向
1)一般函数,在全局作用域中,指向window;严格模式下,this指向undefined
2)对象的方法里调用,指向调用该方法的对象
3)构造函数中的this指向该构造函数的实例
4)箭头函数的this,指向箭头函数定义时所处环境的this,而非箭头函数执行时的所在对象,默认使用父级的this指向
5)事件绑定中的this,this指向事件源;事件监听和事件绑定基本一致
事件源.onclik = function(){ } //this -> 事件源事件源.addEventListener(function(){ }) //this->事件源
6)行内绑定
<input type="button" value="按钮" onclick="clickFun()">
<script>
function clickFun(){
this // 此函数的运行环境在全局window对象下,因此this指向window;
}
</script>
7)定时器中的this指向看具体场景
例如:
var obj = {
fun:function(){
this ;
}
}
setInterval(obj.fun,1000); // this指向window对象
setInterval('obj.fun()',1000); // this指向obj对象
7、原型、原型链
8、new的原理
在调用new的过程中都干了什么?
1)在内存中创建一个新对象
2)新对象内部的prototype特性被赋值为构造函数的prototype属性
3)构造函数内部的this被赋值为这个新对象(即this指向新对象)
4)执行构造函数内部的代码(即给新对象添加属性)
5)如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象
function myNew() {
// 创建第一个对象
var newObj = new Object()
// 取出第一个参数,获得构造函数
var constructor = Array.prototype.shift.call(arguments)
// 连接原型,新对象可以访问原型中的属性
newObj.__proto__ = constructor.prototype
// 执行构造函数,绑定this,并为新对象添加属性
var result = constructor.apply(newObj, arguments)
return typeof result === 'object' ? result : newObj;
}
9、call、apply、bind原理
function myCall(context) {
context = context || window
context.fn = this
var args = arguments.slice(1)
var result = context.fn(...args)
delete context.fn
return result
}
function myApply(context, arr) {
context = context || window
context.fn = this
var result = arr ? context.fn(...arr) : context.fn()
delete context.fn
return result
}
function myBind(obj) {
if (typeof this !== 'function') {
throw new TypeError('错误')
}
const self = this // 拿到执行bind方法的函数
const arr = Array.prototype.slice.call(arguments, 1)
var o = function() {} // 使用o做一个中间过渡
newF = function() { // bind返回一个函数,另外可以new
// bind有个函数柯里化的功能,将参数收集起来,按顺序排列好
var arr2 = Array.prototype.slice.call(arguments)
arrSum = arr.concat(arr2)
if (this.instanceof o) { // 判断调用函数是否是o的原型链上的
that.apply(this, arrSum)
} else {
that.apply(obj, arrSum)
}
}
o.prototype = that.prototype // o的原型指向调用函数的原型
newF.prototype = new o
return newF
}
10、var const let对比
- let 声明的变量具有块作用域的特征。
- 在同一个块级作用域,不能重复声明变量。
- let 声明的变量不存在变量提升,换一种说法,就是 let 声明存在暂时性死区(TDZ)。 - const 定义的变量,一旦定义后,就不能修改,即 const 声明的为常量。
11、事件循环
事件队列:
- 一个线程中,事件循环是唯一的,但是任务队列可以有多个;
- 任务队列又分macro-task(宏任务)和micro-task(微任务);
- macro-task包括:script(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI rendering;
- micro-task包括:process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)
- setTimeout/Promise等称为任务源,而进入任务队列的是他们制定的具体执行任务;来自不同任务源的任务会进入到不同的任务队列,其中setTimeout与setInterval是同源的;
宏任务可以理解成每次执行栈执行的代码就是一个宏任务。
事件循环运行机制
(1)执行一个宏任务(栈中没有就从事件队列中获取)
(2)执行过程中如果遇到微任务,就将它添加到微任务的任务队列中;
(3)宏任务执行完毕后,立即执行当前微任务队列的所有微任务;
(4)当前微任务执行完毕,开始检查渲染,然后GUI线程接管渲染;
(5)渲染完毕后,JS线程继续接管,开始下一个宏任务。
Node的事件循环
后续补上。。。
12、继承的实现
13、cookie和localStorage和sesstionStorage的区别
①传递方式不同
cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。
sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。
②数据大小不同
cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。
存储大小限制也不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。
sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。
③数据有效期不同
sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;
localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;
cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。
④作用域不同
sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;
localStorage 在所有同源窗口中都是共享的;
cookie也是在所有同源窗口中都是共享的。
14、事件捕获、事件冒泡
事件触发顺序是由内到外的,这就是事件冒泡。如果点击子元素不想触发父元素的事件,可使用event.stopPropagation();
事件捕获过程与事件的冒泡阶段是一个相反的阶段,即事件由祖先元素向子元素传播,和一个石子儿从水面向水底下沉一样,要说明的是在 IE,opera浏览器中,是不存在这个阶段的
15、promise实现
function reslovePromise(promise2, x, resolve, reject) {
// 判断x是不是promise
// 规范里规定了一段代码,这个代码可以实现我们的promise
// 和别人的promise进行交互
if(promise2 === x) {
return reject(new TypeError('循环引用'))
}
if (x !== null && (typeof x === 'function' || typeof x === 'object')) {
try{
let then = x.then
if (typeof then === 'function') { // 如果then是一个函数,就认为它是promise
// call的第一个参数是this,后面的是成功的回调和失败的回调
then.call(x, y => { // 如果y是promise,继续递归解析promise
reslovePromise(promise2, y, resolve, reject)
}, err => {
reject(err)
})
}
}catch(e){
//TODO handle the exception
}
} else {
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = 'pending'
this.value = undefined
this.reason = undefined
this.onResolvedCallBacks = []
this.onRejectedCallBacks = []
let resolve = (data) => {
if(this.status === 'pending') {
this.status = 'fulfilld'
this.value = data
this.onResolvedCallBacks.forEach(fn => fn())
}
}
let reject = (err) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.reason = err
this.onRejectedCallBacks.forEach(fn => fn())
}
}
// 执行时可能会发生异常
try{
executor(this.res)
}catch(e){
reject(e) // 失败
}
}
then(onFulfilled, onRejected) {
let promise2;
if(this.status === 'resolved') {
promise2 = new Promise((resolve, reject) => {
let x = onFulfilled(this.value)
// 看x是不是promise,如果是promise 取他的结果
// 如果返回的是一个普通值,作为promise2的结果返回
// reslovePromise 可以解析promise2 和 x 之间的关系
reslovePromise(promise2, x, resolve, reject)
})
}
if(this.status === 'rejected') {
promise2 = new Promise((resolve, reject) => {
let x = onRejected(this.reason)
reslovePromise(promise2, x, resolve, reject)
})
}
if(this.status === 'pending') {
promise2 = new Promise((resolve, reject) => {
this.onResolvedCallBacks.push(() => {
let x = onFulfilled(this.value)
reslovePromise(promise2, x, resolve, reject)
})
this.onRejectedCallBacks.push(() => {
let x = onRejected(this.value)
reslovePromise(promise2, x, resolve, reject)
})
})
}
return promise2
}
}
16、promise.all实现
myAll = function(promises) {
let results = []
let promiseCount = 0
let length = promises.length
return new Promise((resolve, reject) => {
for(let i = 0 ; i < promises.length; i++) {
Promise.resolve(promises[i]).then(res => {
results[i] = res
promiseCount++
if(promiseCount === length) {
resolve(results)
}
}, err => {
reject(err)
})
}
})
}
17、深拷贝和浅拷贝
浅拷贝:只复制对象的指针,不复制对象本身,新旧对象共享一块内存
深拷贝:复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变
浅拷贝实现:
Object.assign()
注意:
- 当对象只有一级属性为深拷贝;
- 当对象中有多级属性时,二级属性后就是浅拷贝;
深拷贝实现:
1)使用递归方式,逐个属性进行拷贝
2)利用JSON.parse(JSON.strigfy(obj)),但此方法对对象中的function无法拷贝
3)jquery中的extend方法
let a = [0, 1, [2, 3], 4]
let b = $.extend(true, [], a)
a[0] = 1
a[2][0] = 1 // [1,1,[1,3],4]
console.log(b) // [0,1,[2,3],4]
Vue
1、v-if和v-for哪个优先级高,为什么一般不一起使用?
v-for优先级高,源码中的判断是先判断了for循环,再判断if。若一起用,会先执行for循环,在判断if
2、vue中data为什么是函数
因为如果是对象的话,会导致它们共用一个data对象,那么状态将会影响所有的实例; 采用函数形式定义,在initData时会将其作为工厂函数返回全新的data对象,规避了多实例之间状态污染问题 根实例只有一个,不需要担心这种情况
3、vue中set的原理
对于数组来说,调用重写的splice方法进行修改,可以更新视图
对于对象来说,调用defineReactive方法,变成响应式,然后调用dep.notify(),主动更新视图
4、key的作用:
为了高效更新虚拟dom,原理是vue在patch过程中,可以通过key来判断两个节点是否是同一个,避免更新 不同元素,减少dom操作量
5、diff
1) diff算法:通过新旧虚拟dom做对比,将变化的地方更新在真实dom上
2) vue2为了降低watcher粒度,每个组件只有一个watcher与之对应($mounte-》new watcher()),只有 引入diff算法才能精确找到发生变化的地方
3) vue中diff执行的时刻是组件实例执行其更新函数时
4) diff遵循深度优先、同级比较的策略 -》 比较方法有4种 ,如果没有找到相同节点 -》 遍历查找,最后处 理剩下的节点
6、Vue组件的渲染流程
1)在渲染父组件时会创建父组件的虚拟节点,其中可能包括子组件的标签
2)在创建虚拟节点时,获取组件的定义使用Vue.extend生成组件的构造函数
3)将虚拟节点转化为真实节点时,会创建组件的实例,并调用组件的$mount方法。
4)所以组件的创建过程是先父后子
7、mvc、mvp、mvvm
1) 这三者都是为了解决model和view的耦合问题
2)mvc,优点:分层清晰;缺点:数据流混乱
3)mvp,presenter作为中间层负责mv通信,解决了两者的耦合问题,但p层过于臃肿会导致维护问题
4)mvvm,不仅解决了耦合问题,还解决了维护两次映射关系的大量繁杂代码和dom操作代码,提高 开发效率、可读性的同时,还保持了优越的性能表现
8、vue组件间的通信
1)props
2)vuex
3)bus.on
4) provide inject
9、vue query和params传参的区别
1) query传参,参数会拼接到页面路径(get),页面跳转后刷新也可正常取值,路由跳转路径需要用path;
2) params传参,参数不会拼接到页面路径(post),页面跳转后刷新值不存在,路由跳转路径需要用name;
10、watch和computed的区别
watch是监控无缓存,computed是计算且有缓存;不能使用箭头函数,箭头函数没有this,会继承它的 父级(window)
11、vue生命周期
1)beforeCreate: 实例创建出来之前,会执行它
2)created: 可以访问data和methods
3)beforeMount:模板已经在内存中完成,但是尚未把模板渲染到页面中
4)mounted:模板已经挂在完成
5)beforeUpdate:数据已经更新了,但是未更新视图
6)updated:视图更新完毕
7)beforeActive
8)activited:这两个是配合keepAlive使用的,可以进行页面缓存
9)beforeDestory:销毁前
10)destoryed: 销毁后
12、nextTick的原理:
1)vue用异步队列的方式来控制dom更新和nexttick回调先后执行
2)microtask因为其高优先级特性,能确保队列中的微任务在一次时间循环前被执行完毕 3)因为兼容性问题,vue不得不做了microtask想macrotask的降级方案(即用settimeout兜底)
13、vue生命周期,父子组件生命周期,路由生命周期
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
父beforeUpdate->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
14、v-if和v-show的区别
-
**v-if:**通过对Dom元素进行添加和删除,来完成显示和隐藏!
-
**v-show:**通过对display属性值进行改变来完成显示和隐藏;不会对Dom元素进行添加和删除!
15、vue双向绑定原理
16、vue2和vue3的区别
proxy dep.notify( watch update() ) dep.addsub() watcher类 进行数据更新-》vnode的更新
17、vue-router原理
1)hash
hash("#") 的作用是加载 URL 中指示网页中的位置。# 本身以及它后面的字符称之为 hash,可通过 window.location.hash 获取
hashChange()方法,监听hash的改变,每次改变hash,都会在浏览器历史中增加一个记录
HashHistory.push() 将新路由添加到浏览器访问历史的栈顶
HashHistory.replace(),replace()方法与push()方法不同之处在于,它并不是将新路由添加到浏览器访问历史的栈顶,而是替换掉当前的路由
2) history
History interface 是浏览器历史记录栈提供的接口,通过back()、forward()、go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。
window.history.pushState() 与hash模式类似,只是将window.hash改为history.pushState
window.history.replaceState() 与hash模式类似,只是将window.replace改为history.replaceState
popState()方法监听
18、vue常用的修饰符
.lazy:输入框改变,这个数据就会改变,lazy这个修饰符会在光标离开input框才会更新数据:
.trim:输入框过滤首尾的空格:
.stop:等同于 JavaScript 中的 event.stopPropagation() ,防止事件冒泡;
.prevent :阻止默认行为,相当于调用了event.preventDefault()方法,比如表单的提交、a标签的跳转就是默认事件;
.capture :与事件冒泡的方向相反,事件捕获由外到内;
.self :只会触发自己范围内的事件,不包含子元素;
.once :只会触发一次。
.sync:对prop进行双向绑定
19、为什么说Vue是渐进式框架
在声明式渲染(视图模板引擎)的基础上,我们可以通过添加组件系统、客户端路由、大规模状态管理来构建一个完整的框架。更重要的是,这些功能相互独立,你可以在核心功能的基础上任意选用其他的部件,不一定要全部整合在一起。可以看到,所说的“渐进式”,其实就是Vue的使用方式,同时也体现了Vue的设计的理念
20、Vue2和Vue3的区别
1)生命周期:
整体变化不大,除beforeCreate和created由setup替代之外,其他前面加上了‘on’
2)多根节点
vue2只能单根节点,vue3可以多根节点
3)Composition Api
Vue2 是选项API(Options API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期钩子等),导致代码的可读性变差。当需要修改某个逻辑时,需要上下来回跳转文件位置。
Vue3 组合式API(Composition API)则很好地解决了这个问题,可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。
4)响应式原理:
Vue2 响应式原理基础是 Object.defineProperty;Vue3 响应式原理基础是 Proxy。
5)虚拟dom
Vue3 相比于 Vue2,虚拟DOM上增加 patchFlag 字段。
// patchFlags 字段类型列举
export const enum PatchFlags {
TEXT = 1, // 动态文本内容
CLASS = 1 << 1, // 动态类名
STYLE = 1 << 2, // 动态样式
PROPS = 1 << 3, // 动态属性,不包含类名和样式
FULL_PROPS = 1 << 4, // 具有动态 key 属性,当 key 改变,需要进行完整的 diff 比较
HYDRATE_EVENTS = 1 << 5, // 带有监听事件的节点
STABLE_FRAGMENT = 1 << 6, // 不会改变子节点顺序的 fragment
KEYED_FRAGMENT = 1 << 7, // 带有 key 属性的 fragment 或部分子节点
UNKEYED_FRAGMENT = 1 << 8, // 子节点没有 key 的fragment
NEED_PATCH = 1 << 9, // 只会进行非 props 的比较
DYNAMIC_SLOTS = 1 << 10, // 动态的插槽
HOISTED = -1, // 静态节点,diff阶段忽略其子节点
BAIL = -2 // 代表 diff 应该结束
}
6)diff算法
patchFlag 帮助 diff 时区分静态节点,以及不同类型的动态节点。一定程度地减少节点本身及其属性的比对。