JavaScript知识点梳理

459 阅读1小时+

JavaScript 知识梳理

第一周

一、概述:JavaScript

1). 简称js,是一个运行在客户端浏览器端的解释型、弱类型、面向对象脚本语言

  • js的运行环境:
    a. 浏览器自带js解释器,不需要安装任何环境
    b. 等到我们学习Node.js时候,确实需要自己安装一个服务器端的环境

  • 编译型:在运行程序之前,需要先检查语法是否正确,如果不正确,直接不运行 - 严格,比如:Java、C++...

  • 解释型:在运行程序之前,不需要检查语法是否正确,直接运行,碰到错误就会停止后续代码 - 更加自由,比如:JavaScript、Node.js、PHP...

  • 弱类型:变量保存的数据可以是随意的,数据类型由数据来决定 - 更自由

  • 强类型:变量保存的数据,由数据的类型来决定了能保存什么数据 - 比如Java - 更严格

  • 面向对象

二、如何使用JS
  • 使用方式:2种
    1、直接在HTML写上一个script标签,在里面书写JS代码
<script>JS代码</script>

2、创建一个xx.js文件,在其中写入JS代码,最后在HTML引入 - 正式开发时使用的方式

<script src="js/xx.js">
此处不可以书写JS代码
</script>
三、***输出方式/打桩输出/检查错误:3种 - 给程序员使用的
  • *在控制台打印输入日志:console.log(输出的内容)

  • 在页面上输出日志:document.write(输出的内容);//document此HTML文档 write写入 - 支持标签,但是有一个缺点但是如果绑定了点击事件,会将页面上所有的内容全部替换掉,那你的网页就白写了

  • 在浏览器自带的警告框输出日志:alert(你想要输出的东西);//有时候会卡住整个浏览器,导致用户只能看到一个白板

四、变量和常量:

以后我们回答问题,最好都要向着三方面回答:www(what、when、why)+ h(how)

  1. *变量:创建后,值可以再次修改
  2. 如何使用:var 变量名=值;
  3. 不能以数字开头
  4. 建议使用驼峰命名法或下划线命名法
  5. 命名要尽量的见名知意:name,age,gender,hobby,height,weight
  6. name这个变量名是一个关键字
  7. 变量名不能是关键字
  8. 变量可以只创建,不赋值,默认值为undefined
  9. 多个变量连续创建,以逗号隔开
五、数据类型有哪些

1.原始/基本/值类型:5个

  • *Number - 数字,取值有无数个,而且数字直接写,不用加任何东西(控制台输出的颜色是蓝色)
  • *String - 字符串,取值有无数个,但是必须加上" "(控制台输出的颜色是黑色)
  • *Boolean - 布尔,取值只有2个,分别叫做true(真|对) 或 false(假|错) - 一般用于当作条件判断
  • Null - 空,取值只有一个,就是null,唯一的作用就是用于释放变量释放内存的,节约内存空间
  • Undefined - 翻译未定义,取值只有一个,就是undefined,变量默认值为undefined
拓展:查看数据类型:typeof(变量)
六、运算符:
一、 *算术运算符:+ - * / %
  • %:取余,俗称模,两个数相除,不取商,而是取除不尽的余数
  • *任意数%2 - 判断奇偶数
  • 取出某个数字的后n位
console.log(1234%10);//4

console.log(1234%100);//34

console.log(1234%1000);//234

2. *带有隐式转换

true->1
false->0
undefined->NaN
null->0
"100"->100
"100px"->NaN

3. NaN:Not A Number:不是一个数字,但是他确实是数字类型-

  • 参与任何算术运算结果仍为NaN
  • 参与任何比较运算结果都为false
  1. +运算符:如果碰上一个字符串,左右两边都会的转为字符串,+运算不再是+运算,而是拼接操作
二、*关系/比较运算符:>,<,>=,<=,==,!=,===,!==
  1. 结果:一定是一个布尔值
  2. 带有隐式转换:默认左右两边都会转为数字在比较大小
  3. 如果参与比较的两边【都是】一个字符串!则会按位PK每个字符的十六进制unicode号(十进制ASCII码)
    数字0-9<大写A-Z<小写a-z<汉字
    NaN参与任何比较运算结果都为false
    !isNaN(x); 专门为了判断NaN而存在的,结果一定是一个布尔值: true->是一个有效数字 false->是一个NaN
三、*逻辑运算符:

&&:与(并且)
全部条件都满足,结果才为true 只要一个不满足,结果就为false

||:或者
全部条件都不满足,结果才为false 只要一个满足,结果就为true

!:颠倒布尔值
!true->false
!false->true

四、赋值运算符:=,+=,-=,=,/=,%=
  1. =:赋值符号,将=右边的东西,保存到=的左边的变量名中
  2. i=i+1 ==> i+=1
五、*自增自减运算符:

++,--
i++ === i+=1 === i=i+1
固定的每次只能+1
笔试题: 前++ 和 后++的区别?
1、如果单独使用,前++和后++,没有任何区别!
2、但是如果参与了其他表达式,变量始终都会+1,但是前++和后++【返回的结果】不同
前++,返回的是加了之后的新值
后++,返回的是加了之前的旧值

var i=2 
sum=i++ + ++i + ++i + i++ + ++i + i
console.log(i)
console.log(sum)
六、位运算:

左移:m<<n,读作m左移了n位,翻译:m*2的n次方
右移:m>>n,读作m右移了n位,翻译:m/2的n次方

程序的流程控制语句:3种

1、顺序结构 - 默认:从上向下依次执行每一句话
一、if....else
1、一个条件,一件事,满足就做,不满足就不做
if(条件){
操作;
}
一个条件,两件事,满足就做第一件,不满足就做第二件
				if(条件){
					操作;
				}else{
					默认操作;
				}

多个条件,多件事,满足谁就做谁
				if(条件1){
					操作1;
				}else if(条件2){
					操作2;
				}else{
					默认操作;
				}

注意: 1、分支只要满足了一条路,就不会再走别的路 2、else if这句话想写多少,由程序员自己决定 3、else这句话可以省略不写,如果条件都不满足,则什么都不执行 4、分支可以嵌套

二、switch...case分支:
语法:
				switch(变量/表达式){
					case1:
					操作1;
					break;
					case2:
					操作2;
					break;
					default:
					默认操作;
				}

特殊:
1.问题:默认只要一个case满足后,会将后续所有的操作全部做完
解决:关键字break;
建议:每一个case的操作后面都可以跟上一个break;
有的地方也可以不加break:

  • 最后一个操作default可以省略break
  • 如果中间多个条件,做的操作是一样的,可以省略掉中间部分!
  • case在做比较的时候是不带隐式转换的
  • default可以省略不写,条件都不满足,则什么都不会执行

面试题:if vs switch的区别?谁好谁坏?

1、switch...case:优点:执行效率高,*速度比较快(他比较的时候,case做的不是范围查找而是等值比较)
缺点:必须要直到最后的结果是什么才可以使用switch case去判断!

2、if...else:优点:可以做范围判断
缺点:执行效率较慢,速度慢(做的范围判断)

建议:代码开发完毕过后,要做代码优化,要尽量的少用if...else...,尽量的将if...else替换为switch...case...

三、三目运算:纯粹就是为了简化【简单的】分支结构
语法:
扩展:短路逻辑:条件&&(操作);	===	if...

条件?操1:默认操作;		===	if...else...

条件1?操作1:条件2?操作2:默认操作;	===	if...else if...else

注意: 1、默认操作不能省略,省略了会报错 - 其实还算是一个优点 2、如果操作比较复杂,不能使用三目运算:【操作只能有一句话】
页面上一切的数据js获取到后都是字符串类型!

2、*强制(显示)数据类型转换:
1、转字符串:2种

1、var str=x.toString();//x不能是undefined和null,会报错,undefined和null不能使用.做任何操作!
2、var str=String(x);//万能的,任何人都可以转为字符串,完全等效于隐式转换,其实String()就是隐式转换的底层原理,还不如+""

2、***转数字:

1、*parseInt(str/num) - parse解析 Int整型 - 专门用于将【字符串转为整数】的 执行原理:从左向右依次读取每个字符,碰到非数字字符就停止转换,如果一来就不认识则为NaN,不认识小数点

console.log(parseInt(35.5));//35
console.log(parseInt("35.5"));//35
console.log(parseInt("3hello5"));//3
console.log(parseInt("hello35"));//NaN
console.log(parseInt("35px"));//35
console.log(parseInt(true));//NaN
console.log(parseInt(false));//NaN
console.log(parseInt(undefined));//NaN
console.log(parseInt(null));//NaN

2、*parseFloat(str) - parse解析 Float浮点数(小数) - 专门用于将【字符串转为小数】的

执行原理:几乎和parseInt一致,认识第一个小数点
console.log(parseFloat(35.5));//35.5
console.log(parseFloat("35.5"));//35.5
console.log(parseFloat("3hello5.5"));//3
console.log(parseFloat("hello5.5"));//NaN
console.log(parseFloat("35.5px"));//35.5
console.log(parseFloat(".5px"));//0.5
console.log(parseFloat("35.5.5"));//35.5

3、var num=Number(x);//万能的,任何人都可以转为数字,完全等效于隐式转换,其实Number()就是隐式转换的底层原理,还不如-0 *1 /1

3、转布尔:

Boolean(x);//万能的,任何人都可以转为布尔,完全等效于隐式转换,其实Boolean()就是隐式转换的底层原理,还不如 !!x
***只有6个会为false:0,"",undefined,null,NaN,false - 其余的全部都是true

