(一) JS基础篇
一、JS基础
1.变量类型和计算
1.1 题目
JS中使用typeof能得到哪些类型
1
2
3
4
5
6
7
8
9typeof undefined //undefined
typeof 'abc' //string
typeof 123 //number
typeof true //boolean
typeof {} //object
typeof [] //object
typeof null //object,这是一个历史错误
typeof console.log //function
//typeof可以详细区别值类型(undefined、string、number、boolean),但不能详细区分引用类型
何时使用 === 何时使用 ==
1
2
3
4
5if(obj.a == null){
//这里相当于 obj.a === null || obj.a === undefined, 简写形式
//这是 jquery 源码推荐的写法
//除了这种情况外 其它情况一律采用===写法
}
JS中有哪些内置函数
1
2
3
4
5
6
7
8
9
10//数据封装类对象(原生对象,需要new)
Number
String
Boolean
Object
Array
Function
Date
RegExp
Error
JS变量按照存储方式分为哪些类型,并描述其特点
1
2
3
4
5
6
7
8
9
10
11//值类型 (互不影响)
var a=10
var b=a
a=11
console.log(b) //10
//引用类型(相互影响)
var obj1={ x:100 }
var obj2=obj1
obj2.x=200
console.log(obj1.x) //200
如何理解JSON
1
2
3//JSON不仅是一种数据格式,还是JS的内置对象
JSON.stringfy({a:10,b:20})
JSON.parse('{"a":10,"b":20}')
1.2 涉及知识点
变量类型
- 值类型 vs 引用类型({} [] function)
- typeof运算符详解(number string boolean undefined object function)
变量计算
强制类型转换
字符串拼接
1
100 + '10' // '10010'
==运算符
1
2
3100 == '100'
'' == 0
null == undefinedif语句
1
2
3if(100) { console.log(110) }
if('') { console.log(110) }
//if语句的条件转换为false的情况:0 '' false NaN null undefined逻辑运算符
1
2
3
4
5
6
710 && 0 //0 , 10->true
'' || 'abc' //"abc", ''->false
//检验一个变量是true或false
var a=100
!!a //true
var b=''
!!b //false
2.原型和原型链
2.1 题目
如何判断一个变量是数组类型
1
2
3var arr=[];
arr instanceof Array;//true
typeof arr;//object,typeof 无法判断是否是数组类型写一个原型链继承的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//封装一个 DOM 查询的例子
function Elem(id){
this.elem=document.getElementById(id);
}
Elem.prototype.html=function(val){
var elem=this.elem
if(val){
elem.innerHTML=val;
return this;//链式操作
}else{
return elem.innerHTML;
}
}
Elem.prototype.on=function(type,fn){
var elem=this.elem;
elem.addEventListener(type,fn);
return this;//链式操作,this指向构造函数的实例
}
var el=new Elem('div1');
console.log(el.html());
//链式操作
el.html('<p>hello world!</p>').on('click',function(){
alert('clicked');
}).html('<p>javascript</p>');
描述 new 一个对象的过程
1
2
3
41. 创建一个新对象
2. this指向这个新对象
3. 执行代码,即对this赋值
4. return thiszepto (或其他框架) 源码中如何使用原型链
2.2 涉及的知识点
构造函数
构造函数—扩展
原型规则和实例
所有的引用类型(对象 数组 函数),都具有对象特性,即可自由扩展属性(除了 null 以外)
1
2
3
4var obj={};obj.a=100;
var arr=[];arr.a=100;
function fn() {}
fn.a=100;所有的引用类型(对象 数组 函数),都有一个 proto 属性(隐式原型),属性值是一个普通对象
所有函数,都有一个prototype属性(显式原型),属性值也是一个普通对象
所有的引用类型(对象 数组 函数), proto 属性指向它的构造函数的 prototype 属性值
当试图得到一个对象的某个属性时,如果此对象本身没有这个属性,那么会去它的 proto (即它的构造函数的prototype)中寻找
补充
1.this 指向
1 | //构造函数 |
2.循环对象自身的属性
1 | var item |
原型链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//构造函数
function Foo(name,age){
this.name=name;
}
Foo.prototype.alertName=function (){
alert(this.name);
}
//创建实例
var f=new Foo('winnie');
f.printName=function (){
console.log(this.name);
}
//测试
f.printName();
f.alertName();
f.toString();//要去f.__proto__.__proto__中寻找 提醒:当试图得到一个对象的某个属性时,如果此对象本身没有这个属性,那么会去它的 proto (即它的构造函数的prototype)中寻找
instanceof
用于判断 引用类型 属于哪个 构造函数 的方法
判断逻辑:顺着 f 的 proto 一层一层往上
3.作用域和闭包
3.1 题目
说一下对变量提升的理解
- 变量定义
- 函数声明(注意和函数表达式的区别)
提示:与执行上下文相关
说明 this 几种不同的使用场景
作为构造函数执行
作为对象属性执行
作为普通函数执行
call apply bind
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28//构造函数
function Person(name){
this.name=name;
console.log(this);//Person {name: "winnie"}
}
var f=new Person('winnie');
//对象属性
var obj={
name:'winnie',
printName:function(){
console.log(this);//{name: "winnie", printName: ƒ}
}
}
obj.printName();
//普通函数
function fn(){
console.log(this);//window
}
fn();
//call apply bind
var fn1=function (name){
alert(name);
console.log(this);//{x:100}
}.bind({x:100})
fn1('winnie');
创建10个标签,点击的时候弹出来对应的序号
1
2
3
4
5
6
7
8
9
10
11
12
13var i;
for(i=0;i<10;i++){
(function(i){
//函数作用域
var a = document.createElement('a');
a.innerHTML= i+'<br/>';
a.addEventListener('click',function(e){
e.preventDefault();
alert(i);//自由变量,要在父级作用域获取值
})
document.body.appendChild(a);
})(i)
}如何理解作用域
1.没有块级作用域
2.只有全局和函数作用域(函数里面的变量,外面无法访问和修改,只能函数里面使用)
要点:
自由变量:当前作用域没有定义的变量
作用域链,即自由变量的查找:函数的父级作用域,是函数定义时的父级作用域,并非执行时的父级作用域
1
2
3
4
5
6
7
8
9
10
11
12var a=100;
function F1(){
var b=200;
function F2(){
var c=300;
console.log(a);//a是自由变量,查询链:F2->F1->全局
console.log(b);
console.log(c);
}
F2();
}
F1();闭包的两个场景
- 函数作为返回值
- 函数作为参数传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24function F1(){
var a=100;
return function F2(){
console.log(a);
}
}
var f1=F1();//返回函数F2
var a=200;
f1();//100 自由变量查找的父级作用域,是函数定义时的父级作用域
//函数作为参数传递
function F1(){
var a=100;
return function (){
console.log(a);
}
}
var f1=F1();
function F2(fn){
var a=200
fn();//fn定义时的父级作用域是F1,执行时的父级作用域是F2
}
F2(f1);//100
实际开发中闭包的应用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//闭包实际应用中主要用于封装变量,收敛权限
function isFirstLoad(){
var _list=[];
return function(id){
if(_list.indexOf(id)>=0){
return false;
}else{
_list.push(id);
return true;
}
}
}
// 使用
var firstLoad=isFirstLoad();
firstLoad(10);//true
firstLoad(10);//false
3.2 涉及知识点
执行上下文
- 范围:一段< script >或者一个函数
- 全局(一段< script > ):变量定义、函数声明在执行之前往上提升
- 函数:变量定义、函数声明、this、arguments在执行之前往上提升
this
this要在执行时才能确认值,定义时无法确认
作用域
作用域链
闭包
4.异步与单线程
4.1 题目
同步和异步的区别是什么?分别举一个同步和异步的例子
- 同步会阻塞代码执行,而异步不会
- alert是同步,setTimeout是异步
1
2
3
4
5
6
7
8
9
10
11//异步
console.log(100);
setTimeout(function(){
console.log(200)
},1000);
console.log(300);
//同步
console.log(100);
alert(200); //1s之后点击确定
console.log(300);
一个关于setTimeout的笔试题
1
2
3
4
5
6
7
8
9
10
11console.log(1);
setTimeout(function(){
console.log(2)
},0);
console.log(3);
setTimeout(function(){
console.log(4);
},1000);
console.log(5);
//连续打印1 3 5 2,等待一秒后打印4
前端使用异步的场景有哪些
- 定时任务:setTimeout,setInterval
- 网络请求:ajax请求,动态
加载
- 事件绑定
共同特点:都需要等待,不使用异步,会造成代码执行阻塞
4.2 涉及的知识点
- 什么是异步(对比同步)
- 前端使用异步的场景
- 异步和单线程
5.其它知识
5.1 题目
获取 2017-06-10 格式的日期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function formatDate(dt){
if(!dt){
var dt=new Date();
}
var year=dt.getFullYear();
var month=dt.getMonth()+1;
var date=dt.getDate();
//强制类型转换
if(month<10){
month='0'+month;
}
if(date<10){
date='0'+date;
}
return year+'-'+month+'-'+date;
}
获取随机数,要求是长度一致的字符串格式
1
2var random=(Math.random()+'0000000000').slice(0,10);
console.log(random);
写一个能遍历对象和数组的通用forEach函数
1
2
3
4
5
6
7
8
9
10
11
12function forEach(obj,fn){
if(obj instanceof Array){
obj.forEach(function(item,index){
fn(index,item);
});
}
else{
for(key in obj){
fn(key,obj[key]);
}
}
}
5.2 涉及知识点
日期
1
2
3
4
5
6
7
8
9Date.now(); //获取当前时间毫秒数
var dt=new Date();
dt.getTime();//获取毫秒数
dt.getFullYear();//年
dt.getMonth();//月(0-11)
dt.getDate();//日(0-31)
dt.getHours();//时(0-23)
dt.getMinutes();//分(0-59)
dt.getSeconds();//秒(0-59)Math
- Math.random()
对象API
- for in
数组API
forEach 遍历所有元素
1
2
3
4var arr=[1,2,3];
arr.forEach(function(item,index){
console.log(index,item);
});
every 判断所有元素是否都符合条件
1
2
3
4
5
6
7
8var arr=[1,2,3];
var res=arr.every(function(item,index){
//用来判断数组的所有元素,全部都满足条件
if(item<4){
return true;
}
})
console.log(res);//true
some 判断是否有至少一个元素符合条件
1
2
3
4
5
6
7
8var arr=[1,2,3];
var res=arr.some(function(item,index){
//用来判断数组的所有元素,只要有一个满足条件即可
if(item<2){
return true;
}
})
console.log(res);//true
sort 排序
1
2
3
4
5
6
7
8
9var arr=[1,4,2,3,5];
var arr2=arr.sort(function(a,b){
//从小到大排序
return a-b;
//从大到小排序
//return b-a
})
console.log(arr2);//[1,2,3,4,5]
map 对数组重新组装,生成新数组
1
2
3
4
5
6var arr=[1,2,3,4];
var arr2=arr.map(function(item,index){
//将元素重新组装,并返回
return '<b>'+item+'</b>';
})
console.log(arr2);//["<b>1</b>", "<b>2</b>", "<b>3</b>", "<b>4</b>"]
filter 过滤符合条件的元素
1
2
3
4
5
6
7
8var arr=[1,2,3];
var arr2=arr.filter(function(item,index){
//通过某一条件过滤数组
if(item>=2){
return true;
}
});
console.log(arr2);//[2, 3]
二、JS-Web-API
1.DOM
1.1 题目
DOM是哪种基本的数据结构?
树
DOM操作常用的API有哪些?
- 获取DOM节点,以及节点的property和Attribute
- 获取父节点,获取子节点
- 新增节点,删除节点
DOM节点的attr和property有何区别
- property只是一个JS对象属性的修改
- Attribute是对html标签属性的修改
1.2 涉及知识点
DOM(Document Object Model 文档对象模型)本质
浏览器把拿到的html代码,结构化为一个浏览器能识别并且JS可操作的一个模型。
DOM节点操作
获取DOM节点
1
2
3
4var div1=document.getElementById('div1');//元素
var divList=document.getElementsByTagName('div');//集合
var containerList=document.getElementsByClassName('.container');//集合
var pList=document.querySelectorAll('p');//集合property
1
2
3
4
5
6
7
8
9var pList=document.querySelectorAll('p');
var p=pList[0];//p本质上是一个JS对象,指向node节点
console.log(p.style.width);//获取样式
p.style.width='100px';//修改样式
console.log(p.className);//获取class
p.className='p1';//修改class
//获取nodeName和nodeType
console.log(p.nodeName);
console.log(p.nodeType);
Attribute
1
2
3
4
5
6var pList=document.querySelectorAll('p');
var p=pList[0];
p.getAttribute('data-name');
p.setAttribute('date-name','imooc');
p.getAttribute('style');
p.setAttribute('style','font-size:30px;');
DOM结构操作
新增节点
1
2
3
4
5
6
7
8var div1=document.getElementById('div1');
//添加新节点
var p1=document.createElement('p');
p1.innerHTML="This is p1";
div1.appendChild(p1);//添加新创建的元素
//移动已有节点
var p2=document.getElementById('p2');
div1.appendChild(p2);
获取父元素
获取子元素
删除节点
1
2
3
4var div1=document.getElementById('div1');
var parent=div1.parentElement;
var child=div1.childNodes;
div1.removeChild(child[0]);
2.BOM
2.1 题目
如何检测浏览器的类型
参考下面的navigator
拆解url的各部分
参考下面的location
2.2 涉及的知识点
navigator
1
2
3var ua=navigator.userAgent;
var isChrome=ua.indexOf('Chrome');
console.log(isChrome);screen
1
2console.log(screen.width);
console.log(screen.height);location
1
2
3
4
5
6
7//针对url
console.log(location.href);//完整的url
console.log(location.host);//域名
console.log(location.protocol);//协议
console.log(location.pathname);//除域名和协议外的url部分,是路径
console.log(location.search);//?后面的查询字符串,查询参数
console.log(location.hash);//# 后面的hash参数history
1
2history.forward();
history.back();
3.事件
3.1题目
编写一个通用的事件绑定函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28function bindEvent(elem,type,selector,fn){
if(fn==null){
fn=selector;
selector=null;
}
elem.addEventListener(type,function(e){
var target;
if(selector){
target=e.target;
if(target.matches(selector)){
fn.call(target,e);
}
}else{
fn(e);
}
})
}
//使用代理(四参数)
var div1=document.getElementById('div1');
bindEvent(div1,'click','a',function(e){
console.log(this.innerHTML);
})
//不使用代理(三参数)
var a=document.getElementById('a1');
bindEvent(div1,'click',function(e){
console.log(a.innerHTML);
})
描述事件冒泡流程
- DOM树形结构
- 事件冒泡
- 阻止冒泡
- 冒泡的应用(代理)
对于一个无限下拉加载图片的页面,如何给每个图片绑定事件
- 使用代理
- 知道代理的两个优点
3.2 涉及的知识点
通用事件绑定
事件冒泡
代理
好处:
- 代码简洁(不需要写很多事件绑定)
- 减少浏览器内存占用(绑定一次事件和绑定一千次是不一样的)
原理:事件冒泡
4.Ajax
4.1 题目
手动编写一个ajax,不依赖第三方库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25function ajax(url,fnSucc,fnFail){
//创建oAjax对象(拥有一个手机)
if(window.XMLHttpRequest){
var oAjax=new XMLHttpRequest();//非IE6
}else{
var oAjax=new ActiveXObject('MicroSoft.XMLHTTP');//IE6
}
//连接到服务器(拨号)
oAjax.open("GET",url,true);
//发送请求(说)
oAjax.send();
//接收返回值(听)
oAjax.onreadystatechange=function (){
if(oAjax.readyState==4){
if(oAjax.status==200){
fnSucc(oAjax.responseText);
}else{
if(fnFail){
fnFail(oAjax.status);
}
}
}
}
}
跨域的几种实现方式
详情见跨域。
4.2 涉及的知识点
- XMLHttpRequest
- readyState(服务器和浏览器,进行到哪一步了,请求状态)
- 0 ——未初始化,还没调用open方法,没有连接到服务器
- 1——(载入)已调用send()方法,正在发送请求
- 2——(载入完成)send()方法完成,浏览器已接收到全部响应内容
- 3——(解析)正在解析响应内容
- 4——(完成)响应内容解析完成,还要判断是否解析成功
- 状态码说明
- 2xx -表示成功处理请求。如:200
- 3xx - 表示重定向,浏览器直接跳转
- 4xx - 客户端请求错误,如404
- 5xx - 服务端请求错误
- 跨域
- 定义:浏览器有同源策略,不允许 ajax 访问其他域接口
- 条件:协议、域名、端口,有一个不同就算跨域
- 有三个标签允许跨域加载资源:
- < img src=xxx> 用于打点统计,统计的网站可能是其他域
- < link href=xxx> 可以使用CDN ,CDN也是其他域
- < script src=xxx> 可以使用CDN,也可以用于JSONP
5.存储
5.1 题目
- 请描述一下cookie、sessionStorage和localStorage的区别
- 容量:前者是4KB,后者是5MB
- 是否会携带到ajax中:前者是每次http请求都会携带,后者是HTML5专门为存储而设计
- API易用性:前者需要封装document.cookie,后者有专门获取和修改的getItem和setItem两个API
5.2 涉及的知识点
cookie
- 本身用于客户端和服务器端通信
- 但是它有本地存储的功能,于是就被”借用”
- 使用 document.cookie=… 获取和修改即可(获取的是字符串)
缺点:
- 存储量太小,只有4KB
- 所有 http 请求都带着,会影响获取资源的效率
- API简单,需要封装才能用 document.cookie=……
locationStorage和sessionStorage
- HTML5专门为存储而设计,最大容量5M
- API简单易用
- localStorage.setItem(key,value);localStorage.getItem(key)
建议:
统一使用try-catch封装(ios safari 隐藏模式下 localStorage.getItem会报错)
三、JS开发环境
1.git
常用Git命令
git add .
git status
git diff
git checkout xxx —还原到改动前
git commit -m “xxx” —提交到本地仓库
git push origin master —提交到远程仓库
git pull origin master —拉取远程仓库master分支上的代码
下列命令常用于多人协作:
git branch —列出所有的分支
git checkout -b xxx —创建新的分支,并切换
git checkout xxx —切换分支
git merge xxx —将当前分支与xxx分支合并。将本地分支与master分支合并之前,要将master分支中最新的代码拉取到本地。
2. 模块化
不使用模块化的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26//util.js getFormatDate函数
//a-util.js aGetFormatDate函数 依赖getFormatDate
//a.js 依赖aGetFormatDate函数
//util.js
function getFormatDate(date,type){
//type==1 返回 2019-02-09
//type==2 返回 2019年2月9日
}
//a-util.js
function aGetFormatDate(date){
//返回 2019年2月9日 格式
return getFormatDate(date,2);
}
//a.js
var dt=new Date();
console.log(aGetFormatDate(dt));
//使用,按顺序引用
<script src='util.js'></script>
<script src='a-util.js'></script>
<script src='a.js'></script>
//这些代码中的函数必须是全局变量,才能暴露给使用方,会造成全局变量污染
//a.js知道要引用a-util.js,但并不知道要引用util.js,依赖关系不明确
使用模块化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//util.js
export {
getFormatDate:function(date,type){
//type==1 返回 2019-02-09
//type==2 返回 2019年2月9日
}
}
//a-util.js
var getFormatDate=require('util.js');
export{
aGetFormatDate:function(date){
//返回 2019年2月9日 格式
return getFormatDate(date,2);
}
}
//a.js
var aGetFormatDate=require('a-util.js');
var dt=new Date();
console.log(aGetFormatDate(dt));
//直接<script src='a.js'></script> 其他的根据依赖关系自动引用
//输出的两个函数,不需要声明为全局变量,避免了变量污染和覆盖的问题
AMD(Asynchronous Module Definition 异步模块定义)
- require.js
- 全局 define 函数
- 全局 require 函数
- 依赖JS会自动、异步加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40//util.js
define(function(){
return {
getFormatDate:function(date,type){
if(type===1){
return '2019-02-09';
}
if(type===2){
return '2019年2月9日'
}
}
}
});
//a-util.js
define(['./util.js'],function(util){
return {
aGetFormatDate:function(date){
return util.getFormatDate(date,2);
}
}
});
//a.js
define(['./a-util.js'],function(aUtil){
return {
printDate:function(date){
console.log(aUtil.aGetFormatDate(date));
}
}
});
//main.js 入口文件,define过的函数可以require
require(['./a.js'],function(a){
var date=new Date();
a.printDate(date);
});
//使用:首先要下载requirejs文件
<script src="./require.js" data-main='./main.js'></script>
CommonJS
- nodejs 模块化规范,现在被前端大量使用,原因:
- 前端开发依赖的插件和库,都可以从 npm 中获取
- 构建工具的高度自动化,使得使用npm的成本非常低
- CommonJS 不会异步加载JS,而是 同步 一次性加载出来
- 需要构建工具支持
- 一般和 npm 一起使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//util.js
module.exports={
getFormatDate:function(date,type){
if(type===1){
return '2019-02-09';
}
if(type===2){
return '2019年2月9日'
}
}
}
//a-util.js
var util=rquire('./util.js');
module.exports={
aGetFormatDate:function (date){
return util.getFormatDate(date,2);
}
}
//a.js
var aUtil=require('./a-util');
module.exports={
print:function(date){
console.log(aUtil.aGetFormatDate(date));
}
}- nodejs 模块化规范,现在被前端大量使用,原因:
AMD 和 CommonJS的使用场景
- 需要异步加载JS ,使用AMD
- 使用了npm 之后建议使用CommonJS
3.上线和回滚
- 上线流程要点
- 将测试完成的代码提交到git版本库的master分支
- 将当前服务器的代码全部打包并记录版本号,备份
- 将master分支的代码提交覆盖到线上服务器,生成新的版本号
- 回滚流程要点
- 将当前服务器的代码打包并记录版本号,备份
- 将上一个版本的代码解压,覆盖到线上服务器,并生成新的版本号
四、运行环境
1.页面加载过程
1.1 题目
从输入url到得到html的详细过程
见下述知识点中加载一个资源的过程
window.onload 和 DOMContentLoaded 的区别
1
2
3
4
5
6window.addEventListener('load',function(){
//页面的全部资源加载完才会执行,包括图片、视频等
});
document.addEventListener('DOMContentLoaded',function(){
//DOM 渲染完即可执行,此时图片、视频还可能没有加载完
});
1.2 知识点
- 加载资源的形式
- 输入url( 或跳转页面 )加载html
- 加载html中的静态资源(js、css文件、图片等)
- 加载一个资源的过程
- 浏览器根据 DNS 服务器得到域名的 IP 地址
- 向这个 IP 的机器发送 http 请求
- 服务器收到、处理并返回 http 请求
- 浏览器得到返回内容
- 浏览器渲染页面的过程
- 根据 HTML 结构生成 DOM Tree
- 根据 CSS 生成 CSSOM
- 将 DOM Tree 和 CSSOM 整合形成 RenderTree
- 根据 RenderTree 开始渲染和展示
- 遇到 < script >时,会执行并阻塞渲染
2.性能优化
原则
- 多使用内存、缓存或其他方法
- 减少 CPU 计算、减少网络请求
加载资源优化
- 静态资源的压缩合并
- 静态资源缓存
- 通过链接名称控制缓存
- 只有内容改变的时候,链接名称才会改变
- 使用CDN(content delivery network) 让资源加载更快
- 使用 SSR (server side render) 后端渲染,数据直接输出到HTML
- 现在 Vue React 提出了这样的概念
- 早期的 jsp php asp 都属于后端渲染
渲染优化
CSS 放前面,JS放后面
懒加载(图片懒加载、下拉加载更多)
1
2
3
4
5
6
7<img src="preview.png" data-realsrc="abc.png">
<script type="text/javascript">
var img1 = document.getElementById('img1');
img1.src = img1.getAttribute('data-realsrc');
</script>
//先加载一个体积较小的图片(浏览器可能还有缓存),浏览器渲染会更快减少 DOM 查询,对 DOM 查询做缓存
1
2
3
4
5
6
7
8
9
10
11//未缓存 DOM 查询
var i
for(i=0;i<document.getElementsByTagName('p').length;i++){//每循环一次就要重新查询一次,性能消耗大
//todo
}
//缓存了 DOM 查询
var pList = document.getElementsByTagName('p');//一次 DOM 查询
var i ;
for(i=0;i<pList.length;i++){
//todo
}减少 DOM 操作,多个操作尽量合并在一起执行
1
2
3
4
5
6
7
8
9
10
11//合并 DOM 插入
var listNode = document.getElementById('list');
//要插入 10 个 li 标签,一般情况要进行10次 DOM 插入
var frag=document.createDocumentFragment();
var x,li;
for(x=0;x<10;x++){
li = document.createElement('li');
li.innerHTML = "List item" + x;
frag.appendChild(li);//这个插入不会触发 DOM 操作
}
listNode.appendChild(frag);//这里触发一次DOM插入事件节流和函数防抖
防抖 原理:延迟一段时间再执行,如果在延迟的时间内继续触发,会重新计算。
节流 原理:隔一段时间,执行一次。就像水龙头滴一样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//防抖
let debounce=(func,delay)=>{
let tId,context,args;
return function(){
context=this;
args=[].slice.call(arguments,0);
clearTimeout(tId);
tId=setTimeout(fuction(){
func.apply(context,args);
},delay);
}
}
//节流
let throttle=(func,delay)=>{
let timer=null,context,args;
return function(){
context=this;
args=[].slice.call(arguments,0);
if(!timer){
timer=setTimeout(function(){
timer=null;
func.apply(context,args);
},delay);
}
}
}
尽早执行操作(如DOMContentLoaded)
3.安全性
XSS (cross site script) 跨站脚本攻击
简单事例
- 在新浪博客写一篇文章,同时偷偷插入一段< script >。( 脚本代码中,获取cookie,并发送给攻击者的服务器)
- 发布博客,有人查看博客内容
- 会把查看者的 cookie 发送到攻击者的服务器
预防方法
- 前端替换关键字,例如替换 < 为 < ; > 为 > ; (影响性能)
- 后端替换
CSRF/XSRF (Cross-site request forgery)跨站请求伪造
- 简单事例
- 你已登录一个购物网站,正在浏览商品。该网站的付费接口是 xxx.com/pay?id=100 但是没有任何验证。
- 然后你收到一封邮件,隐藏着 < img src=”xxx.com/pay?id=100” >。一旦查看邮件,就会向付费接口发送请求。
- 你查看邮件的时候,就已经悄悄的付费购买了。
- 预防方法
- 增加验证流程,如输入指纹、密码、短信验证码 (后端预防为主)
- 简单事例
(二) JS高级篇
一、ES6
题目:
- ES6 模块化如何使用,开发环境如何打包
- 语法:import export (注意有无 defult)
- 环境:babel 编译 ES6语法,模块化可用 webpack 和 rollup
- 扩展:说一下自己对模块化标准统一的期待
- Class 和普通构造函数有何区别
- Class 在语法上更加贴合面向对象的写法
- Class 实现继承更加易读、易理解
- 更易于写 java 等后端语言人员的使用
- 本质还是语法糖
- Promise 的基本使用和原理
- new Promise 实例 ,并且 return
- new Promise 时要传入resolve、reject两个参数
- 成功时执行resolve() 失败时执行reject()
- then监听结果
- 总结一下ES6其他常用功能
1.模块化
模块化的基本语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//util.js
export default{
a:100
}
//util2.js
export function fn1(){
alert('fn1');
}
export function fn2(){
alert('fn2');
}
//index.js
import util from './util.js'
import { fn1,fn2 } from './util2.js'
console.log(util);
fn1();
fn2();
开发环境配置
- .babelrc
- rollup.config.js 或 webpack.config.js ,rollup功能单一,webpack功能强大
关于JS众多模块化标准
- 一开始没有模块化
- AMD 成为标准,require.js (也有CMD)
- 前端打包工具,使得nodejs模块化可以被使用(CommonJS)
- ES6出现 ,想统一现在所有模块化标准
- nodejs 积极支持ES6 ,但浏览器尚未统一,需要babel转译
- 可以自造lib,但是不要自造标准
2.class
1 | //Class 语法 |
3.promise
1 | //所有图片都加载完再添加到页面 |
4.Generator和async
1 | //Generator函数与iterator遍历器的关系 |
5.Proxy和Reflect
1 | function validate(target,validator){ |
6.decorator
1 | //修饰属性 |
7.其他常用功能
- let/const
- 多行字符串/模板变量
- 解构赋值
- 块级作用域
- 函数默认参数
- 箭头函数
二、原型
题目:
说一个原型的实际应用
- 描述一下 jquery 如何使用原型
- 描述一下 zepto 如何使用原型
- 再结合自己的项目经验,说一个自己开发的例子
原型如何体现它的扩展性
说一下 jquery 和 zepto 的插件机制
1
2
3
4//简单的插件例子
$.fn.getNodeName=function(){
return this[0].nodeName;
}扩展到 $.fn , 好处是:
- 只有$ 会暴露在window全局变量(限制,一般一个库只暴露一个全局变量)
- 将插件扩展统一到$.fn.xxx这一个接口,方便使用
结合自己的开发经验,做过的基于原型的插件
1.jquery 原型实现
1 | // 入口函数 |
2.zepto 原型实现
1 | //空对象 |
三、异步
题目:
什么是单线程,和异步有什么关系
- 单线程就是同一时间只能做一件事,两段 JS 不能同时执行
- 原因是为了避免 DOM 渲染的冲突
- 异步是一种 “无奈” 的解决方案,仍然存在很多问题
- 没按照书写顺序执行,可读性差
- callback中不容易模块化
什么是event-loop
事件轮询,JS 实现异步的具体解决方案:
同步代码,直接执行
异步函数先放在 异步队列 中
待同步函数执行完毕后,轮询执行异步队列中的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30$.ajax({
url:"xxxx",
success:function(result){
console.log('a');
}
})
setTimeout(function(){
console.log('b')
},100);
setTimeout(function(){
console.log('c')
});
console.log('d');
//主进程
console.log('d');
//异步队列
//立刻被放入
function(){
console.log('c');
}
//100ms 之后被放入
function(){
console.log('b');
}
//ajax 加载完成时放入
function(result){
console.log('a');
}
- 是否用过 jQuery的deferred
- Promise的基本使用和原理
- 介绍一下 async/await (和Promise 的区别、联系)
- 总结一下当前 JS 解决异步的方案
四、virtual dom(虚拟DOM)
题目:
vdom是什么?为何会存在vdom?
vdom是用 JS 模拟DOM结构,存在原因:
- DOM 操作非常”昂贵”
- 将DOM操作放在JS层,提高效率
vdom如何应用,核心API是什么?
可用snabbdom的用法来举例,核心API:h函数、patch函数
介绍一下diff算法
什么是 diff 算法
linux的基础命令
去繁就简
关注核心流程
vdom 为何用 diff 算法
DOM 操作是”昂贵”的,因此要尽量减少 DOM 操作
找出本次DOM必须更新的节点来更新,其他的不更新
- 这个”找出”的过程,就需要 diff 算法
diff 算法的实现流程
- patch(container,vnode) —> 核心函数 createElement
- patch(vnode,newVnode) —> 核心函数 updateChildren
1.设计一个需求场景
1 | //1.将以下数据展示成一个表格 2.随便修改数据中的一个信息,表格也随着修改 |
2.patch(container,vnode)和patch(vnode,newVnode)核心实现
1 | //vnode -> 真实dom节点 |
五、MVVM 和 Vue
题目:
说一下使用 jQuery 和使用框架的区别
- 数据和视图的分离,解耦(开放封闭原则:对扩展开放,对修改封闭)
- jQuery 数据和视图没有分离;vue 数据和视图分离
- 以数据驱动视图,只关心数据变化,DOM 操作被封装
- vue 以数据驱动视图,并没有操作DOM;jQuery则相反
- 数据和视图的分离,解耦(开放封闭原则:对扩展开放,对修改封闭)
说一下对MVVM的理解
MVC(M-Model V-View C-Controller)
- M - Model 数据
- V - View 视图、界面
- C - Controller 控制器、逻辑处理
MVVM(M-Model V-View VM-ViewModel)
- Model - 模型、数据
- View - 视图、模板(视图和模型是分离的)
- ViewModel - 连接 Model 和 View
三者之间的联系,以及对应到示例代码:
**MVVM/Vue三要素:**
* 响应式:vue 如何监听到data中每个属性的变化?
* 模板引擎:vue的模板如何被解析,指令如何处理?
* 渲染:vue的模板如何被渲染成html?以及渲染过程
vue 中如何实现响应式
- 关键是理解 Object.defineProperty
- 将 data 的属性代理到 vm 上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20var vm={};
var data={
name:"winnie",
age:20
};
var key,value;
for(key in data){
(function(key){
Object.defineProperty(vm,key,{
get:function(){
console.log('get',data[key]);
return data[key];
},
set:function(newVal){
console.log('set',newVal);
data[key]=newVal;
}
})
})(key)
}vue中如何解析模板
模板:本质上是一段字符串,与 html 格式很像,但有很大区别,不过最终还是转换为html来显示。有逻辑( 如 v-if v-for ),嵌入JS变量……
模板必须转换为 JS 代码,原因如下:
- 有逻辑(v-if v-for),必须用 js 才能实现(图灵完备)
- 转换为 html 渲染页面,必须用 JS才能实现(JS可以操作DOM)
因此,模板最终转换成一个JS函数(render函数)
render 函数 ,函数执行返回 vnode ——> 类比 snabbdom 中的 h函数
updateComponent ——>类比snabbdom中的 patch 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40//模板
<div id="app">
<p>{{price}}</p>
</div>
//render 函数,执行之后返回的是vnode
with(this){
return _c(
'div',
{
attrs:{"id":"app"}
},
[
_c('p',[_v(_s(price))])
]
)
}
/* this 即 vm ,price 即 this.price 也即 vm.price ,即 data 中的 price ,_c 即 this._c 即 vm._c */
// vm._c 比较 snabbdom中 h 函数的写法
var vnode=h('ul#list',{},[
h('li.item',{},'Item 1'),
h('li.item',{},'Item 2')
])
//updateComponent 和 patch 函数
vm._update(vnode){
const preVnode=vm._vnode;
vm._vnode=vnode;
if(!preVnode){
vm.$el=vm.__patch__(vm.$el,vnode); //snadddom 中 patch(container,vode)
}else{
vm.$el=vm.__patch__(preVnode,vnode); //snadddom 中 patch(vnode,newVnode)
}
}
function updateComponent(){
// vm._render 即上述的 render 函数,返回 vnode
vm._update(vm._render())
}
vue的整个实现流程 (关键点:render 函数/h函数、Object.defineProperty、updateComponent/patch 函数)
第一步:解析模板成 render 函数
with 的用法
模板中的所有信息都被render 函数包含
- 模板中用到的data 中的属性,都变成了JS 变量
- 模板中的 v-model v-for v-on 都变成了JS 逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70//模板
<div id="app">
<div>
<input v-model="title">
<button v-on:click="add">submit</button>
</div>
<div>
<ul>
<li v-for="item in list">{{item}}</li>
</ul>
</div>
</div>
//render 函数 ,返回 vnode
with(this){ // this 就是 vm
return _c(
'div',
{
attrs:{"id":"app"}
},
[
_c(
'div',
[
_c(
'input',
{
directives:[
{
name:"model",
rawName:"v-model",
value:(title),
expression:"title"
}
],
domProps:{
"value":(title)
},
on:{
"input":function($event){
if($event.target.composing)return;
title=$event.target.value
}
}
}
),
_v(" "),
_c(
'button',
{
on:{
"click":add
}
},
[_v("submit")]
)
]
),
_v(" "),
_c('div',
[
_c(
'ul',
_l((list),function(item){return _c('li',[_v(_s(item))])})
)
]
)
]
)
}第二步:响应式开始监听
- Object.defineProperty
- 将 data 的属性代理到vm上
- 双向数据绑定就是既有 get 又有 set
第三步:首次渲染,显示页面,且绑定依赖
- 初次渲染,执行updateComponent,执行 vm._render()
- 执行 render 函数,会访问到 vm.list vm.title(vm中的属性),并且返回vnode
- 访问 vm 中的属性 会被响应式的 get 方法监听到
- 执行updateComponent,会走到 vdom 的patch方法
- patch 方法将 vnode 渲染成真实的DOM,初次渲染完成 [ patch(container,vnode)的核心函数是createElement ,功能是将vnode转换为真实dom节点 ]
为何要监听get ,直接监听set 不行吗?
- data中有很多属性,有些会被用到,有些可能不会被用到。
- 被用到的会走到get,不被用到的不会走到get。
- 未走到 get 的属性,set的时候我们也无需关心,可以避免不必要的重复渲染。
第四步:data 属性变化,触发 rerender
- 修改属性,会被响应式的set监听到
- set 中执行 updateComponent ,重新执行 vm.render() ——>异步
- 生成的 vnode 和 preVnode,通过 patch 进行对比 [ patch(vnode,newVnode) 核心函数 updateChildren ,功能是两个虚拟dom的对比和更新 ]
- 渲染到html中 (不相同的部分直接替换)
六、React 和 组件化
题目:
说一下对组件化的理解
- 组件的封装:封装视图 ( JSX )、数据( this.state )、变化逻辑( this.setState(…) )
- 组件的复用:props传递、复用
JSX 本质是什么
- JSX 本质上为我们提供了创建React元素方法的语法糖
- React.createElement(‘div’,{ id:’div1’ },child1,child2,child3)
- React.createElement(‘div’,{ id:’div1’ },[…])
- JSX 需要被解析成 JS 才能在浏览器中运行( JSX 要转换成 JS 代码,最终这个代码要生成 vnode) ——> 类比Vue中的模板需要被转换成一个JS函数( render 函数 )
- JSX 是独立的标准,可被其他项目使用(比如Preact)
- JSX 本质上为我们提供了创建React元素方法的语法糖
JSX 和 vdom 的关系
JSX 是React.createElement 方法的语法糖,该方法返回一个记录了某个DOM节点所有信息的对象(vnode)。换言之,通过它我们就可以生成真正的DOM,这个记录信息的对象我们称之为虚拟DOM。
为何需要 vdom
vdom 是 React 初次推广开来的,结合 JSX
JSX 就是模板,最终要渲染成 html
- 初次渲染 + 修改 state 后的 re-render
- 正好符合 vdom 的应用场景:数据和视图分离、数据驱动视图
React.createElement 和 h,都生成 vnode
何时应用 vdom 中的 patch 函数:ReactDOM.render(…) 和 setState
自定义组件的解析:先初始化实例,然后执行实例的render方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<div>
<Input addTitle={this.addTitle.bind(this)}/>
<List data={this.state.list}/>
</div>
/*
React.createElement(
"div",
null,
React.createElement(Input, { addTitle:this.addTitle.bind(this) }),
React.createElement(List, { data: this.state.list })
);
*/
// React.createElement(List, { data: this.state.list })
// var list = new List({ data: this.state.list })
// var vnode = list.render()说一下 setState 的过程
setState 是异步的
- 你无法规定或者限制用户如何使用 setState,可能出现一次执行多次setState,没必要每次都重新渲染,降低性能
- 即便是每次都重新渲染,用户也看不到中间效果,只看到最终结果
setState 的过程
- 每个组件实例,都有 renderComponent 方法。因为每个组件都继承了 Component这个父类,此类中包含 renderComponent 方法。
- 执行 renderComponent 方法时,会重新执行实例的 render 方法
- render 函数返回newVnode ,然后拿到 preVnode
- 最后,执行patch(preVnode,newVnode)
1
2
3
4
5
6
7
8
9
10
11
12//模拟 renderComponent
class Component {
constructor(props) {
}
renderComponent() {
const newVnode = this.render()
const preVnode = this._vnode
patch(prevVnode, newVnode)
this._vnode = newVnode
}
}
阐述自己对 React 和 Vue 的认识
- 两者的本质区别
- Vue—本质是 MVVM 框架,由 MVC 发展而来
- React— 本质是前端组件化框架,由后端组件化发展而来
- 模板和组件化的区别
- Vue 使用模板,React 使用JSX
- React 本身就是组件化,没有组件化就不是React
- Vue 也支持组件化,不过是在 MVVM 上的扩展
- 两者共同点
- 都支持组件化
- 都是数据驱动视图(只关心数据的变化,DOM操作被封装)
- 两者的本质区别
七、hybrid
(三)全面篇
一、一面/二面
1.页面布局
题目:
假设高度已知,请写出三栏布局,其中左栏、右栏宽度各为300px,中间自适应
解决方案
代码详情见 自适应布局
扩展延伸
- 七种方案的优缺点
- 浮动布局
- 局限性:浮动元素是脱离文档流,需要清除浮动,这个处理不好的话,会带来很多问题,比如高度塌陷等
- 优点:比较简单,兼容性也较好
- 绝对定位布局
- 局限性:绝对定位是脱离文档流的,意味着下面的所有子元素也会脱离文档流,这就导致了这种方法的有效性和可使用性是比较差的
- 优点:快捷方便,而且也不容易出问题
- flex 布局
- 局限性:不能兼容IE8及以下浏览器
- 优点:CSS3新出的布局方式,是为了解决上述两种布局方式的不足而出现的,是比较完美的。而且移动端的布局也常用flexbox
- 表格布局
- 局限性:当其中一个单元格高度超出的时候,两侧的单元格也是会跟着一起变高,而这种效果有时候不是我们想要的
- 优点:兼容性很好,在flex布局不兼容时,可以尝试表格布局
- grid 布局
- 局限性:只支持高版本的浏览器;grid某些属性不易懂
- 优点:真正的 css 框架,和 flex 相比,它是二维的,而 flex 是一维的;做大布局使用,grid-gap 属性非常好用
- 圣杯布局 VS 双飞翼布局
- 布局要求(相同点):
- 三列布局,中间宽度自适应,两边定宽
- 中间栏要在浏览器中优先展示渲染
- 允许任意列的高度最高
- 不同点:
- 圣杯布局:借助的是非主要元素(left、right)覆盖了其父元素(container)的padding值所占据的宽度。也就是,同一个杯子,非主要元素只是占据了全部容器的padding值部分
- 双飞翼布局:给中间部分(center)添加一个外层元素(container),非主要元素(left、right)所占据的空间是主要部分(center)的margin空间。也就是,像鸟的两个翅膀,与主要部分脱离
- 优缺点:
- 缺点:最好设置页面最小宽度,要不然当页面缩放到一定程度时,页面布局会被破坏,影响浏览
- 优点:允许任意列的高度最高,比表格布局灵活性高
- 布局要求(相同点):
- 浮动布局
- 当中间部分内容超出已知高度时,哪些方案可以正常显示
- grid布局、flex布局、table布局、绝对定位布局、圣杯布局、双飞翼布局
- 前三者是三个部分的高度一起增加,flex布局和grid布局可以分别通过设置align-items为flex-start、start解决;后三者是只有中间部分高度变高,两侧高度不受影响
- 七种方案的优缺点
页面布局的变通
- 三栏布局
- 左右宽度固定,中间自适应
- 上下高度固定,中间自适应
- 两栏布局
- 左宽度固定,右自适应
- 右宽度固定,左自适应
- 上高度固定,下自适应
- 下高度固定,上自适应
- 三栏布局
小结
- 语义化掌握到位
- 页面布局理解深刻
- CSS基础知识扎实
- 思维灵活且积极上进
- 代码书写规范
2.CSS盒模型
题目:
谈谈你对CSS盒模型的认识?
- 概念和分类
- CSS盒模型就是一个盒子,封装周围的HTML元素,它包括内容content、边框border、内边距 padding、外边距margin
- CSS盒模型分为标准模型和IE模型
标准模型和IE模型的区别
- 标准模型:width=内容content的宽度(默认) ; 设置方式为 box-sizing:content-box
- IE模型:width=内容 content+内边距 padding+边框 border 的宽度 ;设置方式 为box-sizing:border-box
通过js如何获取盒模型的宽高
- dom.style.width/height 只能获取到dom的内联样式
- dom.currentStyle.width/height 获取到的是dom实际宽高,但这种方式只在IE中可以使用
- window.getComputedStyle(dom,null).width/height 获取到的是dom实际宽高,但是不支持IE
- dom.offsetWidth/offsetHeight 最常用的,兼容性最好
- dom.getBoundingClientRect().width/height 计算dom的绝对位置,根据视窗左上角对应位置的绝对位置,可以拿到四个值:left、top、width、height
实例题(根据盒模型解释边距重叠)
- 边距重叠:指两个或多个盒子相邻边界重合在一起形成一个边界。水平方向边界不会重叠,垂直方向会重叠,垂直方向的实际边界是边界中的最大值。
1
2
3
4
5
6
7
8
9
10
11
12
13<section class="box" id="sec">
<style media="screen">
#sec{
background: #f00;
}
.child{
height: 100px;
margin-top: 10px;
background: yellow
}
</style>
<article class="child"></article>
</section>BFC(边距重叠解决方案)
BFC的基本概念
Block Formatting context,块级格式化上下文
BFC的原理(BFC的渲染原则)
- 属于同一个BFC的两个相邻box的margin会在垂直方向上发生重叠
- BFC区域不会与float box重叠 ——>自适应两栏布局
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素
- 计算BFC的高度时,浮动元素也参与计算 ——>清除内部浮动,防止高度塌陷
补充:
BFC内部的元素和外部的元素绝对不会互相影响,因此,当BFC外部存在浮动时,它不应该影响BFC内部Box的布局,BFC会通过变窄,而不与浮动有重叠。同样的,当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动的高度
如何创建BFC
- 脱离文档流:float不为none;position为absolute或fixed
- overflow不为visible (hidden,auto,scroll)
- display为”table-cell”,”table-caption”,”inline-block”,”flex”等
BFC的使用场景
- 自适应两栏布局
- 清除内部浮动
- 防止垂直margin重叠
3.DOM事件
4.HTTP协议类
- HTTP协议的主要特点
- 简单快速
- 灵活
- 无连接
- 无状态
- HTTP报文的组成部分
- HTTP方法
- GET 获取资源
- POST 传输资源
- PUT 更新资源
- DELETE 删除资源
- HEAD 获得报文首部
- POST和GET的区别
- HTTP状态码
- 什么是持久连接
- 什么是管线化
5.原型链
6.面向对象
7.通信类
- 什么是同源策略及限制
- 前后端如何通信
- 如何创建Ajax
- 跨域通信的几种方式
8.安全类
- CSRF
- XSS
9.算法类
排序
蛮力法:选择排序和冒泡排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//选择排序
function selectionSort(arr){
let len=arr.length,min,temp;
for(let i=0;i<len-1;i++){
min=i; //假定无序区的第一个元素为最小值
for(let j=i+1;j<len;j++){
if(arr[j]<arr[min]){
min=j;
}
}
temp=arr[i];
arr[i]=arr[min];
arr[min]=temp;
}
return arr;
}减治法:插入排序和拓扑排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33//插入排序(类似于扑克牌的插入)
function insertSort(arr){
let len=arr.length,temp,j;
for(let i=1;i<len;i++){
temp=arr[i];//待插入变量
j=i-1;//循环有序区的变量
while(j>=0 && arr[j]>temp){
arr[j+1]=arr[j];//比插入元素大的元素后移
j=j-1;
}
arr[j+1]=temp;
}
return arr;
}
//希尔排序,也称递减增量排序,是插入排序的一种更高效的改进版本。本质就是对每个子序列进行插入排序。
function shellSort(arr){
let len=arr.length;
let gap=len;
let temp,j;
while(gap>1){
gap=Math.floor(gap/3)+1;
for(let i=gap;i<len;i++){//假设每个子序列的第一个元素已排好序,从第二个元素开始插入
temp=arr[i];//待插入元素
j=i-gap;//循环有序区的变量
while(j>=0 && arr[j]>temp){
arr[j+gap]=arr[j];//子序列的元素后移一位
j=j-gap;//判断子序列的下一个元素
}
arr[j+gap]=temp;//插入到空位置
}
}
return arr;
}分治法:合并排序和快速排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//快速排序,递归实现
function quickSort(arr){
if(arr.length<=1){
return arr;
}
let pivotIndex=Math.floor(arr.length/2);//基准下标
let pivot=arr.splice(pivotIndex,1)[0];//基准元素,splice 会改变原始数组,此时数组长度减一
let left=[],right=[];
for(let i=0;i<arr.length;i++){
if(arr[i]<pivot){
left.push(arr[i]);
}else{
right.push(arr[i]);
}
}
return quickSort(left).concat([pivot],quickSort(right));
}
堆栈、队列、链表
递归
波兰式和逆波兰式