前端面试题【持续更新...】

前端面试题-HTML

一、 行内元素有哪些?块级元素有哪些? 空(void)元素有哪些?
	行内	span img input
	块级	div footer p h1-h6
	空	hr br 
	
	如何转换
		display:inline-block
			不独占一行,可以设置宽高
		display:nline	
			不独占一行,宽高设置不生效
		display:blobk
			独占一行,可以设置宽高
二、 页面导入样式时,使用link和@import有什么区别?
	区别一:link先有,后有@import
	区别二:浏览器先加载link标签,后加载@import
三、title与h1的区别、b与strong的区别、i与em的区别?
	title与h1的区别
		title与h1的定义
			title: 囊括了网站的信息,可以告诉浏览器搜索引擎或者用户关于这个网站的内容主题是什么。浏览器的标题,便于SEO,
			h1: 文章主题内容,内容的标题,便于蜘蛛爬取
		title与h1的区别
			title:网站标题上,h1:网页内容上
			对于SEO来说,title比h1重要
		场景
			网站的logo都是用h1标签包裹的
	b与strong的区别
		定义
			b:实体标签,给文字加粗,没含义
			strong:表示标签内字符比较重要,用以强调。加强字符语气
	i与em的区别
		定义
			i:实体标签,给文字倾斜,没含义
			em:标签内字符重要,用以强调。加强字符语气
		场景
			i:字体图标,em:专业术语(生物,医药名词)
四、img标签的title和alt有什么区别?
	区别一
		title:鼠标移上去显示
		alt:图片无法加载显示
	区别二
		SEO上,蜘蛛抓取不到图片内容,为了增加SEO效果加入alt属性描述图片内容或关键字
五、png、jpg、gif 这些图片格式解释一下,分别什么时候用?
	png	无损压缩,尺寸体积大,适合做小图标
	jpg/jpeg	压缩算法,微失真,体积小,中大图片
	gif		动图
	webp		同时支持有损和无损,相同质量的图片,具有更小的体积,兼容性不太好
一、什么是语义化标签?
	语义化标签就是指这个标签本身直观表达出了它所包含的内容是什么。
	优点:
		代码结构得到了优化,即使没有css,也能呈现出完整、清晰的结构,更加方便阅读和理解,同时提高了团队合作的效率
		有利于搜索引擎的优化
		爬虫依赖标签确定关键字的权重,可以帮助爬虫爬出更多有效信息
		方便其他设备解析(如屏幕阅读器、盲人阅读器、移动设备)以语义的方式来渲染网页
		方便后期维护
	常见的语义化标签
		< h1>~< h6>标签:标题标签,h1等级最高,h6等级最低
		header元素:用于定义页面的介绍展示区域,通常包括网站logo、主导航、全站链接以及搜索框
		nav元素:定义页面的导航链接部分区域
		main元素:定义页面的主要内容,一个页面只能使用一次。
		article元素:定义页面独立的内容,它可以有自己的header、footer、sections等
		section元素:元素用于标记文档的各个部分,例如长表单文章的章节或主要部分
		aside元素:一般用于侧边栏
		footer元素:文档的底部信息
		small元素:呈现小号字体效果
		strong元素:用于强调文本
二、before和after中双冒号和单冒号有什么区别?解释一下这两个伪元素的作用 / 伪元素和伪类的区别
	定义
		伪类:存在DOM文档中,(无标签,找不到,  只有符合触发条件时才能看到 ),  逻辑上存在但在文档树中却无须标识的“幽灵”分类。常见的 :hover		:link
		伪元素/伪对象:不存在在DOM文档中,是虚拟的元素,是创建新元素。 这个新元素(伪元素)  是某个元素的子元素,这个子元素虽然在逻辑上存在,但却并不实际存在于文档树中。常见的 ::after ::before
	区别:
		因为伪类是类似于添加类所以可以是多个,而伪元素在一个选择器中只能出现一次,并且只能出现在末尾 
		伪类本质上是为了弥补常规CSS选择器的不足,以便获取到更多信息;
		伪元素本质上是创建了一个有内容的虚拟容器;
	:after/::after和:before/::before的异同
	相同:
		都可以用来表示伪类对象,用来设置对象前的内容
	不同:
		:before/:after是Css2的写法,::before/::after是Css3的写法	

面试题-CSS

一、 介绍一下CSS的盒子模型
	CSS的盒子模型:标准盒模型 	IE盒模型
	区别:
		标准盒模型	margin + border + padding + content
		IE盒模型		margin + content
		width的计算不同 一个是只有content,一个是包含了content + padding + border
	如何转换
		border-box : content-box	标准盒模型
		border-box : border-box		IE盒模型
二、 line-height和heigh区别
	line-height:行高,每一行的高度,可写比例1,1.5等,文字换行会增大盒子的高度(行数*行高)
	heigh:死值,就是盒子的高度
三、CSS选择符有哪些?哪些属性可以继承?
	CSS选择符
		*	通配
		#	id选择器
		. 	类选择器
		div p	 标签选择器
		+	相邻选择器
		ul li 	后代选择器
		> 	子元素选择器
		a[href]  属性选择器
	CSS哪些属性可以继承
		字体系列:font-family		font-weight
		文字系列:font-size		color		line-height	text-align...
		元素可见性:visibility
		列表布局属性:list-style:列表风格,包括list-style-type、list-style-image等
		光标属性:cursor:光标显示为何种形态
		不可继承:border 	padding 	margin
四、CSS优先级算法如何计算?
	优先级比较:!important 	>	内联样式		>	 id   >   class   > 标签   >   通配
	权重计算
		内联: 1000
		id: 	  100
		类:		10
		标签/伪	1
		* > +    0
五、用CSS画一个三角形
	边框画
		border:   13px  solid	reba(225, 225, 225, 0)
		border-buttom : 13px  solid	reba(225, 225, 225, 1)
六、一个盒子不给宽度和高度如何水平垂直居中?
	第一种
		display:flex 
		justify-content:center
		align-items:center
	第二种
		position:absolute;
		left:50%;? ? /* 定位父级的50% */
		top:50%;
		transform: translate(-50%,-50%); /*自己的50% */
	transform
七、display有哪些值?说明他们的作用。
	none: 表示隐藏对象,不留位置。
	inline(默认值):表示指定对象为内联元素。
	block: 指定对象为块元素。
	inline-block: 指定对象为内联块元素。
	flex: 将对象作为弹性伸缩盒显示。
	table: 指定对象作为块元素级的表格。
八、对BFC规范(块级格式化上下文:block formatting context)的理解?
	定义
		块级格式化上下文
	原则
		一个元素开启BFC,那么内部元素样式不影响外部元素
	如何触发
		float值不是none
		overflow值不是visible
		display值是inline-block	table-cell
		position值是absoute	fixed
九、清除浮动方式有哪些?
	1. 触发BFC
	2. 多包裹一层,设置clear:both
	3.	:after{
			content:' ';
			display:block;
			clear:both;
		}	
十、在网页中的应该使用奇数还是偶数的字体?为什么呢?
	偶数:让文字在浏览器上表现更好看
	ps:UI给前端一般设计图都是偶数的,不管布局还是转换px,方便
