html+css+vue面试

  1. v-model的实现,双向绑定的原理

    我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:
    
    
    1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
    
    
    2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
    
    
    3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器
    
  2. vue的虚拟dom,为什么要用

    创建真实DOM的代价高:真实的 DOM 节点 node 实现的属性很多,而 vnode 仅仅实现一些必要的属性,相比起来,创建一个 vnode 的成本比较低。
    
    触发多次浏览器重绘及回流:使用 vnode ,相当于加了一个缓冲,让一次数据变动所带来的所有 node 变化,先在 vnode 中进行修改,然后 diff 之后对所有产生差异的节点集中一次对 DOM tree 进行修改,以减少浏览器的重绘及回流。
    
    虚拟dom由于本质是一个js对象,因此天生具备跨平台的能力,可以实现在不同平台的准确显示。
    
    Virtual DOM 在性能上的收益并不是最主要的,更重要的是它使得 Vue 具备了现代框架应有的高级特性。
    

Http

在浏览器中输入url回车之后发生了什么
由字母组成的url只是方便了我们自己的记忆,所以就需要把url解析为ip地址
-DNS就是把地址解析为ip地址的这一过程,其实dns就是一个数据库,这个数据库中记录着很多的url和对应的IP地址,这样就可以找到IP地址了
有了ip地址,我们就可以在互联网中找到指定的服务器
-虽然知道了具体的ip地址,但是在正式的发送数据之前还是得建立Tcp连接,建立TCP连接就是要在发送数据之前建立通道,目的是防止所有的数据都直接让服务器进行处理导致发生数据混乱,建立连接就是使用tcp的三次握手和四次分手
建立连接之后:浏览器会发送http请求报文给服务器
-报文的格式 由 请求行 请求头 空行 请求数据组成
请求行中包括请求方法,请求地址和HTTP协议版本组成
请求头 部就是一些关于浏览器的信息(键值对组成) 
-服务器:
当服务器收到HTTP报文之后会,会处理报文请求并且做出响应
报文的格式由状态行(404,200...),响应头部,空行和响应数据组成
响应头部:键值对
响应数据-
-浏览器在接受服务器的响应内容之后页面就开始进行渲染了(对html css js进行解析)
什么是http缓存
互联网中的缓存也是为了重复使用而被存储的,这样可以减少服务器和客户端之间通信的次数,降低网络延迟,加速页面加载,提高用户体验
http是一个协议
--了解http协议规定缓存是如何让工作,如何存储和各种类型等等事情(就是web缓存)
服务器将信息交给不同代理服务器以不同的形式进行缓存
缓存可以大大的减缓对于数据库的重读资源操作
http缓存的运作方式
1.请求的资源的时候看一下手否有缓存,如果没有就向服务器索取
2.如果有的话就查看缓存是否过期
3.如果没有过期就可以直接使用,如果过期了就得和服务器进行验证
4.如果服务器告知没有过期的话就可以继续使用,如果过期了就返回新的资源作为缓存

--header中有控制缓存的字段-Cache-Control
	含有max-age=31536000	以秒为单位告知最长缓存时间
	no-cache:可以进行缓存但是每次使用之前都要和服务器进行确认,代理服务也不能进行缓存
	private:标识资源只能给当前的服务器将进行缓存,其他什么服务器都不能进行缓存,相反的public
	
etag:用来标识资源,每次更新后,新的etag也会被服务器进行更新
Expires:表示缓存截至日期
Last-Modified:表示最近最后一次更改时间
Http
http的核心就是一个协议
服务端只有在客户端请求的时候才会响应
http规定了请求报文和响应报文的格式
-请求和响应报文的格式
请求行-方法(get。post。。)请求的url,请求的版本
响应行-http版本,状态码,原因短语
请求头-响应头
请求的主题
响应的主题
-get和post
get是一种获取的方法
post-偏向于传输提交数据的方法
--Content-type:数据类型
http支持传输任意数据类型
-报文是按照什么形式发送过去的呢?
把网络通信按照Tcp/ip模型划分,最顶层的两层是有应用层和传输层
应用层最靠近用户的那一层-每次输入内容网址前面都会有http或者HTTP协议的字样和后面带有80端口(目前浏览器自动补全,服务器默认是80端口)
浏览器可以根据我们输入的网址可以分析出服务器的地址和资源位置,这样就可以生成报文
http属于应用层,是直接提用户访问资源的一种协议
对于http来说重要的是传输层,一位内传输层是协议始来配合它定义传输数据的方式,就是在传输层中找到tcp协议进行传输
-目前http都是持久连接,发送一个请求后还能继续发送下一个请求
--服务器不会把请求的每个状态都记录下来,这就是http的无状态
无状态会导致,第一次访问登陆了,第二访问又需要输入信息登录,是麻烦的
即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;

tcp三次握手四次分手
存在于传输层-建立端到端的连接
ip地址加上端口号就组成了套接字socket
tcp报文里面又SYN,ACK和FIN等标识,如果设置为1就是开启,设置为0就是关闭这些标识
syn表示同步的意思,开启同步之后客户端就可以和服务端互相发送消息
第一次握手-客户端将SYN可seq(序号)发送给服务端,测试服务端的接受能力
第二次握手-服务端发送给客户端syn和ack(确认号)来确认同步,服务器发送的确认号就是根据对方的序号+1得到的
第三次握手-客户端可以进行确认,如果不确认,服务器不知道自己发送出去的“确认同步”是否被接受,于是必须在发送一次报文来时连接正式建立,这时候客户端会把ACK开启,序号就根据对方的确认号+1,确认号就是对方的序号+1;
服务器为了防止被攻击,所以服务器干脆不保存自己的序号,而是根据服务器的ip地址和端口号等私有信息进行算法的运算得到序号
--四次挥手
握手之后就建立来连接-这个时候客户端就可以发送HTTP请求了
当内容交流完毕后,各自可能就会发送关闭连接的要求,这个过程就是四次挥手-客户端和服务端都能主动发起关闭请求
假设时客户端主动发起的关闭要求
这时候客户端就会开启两个控制位FIN和ACK,FIN就是Finish结束的意思,就是结束会话
因为在发送HTTP请求和响应的时候序号和确认号被不断递增。所以这里就不用固定数字来表示序号和确认号了
此时服务器会发送一个ack来进行确认,自己的序号用对方的确认号,自己的确认号用对方的序号+1,虽然发送了序号和确认号,但是此时的客户端并未正式的关闭通道,因为服务端此时还可能又需要发送的数据
服务端发送完数据后会再发送一个FIN+ACK来进行最后的确认(此时序号和确认好不需要改变)
最后客户端得到最终的结束确认以后会发送ACK来进行确认,此时自己的序号要用到对方的确认号,自己的确认号是对方的序号+1
需要四次分手的原因是观察是否还有未发送完毕的数据
什么是csdn,CSDN能够做什么,我们为什么要了解
   
网络体系按照osi划分的话可以分为七层
1.物理层
2.数据链路层
3.网络层
4.传输层-建立端到端的连接
5.会话层
6.表示层
7.应用层
async和await
async 函数永远返回一个Promise
    使用 关键字 async 来表示,在函数内部使用await 表明当前函数是异步函数 不会阻塞线程导致后续代码停止运行
    不管在函数体内 return 了什么值, async 函数的实际返回值总是一个 Promise 对象. 
await:等待的意思
	await意思是async wait(异步等待)。这个关键字只能在使用async定义的函数里面使用。任何async函数都会默认返回promise,并且这个promise解析的值都将会是这个函数的返回值,而async函数必须等到内部所有的 await 命令的 Promise 对象执行完,才会发生状态改变

与简历补充-

  • 为何离职?—— 不要吐槽前东家,说自己的原因(想找一个更好的发展平台等)。
  • 能加班吗?—— 能!除非你特别自信,能找到其他机会。
  • 遇到不会的问题,要表现出自己积极的一面(不好意思哈,确实是我的知识盲区,可以跟我说下 xxx 吗,我回去研究一下)

1.自我介绍

  • 各位面试官,大家好

    我叫王宽,来自于聊城大学,大学期间是学的软件工程专业,我今天应聘的职位是web前端开发

    在校期间我们培训了HTML CSS JavaScript,在课余我喜欢通过逛掘金和csdn,github来了解一些前端的前沿的开发技术,并且通过自学学习了vue . 我有过半年的企业实习经验,做过几个项目有团队项目和个人项目,在实习期间我清楚的了解了开发的流程,也巩固了在大学学习的知识点,并且意识到团队合作的重要性。所以我很感谢这段实习的经历。我的职业规划是能否在一年之内技术有进一步的提升,五年之内能当上项目经理,以上就是我的自我介绍,

2.如何符合w3c规范?

  1. 所有的标签都使用小写字母
  2. 所有的属性值都放在引号里
  3. 确保所有成对标签出现的顺序、不成对的标签都用/>结束, ”/”和”>”之间不要有空格
  4. 为图片添加alt
  5. 关闭所有的标签

3.计算机网络

  • GET:通用获取数据
  • HEAD:获取资源的元信息
  • POST:提交数据
  • PUT:修改数据
  • DELETE:删除数据

4.你对计算机网络的认识怎么样

应用层、表示层、会话层、传输层、网络层、数据链路层、物理层

5.问:HTTPS 是什么?具体流程

HTTPS 是在 HTTP 和 TCP 之间建立了一个安全层,HTTP 与 TCP 通信的时候,必须先进过一个安全层,对数据包进行加密,然后将加密后的数据包传送给 TCP,相应的 TCP 必须将数据包解密,才能传给上面的 HTTP。

浏览器传输一个 client_random 和加密方法列表,服务器收到后,传给浏览器一个 server_random、加密方法列表和数字证书(包含了公钥),然后浏览器对数字证书进行合法验证,如果验证通过,则生成一个 pre_random,然后用公钥加密传给服务器,服务器用 client_random、server_random 和 pre_random ,使用公钥加密生成 secret,然后之后的传输使用这个 secret 作为秘钥来进行数据的加解密。

6.问:三次握手和四次挥手

为什么要进行三次握手:为了确认对方的发送和接收能力。

  • 三次握手

三次握手主要流程:

  • 一开始双方处于 CLOSED 状态,然后服务端开始监听某个端口进入 LISTEN 状态
  • 然后客户端主动发起连接,发送 SYN,然后自己变为 SYN-SENT,seq = x
  • 服务端收到之后,返回 SYN seq = y 和 ACK ack = x + 1(对于客户端发来的 SYN),自己变成 SYN-REVD
  • 之后客户端再次发送 ACK seq = x + 1, ack = y + 1给服务端,自己变成 EASTABLISHED 状态,服务端收到 ACK,也进入 ESTABLISHED

SYN 需要对端确认,所以 ACK 的序列化要加一,凡是需要对端确认的,一点要消耗 TCP 报文的序列化

  • 为什么不是两次?

无法确认客户端的接收能力。

如果首先客户端发送了 SYN 报文,但是滞留在网络中,TCP 以为丢包了,然后重传,两次握手建立了连接。

等到客户端关闭连接了。但是之后这个包如果到达了服务端,那么服务端接收到了,然后发送相应的数据表,就建立了链接,但是此时客户端已经关闭连接了,所以带来了链接资源的浪费。

  • 为什么不是四次?

四次以上都可以,只不过 三次就够了

  • 四次挥手

  • 一开始都处于 ESTABLISH 状态,然后客户端发送 FIN 报文,带上 seq = p,状态变为 FIN-WAIT-1

  • 服务端收到之后,发送 ACK 确认,ack = p + 1,然后进入 CLOSE-WAIT 状态

  • 客户端收到之后进入 FIN-WAIT-2 状态

  • 过了一会等数据处理完,再次发送 FIN、ACK,seq = q,ack = p + 1,进入 LAST-ACK 阶段

  • 客户端收到 FIN 之后,客户端收到之后进入 TIME_WAIT(等待 2MSL),然后发送 ACK 给服务端 ack = 1 + 1

  • 服务端收到之后进入 CLOSED 状态

客户端这个时候还需要等待两次 MSL 之后,如果没有收到服

7.Promise的all和race有什么区别

#### Promise.all和Promise.race的介绍

- 相似点:
  - 这两个都是`Promise`的方法,并且传入的参数都是一个`Promise`的数组。
  - 都会返回一个`Promise`实例
- 区别:
  - all: 传入的所有`Promise`最终都转化为**fulfilled**态时,则会执行**resolve**回调,并将返回值是的所有的`Promise`的`resolve`的回调的**value**的数组。其中一个任何`Promise`为**reject**状态时,则返回的`Promise`的状态更改为**rejected**。
  - race: 传入的所有`Promise`其中任何一个有状态转化为**fulfilled**或者**rejected**,则将执行对应的回调。

8.堆和栈的区别

栈
2.1 简介
栈 是一种遵循 后进先出(LIFO) 原则的有序集合。新添加和待删除的数据都保存在栈的同一端栈顶,另一端就是栈底。新元素靠近栈顶,旧元素靠近栈底。 栈由编译器自动分配释放。栈使用一级缓存。调用时处于存储空间,调用完毕自动释放。
-javaScript中,数据类型分为基本数据类型和引用数据类型,基本数据类型包含:string、number、boolean、undefined、null、symbol、bigint这几种。在内存中这几种数据类型存储在栈空间,我们按值访问。
-执行栈(函数调用栈)
我们知道了基本数据结构的存储之后,我们再来看看JavaScript中如何通过栈来管理多个执行上下文。
程序执行进入一个执行环境时,它的执行上下文就会被创建,并被推入执行栈中(入栈)。
程序执行完成时,它的执行上下文就会被销毁,并从栈顶被推出(出栈),控制权交由下一个执行上下文。
JavaScript中每一个可执行代码,在解释执行前,都会创建一个可执行上下文。
-全局可执行上下文:每一个程序都有一个全局可执行代码,并且只有一个。任何不在函数内部的代码都在全局执行上下文。
-函数可执行上下文:每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都被调用时都会创建它自己的执行上下文。
--创建一个栈(实现栈方法)
我们需要自己创建一个栈,并且这个栈包含一些方法。
push(element(s)):添加一个(或多个)新元素到栈顶
pop():删除栈顶的元素,并返回该元素
clear():清空栈

 堆内存
JavaScript 的数据类型除了原始类型,还有一类是 Object 类型,它包含:

Object
Function
Array
Date
RegExp

Object 类型都存储在堆内存中,是大小不定,复杂可变的。
Object 类型数据的 指针 存储在栈内存空间, 指针实际指向的值存储在堆内存空间。
 为什么会有堆内存、栈内存之分
通常与垃圾回收机制有关。为了使程序运行时占用的内存最小。
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;

队列

队列
4.1 简介
队列遵循FIFO,先进先出原则的一组有序集合。队列在尾部添加元素,在顶部删除元素。
任务对垒
JavaScript是单线程,单线程任务被分为同步任务和异步任务。同步任务在调用栈中等待主线程依次执行,异步任务会在有了结果之后,将回调函数注册到任务队列,等待主线程空闲(调用栈为空),放入执行栈等待主线程执行。