1、*****循环结构

循环结构:反复执行 相同 或 相似的操作

循环三要素:

  1. 循环条件:开始 - 结束,循环的次数
  2. 循环体:你要做的操作
  3. 循环变量:记录着我们当前在哪一次,而且他会不断的变化,变化往往都是向着不满足循环条件前进的
一、while循环:

语法: var 循环变量=几; while(循环条件){ 循环体; 循环变量变化; }

 //创建变量i
 var i=1
 //判断i是否小于等于1000,如果为true则执行一次循环体,
 i+1再次判断如果为false则退出循环
        while(i<=1000){
            console.log(i+"hello world")
            i++
        }

特殊: 1、有的时候可能真的需要使用死循环:默认永远不会停止的循环
何时使用:【不确定循环次数的时候】
while(1){操作}

break - 退出整个循环 - 多半都是用于配合死循环使用的
continue - 退出本次循环 - 还会执行下一次操作

二、*for循环:
语法:
			for(var 循环变量=几;循环条件;循环变量变化){
				循环体;
			}

特殊: 死循环:for(;;){操作}

面试题:whilefor 的区别?
whilefor在原理上来说几乎没有区别
一般不确定循环次数的时候会使用while循环 - 死循环
一般确定循环次数的时候推荐for循环 

三、do...while循环:
语法:
			var 循环变量=几;
			do{
				循环体;
				循环变量变化;
			}while(循环条件)

面试题:while 和 do...while 的区别?

区别只看第一次,如果第一次条件都满足,那么两者没有区别
如果第一次条件都不满足,while一次都不会执行,而do...while至少会执行一次

2、函数
1、概念:Function - 函数,也称之为方法,先【预定义】好,以后可以【反复使用】的【代码段】
1、定义/创建/声明:
			function 函数名(){
				函数体/代码段;
			}

		2、调用函数:2种
			1、在JS内部写:函数名() - 程序员写几次就会调用几次

			2、在HTML页面上绑定事件:
                        <elem onclick="函数名()"></elem> - 任何元素都可以绑定点击事件

1、创建出带有形参的函数:其实就是一个变量,只不过不需要写var,而且不需要赋值,所以称之为叫做形式参数,简称形参

function 函数名(形参,...){
函数体/代码段;
}

2、使用带参数的函数时,记得要必须传入实参 - 实际参数
函数名(实参,...)
注意:传参的时候顺序是不能乱,必须和形参的顺序一一对应上

总结:没有谁好谁坏这种说法
1、不带参数的函数,用于执行一些固定操作
2、带参数的函数,可以根据我们传入的实参的不同,执行的操作略微不同

总结:循环也可以反复执行,函数也可以反复执行,他们的区别在哪里?时机不同

  • 循环:几乎是一瞬间就执行完毕了
  • 函数:需要调用后才会执行

第二周

一、自定义函数:function
1、函数的创建
  • 声明方式 function 函数名(形参){函数体}
  • 直接量方式 var 函数名=function(形参1,形参2,....){函数体}

函数名就是一个变量,函数名尽量不重复,否则后面会覆盖前面的

2、函数的调用:
  • 函数名(实参1,实参2,.....)
3、作用域
  1. 全局作用域:全局变量 和 全局函数,在页面的任何一个位置都可以使用
  2. 函数作用域:局部变量 和 局部函数,只能在函数调用时,内部可用
    带来了变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错
    缺点: 1. 局部可以使用全局的,但是全局不能使用局部的,解决:加return
    2、千万不要在函数中对着未声明的变量赋值 - 全局污染:全局本身没有这个东西,但是函数作用域却给添加上了,降低网页的性能!
function 函数名(形参列表){
	函数体;
	return 返回值;
	}
调用:var result=函数名(实参列表);

其实return的本意其实是退出函数,但是如果return后面跟着一个数据 顺便将数据返回到函数作用域的外部,但是return只负责返回不负责保存,所以调用函数时,需要拿一个变量去接住返回值

四、声明提前 - 只会出现在笔试

原理:
程序正式执行以前,会将var声明的变量(轻)和function声明的函数(重),都会提前到当前作用域的顶部,但是赋值留在原地
强调:
声明方式创建的函数会完整的提前(第一种)
直接量方式创建的函数不会完整提前,只有变量名部分会提前(第二种)

五、重载:

相同的函数名,根据传入的实参的不同,自动选择对应的函数去执行,但是JS不支持,如果函数名重复了,后面的肯定会覆盖前面的
目的:减轻程序员的压力,记住一个方法就可以执行很多的操作 解决:在【函数内部】自带一个arguments的对象(类数组对象),不需要去创建
作用:哪怕没有写任何形参,它也可以接受住所有的实参
固定套路: 1、通过下标去获取传入的某一个实参:arguments[i] - i下标是从0开始的!
2、通过length去获取到到底传入了几个实参:arguments.length;
所以我们可以通过判断传入的实参的不同,执行不同的操作,变相实现重载操作

六、数组的基础

数组:创建一个变量保存【多个数据】
数组都是线性排列的,除了第一个元素,每个元素都有唯一的前驱元素 除了最后一个元素,每个元素都有唯一的后继元素
每个元素都有一个自己的位置,称之为叫做下标,下标都是从0开始的,到最大长度-1结束

  1. 创建数组
1、*直接量方式:var arr=[];//空数组
var arr=[数据1,...];

2、构造函数方式:var arr=new Array();//空数组
var arr=new Array(数据1,...);

2. *获取数组之中的元素:

数组名[i]

3. *后续添加/替换元素:

数组名[i]=新数据;		
如果下标处没数据则为添加,如果下标处有数据则为替换

  1. *数组具有三大不限制:
  • 不限制元素的类型
  • 不限制元素的长度
  • 不限制下标越界
    如果获取元素时,下标越界,返回的是一个undefined
    如果添加元素时,下标越界,会得到一个稀疏数组,如果搭配上学过的循环去遍历获取每个元素,那么会得到很多很多的undefined
一、DOM

Document Object Model:文档对象模型,专门用于操作HTML文档的,提供了一些方法给你。

二、DOM树概念

DOM将我们的HTML看作了是一个倒挂的树状结构,但是树根不是HTML标签,而是document对象
document对象:不需要程序员创建,由浏览器的js解释器自动创建,一个页面只有一个document树根
DOM会将页面上的每个元素、属性、文本、注释等等都会被视为一个DOM元素/节点/对象

三、查找元素:两大方面
  1. 直接通过 HTML 的特点去查找元素
通过 ID 去查找元素:
var elem=document.getElementById("id值");

特殊:
1、返回值,找到了返回当前找到的DOM元素,没找到返回一个null
2、如果出现了多个相同id,只会找到第一个

通过 标签名 去查找元素:
var elems=documet/已经找到的某个父元素.getElementsByTagName("标签名");

特殊:
1、返回值,找到了返回的是一个类数组DOM集合,没找到返回空集合
2、*js只能直接操作DOM元素,不能直接操作DOM集合!解决:要么下标拿到某一个元素,要么遍历拿到每一个元素
3、不一定非要从document开始查找,如果从document去找,会找到所有的元素,可以换成我们已经找到的某个父元素

通过 class名 去查找元素:
var elems=document/已经找到的某个父元素.getElementsByClassName("class名");

特殊:
1、返回值,找到了返回的是一个类数组DOM集合,没找到返回空集合
2、*js只能直接操作DOM元素,不能直接操作DOM集合!解决:要么下标拿到某一个,要么遍历拿到每一个

  1. 通过 元素之间的关系 去查找元素:
    前提:至少要【先找到一个元素】才可以使用关系网:
1、父亲:elem.parentNode;//单个元素
2、儿子:elem.children;//集合
3、第一个儿子:elem.firstElementChild;//单个元素
4、最后一个儿子:elem.lastElementChild;//单个元素
5、前一个兄弟:elem.previousElementSibling;//单个元素
6、后一个兄弟:elem.nextElementSibling;//单个元素
四、操作元素

<标签名 属性="" style="样式">内容</标签名>

  1. 内容:
1、elem.innerHTML - 获取或设置开始标签到结束标签之间的内容,【支持识别标签的】
获取:elem.innerHTML;
设置:elem.innerHTML="新内容";
2、elem.innerText - 获取或设置开始标签到结束标签之间的纯文本,不支持识别标签的
获取:elem.innerText;
设置:elem.innerText="新内容";
3、input.value; - 专门获取/设置input的内容
获取:input.value;
设置:input.value="新内容";

2. 属性

1、*获取属性值:elem.getAttribute("属性名");
2、*设置属性值:elem.setAttribute("属性名","属性值");

简化版:

1、*获取属性值:elem.属性名;
2、*设置属性值:elem.属性名="属性值";
简化版有两个缺陷:
class必须写成className - ES2015(ES6)class变成了一个关键字
只能操作标准属性,不能操作自定义属性

3. 样式:

获取样式:elem.style.css属性名;
设置样式:elem.style.css属性名="css属性值";

特殊点:

1、css属性名,有横线的地方,要去掉横线,变为小驼峰命名法
background-color--->	backgroundColor
border-radius--->	borderRadius

强调:
一切的获取,都是为了做判断! 一切的设置,说白了就是在修改!

  1. 绑定事件:
elem.on事件名=function(){
	操作;
	***关键字this - 这个,只能在【事件】内使用
	如果单个元素绑定事件,this->这个元素
	如果多个元素绑定事件,this->当前触发事件的元素!!!
		}