十一、position有哪些值?分别是根据什么定位的?有什么作用?
	static[默认]	没有定位
	fixed		固定定位		相对于浏览器窗口进行定位
	relative	相对定位		相对于其正常位置进行定位
	absolute绝对定位		相对于 static 定位以外的第一个父元素进行定位
	relative和absolute区别
		relative不脱离文档流,absolute脱离文档流
		relative相对于自身,absolute相对于 static 定位以外的第一个父元素进行定位
		relative如果有left、right、top、bottom、只会生效left、top
		absolute如果有left、right、top、bottom、都会生效
十二、双飞翼布局
	html代码:
		<div class="container">
			<div class="content">
				<div class="center">中间</div>
			</div>
			<div class="left">左边</div>
			<div class="right">右边</div>
		</div>
	css代码:
			.content,.left,.right{
				float: left;
				height: 200px;
			}
			.content{
				width: 100%;
				background-color: red;
			}
			.left{
				width: 300px;
				background-color: pink;
				margin-left: -100%;
			}
			.right{
				width: 300px;
				background-color: grey;
				margin-left: -300px;
			}
			.center{
				height: 200px;
				margin-left: 300px;
				margin-right: 300px;
			}
			.container::after{
				content: "";
				display: block;
				clear: both;
			}				
十三、什么是CSS reset?
	reset		是css文件,用来重置css样式
	normalize		也是css文件,也用来重置css样式,但是它增强了跨浏览器渲染的一致性,是一个重置样式库
十四、css sprite是什么,有什么优缺点
	sprite	精灵图 / 雪碧图
		多个小图标合并成一个大图片
	优点:减少http请求次数,提升性能
	缺点:维护较差(图片位置进行修改 / 内容宽高进行修改)
十五、display- none;与visibility- hidden;的区别
	1. 占用位置区别
		display- none		不占用位置
		visibility- hidden	虽然隐藏了,但是占用位置
	2. 重绘和回流
			都会产生重绘
			display- none		会产生一次回流
			visibility- hidden	不会回流
			ps:回流一定重绘,重绘不一定回流
				产生回流的情况:改变元素位置(left、top。。。)显示隐藏元素
				产生重绘情况:样式改变(背景颜色。。。)		
十六、opacity和rgba区别
	共同:实现透明效果
		opacity:0-1之前 
			0完全透明 1不透明
		rgba:R红色	G绿色	B蓝色	取值为正整数 / 百分数 	A透明度取值0-1之间
	区别
		opacity会继承父元素的opacity属性
		rgba不会继承
十七、如何关闭IOS键盘首字母自动大写?
		<input type='text' autocapitalize="off" />
	在form元素上设置该属性,让里面的每个元素都继承autocapitalize的设置。
		<form autocapitalize="off">
		  <input type="text"  placeholder="继承 form 的设置">
		  <textarea></textarea>
		</form>
	autocapitalize="words",每个单词的开头字母会自动大写。
	autocapitalize="characters",每个字母都会大写。
	autocapitalize="sentences",每句开头字母会自动大写。
十八、怎么让Chrome支持小于12px的文字?
	zoom:变焦;
		.span1 {
			font-size: 12px;
			display: inline-block;
			zoom: 0.8;
    	}
	-webkit-transform:scale():缩放;
		-webkit-transform:scale()只对可以定义宽高的元素生效,如果是行内元素的话,应该先转为行内块元素。
		.span1 {
			font-size: 12px;
			display: inline-block;
			-webkit-transform:scale(0.8);
		}
十九、rem和em区别,px和rpx的区别
	px像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。
	em相对于父元素的font-size
	rem相对于根元素的font-size
	rpx 是微信小程序解决自适应屏幕尺寸的尺寸单位。微信小程序规定屏幕的宽度为750rpx。
二十、IOS系统中元素被触摸时产生的半透明灰色遮罩怎么去掉?
	a,button,input,textarea{-webkit-tap-highlight-color: rgba(0,0,0,0;)}
二十一、webkit表单输入框placeholder的颜色值能改变吗?怎么改变
	input::-webkit-input-placeholder {
		color: red;
	}
二十二、禁止触发系统菜单和长按选中
	禁止ios 长按触发系统的菜单,禁止ios&android长按时下载图片
		html,body{
		  touch-callout: none;
		  -webkit-touch-callout: none;
		  
		  user-select: none;
		  -webkit-user-select: none;
		}
	禁止ios和android用户选中文字
		html,body{
		  user-select: none;
		  -webkit-user-select: none;
		}
二十三、自适应布局
	让同一个页面自动适应不同大小的设备,从而解决为不同设备提供不同版本的页面问题。
		移动端设置meta
	方案【移动端】:淘宝无限适配 flexible.js + 布局单位使用rem
	原理:
		window.onresize = function(){} 监听设备的宽高更改
		获取到元素的视图宽高 / 10
		将值赋值给节点
		每次更改调用2,3步骤
二十四、响应式布局
	一个url可以响应多端
	语法结构
		  @media only screen and (min-width: 1200px) {
			html {
			  font-size: 20px;
			}
		  }
     	only:可以排除不支持媒体查询的浏览器
		screen:设备类型
		min-width | max-width
		min-height | max-height 
	问题:
		pc端1980的图,手机端加载慢怎么办
	响应式图片
	
		<picture>
			<source srcset='1.jpg' media='(min-width:1000px)'>
			<source srcset='2.jpg' media='(min-width:700px)'>
			<img srcset='3.jpg'>
		</picture>
		
		picture标签往往和<source>元素(可以多个)、<img>元素(最多一个)一同使用。
		img标签提供了 srcset 用来提供同一图片的不同大小的版本
		<img src="" srcset="" sizes="" alt="">
		
		其他例子:
			<!-- 根据设备DPR提供不同尺寸的图片 -->
			<img
				src="srcset-x1.png"
				srcset="
					srcset-x1.png 1x,
					srcset-x2.png 2x,
					srcset-x3.png 3x" 
				alt="srcset for img"
			/>
			<!-- 根据视窗大小提供不同尺寸的图片 -->
			<img
				alt="A baby smiling with a yellow headband."
				src="/baby-640w.jpg"
				srcset="
					/baby-320w.jpg 320w,
					/baby-640w.jpg 640w,
					/baby-960w.jpg 960w,
					/baby-1280w.jpg 1280w,
					/baby-1920w.jpg 1920w,
					/baby-2560w.jpg 2560w"
				sizes="
					(max-width: 500px) calc(100vw - 2rem),
					(max-width: 700px) calc(100vw - 6rem),
					calc(100vw - 9rem - 200px) "
			/>

二十五、网页布局方案
	什么情况下采用响应式布局?
		Bootstrap	网页加载慢	兼容
		数据不是特别多,用户量不是特别大,纯展示类的项目适合响应式布局
		例如:网站官网,专题页面
		特别追求性能的项目,不太适合响应式。因为如果添加了很多的响应式会造成加载速度变慢
		
	PC + 移动端应该做什么样的布局方案
		访问量还可以或比较大,类似淘宝
		PC是一套,会加入一点点响应式
		移动端是一套,会使用自适应布局方式
	PC的设计图
		UI给定1980的图,	笔记本是1280的,如何设计?
		第一种:将1980的图等比缩放成1280的
		第二种:换1980的电脑
	移动端设计图
		UI永远给定的是750,

面试题-JS

