1.父元素伸缩,自元素自适应,10像素间距
2.选择器优先级
3.浏览器对用户呈现出一个页面的过程
4.prevent default
5.事件的属性和方法
6.this指向
7.es5实现继承
8.prototype和构造函数调用关系
9.promise并行三个,将结果存入数组,滑动窗口
10.eventloop对set timeout和promise的处理
11.vue 样式模块化
12.怎样区分伪类和伪元素
13.web存储方式
14.简述instanceOf为什么不是绝对安全?
15.String对象的方法
16.实现千位分隔符 1234567->1,234,567
17.写出3个以上能改变DOM的js native函数
18.call和apply的区别
19.浏览器实现js多线程,提供原生对象是?
20.快速排序
21.二分查找
22.实现布局,子元素宽占父元素的50%,高和宽相等,自适应。
23.keep-alive原理 TCP原理,VUE<keep-alive>组件原理
24.vue tab页跳转时, 保存之前的滚动位置
25.window load event和document DOMContentLoaded event的区别
26.给定两个排序好的数组a,b,长度分别为n,m,给出一个高效算法,查找A中的哪些元素存在于B中。
27.实现一个函数,入参是一个字符串,判断是否有重复字母,如果包含,返回第一个重复字母的下标,不包含返回-1。
28.对象数组a=[{x:7,y:6} , {x:1,y:2} , {x:3,y:4}],按照x的大小进行排序。
瓜子二手车
29.阶乘函数
30.call() apply()和bind()的区别?
31.实现tooltip, css画钝角三角形
32.判断一个对象是函数的几种方法。
33.实现商品列表,一行3个,屏幕宽度不定,商品元素间距50px,距离父元素边距50px。
34.向一个数组增加元素的方法
35.盒子模型有哪几种, width100padding10的盒子, 占据宽度是多少?
1.父元素伸缩,子元素自适应,10像素间距
答:https://www.jianshu.com/p/19538a0ffb8c
2.选择器优先级
答:
内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器,!important 的属性拥有最高优先级
html
3.浏览器对用户呈现出一个页面的过程
答:dns解析-》tcp链接-》发送HTTP请求-》服务器处理请求并且返回报文-》浏览器解析渲染页面-》链接结束
DNS解析:
是一个将网址解析成IP 地址的过程。
首先从本地域名服务器中查找,如果找不到就继续向上根域名服务器查找,直到顶级域名,这个过程中存在dns优化有的环节。当查找资源时, 会先找缓存,(浏览器缓存-》系统缓存-》路由器缓存等等),也会根据机器的负载量和距离用户的位置进行dns负载均衡。
建立TCP连接:
A.客户端发送syn到服务器要求连接
B.服务端向客户端发送ack
C.客户端收到ack并确认后,向服务端发送ack,连连接建立。
发送HTTP请求:
tcp连接建立之后,开始通过HTTP协议传输资源,根据情况判断是否使用HTTPS,HTTP包括请求行,请求报头,请求正文(post,put客户端向服务器传输数据的情况)。keepalive什么的可以在请求头里添加。
服务器处理请求并返回HTTP报文:
服务端接到请求开始对tcp进行处理,对http进行解析,按照报文格式封装成HTTP request对象。响应报文码(1xx:请求已接受,2XX:成功,3xx:重定向,4xx:客户端错误,5xx:服务端错误)
浏览器渲染页面:
边解析边渲染,解析html,构建dom树,解析css,构建cssom。
如果dom构建的过程中遇到了css的link,那就会先去加载并构建cssom,这个过程不是一次性的。css和同步的js文件都是阻塞DOM树渲染的,但不阻塞DOM解析,直到js加载并且执行完毕。遇到阻塞的css也会延迟js的执行和dom构建。(因为js可能会修改dom或者cssom),css同样,当cssom构建时,js也会停止被阻塞,等待cssom构建完成。
CSS阻塞吗?
1.CSS会阻塞DOM的渲染,不会阻塞DOM树的解析。
2.CSS会阻塞js的执行。
defer & async
1.正常模式
<script src="script.js"></script>
遇到这样的js标签,浏览器会立即加载并执行,不等待后续载入的文档元素。
2.async模式
<script async src="script.js"></script>
有async的js文件会和后续的DOM解析渲染并行执行,当js加载完成,立即执行,这时html解析暂停。因此不会按照标签引入顺序执行。
3.defer模式
<script defer src="script.js"></script>
有defer的js文件的加载,也会和文档的解析构建并行。这一点与async一致。
不同的是,defer的js文件加载完不会立即执行, 会等到所有文档解析完成后,DOMContentLoaded事件触发之前完成, 因此会按照引入顺序执行。
DOMContentLoaded & onload
DOM解析完(阻塞DOM的内容解析完,DOM才真正解析完)会触发DOMContentLoaded事件。如果在DOMContentLoaded之后引入css样式表,DOMContentLoaded可能无法获取样式表里的样式,此时DOM树已经构建完成,但外部css文件还没加载完成,这也是css文件放在头部的原因。
onLoad
页面的所有资源被加载以后触发onLoad事件,会在DOMContentLoaded之后触发。
这个过程中有两个重要的过成是回流和重绘。计算盒模型的大小位置还有解析颜色字体等 属性,这些都确定下来的时候开始repain,合成一个rendertree渲染树,render-tree中必须同时存在dom和cssom,浏览器开始布局并渲染到屏幕上。首次加载必然会经历回流和重绘的过程。
重排(回流)和重绘
// DOM重构,浏览器重排重绘
无论何时总会有一个初始化的页面布局伴随着一次绘制。(除非你希望你的页面是空白的:))之后,每一次改变用于构建渲染树的信息都会导致以下至少一个的行为:
部分渲染树(或者整个渲染树)需要重新分析并且节点尺寸需要重新计算。这被称为重排。注意这里至少会有一次重排-初始化页面布局。
由于节点的几何属性发生改变或者由于样式发生改变,例如改变元素背景色时,屏幕上的部分内容需要更新。这样的更新被称为重绘。
重排和重绘代价是高昂的,它们会破坏用户体验,并且让UI展示非常迟缓。
什么情况会触发重排或重绘?
任何改变用来构建渲染树的信息都会导致一次重排或重绘。
添加、删除、更新DOM节点
通过display: none隐藏一个DOM节点-触发重排和重绘
通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化
移动或者给页面中的DOM节点添加动画
添加一个样式表,调整样式属性
用户行为,例如调整窗口大小,改变字号,或者滚动。
一些重排可能开销更大。想象一下渲染树,如果你直接改变body下的一个子节点,可能并不会对其它节点造成影响。但是当你给一个当前页面顶级的div添加动画或者改变它的大小,就会推动整个页面改变-听起来代价就十分高昂。
每次重排重绘浏览器都会立即执行吗?
浏览器一直致力于减少这些消极的影响,浏览器会创建一个变化的队列,浏览器可以向队列添加或变更这些变化,在一个特定的时间或达到一定的数量时,执行一次重排或重绘,通过这种方式,多次重排或重绘会整合起来最终减少重排或重绘的次数,以节省浏览器渲染的开销。
所以 ,同时set和get样式是非常糟糕的做法
// no-no!
el.style.left = el.offsetLeft + 10 + "px";
如何减少重排重绘呢?
- 离线编辑样式修改,可以通过创建documentFragment临时保存变动,然后一次性更新到dom上。
- 复制要多次更新的节点,在副本上进行操作,然后一次性替换。
- 节流防抖
一个问题,为什么会遇到浏览器展示页面开始没有样式,过一会样式才可用的情况?
看到的一个答案,有可能是这个原因,但是我不确定。
开发环境会把css都打包到js里,所以要等js加载好了才有样式,因此会出现这种情况;但是在生产环境下,css会生成css文件,并插入到<style />里,因此就不会出现这种情况了。
两个优化点:css先加载,js后加载
js尽量不要修改dom树。
js
4.prevent default
答:取消事件的默认行为,cancelable为true可以使用这个方法。
5.事件的属性和方法
答:type/target/currentTarget/cancancelable/bubbles/defaultPrevented/detail/eventPhace/trusted/view
preventDefault()/stopPropagation()/stopImmediatePropagation()
6.this指向
答:在运行时基于函数的执行环境绑定的,
在事件中,this始终指向currentTarget,
执行环境的层层嵌套形成了作用域链,在作用域链中访问变量只能自底向上搜索。
7.es5实现继承
答:
构造函数,原型,实例之间的关系?
每个构造函数都有一个原型对象,每个原型对象都有一个指向构造函数的指针,每个实例有一个指向原型的指针。继承本质上是重写原型对象,将原型代之以另一个类型的实例。
A.父类构造函数
function SuperType(name){
this.name = name;
this.numbers = [1,2,3,4];
}
B.父类原型添加原型方法
SuperType.prototype.superFunction = function() {
alert (this.name);
}
C.子类构造函数,在构造函数中用call调用父类构造函数,并且定义子类的构造 函数属性
function SubType(name,age){
SuperType.call(this,' nick');
this.age = age;
}
D.改写子类原型对象,使之指向父类的实例,创建链条。
SubType.prototype = new SuperType();
E.子类的构造函数自己定义,断开与父类构造函数的关系
SubType.prototype.constructor = SubType();
F.子类定义自己的原型方法
SubType.prototype.subFunction = function(){
alert (this.age);
}
G.创建子类实例
var instance = new SubType('anne','10');
8.prototype和构造函数调用关系
答:每个构造函数都有一个原型对象,每个原型对象都有一个指向构造函数的指针,每个实例有一个指向原型的指针。继承本质上是重写原型对象,将原型代之以另一个类型的实例。
9.promise并行三个,将结果存入数组,滑动窗口
Promise.all() 并行且按照参数传入顺序输出
/////
var promiseArr = [p1,p2,p3,p4,p5,p6,p7,p8];
var resultArr = [];
for(let i = 0; i < promiseArr.length; i++){
let tempArr = [promiseArr[i * 3] , promiseArr[i * 3+1] , promiseArr[i*3+2]];
resultArr.push(promise.all(tempArr))
}
10.eventloop对set timeout和promise的处理
js是单线程语言,他作为浏览器脚本语言,与用户互动, 以及操作dom,这决定了它只能是单线程语言,否则会有非常麻烦的同步问题。
H5提出了WebWorker标准。允许js脚本创建多个线程。但是子线程完全受主线程控制。且不得操作DOM,所以这个标准并没有改变js单线程的本质。
js执行过程中, 所有的任务分为两种,同步任务和异步任务,同步任务在主线程上形成一个执行栈按顺序执行, 遇到异步任务就将这个异步任务添加到任务队列(task queue)中,一旦执行栈中所有的同步任务执行完毕,系统就会去读task queue,拿到执行栈中执行。异步任务的task queue 分为宏任务队列(macrotask)和微任务队列(microtask),不同的异步任务会进入不同的队列,宏任务包括常见的settimeout,setinterval,setImmediate,UI rendering,requestAnimationFrame等,microtask包括promise ,node 的process.nextTick,等。同步任务执行完毕后,js引擎会先去查看microtask queue,若有待执行的微任务就会先执行所有microtask,然后再去执行一个宏,再去执行所有微。
settimeout会被放进宏任务队列(macrotask队列)一次取出一个执行
Promise会被放进微任务队列(microtask队列)每次循环执行所有microtask。
- vue 样式模块化
12.怎样区分伪类和伪元素
答:针对作用选择器的效果,伪类需要添加类来达到效果,而伪元素需要增加元素,所以一个叫伪类,另外一个叫伪元素。
13.web存储方式
大搜车
答:web存储目的是在设备不能上网的情况下依然可以运行应用。
熟知的有cookie,sessionStorage,localStorage,indexDB。
第一步 离线检测
开发离线应用的第一步是要确定设备在线还是离线。h5为此定义navigator.online属性,但是存在兼容性问题,h5还定义了两个事件,online和offline,网络状态变化时触发。为了检测设备是否离线,在页面加载后,最好先通过navigator.onLine取得初始状态,然后通过online和offline事件来确定网连接状态是否变化。
第二步 HTML5的应用缓存(application cache),(manifest)
appCache是专门为开发离线web应用而设计的。是从浏览器缓存分出来的一块区域,想要在这个缓存中保存数据,可以使用描述文件(manifest file)列出需要缓存和下载的资源。要将描述文件与页面关联起来,可以在<html>中的manifest属性中指定这个文件的路径。例如:
<html manifest="/offline.manifest"></html>
描述文件的扩展名以前是.manifest,现在推荐使用.appcache。
applicationCache对象的status属性可以查看appcache当前的状况。
第三步 数据存储
1. cookie(键值对)
限制 :
1.cookie是绑定在特定域名下,这个限制确保了存储在cookie中的信息只能让批准的接受者访问,其他域不能访问。
2.每个域的cookie总数是有限的。不同浏览器各有不同,超过单个域名的限制个数后,就会清除以前的cookie。
3.浏览器对cookie的尺寸也有限制。大约都是4kb,如果你尝试创建超过最大尺寸的cookie,会被悄无声息地丢掉。
cookie的构成:
名,值,域,路径,失效时间,安全标志。
Set-Cookie:name=value;domain=.wrox.com;path=/;22-Jan-07 07:10:24 GMT;secure
只有名值对会发送给服务器。所有的名值对都是经过URL编码的,所以必须使用decodeURIComponent()来解码。
server设置cookie,有效时间内, client每次访问相同域名都会携带这个cookie,默认情况下,当浏览器关闭时cookie会被删除,不过也可以自己设置过期时间,因此, cookie可以在浏览器关闭之后依然保存在用户的机器上。为了绕开浏览器单域名下的cookie个数限制, 产生了子cookie,子cookie是存在单个cookie中的更小段的数据。cookieUtil对象操作cookie的存储方法。
2. Web Storage
以window对象的属性存在。包含session storage和local storage。可以使用length属性判断有多少键值对存在于storage中,IE8提供了一个remainingSpace属性,用于获取还可以使用的存储空间的字节数。storage是保存在磁盘上的。
sessionStorage
sessionStorage对象用于存储特定于某个会话的数据,也就是该数据只保存到浏览器关闭, 可以跨越页面刷新存在,如果浏览器支持, 浏览器重启后依然可用。大小大约都是2.5MB。
localStorage
sessionStorage对象主要用于仅针对会话的小段数据的存储,如果需要跨越会话存储数据,localStorage更合适。要访问同一个localStorage对象,页面必须来自同域名(子域名无效)同协议,同端口。数据保留到通过JS删除或者用户主动清除浏览器缓存。大小大约都是5MB。
3. INdexedDB
Indexed Database API简称IndexedDB。是浏览器中保存结构化数据的数据库。大多数操作是以异步请求的方式进行的,差不多每一次IndexedDB操作,都需要你注册onerror或onseccess事件处理程序。
14.简述instanceOf为什么不是绝对安全?
BOM
答:因为在使用框架的情况下,浏览器会存在多个global对象。每个框架中定义的全局变量,会自动成为框架中window对象的属性。由于每个window对象都包含原生类型的构造函数,因此每个框架都有一套自己的构造函数,这个问题会影响到对跨框架传递的对象使用instanceOf操作符。
- String对象的方法
答:方法名---返回值---参数
- 字符方法 :
- charAt()--给定位置的字符串--一个参数(字符位置)
- charCodeAt()--给定位置的字符编码--一个参数(字符位置)
- 字符串操作方法:(参数为负数时的处理机制不同)
- concat()--新--任意多个参数拼接
- slice()--新--一或两个参数(开始位置,结束位置后一个)
- substring()--新--一或两个参数(开始位置,结束位置后一个)
- substr()--新--一或两个参数(开始位置,个数)
- 字符串位置方法:
- indexOf()--index--1或2个参数(str, index)
- lastIndexOf()--index--1或2个参数(str,index)从后向前
- trim()方法--新--去掉首尾空格
- 字符串大小写转换方法:
- toLowerCase()--新
- toLocaleLowerCase()--新
- toUpperCase()--新
- toLocaleUpperCase()--新
- 字符串匹配方法:
- match()--array--一个参数(正则表达式或RegExp对象)
- search()--第一个匹配项的索引--一个参数(正则表达式或RegExp对象)
- replace()--新--两个参数(RegExp/str , str/function)
- split()--指定分隔符的array(可将string转为数组)--(RegExp/str , number)
- localeCompare()方法--1,0,-1--str
- fromCharCode()方法--转换后的字符串--参数是一系列UTF-16代码单元的数字
16.实现千位分隔符 1234567->1,234,567
彩视
答:
- /(?=(\B\d{3})+$)/g
https://juejin.im/post/5abb5b01f265da237f1e5a92 - (123456789).toLocaleString('en-US')(骚操作)
17.写出3个以上能改变DOM的js native函数
独到科技
答:appendChild() / insetBefore() / replaceChild() / removeChild() / createElement()
18.call和apply的区别
答: 这两个函数都是非继承而来的方法,用途是在特定的作用域中调用函数,设置函数体内this对象的值。
apply()
接收2个参数,第一个是函数运行的作用域,第二个是参数数组,其中,第二个参数可以是Array的实例,也可以是arguments对象。
call()
和apply()区别仅仅是接收参数的方式不同,第二个参数必须逐个列出来。
func.apply(this,[num1,num2]);
func.apply(this,arguments);
func.call(this,num1,num2);
19.浏览器实现js多线程,提供原生对象是?
答:js实现多线程的方式有很多种, ajax,异步回调函数,settimeout setinterval,webworker,多线程webworker是h5标准的一部分,worker是window对象的一个方法。
这一套标准定义了一系列api,允许一段js程序运行在主线程之外的另一个线程中,把一些一步操作交给worker线程,在主线程工作时,worker线程在后台运行,互不干扰,等到worker完成后再将结果返回给主线程。
实例化worker对象并传入要执行的js文件名就可以创建一个新的web worker。
var worker = new Worker("stufftodo.js");
这行代码会导致浏览器下载这个js文件,但只有worker收到消息才会执行文件中的代码。要给worker传递消息,可以使用postMessage()方法,消息内容可以是任何能够被序列化的值,可以序列化为JSON结构的任何值都可以作为参数传递给postMessage(),这就意味着传入的值时被复制过去的,不是直接传递过去的。
worker返回的也是任何能够被序列化的值。
worker是通过message和error事件与页面通信的。
Worker全局作用域
关于web worker最重要的是,他所执行的js代码完全在另一个作用域中,与当前网页的代码不共享作用域。worker中的代码不能访问DOM,也不能通过任何形式影响页面的外观。
worker的全局对象是worker本身,web worker本身是一个最小化的运行环境。
webworker存在两个限制:
同源限制:worker运行的脚本必须与主线程脚本文件同源。
访问限制:worker的全局对象和主线程的全局对象不是同一个上下文环境,无法使用document,window,parent等,window改写成self.
20.快速排序
彩视
https://blog.csdn.net/zhao529670074/article/details/80776253
答:
1.选基准,pivot,就选数组最左边元素
2.小于的放pivot左边,大于的放pivot右边
3.递归
时间复杂度为O(nlogn)
var arr=[5,7,2,9,3,8,4,7,1];
// 每次选择最左边的数作为基数
function quickSort(arr){
if (arr.length<2) { return arr; }
// 定义左指针
var left=0;
// 定义右指针
var right=arr.length-1;
//开启每一轮的排序
while(left<right){
// 寻找右边比arr[0]小的数的下标
while(arr[right]>=arr[0] && left<right){
right=right-1;
}
// 寻找左边比arr[0]大的数的下标
while(arr[left]<=arr[0] && left<right){
left++;
}
//当左边指针与右边指针相遇后,交换arr[0]与当前两个指针所在的元素
if (right==left) {
let mid=arr[right];
arr[right]=arr[0];
arr[0]=mid;
break;
}
// 当左指针小于右指针的位置,交换两个指针当前位置的元素
let tem=arr[right];
arr[right]=arr[left];
arr[left]=tem;
}
//递归实现
return quickSort(arr.slice(0,left)).concat(arr.slice(left,right+1)).concat(quickSort(arr.slice(right+1)));
}
//对数组进行排序
console.log(quickSort(arr));
21.二分查找
彩视
答:有序查找
1.找到有序数组的中间元素
2.比较key和中间元素的大小,大于去右边找,小于去左边找
3.大于的情况,把边界low设置为中间元素往后一个,小于的情况,把边界high设置为中间元素往前一个。
O(nlogn)
function binarySearch(arr,key){
var low = 0 , high = arr.length-1;
while(low <= high){
let mid = parseInt((low + high)/2);
if(key > arr[mid]){
low = mid+1;
}else if(key < arr[mid]){
high = mid-1;
}else {
return mid;
}
}
return -1;
}
var arr = [1,2,3,4,5,6,7,8,9];
console.log(binarySearch(arr , 9));
22.实现布局,子元素宽占父元素的50%,高和宽相等,自适应。
跟谁学
答:原理:padding-top,padding-bottom是根据父元素的宽度计算而不是height。
<div class="a">
<div class="b">
</div>
<div class="b">
</div>
</div>
.a{
width:300px;
height:600px;
background:pink;
font-size:0;
}
.b{
width:40%;
background:#f13a70;
padding-top:40%;
display:inline-block;
}
- keep-alive原理
TCP keep-alive
VUE <keep-alive>组件原理
北明软件
答:
- TCP
Connection设置为Keep-alive用于告诉客户端本次HTTP请求结束之后并不需要关闭TCP连接,这样可以使下次HTTP请求使用相同的TCP通道,节省TCP连接建立的时间。
在 HTTP 1.0 时期,每个 TCP 连接只会被一个 HTTP Transaction(请求加响应)使用,请求时建立,请求完成释放连接。当网页内容越来越复杂,包含大量图片、CSS 等资源之后,这种模式效率就显得太低了。所以,在 HTTP 1.1 中,引入了 HTTP persistent connection 的概念,也称为 HTTP keep-alive,目的是复用TCP连接,在一个TCP连接上进行多次的HTTP请求从而提高性能。
HTTP1.0中默认是关闭的,需要在HTTP头加入"Connection: Keep-Alive",才能启用Keep-Alive;HTTP1.1中默认启用Keep-Alive,加入"Connection: close ",才关闭。
- VUE <keep-alive>组件
开发中, 为了优化组件的缓存,切换页面时保存状态,使用keep-alive,keep-alive是vue的一个内置抽象组件,在他的created钩子里面定义了this.cache和this.keys,props定义了include,exclude,max。keep-alive实现了render函数,在keep-alive组件渲染时候会执行。
由于我们也是在keep-alive内部写dom,所以第一步先获取他的插槽$slots,再获取到他的第一个子节点。
const slot = this.$slots.default
const vnode: VNode = getFirstComponentChild(slot)
由于keep-alive只获取第一个子节点,所以一般搭配看component动态组件或者router-view使用。
接下来,判断组件名字和include,exclude的关系。
这里match函数做匹配的逻辑兼容了数组,字符串,和正则的情况。
include的情况,去找缓存。
命中缓存,就从缓存中拿VNode实例,并且调整key的顺序排在最后一个。
未命中,就把VNode设置进缓存,如缓存数量超过了配置的max值, 就删掉缓存队列中的第一个。这里有个判断,就是删掉的节点是不是当前的tag,是就直接删掉,不是的话,要调用缓存的组件实例 的$destroy钩子。
如果是exclude的情况,就直接返回VNode。
<keep-alive>组件也观测include和exclude的情况,对cache做遍历,如果缓存的节点名和新的规则不匹配,就把缓存的节点删除。
24.vue tab页跳转时, 保存之前的滚动位置
答:有两种方式:vue-router的滚动行为和keep-alive
在源码中,对于浏览器默认行为,调用自定义的scrollBehavior函数后传参的savedPosition为保存的position,对于跳转这种则传参的savedPosition为null。
基本我们可以提炼出:通过跳转的是需要进行数据刷新的;而通过默认浏览器行为的是不需要进行浏览器刷新的。
- vue-router 中的滚动行为
滚动行为:这个功能只在支持history.pushState的浏览器中使用,只在通过浏览器触发前进后退按钮时可用。
貌似不能做条件判断是否要保持状态
scrollBehavior 在组件生命周期的mounted后beforeUpdated前执行
savedPosition是个位置对象
const router = new VueRouter({
routes: [],
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition;
} else {
return {x:0,y:0}
}
}
})
也可以模拟滚动到锚点的行为
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash
}
}
}
可以返回一个promise得到预期位置的描述
因为scrollBehavior 在组件生命周期的mounted后beforeUpdated前执行,mounted时期发出的异步请求并没有等到返回,滚动事件已经触发,所以等数据请求回来时会发现滚动 位置不准确。所以必须使用settimeout将滚动事件加到异步队列中,位于异步请求之后。(settimeout是加在了本轮主线程的末尾,但是settimeout中的异步事件放到了和异步请求同一个队列中。)
scrollBehavior (to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ x: 0, y: 0 })
}, 500)
})
}
- keep-alive
可以做tab切换,页面跳转时的滚动位置保存。
每次切换新标签时, vue都创建了一个新的currentTabComponent。
路由跳转时,vue-router不保存被切换组件的状态,旧的会被销毁,新组建被创建,走一遍完整的生命周期流程。
要求被包裹的组件有自己的名字,注意不是路由名字。
keep-alive是个抽象组件(或称为功能型组件),存在于initLifeCycle中,实际上不会被渲染在DOM树中。它的作用是在内存中缓存组件(不让组件销毁),等到下次再渲染的时候,还会保持其中的所有状态,并且会触发activated钩子函数。因为缓存的需要通常出现在页面切换时,所以常与router-view一起出现。
可以只缓存一些组件
如果只想渲染某一些页面/组件,可以使用keep-alive组件的include/exclude属性。include属性表示要缓存的组件名(即组件定义时的name属性),接收的类型为string、RegExp或string数组;exclude属性有着相反的作用,匹配到的组件不会被缓存。假如可能出现在同一router-view的N个页面中,我只想缓存列表页和详情页,那么可以这样写:
<keep-alive :include="['ListView', 'DetailView']">
<router-view />
</keep-alive>
可以实现条件渲染
上面include的写法不是常用的,因为它固定了哪几个页面缓存或不缓存,假如有下面这个场景:
现有页面:首页(A)、列表页(B)、详情页(C),一般可以从:A->B->C;
B到C再返回B时,B要保持列表滚动的距离;
B返回A再进入B时,B不需要保持状态,是全新的。
具体细节:
https://juejin.im/post/5b407c2a6fb9a04fa91bcf0d
scrollbehavior和keepalive标签有什么区别?
我的理解是,从本质上讲,scrollbehavior是在缓存里记录了上一个页面的滚动位置信息,而keepalive是把不活动的组件保存在内存中, 而不是直接销毁。
scrollbehavior只能控制浏览器的返回前进,没法处理tab切换这种,而keepalive可以。
25.window load event和document DOMContentLoaded event的区别
独到科技
答:
load事件在整个页面全部加载完才会触发,包括所有以来的资源,样式,图片。
load,页面上所有的资源(图片,音频,视频等)被加载以后才会触发load事件,简单来说,页面的load事件会在DOMContentLoaded被触发之后才触发。
不同于DOMContentLoaded,一旦DOM加载完,就会触发。
在这里我们可以明确DOMContentLoaded所计算的时间,当文档中没有脚本时,浏览器解析完文档便能触发 DOMContentLoaded 事件;如果文档中包含脚本,则脚本会阻塞文档的解析,而脚本需要等位于脚本前面的css加载完才能执行。在任何情况下,DOMContentLoaded 的触发不需要等待图片等其他资源加载完成。
26.给定两个排序好的数组a,b,长度分别为n,m,给出一个高效算法,查找A中的哪些元素存在于B中。
彩视
答:
- ES7的includes 和 filter 做交并差集
var a = [1,3,5,7,9];
var b = [1,5,6,8,10];
let c = [];
c = a.filter(i => b.includes(i));
- Array.from 和 set不允许重复的特点
var a = [1,3,5,5,7,9,9,10,10];
var b = [1,5,5,6,8,10];
let c = [];
let aSet = new Set(a);
let bSet = new Set(b);
c = Array.from(aSet).filter(i => bSet.has(i))
console.log(c);
- ES5 filter 和 indexOf
此方法不能去重
由于indexOf方法对于NAN始终返回-1,所以有NAN的情况要特殊处理
let a = [1,3,5,5,7,9,9,10,10,'s',NAN];
let b = [1,5,5,6,8,10,'s',NAN];
let ahasNAN = a.some(i => isNAN(i));
let bhasNAN = b.some(i => isNAN(i));
let c = [];
c = (a.filter((i) => b.indexOf(i)>-1)).concat(ahasNAN && bhasNan ? [NAN] : []);//考虑NAN
c = a.filter((i) => b.indexOf(i)>-1);//不考虑NAN
我没成功
27.实现一个函数,入参是一个字符串,判断是否有重复字母,如果包含,返回第一个重复字母的下标,不包含返回-1。
大搜车
答:
let a = [1, 2, 2, 3, 3, 4, 5]
let b = [];
let res = -1;
for (let i = 0; i < a.length; i++) {
if (b.includes(a[i])) {
res = i;
break;
} else {
b.push(a[i]);
}
}
console.log(res);
console.log(b);
28.对象数组a=[{x:7,y:6} , {x:1,y:2} , {x:3,y:4}],按照x的大小进行排序。
瓜子二手车
答:
js page113,Function类型,比较函数,一个函数返回另一个函数
function compare(x) {
return function(obj1, obj2) {
var value1 = obj1.x;
var value2 = obj2.x;
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
}
var objArr = [{x:4,y:10},{x:12,y:2},{x:-3,y:9}];
console.log(objArr.sort(compare('x')));
29.阶乘函数
未经优化的版本
function factorial(num) {
if (num <= 1) {
return 1
} else {
return num * factorial(num - 1);
}
}
console.log(factorial(5));
但问题是这个函数的执行与函数名factorial紧紧耦合了,为了消除这种紧密耦合的现象,可以使用arguments.callee。
arguments对象有个callee属性,callee是个指针,指向拥有这个arguments对象的函数。
但是严格模式下访问arguments.callee会报错
function factorial(num) {
if (num <= 1) {
return 1
} else {
return num * arguments.callee(num-1);
}
}
console.log(factorial(5));
使用具名函数,这种方式在严格模式和非严格模式下都行得通。
var factorial = function f(num){
if(num <=1){
return 1;
}else{
return num * f(num-1);
}
};
- call() apply()和bind()的区别?
答:call和apply改变了函数的this上下文之后立即执行函数,而bind则是返回改变了上下文后的一个函数。
31.实现tooltip, css画钝角三角形
瓜子二手
答:操作边的宽度就可以实现钝角锐角三角形
32.判断一个对象是函数的几种方法。
1.typeof操作符
typeof func == "function" ; //true
typeof操作符一般用来检测基本类型,返回小写字符串,检测引用类型时,除了function返回‘function’之外,其余都返回object,因此不适合检测引用类型。
toString(最佳)
Object.prototype.toString.call(fn)=== '[object Function]';
-
Constructor构造函数法
null和undefined没有构造函数,如果不能确定类型,不能用此方法检测。
-
instanceof操作符
instanceof操作符假定只有一个全局环境,如果有多个框架,就会有多个不同版本的构造函数,instanceof就不会生效。
33.实现商品列表,一行3个,屏幕宽度不定,商品元素间距50px,距离父元素边距50px。
瓜子二手车
<div class='a'>
<div class='b-wrap'>
<div class='b'>
</div>
<div class='b'>
</div>
<div class='b'>
</div>
<div class='b'>
</div>
</div>
</div>
.a {
width: 500px;
height: 500px;
background: #3c3c3c;
padding: 50px;
}
.b-wrap {
width: 100%;
height: 100%;
background: pink;
}
.b {
float: left;
background: #aaaa12;
margin-bottom: 50px;
// 此处注意,减号左右要有空格, 否则计算属性不生效
width: calc((100% - 100px)/3);
padding-top: calc((100% - 100px)/3);
}
.b:nth-child(2) {
margin: 0 50px;
}
然后我发现这个方法不对。。。。
用flex:
这是个遗留问题,我不会了。
34.向一个数组增加元素的方法
瓜子二手车
答:以下三个方法都修改原数组
- push()
- unshift() //从前面添加一个元素
- splice() // 可插入、替换。删除元素项,接受三个参数:起始index,删除的个数,剩下的参数都是用来添加or替换的项
35.盒子模型有哪几种, width100padding10的盒子, 占据宽度是多少?
微店
答:当文档布局时,浏览器渲染引擎会把元素都表示为矩形的盒子。
box-sizing属性有三个值
-
content-box:默认值
包含content,如图,元素最终宽度由width+padding+border决定,width属性设置的只是content部分的宽度。
-
border-box:
包含border+padding+content,width设置的宽度是border+padding+content。
inherit
从父级继承。