一、数组的基础
  1. 创建数组
1、*直接量方式:var arr=[];//空数组
var arr=[数据1,...];

2、构造函数方式:var arr=new Array();//空数组
var arr=new Array(数据1,...);

第二个方法有一个坑:new Array(num);//创建了一个长度为num的空数组,里面没有任何东西,只有无数的undefined

  1. 面试题:按值传递:可能会这么出题:var a=x; var b=a;修改a,b变不变,或者,修改b,a变不变?
    如果传递的是原始类型:
    其实是复制了一个副本给对方,两者互不影响
    如果传递的是引用类型:
    js中不是原始类型,就是引用类型(数组、函数...都是引用类型) - 【浅拷贝】

因为引用类型很大,比原始类型大得多,不可能保存在变量本地,只是保存了一个地址值而已,其实是把自己的地址值赋值给了对方,而两者使用的是同一个地址值,一个人修改,另一个也会跟着变化
引用类型,在比较时,其实不是看的值,而是看的地址值作比较

3.***面试题:如何释放一个引用类型呢?
一定要看清楚有几个变量引用着这个引用类型,每个变量都要释放后才能释放干净

二、hash(关联)数组

下标是可以自定义的

  1. 创建:2步
1、创建空数组:var arr=[];
2、为数组添加自定义下标并且赋值:arr["自定义下标"]=新值

2. 访问:

arr["自定义下标"]

3. 强调:hash数组的length会失效,永远为0!
遍历hash,不能再使用for循环,必须使用for in循环

语法:
for(var i in 数组名){
//i -> 下标
//数组名[i] -> 当前次元素
}

4. hash数组的原理:
hash算法:将字符串,计算出一个尽量不重复的数字(地址值)

添加元素:js解释器会将自定义下标交给hash算法,
得到一个数字(地址值),直接将你要保存的数据放到此地址之中保存起来

获取元素:js解释器会将指定的下标再次交给hash算法,得到一个和当初保存时
完全一样的数字(地址值),通过此地址值就可以找到你当初保存的数据,取出来使用

js里面一切的东西都是对象,万物皆对象,除了undefined和null,【一切对象的底层都是hash数组】

三、数组的API
  1. arr 转 str:
    不会修改原数组
var str=arr.join("自定义连接符");
1、鄙视题:将数组里面得内容拼接为一句话/单词 - 无缝拼接,其实就是拼接了一个空字符串
var arr=["h","e","l","l","o"," ","w","o","r","l","d"];
	console.log(arr.join(""))
2、***将数组拼接为DOM页面元素 - 第一次遇到数据渲染页面,这仅仅只是数据渲染的基础
var arr=["-请选择-","北京","南京","西京","东京","重庆"];
var str="<开始>"+arr.join("</结束><开始>")+"</结束>";
sel.innerHTML=str;

2. 拼接数组:添加元素的新方式
不修改原数组

将你传入的实参全部拼接为arr的末尾
var newArr=arr.concat(新值1,arr1,...);

1、不修改原数组,只会返回一个新数组
2、concat支持传入数组参数,悄悄的将你传入的数组打散为单个元素再拼接

  1. 截取子数组:只想取出数组中的某一个部分
    不修改原数组
根据你传入的开始下标一直截取到结束下标
var subArr=arr.slice(starti,endi+1);
特殊:
1、不修改原数组,只会返回一个新数组
2、含头不含尾
3、endi可以省略不写,如果省略不写,则从starti位置一直截取到末尾
4、starti和endi都可以省略不写,那么从头到尾完整的复制一份,此操作也叫做深拷贝!复制了一个副本给对方,两者互不影响。
5、支持负数参数,-1代表倒数第1个

4. 删插替

删除:var dels=arr.splice(starti,n);//n代表删除的个数

虽然他直接修改原数组,但是也有返回值,返回的是被删除的数据组成的一个新数组

插入:var dels=arr.splice(starti,0,新值1,...);

1、原starti位置的元素以及后续元素都会向后移动
2、尽量的不要插入一个数组,会导致我们的数组一些是一维,一些是二维,遍历的时候极不方便

替换:var dels=arr.splice(starti,n,新值1,...);
特殊:删除的个数和插入的个数不必相同

5. 翻转数组

arr.reverse();
一、数组排序
1. 冒泡排序
var arr=[12,3,25,4,7,687,6,354,534,8,62,33,4];
for(var j=1;j<arr.length;j++){
   for(var i=0;i<arr.length-j;i++){
    if(arr[i]>arr[i+1]){
	var m=arr[i];
	arr[i]=arr[i+1];
	arr[i+1]=m;
	}
    }
}
console.log(arr);

2. 数组的API排序(sort)

默认:将数组中的元素转为字符串后,再按位PK每个字符的unicode号(ascii码)!

  1. 按照数字升序排列
arr.sort(function(a,b){
    //a为后一个,b为前一个
    return a-b;
	})
2:按照数字降序排列:
arr.sort(function(a,b){
    return b-a;
})

强调: 1、以后只要网页上有排序功能,说明他的底层一定是一个数组,因为js之中只有数组可以排序

二、栈和队列

添加元素和删除元素的新方式

1. 栈

其实就是数组,只不过一端封闭了,只能从另一端进出
开头进:

arr.unshift(新值,...);
//缺点:添加元素的新方式,
//向前添加:缺点:导致其他元素的下标都会发生变化

开头出:

var first=arr.shift();
//删除元素的新方式缺点,向前删除,
//一次只会删除一个,也有返回值,
//返回的就是被删除的元素:导致其他元素的下标都会发生变化

结尾进:

arr.push(新值,...);//添加元素的新方式,向后添加

结尾出:

var last=arr.pop()
//删除元素的新方式缺点,向后删除,
//一次只会删除一个,也有返回值,返回的就是被删除的元素
2.队列

其实就是数组,只不过一端进,从另一端出

开头进:arr.unshift(新值,...);
结尾出:var last=arr.pop();
结尾进:arr.push(新值,...);
开头出:var first=arr.shift();
三、ES5提供了3组6个新的API
1.判断:2个

判断的结果肯定都是一个布尔值
every:
每一个,要求所有的元素都满足条件才为true,只要有一个不满足则为false,非常类似于&&

var bool=arr.every(function(val,i,arr){
//val - 当前的值
//i - 当前的值的下标
//arr - 当前数组本身
return 判断条件;
})

some:
有一些,要求只要有一个元素满足结果就为true,全部条件不满足结果才为false,非常类似于||

var bool=arr.some(function(val,i,arr){
	return 判断条件;
	})
2.遍历

拿到数组中的每个元素做相同 或 相似的操作

forEach - 直接修改原数组
arr.forEach(function(val,i,arr){
	操作;//arr[i]*=2
})
map - 不会修改原数组,返回一个新数组
var newArr=arr.map(function(val,i,arr){
	return 操作;//val*2
	})
3.过滤和汇总

过滤:
筛选出需要的部分,原数组是不会发生变化。

var subArr=arr.filter(function(val,i,arr){
   return 判断条件;//val%2==0筛选偶数元素
        })

汇总:

var result=arr.reduce(function(prev,val,i,arr){
	return prev+val;
	})

ES6:箭头函数,专门简化一切的匿名回调函数
公式:function去掉,()和{}之间添加=>,如果形参只有一个,那么()省略,如果函数体只有一句话,那么{}省略,如果函数体只有一句话并且是return,那么{}和return都省略

四、二维数组

数组的元素,又引用着另一个数组

创建:
var h52302=[["张三",18,3500],
            ["李四",19,4500],
	    ["王五",20,2500]
		];
访问:数组名[行下标][列下标];

列下标越界:返回undefined
行下表越界:得到的是一个报错,因为行下标越界已经得到undefined了,undefined没有资格再加[]做操作

遍历二维数组:必然两层循环,外层循环控制行,内层循环控制列
for(var r=0;r<arr.length;r++){
     for(var c=0;c<arr[r].length;c++){
	console.log(arr[r][c])
		}
	}
五、、*****String的概念:

什么是字符串:多个字符组成的【只读】字符【数组】

和数组有相同点:
1、字符串中的个数:str.length
2、获取字符串中的某个字符:str[i]
3、遍历字符串


***引用/对象类型:11个
*String Number Boolean -> 包装类型

*Array *Function Date(日期) Math(数学) *RegExp(正则:验证)
Error(错误)
Object(面向对象) Global(全局对象)
- 只不过在浏览器端被window对象给代替了,自然保存着全局变量和全局函数,只不过window可以省略不写,而在node.js中全局对象就叫做global

***包装类型:专门用于将原始类型的值封装为一个引用类型的对象的 为什么:原始类型的值原本是没有任何属性和方法,意味着原始类型本身是不支持.去做任何操作的

一、字符串的概念

什么是字符串:多个字符组成的【只读】字符【数组
和数组有相同点:

  1. 字符串中的个数:str.length
  2. 获取字符串中的某个字符:str[i]
  3. 可以遍历字符串
二、String的API

只有字符串可以使用的函数,都不修改原字符串

1、转义字符:\

作用:

  1. 将字符串中和程序冲突的字符转为原文
  2. 包含特殊功能的符号:
换行:\n
制表符:\t	//	大空格,跟tab键效果是一样

3. 输出unicode编码的字符:

汉字的第一个字:\u4e00 - ascii码:19968
汉字的最后一个字:\u9fa5 - ascii码:40869
2、大小写转换