数组的方法有哪些?
	some()
		不改变原数组,返回Boolean
		有一项匹配返回true,全部不匹配才false
	every()
		不改变原数组,返回Boolean
		有一项不匹配返回false,全部匹配才true
	find()
		不改变原数组,返回具体的值 / undefined
		返回第一个匹配到的值,返回具体的内容,并且只寻找一次,没有返回undefined
	filter()
		不改变原数组,返回所有匹配的值
		返回一个新数组,返回所有匹配的值,如果没有值则返回空数组,不会对空数组进行检测,要有return
	unshift()	头+1
		改变原数组,返回新数组长度
		向数组首位添加新内容
	shift()	头-1
		改变原数组,返回被删除的项
		删除数组第一项
	push()	尾+1
		改变原数组,返回新增后数组长度
		向数组末尾添加新内容
	pop()	尾-1
		改变原数组,返回被删除的项
		删除数组最后一项
	slice(n, m)
		不改变原数组,返回一个新数组
		按照条件查找出其中的部分内容从n开始,到m处(不包含m)
		array.slice(0)原样输出内容,可以实现数组克隆
	splice(n,x,m)
		增
			改变原数组,返回空数组
			从n开始(包含),删除x项,把m添加至n的前面
		删
			改变原数组,返回删除的数组
			从n开始(包含),删除x个
	sort()
		改变原数组,返回新数组
		对数组进行排序(默认从小到大排序,根据字符串的Unicode编码排序)
	join('、')
		不改变原数组,返回拼接好的字符串
		用指定分隔符将数组每一项拼接为字符串
	replace
	concat()
		不改变原数组,返回连接好的新数组
		拼接数组 和 数组 和 具体的值
	indexOf()
		不改变原数组,返回索引 / -1
		检测当前值在数组中第一次出现位置的索引
	lastIndexOf()
		不改变原数组,返回最后索引 / -1
		检测当前值在数组中最后一次出现位置的索引
	includes()
		不改变原数组,返回Boolean
		判断一个数组是否包含一个指定的值
	reverse()
		改变原数组,倒序后新数组
		把数组倒过来排列
	forEach()
		不改变原数组,没有返回值
		循环遍历数组每一项
		forEach中不能使用continue和break,forEach中不能跳出,只能跳过(return跳过)
JS继承有哪些方式
	方式一:ES6
		class myClass{}
		class children extends myClass{
			constructor(){
				super()
			}
		}
	方式二:原型链继承
		function myClass(){}
		function children(){}
		children.prototype = new myClass()
		共享
	方式三:借用构造函数继承
		function myClass(){}
		function children(){
			myClass.call(this)
		}
		实现不了共享
	方式四:组合继承
		function myClass(){}
		function children(){
			myClass.call(this)
		}
		children.prototype = new myClass()
数据类型和考题
	基本类型
		Number		String	Boolean		Null 		Undefined	 	Symbol 	BigInt
	引用类型
		object
	隐式转换考题
		字符串和其他类型相加,都会返回字符串
		undefined和数值相加,返回NaN
null和undefined的区别
	1、 定义
		undefined原理上可以说是没有找到		无的值
		null原理上意思为空对象		无的对象
	2、null和undefined的区别
		1.首先是数据类型不一样
			用typeof进行判断,null的数据类型是object,undefined的数据类型是undefined。
		2.null和undefined 两者相等,但是当两者做全等比较时,两者又不等。(因为它们的数据类型不一样)
		3.转化成数字的值不同
			Number(null)		 0
			Number(undefined)		NaN
		4.null代表"空",代表空指针;undefined是定义了没有赋值

	3、null和undefined的运用场景
			undefined
				1、变量被声明时,但没有赋值时,就等于undefined
				2、调用函数时,应该提供的参数没有提供,该参数等于undefined
				3、对象没有赋值的属性,该属性的值为undefined
				4、函数没有返回值时,默认返回undefined
			null
				1、作为函数的参数,表示该函数的参数不是对象(不想传递参数)
				2、作为对象原型链的终点
				3、如果定义的变量准备在将来用于保存对象,此时可以将该变量初始化为null
相等==和全等===有什么不同?
	== :比较的是值
		string == number || boolean || undefined 都会隐式转换
		通过valueOf转换(valueOf 方法通常由 JavaScript 在后台自动调用)
	=== :除了比较值,还会比较类型
JS微任务和宏任务
	JS是单线程语言
	JS代码执行流程:同步执行完 -> 事件循环
	同步的任务都执行完了,才会执行事件循环的内容
		进入事件循环:请求、定时器、事件。。。
	事件循环中包含【微任务、宏任务】
	微任务:promise.then
	宏任务:setTimeout
	要执行宏任务的前提是清空了本轮的微任务
	流程:同步 -> 事件循环【微任务、宏任务】 -> 微任务 -> 宏任务 -> 微任务
JS作用域
	在var声明时,除函数外,JS没有块级作用域,在函数中 变量 函数 方法 属性 的可执行作用区域 就是作用域    
    函数内部可以调用外部函数变量,外部函数不可以调用内部函数变量
    查找变量时,函数内部变量优先
    JS声明提升
    JS有声明提升
    提升优先级: 变量声明 > 函数声明 > 参数 > 变量提升
    面试时: 1. 先看函数作用域有没有声明,变量声明提升
            2. 普通声明函数不看写的顺序
JS判断变量是不是数组,你能写出哪些方法?/ 判断是否是数组?
	1. Array.isArray()
	2. 原型
	3. instancesof
	4. 
slice是什么用处?splice是否会改变原数组?

JS数组去重
	var arr1 = [1,2,2,3,3,4,1,1,1,1,1,4,4]
	第一种
		new Set()返回的是一个对象
			Array.from(new Set(arr1));
			function unique0(arr){    
				return [...new Set(arr)];
			}
	  
	第二种
		function unique(arr){
			var brr = [];
			  for(var i=0;i<arr.length;i++){
				if(brr.indexOf(arr[i]) == -1){
				  brr.push(arr[i])
				}
			  }
			return brr
		}
		console.log(unique(arr1))

	第三种
		function unique1(arr){
			let brr = [];
			arr = arr.sort()
			for (let i = 0; i < arr.length; i++) {
			    if(arr[i] !== arr[i-1]){
				    brr.push(arr[i])
			    }      
			}
			return brr
		}
找出多维数组最大值

给字符串新增方法实现功能

找出字符串出现最多次数的字符以及次数
	判断一个字符串中出现次数最多的字符,并统计次数
		let s = 'aaaaddddddddddddvbvvvvvvvvrsdaaaaaaaaaaasddddddd'
	  
		 function Strnum(str){
			let obj = {};
			for(var i=0;i<str.length;i++){	
				let key = str[i]
				if( !obj[key] ){
					obj[key] = 1
				}else{
					obj[key]++
				}
			}
			return obj;
		 }
		Strnum(s)

	找出字符串出现最多次数的字符以及次数
		  let s = 'aaaaddddddddddddvbvvvvvvvvrsdaaaaaaaaaaasddddddd'
		  let obj = {}
		  
		  for(let i=0;i<s.length;i++){
			let key = s[i]
			obj[key] ? obj[key]++ : obj[key] = 1
		  }
		  console.log(obj)
		  
		  let max = 0
		  for(let key in obj){//获取最大值
			if(max < obj[key]){
				max = obj[key]
			}
		  }
		  for(let key in obj){
			if(max === obj[key]){
				console.log('出现次数最多的是' + key);
				
				console.log('出现' + max + '次');
			}
		  }
new操作符具体做了什么
	1. 创建一个空的对象
	2. 将空对象的原型链指向 / 连接构造函数原型链
	3. 将空对象作为构造函数的执行上下文、也就是更改this指向
	4. 对构造函数有返回值的处理判断:return返回的是基本数据类型则不受影响,返回引用类型则new不起作用