9.transition、transform、translate的区别

–第一章 面试基础篇

1.1 HTML面试题

行内元素有哪些?块级元素有哪些?空(void)元素有哪些?
行内元素:a、b、span、img、input、strong、select、label、em、button、textarea
块级元素:div、ul、li、dl、dt、dd、p、h1-h6
空元素:即系没有内容的HTML元素,例如:br、meta、hr、link、input、img

如何设计到元素之间的转换(扩展)

display:block 			//切换到块级
display:inline-block 	//切换到行内块
display:inline 			//行内元素
页面导入时,使用link和@import有什么区别?
1.link 是一个标签,@import是css中的内容
2.link先加载,@import后加载(重点)
	页面被加载时,link 会同时被加载,而 @import 引用的 CSS 会等到页面被加载完再加载
3.兼容问题
	import 是 CSS2.1 提出的,只在 IE5 以上才能被识别,而 link 是 XHTML 标签,无兼容问题。
共同的作用是引入样式表的
title与h1的区别,b与strong的区别,i与em的区别
title标签和h1标签的区别:
 1.title是网页标题,h1是内容
 2.在做网页seo的层面上 title > h1 [重点]
 
b和strong的区别:
 1.语义化
 2.b只是一个加粗的标签,没什么特殊含义,strong是一个加粗,但是又特殊含义(强调:阅读器和seo的层面上体现出来)
 3.在未来b标签可能会被移除

i和em的区别:
 1.语义化
 2.都是倾斜,i没有特殊含义,em有特殊含义,i现在一般都用来做图标了
img标签的title和alt有什么区别?
title:鼠标移入显示的问题
alt:图片没有加载显示的文字

在做seo,alt属性是可以解决蜘蛛抓取不到图片的问题
补充:
在做网页的时候,logo图使用h标签进行包裹,并且img中使用alt属性,以便进行搜索引擎优化
png,jpg,gif这些图片格式解释一下,分别什么时候用?
jpg,jpeg  :适合大图片(banner),同样的相对于png体积小一点。【压缩会失帧,没有png清晰】
png       :适合小图标(图标),同样的图片体积大			【不怎么失帧】
gif		  :动图
HTML语义化是什么?
HTML语义化就是让页面内容结构化,它有如下优点
1、易于用户阅读,样式丢失的时候能让页面呈现清晰的结构。
2、有利于SEO,搜索引擎根据标签来确定上下文和各个关键字的权重。
3、方便其他设备解析,如盲人阅读器根据语义渲染网页
4、有利于开发和维护,语义化更具可读性,代码更好维护,与CSS3关系更和谐

例如如下标签

<header>代表头部
<nav>代表超链接导航栏区域
<main>定义文档主要内容
<article>可以表示文章、博客等内容
<aside>通常表示侧边栏或嵌入内容
<footer>代表尾部
前端浏览器常见兼容性问题及解决方案
1. 最常见的:每个浏览器的默认margin,padding大小都不同,当设置定位时会有些许差异。
	解决方案:这也是最容易解决的, 在css样式文件中重置margin,padding
*{
    margin:0;
    padding:0;
}
2. 图片默认有间距:当几个img标签放到一起时,有些浏览器会有默认间距,加上第一条的设置的通配符样式也无用。
       解决方式:使用float属性让其浮动,消除间距

3. 边距重叠问题:当相邻的两个元素都设置了margin边距时,margin会取最大值,舍弃最小值。
 解决方案:为了不让边重叠,可以给子元素增加一个父级元素,并设置父级元素为overflow:hidden
href和src的区别
href:超文本引用或参照
src:是资源的缩写
使用 href的属性:link和a元素
使用src的元素:img,style,script,input
href 建立一个连接通道,这个连接通道连接着其他资源
src 引用的资源直接成为当前文档的一部分,并且这个会下载资源绑定到文档上,并且浏览器下载的过程中遇到这个属性会暂停其他资源的下载和处理,直到这个资源下载和处理完成
总结:href是一个超链接,把当前文档和资源进行连接,引用资源的时候通过通道就可以进行引用
	 src会把资源下载下来代替当前的元素,然后嵌入到文档中
css单位rem和em的区别,vh和vw的区别
rem比em多了一个r,这个r代表root根的意思

浏览器默认字体大小为16px,可以通过html标签来设置字体的大小

rem是和根有关系的,rem字体大小就是根据根元素中的字体大小来确定的

em 和根没有太大关系,在没有设置字体大小的适合,1em直接是浏览器默认字体的大小,em会继承父元素font-size的大小

vh是相对于可视区域的高度,100vh就是可视区域的高度

vw,viewpoint width的缩写,视窗宽度,1vw等于视窗宽度的1%。

举个例子:浏览器宽度1200px, 1 vw = 1200px/100 = 12 px。

vm,相对于视口的宽度或高度中较小的那个。其中最小的那个被均分为100单位的vm

举个例子:浏览器高度900px,宽度1200px,取最小的浏览器高度,1 vm = 900px/100 = 9 px。
line-height
行距:在字体的上下部分有行距存在,行距有上下部分之分,上面的是上半行距

行距 = line-height - font-size
line-height有三种赋值方式,数字,长度,和百分比

line-height:100%;的计算方式是,先得到这个字体的大小,使用100%和字体大小相乘,得到行高的值

在body中设置line-height,则它的子元素标签会继承它的属性

设置line-height:建议使用数字的形式,它的值为2的话,那么行高的值为2*font-size
line-height垂直居中
当line-height和height的高度相同时,会使得内部的行内元素垂直居中

1.2 css面试题介绍

在网页中应该使用奇数还是偶数字体?为什么呢?
偶数:文字是偶数的情况,表现更好看

偶数在页面的表现层上更加的饱满,好看
css外边距塌陷

外边距塌陷的初衷就是为了,设置margin-top的时候这个会出现一个上外边距,但是当下部也需要外边距的时候,可以直接使用margin-bottom,方便使用

开始目的是解决段落之间的空隙问题

什么时候会出现外边距塌陷

1.垂直方向
2.块级元素,不是行内元素,也不是行内块元素

外边距计算

1.如果是两个正数 则取最大的数

2.两个负数 取绝对值最大的数

3.一正一负的话取两个数相加的和

父子元素也会与这个问题,
父元素设置外顶边距,和子元素设置外顶边距,最终的顶边距也会按照上述的外边距计算原则

**解决父子外边距合并的办法**

1. 给父盒子设置相对定位,给子盒子设置绝对定位,之后给子盒子加上一个margin值
2.行内块元素不会发生外边距塌陷:这样可以给一个盒子设置成行内块元素:display:inline-block
 注意:一定要为行内块元素设置宽高
3.子盒子使用相对定位
 注意:使用相对定位就不能使用margin-top,只能使用top值
4.給子盒子添加浮动
5.给父盒子添加overflow:hidden;这样有一个缺陷,就是容易隐藏掉一些东西
6.使用内边距-给父盒子添加顶部内边距
7.给父盒子添加一个边框
对BFC规范(块级格式化上下文:block formatting context)的理解?
起到隔离保护的作用,形成独立的渲染区域,内部元素渲染不会影响到外界
实际工作中主要是使用BFC解决问题的,一般问,怎么使用BFC解决各种问题?

**创建BFC的方式**
1.使用positation定位,positation:absolute或者fixed
2.float:不是none
3.overflow:不是visible
4.display:inline-block或者flex

应用场景;清除浮动等
要把文字包裹图片,是给图片添加float布局
设置图片和文字都设置浮动,这时候二者都会脱离正常的文档流,实际上浮动元素本身就是一个BFC,浮动起来后自己就是一个隔离区,解决这个浮动问题就需要使用bfc,设置隔离区域,把里面的元素隔离起来,防止外部内容进入,也就是常说的清除浮动
可以给它们的父元素添加overflow来触发BFC,比如overflow:auto;这时候二者就回到了正常的文档流了,overflow这里可以使用auto,hidden,scroll都可以,但是要消息hiddden,会把超出的元素进行隐藏

第二种方法,给父盒子设置display:flex;或者添加添加display:inline-block;;这时候父盒子会把子盒子强行包裹起来

比如父子盒子内外边距塌陷的问题,我们可以使用display:inline-block;
css清除浮动使子盒子支撑起父盒子
子元素设置浮动之后,父盒子没有设置高度,导致父盒子不能被子和支撑起来
可以设置父盒子的高度,但是这里并不知道后面要添加多少子元素,所以父盒子的高度不确定,后期不容易维护

使用清除浮动来处理这种糟糕的布局
1.在最后加上一个空标签,给这个标签加上clear:both;,理解起来容易,但是会产生很多空标签,浪费大量资源
3.案例中经常设置一个cleafix的css类,需要清除浮动的时候给父级盒子添加这个类
.clearfix:after {
    content: "";
    /* 在后面加上一个元素并且必须是块级元素 */
    display: block;
    /* 设置不看见这个元素 */
    height: 0;
    /* 清除浮动的核心 */
    clear: both;
    /* 将这个元素隐藏 */
    visibility: hidden;
}
如何让Chrome支持小于12px的文字?
涉及到过度中的缩放属性
transform:scale(0.8)
怎么添加、移除、复制、创建、和查找节点

创建新的节点

createElement()  //创建一个具体的元素
createTextNode()  //创建一个文本节点

添加,移除,替换,插入

appendChild()  //添加
removeChild()  //移除
replaceChild()  //替换
insertBefore()  //插入

查找

getElementsByTagName()  //通过标签名称
getElementsByName()   //通过元素的Name属性的值
getElementById()		//通过元素ID,唯一性
document.querySelector() //可以查找id,name,标签名
css 水平居中
设置行内元素水平居中:
	设置其父元素text-align:center(text:文字。align排列的意思)(垂直排列使用line-height)
设置块级元素水平居中:
	设置宽度(块级元素不设置宽度的话默认占满一栏,这样居中就没效果)➕margin:0 auto 
设置浮动元素居中:
	通过位置,position:relative;left:50%;transform:translateX(-50%)
	
注意:行内元素加了绝对定位的话会使它变成块级元素
css垂直居中
1.使用padding来设置垂直居中
	优点:简单,只需设置上下内边距即可
	缺点:不能设置父元素的垂直高度
2.设置父元素高度的情况下使用line-height属性
span标签可以直接垂直居中,但是改为p标签之后就不垂直居中了,这是因为p标签有一个外边距,我们只需要设置外边距为0就可以
可以使用父元素设置固定高度。但是这种垂直方法一般适用于单行文字,但是line-heght有一个致命的因素,当有多行文本的时候,它就控制不住了

3.使用flex布局
display:flex;
flex-direction:column;
justify-content:center; //父元素设置
align-items:center;

优点:简单易懂,有兼容性问题ie9+
4.使用position布局

父元素设置相对布局,子元素设置绝对布局
position:absolute;
top:50%;
tasform:translateY(-50%)
但是绝对定位会脱离文档流

5.使用vertical-align