将字符串中的每个英文字符统一的转为大写 或 小写
只要程序不区分大小写,就要【先统一】的转为大写 或 小写,再比较(做验证码)

使用:
var 大写=str.toUpperCase();
var 小写=str.toLowerCase();
3、获取字符串中的指定位置的字符
str.charAt(i)	===>	不如str[i];
4、获取字符串中的指定位置的字符的ascii码
var ascii=str.charCodeAt(i);//转换字符串某个位置的ASCII码

*通过ascii码转回原文:
var 字=String.fromCharCode(ascii码);
5、检索字符串

检查索引 - 检查下标,获取关键字的下标

var i=str.indexOf("关键字",starti);

从starti位置开始,查找右侧第一个关键字的第一个字符的位置 starti可以省略,默认从0位置开始查找的
返回值:找到了,返回的第一个关键字的第一个字符的下标位置 *没找到,返回-1,其实我们根本不关心下标为多少,我们只关心下标为不为-1 默认只能获取第一个关键字的下标,如何才能获取到所有的关键字的下标呢?

var str="no zuo no die no can no bibi";
var index=-1;
while((index=str.indexOf("no",index+1))!=-1){
console.log("找到了关键字,下标为:"+index);
}
6、拼接字符串
var newStr=str.concat(新字符串,...)	还不如+运算
7、截取字符串:3个
var subStr=str/arr.slice(starti,endi+1);//用法和数组完全一致

//用法和slice几乎一致,但是不支持负数参数
var subStr=str.substring(starti,endi+1);

//n代表的是截取的个数,不必考虑含头不含尾
var subStr=str.substr(starti,n);
8、替换字符串
var newStr=str.replace("固定关键字"/RegExp,"新内容");
9、切割/分割/分隔字符串

作用:将字符串切割为数组:str<==>arr

var arr=str.split("自定义切割符")

特殊:
1、切割后,切割符就不在了
2、如果你的切割符写的是"",切散每一个字符

10、去掉空白字符
str.trim()/str.trimStart()/str.trimEnd() 

第三周

Math对象

属性:Math有一些属性,涉及到科学计数法,但是几乎用不到,Math.PI

API:

1、上取整:超过一点点,就取下一个整数

var num=Math.ceil(num);//小数点位数不能超过15位,否则此方法会失效,只能取整

2、下取整:无论超过多少,都会省略掉小数部分

var num=Math.floor(num);

3、四舍五入取整:

var num=Math.round(num);//只看第一位小数

以上三个+*parseInt(str)去掉单位+*num.toFixed(d);

*num.toFixed(d):优点:
1、传入自定义保留小数位数,并且也带有四舍五入的操作
2、解决浏览器带来的舍入误差:比如:2-1.6 === 0.399999999999 
缺点:结果是一个字符串,建议搭配上parseFloat使用

不允许使用toFixed的情况下,自己封装一个函数,由用户传入数字以及保留的小数位数,进行四舍五入,返回一个数字

function toFixed(num,d){
        num*=(10**d);
	num=Math.round(num);
	num/=(10**d);
	return num;
		}
var result=toFixed(Math.PI,4);
console.log(result)
2、乘方和开方
*乘方:Math.pow(底数,幂); -> 更简化的方法:底数**幂
开方:Math.sqrt(num); -> 仅仅只能开平方
3、*最大值和最小值:
var max/min=Math.max/min(a,b,c,d,e,f,g,....);//自动在传入的数字中找到最大值或最小值
问题:本身不支持数组参数
解决:固定用法:Math.max/min.apply(Math,arr);//apply具有打散数组的功能
4、绝对值:
Math.abs(-1);//1
5、***随机数:Math.random():在0~1之间取一个随机的小数
公式:parseInt(Math.random()*(max-min+1)+min)
Date对象:日期对象,提供了操作日期和时间的API:
创建:41、*创建一个当前日期
	var now=new Date();
		
2、*创建一个自定义时间
	var birth=new Date("yyyy/MM/dd hh:mm:ss");

3、创建一个自定义时间
	var birth=new Date(yyyy,MM,dd,hh,mm,ss);//修改月份,从0~11月,0代表是1月

4、*复制一个日期:
为什么:日期的所有的API都是直接修改原日期的,无法获得修改之前的日期
所以,在执行API之前先进行复制,然后再操作复制后的日期
	var end=new Date(start);
使用:

1、两个日期对象之间,可以相减,得到一个毫秒差(大-小) 2、API:

分量:时间的单位
年月日星期:FullYear Month Date Day
时分秒毫秒:Hours Minutes Seconds Milliseconds
每一个分量都有一对儿getXXXX/setXXXX
其中getXXX负责获取一个分量的值
其中setXXX复制设置一个分量的值
特殊:
1、取值范围:
	FullYear - 当前年份的数字
	Month - 0~11
	Date - 1~31
	Hours - 0~23
	Minutes、Seconds:0~59
	Day - 0~60代表星期天,外国人的眼里星期天才是一个星期的第一天
2、任何分量都有set操作,唯独Day没有set
3、如果希望对某个分量进行加减操作的话
date.setXXXX(date.getXXXX()+/-n)
4、格式化日期为本地字符串:
date.toLocaleString() - 垃圾:具有兼容性问题,我们一般会选择自己封装一个format函数来进行格式化
3、***定时器:
     1、*周期性定时器:每过一段时间会执行一次,先等后做,会反复执行,需要自己写停止才能停止
开启:timer=setInterval(callback,间隔毫秒数);
停止:clearInterval(timer);

     2、一次性定时器:等待一段时间,只会做一次就结束了
开启:timer=setTimeout(callback,间隔毫秒数);
停止:clearTimeout(timer);

同步技术:代码必须一行一行的执行,前面的没做完,后面的就只能等着,目前为止几乎所有的代码,都属于同步技术
异步技术:无论我们这一块代码多么好事,也不会卡住后续代码,目前这个技术只有定时器,最牛逼的异步技术(AJAX)

扩展:如何使用JS创建页面DOM元素:
	//1、创建空标签
		var elem=document.createElement("标签名");
	//2、为这个空标签,设置必要的属性或事件
		elem.属性名="属性值";
		elem.on事件名=function(){操作}
	//3、将我们的元素放上到DOM树
		父元素.appendChild(elem);
1、BOM:Browser Object Model

浏览器对象模型,专门用于操作浏览器的

2、window对象
  1. 代替全局对象global:保存着全局变量和全局函数
  2. 指代当前窗口本身
属性:专门获取大小
  • 获取浏览器的完整的大小:outerWidth/outerHeight
  • 获取浏览器的文档显示区域的大小:innerWidth/innerHeight
  • 获取屏幕的完整的大小,跟window没关系:screen.width/height
方法:
  • 打开链接的新方式:
1. 当前窗口打开,可以后退
open("url","_self");
2、当前窗口打开,禁止后退,使用场景:比如电商网站,结账后禁止后退
history:当前【窗口的历史记录】
location:当前【窗口的正在打开的url】
location.replace("新url");//叫做替换,不叫作跳转
3、新窗口打开,可以打开多个
open("url","_blank")
4、新窗口打开,只能打开一个,使用场景:比如电商网站,只允许用户打开一个结账界面
open("url","自定义一个name");
a标签的其他用途:
1、跳转
2、锚点
3、下载:<a href="xx.exe/rar/zip/7z...">内容</a>
4、打开图片和txt文件:<a href="xx.图片的后缀/txt">内容</a>
5、直接书写JS - 不需要绑定点击事件:<a href="javascript:js代码;">内容</a>
  • 打开新窗口/新连接:
var newW=open("url","target","width=?,height=?,left=?,top=?");

特殊:
1、如果你没有传入第三个参数,那么窗口回合浏览器融为一体,浏览器有多大,窗口就有多大
2、如果你传入第三个参数,那么窗口回合浏览器分离开,独立存在
3、可以拿个变量接住这个窗口,以后用于做其他操作

  • 关闭窗口:
window/newW.close()
  • 改变新窗口的大小:
newW.resizeTo(新宽,新高)
  • 改变新窗口的位置:
newW.moveTo(新X,新Y)
  • window提供的三个框:
警告框:alert("警告文字");
输入框:var user=prompt("提示文字");
确认框:var bool=confirm("提示文字");
  • 定时器也是window的
  • window的专属事件:
1window.onload事件
// load:加载:等待其他所有的资源加载完毕后才会执行的代码,放在里面的代码其实是要最后才会执行
// 只要把js放在HTML的后面,就不需要此操作
2window.onresize事件
// 窗口如果大小发生了变化,就会触发,搭配上上面的判断innerWidth可以理解为这是JS版本的css媒体查询
window.onscroll事件 - 滚动事件,一旦滚动就会触发
//1、获取滚动条当前的位置:window.scrollY
//2、获取元素距离页面顶部有多远:elem.offsetTop/offsetLeft
  • 本地/客户端存储技术:保存着在浏览器里面的数据
    cookie:淘汰了,存储大小只有2kb,而且操作极其复杂,尤其要到处切割的,最多只能保存30天
    webStorage:H5(概念统称)带来的一个新特性,存储的大小由8mb,永久保存,而且操作非常简单!
    分类2种:
  1. sessionStorage - 会话级,只要浏览器一旦关闭,数据就会清除
  2. localStorage - 本地级,只要不清空就永远存在