闭包
	闭包是一个函数到创建函数作用域的连接 闭包关闭了函数的自由变量
    闭包可以:内部函数访问外部函数变量
    闭包例子1: 
          var li = document.getElementsByTagName('li')
          for(var i=0;i<li.length;i++){
              li[i].onclick = function(){
                console.log(i)
              }
          }
		当上述代码控制台输出时,内容都是一样的,是因为for循环先执行完毕 此时i已经循环完成了
		那么需要i随着点击不同的li而变化就需要使用闭包
          var li = document.getElementsByTagName('li')
          for(var i=0;i<li.length;i++){
          (
              li[i].onclick = function(){
              console.log(i)
              }
          )(i)
          }
		将i作为参数,使其内部函数引用外部函数并且i不会被销毁,而是在内存中继续使用.这就是闭包
	闭包例子2: 
		 function fn(){
			var a = 1
			return function gn(){
				console.log(a)
			}
		 }
		var fun = fn()
		fun()
		返回的函数引用了函数内部的变量,使得变量a不能被销毁,而是一直在内存中。这也是闭包
    闭包会造成变量的生命周期延长,会使变量滞留在内存中.会造成内存的损耗.在ie中甚至会内存泄漏
原型链
	原型: 为了对象的共享属性和共享方法建立的
		对象原型: __proto__
		函数原型: prototype
	对象或属性查找顺序
		对象本身 -> 构造函数 -> 对象原型 -> 构造函数原型 -> 对象原型的原型
	原型链 : 把原型串联起来 其顶端是null
说一下call、apply、bind的区别
	共同点: 功能是一致的 都是改变函数体内的this指向 
	区别: 1. 
			函数.call()  可以立即执行
			函数.apply() 可以立即执行
			函数.bind()  不会立即执行,返回一个函数  所以需要 函数.bind()()
			2.参数不同
			函数.call(fn, c1, c2...)  多个参数顺序输入
			函数.apply(fn, [c1, c2]) 参数为数组
			函数.bind(fn, c1, c2...)()  参数一个一个传入
	场景: 
		日常使用call
		apply取数组最大值
			Math.max.apply(null, arr1)
		bind使用在onclick改变this指向另外一个元素时
		
		function Parent(age, p){
			this.age = age;
			this.p = p
		}
		function Child(){
			this.name = '素肚'
		}
		
		Child.call(Parent, 25, 12)
		Child.apply(Parent, [25, 12])	
		Child	.bind(Parent, 25,12)()	
深拷贝和浅拷贝
	浅拷贝:  只复制了内存中地址的指向(引用)   内存中数据改变都会改变
		一些浅拷贝实现 : arr2=arr1  obj2 = Object.assign(obj1)
	深拷贝:  在内存中重新开辟一个空间复制过来  内存中一个对象属性的改变不影响另外一个对象的属性  两个空间数据互不影响
		深拷贝的实现:
			1. JSON.parse(JSON.stringify())  一维数组
			2. 递归
				let arr = [1,3,6,8,[2,5,6,4,85],1]
				
				function Clone(arr){
					let newArr = Array.isArray(arr) ? [] : {};					
					for(let i in arr){
						newArr[i] = typeof arr[i] == 'object' ? Clone(arr[i]) : arr[i]
					}
					return newArr
				}
	
JS中的垃圾回收机制
	JS有两种垃圾回收机制的方式
		1. 标记清除
			变量进入执行栈 -> 标记 ‘进入环境’ 
			变量离开执行栈 -> 标记‘离开环境’ 
			添加其他标记 -> 在此之后去掉环境中的变量和被环境变量引用的变量,讲这些没用的变量回收
		2. 引用计数
			跟踪一个值的引用次数,引用时+1,不再引用时-1,当引用次数为0即回收
JS同源策略
js的arguments到底是什么?
.native 修改事件为原生事件
事件冒泡

字符串转数值

延迟加载JS有哪些方式?
1. 当外部加载js文件时,应该将js脚本放在最后,当全部的文件都加载完成后,再开始加载执行js脚本。
2. 在每一个脚本文件最外层设置一个定时器。setTimeout
3. 动态创建script标签,当页面的全部内容加载完毕后,在执行创建挂载。
4. 通过window.onload来执行脚本代码。
5. async
	加载完毕立即执行,谁先加载完毕谁执行
	<script async src="./a.js"></script>
6. defer	
	浏览器html解析完毕才会执行js代码,顺次执行js脚本
	<script defer src="./a.js"></script>	
