百度提前批-百度智能小程序(面经详解)


在牛客网看面经时随机找的一个面经,将问到的知识点详细介绍概括了一下,有一些知识点参见其他博客,希望能帮助到更多人,最后大家一起学习进步,加油!

百度提前批-百度智能小程序(面经详解)

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代码
  1. 匿名插槽
  2. 具名插槽
  3. 作用域插槽

可参见博客

11、TCP三次握手四次挥手

img

序列号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小写的单词表示序号。

img

三次握手过程:

img

第一次握手:建立连接时,客户端发送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),此包发送完毕,客户端和服务器进入ESTABLISHEDTCP连接成功)状态,完成三次握手。

四次挥手过程:

img

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)来设置字体大小的。(以设置字体大小为例,同样可用于widthmargin等)

改相关的字体配置,都是默认显示 font-size 是 16px 即:

html {
    font-size:16px;
}

那么如果我们想给一个 p 标签设置 12px 字体大小那么用 rem 来写就是:

p {
    font-size: 0.75rem; //12÷16=0.75(rem)
}

参见博文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值