操作:
1、添加:xxxxStorage.属性名="属性值";
2、读取:xxxxStorage.属性名
3、删除:xxxxStorage.removeItem("属性名");
4、清空:xxxxStorage.clear();//删全部

BOM的常用对象:最重要的三个东西:定时器、客户端存储技术、event

  1. history对象:保存了当前窗口的历史记录(过去的url)
前进:history.go(1);
后退:history.go(-1);
刷新:history.go(0);
  1. location对象:保存了当前窗口的正在打开的url(现在的url)

    ***程序员必备常识:一个url由及部分组成?分别每个部分有什么?

5部分:
1、协议:https(加密)/http(未加密)/ftp(传输文件) /ws(直播) - 前两个都属于叫做 请求 - 响应 模型,专门用于实现网站开发
2、主机号|ip地址|域名:域名是需要花钱购买的,主机号|ip地址是免费的,127.0.0.1才真的叫主机号,只能自己访问自己
3、端口号:https默认端口为443,http默认端口为80,只有默认端口可以省略不写!
4、***文件的相对路径|路由:百度加密
5、***查询字符串|请求消息:前端传输到后端的东西,前端对后端说的话,就是form表单get提交带来的东西
属性:获取url的5个部分的内容,但是不需要记忆,直接输出location对象即可查看
		协议:location.protocal
		域名:location.hostname
		端口号:location.port
		路由:location.pathname
		请求消息:location.search
方法:
		跳转:location="新url" - 替换当前窗口,可以后退
		跳转后,禁止后退:location.replace("新url");
		刷新:location.reload();
DOM:

1、核心DOM:无敌的,既可以操作HTML又可以操作XML,但是语法相对比较复杂

2、HTML DOM:只能操作HTML,不能访问一切自定义的东西,但是语法简单

3、XML DOM:只能操作XML数据格式,被淘汰了,被JSON数据格式代替了

查找元素:
1、通过关系找元素:前提:至少要找到一个元素,才可以调用关系网
                父:xx.parentNode;
		子:xx.children; - 集合
		第一个儿子:xx.firstElementChild;
		最后一个儿子:xx.lastElementChild;
		前一个兄弟:xx.previousElementSibling;
		后一个兄弟:xx.nextElementSibling;
2、直接找元素:2大类:通过HTML的特点去查找元素
var elems=document.getElementsByXXXXXX();//返回的是一个动态集合HTMLCollection
第二大类:2个:通过CSS选择器去查找元素
var elem=document.querySelector("任意css选择器");
缺陷:只能找到单个元素,如果匹配到多个,也只会返回第一个,没找到,返回null
***var elems=document.querySelectorAll("任意css选择器");
优点:
	1、找到了是一个集合,没找到是一个空集合
	2、复杂查找时,非常简单
	3、返回的是一个静态集合NodeList

面试题:document.getXXX 和 document.queryXXX的区别?

	1、后者更适合复杂查找
	2、动态集合和静态集合的区别?
		1、动态集合:每一次DOM发生变化,他都会悄悄的再次查找页面元素,让页面和数据保存一致,但是效率也就低下了 - 不支持forEach
		2、静态集合:每一次DOM发生变化,他都不会悄悄的再次查找页面元素,让页面和数据保存不一致,但是效率也就高了 - 支持forEach
操作样式:

1、内联样式:

获取:elem.style.css属性名; - 只能获取内联样式
设置:elem.style.css属性名="css属性值";

2、样式表样式

//获取想要操作的样式表
	var sheet=document.styleSheets[i];
	//获取此样式表中所有的样式规则
	var rules=sheet.cssRules;
	//数出你想要操作的那个样式规则
	var rule=rules[i];
	//操作
	console.log(rule.style.background);
	rule.style.background="purple"
操作属性:
1、*获取属性值:
核心DOMelem.getAttribute("属性名");
HTML DOMelem.属性名
2、*设置属性值:
核心DOM:elem.setAttribute("属性名","属性值");
HTML DOM:elem.属性名="属性值"
3、删除属性:设置属性值为空字符串,确实某些属性可以算是删除,但是只是删除了属性值,属性名还在,而有的属性哪怕只有一个属性名,也会具有作用(checked、href、readonly...)
*核心DOM:elem.removeAttribute("属性名");
HTML DOM:elem.属性名="";

如何创建元素以及上树:3步

1、创建空标签:
	var elem=document.createElement("标签名");

2、为其设置必要的属性和事件
	elem.属性名="属性值";
	elem.on事件名=function(){操作}
3、上树:3种
\*父元素.appendChild(elem);//在父元素末尾追加一个子元素elem
父元素.insertBefore(elem,已有子元素);//在父元素里面追加一个子元素elem,但是会放在已有子元素的前面
父元素.replaceChild(elem,已有子元素);//在父元素里面追加一个子元素elem,但是elem会替换掉已有子元素
删除元素:elem.remove();

扩展:

1、input的两个专属事件:onfocus(获取焦点)和onblur(失去焦点)
2、创建变量:在ES6新增了一个let关键字 - 建议忘掉var
	let 变量名=值;
作用:
	1、解决了声明提前,一定要先创建再使用
	2、带来了块级作用域:一个{}就是一个快,此变量只能在那个{}里面使用!
	3、如果用let去当作下标绑定事件,那么他会记录着你当前元素的下标,不再需要自定义下标了!其实forEach的那个形参i就是用let创建的!
3//将类数组对象变为普通数组!ES5提供了此API
		接住=Array.from(类数组);
1、递归:简单来说就是再函数之中再一次调用了函数自己

何时使用:专门用于【遍历层级不明确的情况】 - DOM树和【数据】

如何使用:2function 函数名(root){
1、第一层要做什么直接做!

2、判断有没有下一层,如果有下一层则再次调用此方法,只不过传入的实参是自己的下一层
		}
函数名(实际的根元素/根数据)

算法:深度优先!优先遍历当前节点的子节点,子节点遍历完毕才会跳到兄弟节点
缺陷:不要过多使用,性能相对较差,同时开启大量的函数调用,浪费内存,我们只在一个情况:【遍历层级不明确的情况】

递归 vs 纯循环:
递归:
        优点:简单易用
        缺点:性能低
纯循环:
        优点:几乎不占用内存
	缺点:难得很
绑定事件
1、在HTML页面上书写事件属性:
<elem on事件名="函数名(实参)"></elem>
	缺点:
		1、不符合内容(HTML)与样式(CSS)与行为(JS)的分离
		2、无法动态绑定,一次只能绑定一个元素
		3、不支持绑定多个函数对象
2、*在JS中使用事件处理函数属性:
elem.on事件名=function(){操作}

	优点:
		1、符合内容(HTML)与样式(CSS)与行为(JS)的分离
		2、可以动态绑定

	缺点:
		1、不支持绑定多个函数对象 
3、*在js中使用事件API:如果不用考虑老IE,他还是不错的
主流:elem.addEventListener("事件名",callback);
老IE:elem.attachEvent("on事件名",callback);
兼容:
	if(elem.addEventListener){
		elem.addEventListener("事件名",callback);
		}else{
		elem.attachEvent("on事件名",callback);
		}
优点:
	1、符合内容(HTML)与样式(CSS)与行为(JS)的分离
	2、动态绑定
	3、支持绑定多个函数对象
缺点:有兼容性问题
扩展:
	1select&option只要他们可以简化创建元素&上树!
	一句话简化4个操作:
	select.add(new Option("innerHTML","value"))
属于BOM的重点只有3个:定时器 + 客户端存储技术 + event(事件对象):
1、事件周期:从事件发生,到所有事件处理函数执行完毕的全过程:
3个阶段:
	1、捕获阶段:由外向内,记录着要发生的事件有哪些
	2、目标优先触发:目标元素->当前点击的实际发生事件的元素
	3、冒泡触发:由内向外,依次执行我们之前记录着的要发生的事件
2、*****获取事件对象event:

主流:会自动作为事件处理函数的第一个形参传入e

老IE:event; - 老IE全局有这个变量

兼容:event;//不光老IE可用,主流也可用

***获取鼠标的坐标/位置
获取鼠标相对于屏幕的坐标:e.screenX/Y
获取鼠标相对于浏览器窗口/客户端/文档显示区域的坐标:e.clientX/Y
获取鼠标相对于网页的坐标:e.pageX/Y
***阻止事件冒泡 - 笔试面试中

主流:e.stopPropation();

老IE:e.cancelBubble=true;

兼容:e.cancelBubble=true; //不光老IE可用,主流也可用

*****事件委托/利用事件冒泡 - 开发中常用:

提升网页性能,有了它我们的事件函数也可以简化为箭头函数
优化:如果多个子元素定义了相同 或 相似的事件操作,那么最好只给【父元素定义一次】

目标元素target:实际触发事件的元素

主流:e.target;

老IE:e.srcElement;

兼容:e.srcElement;//第3次见到小三上位,不光老IE可用,主流也可用

***组织浏览器的默认行为:

比如:a标签默认就可以跳转,提交按钮可以提交表单,右键自带一个弹出框,F12自带一个控制台,F5自带刷新,F11自带全屏...

主流:e.preventDefault();

老IE:e.returnValue=false;

兼容:e.returnValue=false;//不光老IE可用,主流也可用

新事件:

1、右键事件 - window.oncontextmenu

2、键盘事件:一般用于游戏开发比较多 + 都要搭配上键盘的键码

window.onkeydown - 按下和按住都会触发,任何键盘按键都可以触发