设置为inline-block或者inline在设置vertical-align
但是vertical-align不能影响块级元素中文本的对齐方式,但是可以控制单元格中元素的垂直方向对齐方式
当一个元素只有这一个元素没有其他单元格可以对比的话,这个垂直的属性就不会生效,解决的话可以给他设置一个兄弟元素,作为一个单元格进行对照
CSS选择器有哪些?哪些属性可以继承?
CSS选择符:
id选择器(#myid)
类选择器(.myclassname)
标签选择器(div, h1, p)
相邻选择器(h1 + p)
子选择器(ul > li)
后代选择器(li a)
通配符选择器(*)
属性选择器(a[rel=”external”])
伪类选择器(a:hover, li:nth-child)

可以继承的:
    font-size    font-weight    font-style    font-family    color
浏览器是怎样解析CSS选择器的?

CSS选择器的解析是从右向左解析的。若从左向右的匹配,发现不符合规则,需要进行回流,会损失很多性能。若从右向左匹配,先找到所有的最右节点,对于每一个节点,向上寻找其父节点直到找到根元素或满足条件的匹配规则,则结束这个分支的遍历。两种匹配规则的性能差别很大,是因为从右向左的匹配在第一步就筛选掉了大量的不符合条件的最右节点(叶子节点),而从左向右的匹配规则的性能都浪费在了失败的查找上面。
而在 CSS 解析完毕后,需要将解析的结果与 DOM Tree 的内容一起进行分析建立一棵 Render Tree,最终用来进行绘图。在建立 Render Tree 时,浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果(Style Rules)来确定生成怎样的 Render Tree。

ul li span.red{ color :red }

以前误以为是 先找ul 再找 li 然后找 li 中 类名为 red 的span 给画上颜色

其实事实上 先找到 类名为 red 的span 给画上红色, 然后 向上找 li 标签,再往上找出 ul 标签, 然后针对规则,画上红色

如何使用css绘制三角形
绘制出一个三角形我们常用的就是隐藏三个边框,只保留一个边框,其他的三个边框设置为透明
border:250px solid transparent;
border-bottom-color:red;
伪类和伪元素
常见的伪类
状态类::link(未点击的时候),:visited,:hover,:active,:focus
结构类::first-child,:last-child,:nth-child,:nth-of-type
表单类::checked,:disabled,:required

常见的伪元素
::before,::after

区别:
伪类用于当已有元素处于的某个状态时,为其添加对应的样式,这个状态是根据用户行为而动态变化的。
伪元素用于创建一些不在文档树中的元素,并为其添加样式。
设置超文本替换为省略号的方法
1.设置宽度
2.强行文本在一行上显示
	white-space:nowrap:文本超出的地方不换行
3.隐藏溢出内容
	overflow:hidden
4.溢出进行省略
	text-overflow:ellipsis;把文本溢出的部分使用省略号进行省略
display none,visibility hidden ,opacity 0 的区别
display none设置隐藏,隐藏后不占有位置,给父元素设置隐藏后子元素也会直接隐藏,这时候即使我们直接给子元素设置display:block;也是没有办法显示的,会发生回流

visibility hidden:设置隐藏后依旧占有原位置,设置了这个hidden和display:none之后自身的事件无法触发了,可以设置子元素的复原,使用visibility:viiable,会发生重绘

opacity 0;是设置了透明度为0,它的自身事件还是可以触发的
重绘:
DOM树没有元素增加或删除,只是样式的改变,针对浏览器对某一元素进行单独的渲染,这个过程就叫做重绘
回流:
DOM树中的元素被增加或者删除,导致浏览器需要重新的去渲染整个DOM树,回流比重绘更消耗性能,
	当改变影响文档内容或者结构,或者元素位置时,回流操作就会被触发,一般有以下几种情况:
        1:改变窗口大小
        2:改变文字大小
        3:内容的改变,如用户在输入框中敲字
        4:激活伪类,如:hover
        5:操作class属性
        6:脚本操作DOM
        7:计算offsetWidth和offsetHeight
        8:设置style属性
发生回流必定重绘,重绘不一定会导致回流。
css不使用border创建边框

这个面试题实际上是考box-shadow

box-shadow形成其他形式的边框

box-shadow常见的用法就是两个数据加一个颜色

box-shadow:x轴偏移 y轴偏移 颜色

全部的简写形式

box-shadow:x轴偏移 y轴偏移 模糊半径 扩散半径 颜色 inset

当input获得焦点的时候给input设置显示阴影边框
input:focus{
border:none;
padding-bottom:0;
box-shadow:0 0 0 5px red;
}
还可以使用阴影设置内边框,使用insert
Flexbox布局
要设置flexbox就首先要为父元素设置display:flex
	主要的意思是为父元素设置伸缩效果,使得父元素里面的子元素产生相应的效果,也就是说子元素也是可以设置对应的flex效果
flex是按照两个轴进行排列的,重要的就是这两个轴,默认从左到右是主轴
	垂轴默认从上到下

设置主轴方向使用 justify-content:center;居中,垂直方向居中,align-items:center
设置那个是主轴使用flex-direction:row;(默认),设置为是垂直方向是column

space-around 平分剩余空间。space-between 先两边贴边,再平分剩余空间(重要)

设值flex-wrap:wrap;当一项显示不下的时候实现两栏布局

align-self;只给这个元素添加效果
css calc()应用场景

假如使用padding把图片推向页面的中间的话,需要根据图片的宽度来计算padd的值

calc是支持多个不同的单位进行混合运算的,一次还可以进行多个运算。也可以进行嵌套运算

组合不同单元; .foot元素总是小于它父元素宽度 50px; //使用 calc(),

计算值是表达式它自己,而非表达式的结果 .foot { width: calc(100% - 50px); }

什么是css reset
reset.css === 》 重置css样式的
	非要说它的问题:体积大了一点点
normalize.css 是现在用的比较多的,但是和reset有区别
	是干什么:为了增强跨浏览器渲染的一致性(一定程度上),维护的一个css 重置样式库
css sprite是什么,有什么优缺点
雪碧图 === 精灵图

1.是什么:把一堆小图标放在一堆大图片上
2.优点:减少了http的请求,做了性能上的优化
3.缺点:维护性,修改性较差
使用的时候
div{
	width:17px;
	height:17px;
	background:url("./xxx.png");
	background-position: -2px -44px;
}
position有哪些值?有什么作用
static【默认】 没有定位
fixed 相对于浏览器窗口可视区域进行定位
absolute 相对于第一个拥有relative的父元素进行定位
relative 相对于自身进行定位

absolute 和 relative 的区别?
1.absolute脱离文档流,relative不脱离文档流
2.relative只有两个值(left,right,top,bottom如果同时存在left干掉right,top干掉bottom),absolute可以写四个值
补充:
1.浏览器默认的排版方式就是文档流(或者叫标准流)排版方式。脱离文档流就是不按照文档流的排版方式。
 文档流的排版方式是:块级元素垂直排布,行内元素和行内块级水平排布。不脱离就是按照这种方式排版,从左到右,从上到下。
2.position:absolute之后float应该就不起作用了
一个盒子不给宽度和高度如何进行水平垂直居中
1.弹性盒(display:flex)布局
	//注:flex的垂直居中的时候会直接把中轴剧中到中间位置,不用再次进行调整
2.定位 + transfrom:translate(-50%,-50%)
介绍一下css盒子模型
CSS的盒子模型有哪些:标准盒子模型、IE盒子模型
CSS的盒子模型区别:
	标准盒子模型:margin、border、padding、content
	IE盒子模型 :margin、content( border +  padding  + content )
			content就是给定的盒子的最终宽度
通过CSS如何转换盒子模型:
	box-sizing: content-box;	/*标准盒子模型*/
	box-sizing: border-box;	  /*IE盒子模型*/
display:node;与visibility:hidden;的区别
1.display:none不占位置,visibility:hidden占位置
	原理:visibility:hidden进行第一次绘制,
	display: none第一次不绘制在其显示的时候才会进行绘制,这种回流的问题是无法解决
2.display:none重绘和回流。visibility:hidden重绘

触发回流必定会触发重绘
触发重绘不一定会触发回流
css Web安全色

页面中的色素位是红色两位,绿色两位,蓝色两位,表示明暗程度的两位,总共组成2的8次方种,也就是组成256种颜色

有40种保留色,216种安全色

web安全色的定义就是使用6种红色,6种绿色,6种蓝色进行组合:6x6x6=216种组合,比如#006699,可以简写。但是页面种支持的颜色超过256种

css设置等高布局

等高布局就是左边和右边两边的高度不一致的问题

如果不考虑兼容性的问题可以使用flex布局
给父盒子添加display:flex; flex-deriction:row;
子盒子设置:flex:1;

可以给要设置等高布局的元素添加

div.left{
    padding-bottom:9999px;
    margin-bottom:-9999px;
}
二者共同的父元素设置
section{
    //设置了BFC
    overflow:hidden;
}

可以采用视觉欺骗的方式

给左右两边较低的那个高度的背景颜色设成父盒子的背景颜色
浏览器是如何渲染页面的
1.浏览器将从服务器获取的HTML文档构建成文档对象模型DOM树.
2.样式将被载入和解析,构成层叠样式表模型CSSOM(CSS树).cssom树不包含围在屏幕上打印的元素 
3.把cssom树和DOM树结合在一起,渲染树(rendering tree)将会被创建,代表一系列将被渲染的对象。渲染树映射除了不可见元素(例如**<head>**或者含有**display:none;**的标签和尺寸为0的元素)外的所有DOM结构。每一段文本字符串都将划分在不同的渲染对象中,每一个渲染对象都包含了它相应的DOM对象以及计算后的样式。换句话讲,渲染树是DOM的直观表示。
4.渲染树的每个元素包含的内容都是计算过的(元素的大小和在元素上的位置),它被称之为布局layout.浏览器使用一种流式处理的方法,只需要一次pass绘制操作就可以布局所有的元素。
5.最后布局完成,渲染树将转化为屏幕上的实际内容,这一步被称为绘制painting.
http常见的状态码
> **1xx**
(Informational) 信息性状态码,表示正在处理。

> **2xx**

(Success) 成功状态码,表示请求正常。

- 200 ok 请求被成功处理。
- 204 No Content 该状态码表示服务器接收到的请求已经处理完毕,但是服务器不需要返回响应体。
- 206 Partial Content 该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求。

> **3xx**

(Redirection) 重定向状态码,表示客户端需要进行附加操作。

- 301 Moved Permanently 永久性重定向。
- 302 Found 临时性重定向。

> **4xx**

(Client Error) 客户端错误状态码,表示服务器无法处理请求。

- 400 Bad Request 指出客户端请求中的语法错误。
- 401 Unauthorized 该状态码表示发送的请求需要有认证。
- 403 Forbidden 该状态码表明对请求资源的访问被服务器拒绝了。
- 404 Not Found 该状态码表明服务器上无法找到指定的资源。

> **5xx**

(Server Error) 服务器错误状态码,表示服务器处理请求出错。

- 500 Internal Server Error 该状态码表明服务器端在执行请求时发生了错误。
- 502 Bad Gateway 该状态码表明服务器网关错误。
- 503 Service Unavailable 该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
一次完整的http事务是怎么一个过程

1.域名解析

2.发起TCP的3次握手

3.建立TCP连接后发起http请求

4.服务器响应http请求

5.浏览器得到html代码

6,浏览器解析html代码,

7.并请求html代码中的资源(如js、css、图片等)

8.浏览器对页面进行渲染呈现给用户

列举了解的HTML5,CSS3的新特性
对于目前市场上来说HTML5中需要掌握的新特性有:
1.语义化更好的内容标签(header,nav,footer,aside,article,section)
2.音频、视频(audio,video)
3.本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
4.sessionStorage 的数据在浏览器关闭后自动删除
5.表单控件date、time、email、url、search

**CSS3**
在回答这个问题的策略:先告诉面试官CSS3在CSS的基础上面,不仅添加了新的属性还有选择器也做了扩展。
接下来开始列举:
1)先从颠覆性的属性开始比如:rotate(旋转),translate(位移),scale(缩放)等
2)Css3的动画 animation
3)还有是css3的过渡, transition: all 1s;
4)再列举一些简单的属性比如说: border-radius(圆角),box-shadow(盒子阴影),border-shadow(边框阴影)等
5)新增了结构伪类选择器,像 first-child,nth-child(n)之类的

1.3 HTML和CSS基础的话

观看xmind文档中的HTML1-7

1.4 js面试题

js的数据类型有哪些
基本类型
null undefined number string boolean
引用数据类型统称为Object类型,细分的话有
Object Array Date Function RegExp

基本数据类型的数据直接存储在栈中;而引用数据类型的数据存储在堆中,在栈中保存数据的引用地址,这个引用地址指向的是对应的数据,以便快速查找到堆内存中的对象。
顺便提一句,栈内存是自动分配内存的。而堆内存是动态分配内存的,不会自动释放。所以每次使用完对象的时候都要把它设置为null,从而减少无用内存的消耗

类型转换

1.使用减号可以把其他类型转为number类型,或者前面单独一个加号
null和undefined的区别
在==运算符中认为null与undefined相等,当然在===运算符中认为null与undefined是不相等的。
作者先设计的null后设计的undefined
1. 标识无的值最好别是对象,null的typeof是object
2. null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。

作者设计了undefined来填补自己设计null的坑
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义
js类型之间的转化
显式转换:转型函数 Boolean(),Number(),parseInt(),parseFloat(),.toString(), String()
隐式转化:
	1.转为string类型的 num  +  ""
	2.隐式转为字符串: var str="123";--var num=str-0;
	3.隐式转为boolean类型:var bool=!!str;
js函数传参形式(值传递or引用传递)
在ECMAScript中所有的函数的参数都是按值传递的。就是把函数外部的值复制给函数 内部的参数,就是把值从一个变量复制到另一个变量一样。基本数据类型的值传递如同基本类型变量的复制一样,而引用类型值得传递,则如同引用类型变量的复制一样。

基本类型值
function add(num){
    num += 10;
    return num;
}
var count = 20;
var result = add(count);
console.log(result); // 30
console.log(count); // 20 ,没有变化
引用类型值
function setName(obj){
    obj.name = '嘉明';
    obj = new Object();
    obj.name = '庞嘉明';
}
var person = new Object();
setName(person);
JS 作用域考题
1. 作用域链
先在内部找,内部找不到就去外部找
不能外部向内部找
2.变量提升【悬挂声明】
3.优先级
	变量 > 声明函数【不看书写的顺序】 > 参数 > 提升
	
**注意
1.window.xxx 或者 xxx(前面没有var。let。const)
2.除函数外,其他的没有作用域(if,for,switch...)
笔试题练习
var a = 5;
function test(){
 
 	a = 0;//这里的a对变量提升的a进行重新的赋值
 	console.log( a ); // 0
 	console.log( this.a ) // 5,普通函数中的this指代的是window,所以直接去外进行查找
 	var a;
 	console.log( a )//这里的a已经进行了重新赋值了
}
笔试练习题2
对象在原型中的查找

//函数是对象
function Fun(){
	this.name = "1"; //B
}

Fun.prototype.name = "2"; //D
let o = new Fun();
o._proto_.name="3" //C
o.name = "4" // A
查找的途径
	1. 查找对象的本身
	2. 查找构造函数
	3. 对象原型找
	4. 构造函数原型找
笔试练习题3
function foo(){
	getName = function(){console.log(1)} //全局变量声明了一个值
	return this
}
foo.getName = function(){console.log(2)}
foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)} //声明函数的类型
function getName(){console.log(5)} //变量的优先级大于函数所以这一函数基本没有用到

foo.getName();//2
getName();//4
foo().getName();//1
getName();//1
new foo().getName(); //3
js判断变量是不是数组
typeof千万别写

1.Array.isArray()
	let arr1 = [1,2,3]
	console.log(Array.isArray(arr1));
	
2.constructor
	let arr2 = [1,2,3];
	console.log(arr.construtor.toString().indexOf('Array'))
	
3.instanceof
	let arr = [1,2,3]
	console.log(arr instanceof Array)
JavaScript运算符typeof和instanceof的区别
**typeof 检测数据类型**输出这个类型的名字

检测出的类型有 undefined,Null,Boolean,Number和String
复杂数据类型 Object

如果是function函数返回的是function类型的,数组和对象都返回object类型,使用new出来的也是返回object类型

特点:返回一个小写字母的类型字符串,只需要一个操作数,操作数可以是 简单数据类型或者函数或者对象
instanceof 检测对象之间的关联性
instanceof 的左边一定要是实例
instanceof 的所有引用类型的值都是一个实例,不是基本类型,所以左边的操作数一定要是一个引用类型值 
说一下ES6新增特性?
答:ES6新增特性常用的主要有:let/const,箭头函数,模板字符串,解构赋值,模块的导入(import)和导出(export default/export),Promise,还有一些数组字符串的新方法,其实有很多,我平时常用的就这些
数组的方法:
1.Array.from()//可以把json对象或者伪数组转为数组
2.Array.of()//  它负责把一堆文本或者变量转换成数组-Array.of(3,4,5,'zhang','li');
3.Array.find()//返回查找出来符合条件的第一个值
4.for…of //遍历出数组的索引
let arr=[1,2,3,4,5,6,7,8,9];
console.log(arr.find(function(value,index,arr){
    return value > 5;
})) //输出6  注意:在函数中如果找到符合条件的数组元素就return,并停止查找

GET和POST请求的区别
GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditPosts.aspx?name=test1&id=123456.
POST方法是把提交的数据放在HTTP包的Body中.

get:发送一个请求来取得服务器上的某一资源
post向URL指定的资源提交数据或附加新的数据

GET提交的数据大小有限制(因为浏览器对URL的长度有限制)谷歌大约四千多个
字
而POST方法提交的数据没有限制

GET方式提交数据,会带来安全问题,比如一个登录页面,通过GET方式提交数据时,用户名和密码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码.
给字符串新增方法的实现功能
var str1 = 'o';
String.prototype.addStr = function(s){
    //str1调用的这个方法,所以this指向的时str1
	return this + s
}
console.log(str1.addStr('abc'))// 'oabc'
sort 背后的原理是什么
sort排序始终是根据元素的unicode编码进行排序
冒泡排序
冒泡排序是一种简单的算法。

数组没有按照从大到小进行排列,这时候我们就能使用冒泡排序进行重新排列了,目的是从小到大排列

首先对比前两个对比,随后对比第二个和第三个对比,一致性和杨对比到最后,把数值大的放到前面,数值小的放到后面,第一次已经选择出来了一个最大的值,随后每次只要对比除了最后一个的元素就可以了,这一值进行,直到最后只剩下两个数值i进行比较,这时候一对比后排序就完成了

使用双层for循环,外边一层表示要剔除最后的一个最大的数,长度需要减去1

举例:

