三、HTML / CSS 混合篇
1. HTML5、CSS3 里面都新增了那些新特性?
HTML5
- 新的语义标签
- article 独立的内容。
- aside 侧边栏。
- header 头部。
- nav 导航。
- section 文档中的节。
- footer 页脚。
- 画布(Canvas) API
- 地理(Geolocation) API
- 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
- sessionStorage 的数据在浏览器关闭后自动删除
- 新的技术webworker, websocket, Geolocation
- 拖拽释放(Drag and drop) API
- 音频、视频API(audio,video)
- 表单控件,calendar、date、time、email、url、searc
CSS3
- 2d,3d变换
- Transition, animation
- 媒体查询
- 新的单位(rem, vw,vh 等)
- 圆角(border-radius),阴影(box-shadow),对文字加特效(text-shadow),线性渐变(gradient),旋转(transform)transform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);//旋转,缩放,定位,倾斜
- rgba
四、JS 篇
1. JS 数据类型 ?存储上的差别?
数据类型主要包括两部分:
- 基本数据+类型: Undefined、Null、Boolean、Number 和 String,Symbol(创建后独一无二且不可变的数据类型 )
- 引用数据类型: Object (包括 Object 、Array 、Function)
存储区别:
- 基本数据类型存储在栈中
- 引用类型的对象存储于堆中
2.数组常用方法?
增:
- push() 向数组的末尾添加一个或更多元素,并返回新的长度
- unshift() 在数组开头添加任意多个值,然后返回新的数组长度
- splice() 传入三个参数,分别是开始位置、0(要删除的元素数量)、插入的元素,返回空数组
- concat() 首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组,不会影响原始数组
删:
- pop() 方法用于删除数组的最后一项,同时减少数组的length 值,返回被删除的项
- shift() 方法用于删除数组的第一项,同时减少数组的length 值,返回被删除的项
- splice() 传入两个参数,分别是开始位置,删除元素的数量,返回包含删除元素的数组
- slice() 传入两个参数,分别是开始位置和结束位置,不包括结束值,返回一个新数组,不影响原数组
改:
- splice() 传入三个参数,分别是开始位置,要删除元素的数量,要插入的任意多个元素,返回删除元素的数组,对原数组产生影响
查:
- indexOf() 返回要查找的元素在数组中的位置,如果没找到则返回 -1
- includes() 返回要查找的元素在数组中的位置,找到返回true,否则false
3.JavaScript字符串的常用操作方法有哪些?
增:
- 字符串可以通过‘+’以及${}进行字符串拼接
- concat 用于将一个或多个字符串拼接成一个新字符串
删:三个函数都接收一个或两个参数,跟数组中slice相似
- slice()
- substr() 接受两个参数:起始索引和要提取的字符数
- substring() 接受两个参数:起始索引和结束索引 不包括结束位置的字符
改:
- trim()、trimLeft()、trimRight() 删除前、后或前后所有空格符,再返回新的字符串
- repeat() 接收一个整数参数,表示要将字符串复制多少次,然后返回拼接所有副本后的结果
- padStart()、padEnd() 复制字符串,接收两个参数,第一个参数是长度,第二个参数是想要填充的字符,如果小于指定长度,则在相应一边(end/start)填充字符,直至满足长度条件
- toLowerCase()、 toUpperCase() 大小写转化
查:
- chatAt() 返回给定索引位置的字符,由传给方法的整数参数指定
- indexOf() 从字符串开头去搜索传入的字符串,并返回位置(如果没找到,则返回 -1 )
- startWith() 从字符串中搜索传入的字符串,判断开头字符串是否与期待值相同,并返回一个表示是否包含的布尔值
- includes() 从字符串中搜索传入的字符串,判断字符串是否包含期待值,并返回一个表示是否包含的布尔值
4.JavaScript字符串的常用转换方法和模板匹配方法?
转换方法:
- split() 把字符串按照指定的分割符,拆分成数组中的每一项
模板匹配方法:
- match() 接收一个参数,可以是一个正则表达式字符串,也可以是一个RegExp对象,返回数组
- search() 接收一个参数,可以是一个正则表达式字符串,也可以是一个RegExp对象,找到则返回匹配索引,否则返回 -1
- replace() 接收两个参数,第一个参数为匹配的内容,第二个参数为替换的元素(可用函数)
5.JavaScript 中的类型转换机制
显示转换:
- Number()
- 字符串:如果可以被解析为数值,则转换为相应的数值
- 字符串:如果不可以被解析为数值,返回 NaN
- 空字符串转换为0
- 布尔值:true 转成 1,false 转成 0
- undefined:转成 NaN
- null:转成0
- 对象:通常转换成NaN(除了只包含单个数值的数组)
- parseInt() parseInt相比Number,就没那么严格了,parseInt函数逐个解析字符,遇到不能转换的字符就停下来
- String() 可以将任意类型的值转化成字符串
- 特殊:如果接受的是对象则返回[object,object] 如果是数组【1,2,3】返回1,2,3
- Boolean() 可以将任意类型的值转为布尔值
隐式转换:
- 在+运算中,一旦存在字符串,则会进行字符串拼接操作
- 除了+有可能把运算子转为字符串,其他运算符都会把运算子自动转成数值 。常用就是将字符串转为数值 字符串-0 = 数值
6.null 和 undefined 的区别?
- null和undefined不能通过==来判断。
- undefined
- 这个变量从根本上就没有定义
- 隐藏式 空值
- null
- 这个值虽然定义了,但它并未指向任何内存中的对象
- 声明式 空值
7. “ ===”、“ ==”的区别?
==: 如果操作数相等,则会返回 true
- 两个都为简单类型,字符串和布尔值都会转换成数值,再比较
- 简单类型与引用类型比较,对象转化成其原始类型的值,再比较
- 两个都为引用类型,则比较它们是否指向同一个对象
- null 和 undefined 相等
- 存在 NaN 则返回 false
===:只有在无需类型转换运算数就相等的情况下,才返回 true,需要检查数据类型
区别:
- 相等操作符(==)会做类型转换,再进行值的比较,全等运算符不会做类型转换
8. “eval是做什么的?
它的功能是把对应的字符串解析成 JS 代码并运行;
应该避免使用 eval,不安全,非常耗性能(2次,一次解析成 js 语句,一次执行)。
9. 箭头函数有哪些特点?
不需要function关键字来创建函数
省略return关键字
改变this指向
10. var、let、const 区别?
var 存在变量提升。
let 只能在块级作用域内访问。
const 用来定义常量,必须初始化,不能修改(对象特殊)
11. new操作符具体干了什么呢?
1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。
12.深拷贝浅拷贝的区别?如何实现一个深拷贝?
浅拷贝: 指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝 , 两个对象指向同一个地址
深拷贝: 深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性
如何实现深拷贝:
- JSON.stringify()
- 手写循环递归
- _.cloneDeep()
- MessageChannel 新增
- jquery的extend
13.对作用域链的理解
- 作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的
- 简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期
14.JavaScript原型,原型链 ? 有什么特点?
- 原型:
- JavaScript的所有对象中都包含了一个 [__proto__] 内部属性,这个属性所对应的就是该对象的原型
- JavaScript的函数对象,除了原型 [__proto__] 之外,还预置了 prototype 属性
- 当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 [__proto__]
- 原型链:
- 当一个对象调用的属性/方法自身不存在时,就会去自己 [__proto__] 关联的前辈 prototype 对象上去找
- 如果没找到,就会去该 prototype 原型 [__proto__] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止。从而形成了所谓的“原型链”
原型特点:
- JavaScript对象是通过引用来传递的,当修改原型时,与之相关的对象也会继承这一改变
15.请解释什么是事件代理
- 事件代理(Event Delegation),又称之为事件委托。是 JavaScript 中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好处是可以提高性能
- 可以大量节省内存占用,减少事件注册,比如在table上代理所有td的click事件就非常棒
- 可以实现当新增子对象时无需再次对其绑定
16.Javascript如何实现继承?
- 构造函数绑定:使用 call 或 apply 方法,将父对象的构造函数绑定在子对象上
- 实例继承:将子对象的 prototype 指向父对象的一个实例 Cat.prototype = new Animal();
- 拷贝继承:如果把父对象的所有属性和方法,拷贝进子对象
- 原型继承:将子对象的 prototype 指向父对象的 prototype F.prototype = Parent.prototype;
- ES6 语法糖 extends:class ColorPoint extends Point {}
17.谈谈This对象的理解
- this总是指向函数的直接调用者(而非间接调用者)
- 如果有new关键字,this指向new出来的那个对象
- 在事件中,this指向触发这个事件的对象,特殊的是,IE中的attachEvent中的this总是指向全局对象Window
18.事件模型
W3C中定义事件的发生经历三个阶段:捕获阶段(capturing)、目标阶段(targetin)、冒泡阶段(bubbling)
- 冒泡型事件:当你使用事件冒泡时,子级元素先触发,父级元素后触发
- 捕获型事件:当你使用事件捕获时,父级元素先触发,子级元素后触发
- DOM事件流:同时支持两种事件模型:捕获型事件和冒泡型事件
- 阻止冒泡:在W3c中,使用stopPropagation()方法;在IE下设置cancelBubble = true
- 阻止捕获:阻止事件的默认行为,例如click - 后的跳转。在W3c中,使用preventDefault()方法,在IE下设置window.event.returnValue = false
19.new操作符具体干了什么呢?
- 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型
- 属性和方法被加入到 this 引用的对象中
- 新创建的对象由 this 所引用,并且最后隐式的返回 this
20.JavaScript中执行上下文和执行栈是什么?
执行上下文: 是一种对Javascript代码执行环境的抽象概念,也就是说只要有Javascript代码运行,那么它就一定是运行在执行上下文中
- 全局执行上下文:只有一个,浏览器中的全局对象就是 window对象,this 指向这个全局对象
- 函数执行上下文:存在无数个,只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文
- Eval 函数执行上下文: 指的是运行在 eval 函数中的代码,很少用而且不建议使用
执行栈:也叫调用栈,具有 LIFO(后进先出)结构,用于存储在代码执行期间创建的所有执行上下文
- 当Javascript引擎开始执行你第一行脚本代码的时候,它就会创建一个全局执行上下文然后将它压到执行栈中
- 每当引擎碰到一个函数的时候,它就会创建一个函数执行上下文,然后将这个执行上下文压到执行栈中
- 引擎会执行位于执行栈栈顶的执行上下文(一般是函数执行上下文),当该函数执行结束后,对应的执行上下文就会被弹出,然后控制流程到达执行栈的下一个执行上下文
21.typeof 与 instanceof 区别?
typeof 操作符返回一个字符串,表示未经计算的操作数的类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GFG2mFwF-1686967105042)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1683945877205.png)]
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
区别:
- typeof会返回一个变量的基本类型,instanceof返回的是一个布尔值
- instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型
- 而typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了function 类型以外,其他的也无法判断
22.判断是否为数组的5种方法?
- instanceof data instanceof Array
- constructor data.constructor == Array
- Array.isArray() Array.isArray(data) 最推荐
- typeof typeof(data)
- Object.prototype.toSrtring.call()
23. 判断一个值是什么类型有哪些方法?
- typeof 运算符
- instanceof 运算符
- Object.prototype.toString.call 方法
- constructor
24. ajax过程?
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象.
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.
(6)使用JavaScript和DOM实现局部刷新.
25.Ajax原理,ajax优缺点?
- Ajax的原理简单来说是在用户和服务器之间加了—个中间层(AJAX引擎),通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。使用户操作与服务器响应异步化。这其中最关键的一步就是从服务器获得请求数据
- Ajax的过程只涉及JavaScript、XMLHttpRequest和DOM。XMLHttpRequest是ajax的核心机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tk5uFVRn-1686967105043)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1683946428125.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R52CumK9-1686967105044)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1683946512993.png)]
- 优点:
- 通过异步模式,提升了用户体验.
- 优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用.
- Ajax在客户端运行,承担了一部分本来由服务器承担的工作,减少了大用户量下的服务器负载。
- Ajax可以实现动态不刷新(局部刷新)
- 缺点:
- 安全问题 AJAX暴露了与服务器交互的细节。
- 对搜索引擎的支持比较弱。
- 不容易调试。
26.bind、call、apply 区别?
bind、call、apply用来改变this指向
apply:接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入
改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次 fn.apply(null,[1,2,3]);
call: 第一个参数也是this的指向,后面传入的是一个参数列表 fn.call(obj,1,2,3)
bind: bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入) 返回的是新的函数
27.如何实现一个bind?
实现bind三步:
- 修改this指向
- 动态传递参数
- 兼容new关键字
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Au8o7qGB-1686967105045)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1683947651751.png)]
28.说说你对正则表达式的理解?应用场景?
正则表达式是一种用来匹配字符串的强有力的武器
应用场景:
- 验证QQ合法性(5~15位、全是数字、不以0开头):
- 验证手机号格式
29.对事件循环的理解(详细)?
前提: JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环
在JavaScript中,所有的任务都可以分为
- 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
- 异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等
同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。上述过程的不断重复就事件循环
异步任务分为微任务和宏任务:
微任务: 一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前
- Promise
- MutaionObserver 监听dom发生改变的
- Object.observe(已废弃;Proxy 对象替代)
- process.nextTick(Node.js)
宏任务: 宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合
- script (可以理解为外层同步代码)
- setTimeout/setInterval
- postMessage、MessageChannel
- setImmediate、I/O(Node.js)
执行顺序:
- 先执行同步代码,
- 遇到异步宏任务则将异步宏任务放入宏任务队列中,
- 遇到异步微任务则将异步微任务放入微任务队列中,
- 当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,
- 微任务执行完毕后再将异步宏任务从队列中调入主线程执行,
- 一直循环直至所有任务执行完毕。
30.DOM常见的操作有哪些?
- 创建节点
- createElement 创建新元素,接受一个参数,即要创建元素的标签名
- 查询节点
- querySelector 传入任何有效的css 选择器,即可选中单个 DOM元素(首个) 如果页面上没有指定的元素时,返回 null
- querySelectorAll 返回一个包含节点子树内所有与之相匹配的Element节点列表,如果没有相匹配的,则返回一个空节点列表
- 更新节点
- innerHTML 不但可以修改一个DOM节点的文本内容,还可以直接通过HTML片段修改DOM节点内部的子树
- innerText
- style dom对象.style.样式属性 = ‘’
- 添加节点
- innerHTML
- appendChild 把一个子节点添加到父节点的最后一个子节点
- insertBefore(新dom,指定dom对象) 把子节点插入到指定的位置的前面
- setAttribute 在指定元素中添加一个属性节点,如果元素中已有该属性改变属性值
- 删除节点
- removeChild 拿到父节点,要删除的节点dom对象。父.removeChild(子)
31.说说你对BOM的理解,常见的BOM对象你了解哪些?
BOM (Browser Object Model),浏览器对象模型,提供了独立于内容与浏览器窗口进行交互的对象 。
浏览器的全部内容可以看成DOM,整个浏览器可以看成BOM。
BOM对象:
- window: Bom的核心对象是window,它表示浏览器的一个实例 。 在浏览器中,window对象有双重角色,即是浏览器窗口的一个接口,又是全局对象
- location:获取url地址信息
- navigator: 对象主要用来获取浏览器的属性,区分浏览器类型
- screen: 保存的纯粹是客户端能力信息,也就是浏览器窗口外面的客户端显示器的信息,比如像素宽度和像素高度
- history: 主要用来操作浏览器URL的历史记录,可以通过参数向前,向后,或者向指定URL跳转
32.BOM和DOM区别?
- BOM(浏览器对象):与浏览器交互的方法和对象
- BOM是浏览器对象模型,它指的是将浏览器当作一个对象来对待,这个对象主要定义了与浏览器进行交互的方法和接口
- BOM的核心是window,而window对象具有双重角色,它既是js访问浏览器窗口的一个接口,又是一个全局对象(Global)
- 这就意味着网页中定义的任何对象、变量和函数,都会作为全局对象的一个属性或者方法存在
- DOM(文档对象模型):处理网页内容的方法和接
- DOM是文档对象模型,它指的是把文档当作一个对象来对待,这个对象主要定义了处理网页的内容和接口
33.如何解决跨域问题?
了解同源策略: 同源是指"协议+域名+端口"三者相同,它是浏览器最核心也最基本的安全功能如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击
- 通过jsonp跨域
- nginx代理跨域
- nodejs中间件跨域
- 后端在头部信息中设置安全域名
- 前端代理
34.异步加载js方式?
- 设置``属性 async="async" (一旦脚本可用,则会异步执行)
- 动态创建 script DOM:document.createElement('script');
- XmlHttpRequest 脚本注入
- 异步加载库 LABjs
- 模块加载器 Sea.js
35.哪些操作会导致内存泄漏?
内存泄漏: JavaScript 内存泄露指对象在不需要使用它时仍然存在,导致占用的内存不能使用或回收
- 未使用 var 声明的全局变量
- 闭包函数(Closures)
- 循环引用(两个对象相互引用)
- 控制台日志(console.log)
- 移除存在绑定事件的DOM元素(IE)
- setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏
- 垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收
36.XML和JSON的区别?
- 数据体积:JSON数据体积更小
- 数据交互:JSON与JavaScript得交互更加方便,更容易被解析,更好的数据传输
- 传输速度:JSON传输速度快
- 数据描述:JSON对数据的描述相比XML较差
37.说说你对递归得理解?
递归: 在数学与计算机科学中,是指在函数的定义中使用函数自身的方法
38.说说你对函数式编程的理解?优缺点?纯函数,高阶函数,柯里化
主要的编程范式有三种:命令式编程,声明式编程和函数式编程
函数式编程: 更加强调程序执行的结果而非执行的过程,简单来讲,就是要把过程逻辑写成函数,定义好输入参数,只关心它的输出结果
纯函数: 纯函数是对给定的输入返还相同输出的函数,并且要求你所有的数据都是不可变的,即纯函数=无状态+数据不可变
- 函数内部传入指定的值,就会返回确定唯一的值
- 不会造成超出作用域的变化,例如修改全局变量或引用传递的参数
高阶函数: 是以函数作为输入或者输出的函数被称为高阶函数
柯里化: 把一个多参数函数转化成一个嵌套的一元函数的过程
优点:
- 更好的管理状态
- 更简单的复用
- 减少代码量,提高维护性
缺点:
- 性能:函数式编程相对于指令式编程,性能绝对是一个短板,因为它往往会对一个方法进行过度包装,从而产生上下文切换的性能开销
- 资源占用:在 JS 中为了实现对象状态的不可变,往往会创建新的对象,因此,它对垃圾回收所产生的压力远远超过其他编程方式
- 递归陷阱:在函数式编程中,为了实现迭代,通常会采用递归操作
39.Javascript中如何实现函数缓存?函数缓存有哪些应用场景?
函数缓存,就是将函数运算过的结果进行缓存
实现方式: 实现函数缓存主要依靠闭包、柯里化、高阶函数
应用场景:
- 对于昂贵的函数调用,执行复杂计算的函数
- 对于具有有限且高度重复输入范围的函数
- 对于具有重复输入值的递归函数
- 对于纯函数,即每次使用特定输入调用时返回相同输出的函数
40. JSON 的了解?
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。
它是基于JavaScript的一个子集。数据格式简单, 易于读写, 占用带宽小
{‘age’:‘12’, ‘name’:‘back’}
41. document.write 和 innerHTML 的区别?
document.write 只能重绘整个页面
innerHTML 可以重绘页面的一部分
42. 请解释一下 JavaScript 的同源策略?
概念:同源策略是客户端脚本(尤其是Netscape Navigator2.0,其目的是防止某个文档或脚本从多个不同源装载。
这里的同源策略指的是:协议,域名,端口相同,同源策略是一种安全协议。
指一段脚本只能读取来自同一来源的窗口和文档的属性。
43. 介绍一下闭包和闭包常用场景?
- 闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包常见方式,就是在一个函数的内部创建另一个函数
- 使用闭包主要为了设计私有的方法和变量,闭包的优点是可以避免变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念。
- 闭包有三个特性:
- 函数内再嵌套函数
- 内部函数可以引用外层的参数和变量
- 参数和变量不会被垃圾回收机制回收
- 闭包的好处: 能够实现封装和缓存等;
- 闭包的缺点就是常驻内存,会增大内存使用量,使用不当会造成内存泄漏
- 应用场景:
- 常见的防抖节流
- 使用闭包可以在 JavaScript 中模拟块级作用域
- 闭包可以用于在对象中创建私有变量
44. javascript的内存(垃圾)回收机制?
- 垃圾回收器会每隔一段时间找出那些不再使用的内存,然后为其释放内存
- 标记清除方法(mark and sweep),
- 这是JavaScript最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”
- 垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了
- 引用计数方法(reference counting)
- 在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个 变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
- 在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的, 也就是说只要涉及BOM及DOM就会出现循环引用问题。
45. 用js递归的方式写1到100求和?
function add(num1, num2) { const num = num1 + num2; if(num2 === 100) { return num; } else { return add(num, num2 + 1) } } var sum = add(1, 2);
46. 事件队列(宏任务微任务)
可以分为微任务(micro task)队列和宏任务(macro task)队列。
微任务一般比宏任务先执行,并且微任务队列只有一个,宏任务队列可能有多个。另外我们常见的点击和键盘等事件也属于宏任务。
下面我们看一下常见宏任务和常见微任务。
常见宏任务:
- setTimeout()
- setInterval()
- setImmediate()
常见微任务:
- promise.then()、promise.catch()
- new MutaionObserver()
- process.nextTick()
微任务和宏任务的本质区别。
- 宏任务特征:有明确的异步任务需要执行和回调;需要其他异步线程支持。
- 微任务特征:没有明确的异步任务需要执行,只有回调;不需要其他异步线程支持。
setTimeout(function () { console.log("1"); }, 0); async function async1() { console.log("2"); const data = await async2(); console.log("3"); return data; } async function async2() { return new Promise((resolve) => { console.log("4"); resolve("async2的结果"); }).then((data) => { console.log("5"); return data; }); } async1().then((data) => { console.log("6"); console.log(data); }); new Promise(function (resolve) { console.log("7"); resolve() }).then(function () { console.log("8"); }); // 2 4 7 5 8 3 6 async2的结果 1
47.async/await, Generator
async 是一个通过异步执行并隐式返回 Promise 作为结果的函数。是Generator函数的语法糖,并对Generator函数进行了改进。
改进:
- 内置执行器,无需手动执行 next() 方法。
- 更好的语义
- 更广的适用性:co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
- 返回值是 Promise,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then() 方法进行调用。
- async 隐式返回 Promise 作为结果的函数,那么可以简单理解为,await后面的函数执行完毕时,await会产生一个微任务(Promise.then是微任务)。
Generator 是 ES6 引入的新概念,它允许在函数执行过程中暂停和恢复它们的状态。通过 function* 声明一个 Generator 函数,可以在函数体内使用关键字 yield 来生成一个状态,并将函数挂起,等待下一次调用。Generator 函数返回一个可迭代对象,可以通过 next() 方法获取当前生成器的状态值。使用 Generator 函数可以更简单地实现异步操作,避免回调嵌套带来的问题。
48. JavaScript 是单线程的,浏览器是多进程的
- 每打开一个新网页就会创建一个渲染进程
- 渲染进程是多线程的
- 负责页面渲染的 GUI 渲染线程
- 负责JavaScript的执行的 JavaScript 引擎线程
- 负责浏览器事件循环的事件触发线程,注意这不归 JavaScript 引擎线程管
- 负责定时器的定时触发器线程,setTimeout 中低于 4ms 的时间间隔算为4ms
- 负责XMLHttpRequest的异步 http 请求线程
- GUI 渲染线程与 JavaScript 引擎线程是互斥的
- 单线程JavaScript是因为避免 DOM 渲染的冲突,web worker 支持多线程,但是 web worker 不能访问 window 对象,document 对象等。
49.说说 Javascript 数字精度丢失的问题,如何解决?
例子:0.1+0.2===0.3 =>false 涉及IEE754标准
问题原因:
- 计算机存储双精度浮点数需要先把十进制数转换为二进制的科学记数法的形式,然后计算机以自己的规则{符号位+(指数位+指数偏移量的二进制)+小数部分}存储二进制的科学记数法
- 因为存储时有位数限制(64位),并且某些十进制的浮点数在转换为二进制数时会出现无限循环,会造成二进制的舍入操作(0舍1入),当再转换为十进制时就造成了计算误差
解决:
- 使用 toFixed() 方法:将浮点数转化为一个指定位数小数的字符串形式
- 使用第三方库,Math.js、BigDecimal.js
50.说说你对模块化方案的理解,比如 CommonJS、AMD、CMD、ES Module 分别是什么?
- CommonJS加载模块同步,主要用于服务器端,它主要依靠require和exports来实现模块化,require用户加载模块,exports用于导出模块
- AMD异步模块定义,他解决了在浏览器环境下文件以来管理,模块加载的问题。与commonjs不同,AMD使用异步方式加载模块
- CMD通用模块定义,cmd也是为了解决浏览器端模块化出现的,与AMD不同的是它使用同步方式加载模块,主要依赖require和define来实现模块化
- ES Module是es6新增的模块化方案,支持在浏览器和node.js使用,并且已经得到了嵌入式运行环境的支持,与commonjs和amd不同,ES Module是静态加载它使用import和export关键字实现模块化
51.用过哪些设计模式?
- 工厂模式:
- 工厂模式解决了重复实例化的问题,但还有一个问题,那就是识别问题
- 主要好处就是可以消除对象间的耦合,通过使用工程方法而不是new关键字
- 构造函数模式
- 使用构造函数的方法,即解决了重复实例化的问题,又解决了对象识别的问题,该模式与工厂模式的不同之处在于,直接将属性和方法赋值给 this对象;
52.javascript有哪些方法定义对象?
- 对象字面量: var obj = {}; 原型是Object.prototype
- 构造函数: var obj = new Object();
- Object.create(): var obj = Object.create(Object.prototype);
53.说说你对promise的了解
54.web开发中会话跟踪的方法有哪些?
- cookie
- session
- url重写
- 隐藏input
- ip地址
55.介绍js有哪些内置对象?
- Object 是 JavaScript 中所有对象的父对象
- 数据封装类对象:Object、Array、Boolean、Number 和 String
- 其他对象:Function、Arguments、Math、Date、RegExp、Error
56.eval是做什么的?
- 它的功能是把对应的字符串解析成JS代码并运行
- 应该避免使用eval,不安全,非常耗性能(2次,一次解析成js语句,一次执行)
- 由JSON字符串转换为JSON对象的时候可以用eval,var obj =eval('('+ str +')')
57.parseInt函数,[“1”, “2”, “3”].map(parseInt) 答案是多少?
parseInt函数接收两个参数第一个参数是要被解析的字符串,第二个参数是一个可选的进制数。
答案:【1,NaN,NaN】
解析:
- [‘1’,‘2’,‘3’].map(parseInt)通过map便利的数组会将索引作为第二个参数传入,所以会以parseint(2,1)第二个参数1不是合法的进制数,paeseint(3,2)这里因为3不是二进制数
58.javascript 代码中的"use strict";是什么意思?说说严格模式的限制
- use strict是一种ECMAscript 5 添加的(严格)运行模式,这种模式使得 Javascript 在更严格的条件下运行,使JS编码更加规范化的模式,消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
限制:
- 变量必须声明后再使用
- 函数的参数不能有同名属性
- 不能使用with语句
- 禁止this指向window
59.js延迟加载的方式有哪些?
- 设置``属性 defer="defer" (脚本将在页面完成解析时执行)
- 动态创建 script DOM:document.createElement('script');
- XmlHttpRequest 脚本注入
- 延迟加载工具 LazyLoad
60.同步和异步的区别?
- 同步:浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完,页面刷新,新内容出现,用户看到新内容,进行下一步操作
- 异步:浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内容也会出现,用户看到新内容
61.ES6新特性
- 增加了let和const命令,用来声明变量。
- 新增模板字符串(为JavaScript提供了简单的字符串插值功能)
- 箭头函数
- for-of(用来遍历数据—例如数组中的值。)
- arguments对象可被不定参数和默认参数完美代替。
- 扩展运算符,解构赋值
- ES6将promise对象纳入规范,提供了原生的Promise对象。
- 增加了块级作用域,let命令实际上就增加了块级作用域。
- 还有就是引入module模块的概念
- Generator 生成器
- 类和继承
62.let,const,var区别?
通过const声明的对象属性不可修改!!!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUkDO8Ka-1686967105046)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1684154487983.png)]
63.let和var区别?
- let命令不存在变量提升,如果在let前使用,会导致报错
- 如果块区中存在let和const命令,就会形成封闭作用域
- 不允许重复声明,因此,不能在函数内部重新声明参数
64.如果new一个箭头函数的会怎么样?
箭头函数是ES6中的提出来的,它没有prototype,也没有自己的this指向,更不可以使用arguments参数,所以不能New一个箭头函数。
65.箭头函数与普通函数的区别?
- 箭头函数比普通函数更加简洁
- 箭头函数没有自己的this
- 箭头函数继承来的this永远不会改变
- 箭头函数不能走为构造函数
- 箭头函数没有arguments
- call,bind,apply无法改变this指向
66.扩展运算符的作用
扩展运算符:(…)
对象扩展运算符:用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中
数组扩展运算符:可以将一个数组转为用逗号分隔的参数序列,且每次只能展开一层数组
67.ES6中模板语法与字符串处理
在es6之前拼接字符串采用的是字符串通过’+'拼接,很麻烦
es6字符模板使用:``结合${变量名}
var name='lc' var age = 27 var address = 'zg' var information = `my name ${name},I am ${age} years old this year,I'm from ${address}`
68.map与forEach的区别
- forEach方法,是最基本的方法,就是遍历与循环,默认有3个传参:分别是遍历的数组内容item、数组索引index、和当前遍历数组Array,不会改变原有数组
- map方法,基本用法与forEach一致,但是不同的,它会返回一个新的数组,所以在callback需要有return值,如果没有,会返回undefined
69.Js动画与CSS动画区别及相应实现
- CSS3的动画的优点
- 在性能上会稍微好一些,浏览器会对CSS3的动画做一些优化
- 代码相对简单
- 缺点
- 在动画控制上不够灵活
- 兼容性不好
- JavaScript的动画正好弥补了这两个缺点,控制能力很强,可以单帧的控制、变换,同时写得好完全可以兼容IE6,并且功能强大。对于一些复杂控制的动画,使用javascript会比较靠谱。而在实现一些小的交互动效的时候,就多考虑考虑CSS吧
70.gulp是什么?
- gulp是前端开发过程中一种基于流的代码构建工具,是自动化项目的构建利器;它不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成
- Gulp的特点:
- 易于使用:通过代码优于配置的策略,gulp 让简单的任务简单,复杂的任务可管理
- 构建快速 利用 Node.js 流的威力,你可以快速构建项目并减少频繁的 IO 操作
- 易于学习 通过最少的 API,掌握 gulp 毫不费力,构建工作尽在掌握:如同一系列流管道
71.事件的各个阶段, addEventListener
捕获阶段–目标阶段–冒泡阶段
- 由此,addEventListener的第三个参数设置为true和false的区别已经非常清晰了
- true表示该元素在事件的“捕获阶段”(由外往内传递时)响应事件
- false表示该元素在事件的“冒泡阶段”(由内向外传递时)响应事件 默认false
72.数组some函数和every函数
every 函数接受一个函数作为参数,这个函数会被依次应用到数组中的每个元素。该函数应该返回一个布尔值:
- 如果返回 true,则表示当前元素满足条件,继续检查下一个元素。
- 如果返回 false,则表示当前元素不满足条件,every 函数将立即返回 false,不再检查后面的元素
some 函数接受一个函数作为参数,这个函数会被依次应用到数组中的每个元素。该函数应该返回一个布尔值:
- 如果返回 true,则表示当前元素满足条件,some 函数将立即返回 true,不再检查后面的元素。
- 如果返回 false,则表示当前元素不满足条件,继续检查下一个元素。
73.数组乱序
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; arr.sort(() => Math.random() - 0.5)
74.如何渲染几万条数据并不卡住界面,requestAnimationFrame
requestAnimationFrame 是一个由浏览器提供的 API,用于优化页面动画、避免出现卡顿、异闻和掉帧的情况。它能够在浏览器下一次重绘之前,通知浏览器调用一个指定的函数来更新动画,从而使得动画呈现更加流畅、自然。
实现流程:假如有一万条数据,我们还需要设置每次渲染条数,总计渲染次数,当前渲染次数三个变量。首先自动执行一次渲染函数(loop),通过条件判断当前渲染次数是否小于总渲染次数进行继续执行,满足条件调用window.requestAnimationFrame(具体渲染函数add);add函数中创建空标签用来接收每次渲染的结构,减少回流次数,当前渲染次数+1,继续执行loop函数
// 插入十万条数据 const total = 100000 // 一次插入 20 条,如果觉得性能不好就减少 const once = 20 // 渲染数据总共需要几次 const loopCount = total / once // 当前渲染次数 let countOfRender = 0 let ul = document.querySelector("ul"); function add() { // 优化性能,插入不会造成回流 createDocumentFragment是一个指向空DocumentFragment对象的引用。下面先将20条插入空元素避免回流 const fragment = document.createDocumentFragment(); for (let i = 0; i < once; i++) { const li = document.createElement("li"); li.innerText = Math.floor(Math.random() * total); fragment.appendChild(li); } // 一次性添加dom,减少回流次数 ul.appendChild(fragment); countOfRender += 1; loop(); } function loop() { if (countOfRender < loopCount) { window.requestAnimationFrame(add); } } loop();
75.获取到页面中所有的checkbox怎么做
var domList = document.getElementsByTagName(‘input’) var checkBoxList = []; var len = domList.length; //缓存到局部变量 while (len--) { //使用while的效率会比for循环更高 if (domList[len].type == ‘checkbox’) { checkBoxList.push(domList[len]);、 } }
76.怎样添加、移除、移动、复制、创建和查找节点
创建节点:
- createElement() 创建一个具体元素
- createDocumentFragment() 创建一个dom片段
- createTextNode() 创建一个文本节点
添加、移除、替换、插入:
- appendChild() 添加
- removeChild() 移除
- replaceChild() 替换
- insertBefore() 插入
查找:
- getElementsByTagName() //通过标签名称
- getElementsByName() //通过元素的Name属性的值
- getElementById() //通过元素Id,唯一性
77.window.onload和$(document).ready
- window.onload()方法是必须等到页面内包括图片的所有元素加载完毕后才能执行。
- $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕
78.addEventListener()和attachEvent()的区别
- addEventListener()是符合W3C规范的标准方法; attachEvent()是IE低版本的非标准方法
- addEventListener()支持事件冒泡和事件捕获; - 而attachEvent()只支持事件冒泡
- addEventListener()的第一个参数中,事件类型不需要添加on; attachEvent()需要添加'on'
- 如果为同一个元素绑定多个事件, addEventListener()会按照事件绑定的顺序依次执行, attachEvent()会按照事件绑定的顺序倒序执行
79.数组去重
利用ES6 Set去重(ES6中最常用)
var arr = [1,2,2,2,3,3,3,3];
console.log(new Set(arr))
遍历去重
include,indexof
80.判断两个对象相等
正常通过==比较,不可以判断对象是否相等
需要通过JSON.stringify()先转化为字符串再进行比较
81.防抖节流
节流: 如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数,不管在这个中间有多少次触发这个事件,执行函数的频次总是固定的;
防抖: 当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间(非常短的时间),当事件密集触发时,函数的触发会被频繁的推迟,只有等待了一段时间也没有事件触发,才会真正的执行响应函数
82.检测浏览器版本版本有哪些方式?
- 根据 navigator.userAgent UA.toLowerCase().indexOf('chrome')
- 根据 window 对象的成员 'ActiveXObject' in window
83.javascript基本规范
- 不要在同一行声明多个变量
- 请使用===/!==来比较true/false或者数值
- 使用对象字面量替代new Array这种形式
- 不要使用全局函数
- Switch语句必须带有default分支
- If语句必须使用大括号
- for-in循环中的变量 应该使用let关键字明确限定作用域,从而避免作用域污
84.npm和yarn,pnpm的优势是什么
npm:
- npm 是 Node.js 自带的包管理器,因此使用 npm 时不需要另外安装软件包,而且能够使用大量的第三方包。npm 的命令简单易学,而且能够满足大部分项目的需求。
yarn :
- 提供了更好的性能和速度,安装包时能够并行下载,从而提高了安装的效率
- 引入了锁定文件的概念,用于确保开发环境和生产环境的包版本一致性,从而避免了由于包版本不兼容而引发的问题。
- 支持离线模式,如果项目中已经安装了所需的包,yarn 不需要从互联网中下载包,而是直接使用本地缓存中的包。
pnpm:
- 最大优势是节约磁盘空间 pnpm 只在项目中安装一份软件包
- pnpm 不需要重新安装所有依赖项,而是对每个工程和依赖进行增量安装
85.导致页面加载白屏时间长的原因有哪些,怎么进行优化
原因:
- 大量 HTTP 请求:在页面加载过程中,浏览器需要请求服务器获取页面的 HTML、CSS、JavaScript、图片等资源,如果请求过多,会导致页面加载时间变长。可以通过减少 HTTP 请求的数量来优化加载速度,例如合并 CSS 和 JavaScript 文件,压缩图片等。
- 大量 JavaScript 代码:当浏览器下载并解析 JavaScript 代码时,页面的渲染会被阻塞,这也会导致页面加载时间变长。可以通过将 JavaScript 代码异步加载、延迟加载或分割成多个小文件来优化加载速度。
- 大量 CSS 代码:与 JavaScript 类似,CSS 代码也会阻塞页面渲染,可以通过压缩 CSS 代码、减少 CSS 文件的大小和数量、使用外部链接等方法来优化加载速度。
- 服务器响应时间过长:如果服务器响应时间过长,也会导致页面加载时间变长。可以通过升级服务器硬件、优化代码等方式来减少服务器响应时间。
- 不合理的 DOM 结构:如果页面的 DOM 结构不合理,也会导致页面加载时间变长。可以通过减少 DOM 节点数量、避免使用 table 布局、使用 CSS Sprite(雪碧图) 等方式来优化加载速度。
优化:
- 压缩 HTML、CSS、JavaScript、图片等资源,减少文件大小。
- 合并 CSS 和 JavaScript 文件,减少 HTTP 请求的数量。
- 将 JavaScript 代码异步加载、延迟加载或分割成多个小文件。
- 使用浏览器缓存,避免重复下载资源。
- 使用外部链接或 CDN 加速器等方式来加速资源加载。
- 减少 DOM 节点数量,避免使用 table 布局等方式来优化页面渲染速度。
86.DOM克隆操作
深克隆(克隆元素内文本节点加上所有后辈元素节点),
浅克隆(克隆元素本身,不克隆文本节点和后辈节点)
cloneNode()接受一个可选值为true或false的参数。True 表示克隆元素和它的所有子节点。False表示克隆元素但不包含它的子节点
87.沙箱模式
沙箱模式是一种软件设计模式,用于创建一个独立的、受保护的执行环境,隔离代码与外部世界的交互,并限制代码所能访问的资源和功能。在JavaScript中,沙箱模式通常通过将代码封装在匿名函数中并立即调用来实现。这样可以创建一个私有作用域,其中的变量也不会泄漏到全局作用域,从而避免了变量名冲突和数据污染。
虽然闭包和沙箱模式是不同的概念,但它们可以相互结合,以实现更高级别的编程需求。例如,可以使用闭包来创建一个沙箱,限制函数所能访问的变量范围。这种组合可以使代码更加清晰、安全和易于维护。
88.string和tostring什么区别
string和ToString方法的区别在于它们的作用不同。
string是一种数据类型,用于存储和处理字符串,而ToString是一个通用方法,用于将数据转换为字符串。当我们需要将一些非字符串类型的数据转换为字符串时,就可以使用ToString方法来实现。
toString接收一个参数。可将数值字符串转化为对应进制数
string可以将任何数据类型转化为字符串,toString不可以转换null和undefined
89.js数据存储方式
- Cookie:Cookie是一种在浏览器端存储数据的机制,它可以支持长期存储数据,并且数据可以在不同的页面之间共享。使用JavaScript可以通过document.cookie属性来操作Cookie。
- Web Storage:Web Storage提供了Session Storage和Local Storage两种机制,都是HTML5新增的。Session Storage用于临时性的会话数据存储,数据在用户关闭浏览器后将被清除,而Local Storage则可以长期存储数据,即使用户关闭浏览器也不会丢失。使用JavaScript可以通过window.sessionStorage和window.localStorage对象来操作这两种存储机制。
- IndexedDB:IndexedDB是一种在浏览器中存储大量结构化数据的机制,它类似于一个本地数据库。IndexedDB提供了一个异步的API,可以在浏览器中建立对象存储空间,在其中存储键值对数据。使用JavaScript可以通过IndexedDB API来操作这种存储机制。
- Web SQL:废弃
90.cookie,session,token区别
- Cookie:Cookie是在用户端存储数据的一种机制,它可以存储一些简单的用户信息和标识。服务器通过设置Cookie并发送到客户端,在下次请求时客户端会自动携带该Cookie,从而实现对用户身份的验证或其他操作。Cookie的缺点是可能面临安全问题,因为Cookie存储在客户端,容易遭受窃取或伪造攻击。
- Session:Session是在服务器端存储数据的一种机制,它可以保存一些复杂的用户信息和状态,用于实现对用户身份的验证和跟踪。服务器使用一个唯一的Session ID来和客户端进行交互,从而避免了安全性问题。但是Session也存在一些缺点,例如对服务器负载压力较大等问题。
- Token:Token是一种包含用户身份和权限信息的加密字符串,通常由服务器生成并发送给客户端。客户端使用Token代替Cookie或Session来进行身份验证和数据传输。Token的优点是可以减轻服务器压力,减少网络流量和延迟,并且减少了安全性问题。Token的缺点是需要保证其加密和传输的安全性。
Cookie适用于简单的身份验证和数据存储,Session适用于需要复杂状态管理和用户跟踪的场景,而Token则更适合于分布式系统和APP等跨平台的数据传输。
91.js十进制转二进制
- 使用 toString() 方法和参数 2:
let num = 123 num.toString() //'123' num.toString(2) //'1111011'
92.如何让多个异步函数顺序执行
- 使用 Promise 的 then 方法链接异步任务。
- 使用 async/await 关键字。
93.settimeout事件机制
setTimeout() 方法的实现基于事件机制,它会将回调函数添加到事件队列中。当指定的延迟时间到达后,该回调函数被推入到事件队列的最后,等待 JavaScript 引擎空闲时执行。
由于 JavaScript 的单线程特性和事件循环机制,多个任务可能会阻塞事件队列,从而导致异步代码无法按照预期的顺序执行,我们需要合理地使用定时器以避免这种情况。
如果设置的延迟时间小于 4 毫秒,则实际的延迟时间可能会大于设置值,因为浏览器通常会使用 4 毫秒的最小时间间隔来执行定时器任务。
94.bind连续绑多次最终this指向
先看代码:
function say() { alert(this.x); }; var a = say.bind({x: 1}); var b = a.bind({x: 2}); b(); // 这里会输出1还是2呢? 答案:1
无论使用bind绑定多少次,最终原函数的this值是由第一次绑定传的参数决定的。
95.mep和set
- Map:可迭代的集合,其中每个元素都由一个键和一个相应的值组成。Map 中的键可以是任何类型,而值也可以是任何类型,包括对象引用和原始值。
- Set:一种只含有唯一值的集合,不允许出现重复项。Set 中的值可以是任何类型,包括对象引用和原始值。
96.大文件上传
- 分片上传:将大文件分成多个小文件,每个小文件单独上传,最后在服务器端进行合并操作,这样可以减少上传和下载的时间,并且一旦上传失败只需要重新上传失败的那个分片,而不需要整个文件重新上传。
- 断点续传:在分片上传的基础上,可以利用本地存储技术记录已经上传的分片信息,当上传失败时,可以根据已经上传的分片信息来继续上传失败的那个分片,从而实现断点续传。
- 流式上传:在传统的上传方式中,文件需要完全读入内存才能上传,而在流式上传中,文件是按照流的方式逐个读取上传,可以避免一次性读取整个大文件,从而减少内存的占用。
- 压缩上传:对于某些文件类型,比如文本文件、图片、视频等,可以先对其进行压缩再上传,可以减少文件的大小并加快上传速度。
- 使用断电续传 SDK:除了自己实现断点续传外,也可以使用一些第三方的断点续传 SDK,如七牛、阿里云等都提供了相关的 SDK,可以简化大文件上传的实现。
97.移动端Click300毫秒点击延迟 解决办法
产生原因: 浏览器需要等待一段时间(大约 300 毫秒)来检测用户是单击还是双击。
- 使用 FastClick:FastClick 是一个 JavaScript 插件,它可以通过消除移动浏览器上的点击延迟来提高 Web 应用程序的响应速度。使用 FastClick 只需要在页面加载完成后引入库文件,并在需要绑定快速点击的元素上通过 FastClick.attach(element) 方法进行绑定即可。
- 使用 touch 事件:如果不想使用第三方库或插件,也可以通过原生的 touch 事件来模拟快速点击。例如,可以通过监听 touchstart 事件来代替 click 事件,实现更快的响应速度。
- 设置 meta 标签:将以下 meta 标签添加到 HTML 文件的 head 标签中,可以告诉浏览器不要缩放页面,并且禁用缩放手势,从而提高点击响应速度:
98.如何判断一个对象为空对象
通过 Object.keys(obj) 方法获取对象的所有属性名,并判断属性数量是否为 0 来实现 、
let obj = {'name':'zs'} Object.keys(obj).length //1 let objs = {} Object.keys(objs).length //0
99.数组常用方法
- push:向数组末尾添加一个或多个元素,并返回新的长度。
- pop:删除并返回数组最后一个元素。
- shift:删除并返回数组第一个元素。
- unshift:向数组开头添加一个或多个元素,并返回新的长度。
- concat:合并两个或多个数组,并返回新的数组。不改变原数组。
- join:将数组中的所有元素转化为字符串,并用指定的分隔符连接起来。
- slice:返回数组的一个片段(浅拷贝),不影响原数组。
- splice:在数组中添加或删除元素,可修改原数组。
- sort:对数组元素进行排序,默认按照 Unicode 码点升序排列,可传入回调函数实现自定义排序。
- reverse:翻转数组元素顺序,改变原数组。
- indexOf:查询元素在数组中第一次出现的位置,找到返回其下标,否则返回-1。
- lastIndexOf:从数组末尾开始查询元素在数组中最后一次出现的位置,找到返回其下标,否则返回-1。
- filter:返回由满足回调函数条件的所有元素组成的新数组,不改变原数组。
- map:返回一个新数组,其中的元素是对原有数组元素应用回调函数后得到的结果。
- reduce:累加器方法,对数组的每个元素(从左到右)执行一个回调函数,返回单个值。
- some:判断数组是否具有满足条件的值,有就返回true
- every:判断数组所有值是否都满足条件,都满足返回true
- forEach:循环数组
100.数组扁平化
- 使用递归
- 使用 reduce 方法:reduce 方法可以用来将数组中的每个元素累加到一个结果中
- 使用 flat 方法:
- ES2019 中引入了数组的 flat 方法,可以将嵌套的数组扁平化成一维数组。
- flat 方法只能够将嵌套的层数降至一维,如果需要将多维数组扁平化成一维数组,则需要传递一个大于等于嵌套层数的参数 arr.flat( Infinity ), Infinity 表示扁平化任何深度的嵌套数组
101.for…in 和 for … of区别
for…in 循环是用来遍历对象属性的,它可以枚举目标对象的所有可枚举属性,包括继承链上的属性,但遍历的顺序是不确定的
for…of 循环是用来遍历可迭代对象 (Iterable) 的,它可以遍历数组、字符串、Map、Set 等内置的可迭代对象,但不能遍历普通的对象,也不能遍历对象的属性
区别:
- for…in遍历数组返回下标,遍历对象返回键
- for…of遍历数组返回数据,不可以遍历普通对象
102.伪数组,伪数组转换为数组
伪数组是一种类数组对象,它具有类似数组的结构和特性,但并不是真正的数组。
在 JavaScript 中,常见的伪数组包括函数参数 arguments、DOM 元素集合 NodeList 和 HTMLCollection 等。
伪数组和数组区别:
- 伪数组没有数组的方法和属性(如 push、pop、length),不能使用数组相关的循环方法(如 forEach、map、reduce)等。但它们具有类数组的结构,可以通过下标来访问元素,并且拥有 length 属性
转换: Array.from() 方法或者扩展运算符
103.二维数组应用场景
- 游戏开发: 在游戏开发中,二维数组通常被用来表示游戏场景、地图、迷宫等
- 图像处理: 在图像处理中,二维数组常用于表示图像的像素点,每个像素点可以用一个颜色值来表示
- 数字计算: 在数学计算中,二维数组经常用于存储和处理矩阵
五、Vue 篇
1. 谈谈你对MVVM开发模式的理解?
MVVM是一种简化用户界面的实践驱动编程方式。在当前主流的前后端分离的开发模式中,MVVM模式的优越性日益体现,相较于经典的MVC模式,其对于程序模块的封装很好地解决了前后端信息交互的冗余和繁琐
MVVM分为Model、View、ViewModel三者。
Model 代表数据模型,数据和业务逻辑都在Model层中定义;
View 代表UI视图,负责数据的展示;
ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作;
Model 和 View 并无直接关联,而是通过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步。
这种模式实现了 Model 和 View 的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作 dom。
为什么使用MVVM:低耦合,可复用,独立开发,可测试
2. v-if 和 v-show 有什么区别?
- 手段
- v-if是动态的向DOM树内添加或者删除DOM元素;
- v-show是通过设置DOM元素的display样式属性控制显隐;
- 编译
- v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译;
- v-show是在任何条件下,无论首次条件是否为真,都被编译,然后被缓存,而且DOM元素保留;
- 性能消耗:
- v-if有更高的切换消耗;
- v-show有更高的初始渲染消耗
- v-if指令可以应用于template包装元素上,v-show不支持
3.r o u t e 和 route和route和router区别
- $route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数
- $router 是“路由实例”想要导航到不同URL 对象包括了路由的跳转方法,钩子函数等。通过push、replace、go、back等方法,来实现页面间的跳转
4.vue自定义指令
vue指令
vue2
局部注册:directive选项
directives: { 'focus': { bind(el, binding, vnode) { el.focus() } } }
全局注册:main.js
Vue.directives('focus',{ bind(el, binding, vnode) { el.focus() } })
生命周期:
- bind:只调用一次,指令第一次绑到元素调用,用于初始化
- inserted:被绑定元素插入父节点时调用
- update:所在组件vnode更新调用
- componentUpdate:指令在组件的vnode及子组件的vnode全部更新完调用
- ubind:只调用一侧,指令解绑
vue3
局部注册:引入 import { Directive , DirectiveBinding } from ‘vue’ 分别校验vFocus,binding
<template> <input type="text" v-focus="{ color: 'red' }" /> </template> <script setup> const vFocus = { created(el, binding) { el.style.backgroundColor = binding.value.color; console.log(el, binding.value.color); //<input type="text" style="background-color: red;"> 'red' }, }; </script>
全局注册:main.js,app.vue如上引入
import { createApp } from 'vue' import App from './App.vue' const app = createApp(App) app.directive('focus', { created(el, binding) { el.style.backgroundColor = binding.value.color; console.log(el, binding.value.color); //<input type="text" style="background-color: red;"> 'red' } }) app.mount('#app')
生命周期:
- created 元素初始化的时候
- beforeMount 指令绑定到元素后调用 只调用一次
- mounted 元素插入父级dom调用
- beforeUpdate 元素被更新之前调用
- update 这个周期方法被移除 改用updated
- beforeUnmount 在元素被移除前调用
- unmounted 指令被移除后调用 只调用一次
5.vue项目优化
- 代码层面
- 长列表性能优化: Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了
- 事件销毁, beforeDestroy生命周期函数内执行销毁逻辑。
- 图片懒加载
- 路由懒加载
- 按需加载插件
- v-if,v-for避免同时使用,v-for遍历添加key
- v-if,v-show选择
- keep-alive组件缓存
- input防抖节流
- 基础的web技术优化
- 开启gzip压缩
- 浏览器缓存
- CDN加速
- webpack优化
6.vue模板如何编译
Vue的模板编译就是将“HTML”模板编译成render函数的过程。这个过程大致可以分成三个阶段:
- 解析阶段:将“HTML”模板解析成AST语法树;
- 核心 parseHTML( template ,{}) Vue定义了很多匹配HTML的正则表达式 ,parseHTML根据正则匹配
- parseHTML是解析模板字符串的“主线程”,它的第一个参数是要解析的模板字符串, 也就是单文件组件中最外层 所包裹的部分;第二个参数是一个选项对象,它会包含一些回调,以及一些配置项。
- 选项对象:
- start( tag, attrs, unary ) 匹配到开始标签时的回调,tag为当前标签的标签名,attrs为该标签上的属性列表,unary为当前标签是否为自闭合标签
- end() 匹配到结束标签时的回调
- chars(text) 匹配到文本节点的回调
- comment(text) 匹配到注释节点的回调,其处理逻辑跟文本的处理逻辑类似
- 优化阶段:从AST语法树中找出静态子树并进行标记(被标记的静态子树在虚拟dom比对时会被忽略,从而提高虚拟dom比对的性能);
- 上面简单介绍过,优化阶段的工作就是标记静态子树,标记静态子树后主要有以下两个优点:
- 生成虚拟dom的过程中,如果发现一个节点是静态子树,除了首次渲染外不会生成新的子节点树,而是拷贝已存在的静态子树;
- 比对虚拟dom的过程中,如果发现当前节点是静态子树,则直接跳过,不需要进行比对。
- 标记静态子树的过程分为两个步骤:
- 遍历AST语法树,找出所有的静态节点并打上标记(注:当前节点及其所有子节点都是静态节点,当前节点才会被打上静态节点的标记)
- 遍历经过上面步骤后的树,找出静态根节点,并打上标记(注:静态根节点是指本身及所有子节点都是静态节点,但是父节点为动态节点的节点,找到了静态根节点也就找到了“静态子树”)
- 代码生成阶段:通过AST生成代码字符串,并最终生成render函数。
7.vue2响应式原理
vue 采用了几个核心部件 : Observer ,Dep, Watcher ,Scheduler
- observer把一个普通的对象转换成响应式的对象
- observer 把对象的每个属性通过 object.defineProperty 转换为带有 getter 和 setter 的属性
- Dep 表示依赖, vue 会为响应式对象中的每一个属性,对象本身,数组本身创建一个 dep 实例,每个 dep 实例都可以做两件事情 :
- 记录依赖:是谁在用我
- 派发更新:我变了,我要通知那些用我的人
- watcher 在函数执行的过程中,如果发生了依赖记录,那么 dep 就会把这个全局变量记录下来,表示有一个 wathcer 用到了我这个属性。
- Scheduler 不会立即执行更新,通过nexttick异步更新
8.vue3响应式原理
通过Proxy(代理): 拦截对象中任意属性的变化,包括:属性值的读写,属性的增加,属性的删除等。
通过Reffect(反射): 对源对象的属性进行操作, Reflect不是一个函数对象,因此它是不可构造的。
9.刷新浏览器后,Vuex的数据是否存在?如何解决?
不存在
原因: 因为 store 里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,store里面的数据就会被重新赋值初始化。
我们有两种方法解决该问题:
- 使用 vuex-along
- 使用 localStorage 或者 sessionStroage
10.vue和react共同点?区别
共同点:
- 数据驱动视图
- 组件化
- 都使用 Virtual DOM
不同点:
- 核心思想不同
- vue定位就是尽可能的降低前端开发的门槛,让更多的人能够更快地上手开发。这就有了vue的主要特点:灵活易用的渐进式框架,进行数据拦截/代理,它对侦测数据的变化更敏感、更精确。
- react 定位就是提出 UI 开发的新思路 React推崇函数式编程(纯组件),数据不可变以及单向数据流,当然需要双向的地方也可以手动实现, 比如借助onChange和setState来实现。
- 组件写法
- React推荐的做法是JSX + inline style, 也就是把 HTML 和 CSS 全都写进 JavaScript 中
- Vue 推荐的做法是 template 的单文件组件格式(简单易懂,从传统前端转过来易于理解),即 html,css,JS 写在同一个文件(vue也支持JSX写法)
- diff算法
- 响应式原理
- vue2采用object.defineProperty ,vue3采用proxy,reflect
- React基于状态机,手动优化,数据不可变,需要setState驱动新的state替换老的state。
11.vue双向数据绑定原理
简易实现:v-model分为两部分,通过v-bind绑定值,再通过v-on:input来通步修改值
原理:
- 需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
- 通过dep来理清依赖关系,watcher在依赖中添加自身
- compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
- 待属性变动dep.notice()通知时,能调动watcher自身的update方法,并处罚compile回调渲染视图
12.computed和watch区别
computed计算属性,watch监听属性
- 计算属性不在 data 中,它是基于data 或 props 中的数据通过计算得到的一个新值。watch 可以监听的数据来源:data,props,computed内的数据
- component中有get和set方法,会默认缓存计算结果。watch不支持缓存,支持异步, immediate监听属性立即执行一次,deep开启深度监听
13.Vuex
Vuex是一种状态管理模式,存在的目的是共享可复用的组件状态。
主要包括以下几个模块:
- State => 基本数据,定义了应用状态的数据结构,可以在这里设置默认的初始状态。
- Getter => 从基本数据派生的数据,允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
- Mutation => 是唯一更改 store 中状态的方法,且必须是同步函数。
- Action => 像一个装饰器,包裹mutations,使之可以异步。用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
- Module => 模块化Vuex,允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
14.vuex辅助函数
mapState, mapMutations, mapActions, mapGetters
mapState和mapGetters:
- 两者都放在 computed中,以mapState举例
import { mapState } from 'vuex' computed中 computed:{ ...mapState(['data']) //data是vuex存放的state中的属性,此时{{data}}可使用 }
mapMutations, mapActions:
- 放在组件的methods属性中 。使用与上类似
15.vuex模块化使用
当我们开发的项目比较大时,store中的数据就可能比较多,这时我们store中的数据就可能变得臃肿,为了解决这一问题,我们就需要将store模块化(module)
前提:创建两份js文件,含有属性与vuex写法相同,需要通过 namespaced:true开启命名空间store/index.js:在modules中引入文件
使用:
- 访问state数据:
- 第一种方式:this.$store.state.moduleA.sum
- 第二种方式: ...mapState('moduleA',['sum','number'])
- action提交mutation
- 第一种方式:需要传参this.小明,无需传参store.dispatch(′moduleB/addZhang′,name:′小明′,age:18),无需传参this.store.dispatch('moduleB/addZhang',{name:'小明',age:18}) ,无需传参this.store.dispatch('moduleB/addServer')
- 第二种方式:...mapActions('moduleB',['addZhang'])
- getters计算属性
- 第一种方式: this.$store.getters['moduleB/firstName']
- 第二种方式:...mapGetters('moduleB',['firstName'])
16.vue中mixin
mixin(混入): 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。
本质其实就是一个js对象,它可以包含我们组件中任意功能选项,如data、components、methods 、created、computed等等
我们只要将共用的功能以对象的方式传入 mixins选项中,当组件使用 mixins对象时所有mixins对象的选项都将被混入该组件本身的选项中来
具体使用:
- 创建mixins.js文件
let mixin = { created() { console.log('我是mixin中的'); }, methods: { hellow() { console.log('你好'); }, }, } export default mixin
- 局部使用
import mixin from "./mixins"; export default { mixins: [mixin], mounted() { this.hellow();//你好 }, };
- 全局使用main.js
import { createApp } from 'vue' import App from './App.vue' import mixins from "./mixins"; const app = createApp(App) app.mixin(mixins) app.mount('#app')
17.Vue中给对象添加新属性时,界面不刷新怎么办?
原因:vue2响应式采用object.defineProperty进行劫持,那个添加新属性时,新的属性不会具有get和set方法,不是一个响应式所以界面不刷新
解决:Vue.set() 向响应式对象中添加一个property,并确保这个新 property 同样是响应式的
vue3通过proxy劫持和reflect映射实现响应式,不会有这个问题
18.vue组件通讯方式
- 通过 props 传递
- props校验:name:{type:String,required:true,default:默认值} required是否必要
- 通过 $emit 触发自定义事件
- 使用 ref
- EventBus
- Provide 与 Inject
- Vuex
19.vue3setup的父传子怎么去写?
介绍三种方法:
第一种:使用vue2写法通过props和$emit
第二种:setup函数写法
- setup(props,context),通过props接收数据,通过context.emit(‘调用父组件方法’,传递参数)
第三种:script中setup
- vue3自带defineProps,defineEmits
const emits = defineEmits(["changeNumber"]); // 也可以不赋值,取值通过{{num}}获取 const props = defineProps({ num: { type: Number, default: () => [], }, list: { type: Array, }, }); const changeNum = function () { emits("changeNumber", 888); // console.log(11111111111); };
20.setup可不可以直接写async和await?
可以
setup 语法糖中可直接使用 await,不需要写 async , setup 会自动变成 async setup
<script setup> import Api from '../api/Api' const data = await Api.getData() console.log(data) </script>
21.vue生命周期
vue2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8P7sspQX-1686967105047)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1685089235304.png)]
beforeCreate – 首次访问data
created – 首次访问this生命周期
mounted – 页面展示
vue3
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yfSFfojq-1686967105048)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1685089301710.png)]
区别:
- beforeCreate -> setup() 开始创建组件之前,创建的是data和method
- created -> setup()
- beforeMount -> onBeforeMount 组件挂载到节点上之前执行的函数。
- mounted -> onMounted 组件挂载完成后执行的函数
- beforeUpdate -> onBeforeUpdate 组件更新之前执行的函数。
- updated -> onUpdated 组件更新完成之后执行的函数。
- beforeDestroy -> onBeforeUnmount 组件挂载到节点上之前执行的函数。
- destroyed -> onUnmounted 组件卸载之前执行的函数。dszhuoyi
- activated -> onActivated 组件卸载完成后执行的函数
- deactivated -> onDeactivated
22.说说 Vue 中 CSS scoped 的原理
添加scoped标签后会给组件中所有标签元素,添加一个唯一标识,这个唯一标识就是自定义属性,data-v-xxxxxxxx这样的字眼,同时对应的样式选择器也会添加这个唯一的属性选择器
23.$nextTick原理
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
nextTick原理
24.data是函数不是对象
vue是一个单页面应用最终所有的实例都会挂载到app.vue文件,如果data是一个对象那么会导致数据污染。通过函数返回对象的方式,利用函数作用域的限制避免数据污染
25.路由守卫
vue路由守卫分为三种:全局路由守卫,独享路由守卫,组件路由守卫
to: 进入到哪个路由去
from: 来自哪个路由
next:是否跳转
- 全局守卫: router.beforeEach((to,from,next)=>{})
- 独享路由守卫: beforeEnter:(to,from,next)=>{}
- 组件路由守卫: beforeRouteEnter:(to,from,next)=>{}, beforeRouteUpdate , beforeRouteLeave
26.vue设置全局变量
方法一:
- vue2.x挂载全局是使用 Vue.prototype.的形式来挂载,然后通过xxxx=xxx的形式来挂载,然后通过this.xxxx=xxx 的形式来挂载,然后通过 this.xxx来获取挂载到全局的变量或者方法。
- Vue 3 中,使用 config.globalProperties 、app.config.globalProperties.$data = ‘111’
const {proxy} = getCurrentInstance() console.log(proxy.$data)
方法二:
- provide/inject
27.vue中keep-alive
属性:include和exclude
语法:
// 指定home组件和about组件被缓存 <keep-alive include="home,about" > <router-view></router-view> </keep-alive> // 除了home组件和about组件别的都缓存 <keep-alive exclude="home,about" > <router-view></router-view> </keep-alive>
钩子函数:
- activated 当组件被激活(使用)的时候触发 可以简单理解为进入这个页面的时候触发
- deactivated 当组件不被使用(inactive状态)的时候触发 可以简单理解为离开这个页面的时候触发
进入开启缓存的组件
初始进入和离开 created ---> mounted ---> activated --> deactivated 后续进入和离开 activated --> deactivated
28.vue插槽
slot又名插槽,是Vue的内容分发机制,组件内部的模板引擎使用slot元素作为承载分发内容的出口。插槽slot是子组件的一个模板标签元素,而这一个标签元素是否显示,以及怎么显示是由父组件决定的。slot又分三类,默认插槽,具名插槽和作用域插槽。
默认插槽:又名匿名插槽,当slot没有指定name属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽。
具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
作用域插槽:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。
实现原理:当子组件vm实例化时,获取到父组件传入的slot标签的内容,存放在vm.s l o t 中,默认插槽为 v m . slot中,默认插槽为vm.slot中,默认插槽为vm.slot.default,具名插槽为vm.s l o t . x x x , x x x 为插槽名,当组件执行渲染函数时候,遇到 s l o t 标签,使用 slot.xxx,xxx 为插槽名,当组件执行渲染函数时候,遇到slot标签,使用slot.xxx,xxx为插槽名,当组件执行渲染函数时候,遇到slot标签,使用slot中的内容进行替换,此时可以为插槽传递数据,若存在数据,则可称该插槽为作用域插槽。
29.vue2和vue3区别
双向绑定更新
vue2 的双向数据绑定是利⽤ES5 的⼀个 API ,Object.defineProperty()对数据进⾏劫持 结合 发布订阅模式的⽅式来实现的。
vue3 中使⽤了 ES6 的 ProxyAPI 对数据代理,通过 reactive() 函数给每⼀个对象都包⼀层 Proxy,通过 Proxy 监听属性的变化,从⽽ 实现对数据的监控。
这⾥是相⽐于vue2版本,使⽤proxy的优势如下
1.defineProperty只能监听某个属性,不能对全对象监听 可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)
2.可以监听数组,不⽤再去单独的对数组做特异性操作,通过Proxy可以直接拦截所有对象类型数据的操作,完美⽀持对数组的监听。
获取props
vue2在script代码块可以直接获取props,vue3通过setup指令传递
API不同
Vue2使⽤的是选项类型API(Options API),Vue3使⽤的是合成型API(Composition API)
建立数据data
vue2是把数据放入data中,vue3就需要使用一个新的setup()方法,此方法在组件初始化构造得时候触发。
生命周期不同
vue2 -------- vue3
beforeCreate -> setup() 开始创建组件之前,创建的是data和method
created -> setup()
beforeMount -> onBeforeMount 组件挂载到节点上之前执行的函数。
mounted -> onMounted 组件挂载完成后执行的函数
beforeUpdate -> onBeforeUpdate 组件更新之前执行的函数。
updated -> onUpdated 组件更新完成之后执行的函数。
beforeDestroy -> onBeforeUnmount 组件挂载到节点上之前执行的函数。
destroyed -> onUnmounted 组件卸载之前执行的函数。dszhuoyi
activated -> onActivated 组件卸载完成后执行的函数
deactivated -> onDeactivated
是否支持碎片:vue2.0 只允许有一个根标签,vue3.0支持碎片化,可以拥有多个根节点
main.js文件不同:vue2中我们可以使用pototype(原型)的形式去进行操作,引入的是构造函数 vue3中需要使用结构的形式进行操作,引入的是工厂函数
diff算法不同
更好的支持ts
30.Vue3.0 所采用的 Composition Api (组合式)与 Vue2.x 使用的 Options Api(选项式) 有什么不同?
- options Api 当组件变得复杂,导致对应属性的列表也会增长,这可能会导致组件难以阅读和理解 。composition Api它将功能定义在一起,利于查找和理解
- Composition API 对 tree-shaking 友好,代码也更容易压缩
- Composition API中见不到this的使用,减少了this指向不明的情况
- 如果是小型组件,可以继续使用Options API,也是十分友好的
31.vue3中hook
本质是一个函数,把setup函数中使用的Composition API(组合式api)进行了封装,类似于vue2中的mixin
自定义hook优势:复用代码,让setup中的逻辑更清楚易懂
32.vue组件和插件的区别
组件: Vue 组件是一个可复用的 Vue 实例,可以带有自己的状态和方法。组件可以包含其他组件,从而形成一个复杂的 UI 列表。
优点
- 可以将代码封装成一个可复用的组件,提高开发效率。
- 组件具有良好的可维护性,易于修改和更新。
缺点
- 组件的功能和作用比较独立,不太适用于全局功能的扩展。
- 组件的管理和组织需要一定的规范,否则可能会导致混乱和不易维护。
插件: Vue 插件可以扩展 Vue 的全局功能,在应用程序中可以重复使用。常见的插件如 vue-router、vuex、axios 等。
优点
- 插件可以方便地扩展 Vue 的全局功能。
- 插件可以使代码重复利用,提高开发效率。
- 开源社区中已经有大量的插件可以用于解决常见的问题。
缺点
- 插件具有一定的复杂性,需要更多的学习成本。
- 插件功能可能比较复杂,可能会导致性能下降。
33.vue修饰符
在Vue中,修饰符处理了许多DOM事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理
- 表单修饰符
- .lazy 懒加载,光标离开标签时,才赋值给value
- .trim 过滤首位空格
- .number 限制输入类型为数字或转为数字
- 事件修饰符
- .stop 阻止事件冒泡
- .prevent 组织事件默认行为
- .once 事件只触发一次
- .capture 开启事件捕获
- .self 事件只在自身触发
- 鼠标按键修饰符
- left 左键点击
- right 右键点击
- middle 中键点击
- 键值修饰符
- 普通键(enter、tab、delete、space、esc、up…)
- 系统修饰键(ctrl、alt、meta、shift…)
- v-bind修饰符
- .async 对props进行双向绑定
- .prop 设置自定义标签属性,避免暴露数据,防止污染html结构
- .camel 将命名为驼峰命名法
34.Vue路由中,history和hash两种模式有什么区别?
hash: hash 模式是一种把前端路由的路径用井号 # 拼接在真实 URL 后面的模式。当井号 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 hashchange 事件。
- 优点:浏览器兼容性较好,连 IE8 都支持
- 缺点:路径在井号 # 的后面,比较丑
history: history API 是 H5 提供的新特性,允许开发者直接更改前端路由,即更新浏览器 URL 地址而不重新发起请求
- 优点:路径比较正规,没有井号 #
- 缺点:兼容性不如 hash,且需要服务端支持,否则一刷新页面就404了
35.params和query区别
params 和 query 都是用于传递参数的,但它们的传参方式和使用场景是不同的。
params 通过路由路径传递参数,在路由配置中使用 :paramName 的形式进行声明
const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, }, ], })
query 通过 URL 查询字符串(即问号后面的部分)传递参数,在路由地址后面使用 ? 连接多个参数键值对
不需要在router中配置 /search?q=vue 会自动匹配到search组件
区别:
- params 适合用于必须存在的参数传递,例如用户详情页或文章详情页的访问。
- query 适合用于可选的参数传递,例如搜索功能中关键词的传递。
36.vue2中assets和vue3中public区别 ?
在 Vue 2 中,assets 目录是默认存在的,可以直接在项目的根目录下创建,它通常用来存放组件需要的图片、样式等静态资源文件。这些文件会被打包到 JavaScript 文件中,在代码中使用相对路径引用。
在 Vue 3 中,可以通过配置 vue.config.js 文件来设置 public 目录,它的作用与 assets 目录类似,用来存放静态资源文件。但是,与 Vue 2 不同的是,public 目录下的文件不会被打包,而是会直接复制到输出目录下
37.单页应用如何提高加载速度?
- 使用代码分割:将代码拆分成小块并按需加载(懒加载),以避免不必要的网络请求和减少加载时间。
- 缓存资源:利用浏览器缓存来存储重复使用的文件,例如 CSS 和 JS 文件、图片等。
- 预加载关键资源:在首次渲染之前,先提前加载关键资源,例如首页所需的 JS、CSS 或数据,以保证关键内容的快速呈现。
- 使用合适的图片格式:选择合适的图片格式(例如 JPEG、PNG、WebP 等),并根据需要进行压缩以减少文件大小。对于一些小图标,可以使用 iconfont 等字体文件来代替。
- 启用 Gzip 压缩:使用服务器端的 Gzip 压缩算法对文件进行压缩,以减少传输时间和带宽消耗。
- 使用 CDN:使用内容分发网络(CDN)来缓存和传递文件,以提高文件的下载速度和可靠性。
- 优化 API 请求:尽可能地减少 API 调用的数量,并使用缓存和延迟加载等技术来优化 API 请求的效率。
- 使用服务器端渲染:使用服务器端渲染(SSR)来生成 HTML,以减少客户端渲染所需的时间和资源。但需要注意,SSR 也可能增加了服务器的负担并使网站更复杂。
38.Vue父组件调用子组件的方法
vue中如果父组件想调用子组件的方法,可以在子组件中加上ref,然后通过this.$refs.ref.method调用
<child ref="child"></child> 调用:this.$refs.child.子组件方法
39.vue3中dom获取,ref在组件上使用
<template> <div class="ref"> <h3>ref使用:</h3> <input type="text" ref="input" /> // ref="input" 需要和 const input = ref(null); 相对应 </div> </template> <script setup> import { reactive, ref, createApp, onMounted } from "vue"; let state = reactive({ text: "信息按钮" }); // 同名的 input来进行获取节点 const input = ref(null); onMounted(() => { if (input.value) { input.value.focus(); } }); </script> <style scoped></style>
40.渐进式框架理解
渐进式: 可以理解为没有多做职责之外的事
个人理解:主张最少的,一开始仅基于基础框架构建,随着需求不断扩充
41.页面初始化闪烁
产生原因:当网速较慢,vue.js文件还没有加载完时,在页面上会显示{{message}}的字样,知道vue创建实例,编译模板时,dom才会被替换,所以这个过程屏幕是闪动的。
所以解决这个问题,需要在style样式中设置【v-cloak】{display:none}。在一般情况下,v-clock是一个解决初始化慢导致页面闪动的最佳实践,对于简单的项目很实用。
但是在具有工程化的项目里,比如使用了webpack和vue-router的项目中,html结构只是一个空的div元素,剩余的内容都是由路由去挂载不同的组件完成的,所以不需要v-cloak。
42.vue属性名和method名称一致出现什么问题
vue2中, 这个属性会覆盖掉 methods 中的方法。也就是说,这个方法将无法被正确调用。
vue3中,报错
43.class和style如何动态绑定
class 与 style 动态绑定一般通过对象或者数组来实现
对象写法:适用于要绑定的样式名字样式确定,但动态决定用不用。
数组写法:适用于要绑定的样式名字样式不确定。
<div v-bind:class="{ active: isActive }"></div> //对象写法 <div v-bind:class="[activeClass, errorClass]"></div> //数组写法
44.vue遇到的坑
- data必须是一个函数,而不是一个对象
- vue管理的函数不要写成箭头函数
- 添加属性页面不刷新
- 子路由path不需要添加**/**,path=‘new’
45.v-if和v-for 优先级
实践中不管是vue2或者vue3都不应该把v-if和v-for放在一起使用。
在 vue 2.x 中,在一个元素上同时使用 v-if 和 v-for 时, v-for 会优先作用。
在 vue 3.x 中, v-if 总是优先于 v-for 生效。
vue2中v-for的优先级是高于v-if的,放在一起,会先执行循环在判断条件,并且如果值渲染列表中一小部分元素,也得再每次重渲染的时候遍历整个列表,比较浪费资源。
vue3中v-if的优先级是高于v-for的,所以v-if执行时,它调用相应的变量如果不存在,就会导致异常
46.vue核心原理
数据驱动,组建系统
47.vue自带动画组件, transition
组件是 Vue 提供的用于包裹需要动画效果的元素组件。使用组件可以方便地实现元素的进入和离开动画效果。
<template> <div> <button @click="visible = !visible">Toggle</button> <transition name="fade"> <p v-if="visible">Hello, World!</p> </transition> </div> </template> <script> export default { data() { return { visible: false } }, } </script> <style> .fade-enter-active, .fade-leave-active { transition: opacity .5s; } .fade-enter, .fade-leave-to { opacity: 0; } </style>
48.vue-loader工作原理
- 将一个 .vue 文件 切割成 template、script、styles 三个部分。
- template 部分 通过 compile 生成 render、 staticRenderFns。
- 获取 script 部分 返回的配置项对象 scriptExports。
- styles 部分,会通过 css-loader、vue-style-loader, 添加到 head 中, 或者通过 css-loader、MiniCssExtractPlugin 提取到一个 公共的css文件 中。
- 使用 vue-loader 提供的 normalizeComponent 方法, 合并 scriptExports、render、staticRenderFns, 返回 构建vue组件需要的配置项对象 - options, 即 {data, props, methods, render, staticRenderFns…}。
49.vue的diff算法
diff整体策略为:深度优先,同层比较
- 当数据发生改变时,订阅者watcher就会调用patch给真实的DOM打补丁
- 通过isSameVnode进行判断,相同则调用patchVnode方法
- patchVnode
做了以下操作:
- 找到对应的真实dom,称为el
- 如果都有都有文本节点且不相等,将el文本节点设置为Vnode的文本节点
- 如果oldVnode有子节点而VNode没有,则删除el子节点
- 如果oldVnode没有子节点而VNode有,则将VNode的子节点真实化后添加到el
- 如果两者都有子节点,则执行updateChildren函数比较子节点
- updateChildren
主要做了以下操作:
- 设置新旧VNode的头尾指针
- 新旧头尾指针进行比较,循环向中间靠拢,根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点,从哈希表寻找 key一致的VNode 节点再分情况操作
50.vue和jquery区别
设计理念
Vue.js 是一个现代化的JavaScript框架,专注于构建大型的、可维护的Web应用程序。Vue.js 的核心是组件化,它提供了一种将页面分解成独立、可重用的组件的方式,并且能够非常容易地管理这些组件之间的依赖关系。同时,Vue.js 还内置了状态管理、路由、构建工具等功能,使得构建复杂的 Web 应用程序更加容易。
而 jQuery 则是一个早期的 JavaScript 库,主要关注的是 DOM 操作和处理事件。它的设计理念是将 JavaScript 代码尽可能地简单化,使得使用者可以很容易地完成一些常见的操作(如选择元素、修改样式、处理事件等)。在 jQuery 中,通过链式调用和函数式变成的设计,可以使得代码变得非常简洁易读。
用途
Vue.js 主要用于构建大型的、复杂的 Web 应用程序,它提供了诸如组件化、状态管理、路由等功能,非常适合构建单页面应用(SPA)。
而 jQuery 则更多地用于简化 DOM 操作和事件处理,它适用于编写小型的 Web 应用程序或较为简单的交互效果。同时,由于 jQuery 在浏览器兼容性、性能等方面的优势,它也被广泛应用于一些成熟的网站和CMS系统中。
51.说说你对 SPA 单页面的理解,它的优缺点分别是什么?
SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
- 优点:
用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
基于上面一点,SPA 相对对服务器压力小;
前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
- 缺点:
初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统加载,部分页面按需加载;
前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
52. 怎样理解 Vue 的单向数据流?
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。
这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。
这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
53. 父组件可以监听到子组件的生命周期吗?
比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:
// Parent.vue <Child @mounted="doSomething"/> // Child.vue mounted() { this.$emit("mounted"); }
以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示:
// Parent.vue <Child @hook:mounted="doSomething" ></Child> doSomething() { console.log('父组件监听到 mounted 钩子函数 ...'); }, // Child.vue mounted(){ console.log('子组件触发 mounted 钩子函数 ...'); }, // 以上输出顺序为: // 子组件触发 mounted 钩子函数 ... // 父组件监听到 mounted 钩子函数 ...
当然 @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。
54.Vue3.0 性能提升主要是通过哪几方面体现的
- diff算法优化
- 静态提升: Vue3中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用
- 移除一些不常用的API,再重要的是Tree shanking
- 响应式系统
55.什么是 MVVM?比之 MVC 有什么区别?什么又是 MVP ?
MVC 通过分离 Model、View 和 Controller 的方式来组织代码结构。 用户与页面产生交互时Controller 中的事件触发器就开始工作了,通过调用 Model 层,来完成对 Model 的修改,然后 Model 层再去通知 View 层更新。
MVVM 模式中的 VM,指的是 ViewModel, 它通过双向的数据绑定,将 View 和 Model 的同步更新给自动化了。当 Model 发生变化的时候,ViewModel 就会自动更新;ViewModel 变化了,View 也会更新。
MVP 模式 ,View 层的接口暴露给了 Presenter 因此我们可以在 Presenter 中将 Model 的变化和 View 的变化绑定在一起,以此来实现 View 和 Model 的同步更新
56.vue中hook和react中hook区别
在React中,hook是一种函数,它可以让你在函数组件中添加state、effect等功能。 React 中的hook有useState、useEffect、useContext等。使用hook可以避免使用类组件时可能会出现的繁琐的生命周期方法、this等问题。
在Vue中,hook被称为生命周期钩子函数,它们是在组件实例化过程中自动调用的回调函数。 Vue中的生命周期钩子函数包括beforeCreate、created、beforeMount、mounted等。它们可以用于控制组件的生命周期,以及在组件生命周期特定阶段执行特定的操作。
57.Redux和Vuex的区别
redux是一个范用的js库,vuex是专门服务vue的
相同点:
- state共享数据
- 流程一致:定义全局state,触发,修改state
- 原理相似,通过全局注入store。
不同点:
- Vuex定义了state,getter、mutation、action,module五个对象;redux定义了state、reducer、action;
- Vuex触发方式有两种commit同步和dispatch异步;redux同步和异步都使用dispatch;
- Vuex中action有较为复杂的异步ajax请求;redux中action中可简单可复杂,简单就直接发送数据对象({type:xxx, your-data}),复杂需要调用异步ajax(依赖redux-thunk插件)。
- Redux使用的是不可变数据,而Vuex的数据是可变的。Redux每次都是用新的state替换旧的state,而Vuex是直接修改;
- Redux在检测数据变化的时候,是通过diff的方式比较差异的,而Vuex其实和Vue的原理一样,是通过getter/setter来比较的。
58.vue服务端渲染(SSR),解决了哪些问题?
Vue服务端渲染(SSR)通过在服务器上预先生成Vue组件的HTML字符串,并将其发送到客户端,以实现更快的页面加载速度、更好的搜索引擎优化和更好的用户体验。服务端渲染解决了许多SPA(Single Page Application)应用程序中存在的问题,例如:
- SEO(搜索引擎优化)问题:由于传统的SPA应用程序是在浏览器中构建的,因此搜索引擎无法正确地索引它们的内容。使用Vue SSR,可以在服务器上呈现HTML字符串并向搜索引擎提供更好的友好的页面。
- 性能问题:SPA应用程序需要大量的JavaScript代码来初始化应用程序并交互。这可能导致页面加载时间缓慢,用户体验较差。使用Vue SSR,可以在浏览器中更快地呈现初始HTML的完整标志,并在其中嵌入必要的JavaScript。这样可以加快页面加载速度,并提高用户体验。
- 首屏渲染问题:传统的SPA应用程序在首次加载时可能会需要大量时间才能呈现第一个屏幕,直到JavaScript代码完成下载并执行。使用Vue SSR,可以在服务器上呈现组件,并将其作为HTML字符串发送到客户端,从而实现快速呈现首屏的目标。
59.Vue 3.0中Treeshaking特性是什么,并举例进行说明?
Tree shaking 是一种通过清除多余代码方式来优化项目打包体积的技术
Tree shaking是基于ES6模板语法(import与export),主要是借助ES6模块的静态编译思想,在编译时就能确定模块的依赖关系,以及输入和输出的变量
Tree shaking无非就是做了两件事:
- 编译阶段利用ES6 Module判断哪些模块已经加载
- 判断那些模块和变量未被使用或者引用,进而删除对应代码
作用:
- 减少程序体积(更小)
- 减少程序执行时间(更快)
- 便于将来对程序架构进行优化(更友好)
60. 虚拟 DOM 的优缺点?
优点:
- 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
- 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
- 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
缺点:
- 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
61. 虚拟 DOM 实现原理?
虚拟 DOM 的实现原理主要包括以下 3 部分:
- 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
- diff 算法 — 比较两棵虚拟 DOM 树的差异;
- pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。
62. Vue 中的 key 有什么作用?
key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。
Vue 的 diff 过程可以概括为:oldCh 和 newCh 各有两个头尾的变量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex,它们会新节点和旧节点会进行两两对比,即一共有4种比较方式:newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex,如果以上 4 种比较都没匹配,如果设置了key,就会用 key 再进行比较,在比较的过程中,遍历会往中间靠,一旦 StartIdx > EndIdx 表明 oldCh 和 newCh 至少有一个已经遍历完了,就会结束比较。
所以 Vue 中 key 的作用是:key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速!
- 更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。
- 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快,源码如下:
function createKeyToOldIdx (children, beginIdx, endIdx) { let i, key const map = {} for (i = beginIdx; i <= endIdx; ++i) { key = children[i].key if (isDef(key)) map[key] = i } return map }
62. Object.defineProperty怎么用, 三个参数?,有什么作用啊?
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
obj:需要定义属性的对象 prop:需要定义的属性 {}:要定义或修改的属性描述符。 value: "18", // 设置默认值 (与 get() 互斥) enumerable: true, //这一句控制属性可以枚举 enumerable 改为true 就可以参与遍历了 默认值false writable: true, // 该属性是否可写 默认值false (与 set() 互斥) configurable: true, // 该属性是否可被删除 默认值false get // 当有人读取 prop 的时候 get函数就会调用,并且返回就是 sss 的值 set // 当有人修改 prop 的时候 set函数就会调用, 有个参数这个参数就是修改后的值
63.reactive与ref的区别?
从定义数据角度对比:
- ref用来定义:基本类型数据
- reactive用来定义对象(或数组)类型数据
备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。
从原理角度对比:
- ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)。
- reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
从使用角度对比:
- ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。
- reactive定义的数据:操作数据与读取数据:均不需要.value。
64.v-on可以监听多个方法吗?
可以一个元素绑定多个事件的两种写法
<a v-on='{click:DoSomething,mouseleave:MouseLeave}'>doSomething</a> <button @click="a(),b()">点我ab</button>
65.vue3中如何获取refs,dom对象的方式?vue2中如何使用?
vue3:
(1) setup函数方法内,获取单个ref属性绑定的dom元素:先定义一个空的响应式数据ref定义的,你想获取哪个dom元素,在该元素上使用ref属性绑定该数据即可,通过ref.value即可获取到dom节点
(2) 获取多个ref属性绑定的dom元素。使用ref绑定一个函数,在函数里把dom添加到数组里面
//vue2 <h3 ref="myref">myref</h3> //获取 this.$refs.myref
66.shallowReactive和shallowRef的区别
(1) shallowReactive:只处理对象最外层的响应式(浅响应式)
(2) shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理。
(3) 应用场景:
① 如果有一个对象数据,结构比较深,但变化时候,只是外层属性变化,使用shallowReactive。
② 如果有一个对象数据,后续功能不会修改改对象中的属性,而是生成新的对象来替换,使用shallowRef。
67.provide与inject如何使用
(1) 父子组件传参可以通过props和emit来实现,但是当组件的层次结构比较深时,props和emit就没什么作用了。vue为了解决这个提出了Provide / Inject;provider/inject:简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量
(2) provide需要先引入,我们将需要传递给下级组件的变量通过provider(‘传输key’,变量)
(3) Inject,下级组件通过变量方式接收,person= inject(‘传输key’)
68.toRaw 与 markRaw是什么作用?
(1) toRaw :将一个由reactive生成的响应式对象转化为普通对象
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新.
(2) markRaw:标记一个对象,使其永远不会再成为响应式对象。
应用场景:有些值不应被设置为响应式的,例如复杂的第三方类的库,当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0gRb8BE4-1686967105050)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1685349361040.png)]
69.Readonly和shallowReadonly理解
readonly:让一个响应式数据变为只读的(深只读),readonly是一个函数,他会接收一个响应式的数据
shallowReadonly:让一个响应式数据变为只读的(浅只读),shallowReadonly只限制对象中的第一层数据(不能改动,如:salary),但是嵌套的深层次的value属性值 是可以更改的,我们点击更改按钮测试就能发现,被shallowReadonly包裹的对象的深层次值改变了。
70.toref和torefs区别
toRef 和 toRefs 可以用来复制 reactive 里面的属性然后转成 ref,而且它既保留了响应式,也保留了引用
toref(变量,属性),torefs(整个变量)
区别:
- 在插值表达式中{{}}:
- 访问 toRefs 的值,需要带上 .value 如果不带上,就会出现双引号 {{user.name.value}}
- 访问 toRef 的值,不需要带上 .value {{user.name}}
- 转换属性:
- toRef: 复制 reactive 里的单个属性并转成 ref
- toRefs: 复制 reactive 里的所有属性并转成 ref
71.学习 EventBus
首先,在你的项目中创建一个 eventBus.js 文件,并定义一个空的 EventBus 对象:
import Vue from 'vue'; export const EventBus = new Vue();
发送事件
import { EventBus } from './eventBus.js'; // 发送名为 'myEvent' 的事件 EventBus.$emit('myEvent', data);
接收事件
import { EventBus } from './eventBus.js'; EventBus.$on('myEvent', (data)=>{});
取消监听
EventBus.$off('myEvent');
72.vue2过滤器(vue3取消)
filter 过滤器是一种很常用的功能,它可以用于对数据进行格式化、排序、筛选等操作。在使用过程中,我们只需要在模板表达式中使用管道符 |,并将要使用的过滤器的名称作为参数传递进去即可。
全局过滤器:Vue.filter(‘过滤器名称’,function(){})
局部过滤器:filter选项
filters: { //filterName过滤器名,value是'|'之前的数据 filterName(value) { if (!value) return ''; return '你好'+value.toString() } }
73. vue可以通过计算属性监听计算属性吗
答案:不可以
计算属性依赖于其他属性值,所以我们可以在计算属性中监听这些属性值的变化,并执行一些相关的操作 。 但是,计算属性无法直接监听另一个计算属性的变化,因为一个计算属性的值不是响应式的,它依赖的属性值发生变化时只有它自己才会重新计算,而不会触发其他计算属性的更新