window.onkeypress - 按下和按住都会触发,但是只有数字、字母、回车、空格可以触发,其他人不行

window.onkeyup - 松开按键才会触发,任何键盘按键都可以触发

获取键盘的键码

e.keyCode; - 可以获取到你按了哪个键,每个键都有自己对应的键码 eval(str) - 会悄悄的脱掉字符串的衣服再运算

this的指向 - 难点
  1. 单个元素绑定事件this->这个元素
  2. 多个元素绑定事件this->当前元素
  3. 箭头函数this->外部对象

第四周

1、事件的取消绑定:
  • 如果使用elem.on事件名=()=>{},那么写成:elem.on事件名=null; 可以取消事件绑定
  • 如果你使用elem.addEventListener("事件名",callback); 那么写成 elem.removeEventListener("事件名",callback); - 事件名和回调函数,【必须和添加时是一模一样的】
// btn.onclick=e=>{
//     console.log(1)
// }
// btn.onclick=null
   function f1(){
      console.log(1)
      btn.removeEventListener("click",f1)
       }
 btn.addEventListener("click",f1)
2、this的指向
  1. 单个元素绑定事件this->这个元素
  2. 多个元素绑定事件this->当前元素
  3. 箭头函数中this->外部对象
  4. 函数中的this->当前正在调用函数的这个对象
  5. 定时器的this->window
ES5强制改变this的指向

call/apply:临时的替换了函数之中的this - 借用

语法:

  1. 函数名.call(借用的对象,实参,...) - 单独传入每个实参
  2. 函数名.apply(借用的对象,arr) - 只能传入一个事件要求必须是一个数组,apply可以将数组打散
    强调:call/apply:相当于立刻调用函数,立刻执行的

bind:永久替换了函数中的this -

  1. 创建了一个和原函数功能完全相同的新函数

  2. 将新函数的this永久绑定为了指定对象

  3. 将新函数的部分参数永久固定

    语法:var 新函数名=函数名.bind(永久对象,永久实参,...) - 不是立刻执行,需要调用

强调:bind绑定得到的新函数没办法被call/apply再次借走!

三个固定套路:

  1. Math.max/min.apply(Math,arr) ===> Math.max/min(...arr)
  2. Object.prototype.toString.call/apply(arr)=="\[object Array]"//toString不需要传入实参
  3. 类数组转为普通数组:
老方式:接住=Array.prototype.slice.call/apply(类数组对象);
新方法:接住=Array.from(类数组对象)
ES6
let、const关键字、箭头函数
模板字符串:
`可以直接识别变量,不再需要+运算去拼接了,而且实现了一个简单的js环境,甚至支持在里面做运算写API都可以
`
var name="张衡",age=18
console.log(`我是${name},年龄${age}`)
解构赋值:

顾名思义:解析结构再进行赋值 - 赋值的新方式,并且得到了增强!
如果赋值符号,左右两边的结构一样,就会解开结构再一一进行赋值

  • 类似数组的解构赋值
let [a,b,c]=[2,3,4]
console.log(a)
console.log(b)
console.log(c)
  • 类似对象的解构赋值
let {a,b=默认值,c}={c:3,a:1,b:2}
console.log(a);
console.log(b);
console.log(c);
  • 调用函数时
function zwjs({name="无名氏",age,hobby}){
	return `我的名字叫${name},今年${age}岁,喜欢${hobby}`;
		}
			
console.log(zwjs({age:18,hobby:"学习"}))
  • 函数的返回的结果,可以有多个
function f1(){
	var a=1;
	var b=2;
	return [a,b];
		}
var [a,b]=f1();
console.log(a);
console.log(b);

新的循环:
for(var val of 数组名){
      //val - 值
	}

缺陷:
1、没有提供过下标,意味着不能修改原数组
2、只能遍历索引数组,不能遍历hash数组,意味着也不能遍历对象

Set和Map:两个新的数据类型:

Set: 类似于数组的一种数据格式 - 去重数组,然后再转回数组

[...new Set(arr)]
... - 三个点扩展运算符,可以脱掉数组/对象的外套

Map: 类似于对象的一种数据格式

var m=new Map();
添加:m.set("键","值");
获取:m.get("键");
删除:m.delete("键");
清空:m.clear();
遍历:m.forEach(callback);
正则表达式

定义字符串中字符出现规则的表达式

何时使用:切割、替换、验证
如何使用:语法:/正则表达式/;

最简单的正则就是关键字原文 "no" -> /no/后缀
后缀:g:找全部		i:忽略大小写
备选字符集:/^[备选字符集]$/;

强调:1、一个中括号,只管一位字符
2、问题:正则表达式默认只要满足了就不管后续了

解决:前加^后加$,两者同时使用,代表要求从头到尾【完全匹配】/^[备选字符集]$/; - 验证必加

特殊:如果备选字符集中ascii码是连续的,那么可用-省略掉中间部分
比如:
	一位数字:[0-9]
	一位字母:[A-Za-z]
	一位数字、字母、下划线:[0-9A-Za-z_]
	一位汉字:[\u4e00-\u9fa5]
	一位数字、字母、汉字、下划线:[0-9A-Za-z\u4e00-\u9fa5_]
	除了xxx之外的:[^0-9] - 很少使用,范围太广了
预定义字符集:
一位数字:\d
一位数字、字母、下划线:\w
一位空白字符:\s	什么叫空白字符:空格、制表符、换行
一位除了换行外的任意字符:.	- 很少使用,范围太广了
量词:规定了一个字符集出现的次数:
有明确数量:                           
        字符集{n,m}:前边相邻的字符集,至少n个,最多m个
	字符集{n,}:前边相邻的字符集,至少n个,多了不限
	字符集{n}:前边相邻的字符集,必须n个
无明确数量:
	字符集?:前边相邻的字符集,可有可无,最多1个
	字符集*:前边相邻的字符集,可有可无,多了不限
	字符集+:前边相邻的字符集,至少一个,多了不限
选择和分组:

选择:在多个规则中选一个
规则1|规则2
分组:
将多个字符集临时组成一组子规则
(规则1|规则2)

指定匹配位置
开头:^
结尾:$
*特殊:两者同时使用,前加^后加$,表示从头到尾要求完全匹配 - 只要你做【验证】
密码强度:

预判公式:

(?![0-9]+$) -> 不能全由数字组成,可能有大写、小写、汉字、日文、韩文、特殊符号....
(?![a-z]+$) ->不能全由小写字母组成,可能有大写、数字、汉字、日文、韩文、特殊符号....
(?![0-9a-z]+$) -> 不能全由数字组成,也不能全由小写字母组成,也不能全由数字和小写的组合组成,至少要有点别的

字符串中支持正则API:

切割:var arr=str.split("固定切割符"/RegExp)

替换

1、基础替换法:
	str=str.replace(RegExp,"新内容");
	//replace支持正则,并且搭配上后缀g就可以找到全部
	//缺陷:替换的新内容是一个固定的
2、高级替换法:
	str=str.replace(RegExp,function(a,b,c){
//	console.log(a);//正则匹配到的关键字
//	console.log(b);//正则匹配到的关键字的下标
//	console.log(c);//原字符串
	return 判断a关键字的长度,而返回不同的星星数量
	});
格式化:
	var id="500103198602215933";
	var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/;
	id=id.replace(reg,function(a,b,c,d,e,f){
//在replace的时候,正则出现了分组,我们会得到更多的形参
//在形参a的后面就会出现n个形参,具体看你有多少分组
//第1个分组获得的内容会保存到第2个形参之中
//第2个分组获得的内容会保存到第3个形参之中
	...
//倒数第二个是下标
//倒数第一个是原文
return `${b}${c}${d}日`
})
console.log(id);
正则对象
创建:
1、直接量:var reg=/正则表达式/后缀;
2、构造函数:var reg=new RegExp("正则表达式","后缀");

API:
验证:var bool=reg.test(用户输入的);
Object:

对象 - Array、String、RegExp、Date... 对象具有属性和方法,都是预定义好的

面向对象 - 三大特点:封装、继承、多态

开发方式

面向过程:过程 - 开始 -> 结束

面向对象:对象(属性和方法),js里面有一句话万物皆对象

封装/创建/定义:封装自定义对象
直接量方式:
	var obj={
	"属性名":属性值,
	...,
	"方法名":function(){
		},
	...
	}

强调: 1、其实属性名和方法名得""可以不加 - 暂时建议你加上,以后我们要学习一个数据格式JSON,他就必须在键上加上""

2、访问对象的属性和方法
	obj.属性名	===	obj["属性名"]
	obj.方法名()	===	obj["方法名"]()
	建议使用去访问对象的属性和方法,更简单

js中一切都是对象,除了undefined和null,一切对象的底层都是hash数组
3、访问到不存在的属性,返回undefined

4、可以随时随地添加新属性和新方法
obj.属性名=新值;
obj.方法名=function(){}

5、如果我希望遍历出对象所有的东西,必须使用for in循环,必须写为obj[i]才能拿到,不要使用.会出问题

this的指向
1、单个元素绑定事件this->这个元素
2、多个元素绑定事件this->当前元素
3、定时器中的this->window
4、箭头函数中的this->外部对象
5、函数中的this->谁在调用此方法,this就是谁
6、构造函数之中this->当前正在创建的对象
预定义构造函数:
var obj=new Object();//空对象
//需要自己后续慢慢添加属性和方法
obj.属性名=新值;
obj.方法名=function(){}
自定义构造函数方式:2步
1、创建自定义构造函数
function 类名(name,age,hobby){
this.name=name;
this.age=age;
this.hobby=hobby;
}
调用构造函数创建对象