const arr = [911,520,888.666.555.2323];
function bubbleSort(arr){
	let temp;
	for(let i = 0;i < arr.length - 1;i++){
        //length - 1 是因为数组的索引是长度减一,最后不需要在和最后一个值进行判断了,所以减去i的值
        //i的值是从0开始,每一加一,最大的数每次遍历一边内循环就会出现一个最大的值
		for(let j = 0;j < arr.length - 1 - i;j++){
			if(arr[j]>arr[j+1]){
                temp = arr[j];
                arr[j]  = arr[j+1];
                arr[j+1] = temp
			}
		}
	}
	return arr;
}
JavaScript 二分法寻找数组元素
假如是1-10之间,会先找一个中间点进行分分割,之后在找出元素,通过不断的对半分割,查找某个元素  
前提数组是有序排列的
定义一个数组
设置一个函数传入一个目标数值作为参数,声明起始值,结束值,中间值,和一个element,作为变量的声明
通过 起始值加上结束值除以2来找出中间值,为了避免出现小数,使用Math.floor属性,把中间的那数组元素赋值给element
判断:要查找的目标元素与middle的值是否相等,如果相等就就返回该数组的下标
否则,判断目标值是比中间的值大还是小 ,从而判断是重新定义start的值或者end的值,最终做出判断,如果都不满足(也就是不存在)的话则返回负一

代码如下:

var arr = [3,4,5,12,16,18,102,105,230]
function searching(target){
	var star = 0,end = arr.length - 1,middle,element;
	while(star <= end){
		middle = Math.floor((star + end) / 2);
		element = arr[middle];
		if(element === target){
			return middle;
		}else if(target < element){
			end = middle - 1;
		}else{
			start = middle + 1;
		}
	} return -1;
}
js的宏任务和微任务
  • 由于js是单线程的,同一时间只能做一件事情

  • 主要是与他的用途有关,主要是用户的交互(比如有一个用户删除,一个用户操作,该以那个为准)

  • 碰到,事件,ajax请求,定时器的时候就会被放到事件循环中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QZ1vj2hT-1648173937179)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20220227151308878.png)]

调用栈:发现异步任务的时候会先把异步任务放入队列里面,异步任务队列分为宏任务队列和微任务队列,队列都按照先入先出的规则

事件循环:event.loop,事件循环会不断取寻找可以执行的任务来执行,在执行完同步任务之后 ,也就是清空了调用栈之后,首先会执行微任务队列的任务,微任务队列的任务清空之后才会去执行宏任务
宏任务:setTimeout,setInterval,DOM事件(比如常见的鼠标点击事件触发以后的回调函数),AJAX请求

微任务:Promise.then().catch().finally(), async/await
笔试练习1
//问:几秒输出几?
for(var i = 0;i<3;i++){
	setTimeout(function(){
		console.log(i)
	},1000*i)
}
//答案是输出三个3
过程是
1.遇到i=1的时候遇到setTimeout的时候会把i=1和setTimeout放到宏任务队列中
2.i=2的时候相同
3.i=3一样
最终输出结果是333

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VKZSkRx5-1648173937180)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20220227153035411.png)]

new操作符具体做了什么
1.创建一个新的对象
2.构造函数的this指向与该对象
3.使新对象的`__proto__`指向原函数的`prototype`
4.执行构造函数中所有代码
5.返回新对象
js原型和原型链

原型:

①所有引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象
②所有函数都有一个prototype(原型)属性,属性值是一个普通的对象
③所有引用类型的__proto__属性指向它构造函数的prototype

原型链:

比如:对象.__proto__.__proto__是可以的

当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
①一直往上层查找,直到到null还没有找到,则返回undefined
闭包
无意的现象(不是作者设计的,是无意发现的)
1.是什么?
	两个函数,作用域是连接的
	外部函数的变量就不自由(驻留在内存中:因为根据作用域的规则,内部可以访问外部的变量,就证明没有被垃圾回收器回收)
	
2.优点:
	内部可以访问外部局部变量,例如场景:for里面有事件,但是事件中的值,永远是for最后的结果(i的值没有被销毁)
	但是可以通过闭包的形式去解决这个问题
	
3.缺点:
	局部(外部函数)变量驻留在内存中,导致性能可能下降
4.解决
	把驻留在内存中的变量,在不用的时候,设置为null
	或者 可以加入js严格模式,在一定层次上解决这个问题,但是不是100%解决
5.什么情况下用到闭包【抽奖系统限制次数】
	1.封装了一个方法 function
	2.function的功能是做请求数据并且渲染的
	3.请求成功获取到数据让函数i+1
比如:
封装一个请求函数,每次请求让其函数加1
function fn(n){
	var n= 1;
	return axios({
		
	}).then(res => {
		n+1
		//请求一次就让n+1
	})
}
//运行
var f = fn();
f()//相当于fn()()


继承
ES6继承
ES6里面的类的写法,使用extends
class Person{
    constructor(name,age){
        this.name=name;
        this.age=age;
    }
    getInfo(){
        console.log(`姓名:${this.name} 年龄:${this.age}`);
    }
    run(){
        console.log('run')
    }
}
class Web extends Person{ //web继承了Person
    constructor(name,age,sex){
        super(name,age);   /*实例化子类的时候把子类的数据传给父类*/
        this.sex=sex;
    }
    print(){
        console.log(this.sex);
    }
}
var w=new Web('张三','30','男');
w.getInfo();
w.print();
//姓名:张三 年龄:30
//男

原型链继承


function  Person(name,age) {
    this.name=name;
    this.age=age;
    this.run=function(){
        console.log(this.name+'---'+this.age);
    }
}
Person.prototype.work=function(){
    console.log('work');
}

function Web(name,age){
    Person.call(this,name,age);  
}
Web.prototype=new Person();

var w=new Web('李四',20);
w.run();
w.work();
//李四---20
//work 

但是有一个弊端就是,原型继承的对象只是一个引用,那么就是每个实例都可以修改,这就是使用原型链继承方式的一个缺点。
因为我们期望的是s2 = [1,2,3]

利用call()做继承

//利用call()做继承
a.call(b);
//使b对象继承a对象,那么b对象拥有a对象所有的内容,也可以调用a对象中的方法

例子:
function a(){
    this.name = function(){
        alert("123");
    }
}
function b(){
    a.call(this);    //这里的this代表b对象本身
}
var b = b();
b.name();    //b对象调用了父类a中的方法,实现继承
​
除了 Child2 的属性 type 之外,也继承了 Parent2 的属性 name。这样写的时候子类虽然能够拿到父类的属性值,解决了第一种继承方式的弊端,但问题是,只能继承父类的实例属性和方法,不能继承原型属性或者方法。

组合继承

function Parent3 (age) {
  this.name = 'parent3';
  this.play = [1, 2, 3];
  this.age = age
}
​
Parent3.prototype.getName = function () {
  return this.name;
}
function Child3() {
  // 第二次调用 Parent3()
  Parent3.call(this,30);
  this.type = 'child3';
}
​
// 第一次调用 Parent3()
Child3.prototype = new Parent3(30);
// 手动挂上构造器,指向自己的构造函数 
Child3.prototype.constructor = Child3;
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);  // 不互相影响
console.log(s3.getName()); // 正常输出'parent3'
console.log(s4.getName()); // 正常输出'parent3'
Promise

文档:promise.md
链接:http://note.youdao.com/noteshare?id=53c184e720ec98ce4e694a00799e3f1d&sub=093B737DB69949C0AC627F6BE28A6940

localStorage,SessionStorage和cookie的区别
1)cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递;而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存;

2)cookie数据有路径(path)的概念,可以限制cookie只属于某个路径下;

3)存储大小限制也不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识;sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大;

4)数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭;

5)作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
在JS中为什么0.2+0.1>0.3?

答:

因为在JS中,浮点数是使用64位固定长度来表示的,其中的1位表示符号位,11位用来表示指数位,剩下的52位尾数位,由于只有52位表示尾数位。

由于只能存储52位尾数位,所以会出现精度缺失,把它存到内存中再取出来转换成十进制就不是原来的0.1了,就变成了0.100000000000000005551115123126,而为什么02+0.1是因为

// 0.1 和 0.2 都转化成二进制后再进行运算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111

// 转成十进制正好是 0.30000000000000004

**面试官:那既然0.1不是0.1了,为什么在console.log(0.1)的时候还是0.1呢?**⭐⭐⭐

答:**在console.log的时候会**二进制转换为十进制,十进制再会转为字符串的形式,在转换的过程中发生了取近似值,所以打印出来的是一个近似值的字符串

通常的解决办法 就是 把小数变为整数在进行计算

(0.1*10+0.2*10)/10==0.3
this的指向问题
1.如果是普通函数调用,this指向window
2.如果是构造函数,也就是通过new调用,this指向构造函数当前new出来的实例
3.如果是对象调用函数,则this指向调用函数的对象
4.如果是call和apply调用,this就指向你手动在call和apply后面设置的对象
5.如果是箭头函数,则它的this指向它的上级作用域
6.闭包函数中的this指向的window
数组去重
//1. params:Array
//2. 利用:arrayObj.indexOf(); 返回下标;
//3. 不存在则返回-1;*
function getArr(arr) {
  let temp = []
  for (let i = 0; i < arr.length; i++) {
    if (temp.indexOf(arr[i]) === -1) {
      temp.push(arr[i])
    }
  }
  return temp
}
console.log(getArr([1, 5, 8. 5, 4, 1, 11, 7, 4, 1]))

2.//简洁的写法
console.log(Array.from(new Set([1,2,3,1])))
console.log([...new Set([1,1,2,2])])
JavaScript实现call、apply和bind

call、apply、bind三者的异同

- 共同点 : 都可以改变this指向
- 不同点:
  - call 和 apply  会调用函数, 并且改变函数内部this指向.
  - call 和 apply  第二个参数不同,call传递参数使用逗号隔开,apply使用数组传递
  - bind  不会调用函数, 可以改变函数内部this指向.


- 应用场景
  1. call 经常做继承. 
  2. apply经常跟数组有关系.  比如借助于数学对象实现数组最大值最小值
  3. bind  不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向. 
1.call()的用法
var o = {
	name: 'andy'
}
 function fn(a, b) {
     console.log(this);
     console.log(a+b)
};
fn(1,2)// 此时的this指向的是window 运行结果为3
//改变了函数fn的这个this指向,原来this指向fn 现在this指向了o 
fn.call(o,1,2)//此时的this指向的是对象o,参数使用逗号隔开,运行结果为3
2.apply()
    apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。
    fun.apply(thisArg,[argArray])
3.bind()
    const bloom = function() {
      window.setTimeout(this.declare.bind(this), 1000);
    };
var let const 有什么区别

除了函数都其他的没有作用域,比如if或者for

1.作用域
    var声明变量没有作用域(有变量提升)
    let,const声明的变量都有自己的作用域(没有变量提升)
2.var和let都是变量,const是常量
3.let不可以重复声明,但是可以声明后重新赋值
  const不可以重新声明,也不可以声明后重新赋值(但是要注意修改的是谁)
箭头函数和普通函数有什么区别
1.this的指向的问题
	箭头函数的this是在箭头函数定义时就决定的,不是调用(在箭头函数定义的时候,所在的环境定义的)
	注意:箭头函数的this是无法改变的
2.箭头函数不能new
3.箭头函数没有arguments
//arguments的使用
function fun(){
  console.log(arguments);
}
fun('tom',[1,2,3],{name:'Janny'}); 
什么情况下this指向window
1.普通函数
2.普通函数闭包的情况,内部函数this指向window
深拷贝和浅拷贝
引用类型通过 = 拷贝的是它的引用地址

什么是浅拷贝?
 把数组/对象第一层的值,复制到新的数组/对象中,第二层互相引用
浅拷贝使用的场景
 修改数组/对象,影响另一个数组/对象,砍断它们之间的联系

如何实现浅拷贝
 for/for...in/扩展运算符/Object.assign()对象用,循环把数组或者对象的第一层数据复制到另外一个数据中,拷贝之后二者没有联系
 数组可以使用arr.slice(0) 解构的话可以用在对象和数组中
#### 如果数组或者对象有嵌套的情况,不是只有一层

注意赋值的情况

let arr = ["小刘""小明"["小花"]];
let brr = [...arr];
arr[2].push("小黑")
console.log(brr);//["小刘",“小明”,["小花","小黑"]]
let brr=[]//这样brr就申请了一个内存地址,通过...arr这样的数组结构后进行赋值,相当于浅拷贝,但是第二层的数组还是拷贝过去有一个引用地址

对象也有这样的特征

什么是深拷贝

把数组或者对象的所有层的值,复制到另外一个数组或者对象中

实现深拷贝的方法

目标:遍历,判断,递归,创建,赋值

通过代码理解

let oldObj = {
	name:"小明",
	age:18,
	grade:[100,90],
	family:{
	fName:"王"
	}
}

对对象进行深拷贝

//注意先写Array类型的判断(因为Array的父类是Object,写在上面的话不管是数组对象都会写进去)
let newObj = {};
function deepClone(newO,old){
	for(let key in old){
		let value = old[key]
		if(value instanceof Array){
			newO[key] = []//被拷贝的是一个数组,创建一个新的数组
            deepClone(newO[key],value)
		}else if(value instanceof Object){
            newO[key]={}
            deepClone(newO[key],value)
        }else{
            newO[key] = value
        }
	}
}

deepClone(newObj,oldObj)

总结就是:递归每层引用类型,遇到对象/数组,创建一个新的,把基础值复制过来

async和await
简洁:异步编程的最高境界就是不关心它是否是异步。async、await很好的解决了这一点,将异步强行转换为同步处理。
async/await与promise不存在谁代替谁的说法,因为async/await是寄生于Promise,Generater的语法糖。

用法
async用于申明一个function是异步的,而await可以认为是async wait的简写,等待一个异步方法执行完成。
规则:
1 async和await是配对使用的,await存在于async的内部。否则会报错
2 await表示在这里等待一个promise返回,再接下来执行
3 await后面跟着的应该是一个promise对象,(也可以不是,如果不是接下来也没什么意义了…)
防抖和节流
区别与应用
函数防抖:将多次操作合并为一次操作进行。
函数节流:使得一定时间内只触发一次函数。原理是通过判断是否有延迟调用函数未执行
如何使用正则实现trim
function MyTrim(String) {
   return String.replace(/(^\s*)|(\s*$)/g,"")
}
find 和 filter的区别

都是数组的方法

find 返回匹配中的第一个值
filter 返回的是一个数组
arr.filter()
filter():过滤,可以过滤数组中的元素,还可以作为过滤器使用

会返回一个新数组,新数组中包含符合条件的所有元素,
array.filter(function(currentValue,index,arr){}, thisValue)
函数方法中的参数意思:	
currentValue:当前值
index:数组的下标
arr:是一个数组
thisValue:可以省略,是指定this的指向
函数方法中有一个返回值-且返回值后面跟的是一个条件且条件的结果是布尔值
   var arr = [3,34,12,31,45]
    var arr1 = arr.filter(function(val,index,arr){
        return val>13
    })
    console.log(arr);//[3, 34, 12, 31, 45] 原数组未改变
    console.log(arr1);// [34, 31, 45]
    var arr2 = arr.filter(function(val,index,arr){
        return val*2 > 33
    })
    console.log(arr2);//[34, 31, 45]
array.some和arr.every的区别

