文章目录
在牛客网看面经时随机找的一个面经,将问到的知识点详细介绍概括了一下,有一些知识点参见其他博客,希望能帮助到更多人,最后大家一起学习进步,加油!
百度提前批-百度智能小程序(面经详解)
1、定位
默认为static,也就是按照文档的流式(flow)定位,将元素放到一个合适的位置,所以在不同的分辨率下,采用流式定位能很好的自适应。可指定相对位移(margin、padding)。
relative(相对定位),相对于元素自己本身的定位,注意:相对定位的元素仍然在文档流中,仍然占据着它本来占据的位置空间--虽然它现在已经不在本来的位置了
。可指定相对位移(left、right、top、bottom)。
absolute(绝对定位),将某个元素放到指定的位置,使用left、right、top、bottom来定位,如果没有父元素,则其位置是相对于body来定位的。注意:绝对定位会使元素从文档流中被删除,结果就是该元素原本占据的空间被其它元素所填充
。
fixed(固定定位),fixed定位的参照物总是当前的文档
。
float(浮动),1)浮动会将元素从文档流中删除,他的空间会被其它元素补上;2)浮动的参数物是父元素,是在父元素这个容器中飘;3)为了清除浮动造成的对浮动元素之后元素的影响,我们在浮动元素之后加一个div,并将这个div的clear设置为both。
2、z-index 、层叠
默认层叠顺序原则:同级元素中,后面的元素覆盖前面的元素;父子元素中,子元素覆盖父元素
z-index可控制层叠顺序,但要在其css中添加一个position属性,注意:其值不能为static
3、作用域(scope)
ES5 全局作用域 局部作用域 ES6添加块级作用域(let、const)
let 不存在变量提升(即不能先使用后声明)、同一个作用域下不能重复定义同样的名称、有着严格的作用域===块级作用域{}
const 声明的一个只读的常量,一旦声明,常量的值就不能改变、const一旦声明了就得初始化,不能只声明不赋值
var x = 10; //全局作用域
function f1(){
var y = 100; //局部作用域
console.log(x)
};
function f2(){
var z = 1000; //局部作用域
console.log(x)
};
console.log(y);
重点:
全局变量和局部变量不同名时,作用域是整个程序
全局变量和局部变量同名时(同时var),全局变量的作用域不包含同名局部变量的作用域
// 面试题
var a = 10;
function f1(){
var b = 2*a; //2*undefined ==>NAN
var a = 20;
var c = a+1; //21
console.log(b);
console.log(c);
};
f1();
4、单例模式
(设计模式)
//单例(单一的实例)
//在系统中只能有且只有一个实例, 如资源管理器
//前端如何使用? 如页面上需要多个弹框,需要限制只能有一个(应用场景)
//例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单例应用</title>
</head>
<body>
<button id="btn">打开弹框</button>
<script>
//创建一个div
// var createWindow = function(){
// var div = document.createElement("div");//在内存中生成一个div
// div.innerHTML = "我是弹框,弹框应该唯一"; //给div加内容
// div.style.display = "none"; //默认隐藏
// document.body.appendChild(div);//把div写入页面
// return div;
// }
// btn.onclick = function(){
// var win = createWindow(); //创建弹框
// win.style.display = "block"; //显示弹框
// }
//缺点:每一次createWindow()都会在内存中生成一个div副本,没有必要
//可以使用单例思想重构
//使用单例模式重构
var createWindow = (function(){
var div;
return function(){ //闭包===函数+外部局部变量(缓存)
if (!div) { //若div不存在则创建,否则直接返回
div = document.createElement("div");//在内存中生成一个div
div.innerHTML = "我是弹框,弹框应该唯一"; //给div加内容
div.style.display = "none"; //默认隐藏
document.body.appendChild(div);//把div写入页面
}
return div;
}
})(); //()表示执行
btn.onclick = function(){
var win = createWindow(); //创建弹框 //等价于29的function
win.style.display = "block"; //显示弹框
}
</script>
</body>
</html>
5、原型链
原型的理解:原型是一个对象 其它对象可以通过它实现属性继承。对象分二种:普通对象和函数对象,prototype是函数对象才拥有的属性。
注意:
//总结:
//每一个函数对象都有一个prototype属性,但是普通对象没有;prototype下面又有constructor,指向这个函数;
//每一个对象都有一个__proto__内部属性,指向它所对应的构造函数的原型对象;
原型链:
//原型链
function F1(){
this.name1 = 'f1'
};
F1.prototype.name = 'object';
function F2(){
this.name2= 'f2'
};
function F3(){
this.name3 = 'f3'
};
F2.prototype = new F1(); //f2的原型是f1
F3.prototype = new F2(); //f3的原型是f2
var f = new F3(); //实例化的处理
f.name1;
//修改
f.__proto__.__proto__.__proto__.name= '12414';
//删除
delete f.__proto__.__proto__.__proto__.name;
6、继承(借用构造函数,寄生组合继承,缺点是什么)
//原型继承
function Animal(){
this.name = "动物名";
this.color=['red','yellow'];
};
Animal.prototype.sayName=function (name){return this.name;};
function Cat(){
};
Cat.prototype = new Animal();
var c1 = new Cat();
var c2 = new Cat();
c1.sayName("c1") //"动物名"
c1.color.push('blue');
c2.color //['red','yellow','blue']
原型继承缺点:原型对象上的属性和方法都能被实例对象共享
问题:1.不能传参 2.父类中的属性和方法一旦赋值给子类的原型属性,这些父类的属性和方法都属于子类的共享属性,修改引用类型(array、object、function)的数据会影响所有子实例的值
//将父对象的构造函数绑定在子对象上 在子类的构造函数内部中调用父类的构造函数
function Animal(name){
this.name = name;
this.color=['red','yellow'];
};
Animal.prototype.sayName=function (name){
return this.name;
};
function Dog (name) {
Animal.call(this,name) //Animal在当前Dog对象中执行
};
var d1 = new Dog('lll');
var d2 = new Dog('555');
d1.color.push('blue');
console.log(d1.color);//(3) ["red", "yellow", "blue"]
console.log(d1.name);//lll
console.log(d2.color);//(2) ["red", "yellow"]
console.log(d2.name);//555
console.log(d1.sayName);//undefined
构造函数继承解决了原型对象中包含引用类型值所带来问题
缺点:不能继承父类的原型对象上的方法
//解决方法--组合继承
function Animal(name){
this.name = name;
this.color=['red','yellow'];
};
Animal.prototype.sayName=function (name){
return this.name;
};
function Dog (name) {
//让父类的属性值继承下来,修改父类的引用类型的值,不会影响其他实例的引用类型的值
Animal.call(this,name)
};
//重写原型对象,把父类共享的方法继承下来
Dog.prototype = new Animal();
Dog.prototype.constructor=Dog;//原型继承会将子类原型上的constructor属性丢失,这里加上
var d1 = new Dog('lll');
var d2 = new Dog('555');
d1.color.push('blue');
console.log(d1.color); //打印出(3) ["red", "yellow", "blue"]
console.log(d1.name); //打印出lll
console.log(d2.color); //打印出(2) ["red", "yellow"]
console.log(d2.name); //打印出555
console.log(d1.sayName); //打印出ƒ(name){return this.name;}
组合继承问题:会调用父类构造函数2次
//寄生组合式继承 --- 解决了组合继承调用父类构造函数2次问题
function Animal(name){
this.name = name;
this.color=['red','yellow'];
};
Animal.prototype.sayName=function (name){
return this.name;
};
function Dog (name) {
//让父类的属性值继承下来,修改父类的引用类型的值,不会影响其他实例的引用类型的值
Animal.call(this,name)
};
//重写原型对象,把父类共享的方法继承下来
Dog.prototype = Object.create(Anmail.prototype) //解决了组合方式重复访问父类构造函数
Dog.prototype.constructor=Dog;//原型继承会将子类原型上的constructor属性丢失,这里加上
var d1 = new Dog('lll');
var d2 = new Dog('555');
d1.color.push('blue');
console.log(d1.color); //打印出(3) ["red", "yellow", "blue"]
console.log(d1.name); //打印出lll
console.log(d2.color); //打印出(2) ["red", "yellow"]
console.log(d2.name); //打印出555
console.log(d1.sayName); //打印出ƒ(name){return this.name;}
7、闭包
闭包:简单来说就是: 一个函数引用函数外部局部变量
由于在javascript中,只有函数内部的子函数才能读取局部变量,所以说,闭包可以简单理解成“定义在一个函数内部的函数
“。所以,在本质上,闭包是将函数内部和函数外部连接起来的桥梁
。
//闭包一
function f1(){
var a = 10; //相对于f2外部局部变量
function f2() { //f2可以访问a
console.log(a)
}
f2();
}
f1();
//闭包二
function a(){
var n = 0; //外部局部变量
this.add = function(){//函数引用外部局部变量
n++;
console.log(n)
}
}
var c = new a();
console.log(c.n) //undefined 因为n是局部
c.add(); //1
c.add(); //2
//闭包三
function a(){
var n = 0; //外部局部变量
function add(){//函数引用外部局部变量
n++;
console.log(n)
}
return add;
}
a()() //柯里化格式 a()()()()()
//闭包四 可以多次执行
var a = function(){
var n = 0;
return function(){ //闭包 匿名函数赋给a
n++;
console.log(n);
}
};
var b = a(); //b---闭包
var c = a(); //c---闭包
b(); //1
c(); //1
//闭包有什么用
//对象封装 能够将name缓存起来,避免污染
var Person = function(){
var name = "张三"; //get set 相当于对这个属性的操作
return {
getName: function(){//匿名函数 闭包
return name;
},
setName: function(newName) {//匿名函数 闭包
name = newName;
}
}
}(); //加上这个括号表示立即执行 Person == {.....}
console.log(Person.getName()) //张三
Person.setName("xxxxx");
console.log(Person.getName()) //xxxxx
//继承 人-----白人 黑人
var Person = function(){
var name = "张三"; //get set 相当于对这个属性的操作
return {
getName: function(){//匿名函数 闭包
return name;
},
setName: function(newName) {//匿名函数 闭包
name = newName;
}
}
};
var BlackPerson = function (){}
BlackPerson.prototype = Person(); //为什么不需要new() 因为已经是一个实例对象(字面量对象)
var p = new BlackPerson(); //p是一个对象,对象中有getName函数,函数引用name形成闭包
p.setName("yyyy");
console.log(p.getName())
总结:
//闭包 难----变化太多 有很多种用法 不难----一个函数引用函数外部局部变量
//原型 在你身上找不到---去原型上找----继续去原型的原型上找
//什么场景 需要维护一个函数的一些信息不变, 可以一直存在 被缓存
//可以多层嵌套 看你怎么使用
//缺点: 忽略
//不易理解, 占用内存
//优点:
//可以缓存数据, 封装, 柯里化, 很灵活
《JavaScript高级编程》书中建议:由于闭包会携带包含它的函数的作用域,因为会比其他函数占用更多内容,过度使用闭包,会导致内存占用过多。
8、Vue生命周期(父子生命周期,每个生命周期都发生了什么)
它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后:
阶段 | 介绍 |
---|---|
beforeCreate----创建前 | 组件实例更被创建,组件属性计算之前,数据对象data都为undefined,未初始化。 |
created----创建后 | 组件实例创建完成,属性已经绑定,数据对象data已存在,但dom未生成,$el未存在 |
beforeMount—挂载前 | vue实例的$el和data都已初始化,挂载之前为虚拟的dom节点,data.message未替换 |
mounted-----挂载后 | vue实例挂载完成,data.message成功渲染 |
beforeUpdate----更新前 | 当data变化时,会触发beforeUpdate方法 |
updated----更新后 | 当data变化时,会触发updated方法 |
beforeDestory—销毁前 | 组件销毁之前调用 |
destoryed—销毁后 | 组件销毁之后调用,对data的改变不会再触发周期函数,vue实例已解除事件监听和dom绑定,但dom结构依然存在 |
//beforecreate:el 和 data 并未初始化
//created:完成了 data 数据的初始化,el没有
//beforeMount:完成了 el 和 data 初始化
//mounted :完成挂载
//具体怎么用呢,通俗一些
//beforecreate : 举个例子:可以在这加个loading事件
//created :在这结束loading,还做一些初始化,实现函数自执行
//mounted : 在这发起后端请求,拿回数据,配合路由钩子做一些事情
//beforeDestroy: 你确认删除XX吗?
// destroyed :当前组件已被删除,清空相关内容
9、Vuex
实例:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
num:100,
flag:true
};
const getters={ //store对象中的计算属性
a(state){
return state.num % 2 ==0 ?'偶数':'奇数'
},
b(state){
return state.num +=10
},
};
const actions = {
ADD({commit,state}){ //context就是$store
commit('ADD');
},
DEL({commit,state}){ //context就是$store
commit('DEL');
}
};
const mutations = {
ADD(state){
state.num+=1;
},
DEL(state){
state.num-=1;
}
};
export default new Vuex.Store({
state,
getters,
mutations,
actions
})
vue代码:
<template>
<div class="home">
<!-- 直接获取store中的state -->
{{$store.state.num}}
<!-- 直接获取store中的getters -->
{{$store.getters.a}}
<!-- 直接触发store中的mutations-->
<button @click="$store.commit('ADD')">添加</button>
<button @click="del()">减少</button>
<!-- {{isEvenOrOdd}} -->
<h1>辅助函数:{{b}}</h1>
<button @click="ADD">添加</button>
<button @click="DEL">减少</button>
</div>
</template>
<script>
//辅助函数 mapGetters mapActions
import {mapState, mapGetters,mapActions } from 'vuex'
export default {
name: 'Home',
//methods:mapActions(['ADD','DEL']), //没有其它事件的处理
methods:{
...mapActions(['ADD','DEL']),
del(){
this.$store.dispatch('DEL'); //触发actions
},
increment(){
this.$store.dispatch('ADD');
},
decrement(){
this.$store.dispatch('DEL');
}
},
//computed:mapGetters(['a','b']) //没有其它状态的处理
computed:{
// ...mapState({
// num:state=>state.num
// }),
...mapGetters(['a','b']) ,
add(){
return this.$store.state.num+10
},
isEvenOrOdd(){
return this.$store.state.num % 2 ==0 ?'偶数':'奇数'
}
}
}
</script>
10、slot插槽
- 插槽最基本的用法就是允许我们想一个组件中插入一些
html
代码 - 有了插槽我们便可以向组件标签内写
html
代码
- 匿名插槽
- 具名插槽
- 作用域插槽
11、TCP三次握手四次挥手
序列号seq
:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号;序列号seq就是这个报文段中的第一个字节的数据编号。
确认号ack
:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号;序列号表示报文段携带数据的第一个字节的编号;而确认号指的是期望接收到下一个字节的编号;因此当前报文段最后一个字节的编号+1即为确认号。
确认ACK
:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效
同步SYN
:连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0。
终止FIN
:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接
PS:ACK、SYN和FIN这些大写的单词表示标志位,其值要么是1,要么是0;ack、seq小写的单词表示序号。
三次握手过程:
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
四次挥手过程:
1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前
面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规
定,FIN报文段即使不携带数据,也要消耗一个序号。
2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就
进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时
候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还
要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接
释放报文(在这之前还需要接受服务器发送的最后的数据)。
4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服
务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状
态,等待客户端的确认。
5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,
客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿
命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。
可以看到,服务器结束TCP连接的时间要比客户端早一些。
常见面试题目:
【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,
SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先
回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发
送FIN报文,因此不能一起发送。故需要四步握手。
【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以
最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该
ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收
到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在
该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment
Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到
2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
【问题3】为什么不能用两次握手进行连接?
答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始
序列号进行协商,这个序列号在握手过程中被发送和确认。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送
一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立
了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S是否已准备好,不知道S建
立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发
来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次
客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就
会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故
障,接着就关闭连接。
12、前端性能优化
- 减少http请求次数:CSS Sprites, JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存 ,图片服务器。
- 前端模板 JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数。
- 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。
- 当需要设置的样式很多时设置className而不是直接操作style。
- 少用全局变量、缓存DOM节点查找的结果。减少IO读取操作。
- 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。
- 图片预加载,将样式表放在顶部,将脚本放在底部加上时间戳。
- 避免在页面的主体布局中使用table,table要等其中的内容完全下载之后才会显示出来,显示比div+css布局慢。
对普通的网站有一个统一的思路,就是尽量向前端优化、减少数据库操作、减少磁盘IO。向前端优化指的是,在不影响功能和体验的情况下,能在浏览器执行的不要在服务端执行,能在缓存服务器上直接返回的不要到应用服务器,程序能直接取得的结果不要到外部取得,本机内能取得的数据不要到远程取,内存能取到的不要到磁盘取,缓存中有的不要去数据库查询。减少数据库操作指减少更新次数、缓存结果减少查询次数、将数据库执行的操作尽可能的让你的程序完成(例如join查询),减少磁盘IO指尽量不使用文件系统作为缓存、减少读写文件次数等。程序优化永远要优化慢的部分,换语言是无法“优化”的。
13、浏览器缓存
这里了解得不是很多
所谓的http缓存,就是浏览器自己给你的一个功能,一个缓存数据库,夹在服务端和客户端中间,你只需要设
置一些参数即可实现 缓存/不缓存/时效内缓存/时效外缓存等(默认存在缓存)
14、快速排序
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
代码实现:
function quickSort(arr,left,right){
var len = arr.length;
var left = typeof left != 'number' ? 0:left;
var right = typeof right != 'number' ? len-1:left;
if(left < right){
var partitionIndex = partition(arr,left,right);
quickSort(arr,left,partitionIndex-1);
quickSort(arr,partitionIndex+1,right);
}
}
function partition(arr,left,right){ //分区操作
var pivot = left,index = pivot+1; //pivot是基准(比较的那个数);index则是向右移动,每次循环记录比基准数小的数的个数,最后交换pivot与index-1,则把arr分成小于pivot的和大于pivot的两个分区
for(var i=index;i<=right;i++){
if(arr[i]<arr[pivot]){
swap(arr,i,index);
index++;
}
}
swap(arr,pivot,index-1);
return index-1;
}
function swap(arr,i,j){ //交换位置
var temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
15、移动端适配rem em
rem是(font size of the root element),意思就是根据网页的根元素来设置字体大小,和 em(font size of the element)的区别是,em是根据其父元素的字体大小来设置,而 rem 是根据网页的根元素(html)来设置字体大小的。(以设置字体大小为例,同样可用于width
、margin
等)
改相关的字体配置,都是默认显示 font-size 是 16px 即:
html {
font-size:16px;
}
那么如果我们想给一个 p 标签设置 12px 字体大小那么用 rem 来写就是:
p {
font-size: 0.75rem; //12÷16=0.75(rem)
}