var obj=new 类名(实参,...)

面向对象

优点:
1、逼格高,所有的属性和方法都保存在一个对象之中 - 更符合现实生活更有意义
2、每个功能特地分开写 - 便于以后维护
3、铁锁链舟 - 一个方法触发多个方法联动

继承:父对象的成员(属性和方法),子对象可以直接使用
1***如何找到原型对象(父对象):保存一类子对象共有属性和共有方法
1、对象名.__proto__; //必须先有一个对象
2、构造函数名.prototype;//构造函数几乎人人都有,除了Math和windownew 构造函数名();//Array、String、Date、RegExp...
两链一包:作用域链和【原型链】和闭包

每个对象都有一个属性:.proto,可以一层一层的找到每个人的父亲,形成的一条链式结构,我们就称之为叫做原型链 可以找到父对象的成员(属性和方法),作用:找共有属性和共有方法,自己没有,会悄悄向上找 最顶层的是Object的原型

有了原型对象,可以设置共有属性和共有方法 1、原型对象.属性名=属性值;
2、原型对象.方法名=function(){}

面向对象
1、判断是自有还是共有:
1、判断自有:obj.hasOwnProperty("属性名");
结果结果为true,说明是自由属性,如果结果为false,有两种可能,有可能是共有,也有可能是没有
2、判断共有:
if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){//in关键字,会自动查找整条原型链上的属性,找到了结果为true,找不到结果为false
		共有
	}else{
		没有
	}
完整公式:
if(obj.hasOwnProperty("属性名")){
		自有
}else{
		if("属性名" in obj){
			共有
		}else{
                        没有
        }
}

修改和删除:自有和共有
自有:
修改:obj.属性名=新属性值;
删除:delete obj.属性名;
共有:
修改:原型.属性名=新属性值;
删除:delete 原型.属性名;
如何为老IE的数组添加indexOf方法 - 这道题不是固定的,可能问如何为一类人创建某个方法
if(Array.prototype.indexOf===undefined){//老IE		Array.prototype.indexOf=function(key,starti){		starti===undefined&&(starti=0);
for(var i=starti;i<this.length;i++){				if(this[i]==key){
		return i;
			}
		}
			return -1;
	}
}

如何判断x是不是一个数组
1、判断x是不是继承Array.prototype
Array.prototype.isPrototypeOf(x);
结果为true,说明是数组,结果为false,说明不是数组
2、判断x是不是由Array这个构造函数创建
	x instanceof Array;
结果为true,说明是数组,结果为false,说明不是数组

3Array.isArray(x) - ES5新增的方法,只有数组可以这么使用
结果为true,说明是数组,结果为false,说明不是数组
4、输出【对象的字符串】形式
在Object的原型上保存着最原始的toString()方法
原始的toString输出的形式:[object 构造函数名]
固定套路:		
Object.prototype.toString.call/apply(x) === "[object Array]";

多态:子对象觉得父对象的成员不好用,就在本地定义了一个同名函数,覆盖了父对象的成员,不严格定义:同一个函数,不同的人使用,表现出来得效果不一样,由多种形态

实现自定义继承
1、两个对象之间设置继承:
子对象.__proto__=父对象
2、多个对象之间设置继承:
构造函数名.prototype=父对象;
时机:应该在开始创建对象之前就设置好继承关系
ES6 - class关键字:简化面向对象(封装、继承、多态)
class 类名 extends 老类名{
        constructor(name,speed,rl){//放在constructor里面得都是自有属性
	super(name,speed);//帮你调用你继承的老类得constructor函数
	this.rl=rl;
	}//放在constructor外面得都是共有方法
//还会继承到老类所有的API,也可以在此处添加属于自己的新的API
			}
var obj=new 类名(实参,...)

Function - 闭包:
作用域:2种

1、全局:随处可用,可以反复使用,缺点:容易被污染

2、函数:只能在函数调用时内部可用,不会被污染,缺点:一次性的,使用完就会释放的。

函数的执行原理:
1、程序加载时:
创建执行环境栈(ECS):保存函数调用顺序的数组
首先压入全局执行环境(全局EC)
全局EC引用着全局对象window
window种保存着我们的全局变量
2、定义函数时
创建函数对象:封装代码段
在函数对象中有一个scope(作用域)属性:记录着函数来自的作用域是哪里
全局函数的scope都是window
3、调用前
在执行环境栈(ECS)压入新的EC(函数的EC)
创建出活动对象(AO):保存着本次函数调用时用到的局部变量
在函数的EC中有一个scope chain(作用域链)属性引用着AO
AO还有parent属性是函数的scope引用着的对象
4、调用时:
正是因为有了前面三步,才会带来变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错


5、调用完:
函数的EC会出栈,没人引用着AO,AO自动释放,局部变量也就释放了
闭包:

希望保护一个可以【反复使用的局部变量】的一种词法结构
如何使用:
1、两个函数进行嵌套
2、外层函数创建出受保护的变量
3、外层函数return出内层函数
4、内层函数在操作受保护的变量

强调:
1、判断是不是闭包:有没有两个函数嵌套,返回内层函数,内层函数再操作受保护的变量
2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量

缺点:受保护的变量,永远都不会被释放,使用过多,会导致内存泄漏 - 闪退,不可多用!

四个事件需要防抖节流

共同:触发的飞快

1、elem.onmousemove
2、input.oninput 
3window.onresize
4window.onscroll

防抖节流公式:
function fdjl(){
    var timer=null;
    return function(){
        if(timer){clearTimeout(timer);timer=null;}
        timer=setTimeout(()=>{
        操作;
        },间隔毫秒数)
    }
}

var inner=fdjl()
总结:
两链一包:
1、作用域链:以函数的EC的scope chain属性为起点,经过AO逐级引用,形成的一条链式结构
作用:查找变量,来带了变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错
2、原型链:每个对象都有一个属性.__proto__,可以一层一层的找到每个人父亲,形成了一条链式结构
作用:找共有属性和共有方法的,哪怕自己没有会悄悄的向上查找,如果最顶层也没有才会报错
最顶层是Object的原型,甚至上面放着我们眼熟的API - toString,怪不得人人都可以使用
3、闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
作用:专门用于防抖节流

第五周

1、保护对象:保护对象的属性和方法
1. 每个属性都有四大特性:
{
value: '很多', //实际保存值的地方
writable: true, //开关,控制着这个属性是否可以被修改 - 默认值为true
enumerable: true, //开关,控制着这个属性是否可以被for in循环遍历到 - 默认值为true
configurable: true //开关,控制着这个属性是否可以被删除 - 默认值为true,总开关:一旦设置为false,其他特性不可以在修改,而且他本身也是一旦设置为false,不可逆
}
2. 修改四大特性:
Object.defineProperty(对象名,"属性名",{
		四大特性
	})
调用一次方法只能保护一个属性的四大特性
Object.defineProperties(对象名,{
		"属性名":{四大特性},
		...
	})
至少方法只调用了一次,但是四大特性写着始终麻烦,甚至不能防止添加
3.三个级别
//1、防扩展:防添加
Object.preventExtensions(obj);

//2、密封:防添加防删除
Object.seal(obj);

//3、冻结:防添加防删除防修改
Object.freeze(obj);
4、*对象的深浅拷贝:
1、浅拷贝:利用按值传递
var obj1={"name":"obj1"};
var obj2=obj1;
2、深拷贝:两者互不影响
	var obj1={"name":"obj1"};
	var obj2={...obj1};

3、*以后如何脱掉后传来的数据的衣服:
var JSONtxt=JSON.stringify(obj);
var jsonObj=JSON.parse(JSONtxt);
此方法也能实现深拷贝
5、*Error对象:错误
语法:
try{
    只放入你可能出错的代码
}catch(err){
    发生错误后才会执行的代码
    err形参就保存着错误的消息
}
抛出自定义错误:只要是错误,后续代码都不会执行
throw new Error("自定义错误消息")
6、柯里化函数
function add(a){
	return function(b){
		return function(c){
			console.log(a+b+c)
	}
		}
			}
			
add(3)(5)(7);
}
1、匿名函数
  1. 自调:只能执行一次,好处:函数中的没有用的变量是会自动释放的,它可以用于代替全局代码写法,两者很相似,都只会执行一次,但是自调会释放没用的东西
(function(){
	操作;
})();

2. 回调:匿名函数不是自调,就是回调

elem.on事件名=function(){}
arr.sort(function(){})
xxx.api(function(){})
var obj={
      "方法名":function(){}
}
一切的回调函数,都可以简化为箭头函数
2、设计模式
单例模式

也称之为单体模式,保证一个类仅有一个实例对象创建,并且提供一个访问它的全局访问点

var h52302=(function(){
        let state=null;
	return function(name,age){
	this.name=name;
	this.age=age;
	if(state){
		return state;
	}
	return state=this;
    }
})();
观察者模式:也有人称呼叫做发布订阅模式
                      let obj={};
//创建订阅者
			function on(id,fn){
				if(!obj[id]){
					obj[id]=[];
				}
				obj[id].push(fn);
			}
			
			on("袍哥",(msg)=>{console.log("小胡来了",msg)})
			
			//发布者的操作
			function emit(id,msg){
				obj[id].forEach(fn=>fn(msg));
			}
			btn.onclick=()=>{
				emit("袍哥","一支穿云箭");
			}