**every()😗*对数组每一项都运行传入的函数,如果每一项的数都返回true,则这个方法返回true

arr.some()

用于检测数组中的元素是否满足指定的条件
如果有满足指定的条件的话就返回true 如果都不满足指定的条件则返回false 
当检测出来第一个元素符合这个条件的时候就直接返回true后面的元素就不在判断

some() 不会对空数组进行检测。
some() 不会改变原始数组。
array.some(function(currentValue,index,arr),thisValue)
currentValue:当前数组的元素
index:当前数组的索引值
arr:当前数组
var arr = [3,34,12,31,45]
var flag = arr.some((val)=>{
  return val>12
})
console.log(flag);//true
单页面应用和多页面应用的区别
单页面应用
第一次进入页面的时候会请求一个html文件,在network中清除一下网络请求,切换到其他组件,此时路径也相应变化,但是并没有新的html文件请求,页面内容也变化了。

多页面应用
每一次页面跳转的时候,后台服务器都会给返回一个新的html文档,这种类型的网站也就是多页网站,也叫做多页应用。
HTTP和HTTPS的区别(必考)
HTTP 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头
HTTP 是明文传输,HTTPS 通过 SSL\TLS 进行了加密
HTTP 的端口号是 80,HTTPS 是 443
HTTPS 需要到 CA 申请证书,一般免费证书很少,需要交费
HTTP 的连接很简单,是无状态的;
HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。

1.5 Git面试题

  • git框架的介绍
    git分为
    开发者工作区,暂存区,仓库区,远程仓库区
    
  • 工作中常用的几个git命令
    新增文件的命令:git add file或者git add .
    提交文件的命令:git commit –m或者git commit –a
    查看工作区状况:git status –s
    拉取/合并远程分支的操作:git fetch/git merge或者git pull
    查看提交记录命令:git reflog
    
  • 新建git分支的命令
    Git pull    从远程分支拉取代码到本地分支
    Git checkout -b xxx    创建并切换到 xxx 分支
    Git push origin xxx    执行推送的操作,完成本地分支向远程分支的同步
    切换并且连接远程分支的操作 git checkout -b xxx origin/xxx
    
    在执行git pull的时候,提示当前branch没有跟踪信息:
    01: git pull origin  远程分支名称
    02:git branch --set-upstream-to=origin/远程分支名称 本地分支名       (先建立远程分支与本地分支的连接,再pull)
    
  • git撤销上次提交的命令
    git reset --soft HEAD~
    
  • git解决冲突
    1.解决本地分支的冲突。利用stash栈
    git stash		//将修改项存入栈中
    git pull		//pull 拉取远程代码到本地
    git stash pop	//从栈中取出修改项
    
    接下来diff一下此文件看看自动合并的情况,并作出相应修改。
    
    2.将分支合并到其他分支上发生冲突
    	1.切换到要合并的分支 git checkout xxx
    	2.执行 git pull 拉取最新代码
    	3.git merge whhh whhh分支将要合并到xxx分支,执行合并的命令---如果出现冲突,左侧导航栏的对应模块项会变色,或者通过终端里的提示,找到相应冲突并解决。
    	4.解决冲突
    

–补充

JavaScript 内置对象

​ 概念
​ JavaScript 中的对象分为3种:自定义对象 、内置对象、 浏览器对象
​ JS 语言自带的一些对象,提供了一些常用的或是最基本而必要的功能

Math对象

​ Math 对象不是构造函数,它拥有一些数学常数属性和数学函数方法
​ Math. Pl 圆周率
​ Math.floor() 向下取整
​ Math. ceil() 向上取整
​ Math. round() 四舍五入版就近取整注意-3.5结果是-3
​ Math. abs() 绝对值
​ Math.max() / Math. min() 求最大和最小值
​ Math. random() 获取范围在[0,1)内的随机值

日期对象

Date
  • 直接把表示日期的字符串传给Date构造函数,那么Date会在后台调用Date.parse()

    • Date.parse() 将传入的日期转为毫秒数
  • Date.UTC()方法返回日期的毫秒值表示,参数是 年,月(0开始),日,时,分,秒,毫秒

    • //例如
      //2000年1月1日零点
      let data2 = new Date.UTC(2000,0)
      
      //本地时间:2005年5月5日下午5点55分55秒
      let allFives = new Date.UTC(2005,4,5,17,55,55)
      
  • new Date()中直接传入日期默认是调用Date.parse(),传入UTC格式的默认隐式调用Date.UTC()

  • new Date() 括号中不管传入的是字符串日期格式还是时间戳,都返回的是标准格式的时间

  • ECMAScript 还提供了 Date.now() 方法,返回方法执行的日期和时间的毫秒数

    • let dnow = Date.now();
      
继承的方法

Date类型重写了 toLocaleString(),toString()和valueof()方法

  • Date类型的toLocaleString方法返回与浏览器运行的本地环境一致的日期和时间,只不过加上了 AM和PM
    • toLocaleString() - 2/1/2019 12:00:00 AM
  • toString() 返回的是带时区的时间
  • valueOf() 方法根本就不返回字符串,这个方法被重写之后返回日期的毫秒表示
日期格式化方法
  • toDateString() //Fri May 05 2000 显示的是周几,月,日,年

  • toTimeString() //17:55:55 GMT+0800 (中国标准时间) 显示的是时分秒加时区

  • toLocaleDateString() // 2000/5/5 返回当前时间的年月日

  • toLocaleTimeString() // 下午5:55:55 返回时间的时分秒

    • 带有本地locae的更加的便捷

    • 使用的时候利用利用标准时间.xxx

    • 例如

    • let nDate = new Date(2000,4,5,17,55,55)
      let localeD = nDate.toLocaleDateString();
      console.log(localeD);//2000/5/5
      
    moment().format('YYYY-MM-DD,HH:mm:ss');
    

date日期对象 是一个构造函数 必须使用new来调用创建我们的日期对象
参数常用的写法
​ 数字型的 2019,10,01
​ 字符串型的’2019-10-1 8:9:9’
需要自行创建实例对象