WebSocket实时通讯
	所有浏览器都支持
	服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
		(1)建立在 TCP 协议之上,服务器端的实现比较容易。(升级协议)
		(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
		(3)数据格式比较轻量,性能开销小,通信高效。
		(4)可以发送文本,也可以发送二进制数据。
		(5)没有同源限制,客户端可以与任意服务器通信。
		(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
		
	var ws = new WebSocket("wss://echo.websocket.org");
	ws.onopen = function(evt) { 
	  console.log("Connection open ..."); 
	  ws.send("Hello WebSockets!");
	};
	ws.onmessage = function(evt) {
	  console.log( "Received Message: " + evt.data);
	  ws.close();
	};
	ws.onclose = function(evt) {
	  console.log("Connection closed.");
	};    
	
Object.freeze() 方法可以冻结一个对象。

vue性能优化
	加载优化
		1. 减少http请求 / 合并http请求
		2. 图标的雪碧图 / 早期的优化手段,现在已废弃
		3. script标签位置在body末尾 / defer / async
		4. link标签
		5. 分包加载
	图片优化
		1. 响应式图片		使用img的srcset,根据不同分辨率显示不同尺寸图片
		2. 图片懒加载		
		3. 图片压缩
		4. 尽量用webp格式
		5. 小图片使用字体图标
	渲染优化
		1. 尽量减少重绘和回流
		2. 使用虚拟dom
		3. 动画元素一定要脱离文档流。不使用left,top而使用transform和opacity
		4. 动画使用requestAnimationFrame / cancelAnimationFrame,不要使用定时器
	首屏加载优化
		1. 懒加载
		2. 长列表
			一万条数据,分页。触底加载更多
		3. 项目的html,css,图片,js文件压缩打包
		4. 服务端渲染 / 预渲染 视项目而定		
	vue优化
		1. keep-alive 缓存组件
		2. 路由懒加载
		3. v-if和v-show的合理应用
		4. computed、watch、methods的合理使用
		5. Object.freeze 冻结对象,纯展示类接口数据
		6. 使用ui组件,按需引入
		
keep-alive(保证组件不被销毁)和activated、deactivated函数
Vue中在mounted中通过this.$refs但是获取不到属性的几种常见情况分析
nexttick使用场景和原理
下拉加载更多和上拉刷新
vue项目SEO优化方案——prerender-spa-plugin插件实现预渲染
javascript设计模式之单例模式、工厂模式、发布订阅模式以及观察者模式
登录后跳转原页面
onclick事件中的this, addEventLister中的this,以及addEventLister的第三个参数


面试题-Vue

Vue 响应式原理
	<script>
        class Vue{
            constructor(options){
                this.$options = options
                this.$watchEvent = {}
                if( typeof options.beforeCreate == 'function'){
                    options.beforeCreate.call(this)
                }
                //绑定data
                this.$data = options.data
                this.proxyData()
                this.observe()
                //绑定dom节点
                this.$el = document.querySelector(options.el)
                //给dom节点进行操作 / 模板解析
                this.compile(this.$el)
            } 
            //1. 给Vue大对象赋data中的属性, 
            //2. data和Vue中的属性双向互通(劫持)  
            //问题:数据发生了变化视图层没有更新         
            proxyData(){
                for (const key in this.$data) {
                    /**
                     * 语法:Object.defineProperty(obj,property,descriptor)
                        参数一:obj
                            绑定属性的目标对象
                        参数二:property
                            绑定的属性名
                        参数三:descriptor
                            属性描述(配置),且此参数本身为一个对象                     *
                     *  */
                    Object.defineProperty(this, key, {
                        get(){
                            return this.$data[key]
                        },
                        set(val){
                            this.$data[key] = val
                        }
                    })
                }
            }           
            //触发数据发生变化执行watch中的updata
            observe(){
                for (const key in this.$data) {
                    let value = this.$data[key]
                    let that = this
                    Object.defineProperty(this.$data, key, {
                        get(){
                            return value
                        },
                        set(val){
                            value = val
                            console.log(that.$watchEvent[key]);
                            if( that.$watchEvent[key] ){
                                that.$watchEvent[key].forEach((item, index) => {
                                    console.log(item);
                                    item.updata()
                                })
                            }
                        }
                    })
                }
            }
            //模板解析
            compile(node){
                // console.log(node);
                node.childNodes.forEach((item, index) => {
                    // console.log(item, index);
                    //元素节点
                    if(item.nodeType == 1){
                        //对元素节点进行事件判断
                        //是否有点击
                        if(item.hasAttribute('@click')){
                            //@click后绑定的属性值
                            let vmKey = item.getAttribute('@click').trim()
                            //绑定事件
                            item.addEventListener('click',(event) => {
                               this.eventFn = this.$options.methods[vmKey].bind(this)
                               return this.eventFn(event)
                            })
                        }
                        //是否有v-model
                        if(item.hasAttribute('v-model')){
                            let vmKey = item.getAttribute('v-model').trim()
                            //检查data里是否有绑定的值
                            if(this.hasOwnProperty(vmKey)){
                                item.value = this[vmKey]
                            }
                            item.addEventListener('input',(event) => {
                               return this[vmKey] = item.value
                            })
                        }
                        //递归
                        if(item.childNodes.length > 0){
                            this.compile(item)
                        }
                    }
                    //文本节点
                    if(item.nodeType == 3){
                        //匹配所有{{}}的正则
                        let reg = /\{\{(.*?)\}\}/g
                        let text = item.textContent
                        // console.log(text);
                        //给节点赋值
                        item.textContent = text.replace(reg,(match, vmKey) => {
                            vmKey = vmKey.trim()
                            //hasOwnProperty(propertyName)方法 是用来检测属性是否为对象的自有属性,如果是,返回true,否者false; 参数propertyName指要检测的属性名;
                            if(this.hasOwnProperty(vmKey)){

                                let watcher = new watch(this, vmKey, item, 'textContent')
                                console.log(watcher, this.$watchEvent[vmKey]);
                                if(this.$watchEvent[vmKey]){
                                    this.$watchEvent[vmKey].push(watcher)
                                }else{
                                    this.$watchEvent[vmKey] = []
                                    this.$watchEvent[vmKey].push(watcher)
                                }
                                return this.$data[vmKey]
                            }
                        })
                    }
                })
            }
        }
        class watch{
            constructor(vm, key, node, attr){
                this.vm = vm
                this.key = key
                this.node = node
                this.attr = attr
            }
            //执行改变updata操作
            updata(){
                this.node[this.attr] = this.vm[this.key]
            }
        }
        new Vue({
            el:'#app',
            data:{
                str:'我是data里的',
                b:'111111111111111111111111'
            },
            beforeCreate(){
                // console.log('函数0000000');
            }
            ,
            methods:{
                btn(e){
                    this.str = '修改数据改变视图'
                    console.log('点点点',this.str);
                }
            }
        })
    </script>
vue2生命周期有哪些?第一次进入组件或页面会执行哪些生命周期?
1. 概念: 
		生命周期 -> 跟人一样从人的出生到过程到结束
		Vue的生命周期 -> Vue中每一个组件都是独立的,每个组件都有自己的生命周期
		组件从创建, 数据初始化, 挂载, 更新, 销毁的过程,就是组件的生命周期
2. 有哪些?
		new Vue() -> init ->
		beforeCreate
		created
		组件初始化阶段,可以获取this.$data, 操作data, computed
		beforeMount
		mounted
		组件挂载阶段, 可以获取this.$data, this.$el, 操作DOM节点
		beforeUpdate
		updated
		数据更新阶段, 数据发生改变触发该生命周期钩子
		beforeDestroy
		destroyed
		组件销毁阶段, 关闭组件或者页面销毁
3. 打开一个组件, 会执行哪些生命周期?
		beforeCreate
		created
		beforeMount
		mounted
4. 如果加入了keep-alive会多哪两个生命周期?
		activated	激活
		deactivated	失活
5. 如果加入了keep-alive,第一次进入页面会执行哪些生命周期?
		beforeCreate
		created
		beforeMount
		mounted
		activated
6. 如果加入了keep-alive,第二次进入页面会执行哪些生命周期?
		只会执行activated
谈谈对keep-alive的了解
	功能:缓存组件不被销毁,提升性能
	使用:
		当进入详情页的时候,进行keep-alive, 下次再进入直接走缓存,不用再次请求
	写法:keep-alive包裹组件
	生命周期:activated, deactivated
v-if和v-show区别
	v-show	display的none/block切换
	v-if 创建与删除dom操作
	使用场景:
		v-show用户频繁切换,显示隐藏开销小
		v-if用户首次进入页面,不会加载盒子
v-if和v-for的优先级
	vue2中 v-for优先级高于v-if,在源码中体现
	不会在同一个节点同时使用
	vue3中 v-if优先级高于v-for,
ref是什么?
	ref是写在标签上的一个属性,是用来获取dom的
	写法:
		ref='imgs'
		this.$refs.imgs
nextTick是什么?
	nextTick()获取更新后的dom内容
	是异步 
	当使用$refs获取dom,当dom里的数据发生改变则需要在nextTick中使用nextTick()获取更新后的内容
	使用better-scroll,就需要使用该属性
scoped原理
	作用:让样式在本组件中生效,不影响其他组件
	原理:给节点新增自定义属性,然后css根据属性选择器添加样式
Vue中如何做样式穿透
	 1. /deep/
	 	/deep/.el-card__body {
			padding: 10px;
	   	}
	2. >>>
		.wrapper >>> .swiper{
   			background: #fff
		}
	3. ::v-deep
		.userstatus ::v-deep button {
		  height: 25px;
		}
vue的组件传值
	1. 父传子
		第一种:
			父:<child :msg="msg"></child>
			子:props:['msg'], / 
				props:{
					msg:{
						type:string,
						default:"这是默认值"
					}
				},
		第二种:
			父:<child ref="childForRef"></child>
				this.$refs.childForRef;
	2. 子传父
		第一种:
			父:<child @click="lcclick"></child>
				  methods: {
					lcclick(){
					  alert('子传父')
					}
				  },
			子:<button @click="lcalter">点我</button>
				lcalter(){
				  this.$emit('lcclick')//通过emit来触发事件
				}
		第二种:
			this.$parent
	3. 兄弟组件传值
		bus总线 / 单独定义一个bus文件或绑定至vue原型上。组件内引入
		兄:methods 中 this.$bus.$emit()
		弟:mounted 中 this.$bus.$on()
	4. 跨级传值
		祖 -> 孙:$attrs
		需要在中间组件 v-bind="$attrs"
		孙 -> 祖:$listeners
	5. 其他
		Vuex通信
		路由传值
			显式传值	this.$router.query
			隐式传值	this.$router.params
vue的事件修饰符
	.stop		阻止事件冒泡
	.prevent		阻止默认事件
	.capture		使用事件的捕获模式
	.self		只有event.target是当前操作的元素才触发事件
	.once		只触发一次
	.passive		事件的默认行为立即执行,无需等待事件回避执行完毕
computed、methods、watch有什么区别
	computed
		有缓存,计算属性,某一个值改变会重新计算并返回
	methods
		没有缓存,
	watch
		数据或路由发生改变才响应执行
props和data优先级谁高
	props > methods > data > computed > watch
vuex有哪些属性?
	State
		1. this.$store.state.count
		2. import { mapState } from 'vuex'
		  ...mapState({
			// 箭头函数可使代码更简练
			count: state => state.count,
		  })
		3. ...mapState([
			  // 映射 this.count 为 store.state.count
			  'count'
			])
	Getter
		是 store 的计算属性
		1. this.$store.getters.doneTodosCount
		2. ...mapGetters([
			  'doneTodosCount',
			])
	Mutation
		更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。可执行同步任务
		1. this.$store.commit('increment')
	Action
		Action 提交的是 mutation,而不是直接变更状态。
		Action 可以包含任意异步操作。
		1. this.$store.dispatch('increment')
	Module
		每个模块拥有自己的 state、mutation、action、getter、
vuex是单向数据流还是双向数据流
	Vuex是单向数据流,必须通过mutation去修改
vuex中的mutations和actions的区别
	共同点:都能写函数方法
	不同:
		mutations能修改state中的数据,同步任务
		actions是可以做异步操作,而且它不能直接改变state,需要提交mutations
vuex如何做持久化存储
	1. cookie或locaStorage
	2. 使用第三方插件vuex-persist插件 实际上也是使用locaStorage
	3. npm install vuex-persistedstate --save
		在store下的index.js中
		import createPersistedState from "vuex-persistedstate"
		const store = new Vuex.Store({
		 // ...
		 plugins: [createPersistedState()]
		})
		默认存储到localStorage

		想要存储到sessionStorage,配置如下

		import createPersistedState from "vuex-persistedstate"
		const store = new Vuex.Store({
		 // ...
		 plugins: [createPersistedState({
		  storage: window.sessionStorage
		 })]
		})
Vue设置代理 / 前端处理跨域的方式?
	跨域的方法有cors、Proxy正向代理、Nginx反向代理、Jsonp
	cors
		在服务器端的响应中加入额外的HTTP头,使浏览器能跨域访问资源。
	Proxy正向代理
		module.exports = {
		  devServer: {
			proxy: {
			  '/': {
				target: 'http://localhost:3000', // 目标服务器
				changeOrigin: true
			  }
			}
		  }
		} 	
vue项目打包完成出现空白页怎么解决?
	1. 配置publicPath:'./'
	2. 路由模式:hash
vue的路由模式?区别是什么?
	hash模式 和 history模式
	区别:
		前端自测:hash模式,每次刷新页面直接更改 # 后的地址,不会重新发送请求
		项目上线:history模式,后端需要做重定向。并且history 每次刷新会重新请求服务器,如果后端没有及时响应就会报错404
说一下什么是SPA?它的缺点
	SPA是单页面应用
	缺点:SEO优化不好、首页加载慢、性能不好
vue路由导航守卫有哪些?
	1. 全局导航守卫
		beforeEach
		beforeResolve
		afterEach
	2. 路由独享守卫
		beforeEnter
	3. 组件内导航守卫
		beforeRouuteEnter
		beforeRouuteUpdate
		beforeRouuteLeave		
vue动态路由
	场景1:
		跳转不同商品详情页
		path:'list/:id'
	场景2:
		根据角色动态生成路由
		
vue2双向绑定原理
	使用Object.definedProperty来做数据劫持,再结合发布-订阅者模式来实现
	1. data中的数据展示页面上:
		实现compile解析器,解析节点中的v-及{{}},并替换成data中对应的数据。
		每替换一次就需要生成一个与之对应的订阅者watcher,并给watcher绑定一个更新函数updata,并添加进订阅者集合
	2. data中的数据改变同步到页面上:
		实现一个observer监听器,通过Object.definedProperty劫持data中的数据,
		数据一旦发生改变,找到对应watcher触发其中的updata进行更新节点内容
	3. 视图中的数据改变影响data:
		监听到v-model时,绑定input事件,当input的value改变,将value赋值给data中对应的数据
对比一下vue3双向绑定 / vue项目的缺点
	比较费性能,数据结构比较复杂的时候,用的递归实现的set和get
	如果往data种新增数据,不是响应式的,得用vue.set来做响应式
	不能用数组下标去改变数组元素,解决方法是对push、shift方法的重写

diff算法
	功能:提升性能
	实现:虚拟dom / 将dom虚拟化,只操作一次dom
		新老节点替换规则:
			新老节点不同,暴力删除旧的,创建新的
			新老节点相同,
			ps: 提升性能,一定要加入key, key是唯一标识。表示在更改前后是否是同一节点.
				只能同级比较,不可以跨级
		旧前 - 新前		旧后 - 新后		旧前 - 新后		旧后 - 新前		查找		创建	
谈一下MVVM框架?
	MVVM,是Model-View-ViewModel的简写,是M-V-VM三部分组成。
		M		数据结构		后端数据
		V		视图				页面 / dom
		VM	视图模型 / vue		模型转换视图 -》数据绑定,视图转换 模型 -》DOM事件监听
	优点:减少DOM操作,提高视图和逻辑的重用性
	缺点:项目庞大后,内存花费更多。bug很难被调试
vue的环境变量
	新建.env.development 
		开发环境变量配置
	新建.env.production 
		生产环境变量配置

面试题-ES6

var、let、const区别
	共同点:
		都可以声明变量
	不同点:
		|      | 变量提升  | 重复声明 | 再次赋值 | 作用域   |   
		| var  |    √     |    √     |    √     |    ×     |   
		| let  |    ×     |    ×     |    √     |    √     |   
		|const |    ×     |    ×     |    ×     |    √     |   
合并对象
	const a = {a:1,b:2}
	const b = {c:3,b:4}
	方法一:
		let obj = Object.assign(a,b); 
		//浅拷贝合并,并且会合并两个对象相同的属性,后者覆盖前者
	方式二:
		let obj = {...a,...b}
	方式三:
		for(let key in y){
			x[key] = y[key]
		}
箭头函数和普通函数的区别
	this指向的区别
		箭头函数中的this是在箭头函数定义时就决定的,而且不可修改(call, apply, bind)
		箭头函数的this指向它定义时,外层的第一个普通函数的this
	箭头函数不能new(不能当做构造函数)
	箭头函数没有prototype
	箭头函数没有arguments
Promise的几种状态
	Promise的三个状态
		pending(进行中,待定)
			只有调用resolve()或reject()才会改变这个状态
		fulfilled / resolved(已成功)
			调用了resolve()
		rejected(已失败)
			调用了reject()

	Generator
		函数前加*
		函数中获取异步结果使用 yield
		获取几个结果就使用几个  函数.next().value.then(res => {})
		只有第一个成功才会执行第二个
		Generator不够简洁,所以语法糖async await
	async await
		直接解析不用再.then
promise解决什么问题
	本质上是异步编程的一种解决方案
		解决回调地狱,不用回调嵌套回调
		代码的可读性问题
		可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
promise与async await的区别
	共同点:
		两者都是处理异步请求的方式
		async/await与Promise一样,是非阻塞的
	不同:
		promise
			回的对象要用then().catch()去处理数据和捕获异常,而且书写方式是链式的,容易造成代码多层堆叠难以维护
			promise.then()的方式返回就可能在请求还没返回时就先执行了外面的操作
			Promise是ES6中的一个内置对象,实际是一个构造函数
		async await
			async/await是基于Promise实现的,它不能用于普通的回调函数
			是通过try{}.cathc{}进行捕获直接抛出异常
			使代码看起来像同步一样,一遇到await就立即先返回结果然后再执行后面的操作;
手写promise
/*
自定义Promise函数模块:IIFE
 */
(function(window){
    const PENDING = 'pending';
    const RESOVLED = 'resolved';
    const REJECTED = 'rejected';
    //Promise构造函数
    //executor执行器函数
     function Promise(executor){
         let that = this;
         this.status = PENDING;   //状态未变之前都是pending
         this.data = undefined;     //未定义的数据
         this.callbacks = [];       //每个元素的结构:onResolved(){},onRejected(){}
         //执行器函数,会立即执行
         //两个改变promise状态的函数
         function resolve(value){
             if(that.status!==PENDING) return
             //调用resolve后,需要将状态改变成为resolved
             that.status = RESOVLED;
             //保存data数据
             that.data= value;
             //如果有未执行的callback,则立即异步执行onResolved,是包含两个回调的对象
             if(that.callbacks.length>0){
                 setTimeout(() => {
                    that.callbacks.forEach(element => {
                        element.onResolved(value)
                    });
                 });
             }
         }
         function reject(reason) {
            if(that.status!==PENDING) return
              //调用reject后,需要将状态改变成为rejected
              that.status = REJECTED;
              //保存data数据
              that.data= reason;
              //如果有未执行的callback,则立即异步执行onRejected,是包含两个回调的对象
              if(that.callbacks.length>0){
                  setTimeout(() => {
                    that.callbacks.forEach(element => {
                         element.onResolved(reason)
                     });
                  });
              }
         }
         //传入的参数为两个函数
         //除了resolve和reject,还有可能自己抛出异常,会失败,此时需要一个try catch
         try {
            executor(resolve,reject)
         } catch (error) {
             reject(error);
         }
     }
     /* 
     //.then接收成功失败的回调
     返回新的Promise
      */
     Promise.prototype.then = function(onResolved,onRejected){
         const that = this;
         // 指定回调函数的默认值(必须是函数)
         onResolved = typeof onResolved === 'function'?onResolved:value =>value  //继续向下传递
         onRejected = typeof onRejected ==='function'? onRejected:reason =>{throw reason}  //抛出异常
         return new Promise((resolve,reject) =>{
            function handle(callback){
                try {
                    const result = onResolved(that.data)
                    if(result instanceof Promise){
                        result.then(resolve,reject
                            // value =>{
                            //     resolve(value)
                            // },
                            // reason=>{
                            //     reject(reason)
                            // }
                        )
                    }
                } catch (error) {
                    reject(error);
                }
            }

            if(this.status ===PENDING){
            //假设当前状态还是pending,将回调保存下来
            this.callbacks.push({
                onResolved(value){
                    handle(onResolved)
                },
                onRejected(reason){
                    handle(onRejected)
                }
            })
            }else if(this.status ===RESOVLED){
                setTimeout(() => {
                //  如果回调是promise,return的promise结果就是这个promise结果
                handle(onResolved)
                });
            }else{
                //如果前面的promise返回的是失败的回调
                setTimeout(() => {
                    handle(onRejected)
                    });
            }
         })
     }
     /*
     接收失败的回调
     返回新的promise
      */
    Promise.prototype.catch = function(onRejected){
        return this.then(undefined,onRejected)
    }
    /*
    promise函数对象的resolve方法
     */
    Promise.resolve = function(value){
        return new Promise((resolve,reject) =>{
            if(value instanceof Promise){
                value.then(resolve,reject)
            }else{
                resolve(value);
            }
        })
    }
    /*
    promise函数对象的reject方法
     */
    Promise.reject = function(reason){
        return new Promise((resolve,reject) =>{
            reject(reason)
    })
    }
     //all接收数组
    Promise.all = function(promises){
        const values = [];
        const resolvedCount = 0;
        return new Promise((resolve,reject)=>{
             //遍历promise获取每个promise的结果
            promises.forEach((p,index) =>{
                Promise.resolve(p).then(
                     //成功的时候需要放入数组中
                    value => {
                         resolvedCount++;
                         values[index] = value;
                         if(resolvedCount == promises.length){
                             resolve(values);
                         }
                    },
                    reason =>{
                        //只要一个失败了,return的promise就失败
                        reject(reason)
                    }
                )
            })
        })
    }
     //race接收数组
    Promise.race = function(promise){
        //返回一个pomise
        return new Promise((resolve,reject) =>{
            Promise.resolve(p).then(
                //一旦有成功,return成功
                value =>{
                    resolve(value);
                },
                //一旦有失败,return失败
                reason =>{
                    reject(reason)
                }
            )
        })
    }

    // promise的resolveDelay,rejectDelay
/* promise函数对象的resolveDelay方法
*/ 
Promise.resolveDelay = function(value,time){
    return new Promise((resolve,reject) =>{
        setTimeout(() => {
         if(value instanceof Promise){
             value.then(resolve,reject)
         }else{
             resolve(value);
         }
        }, time);
    })
 }
 /*
 promise函数对象的rejectDelay方法
 */
 Promise.rejectDelay = function(reason,time){
    return new Promise((resolve,reject) =>{
        setTimeout(() => {
         reject(reason)
        }, time);
 })
 }
})(window)

find和filter的区别
	find() 发现
		array.find(function(currentValue, index, arr),thisValue)
		其中currentValue为当前项,index为当前索引,arr为当前数组
		返回第一个匹配到的值,返回具体的内容,并且只寻找一次。没有返回undefined,不会改变原数组
	filter() 过滤
		array.filter(function(currentValue,index,arr), thisValue);
		返回一个新数组,返回所有匹配的值,如果没有值则返回空数组,不会对空数组进行检测,不会改变原数组,要有return
some和every的区别
	some()
		有一项匹配返回true,全部不匹配才false
	every()
		所有匹配才返回true,只要有一个不匹配就false

面试题-uniapp

uniapp的生命周期
	应用生命周期
		onLaunch		当uni-app 初始化完成时触发(全局只触发一次)
		onShow			当 uni-app 启动,或从后台进入前台显示
		onHide			当 uni-app 从前台进入后台
		onError			当 uni-app 报错时触发
		onUniNViewMessage
		onUnhandledRejection
		onPageNotFound
		onThemeChange
	页面生命周期
		onLoad
		onShow
		onReady
		onHide
	组件生命周期
		跟vue一样
uniapp的条件编译
	在注释中输入代码
	#ifdef H5	只在H5页面中编译 
	#ifndef
	#endif

面试题-小程序

如何自定义头部?
	app.json中配置window的属性
		"navigationStyle":"custom"
如何自定义底部?
	tabBar配置底部 / 顶部的tab切换栏
	不配置tabBar,自己设置底部
不校验URL
	开发:工具 -> 详情 -> 本地设置 -> 不校验合法域名
	项目上线需配置微信公众平台的小程序域名白名单
微信小程序、vue、uniapp的区别
	小程序
		生命周期
			onLoad		一个页面只会调用一次。可以在 onLoad 中获取打开当前页面所调用的 query 参数。(异步操作)
			onShow		每次打开页面都会调用一次。
			onReady	页面初次渲染完成。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
			onHide		页面隐藏。当navigateTo或底部tab切换时调用。
			onUnload	页面卸载。当redirectTo或navigateBack的时候调用。
		数据绑定:<image src="{{imgSrc}}"></image>
		事件处理:<button bindtap="noWork">明天不上班</button>
		数据双向绑定:当表单内容发生变化时,会触发表单元素上绑定的方法,然后在该方法中,通过this.setData({key:value})来将表单上的值赋值给data中的对应值。
		取值:this.data.reason
		事件传参:data-id="{{item.id}}"		e.currentTarget.dataset.*		
	vue
		vue使用web端的标签
		vue生命周期
		数据绑定:<img :src="imgSrc"/>
		事件处理:<button @click="noWork">明天不上班</button>
		数据双向绑定:v-model
		取值:vue中,通过this.reason取值
	uniapp
		uni-app可以通过打包实现一套代码多端运行
		uni-app有自动的框架预载,加载页面的速度更快
		uniapp使用小程序的标签,
		uni.navigateTo 路由与页面跳转
		uni.request 网络请求
小程序跳转十层爆栈问题

面试题-兼容

项目中遇到过哪些兼容问题?
	1. 在IOS键盘中首字母大写的问题(上面有) autocapitalize='off'
	2. IOS日期转换NaN
		2022/9/28 12:00:00
	3. 移动端使用click事件有300毫秒延迟 / 双击会放大缩小
			添加meta设置,禁止缩放meta:user-scalabel = no
	4. 移动端touch事件有穿透(点透)的问题,怎么解决?
			阻止默认行为 e.preventDefault		fastclick.js
	5. 部分android里设置line-height不居中
			把字号内外边距设置为需求大小的2倍,使用zoom进行缩放
	6. 部分安卓机input的placeholder偏上
			设置line-height:normal

面试题-webpack

项目中使用过webpack吗?webpack有什么?
	webpack 模块化打包工具
		最开始是打包js文件的,能处理js文件和json文件
	1. 入口(entry)
		module.exports = {
		单个打包
			entry:'./index.js'
		多个打包
			entry:['./index.js','main.js']
		}
		
	2. 出口(output)
		module.exports = {
			entry:'./index.js',
			output:{
				path:path.resolve(__dirname, 'dist目录'),
				filename: 'index.js文件名称' // 可以不设置,那么会将打包好的文件都放置dist中
			}
		}
	3. loader
		loader是让webpack能够处理其他类型文件,如html,css,less等等
		  module: {
			rules: [{ 
				test: /\.css$/, 
				use: ['style-loader','css-loader']
			}],
		  },
		test 属性,识别出哪些文件会被转换。
		use 属性,定义出在进行转换时,应该使用哪个 loader。
	4. 插件(plugin)
		对于项目进行打包优化,压缩
		4.1 压缩打包html插件	html-webpack-plugin
			const HtmlWebpackPlugin = require('html-webpack-plugin');
			plugins: [
			 new HtmlWebpackPlugin({ 
				 //指定模板页面
				 template: './src/index.html' 
				 //修改默认输出文件名
				 filename: 'index.js文件名称' 
				 //配置不允许注入
				 excludeChunks:['xx']
				 //配置允许注入
				 chunks:['xx']
			 }),
			  new HtmlWebpackPlugin({ 
				 //指定模板页面
				 template: './src/index.html' 
				 //修改默认输出文件名
				 filename: 'index.js文件名称' 
				 //配置不允许注入
				 excludeChunks:['xx']
				 //配置允许注入
				 chunks:['xx']
			 }),
			]
		4.2 压缩打包图片	file-loader + html-withimg-loader (加载图片loader + 让html支持图片loader)
			rules: [{ 
				test: /\.(png|jpg|jpeg|gif)$/,
				use: [{
					loader:'file-loader',
					options:{
						esModule:false,
						outputPath:'img/' //将图片都打包至这个文件夹
					}
				}]
			},{
				test: /\.html$/, 
				use: 'html-withimg-loader'
			}],
		4.3 字体图标的配置		file-loader
			{
				test: /\.(ttf|woff|woff2|svg|eot)$/,
				use: 'file-loader?name=./fonts/[name].[ext]' //目录,原文件名称,源文件格式
			}
		4.4 单独压缩打包分离css
			4.4.1 单独抽离css文件	mini-css-extract-plugin
				const MiniCssExtractPlugin = require("mini-css-extract-plugin");
				{ 
					test: /\.css$/, 
					use: [MiniCssExtractPlugin.loader,'css-loader']
				}
				plugins:[
					new MiniCssExtractPlugin({
						filename: './css/[name].css' 
					})
				]
			4.4.2 压缩css文件	optiize-css-assets-webpack-plugin
				const OptiizeCsAssetsWebpackPlugin = require("optiize-css-assets-webpack-plugin");
				plugins:[
					new OptiizeCsAssetsWebpackPlugin({
						filename: './css/[name].css' 
					})
				]

面试题-网络请求

####### 跨域

	前端:jsonp、vue项目设置代理(打包后无效,设置.env文件)
	后端:cors

####### http和https的区别?

	http
		端口80
		以明文发送内容,不提供任何方式的数据加密
	https
		端口443
		具有安全性的ssl加密传输协议

面试题-web安全

####### xss攻击和Sql注入

	xss攻击:用户输入的内容或文本框需要替换某些特殊字符< > 转换为字符集,等等
	Sql注入:用户输入的内容不可以有“ 或 空格等等

####### 接口安全

	数据的加密解密 AES加密,MD5加密
	对称加密和非对称加密
	加签验签
	token授权机制
	token设置时间 / 时间戳超时机制
	配置白名单
	数据脱敏掩码
	数据参数一些合法性校验

####### token

	token后端生成的,是一个唯一身份令牌
	用户登录成功后,返回
	token + cookie 前端判断token是否过期
	token + localStorage 后端判断token是否过期
	过期重新发送请求 / 跳转登录页面
token的无痛刷新

面试题-其他类面试题

遇到bug会怎么解决?
	bug避免不了,变量,方法名尽量复制过来。
	标点符号,this写的时候不要省略。
	真有bug,报错不要慌。先找出问题在哪里。
	是数据没请求过来?还是请求过来赋值没赋值对。还是赋值了但是数据不是响应式。等等。
	合理利用debugger加断点调试 / 看f12里的报错信息。
	实在找不到就网上查。等等
SEO
	网站一定要多页面
	title描述关键字
	图片、音频、视频的标签属性特别关键
	网站事件长短
	内链外链
	网站不能出现死链接
posted on   苏舒  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示