自己想换工作,所以准备看看面试题,边学习边记录下来,也方便自己查看。
比较初级哦,因为自己也不是很厉害~但会加油哒
从2022年7月24开始记录哒
到2022年8月12日停止记录(有觉得好的会持续更新)
1.margin和padding的区别?
它们的作用对象不同,padding针对于自身的,margin作用于外部对象的。
2.vw和百分比有什么区别?
百分比有继承关系,继承至父级;vw只和设备的宽度有关系;
3.行内与块级元素
行内元素:不会自动换行,不可以设置宽高,由内容决定,对margin设置左右方向有效,而上下无效;
padding设置左右方向有效,而上下无效;
块级元素:默认占满宽度,宽度有继承关系,可以自动换行,设置margin和padding都有效;
4.如何让谷歌浏览器支持小字体
//利用缩放来让字体变小,字体为12px的时候就小不了了。
.small-font{
transform:scale(0.5);
-webkit-transform:scale(0.5);
}
5. flex:1代表什么?
flex:1 === flex: 1 1 0;
第一个参数表示:flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
第二个参数表示:flex-shrink 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
第三个参数表示:flex-basis 给上面两个属性分配多余空间之前,计算项目是否有多余空间。默认值为auto,即项目本身大小。
1.let与var
var a = 100;
function test() {
a = 10;
console.log(a);
console.log(this.a);
var a;
console.log(a);
}
test()
// 10 100 10
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
}, 0)
}
// 5 5 5 5 5
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
}, 0)
}
// 0 1 2 3 4
2.深拷贝与浅拷贝
浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,考本的就是基本类型的值,如果属性是应用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟了一个新的区域存放新对象,且修改新对象不会影响原对象。
数组与对象赋值是浅拷贝;一维的数组和对象可以看做是深拷贝;二维的是浅拷贝;方法不能拷贝;
浅拷贝的实现方式:
1.展开运算符…
2.Object.assign()
let obj1 = {
person: {
name: "不完美女孩"
},
age: '14'
};
let obj2 = Object.assign({}, obj1);
obj2.person.name = "哈哈哈";
obj2.age = '23'
console.log(obj1); // { person: { name: '哈哈哈'}, age: '14' }
3.Array.prototype.concat()
let arr = [1, 3, {
name: '不完美女孩'
}];
let arr2 = arr.concat();
arr2[2].name = '哈哈哈哈';
console.log(arr); //[ 1, 3, { name: '哈哈哈哈' } ]
4.Array.prototype.slice()
let arr = [1, 3, {
name: '不完美女孩'
}];
let arr2 = arr.slice();
arr2[2].name = '哈哈哈哈';
console.log(arr); //[ 1, 3, { name: '哈哈哈哈' } ]
标准的深拷贝应该怎么写?
1.JSON.parse(JSON.stringify())
2.function dee(socur) {
const targetObj = socur.constructor === Array ? [] : {};
for (let keys in socur) {
if (socur.hasOwnProperty(keys)) {
// 应用数据类型
if (socur[keys] && typeof socur[keys] === 'object') {
targetObj[keys] = socur[keys].constructor === Array ? [] : {};
// 递归
targetObj[keys] = dee(socur[keys])
} else {
// 基础数据类型直接赋值
targetObj[keys] = socur[keys]
}
}
}
return targetObj
}
let objc = {
a: '嗯嗯',
b: 1,
c: [1, 2, 3],
d: {
arr: '10',
age: '78'
}
}
let newobjc = dee(objc)
newobjc.a = '哦哦'
newobjc.c.push(5)
newobjc.d.arr = '修改'
console.log(objc, newobjc)
3.promise
为什么会出现promise?
JavaScript是单线程语言,在执行任务的时候,会先执行第一个任务再执行第二个任务,假如第一个任务是个耗时的任务那么第二个任务就一直阻塞不能执行,那么这个时候就需要异步来处理这个操作;
同步:主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务;
异步:不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程;
最常用的异步操作就是将回调函数作为异步回调的参数来使用,而promise是通过then来做的;
new promise 的时候就会被立即执行,因而为了实现用的时候调用所以将promise写在函数里面;
面试题练习感觉这个很不错,可以看一看。
// 面试题
Promise.resolve().then(()=>{
console.log('Promise1');
setTimeout(()=>{
console.log('setTimeout2')
},0)
})
setTimeout(()=>{
console.log('setTimeout1')
Promise.resolve().then(()=>{
console.log('Promise2')
})
})
// Promise1 setTimeout1 Promise2 setTimeout2
4.原型与原型链
(1) 原型 prototype
常规的数组 [ ] 和对象 { } 是没有原型的,原型是函数function特有的;
每一个函数都会有prototype属性,被称为显式原型;
每一个实例对象都会有__proto__属性,其被称为隐式原型;
(2) 原型链 proto => [[prototype]]
原型链是大家都有的。
获取对象时,如果这个对象身上本身没有这个属性时,它就会去他的原型__proto__上去找,如果还找不到,就去原型的原型上去找…一直找到最顶层(Object.prototype)为止,Object.prototype对象也有__proto__属性值为null。
每一个prototype原型上都会有一个constructor属性,指向它关联的构造函数。
找私有属性用:hasOwnProperty
function Person() {
}
var person = new Person();
person.name = 'Kevin';
console.log(person.name) // Kevin
// prototype
function Person() {
}
Person.prototype.name = 'Kevin';
var person1 = new Person();
var person2 = new Person();
console.log(person1.name) // Kevin
console.log(person2.name) // Kevin
// __proto__
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype) // true
//constructor
function Person() {
}
console.log(Person === Person.prototype.constructor) // true
//综上所述
function Person() {
}
var person = new Person()
console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
//顺便学习一下ES5得方法,可以获得对象得原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
//实例与原型
function Person() {
}
Person.prototype.name = 'Kevin';
var person = new Person();
person.name = 'Daisy';
console.log(person.name) // Daisy
delete person.name;
console.log(person.name) // Kevin
//原型得原型
var obj = new Object();
obj.name = 'Kevin',
console.log(obj.name) //Kevin
//原型链
console.log(Object.prototype.__proto__ === null) //true
// null 表示"没用对象" 即该处不应该有值
// 补充
function Person() {
}
var person = new Person()
console.log(person.constructor === Person) // true
//当获取person.constructor时,其实person中并没有constructor属性,当不能读取到constructor属性时,会从person的原型
//也就是Person.prototype中读取时,正好原型中有该属性,所以
person.constructor === Person.prototype.constructor
//__proto__
//其次是__proto__,绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于Person.prototype中,实际上,它
// 是来自与Object.prototype,与其说是一个属性,不如说是一个getter/setter,当使用obj.__proto__时,可以理解成返回了
// Object.getPrototypeOf(obj)
总结:
1、当一个对象查找属性和方法时会从自身查找,如果查找不到则会通过__proto__指向被实例化的构造函数的prototype
2、隐式原型也是一个对象,是指向我们构造函数的原型
3、除了最顶层的Object对象没有__proto_,其他所有的对象都有__proto__,这是隐式原型
4、隐式原型__proto__的作用是让对象通过它来一直往上查找属性或方法,直到找到最顶层的Object的__proto__属性,它的值是null,这个查找的过程就是原型链
5.防抖与节流
函数防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
应用场景:搜索框搜索输入。只需用户最后一次输入完,再发送请求。
手机号、邮箱验证输入检测。
窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
function debounce(fn, delay) {
let timer;
return function () {
let args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(()=> {
fn.apply(this, args);
}, delay);
};
}
函数节流:每隔一段时间,只执行一次函数。
应用场景:滚动加载,加载更多或滚到底部监听。
谷歌搜索框,搜索联想功能。
高频点击提交,表单重复提交。
function throttle(fn, delay) {
let timer;
return function () {
let _this = this;
let args = arguments;
if (timer) {
return;
}
timer = setTimeout(function () {
fn.apply(_this, args);
timer = null; // 在delay后执行完fn之后清空timer,此时timer为假,throttle触发可以进入计时器
}, delay)
}
}
6.闭包
闭包是什么? 方法里面返回方法
闭包存在的意义是什么?(1) 延长变量的生命周期;(2) 创建私有环境;
7.箭头函数和普通函数有什么区别?
(1). 箭头函数更加简洁;
(2). 箭头函数不会创建自己的this,所以它没有自己的this,它只会在自己作用域的上一层继承this,所以箭头函数中的this指向在它定义时就确认了,之后不会再改变,所以箭头函数的this值永远不会改变;
(3). call()、apply()、bind()等方法不能改变箭头函数this的的指向;
(4). 箭头函数不能作为构造函数使用;
(5). 箭头函数没有自己的arguments;
(6). 箭头函数没有prototype(原型),原型是undefined;
8.get请求传参长度的误区
HTTP协议从未规定GET/POST的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。
(1) HTTP协议未规定GET和POST的长度限制
(2) GET的最大长度显示是因为浏览器和web服务器限制了URL的长度
(3) 不同的浏览器和WEB服务器,限制的最大长度不一样
(4) 要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度为8182byte
9.get和post请求在缓存方面的区别
get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
post不同,post一般做的是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适用于请求缓存。
10.DOM操作与BOM操作
(1) DOM操作
例如:document.getElementById 就是dom操作
DOM事件模型和事件流
DOM事件模型分为捕获和冒泡。一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。
(1)捕获阶段:事件从window对象自上而下向目标节点传播的阶段;
(2)目标阶段:真正的目标节点正在处理事件的阶段;
(3)冒泡阶段:事件从目标节点自下而上向window对象传播的阶段。
如何阻止冒泡?
通过 event.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行。
事件代理(事件委托)
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理。
事件代理优点:
使代码简洁;减少浏览器的内存占用;
(2) BOM操作
BOM(浏览器对象模型)是浏览器本身的一些信息的设置和获取,例如获取浏览器的宽度、高度,设置让浏览器跳转到哪个地址。
例如:window.screen对象:包含有关用户屏幕的信息
window.location对象:用于获得当前页面的地址(URL),并把浏览器重定向到新的页面
window.history对象:浏览历史的前进后退等
window.navigator对象:常常用来获取浏览器信息、是否移动端访问等等
11.跨域
什么是跨域?
当协议、子域名、主域名、端口号中任意一个不相同时都算做不同域,不同域之间相互请求资源,就算作“跨域”。
常见的几种跨域解决方案(https://github.com/ljianshu/Blog/issues/55):
JSONP:利用同源策略对 script 标签不受限制,不过只支持GET请求
CORS:实现 CORS 通信的关键是后端,服务端设置 Access-Control-Allow-Origin 就可以开启,备受推崇的跨域解决方案,比 JSONP 简单许多
Node中间件代理或nginx反向代理:主要是通过同源策略对服务器不加限制
vue-cli代理跨域:devServer
12.New操作符做了什么事情?
(1) 创建了一个新对象;
(2) 将对象与构造函数的原型链连接起来;
(3) 将构造函数中的this绑定到新建的对象上;
(4) 根据构造函数返回类型做判断,如果是值类型,返回创建的对象;如果是引用类型,返回这个引用类型的对象;
13.说一下eventloop
event loop是一个执行模型,在不同的地方有不同的实现。
宏队列和微队列
宏队列,macrotask,也叫tasks。例如:
微队列,microtask,也叫jobs。例如
微任务 > DOM渲染 > 宏任务
14.Set 和 Map有什么区别?
(1) Map是键值对,Set是值得合集,当然键和值可以是任何的值;
(2) Map可以通过get方法获取,而set不能因为它只有值;
(3) 都能通过迭代器进行for…of遍历;
(4) Set的值是唯一的可以做数组去重,而Map由于没有格式限制,可以做数据存储;
Map与对象的互换
const obj = {}
const map = new Map([
['a', 2],
['b', 3]
])
for (let [key, value] of map) {
obj[key] = value
}
console.log(obj)
// {a: 2, b: 3}
15.Loader和Plugin 有什么区别
Loader:直译为“加载器”。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到‘loader’。所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。Plugin:直译为“插件”。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。在Webpack运行的生命周期中会广播出许多事件,Plugin可以 监听这些事件,在合适的时机通过Webpack提供的API改变输出结果。
16.在地址栏里输入一个地址回车会发生那些事情?
(1) 解析URL:首先会对URL进行解析,分析所需要使用的传输协议和请求的资源路径。如果输入的URL中的协议或者主机名不合法,将会把地址栏中输入的内容传递给搜索引擎。如果没有问题,浏览器会检查URL中是否出现了非法字符,如果存在非法字符,则对非法字符进行转义后再进行下一过程。
(2) 缓存判断:浏览器会判断所请求的资源是否在缓存里,如果请求的资源在缓存里并且没有失效,那么就直接使用,否则向服务器发起新的请求。
(3) DNS解析:下一步首先需要获取的是输入的URL中的域名的IP地址,首先判断本地是否有该域名的IP地址缓存,如果有则使用,如果没有则向本地DNS服务器发起请求。本地DNS服务器也会检查是否存在缓存,如果没有就会向根域名服务器发起请求,获得负责的顶级域名服务器的地址后,再向顶级域名服务器请求,然后获得负责的权威域名服务器的地址后,再向权威域名服务器发起请求,最终获得域名的IP地址后,本地DNS再将这个IP地址返回给请求的用户。用户向本地DNS服务器发起请求属于递归请求,本地DNS服务器向各级域名服务器发起请求属于迭代请求。
(4) 获取MAC地址:当浏览器得到IP地址后,数据传输还需要知道目的主机MAC地址,因为应用层下发数据给传输层,TCP协议会指定源端口号和目的端口号,然后发给网络层。网络层会将本地地址作为源地址,获取的IP地址作为目的地址。然后将下发给数据链路层,数据链路层的发送需要加入通信双方的MAC地址,本机的MAC地址作为源MAC地址,目的MAC地址需要分情况处理。通过将IP地址与本机的子网掩码相与,可以判断是否与主机在同一个子网里,如果在同一个子网里,可以使用APR协议获取到目的地址MAC地址,如果不在一个子网里,那么请求应该转发给网关,由它代为转发,此时同样可以通过APR协议来获取网关的MAC地址,此时目的主机的MAC地址应该为网关的地址。
(5) TCP三次握手:下面是TCP建立连接的三次握手的过程,首先客户端向服务器发送一个SYN连接请求报文段和一个随机序号,服务端接收到请求后向客户端发送一个SYN ACK报文段,确认连接请求,并且也向客户端发送一个随机序号。客户端接收服务器的确认应答后,进入连接建立的状态,同时向服务器也发送一个ACK确认报文段,服务器端接收到确认后,也进入连接建立状态,此时双方的连接就建立起来了。
(6) HTTPS握手: 如果使用的是 HTTPS 协议, 在通信前还存在 TLS 的一个四次握手的过程。 首先由客户端向服务器端发送使用的协议的版本号、 一个随机数和可以使用的加密方法。 服务器端收到后, 确认加密的方法, 也向客户端发送一个随机数和自己的数字证书。 客户端收到后, 首先检查数字证书是否有效, 如果有效, 则再生成一个随机数, 并使用证书中的公钥对随机数加密, 然后发送给服务器端, 并且还会提供一个前面所有内容的 hash 值供服务器端检验。 服务器端接收后, 使用自己的私钥对数据解密, 同时向客户端发送一个前面所有内容的 hash 值供客户端检验。 这个时候双方都有了三个随机数, 按照之前所约定的加密方法, 使用这三个随机数生成一把秘钥, 以后双方通信前, 就使用这个秘钥对数据进行加密后再传输。
(7) 返回数据: 当页面请求发送到服务器端后, 服务器端会返回一个 html 文件作为响应, 浏览器接收到响应后, 开始对 html 文件进行解析, 开始页面的渲染过程。
(8) 页面渲染: 浏览器首先会根据 html 文件构建 DOM 树, 根据解析到的 css 文件构建 CSSOM 树, 如果遇到 script 标签, 则判端是否含有 defer 或者 async 属性, 要不然 script 的加载和执行会造成页面的渲染的阻塞。 当 DOM 树和 CSSOM 树建立好后, 根据它们来构建渲染树。 渲染树构建好后, 会根据渲染树来进行布局。 布局完成后, 最后使用浏览器的 UI 接口对页面进行绘制。 这个时候整个页面就显示出来了。
(9) TCP四次挥手: 最后一步是 TCP 断开连接的四次挥手过程。 若客户端认为数据发送完成, 则它需要向服务端发送连接释放请求。 服务端收到连接释放请求后, 会告诉应用层要释放 TCP 链接。 然后会发送 ACK 包, 并进入 CLOSE_WAIT 状态, 此时表明客户端到服务端的连接已经释放, 不再接收客户端发的数据了。 但是因为 TCP 连接是双向的, 所以服务端仍旧可以发送数据给客户端。 服务端如果此时还有没发完的数据会继续发送, 完毕后会向客户端发送连接释放请求, 然后服务端便进入 LAST
- ACK 状态。 客户端收到释放请求后, 向服务端发送确认应答, 此时客户端进入 TIME -
WAIT 状态。 该状态会持续 2 MSL( 最大段生存期, 指报文段在网络中生存的时间, 超时会被抛弃) 时间, 若该时间段内没有服务端的重发请求的话, 就进入 CLOSED 状态。 当服务端收到确认应答后, 也便进入 CLOSED 状态。
17.UDP和TCP有什么区别

18.哪些情况会导致内存泄漏
(1) 意外的全局变量:由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收
(2) 被遗忘的计时器或回调函数:设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
(3) 脱离 DOM 的引用:获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
(4) 闭包:不合理的使用闭包,从而导致某些变量一直被留在内存当中。
19.说一下常见的检测数据类型的几种方式?
(1) typeof 其中数组、对象、null都会被判断为Object,其他判断都正确
(2) instanceof 只能判断引用数据类型,不能判断基本数据类型
(3) constructor 它有2个作用 一是判断数据的类型,二是对象实例通过constructor对象访问它的构造函数。需要注意的事情是如果创建一个对象来改变它的原型,constructor就不能来判断数据类型了
(4) Object.prototype.toString.call()
instanceof和typeof的区别:
instanceof:
返回值为布尔值。
instanceof 用于判断一个变量是否属于某个对象的实例。
typeof:
返回值是一个字符串, 用来说明变量的数据类型。
typeof 一般只能返回如下几个结果: number, boolean, string, function, object, undefined。
20.说一下怎么把类数组转换为数组?
//通过call调用数组的slice方法来实现转换
Array.prototype.slice.call(arrayLike)
//通过call调用数组的splice方法来实现转换
Array.prototype.splice.call(arrayLike,0)
//通过apply调用数组的concat方法来实现转换
Array.prototype.concat.apply([],arrayLike)
//通过Array.from方法来实现转换
Array.from(arrayLike)
21.说一下数组如何去重,你有几种方法?
let arr = [1,1,"1","1",true,true,"true",{},{},"{}",null,null,undefined,undefined]
// 方法1
let uniqueOne = Array.from(new Set(arr)) console.log(uniqueOne)
// 方法2
let uniqueTwo = arr => {
let map = new Map(); //或者用空对象 let obj = {} 利用对象属性不能重复得特性
let brr = []
arr.forEach( item => {
if(!map.has(item)) { //如果是对象得话就判断 !obj[item]
map.set(item,true) //如果是对象得话就obj[item] =true 其他一样
brr.push(item)
}
})
return brr
}
console.log(uniqueTwo(arr))
//方法3
let uniqueThree = arr => {
let brr = []
arr.forEach(item => {
// 使用indexOf 返回数组是否包含某个值 没有就返回-1 有就返回下标
if(brr.indexOf(item) === -1) brr.push(item)
// 或者使用includes 返回数组是否包含某个值 没有就返回false 有就返回true
if(!brr.includes(item)) brr.push(item)
})
return brr
}
console.log(uniqueThree(arr))
//方法4
let uniqueFour = arr => {
// 使用 filter 返回符合条件的集合
let brr = arr.filter((item,index) => {
return arr.indexOf(item) === index
})
return brr
}
console.log(uniqueFour(arr))
22.说一下JSON.stringify有什么缺点?
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;
23.说一下for…in 和 for…of的区别?
for…of遍历获取的是对象的键值, for…in获取的是对象的键名;
for…in会遍历对象的整个原型链, 性能非常差不推荐使用,而for…of只遍历当前对象不会遍历原型链;
对于数组的遍历,for…in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of只返回数组的下标对应的属性值;
总结:for…in循环主要是为了遍历对象而生,不适用遍历数组; for…of循环可以用来遍历数组、类数组对象、字符串、Set、Map以及Generator对象
24.说一下HTTP和HTTPS协议的区别?
1、HTTPS协议需要CA证书,费用较高;而HTTP协议不需要
2、HTTP协议是超文本传输协议,信息是明文传输的,HTTPS则是具有安全性的SSL加密传输协议;
3、使用不同的连接方式,端口也不同,HTTP协议端口是80,HTTPS协议端口是443;
4、HTTP协议连接很简单,是无状态的;HTTPS协议是具有SSL和HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP更加安全
25.文档碎片减少DOM操作
let list = document.getElementById('list')
let fragment = document.createDocumentFragment()
for (let i = 0; i < 5; i++) {
const item = document.createElement('li');
item.innerHTML = `项目${i}`
// list.appendChild(item)
fragment.appendChild(item)
}
list.appendChild(fragment)
1.Es6常见的语法你知道哪一些?
let promise 箭头函数 展开运算符 Object.assign()
1.MVC和MVVM
MVVM的特点: 在MVVM的框架下,视图和模型是不能直接通信的,它们通过ViewModal来通信,ViewModel通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中的View 和 ViewModel可以互相通信。
2.v-model的原理
vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
<input type="text" placeholder="输入内容" id="inp">
输入的内容是:<span id="box"></span>
<script>
let obj = {}
document.getElementById('inp').addEventListener('keyup', function() {
obj.inp = event.target.value
})
Object.defineProperty(obj, 'inp', {
set: function(val) {
console.log('设置值')
document.getElementById('box').innerHTML = val
},
get: function() {
console.log('取值')
}
})
</script>
3.data()为什么是函数?
每一个组件都有自己的私有作用域,确保各组件数据不会被干扰。
4.v-if与v-show的区别?使用场景分别是什么?
v-if:显示隐藏是将dom元素整个添加或删除;适用于运行时条件很少改变。
v-show:隐藏则是为该元素添加css–display:none,dom元素依旧还在;适用于非常频繁地切换。(不能用于权限操作)
5.虚拟dom
(1) 虚拟dom是什么?
vue2.x才有的虚拟dom;本质是js对象;
(2) 虚拟dom在vue中做了什么?
[1].将真实dom转化虚拟dom(js对象);
[2].更新的时候做对比;
(3) 虚拟dom是如何提升vue的渲染效率的?
[1].局部更新(节点更新);
[2].将直接操作dom的地方拿到两个js对象之中去做比较;
6.DIFF中的patch() 方法
我对diff 不是很了解,所以没有进行查,学,先放一放,后续进一步了解后再补充吧。
7.
r
o
u
t
e
r
和
router和
router和route的区别
$router:是路由操作对象,只写对象
this.$router.push({
name:'hello'
})
route:路由信息对象,只读对象
this.name = this.$route.params.name;
query和params 有何区别?
用法:query用path来引入,params只能用name来传递,不能使用path。
如果params中把name写成了path,接收参数页面将会是undefined。
query更像是get请求(会在地址栏显示参数);params更像post方法传递(不会在地址栏显示)。
8.什么是vue声明周期?
Vue实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载DOM→渲染、更新→渲染、卸载等一系列过程,我们称这是Vue的声明周期。
Vue声明周期的作用是什么?
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
Vue生命周期总共有几个阶段?
8个阶段:创建前/后,载入前/后,更新前/后,销毁前/后。
第一次页面加载会触发那几个钩子?
第一次页面加载时会触发beforeCreate,created,beforeMount,mounted这几个钩子
每个生命周期适合那些场景?
1、beforeCreate(创建前) :数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据。
2、created(创建后) :实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 $el 属性。
3、beforeMount(挂载前) :在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。此时还没有挂载html到页面上。
4、mounted(挂载后) :在el被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html 页面中。此过程中进行ajax交互。
5、beforeUpdate(更新前) :响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染。
6、updated(更新后):在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
7、beforeDestroy(销毁前) :实例销毁之前调用。这一步,实例仍然完全可用,this 仍能获取到实例。
8、destroyed(销毁后) :实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。
另外还有 keep-alive 独有的生命周期,分别为 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 activated 钩子函数。
9.简述Vue的响应式原理
当一个Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty 将它们转为getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。
每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
10.Hash和history有什么区别
1.hash就是指url后面的#号以及后面的字符,history没有带#;
2.原理:
(1) hash通过监听浏览器的onhaschange()事件变化,查找对应的路由规则;
(2) history原理:理由H5的history中新增的两个API push State()和replace State() 和一个事件onpopstate监听url变化;
3.hash能兼容到IE8,history只能兼容到IE10;
4.由于hash值变化不会导致浏览器向服务器发出请求,而且hash改变会触发hashchange事件(hashchange只能改变#后面的url片段);虽然hash路径出现在url中,但是不会出现在HTTP请求中,对后端完全没有影响,因此改变hash值不会重新加载页面,基本都是使用hash来实现前端路由的。
11.为什么 Vuex 的 mutation 中不能做异步操作?
(1) Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过Action来提交mutation实现,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
(2) 每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的跟踪,给调试带来困难。
12.计算属性和watch有什么区别?以及它们的运用场景?
区别
computed 计算属性:依赖其它属性值,并且computed的值有缓存,只有它依赖的属性值发生改变,下一次获取computed的值才会重新计算computed的值。
watch 侦听器:更多的是观察的作用,无缓存性,类似与某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。
运用场景
当需要进行数据计算,并且依赖与其它数据时,应该使用computed,因为可以利用computed的缓存属性,避免每次获取值时都要重新计算。
当需要在数据变化时执行异步或开销较大的操作时,应该使用watch,使用watch选项允许执行异步操作(访问一个API),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
13.Vue的父子组件生命周期钩子函数执行顺序?
<!-- 加载渲染过程 -->
<!-- 父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted -->
<!-- 子组件更新过程 -->
<!-- 父beforeUpdate -> 子beforeUpdate -> 子updaed -> 父updated -->
<!-- 父组件跟新过程 -->
<!-- 父beforeUpdate -> 父updated -->
<!-- 销毁过程 -->
<!-- 父beforeDestroy -> 子beforeDestroy -> 子destroyed ->父destroyed -->
14.说一下vue3.0你了解多少?
vue2中,我们一般会采用mixin来复用逻辑代码,挺好用的,但是存在一些问题:例如代码来源不清晰、方法属性等冲突。
基于此在vue3中引入了Composition API(组合API),使用纯函数分隔复用代码。
Fragment在vue2中,组件必须只有一个根节点,很多时候会添加一些没有意义的节点用于包裹。Fragment组件就是用于解决这个问题的
15.父子组件传值
props、 e m i t 、 emit、 emit、refs、bus、vuex、provide / inject
16.defineProperty和proxy的区别
Object.defineProperty 是 Es5 的方法,Proxy 是 Es6 的方法
defineProperty 不能监听到数组下标变化和对象新增属性,Proxy 可以
defineProperty 是劫持对象属性,Proxy 是代理整个对象
defineProperty 局限性大,只能针对单属性监听,所以在一开始就要全部递归监听。Proxy 对象嵌套属性运行时递归,用到才代理,也不需要维护特别多的依赖关系,性能提升很大,且首次渲染更快
defineProperty 会污染原对象,修改时是修改原对象,Proxy 是对原对象进行代理并会返回一个新的代理对象,修改的是代理对象
defineProperty 不兼容 IE8,Proxy 不兼容 IE11
17.vuex有哪几种属性?
state:存放公共数据的地方;
getter:获取根据业务场景返回的数据;
mutations:唯一修改state的方法,修改过程是同步的;
action:异步处理,通过分发操作触发mutation;
moule:将store模块分割,减少代码臃肿;
18.vue , 微信小程序 , uni-app属性的绑定
vue和uni-app动态绑定一个变量的值为元素的某个属性的时候,会在属性前面加上冒号":";
小程序绑定某个变量的值为元素属性时,会用两个大括号{{}}括起来,如果不加括号,为被认为是字符串。
18.vue , 微信小程序 , uni-app的页面生命周期函数
vue:
beforeCreate(创建前)
created(创建后)
beforeMount(载入前,挂载)
mounted(载入后)
beforeUpdate(更新前)
updated(更新后)
beforeDestroy(销毁前)
destroyed(销毁后)
小程序/uni-app:
1. onLoad:首次进入页面加载时触发,可以在 onLoad 的参数中获取打开当前页面路径中的参数。
2. onShow:加载完成后、后台切到前台或重新进入页面时触发
3. onReady:页面首次渲染完成时触发
4. onHide:从前台切到后台或进入其他页面触发
5. onUnload:页面卸载时触发
6. onPullDownRefresh:监听用户下拉动作
7. onReachBottom:页面上拉触底事件的处理函数
8. onShareAppMessage:用户点击右上角转发
19.vue、小程序、uni-app中的本地数据存储和接收
vue:
存储:localstorage.setItem(‘key’,‘value’)
接收:localstorage.getItem(‘key’)
微信小程序:
存储:通过wx.setStorage/wx.setStorageSync写数据到缓存
接收:通过wx.getStorage/wx.getStorageSync读取本地缓存,
uni-app:
存储:uni.setStorage({key:“属性名”,data:“值”}) //异步
uni.setStorageSync(KEY,DATA) //同步
接收:uni.getStorage({key:“属性名”,success(res){res.data}}) //异步
uni.getStorageSync(KEY) //同步
20.vue-router 有哪几种导航守卫?
全局守卫、路由独享守卫、路由组件内的守卫