getFullYear() 获取当年
getMonth() 获取当月(0-11getDate() 获取当天日期
getDay() 获取星期几(周日0到周六6)
getHours() 获取当前小时
getMinutes()  获取当前分钟
getSeconds()  获取当前秒钟
getTime() 与valueOf() 相同,得到的是时间戳
getTime()-获取标准时间的时间戳

使用举例如下:

var d2 = new Date("12/03/2021 11:10:30");
console.log(d2);//Fri Dec 03 2021 11:10:30 GMT+0800
var time = d2.getTime();
console.log(time);

获取当前时间的时间戳:

var time = Date.now();
console.log(time);

数组对象

关于vue中使用的原生数组的常用方法
变更方法

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。

这些方法会改变原来数组的值

这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
push()
是向数组的末尾推送元素

返回的是新数组的长度

推送的可以是数组,对象,一些其他元素--如下
<script>
     var arr = [3,34,12,31,45]
     var arr2 = [1,2,5]
     var obj = {arr1:2,arr2:'nihao'}
     var b= 4;
     arr.push(b)
     console.log(arr);// [3, 34, 12, 31, 45, 4]
     var add = arr.push(4,5)
     console.log(arr)//[3, 34, 12, 31, 45, 4, 4, 5]
     arr.push(arr2)
	 console.log(add);//8
     console.log(arr)//[3, 34, 12, 31, 45, 4, 4, 5, Array(3)]
     arr.push(obj)
     console.log(arr)// [3, 34, 12, 31, 45, 4, 4, 5, Array(3), {…}]
  </script>
pop()
是把数组中的最后一个元素从数组中去除-最后一个元素可能是数组也可能是对象

此函数的返回值是去除的那个元素
 var arr = [3,34,12,31,45]
 var arrre = arr.pop()
 console.log(arr);//[3, 34, 12, 31]
 console.log(arrre);//45
shift()
是把数组中的第一个元素删除

返回的是删除的那个元素
var arr = [3,34,12,31,45]
var del = arr.shift()
console.log(arr);// [34, 12, 31, 45]
console.log(del);//3
unshift()
是向数组索引为0的位置添加元素

返回值是新数组的长度
var arr = [3,34,12,31,45]
var add =  arr.unshift(45)
console.log(arr);//[45, 3, 34, 12, 31, 45]
console.log(add);//6
splice()
可以删除数组中的元素,也可以向数组中添加元素,一举两得

然后返回被删除的项目。该方法会改变原始数组

删除元素返回的是一个数组

添加元素返回的是一个空值
arrayObject.splice(index,howmany,item1,.....,itemX)
index是规定添加或者删除元素的位置,使用的是负数的话是从数组的最后一个元素开始查的

howmany:规定删除元素的个数

item1,.....,itemX:是添加的元素
var arr = [3,34,12,31,45]
arr.splice(0,0,1,2,3)//在索引为0的地方开始添加,第二个参数是删除元素的个数
console.log(arr);//[1, 2, 3, 3, 34, 12, 31, 45]
var add = arr.splice(1,0,89)//在索引为1的地方添加
console.log(arr);//[1, 89, 2, 3, 3, 34, 12, 31, 45]
console.log(add);//[] 说明没有返回值
var del =  arr.splice(1,1)//从索引为1的位置开始删除
console.log(arr);//[1, 2, 3, 3, 34, 12, 31, 45]
console.log(del);//[89] 返回的是删除的值,是一个数组
sort()
sort(fun(a,b)) 方法用于对数组中的元素进行排序

fun(a,b) 中的a,b代表数组的前一个元素和后一个元素

	如果是正数就交换位置,-

	在使用的时候 a-b 代表 排列的是从小到大
	在使用的时候 b-a 代表 排列的是从大到小
	
	内部使用数组长度小于等于 22 的用插入排序 InsertionSort,比22大的数组则使用快速排序 QuickSort
var arr = [3,34,12,31,45]
    arr.sort()
    console.log(arr);//[12, 3, 31, 34, 45]
    arr.sort((a,b)=>{
      return a-b
    })
    console.log(arr);//[3, 12, 31, 34, 45]
    arr.sort((a,b)=>{
      return b-a
    })
    console.log(arr);//[45, 34, 31, 12, 3]
reverse()
数组反转:颠倒数组中元素的位置

该方法会改变原来的数组,而不会创建新的数组。
var arr = [3,34,12,31,45]
var arr1 = arr.reverse()
替换数组

变更方法,顾名思义,会变更调用了这些方法的原始数组。相比之下,也有非变更方法,例如 filter()concat()slice()。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组:

concat()
该方法用于连接两个或者多个数组

该方法不会改变原有的数组,而仅仅会返回连接数组的一个副本

可以把concat(a,b)中的参数拼接到数组中,a和b可以是数组,也可以是字符串
    var arr = [3,34,12,31,45]
    var arr1 = [6,7,8,9]
    var arr2 = [61,71,81,91]
    var newarr = arr.concat(2,4)//拼接concat和push的差异,push会改变原数组
    console.log(arr);//[3, 34, 12, 31, 45]
    console.log(arr1);//[3, 34, 12, 31, 45, 2, 4]
    var newarr1 = arr.concat(arr1)//相当于把arr1拼接到arr上,原数组不变返回一个新数组
    console.log(arr);//[3, 34, 12, 31, 45],原数组未发生改变
    console.log(newarr1);// [3, 34, 12, 31, 45, 6, 7, 8, 9]
    var newarr2 = arr.concat(arr1,arr2);
    console.log(newarr2);//[3, 34, 12, 31, 45, 6, 7, 8, 9, 61, 71, 81, 91]
slice()
用于从已有的数组中选出选定的数组元素

slice(start,end)

start,指定数组元素从何处开始

end:指定数组元素从何处结束

如果只写一个参数,就是从这个参数开始一直到最后
var arr = [3,34,12,31,45]
var newarr = arr.slice(2)
console.log(arr);//[3, 34, 12, 31, 45]
console.log(newarr);// [12, 31, 45]
var newarr1 = arr.slice(3)
console.log(newarr1);// [31, 45]
var newarr2 = arr.slice(2,4)
console.log(newarr2);//[12, 31]

迭代方法

数组的五个迭代方法

**every()😗*对数组每一项都运行传入的函数,如果每一项的函数都返回true,则这个方法返回true

**filter()😗*对数组的每一项都运行传入的函数,函数返回true的项会组成数组之后返回;

**forEach()😗*对数组的每一项都运行传入的函数,没有返回值;

**map()😗*对运行的每一项都运行传入的函数,返回由每次函数调用的结果构成的数组

**some()😗*对数组的每一项都运行传入的函数,如果有一项返回true的话,则这个方法返回true

这几个数组的参数都相同都是(currentValue,index,arr),一样的用法

  • 有返回值并且需要判断的话需要在return后面加上一个判断
arr.every()
arr.filter()
filter():过滤,可以过滤数组中的元素,还可以作为过滤器使用

会返回一个新数组,新数组中包含符合条件的所有元素,
array.filter(function(currentValue,index,arr){}, thisValue)
函数方法中的参数意思:	

currentValue:当前值

index:数组的下标

arr:是一个数组

thisValue:可以省略,是指定this的指向

函数方法中有一个返回值-且返回值后面跟的是一个条件且条件的结果是布尔值
   var arr = [3,34,12,31,45]
    var arr1 = arr.filter(function(val,index,arr){
        return val>13
    })
    console.log(arr);//[3, 34, 12, 31, 45] 原数组未改变
    console.log(arr1);// [34, 31, 45]
    var arr2 = arr.filter(function(val,index,arr){
        return val*2 > 33
    })
    console.log(arr2);//[34, 31, 45]
arr.some()
用于检测数组中的元素是否满足指定的条件

如果有满足指定的条件的话就返回true 如果都不满足指定的条件则返回false 

当检测出来第一个元素符合这个条件的时候就直接返回true后面的元素就不在判断

some() 不会对空数组进行检测。
some() 不会改变原始数组。
array.some(function(currentValue,index,arr),thisValue)
currentValue:当前数组的元素

index:当前数组的索引值

arr:当前数组
var arr = [3,34,12,31,45]
var flag = arr.some((val)=>{
  return val>12
})
console.log(flag);//true
arr.forEach()
 方法对数组的每个元素执行一次提供的函数。总是返回undefined
arr.forEach(function(currentValue,index,arr){})
currentValue:当前数组的元素

index:当前数组的索引值

arr:当前数组
var arr = [3,34,12,31,45]
 arr.forEach((x)=>{
   console.log(x);//遍历出来arr中的所有元素
 })
arr.map

map方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值
map()会按照数组中元素的顺序依次遍历处理数组中的元素

arr是一个数组

注意:
map不会对空数组进行检测,map不会改变原始数组

arr.map(function(currentValue,index,arr),thisValue)

参数说明

currentValue 必须 当前元素值
index 可选 当前元素的索引值
arr 可选 当前元素属于的数组对象。

forEach和Map函数用法相似
两者的区别:(两者的参数都是一致的,回调函数的参数也是一样的)
1、forEach()返回值是undefined,不可以链式调用。
2、map()返回一个新数组,原数组不会改变。返回的值就是数组中的新元素。

断言函数

find()-findIndex()

find()和findIndex() 方法使用了断言函数

find() 返回第一个匹配的元素,如果该元素是一个对象就返回这个对象

findIndex() 返回第一个匹配的索引

找到匹配项之后都不再继续进行

const events = [
    {
		name:'matt',
		age:27
    },
    {
    	name:'nino',
    	age:16
    }
];

var obj = event.find((element,index,array) => {
	return element.age < 18;
});
console.log(obj); // {name:'nino',age:16}

归并方法

reduce()和reduceRight()

reduce()方法从数组的第一项开始遍历到最后一项

reduceRight()方法从数组的最后一项开始遍历到第一项

这两个数组共接受四个参数:上一个归并值,当前项,当前项的索引和数组的本身

这个函数的返回的任何值都会作为下一次调用同一个函数的第一个参数。

如果没有给这两个方法传入可选的第二个参数(作为归并的起点值),则第一次迭代将从数组的第二项开始,因此传给归并函数的第一个参数是数组的第一项,第二个参数是数组的第二项

举例

可以使用reduce累加数组的所有的值

let value = [1,2,3,4,5];
const sum = value.reduce((pre,cur,index,array) => {
	return pre + cur;
})
console.log(sum); //15

reduceRight正好与此归并的方向相反

补充数组的其他方法

Array.from

类数组转为数组

Array.from()在转化对象时,要求过于苛刻,因此不适用于转化对象,主要场景如下:

(1)从类数组对象(arguments)生成数组
let fn = function(){
console.log(Array.from(arguments));
}
fn(1,2,3) // [1,2,3]2)从 String 生成数组
Array.from(‘九九欧’); // [“九”,“九”,“欧”]3)从Set生成数组
Array.from(new Set([“九”,“九”,“欧”,“欧”])); // [“九”,“欧”]4)从Map生成数组
Array.from(new Map([[1, ‘a’], [2, ‘b’]])); // [[1, ‘a’], [2, ‘b’]]5)可以接收第二个可选的映射函数参数,这个函数可以直接增强新数组的值
const a1 = [1,2,3,4];
const a2 = Array.from(a1,x ==> x**2);
const a3 = Array.from(a1,function(x){return x**this.exponent},{exponent:2});
console.log(a2); //[1,4,9,16]
console.log(a3); //[1,4,9,16]

可以直接使用arr.map()

( 6) 把类数组的对象转化为数组

let obj = { // 对象里面键值是数组的结构一样可以转,如果属性不是数值那么转出来会是undefined
    "0":1,
    "1":2,
    "2":3,
    length:3 // 不写length,是空数组,一定要写,决定数组之后的长度,大于的时候undefined填充
  }
  console.log(Array.from(obj,(value,index) => value+2)) // [3, 4, 5]
Array.of()

Array.of()可以把一组参数转化为数组;替代了在ES6之前常用的Array.prototype.slice.call(arguments),一种异常笨的方法将argument对象转为数组;

console.log(Array.of(1,2,3,4)); //[1,2,3,4]
console.log(Array.of(undefined)); //[undefined]
arr.join(‘,’)

arr.join(‘,’);–可以把数组转为字符串,这里括号中的逗号就是将每个元素使用逗号作为分隔

const option = [1,,,5];
console.log(option.join('-')); //'1---5'

字符串对象

string.length

返回字符串中的字符数目

var str = 'hello'
console.log(str.length);//5
indexof() 与lastIndexof()

indexof()-字符串中查找给定字符的索引,如果存在返回索引号,如果不存在,则返回-1

lastIndexof()-从后往前找,只找第一个匹配的,找到就返回索引值(索引值也是从前往后的排序),找不到就返回-1

let stringValue = "hello world";
console.log(stringValue.indexOf('o')) //4
console.log(stringValue.lastIndexOf('o')) //7
'JavaScript'.indexOf('aSc') //3
'JavaScript'.indexOf('C++') //-1

如果字符串只有一个o的话,则indexof()和lastIndexof()则返回的是同一个位置

这两个方法都可以接收可选的第二个参数,表示开始搜索的位置。

这意味着,indexOf()会从这个参数指定的位置开始向字符串末尾搜索,忽略该位置之前的字符

lastIdexof()则会从这个参数指定的位置开始向字符串开头搜索,忽略该位置之后直到字符串末尾的字符

let stringValue = "hello world";
console.log(stringValue.indexOf('o' , 6)) //7
console.log(stringValue.lastIndexOf('o' , 6)) //4
练习indexof()和lastIndexof()

找出一段字符串中包含e的所有下标,之后使用数组的方式存储起来

let stringValue = “Lorem ipsum dolor sit amet, consectetur adipisicing elit”
let positions = new Array()
let pos = stringValue.indexof("e")

while(pos > -1){
    positions.push(pos);
    pos = stringValue.indexof("e",pos + 1);
}

console.log(position) //[]

首先取得第一个位置,此后借助循环每次寻找下一个,之后存储到数组中

charAt(index)

返回指定索引位置的字符 (index字符串的索引号)

var str = 'hello'
console.log(str.charAt(2));
concat(str1,str 2,str3…)

可以接收任意多个参数,因此可以一次性拼接多个字符串

不会修改调用它们原始的字符串

var str = 'hello'
var result = stringValue.concat("world","!")
console.log(result)//"hello word!"
console.log(str) // "hello"
slice(),substr()和substring()

这三个方法都返回调用它们的字符串的一个子字符,而且都接收一个或者两个参数。

第一个参数表示字符串的起始位置,第二个位置表示字符串的结束位置。

对slice()和substring()而言,第二个参数是提出结束的位置(即位置之前的字符会被提取出来)

对substr()而言,第二个参数表示返回字符串的数量。

任何情况下,省略第二个字符串都意味着提取到字符串末尾

和concat一样不会修改它们之前原先的字符串

let stringValue = "hello world";
console.log(stringValue.slice(3)) // "lo world"
console.log(stringValue.substring(3)) // "lo world"
console.log(stringValue.substr(3)) // "lo world"
console.log(stringValue.slice(3,7)) // "lo wo"
console.log(stringValue.substring(3,7)) // "lo wo"
console.log(stringValue.substr(3,7)) // "lo world"

当某个参数是负数的时,这三个方法的行为又有不同

  • slice()方法将所有负值参数都当成字符串的长度减一
  • substr()方法将第一个负值的参数加上字符串的长度,第二个参数直接为0
  • substring() 方法会将所有的参数变为0
startsWith(),endWith()和includes()

ECMAScript6增加了3个用于判断字符串中是否包含另一个字符串的方法:

这些方法都会从字符串中搜索传入的字符串,并返回一个表示是否包含的布尔值

  • startsWith() 检索从0开始的匹配项,判断字符串是否以这个字符开头
  • endsWith() 检索开始于索引(string.length - substring.length() )的匹配项,判断这个字符串是否以这个字符结尾
  • includes() 检擦整个字符串
let message = "foobarbaz";

console.log(message.startsWith("foo")); //true
console.log(message.startsWith("bar")); //false

console.log(message.endsWith("baz")); //true
console.log(message.endsWith("bar")); //false

console.log(message.include("bar")); //true
console.log(message.include("qux")); //false

startsWith() 和 includes() 方法接收可选的第二个参数,表示开始搜索的位置

如果传入第二个参数,则意味着这两个方法会从指定位置向字符串末尾搜索,忽略该字符串之前的所有字符串

let message = "foobarbaz";

console.log(message.startsWith("foo")); //true
console.log(message.startsWith("foo",1)); //false

console.log(message.include("bar")); //true
console.log(message.include("bar",4)); //false

endsWith() 方法接收可选的第二个参数,表示应该当作字符串末尾的位置,如果不提供这个参数那么默认就是字符串的长度,如果提供这个参数,那么就好像字符串只有那么多字符一样

let message = "foobarbaz";

console.log(message.endsWith("bar")); //false
console.log(message.endsWith("bar",6)); //true
trim()

ECMAScript 在所有的字符串上都提供了trim() 方法。这个方法会创建字符串的一个副本,删除前后的所有空格,再返回删除后的结果

let stringValue = " hello world "

console.log(stringValue); //  " hello world "
console.log(stringValue.trim()); //  "hello world"

由于trim() 返回的是字符串的副本,因此原始字符串不受影响,即原本前后空格都会保留

另外,trimeLeft() 和 trimRight() 方法分别用于从字符串开始和末尾清理空字符串

repeat()

ECMAScript 在所有的字符串上都提供了repeat()方法。这个方法接收一个整数参数,表示要将这个字符串复制多少次,然后返回所有拼接副本后的结果

let stringValue = "na"
console.log(stringValue.repeat(16)); //nananananananananananananananana
padStart() 和 padEnd()

padStart() 和 padEnd() 方法会复制字符串,如果小于指定长度,则在响应的一边填充字符串,直至满足填充条件

这两个方法,第一个参数是长度,第二个参数是可选的填充字符串(默认是空格)

let stringValue = "foo"
console.log(stringValue.padStart(6)); //   foo
console.log(stringValue.padStart(6,"."));//...foo

console.log(stringValue.padEnd(6,"."));//foo...

可选的第二个参数并不限于一个字符串。如果提供了多个字符串,则会将其拼接并且阶段到指定的长度,此外如果长度小于过等于字符串的长度,则会返回原始的字符串

let stringValue = "foo"

console.log(stringValue.padEnd(8,"bar"));//foobarba
console.log(stringValue.padEnd(2,"bar"));//foo
toUpperCase() 和 toLocaleUpperCase()
  • 都是转化为大写
  • 如果不知道涉及什么语言的话使用特定时区的转化方法-toLocaleUpperCase()
toLowerCase() 和 toLocaleLowerCase()
  • 都是转化为小写
  • 如果不知道涉及什么语言的话使用特定时区的转化方法-toLocalLowerCase()

字符串的匹配方法

match()

match() 方法接收一个参数,可以是一个正则表达式字符串,也可以是一个RegExp对象

返回值中可以得到匹配的字符串值,索引和输入的字符串

var text = "cat,bat,sat,fat";
var pattern1 = /.at/;
var matches = text.match(pattern1);
console.log(matches); //['cat', index: 0, input: 'cat,bat,sat,fat', groups: undefined]
console.log(matches.index); //0
console.log(matches[0]); //cat

如果匹配到的是两个或者更多的话,返回的是一个数组,数组的元素是匹配到的值

var pattern1 = /.at/g;
var matches = text.match(pattern1);
console.log(matches); //['cat', 'bat', 'sat', 'fat']
search()

这个方法唯一的参数和match()方法一样:是正则表达式对象或者是一个字符串

这个方法返回模式第一个匹配的位置索引,如果没有找到就返回 -1

let text = "cat,bat,sat,fat";

let pos = text.search(/at/)
console.log(pos);

这里search(/at/) 返回1,即“at”的第一个字符在字符串中的位置

replace()

为简化字符串替换操作,ECMAScript提供了replace() 方法。

这个方法接收两个参数

  • 第一个参数可以是一个RegExp对象或是一个字符串(这个字符串不会转为正则表达式)

  • 第二个参数可以是一个字符串或一个函数

    • 如果第一个参数是字符串,那么只会替换第一个子字符串
    • 要想替换所有的子字符串,第一个参数必须为正则表达式并且带全局标识
//举例
let text = "cat,bat,sat,fat";

var text1 = text.replace("at", "ond");
console.log(text1); //cond,bat,sat,fat
var text2 = text.replace(/at/g, "ond");
console.log(text2);//cond,bond,sond,fond

replace()的第二个参数可以是一个函数。在只有一个匹配项的时候,这个函数会收到3个参数:

  • 与整个模式匹配成功的字符串
  • 匹配项在字符串中开始的位置
  • 以及整个字符串
function htmlEscape(text){
	return text.replace(/[<>''&]/,function(match,pos,originalText){
		switch(match) {
			case "<":
				return "&lt";
			case ">":
				return "&gt";
			case "&":
				return "&amp";
			case "\''":
				return "&quot";
		}
	})
}
**split() **

把字符串分割为字符串数组。

这个方法会根据传入的分隔符将字符串拆分成数组。作为分隔符的参数可以是字符串,也可以是RegExp对象

还可以传入第二个参数,即数组的大小,确保返回的数组不会超过指定的大小

//通过空格分隔
var str = 'Hello world!'
console.log(str.split(' '));//["Hello", "world!"]

let colorText = "red,blue,green.yellow"

let colors1 = colorText.split(","); //["red","blue","green","yellow"]
let colors1 = colorText.split(",",2); //["red","blue"]
let colors1 = colorText.split(/[^,]+/); //["" , "," , "," , "," , ""]

要把数组元素限制为两个,传入第二个参数2即可

最后,使用正则表达式可以得到一个包含逗号的数组

for of:

遍历的是value,对于数组遍历数组的值,对于对象也是遍历对象的值

for in:

遍历的是key,数组遍历的是索引,对于对象遍历的是键值对的属性

迭代的对象是null或者是undefined的情况下则不执行循环

正则表达式

语法
/正则表达式主体/修饰符(可选)
使用字符串方法

在 JavaScript 中,正则表达式通常用于两个字符串方法 : search() 和 replace()。

search() 方法 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置。

replace() 方法 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

正则表达式修饰符

修饰符 可以在全局搜索中不区分大小写:

修饰符描述
i执行对大小写不敏感的匹配。
g执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m执行多行匹配。
正则表达式模式

方括号用于查找某个范围内的字符:

特殊字符含义用法
^1. 匹配输入字符串的开始位置 2. 用在[]中时表示 非1. /^http/ 匹配以http开头的字符串 2. /[^a-zA-Z]/ 匹配非字母
$匹配输入字符串的结尾位置/.com$/ 匹配以.com结尾的字符串
|二选一,表示 或/a|b/ 匹配a或者b
.小l/./ 匹配换行符之外的其他字符
[]中括号匹配一个字符/[aeiou]/ 匹配字母 aeiou 中的一个
()小括号表示一个子表达式分组匹配的子表达式可以用于以后使用
{}大括号表示限定一个表达式多少次{n} 匹配n次; {n,} 匹配最少n次; {n, m} 匹配n-m次
+匹配前面的子表达式一次或多次/[0-9]+/ 匹配一个数字或多个数字
*匹配前面的子表达式零次或多次/[0-9]*/ 匹配0次数字或多个数字
?1. 匹配前面的子表达式零次或一次 2. 指明一个非贪婪限定符1. /[0-9]?/ 2. /<.*?>/ 匹配一个标签如<p>

里列出常用的字符和其表达的含义:

常用匹配字符含义
[0-9]匹配单个数字0-9
[a-z]匹配单个小写字母
[A-Z]匹配单个大写字母
\s匹配所有空白字符,如空格、换行等
\n匹配所有换行符
\b匹配单词之间的边界(匹配连续的字母、数字的两边)
\w匹配任意字母、数字、下划线
\d等同于[0-9]

量词:

量词描述
n+匹配任何包含至少一个 n 的字符串。
n*匹配任何包含零个或多个 n 的字符串。
n?匹配任何包含零个或一个 n 的字符串。

只能输入英文字母和数字和下划线,不能输入中文

<input onkeyup="value=value.replace(/[^\w\/]/ig,'')"> 
使用 test()

test() 方法是一个正则表达式方法。 reg.test(xx)

test() 方法用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。

使用 exec()

exec() 方法是一个正则表达式方法。

exec() 方法用于检索字符串中的正则表达式的匹配。 reg.exec(xx)

该函数返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。

举例
//比如:是否全部是字母
var isletter = /^[a-zA-Z]+$/.test(val);
//把输入得到汉字或者非数字和字母的全部替换为空
this.msDailyForm[key] = e.target.value.replace(/[^\w/]/ig, '');
//使用exec的方式
/e/.exec("The best things in life are free!");

//创建正则的第二种方式
let pattern = new RegExp("\\[bc\\]at", "gi")
console.log(pattern);//  /\[bc\]at/gi
pattern.toLocaleString()和pattern.toString()的打印的值不同

集合类引用

Object 类型

虽然object没有多少的功能,但是很适合存储和再应用程序间的交换数据

显示创建object的方式有两种

  • 第一种是使用new操作符和object构造函数

    • let person = new Object()
      person.name = "Nicholas";
      person.age = 23;
      
  • 第二种是使用对象字面量表示法

    • let person = {
      	namem:"Nicholas",
      	age:29
      }
      
    • 再对象字面量的表示法中,属性可以是字符串或者是数值,其中数值会转换为字符串

    • 字面量表示法通常只在为了让属性一目了然的时候才会使用

虽然属性是通过点语法来获取的,这也是面向对象语言的惯例,但也可以使用中括号来存取属性。

  • 在使用中括号的时候要在括号内使用属性名的字符串形式

    • console.log(person["name"]) //"Nicholas"
      console.log(person.name) //"Nicholas"
      
  • 使用字符串的主要优势就是可以通过变量访问属性

    • 另外如果属性中包含可能导致语法错误的字符,或者包含关键字。保留字时,也可以使用括号语法

      • 比如
        person["first name"]  = "Nicholas"
        

Array

创建数组

特点:

  • 数组中的每个槽位可以存储任意类型的数据
  • ECMAScript数组也是动态大小的,会随着数据的添加而自动增长‘’

创建的方式

  • 一种是Array构造函数,比如

    • let colors = new Array(20)//创建一个length为20的数组
      //也可以创建包含三个元素的数组
      let colors = new Array("red","blue","green");
      
  • 另外一种创建数组的方式是使用数组字面量的方式

    • let colors = ["red","blue",green]
      
    • 注意使用数组字面量表示创建方法的时候不会调用Array的构造函数

Map

在JavaScript中实现“键/值”式存储可以使用object来方便高效的完成,也就是使用对象属性作为键,再使用属性来引用值。

map的大多数特性都可以通过object类型实现,

vue面试

MVVM

  • MVVM即模型、视图、视图模型(Model-View-ViewModel),ViewModel是MVVM这栋别墅的“管家”。
  • 将其中的 View 的状态和行为抽象化,让我们可以将UI和业务逻辑分开。
  • MVVM的优点是低耦合可重用性独立开发

mvvm.jpg

  • View 接收用户交互请求
  • View 将请求转交给ViewModel
  • ViewModel 操作Model数据更新
  • Model 更新完数据,通知ViewModel数据发生变化
  • ViewModel 更新View数据
key的作用主要是为了高效的更新虚拟DOM

保证某个元素的 key 在其同级元素中具有唯一性。在 Diff 算法中 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染

说一下vue最大特点是什么或者说vue核心是什么
答:vue最大特点我感觉就是“组件化“和”数据驱动“

  组件化就是可以将页面和页面中可复用的元素都看做成组件,写页面的过程,就是写组件,然后页面是由这些组件“拼接“起来的组件树

  数据驱动就是让我们只关注数据层,只要数据变化,页面(即视图层)会自动更新,至于如何操作dom,完全交由vue去完成,咱们只关注数据,数据变了,页面自动同步变化了,很方便
说一下vue常用基本指令有哪些
v-if:根据表达式的值的真假条件渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。
v-show:根据表达式之真假值,切换元素的 display CSS 属性。
v-for:循环指令,基于一个数组或者对象渲染一个列表,vue 2.0以上必须需配合 key值 使用。
v-bind:动态地绑定一个或多个特性,或一个组件 prop 到表达式。
v-on:用于监听指定元素的DOM事件,比如点击事件。绑定事件监听器。
v-model:实现表单输入和应用状态之间的双向绑定
v-pre:跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
v-once:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
vue2.0的生命周期有哪些
系统自带有八个分为四个阶段分别有:
beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,desdroyed

如果使用了keep-alive会在多两个生命周期:actived deactivated
this.$el和this.$data分别在那些生命周期中存在
beforeCreate 啥也没有
created		 有this.$data但是没有this.$el
beforeMount	 有$data但是没有$el
mounted		 有$data也有$el
生命周期的解释
beforeCreate:此时既不能访问data中的数据,也不能访问mthods中的方法
created:此时可以访问data中的数据,但是还不能使用methods中的方法
beforeMount:挂在前-指令中已经解析完毕内存中已经生成dom树 还没渲染到本地
mounted:页面中呈现的是经过编译的dom结构,对dom的操作都有效,此时可以发送一些网络请求和设置定时器等一些初始化操作
beforeUpdate:此时数据是新的,但是页面是旧的,页面和数据还没有同步
updated:数据是新的,页面上的数据也是新的
beforeDestroy:将要销毁但是数据此时没有变化,可以执行一些将要销毁的操作
destroyed:此时数据已经销毁,没有什么意义了
刚进入页面执行的是前四个生命周期:
beforeCreate,created,beforeMount,mounted

如果使用了keep-alive第一次进入页面初次加载会执行前面的五个生命周期:beforeCreate,created,beforeMount,mounted,actived
第二次或者地N次进入页面只会执行:actived
vue组件中data为什么必须是一个函数?
答:因为JavaScript的特性所导致,在component中,data必须以函数的形式存在,不可以是对象。
  组件中的data写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。
谈一谈对keep-alive的理解
1.是什么
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
功能:是用来缓存组件的。==》提升性能
2.使用场景
 就是用来缓存组件,提升项目的性能。具体的实现比如:首页进入到详情页,如果用户每次点击都是相同的,那么详情页就没必要请去求N次了,直接缓存起来就可以,当然如果点击的不是同一个,那么就直接请求
Props
- include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
- exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
怎么使用
//1.缓存所有的路由,在app.vue目录下
<template>
  <div id="app">
  	<keep-alive>
      <router-view/>
    </keep-alive>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>
//2.根据条件缓存
//将路由名称为test的路由进行缓存,其他路由不会缓存
<keep-alive include='test'>
   <router-view/>
</keep-alive>
缓存部分界面
//1.在router目录下的index文件中给路由添加属性
import Vue from 'vue'
import Router from 'vue-router'
const Home = resolve => require(['@/components/home/home'], resolve)
const Goods = resolve => require(['@/components/home/goods'], resolve)
const Ratings = resolve => require(['@/components/home/ratings'], resolve)
const Seller = resolve => require(['@/components/home/seller'], resolve)

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home,
      redirect: 'goods',
      children: [
        {
          path: 'goods',
          name: 'goods',
          component: Goods,
          meta: {
        	keepAlive: false // 不需要缓存
      	  }
        },
        {
          path: 'ratings',
          name: 'ratings',
          component: Ratings,
          meta: {
        	keepAlive: true  // 需要缓存
      	  }
        },
        {
          path: 'seller',
          name: 'seller',
          component: Seller,
          meta: {
        	keepAlive: true  // 需要缓存
      	  }
        }
      ]
    }
  ]
})
//2.在app.vue文件下
<template>
  <div id="app">
  	<keep-alive>
      <router-view v-if="$route.meta.keepAlive">
       </router-view>
    </keep-alive>
    <router-view v-if="!$route.meta.keepAlive"></router-view>//如果不需要缓存的时候执行
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>
v-if和v-show的区别
1.展示的形式不同
v-if是创建一个dom节点
	v-if直接控制的是vue生成的虚拟dom树,当虚拟dom树上的节点被删除时,vue的响应系统会实时的删除对应的真实dom树上的节点;从而控制元素的显隐,开销较大