事件轮询:js其实是单线程应用,代码必须是从上向下执行的,一步一步执行的,如果某一块代码非常耗时,可能会导致整个页面都卡住了
  1. 宏任务:不会再卡住我们的单线程应用,可以让后续代码先走,我们慢慢跟着来,但是问题在于,多个宏任务同时存在,到底谁先执行,谁后执行,分不清楚
  • 定时器:setInterval和setTimeout()
  • ajax
  1. 微任务:ES6提供了Promise对象 - 可以控制异步代码,依然是异步代码,但是可以控制执行的顺序了
function ajax1(resolve){
    setTimeout(()=>{
        console.log(1);
        resolve();//放行函数
        },Math.random()\*5000)
}
function ajax2(){
        return new Promise(resolve=>{
            setTimeout(()=>{
            console.log(2);
            resolve();
},Math.random()\*5000)
})
}
function ajax3(){
return new Promise(resolve=>{
setTimeout(()=>{
console.log(3);
resolve();
},Math.random()\*5000)
})
}
new Promise(ajax1).then(ajax2).then(ajax3);
console.log("后续代码");
Node.js官方模块:
http模块
var http=require("http");
var url=require("url");
var fs=require("fs");
//创建服务器应用
var app=http.createServer();
//为其绑定端口号
app.listen(80);
//为其绑定了请求事件:http:请求 - 响应模型,必须要有一个请求(前端),才有一个响应(后端)
app.on("request",(req,res)=>{
	//req:request(请求)对象,他有一个属性叫做req.url,获取到前端传到后端的路由和请求消息,但是路由和请求消息是融为一体,不方便我们获取某个部分,所以我们可以引入url,进行解析!分开两部分
	var objUrl=url.parse(req.url,true);
	//把路由保存了起来!
	var router=objUrl.pathname;
	if(router=="/"||router=="/index.html"){//判断路由的不同,读取不同的页面给用户看,引入fs文件模块
		//res: response(响应)对象,他有一个方法叫做res.end("响应的内容"/buf) - 可以想应该给前端想要看的东西
		fs.readFile(__dirname+"/public/html/index.html",(err,buf)=>{
			res.end(buf)
		})
	}else if(router.match(/html/)!=null){
		fs.readFile(__dirname+`/public/html${router}`,(err,buf)=>{
			res.end(buf)
		})
	}else if(router.match(/css|js|jpg|png|gif|woff|woff2|ttf2/)!=null){
		fs.readFile(__dirname+`/public${router}`,(err,buf)=>{
			res.end(buf)
		})
	}
})

强调:一切的src和href都是一个请求都是一个路由,这个请求就需要后端来解析,根据不同的请求响应不同的内容!

模块(每个.js文件)分类

1、官方模块:require("官方模块名");
2、第三方模块
3、自定义模块:require("./模块路径");

自定义模块:分为两大类
1、文件模块:创建xx.js去公开了需要公开的内容,主模块引入,就必须写为require("./模块路径");

2、目录模块:31、比如创建m1的文件夹,在其中创建index.js的文件,去公开了需要公开的内容,主模块引入,就必须写为require("./m1");
	2、比如创建m2的文件夹,在其中创建suibian.js的文件,去公开了需要公开的内容,主模块引入,就必须写为require("./m2");
	必须在创建一个必须名package.json的配置文件,写入:{"main":"suibian.js"}
	3、创建一个文件夹必须名为node_modules,在在其中创建创建m3的文件夹,在其中创建index.js的文件,去公开了需要公开的内容,主模块引入,就必须写为require("m3");
mongo就是基于json的数据库:json就是JavaScript Object Notation - JS的一部分
mongo的语法:都是在客户端cmd输入
1、数据库的操作
  1. 查询所有数据库:show dbs
  2. 创建/切換数据库:没有就创建,有了就切换:use 数据库名称
  3. 查看当前选中的数据库:db
  4. 创建数据表:db.createCollection("表名"); 没有限制
  5. 删除数据库:db.dropDatabase(); 一旦删除就不能恢复,不推荐
2、数据表的操作
  1. 创建数据表:
    db.createCollection("表名",{size:5242880,capped:true,max:5000}); - 最大存储空间为5mb,最多存储5000个,意味着这个数据表做了限制,不推荐
  2. 查看目前所有的数据表:db.getCollectionNames();
  3. 删除数据表:db.表名.drop(); - 一旦删除就不能恢复,不推荐
3、数据的操作

1、增:

db.表名.save({键值对,...}) - 一次只能插入一条数据
db.表名.save([{},{},{},{},....]) - 一次插入多条数据

2、删:

db.表名.remove({}) - 不推荐,删除数据库中所有的数据
db.表名.remove({条件}) - 条件依然也是键值对

3、改:

db.表名.update({条件},{$set:{新内容}})

4、查:db.表名.find(); - 找所有

db.表名.find({pwd:"666666"}); - 找到所有密码为666666的数据
db.表名.find({},{name:1,pwd:1}); - 找到所有的数据,但是只返回name和pwd
db.表名.find().sort({age:1}); - 按age升序排列!
db.表名.find({age:{\$gte:18}}) - 拿到所有age大于等于18的数据,
//gt大于 gte大于等于 lt小于  lte小于等于(获取成年人)
db.表名.find({name:/o/}) - 甚至可以用正则(模糊查询)
db.表名.find().skip(5).limit(5) - 跳过前5条,在拿5条数据
db.表名.find().count() - 获取到此表有多少条数据
Node.js操作mongoDB - Node.js官方并没有提供过操作数据库的API

1、安装第三方模块:mongoose

2、使用步骤:

1、引入:var mongoose=require("mongoose");
2、连接数据库:mongoose.connect("mongodb://127.0.0.1/h52302");
3、创建出一个新的数据表的同时,设置数据类型的控制,防止用户乱输
	var UserSchema=mongoose.Schema({
		name:String,
		pwd:String,
		age:Number,
		email:String,
		vip:String
	})
                         模板名  数据控制    表名
var User=mongoose.model("User",UserSchema,"user")

3、增:2步

1、创建要插入的对象
var x=new User({
	name:"袍哥1",
	pwd:"123456",
	age:"123",
	email:"pg@qq.com",
	vip:"1"
})
2、创建好的对象插入到数据表中
x.save().then(()=>{
    操作
})

4、删

User.deleteOne/deleteMany({条件}).then(()=>{
            操作
			})

5、改

User.updateOne/updateMany({条件},{新内容}).then(()=>{
		操作
	})

6、查

User.find({条件}).then((rs)=>{
	rs->查询到的数据
})

第六周

同步交互:用户向服务器端发起请求,直到服务器端进行响应的全过程,用户是不可以做其他事情的(等)

典型:网址提交、表单提交 - 都是属于同步交互

异步交互:用户向服务器端发起请求,直到服务器端进行响应的全过程,用户是可以做其他事情的(不等)

典型:ajax请求

Ajax:Asynchronous JavaScript And XML - 异步的JavaScript和XML

不严格的定义:页面不完全刷新,只会导致页面局部发生变化的技术
目的:在不刷新页面的情况下和服务器端进行交互 - 可以将服务器端的数据放到前端(比较验证,数据渲染)

1、创建ajax核心对象:
	var xhr=new XMLHttpRequest();

2、建立和 服务器 的连接
	xhr.open("GET/POST","路由");

3、向服务器端发送请求消息:
	xhr.send();//但是此方法很特殊

特殊:
    1、GET:send方法会失效,但是还不能省略,必须写为:
            xhr.open("GET","路由?name=value&...");
            xhr.send(null);

    2、POST:send方法可用,必须写为:- 暂时不用
            xhr.open("POST","路由");
            xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
            xhr.send("name=value&...");
4、绑定状态监听事件
	xhr.onreadystatechange=()=>{
		if(xhr.readyState==4&&xhr.status==200){
			xhr.responseText; 
		}
	}

数据格式
1、XML

面试题:HTML、XHTML、DHTML、XML分别是什么?

HTML - 网页:预定义了很多标签
XHTML - 更严格的网页,HTML5->XHTML->HTML4.01,比如<input disabled="disabled" />
DHTML - 动态效果网页,在离线端,网页也具有动态特效,其实并不是新技术也不是新概念,而是现有技术的一个整合统称:HTML+CSS+JS(dom)
XML - 配置文件|数据格式:未知的标记,没有提供过任何预定义标签!全靠自定义

使用xml 必须写上声明标签:<?xml version="1.0" encoding="utf-8"?>
version - 版本号
encoding - 编码格式

前端:依然使用ajax去获取xml

1、路由:写为"xx.xml";
2、xhr.responseText 换成 xhr.responseXML - 可以使用【核心DOM】去操作
2、JSON:JavaScript Object Notation:JS对象表示法,本身就是JS的一部分

作用和XML类似,都是用于当一个数据格式
好处:比XML 更简洁、更快、更容易解析 - 一个优秀的数据格式可以极大的提升程序员的开发效率

哪些属于JSON字符串

1、'[1,2,3,4,5]'
2、'{"key":value}'
3、*'[{},{},{},{}]'
4、'{"names":["袍哥","钟哥","苗姐"],"ages":[18,19,20]}'

后端把数据库的数据取出来整理为一个JSON字符串:JSON.stringify(obj)
前端依然使用ajax去获取响应的数据,真正重要的:xhr.responseText

JSON字符串解析为一个对象
	1eval("("+JSONtxt+")")
	2、*JSON.parse(JSONtxt)