v-show 是display:none/block
	v-show只会在初次渲染dom节点,之后都不会再次渲染,只是通过css来控制元素的显示和隐藏
2.使用场景不同
初次加载v-if要比v-show好,页面不会加载盒子
频繁切换v-show要比v-if好,创建和删除的开销太大了,显示和隐藏开销较小
v-for 和 v-if 优先级
1、v-for 优先于 v-if 被解析
2、如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,浪费了性能
3、要避免出现这种情况,则在外层嵌套template,在这一层进行v-if判断,然后在内部进行v-for循环
4、如果条件出现在循环内部,可通过计算属性提前过滤掉那些不需要显示的项
ref是什么
是用来获取dom的
使用的时候this.$refs.xxx
补充:获取到的dom样-<div>aa</div>
使用场景: 
this.$refs.xx.focus(),使得该元素获取焦点
nextTick是什么?
获取更新后的dom内容
应用场景:
由于在mounted的时候就可以获取到dom,所以在methosd中直接获取的dom是没有跟新后的
所以使用nextTick获取更新后的dom
this.$nextTick(()=>{
	console.log(this.$refs.box.innerHtml)
})

 在created操作dom的时候,是报错的,获取不到dom,这个时候实例vue实例没有挂载

【解决办法】通过:Vue.nextTick(回调函数进行获取)
style中的scoped原理
1.让样式在本组件中生效,不影响其他组件的使用
2.原理:给节点新增自定义属性,然后css根据属性选择器添加样式


需要在组件中局部修改第三方组件的样式,而又不想去除scoped属性造成组件之间的样式污染。此时只能通过/deep/穿透scoped。
css预处理器Sass和Less的比较
CSS 预处理器是一种用来为 CSS 增加一些编程特性的语言,无需考虑浏览器的兼容性问题
例如你可以在 CSS 中使用变量、函数、简单的程序逻辑等一些编程语言中的基本技巧,使得CSS 更为简洁,适应性更强,代码更直观等诸多好处。

1.二者声明变量的方式不同,Less以变量@开头,Sass的变量以$开头
2.Less的继承稍弱,并且有些混乱
Sass能把一个选择器的所有样式继承到另一个选择器上
.class1 {
    border: 1px solid #ddd;
}
.class2 {
    @extend .class1;
    font-size:120%;
}
3,Less不能使用条件语句和循环语句,Sass能够使用条件语句和循环语句
4.伪选择器在less中的书写
.columm {
    background-color: pink;
     &:nth-child(2) {
         flex: 4;
      }
}
5.伪类元素直接和原来一样的使用方式
.columm:hover{
	...
}
vue组件之间如何让传值
1.父组件传值子组件
	父组件:<Header :xxxx="msg"></div>
	子组件:
		props:['xxxx']
		props:{
			xxxx:数据类型
		}
	具体实现:父组件通过import引入子组件,并注册,在子组件标签上添加要传递的属性,子组件通过props接收,接收有两种形式一是通过数组形式[‘要接收的属性’ ],二是通过对象形式{  }来接收,对象形式可以设置要传递的数据类型和默认值,而数组只是简单的接收
2.子组件 传值 父组件
子组件:
	//第一个参数:自定义事件的名称,第二个参数:自定义事件的数据
	this.$emit("chilInput",this.changeVal)
父组件:
	<Header :xxxx="msg" @chilInput="getVal"></div>
	methods:{
		getVal(msg){
			//msg就是子组件传递过来的数据
		}
	}
3.兄弟组件之间的传值

方法一:通过event bus实现
		具体实现:创建一个空的vue并暴露出去,这个作为公共的bus,即当作两个组件的桥梁,在两个兄弟组件中分别引入刚才创建的bus,在组件A中通过bus.$emit(’自定义事件名’,要发送的值)发送数据,在组件B中通过bus.$on(‘自定义事件名‘,function(v) { //v即为要接收的值 })接收数据
方法二:通过vuex实现
		具体实现:vuex是一个状态管理工具,主要解决大中型复杂项目的数据共享问题,主要包括state,actions,mutations,getters和modules 5个要素,主要流程:组件通过dispatch到 actions,actions是异步操作,再actions中通过commit到mutations,mutations再通过逻辑操作改变state,从而同步到组件,更新其数据状态

computed,methods,watch有什么区别?
1.computed vs methods区别
	computed是有缓存的
	methods没有缓存
2.computed vs watch区别
	watch是监听,数据或者路由发生了改变才可以响应
	computed计算某一个属性的改变,如果一个值改变了,计算属性会监听到进行返回
	watch是当前监听的数据改变了,才会执行内部的代码
watch 和 computed 的区别和运用场景

watch:

 是监听属性值的变化,是在数据变化时,要执行异步和开销较大的操作时使用,即一个属性影响多条数据时使用
data(){
  return {
      name:'你好'
   }
  }
   watch:{
      /* 原有写法 */
      name(newval,oldval){
      conslog.log(newval,'新的值')
      conslog.log(oldval,'旧的值')
     }
 }

如果要其在最初绑定的时候就去执行的话,可以换一种写法
我们给属性绑定一个 handler 方法,当然我们没这样写的话,默认也是有 handler 方法的,
然后再添加:immediate:true,告诉 watch 我们给 name 绑定了一个 handler 方法,并且在 watch 声明了 name 之后,就立即执行 handler 方法
watch:{
    name:{
        handler(newval,oldval){
            conslog.log(newval,'新的值')
            conslog.log(oldval,'旧的值')
        },
         immediate:true,//代表watch声明了name属性之后立马执行handler方法
    }
   }
    但当我们给监听的对象添加一个值或者是删除一个值的时候,你会发现 vue 并没有什么变化,这是因为 vue 只会对初始实例化时,在 vue 上的 属性才能实现双向绑定,因为 vue 的本质也还是 JavaScript 的限制,不能监测到在实例化时未被 getter/settter 转换的值
这是就算我们对 name 这个对象进行监听了,也不会造成数据变化视图更新,这是我们就要使用 watch 对对象的深度监听了,deep:true
    
    data(){
        return {
            obj:{
                name:'你好'
            }
        }
    }
    watch:{
        obj:{
            handler(newval,oldval){
                conslog.log(newval,'新的值')
                conslog.log(oldval,'旧的值')
            },
                immediate:true,//代表watch声明了name属性之后立马执行handler方法
                deep:true//可以深度监测到obj对象的属性值变化
        }
    }
但这样的话,deep 等于说是进行深入观察了,一层层的往下查找,给每一个属性都添加一个监听事件,这样性能的开销就大了
当对象属性较多时,每个属性值的变化都会执行 handler。如果只需要监听对象中的一个属性值,则可以做以下优化:使用字符串的形式监听对象属性,这样就不用使用深度监听给每一个对象属性都添加监听事件了,只有 vue 一层层解析,知道遇到要监听属性name时,才给属性name添加监听函数
    data(){
        return {
            obj:{
                name:'你好'
            }
        }
    }
    watch:{
        'obj.name':{
            handler(newval,oldval){
                conslog.log(newval,'新的值')
                conslog.log(oldval,'旧的值')
            },
            immediate:true,//代表watch声明了name属性之后立马执行handler方法
        }
    }

computed

computed:当一个业务逻辑或者是一个属性,在他所依赖的值变化时也要发生变化时使用,即一个属性受多个属性影响时使用
computed 是带有缓存的,我们计算某个值的变化时,会将变化的值存储起来
compiuted 函数必须要用 return 返回结果,和在 methods 使用差不多
不过使用 computed 时,性能开销会比较小一些,因为只有所依赖的值发生变化时,其才会重新计算,然后缓存
    data(){
        return {
            name:'你好'
        }
    }
    computed:{
        name(){
            return 'nihao'
        }
    }

props和data优先级谁高
父组件通过props属性向子组件传递数据,定义组件的时候可以定义一个props属性
props ===》methods ===》 data ===》computed ===》watch
vuex有哪些属性
state,getters,mutations,acyions,modules

State 类似于组件中data存放数据
getters类似于组件中computed
mutations类似于组件中methods
actions提交mutations的
modules把这四个属性再细分,让仓库更好管理

提交mutation是更改Vuex中的store中的状态的唯一方法。
mutation必须是同步的,如果要异步需要使用action。
vuex中的mutations和actions的区别?
mutations:都是同步事务
actions:可以包含任意异步操作
vuex如何持久化存储
vuex本身不支持数据持久化的存储

1.使用localStorage自己写
2.使用插件vuex-persist插件
	使用步骤:
		1.下载npm install --save vuex-persist
		2.引入:import VuexPersistence from 'vuex-persist'
		3。创建一个对象并且进行配置
			const vuexLocal = new VuexPersistence({
    			storage: window.localStorage 
    			}) 
    	4.在vuex的index文件中中进行配置
    	const store = new Vuex.Store({
          state: { ... },
          mutations: { ... },
          actions: { ... },
          plugins: [vuexLocal.plugin]
        }) 
Vuex中状态储存在哪里,怎么改变它
存储在state中,改变Vuex中的状态的唯一途径就是显式地提交 (commit) mutation,this.$store.commit("Fangfa","data"), Fangfa 为mutations属性中定义的方法
vuex的使用
1.简单的调用
state--this.$store.state.count
getters-this.$store.getters.countDouble //countDouble是vuex中定义的的方法名
mutations--store.commit('increment',{xxx:xxx})//第二个参数是提交过去的数据,可以是对象,也可以是一个变量
Actions--this.$store.dispatch('incrementN')//触发vuex中的Actions
Modules--使用了modules后上述的简单调用的时候都要加上模块名
各个属性的调用以及使用
	例如:state--this.$store.mudulesA.state.count
2.一次性引用多个属性
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState,,mapGetters } from 'vuex'
    export default {
      computed:{
           ...mapState(["xxx","xxx"])//xxx代表state中的属性
           ...mapGetters(["countDouble","xxx"])//xxx代表getters中的方法名
    }
}
vuex中module的使用
--index.js下的数据持久化处理
import { createStore } from 'vuex'
import createPersistedState from 'vuex-persistedstate'

// 三个模块
import cart from './modules/cart'
import user from './modules/user'
import category from './modules/category'

export default createStore({
  modules: {
    cart,
    user,
    category
  },
  // 配置插件
  plugins: [
    // 默认存储在localStorage
    createPersistedState({
      // 本地存储名字
      key: 'erabbit-client-pc-124-store',
      // 指定需要存储的模块
      paths: ['user', 'cart']
    })
  ]
})
vue前端设置代理
使用vue脚手架中的devServer.proxy
使用的话
1.创建一个vue.config.js的文件夹(定义的是全局的文件)
module.exports = {
  devServer: {
    proxy: 'http://localhost:4000'
  }
}
之后每次请求都会携带 http://localhost:4000 这个地址,所以我们之后的axios中的请求就直接些文件地址不需要携带基础地址了
打包路径和路由模式
打包的路径可以改变,使用publicPath
默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,例如 https://www.my-app.com/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.my-app.com/my-app/,则设置 publicPath 为 /my-app/。
module.exports = {
   //判断地址是否是生产环境,如果是生产环境的话就运行第一项如果不是就运行第二项
 publicPath: process.env.NODE_ENV === 'production'
    ? '/production-sub-path/'
    : '/'
}
// process.env取到.env.xxx中的数据,获取的是一个对象
const ykProcess: any = Object.keys(process.env).find((item: any) => process.env[item]?.endsWith('/msun-his-app-yk'));

vue路由模式
路由模式有两种:history,hash
1.表现形态不同
    history		:http://localhost:8080/login
    hash		:http://localhost:8080/#/
2.跳转请求
	history		会发送请求
    hash		不会发送请求
3.打包后自测的话需要使用hash模式,history可能会出现空白页

去除#只需要在router中的mode设置为history
介绍一下SPA以及SPA有什么缺点
SPA是什么?单页面应用
单页面的页面跳转,仅刷新局部资源,不会整个页面都刷新
优点:
	1.用户体验好,
	2.快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小
缺点:
	1.SEO优化不好
	2.性能不是特别好
	
多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新

.$route 和 $router 的区别

答:router 是 VueRouter 的 实 例 , 在 script 标签中想要导航到不同的 URL , 使用 router是VueRouter的实例,在script标签中想要导航到不同的URL,使用router是VueRouter的实例,在script标签中想要导航到不同的URL,使用router.push方法。返回上一个历史history用$router.to(-1)
$route为当前router跳转对象。里面可以获取当前路由的name,path,query,parmas等。
vue路径传值的面试题
//显示和隐式的区别就在于显示的会出现在url上,隐式的不会出现在url上
1.显示
	1.1:
		this.$router.push({
			path:'/about',
			query:{
				a:1
			}
		})
	1.2:this.$route.query.a
2.隐式
	2.1:
		this.$router.push({
			path:'/about',
			params:{
				a:1
			}
		})
	2.2:this.$route.params.a
路由导航守卫有哪些?
全局,路由独享,组件内路由守卫
全局路由守卫:beforeEach afterEach(路由跳转完成后触发)
全局写在main,js中
前置守卫
router.beforeEach((to,from,next)=>{
  if(to.path == '/login'){
    next();
  }else{
    alert('您还没有登录,请先登录');
    next('/login');//自动跳转到登录页面
  }
})
路由守卫
跟 methods: {}等同级别书写,组件路由守卫是写在每个单独的 vue 文件里面的路由守卫
beforeRouteEnter(组件进入的时候调用) ,beforeRouteUpdate(同一页面刷新不同数据的时候调用),beforeRouteLeave(离开当前路由调用)
路由独享守卫
路由独享守卫是在路由配置页面单独给路由配置的一个守卫
export default new VueRouter({ routes: [
     {
        path: '/',
        name: 'home', component: 'Home',
        beforeEnter: (to, from, next) => {
        // ...
        }
     }
    ]
})
Vue动态路由

路由之间怎么跳转?有哪些方式?
1、<router-link to="需要跳转到页面的路径">

2、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面

3、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面

4、this.$router.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数
路由之间是怎么跳转的?有哪些方式
1、<router-link to="需要跳转到页面的路径">

2、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面

3、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面

4、this.$touter.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数
编程式导航使用的方法以及常用的方法
路由跳转 : this.$router.push()  
路由替换 : this.$router.replace()  
后退: this.$router.back()  
前进 :this.$router.forward()
vue踩过的坑
第一个是给对象添加属性的时候,直接通过给data里面的对象添加属性然后赋值,新添加的属性不是响应式的

【解决办法】通过Vue.set(对象,属性,值)这种方式就可以达到,对象新添加的属性是响应式的
vue性能优化
1)路由懒加载
组件的延迟加载,可以把页面资源划分为多份,用到的时候才会按需加载,这样减少第一次加载的消耗。
2)使用 CDN 引入第三方库减少服务器压力
3)key保证唯一性
什么是 vue-loader
作用:解析和转换.vue文件。提取出其中的逻辑代码 script,样式代码style,以及HTML 模板template,再分别把他们交给对应的loader去处理

用途:js可以写es6,style样式可以写scss或less

,
category
},
// 配置插件
plugins: [
// 默认存储在localStorage
createPersistedState({
// 本地存储名字
key: ‘erabbit-client-pc-124-store’,
// 指定需要存储的模块
paths: [‘user’, ‘cart’]
})
]
})


#### vue前端设置代理

```js
使用vue脚手架中的devServer.proxy
使用的话
1.创建一个vue.config.js的文件夹(定义的是全局的文件)
module.exports = {
  devServer: {
    proxy: 'http://localhost:4000'
  }
}
之后每次请求都会携带 http://localhost:4000 这个地址,所以我们之后的axios中的请求就直接些文件地址不需要携带基础地址了
打包路径和路由模式
打包的路径可以改变,使用publicPath
默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,例如 https://www.my-app.com/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.my-app.com/my-app/,则设置 publicPath 为 /my-app/。
module.exports = {
   //判断地址是否是生产环境,如果是生产环境的话就运行第一项如果不是就运行第二项
 publicPath: process.env.NODE_ENV === 'production'
    ? '/production-sub-path/'
    : '/'
}
// process.env取到.env.xxx中的数据,获取的是一个对象
const ykProcess: any = Object.keys(process.env).find((item: any) => process.env[item]?.endsWith('/msun-his-app-yk'));

vue路由模式
路由模式有两种:history,hash
1.表现形态不同
    history		:http://localhost:8080/login
    hash		:http://localhost:8080/#/
2.跳转请求
	history		会发送请求
    hash		不会发送请求
3.打包后自测的话需要使用hash模式,history可能会出现空白页

去除#只需要在router中的mode设置为history
介绍一下SPA以及SPA有什么缺点
SPA是什么?单页面应用
单页面的页面跳转,仅刷新局部资源,不会整个页面都刷新
优点:
	1.用户体验好,
	2.快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小
缺点:
	1.SEO优化不好
	2.性能不是特别好
	
多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新

.$route 和 $router 的区别

答:router 是 VueRouter 的 实 例 , 在 script 标签中想要导航到不同的 URL , 使用 router是VueRouter的实例,在script标签中想要导航到不同的URL,使用router是VueRouter的实例,在script标签中想要导航到不同的URL,使用router.push方法。返回上一个历史history用$router.to(-1)
$route为当前router跳转对象。里面可以获取当前路由的name,path,query,parmas等。
vue路径传值的面试题
//显示和隐式的区别就在于显示的会出现在url上,隐式的不会出现在url上
1.显示
	1.1:
		this.$router.push({
			path:'/about',
			query:{
				a:1
			}
		})
	1.2:this.$route.query.a
2.隐式
	2.1:
		this.$router.push({
			path:'/about',
			params:{
				a:1
			}
		})
	2.2:this.$route.params.a
路由导航守卫有哪些?
全局,路由独享,组件内路由守卫
全局路由守卫:beforeEach afterEach(路由跳转完成后触发)
全局写在main,js中
前置守卫
router.beforeEach((to,from,next)=>{
  if(to.path == '/login'){
    next();
  }else{
    alert('您还没有登录,请先登录');
    next('/login');//自动跳转到登录页面
  }
})
路由守卫
跟 methods: {}等同级别书写,组件路由守卫是写在每个单独的 vue 文件里面的路由守卫
beforeRouteEnter(组件进入的时候调用) ,beforeRouteUpdate(同一页面刷新不同数据的时候调用),beforeRouteLeave(离开当前路由调用)
路由独享守卫
路由独享守卫是在路由配置页面单独给路由配置的一个守卫
export default new VueRouter({ routes: [
     {
        path: '/',
        name: 'home', component: 'Home',
        beforeEnter: (to, from, next) => {
        // ...
        }
     }
    ]
})
Vue动态路由

路由之间怎么跳转?有哪些方式?
1、<router-link to="需要跳转到页面的路径">

2、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面

3、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面

4、this.$router.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数
路由之间是怎么跳转的?有哪些方式
1、<router-link to="需要跳转到页面的路径">

2、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面

3、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面

4、this.$touter.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数
编程式导航使用的方法以及常用的方法
路由跳转 : this.$router.push()  
路由替换 : this.$router.replace()  
后退: this.$router.back()  
前进 :this.$router.forward()
vue踩过的坑
第一个是给对象添加属性的时候,直接通过给data里面的对象添加属性然后赋值,新添加的属性不是响应式的

【解决办法】通过Vue.set(对象,属性,值)这种方式就可以达到,对象新添加的属性是响应式的
vue性能优化
1)路由懒加载
组件的延迟加载,可以把页面资源划分为多份,用到的时候才会按需加载,这样减少第一次加载的消耗。
2)使用 CDN 引入第三方库减少服务器压力
3)key保证唯一性
什么是 vue-loader
作用:解析和转换.vue文件。提取出其中的逻辑代码 script,样式代码style,以及HTML 模板template,再分别把他们交给对应的loader去处理

用途:js可以写es6,style样式可以写scss或less
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
2023年最全前端面试(20w字)内容涵盖了许多关键主题,包括HTML5、CSS3、JavaScript、TypeScript、Vue3、React18等技术相关的知识。此外,还包括了常见的八股文题目、手写代码、项目经验等面试常见问题。以下是该面试指南的简要概述: 一、HTML5、CSS3和JS基础知识 该部分内容主要包括HTML5语义化标签、CSS3新特性、JavaScript的基础语法、DOM操作、事件绑定等。在回答面试问题时,需要清晰地介绍每个知识点的相关概念和具体应用。 二、TypeScript 这一部分涵盖了TypeScript的基本语法、类型系统、类型推断、接口等内容。回答问题时,需要对每个知识点进行逐一展开,甚至可以提供一些相关的示例代码。 三、Vue3和React18 这两个主题涵盖了Vue框架和React框架的最新版本。需要重点讨论Vue3和React18的新特性、组件开发、状态管理、路由等方面。回答时,可以结合具体的项目经验和实践来进行阐述。 四、八股文题目 在面试中,常常会遇到一些相对固定的问题,例如介绍JavaScript的执行机制、事件循环、闭包等。针对这些常见问题,需要准备充分的回答,并且能够清晰地表达自己对这些问题的理解。 五、手写代码 面试中,可能会要求候选人手写一些常见的算法题或者代码片段。为了更好地准备,可以提前练习并了解一些常见的编码题目,确保能够熟练地进行编码。 六、项目经验 在回答项目经验问题时,需要能够清晰地介绍自己的项目背景、负责的任务和实现的功能,并且能够突出自己在项目中的亮点和成果。 七、笔试 面试中可能会有笔试环节,考察候选人的编码能力和解决问题的能力。准备笔试时,可以多练习一些常见的编码题,并且熟悉常用的编码工具和技巧。 总之,这份面试指南涵盖了前端开发的关键知识点和常见问题,希望能够帮助候选人更好地准备和应对面试

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值