JS代码知识点回顾

216 阅读54分钟

Day01

一、 JavaScript介绍:简称js,是一个运行在【浏览器端】的【解释型】【弱类型】【面向对象】脚本语言

  1. 可以使用一切编辑工具编写js代码,编辑器并不代表的你的实力
  2. 解释型
  3. 弱类型
  4. 面向对象
  5. 可以做一切css完成不了的效果(轮播、选项卡、购物车、验证...)

二、 如何使用js:

  1. <script> //js代码 </script>
  2. <script src="js路径"> //千万不要在这里再写js代码 </script>
  3. 输出的方式/打桩输出 - 帮助我们以后检查错误:3种
  4. *在控制台输出日志:console.log(想要输出的东西);//console - 控制台 log - 日志,推荐:不会影响到页面
  5. 在页面上输出日志:document.write(想要输出的东西);//document - 文档 write - 写,还支持标签的写法,但是是个垃圾,比如:如果搭配点击事件,会把页面上原有的东西全部替换掉
  6.  3、在弹出框输出日志:alert(想要输出的东西);//警告框,一般般:有时候会卡住整个页面,导致用户只能看到一个白板
    

3、*变量:创建后,可以再次修改

  • 何时使用:以后反复使用到的数据,都要提前把他保存在一个变量之中,以后使用变量名,相当于就是在使用变量的值 语法:var 变量名=值; 特殊:
  1. 变量名其实不是随意的
  2. 不能以数字开头
  3. 建议驼峰命名法或下划线命名法
  4. 命名要尽量的见名知意
  5. 如果你的变量名是name,不管你保存的数据类型是什么,都会悄悄的给你转为一个字符串
  6. 变量名不能是关键字
  7. 变量可以只创建,不赋值,默认值为undefined,但是从今天开始我们要牢记undefined是一个垃圾,拿来做什么都要不得 5、如果多个变量连续创建,简写: var 变量名=值,变量名=值,变量名=值,变量名=值,变量名=值;

5、了解 - 数据类型有哪些?分为两大类

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

1、*number - 数字:取值有无数个,而且数字就是直接写,不用加任何东西(颜色是蓝色)

2、*string - 字符串:取值有无数个,但是必须加上:"" 或 ''(颜色是黑色)

3、*boolean - 布尔:取值只有2个,分别叫true(真|对) 或 false(假|错)- 一般用于当作条件判断(颜色是蓝色)

4、null - 空,取值只有1个,就是null,唯一的作用就是用于释放变量释放内存的,节约内存空间,提升网页的性能,以后会有更好的方式(颜色是灰色)

5、undefined - 取值只有1个,就是undefined,从今天开始我们要牢记undefined是一个垃圾 查看数据类型:typeof(变量);

2、引用/对象类型:有11个,可以暂时理解为有11个对象(属性和方法)等待我们学习

*6、*运算符:

1、*算术运算符:+ - * / %

特殊: 1、%:取余,俗称模,两个数相除,不取商,而是取除不尽的余数 作用:

1、*任意数%2 - 判断奇偶数

2、取出某个数字的后n位 1234%10 -> 4 1234%100 -> 34

2、带有隐式转换:悄悄地会将数据类型转换,发生变化,默认:都是左右两边转为数字再运算 true->1 false->0 undefined->NaN null->0 "1000"->1000 "1000px"->NaN - 暂时解决不了,只有等待以后学习了强制(显示)类型转换才能解决**

NaN::Not A Number:不是一个数字,但是他确实是数字类型,不是一个有效数字,没有优点,但是有两个缺点:* 1、参与任何的算术运算结果仍为NaN 2、参与任何的比较运算结果都是false

3、+运算特殊:如果碰上一个字符串,左右两边都会悄悄的转为字符串,+运算不再是+运算,而是拼接操作

2、*比较/关系运算符:> < >= <= == != === !==

结果:一定是一个布尔值 带有隐式转换:默认左右两边都会悄悄转为数字再比较大小

特殊:

  1. 如果参与比较的左右两边都是一个字符串,则会按位PK每个字符的十六进制的unicode号(十进制ASCII码); 数字0-9<大写A-Z<小写a-z<汉字 常识:汉字的第一个字:是 一 unicode号是 4e00 ascii是19968汉字的最后一个字:是 龥 unicode号是 9fa5 ascii是40869

  2. NaN参与任何的比较运算结果都是false,带来了一个问题:如何判断x是不是NaN,我们不能使用普通的比较运算去判断

!isNaN(x) 结果是一个布尔值:true->说明是有效数字 false->说明是NaN

3、undefined==null//true、

解决:全等:不带隐式转换的等值比较===,要求值相同并且数据类型也要相同 !==:不带隐式转换的不等比较

3、*赋值运算符:= += -= *= /= %=

  1. =:赋值:将=右边的东西,保存到=左边的变量名之中
  2. 后面5个可以理解为是一种升级写法,运算后再保存回变量本身,举例: i=i+1 ==> i+=1;
4、*逻辑运算符:综合比较 - 用于写条件

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

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

!:颠倒布尔值

5、*自增自减运算符:
		++  -- 
		i++  ===>  i+=1  ===> i=i+1

		自增:固定的每次只能+1
		累加:+=每次加几由程序员自己决定

		鄙视题:
			前++ 和 后++的区别?
			如果单独使用,前++和后++,没有任何区别。
			但是如果参与了其他表达式,变量始终都会+1,但是前++和后++的【返回结果】不同
				前++,返回的是加了过后的新值
				后++,返回的是加了之前的旧值

	6、位运算:
		左移:m<<n,读作m左移了n位,翻译:m*2的n次方
		右移:m>>n,读作m右移了n位,翻译:m/2的n次方
		垃圾的原因:底数只能是2

扩展:用户输入框:var 变量=prompt("提示文字");

day02

1、*****分支结构:

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

  1. 顺序结构 - 默认:从上向下依次执行每一句话

  2. 分支结构 - 通过条件判断,选择部分代码执行

  3. 循环结构 - 通过条件判断,选择要不要重复执行某块代码

     3种:
     1if...else...如何使用:3种情况
     	1、一个条件,一件事,满足就做,不满足就不做
     		if(条件){
     			操作;
     		}
    
     	2、一个条件,两件事,满足就做第一件,不满足就做第二件
     		if(条件){
     			操作;
     		}else{
     			默认操作;
     		}
    
     	3、多个条件,多件事,满足谁,就做谁
     		if(条件1){
     			操作1;
     		}else if(条件2){
     			操作2;
     		}else{
     			默认操作;
     		}
    
     	注意:
     		1else...if这句话想写多少,由程序员自己决定
     		2else这句话可以省略不写,但是不推荐,如果不写,条件都不满足的情况,则什么事儿都不会发生
     		3、分支走了一条路,就不会再走别的路
    
     2switch...case 分支:
     	语法:
     		switch(变量/表达式){
     			case1:
     			操作1;
     			break;
     			case2:
     			操作2;
     			break;
     			default:
     			默认操作;
     		}
    
     	特殊:1、问题:默认只要一个case满足后,会将后续所有的操作全部做完
     		解决:break;
     		建议:每一个case的操作后面都跟上一个break
     			有的地方也可以不加break1、最后一个操作default可以省略break
     				2、如果中间多个条件,做的操作是一样的,可以省略掉中间部分
     	          2case在做比较的时候是不带有隐式转换的
     	          3default可以省略不写的,但是不推荐,如果不写,条件都不满足的情况,则什么事儿都不会发生
     		
     面试题:if vs switch的区别?
     	1switch...case:优点:执行效率较高,速度快(他比较时,case做的不是范围查找,而是等值比较)
     		             缺点:必须要知道最后的结果是什么才可以使用
     	2if...else...:       优点:可以做范围判断
     		             缺点:执行效率较慢,速度慢(他做的是范围查找)
     	建议:代码开发完过后,要做代码优化,要尽量的少用if...else...,多用switch...case和三目运算
    
     3、三目运算:简化分支的
     	语法:
     		条件?操作1:默认操作;		===		if...else... 
    
     		条件1?操作1:条件2?操作2:默认操作	===		if...else if..else
    
     	注意:
     		1、默认操作不能省略,省略了会报错
     		2、如果操作复杂,不能使用三目运算:操作只能有一句话,如果操作有多句话还是推荐使用switchif
    

之前了解到隐式转换:多半都是再运算符中悄悄的变化的,但是隐式转换总有力不从心的时候:"15px"*2=NaN

*****页面上一切数据js获取到后都是字符串类型

2、***强制(显示)数据类型转换:

1、转字符串:2种方法

	1var str=x.toString();//x不能undefined和null,会报错,undefined和null不能使用.做任何操作,因为他们不是对象
            
	2var str=String(x);//万能的,任何人都可以转为字符串,但是绝对不要手动使用,完全等效于隐式转换,还不如+""
	不重要:页面上一切数据js获取到后都是字符串类型

2、***转数字:3种方法
	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(".35px"));//NaN
		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"));//3
		console.log(parseFloat("hello35"));//NaN
		console.log(parseFloat("35.5px"));//35.5
		console.log(parseFloat(".35px"));//0.35
		console.log(parseFloat("35.5.5"));//35.5

	3Number(x);//万能的,任何人都可以转为数字,但是绝对不要手动使用,完全等效于隐式转换,还不如 -0 *1 /1

3、转布尔:
	Boolean(x);//万能的,任何人都可以转为布尔,但是绝对不要手动使用,完全等效于隐式转换,还不如 !!x
	***只有6个为false0,"",undefined,NaN,null,false,其余全部都是true。
	我们绝对不会手动使用,但是再分支或循环的条件之中,不管以后代老师写什么,他都会悄悄的转为一个布尔值,自带此方法
	不管代老师写什么,你只需要判断为不为这六个,不为这六个就是true,为这六个就是false
          

Day03

1、循环结构: 问题:在控制台打印输出1000句hello world? console.log("1hello world"); ... console.log("1000hello world");

生活中的循环?
	吃饭
	睡觉
	上课
	上班
	自转
	公转
	活着

循环结构:反复执行 相同 或 相似的操作
	循环三要素:
		1、循环条件:开始 - 结束,循环的次数
		2、循环体:做的操作是什么
		3、循环变量:记录着我们当前在哪一次,而且他会不断的变化

2、3种循环:

1、while循环:
              语法:
		var 循环变量=几;
		while(循环条件){
			循环体;
			循环变量变化;
		}
	执行原理:首先创建出循环变量,判断循环条件,如果条件满足,则做一次循环体操作,并不会退出循环,而会回过头再次判断循环条件满不满足,如果满足,则做一次循环体操作,...直到条件不满足,才会退出循环
	宏观上感觉循环好像一瞬间就结束了,但是微观上循环其实是一次一次执行的

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

		2、死循环其实也会有一天停下来,如何停下来呢?
			break; - 退出整个循环,多半都是用来搭配死循环的
			continue; - 退出本次循环,下一次依然会执行
2、*for循环:和while的原理是一样的,但是他比while看上去更加的简洁,更加的舒服
	语法:
		for(var 循环变量=几;循环条件;循环变量变化){
			循环体;
		}

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

	面试题:whilefor的区别?
		whilefor在原理上几乎没有区别?
		一般来说我们不确定循环次数的时候,会使用while循环 - 死循环
		一般来说我们确定循环次数的时候,会使用for循环 - 大部分情况都是他

3do...while循环:废物:while我们都不想用,何况是更麻烦的do...while - 今日一见,不会再见
	语法:
		var 循环变量=几;
		do{
			循环体;
			循环变量的变化
		}while(循环条件)

	面试题:whiledo...while的区别?
		区别只看第一次,如果第一次条件都满足,那么两者没有任何区别。
			但是如果第一次条件不满足,那么while一次都不会执行
					             那么do...while至少会执行一次
2、***********Function的基础:
1、概念:Function 也叫函数也叫方法,【先预定义】好,以后可以【反复使用】的【代码段】

2、如何使用函数:21、定义/创建/声明:
		function 函数名(){
			函数体/代码段;
		}
		注意:函数创建后,不会立刻执行,需要我们去调用函数

	2、调用函数:21、在js内部写:函数名(); - 程序员写几次就会调用几次

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

	3、我们以后何时使用函数呢?
		1、不希望打开页面立刻执行
		2、希望用户来触发提升用户的体验感
		3、以后每一个独立的功能(作业)都要封装为一个函数
		函数在js里的地位极高,函数是js的第一等公民

	4、但是好像和我们目前见过的别的函数不太一样啊
		一阶段就已经见过函数了:
			rotate(180deg)
			url(2.jpg)
			这种写法叫做带参数的函数:21、创建出带有形参的函数 - 形参:其实就是一个变量,只不过不需要加var,而且不需要赋值,所以称之为叫做形式参数 - 简称形参
				function 函数名(形参,...){
					函数体/代码段;
				}

			2、使用带有形参的函数时,必须传入实参 - 实际参数,就是你传递过去的值
				函数名(实参,...)

			注意:传参的时候顺序是不能乱的,必须和形参的顺序一一对应,数量不多不少

	总结:
		1、不带参数的函数:用于执行一些固定操作
		2、带参数的函数:可以根据我们传入的实参的不同,做的略微不同

总结:循环也能反复执行,函数也能反复执行,他们的区别在哪里? 1、循环:几乎是一瞬间就完毕了 2、函数:需要调用后才会执行

Day04

1、***********自定义函数Function
什么是函数:需要先定义好,以后可以反复使用的一个代码段
何时使用:不希望打开页面立刻执行,以后可以反复使用,希望用户来触发...
如何使用:
    
	1、***创建:21、*【声明方式】创建函数
			function 函数名(形参列表){
				操作;
				return 返回值/结果;
			}

		2、【直接量方式】创建函数:- 不推荐
			var 函数名=function(形参列表){
				操作;
				return 返回值/结果;
			}

			感觉到函数名其实就是一个变量名

	2、调用:
		var 接住返回的结果=函数名(实参列表);

		//其实return的本意是退出函数,但是如果return后面跟着一个数据
		//顺便将数据返回到函数作用域的外部,但是return只负责返回,不负责保存,所以调用函数时要自己拿个变量来接住他
		//就算省略return,默认也有,会return一个undefined
		//*具体需不需要得到函数的结果,看你自己:如果有一天你在全局希望拿着函数的结果去做别的操作,那么记得加return
		//前辈们提供给你的方法,大部分基本上都加了return

	3、***作用域:21、全局作用域:全局变量 和 全局函数,在页面的任何一个位置都可以使用。

		2、函数作用域:局部变量 和 局部函数,在【当前函数调用时,内部可用】

		带来了变量的使用规则:优先使用局部的,局部没有找全局要,全局也没有那就会报错
			特殊点:缺点:
				1、千万不要再函数中对着未声明的变量直接赋值 - 全局污染:全局本身没有这个东西,但是被函数作用域给添加上了
				2、局部可以使用全局的,但是全局不能使用局部的 - 解决:看上面return

	4、***声明提前: - 只会出现在鄙视之中
		规责:在程序正式执行之前,将var声明的变量(轻)和function声明的函数(重),都会悄悄的集中定义在当前作用域的顶部,但是赋值留在原地
		
		强调:
			声明方式创建的函数会完整的提前(第一种)
			直接量方式创建的函数不会完整提前,只有变量名部分会提前(第二种)

		何时使用:永远不会自己使用,干扰我们判断的 - 只会在笔试中遇到,为什么平时开发不会遇到呢?
			只要你遵守以下规则:
				1、变量名和函数名尽量不要重复
				2、先创建后使用

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

2、************数组的基础:
问题:保存1000个同学的名字?
	var name1="钟清翰1";
	...
	var name1000="钟清翰1000";

	不推荐,变量其实就是我们所谓的内存,变量创建的越多,那么我们的内存空间消耗越大,那么网站性能就会越差

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

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

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

	2、获取数组之中的元素:
		数组名[i]

	3、后续添加/替换元素:
		数组名[i]=新数据;
		如果下标处没人则为添加,如果下标处有人则为替换

	4、数组具有三大不限制
		1、不限制元素的类型
		2、不限制元素的长度
		3、不限制下标越界 - 不是一个好东西
				如果获取元素时,下标越界,返回的是一个undefined
				如果添加元素时,下标越界,会得到一个稀疏数组,如果我们搭配上我们学过循环去遍历获取每个元素,那么你会得到很多很多undefined

	问题:自己数下标,难免会数错,导致我们下标越界
	5、解决:数组中有一个唯一的属性:length
		语法:数组名.length
		作用:获取到数组的长度,长度是从1开始的

		三个固定套路:
			1、向末尾添加元素:arr[arr.length]=新数据;
			2、获取数组的倒数第n个元素:arr[arr.length-n];
			3、缩容:删除倒数n个元素:arr.length-=n;

	6、***遍历数组:往往很多情况,我们不会拿出某个元素来使用,而是拿出每个元素来进行 相同 或 相似的操作 - 搭配上循环
		固定公式:
			for(var i=0;i<arr.length;i++){
				arr[i];//当前次元素
			}

Day05

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

2、DOM树概念:DOM将我们的HTML看作了是一个倒挂的树状结构,但是树根不是html标签,而是document的对象

document对象:不需要我们程序员创建,由浏览器的js解释器自动创建,一个页面只有一个document树根

作用:可以通过树根找到我们想要的任何一个DOM元素/节点/对象(属性和方法) DOM会将页面上的每个元素、属性、文本、注释等等都会被视为一个DOM元素/节点/对象

3、查找元素:两大方面: 1、直接通过HTML的特点去查找元素

1、通过 ID 查找元素:
var elem=document.getElementById("id值");
		特殊:
			1、返回值,找到了返回当前找到DOM元素,没找到返回的一个null
			2、如果出现多个相同id,只会找到第一个
			3、记住控制台输出的样子,这个样子才叫做一个DOM元素/节点/对象,才可以下午去做操作
			4、忘记此方法,不允许使用,id不好用,一次只能找一个元素。id留给后端用
			5、其实根本不用查找,id直接可用
2、*通过 标签名 查找元素:
		var elems=document/已经找到的父元素.getElementsByTagName("标签名");
		特殊:
			1、返回值,找到了返回一个类数组DOM集合,没找到得到空集合
			2、js只能直接操作DOM元素,不能直接操作DOM集合,解决:要么下标拿到某一个,要么遍历拿到每一个。
			3、不一定非要从document开始查找,如果从document去找,会找到所有的元素,可以换成我们已经找到的某个父元素
3、*通过 class 查找元素:
		var elems=document/已经找到的父元素.getElementsByClassName("class名");
		特殊:
			1、返回值,找到了返回一个类数组DOM集合,没找到得到空集合
			2、js只能直接操作DOM元素,不能直接操作DOM集合,解决:要么下标拿到某一个,要么遍历拿到每一个。
			3、不一定非要从document开始查找,如果从document去找,会找到所有的元素,可以换成我们已经找到的某个父元素
2、*通过关系去获取元素:前提条件:必须先找到一个元素才可以使用关系
	父元素:elem.parentNode; - 单个元素
	子元素:elem.children; - 集合
	第一个子元素:elem.firstElementChild; - 单个元素
	最后一个子元素:elem.lastElementChild; - 单个元素 
	前一个兄弟:elem.previousElementSibling; - 单个元素 
	后一个兄弟:elem.nextElementSibling; - 单个元素

	为什么要通过关系去找元素呢?不希望影响到别人,只希望影响到自己的关系网
4、操作元素:前提:先找到元素,才能操作元素,3方面
1、内容:
	1、*elem.innerHTML - 获取和设置开始标签到结束标签之间的内容,支持识别标签的
		获取:elem.innerHTML;
		设置:elem.innerHTML="新内容";

	2、elem.innerText - 获取和设置开始标签到结束标签之间的纯文本,不识别标签的
		获取:elem.innerText;
		设置:elem.innerText="新内容";

	以上两个属性都是专门为双标签准备,而有一个单标签也是可以写内容,叫做<input />,我们如何获取?
	3、input.value - 专门获取/设置input里的内容
		获取:input.value;
		设置:input.value="新内容";
2、属性:
	获取属性值:elem.getAttribute("属性名");
	设置属性值:elem.setAttribute("属性名","属性值");

	简化版:
		获取属性值:elem.属性名;
		设置属性值:elem.属性名="属性值";
		简化版的缺点:
			1class必须写为className - ES6(2015年)class变成了一个关键字
			2、不能操作自定义属性
3、样式:
	使用样式的方式:3种
		1、*内联样式
		2、内部样式表
		3、外部样式表 - 一阶段做开发用的都是外部样式表
	
	二阶段我们就要用js来操作【内联样式】
		1、不会牵一发动全身
		2、优先级最高

	获取样式:elem.style.css属性名;
	设置样式:elem.style.css属性名="css属性值";
	特殊点:
		1、css属性名,有横线的地方,去掉横线,变为小驼峰命名法
			border-radius     -----  borderRadius
		2、小缺陷:获取时,我们只能获取到内联样式,因为我们目前学的就是内联样式的操作
4、绑定事件:
	elem.on事件名=function(){
		操作;
		*****关键字this - 这个
			如果单个元素绑定事件,this->这个元素
			如果多个元素绑定事件,this->当前触发事件元素
	}

一切的获取,往往都是为了判断
一切的设置,可以说是添加也可以说是修改

Day06

1.***数组(填坑):

1.创建数组:

 1.直接量:var arr=[值1,...];

 2.构造函数:var arr=new Array(值1,...);

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

2.***面试题:按值传递:var a=x; var b=a;修改a, b变不变,或者修改b, a变不变,传递的如果是原始类型:其实就是复制了一个副本给对方,两者互不影响. 传递的如果是引用类型:js中不是原始类型,就是引用类型(函数、数组,都是引用类型)-这是浅拷贝因为引用类型很大,比原始类型大的多,不可能保存在变量本地,只是保存了一个地址值而已,其实是赋值了自己的地址值给对方,两者用的是同一个地址值,一个修改另一个也会变化

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

在js底层有一个垃圾回收器,只有垃圾回收器的计数器(记录者这个数据有几个人一用着)为0的时候才会删除不要的数据

建议:我们的代码都要封装为一个函数,函数中的一切变量都会自动释放

索引数组:下标是数字组成的数组

2.***hash数组(关联数组):下标是可以自定义的,为什么:索引数组的下标无具体的意义,不便于查找

如何使用:1.创建:2步

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

2.访问元素:arr["自定义下标"];

3.强调:hash数组的length失效了,永远为0! 遍历hash数组:不能使用for循环,必须使用for in循环-其实for in 代老师不爱称呼他叫做一个循环,因为它不需要设置从哪里开始到哪里结束,纯自动化的,专门为了遍历hash数组存在的

    for(var i in 数组名){
        i;//下标
        数组名[i];//当前次元素
    }
4.***hash数组的原理:
    hash算法:将字符串,计算出一个尽量不重复的数字(地址值),字符串内容相同,则计算出来的数字一定也是相同的

    添加元素:将自定义下标交给hash算法,得到一个数字(地址值),直接将你要保存的数据放到此地址保存起来
    获取元素:将指定的自定义下标交给hash算法,得到一个和当初保存是一样的数字(地址值),通过地址找到你当初保存的数据,取出来用
    

5.js里面一切的东西都是对象,万物皆对象,除了undefined和null,[一切对象的底层都是hash数组]学习这块最大的目的:为了对象做准备工作

3.******数组的API:前辈们预定义了很多方法,等待我们学习,我们程序员只需要学会如何使用,就可以直接用上-这些方法只有数组可用
1.*arr转str:
var str=arr.join("自定义连接符")
固定套路:2个
    1.笔试题:将数组里面的内容拼接成一句话/单次-无缝拼接,其实就是拼接了一个空字符串
    var arr=["h","e","l","l","o"," ","w","o","r","l","d"];
    var str=arr.join("");
    console.log(str);
    
    2.将数组拼接成DOM页面元素-第一次遇到数据渲染页面
    
    //拿数据
    
    var arr=["-请选择-","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆"]
    
    //将数组拼接为页面标签字符串
    var str="<开始标签>"+arr.join("</结束标签><开始标签>")+"</结束标签>";
    //巧了:innerHTML能够识别标签
    sel.innerHTML=str;
    
    
2.*数组拼接:添加元素的新方式
将你传入的实参全部拼接到arr的末尾
var newArr=arr.concat(新值1,arr1,...);
    特殊:
    1.不修改原数组,只会返回一个新数组
    2.concat支持传入数据参数,悄悄的将你传入的数组打散为单个元素在拼接
    
3.*截取子数组:
根据你传入的开始下标截取到结束下标
var subArr=arr.slice(starti,endi+1);
特殊:1.不修改原数组,只会返回一个新数组
    2.含头不含尾
    3.endi可以省略不写,如果省略,会从starti位置一直截取到末尾
    4.starti也可以省略,如果两个实参都省略,那么会从头到尾完全的复制一份:此操作也叫做深拷贝-复制了一个副本给对方
    5.支持负数参数,-1代表倒数第一个
  

以上的api都是不修改原数组的

以下的api都是会修改原数组

4.*删插替

删除:var dels=arr.splice(stari,n);//代表删除的个数
特殊:虽然他直接修改原数组,但是也有返回值,返回的是被删除的数据组成的一个新数组,因为前辈们考虑到有可能删除的东西刚好是需要的东西,哪怕没有删除也会返回一个空数组

插入:arr.splice(starti,0,新值,...);

特殊:删除的个数和插入的个数不必相同

5.翻转数组:arr.reverse();今日一见,此生无缘

6.数组排序:

    1.笔试题:冒泡排序:前一个元素和后一个元素进行对比,如果前面一个>后一个,两者就交换位置,但是昨晚一轮发现只有最大的一个数字到了最后,所以再开循环反复使用,固定公式:
    
 var arr=[31,21,54,4376,69,8,8,65,643,52,3,321,5,47,69,87,643,524];
		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);
    console.log(arr)
2.正式开发时:arr.sort();

默认:将数组中的元素转为字符串后,在按位PK每个字符的unnicode号(ASCII码)

问题1:希望按照数字升序排列:

arr.sort(function(a,b)){//此函数叫做匿名回调函数,回调函数不需要我们程序员调用,由前辈们创建好,我们学习如何使用即可,其实前辈们的sort方法悄悄的帮助我们调用了

    //  console.log(a);//后一个数字
    //  console.log(b);//前一个数字
    return a-b;//如果a-b返回的是一个正数,说明后一个>前一个
    //如果a-b返回的是一个负数,说明后一个<前一个
    //如果a-b返回的是一个0,说明后一个==前一个
    //而sort方法会根据你返回的正数/负数/0,来自动考虑要不要交换位置

} 问题2:希望按照数字降序排列:

                arr.sort(function(a,b){
                return b-a;
})

强调:切记:1.以后只要网页上有功能带有排序,他的底层一定是数组,因为js中只有数组可以排序

2.以后网页上有随机的功能,那么他的底层一定是用到了随机数公式!

Day07

1.*****数组的API:

1.栈和队列:添加元素和删除元素的新方式:

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

何时:现实生活中,情况不多:电梯、旅游巴士...优先使用最新的数据

如何使用:开头进:arr.unshift(新值,...);//添加元素的新方式,向前添加,缺点:导致其余元素的下标都发生变化

开头处:var first=arr.shift();//删除元素的新方式,向前删除,一次只能删除一个,缺点:导致其余元素下标都发生变化

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

结尾处:var last=arr.pop();//删除元素的新方式,向后添加

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

开头进:arr.unshift(新值,...);
结尾处:var last=arr.pop();
结尾进:arr.push(新值,...);
开头出:var first=arr.shift();

2.二维数组:数组的元素,又引用着另一个数组

何时:在一个数组,希望再次细分每个分类

创建:

var arr=[   ["预判",18,900],
   ["王刘振",19,1000],
   ["干湿长",20,700]
   ];

访问:arr[行下标][列下标]; 特殊:列下标越界,返回undefined

行下标越界,得到是一个报错,因为行下标越界已经得到一个undefined,undefined没有资格在加[]做操作

遍历二维数组:必然两层循环,外层循环控制行,内层循环控制列

for(var r=0;r<arr.length;r++){
for(var c=0;c<[r].length;c++){
        console.log(arr[r][c]);
    }
}

3.Es5还提供了3组6个API:

1.判断:2个

  every:每一个,要求所有元素都满足条件才会为true,只要有一个不满足则为false,非常类似于我们的&&
  var bool=arr.evry(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){
直接做你想要的操作;
})
map-不修改原数组返回一个新数组
var newArr=arr.map(function(val,i,arr){
return 直接做你想要的操作;
})

3.过滤和汇总

    过滤:筛选出你需要的部分,但是和现实不一样的是原数组并不会发生变化
    var subArr=arr.filter(function(val,i,arr){
    return 判断条件;
    })
    
    汇总:
    var result=arr.reduce(function(prev,val,i,arr){
    return prev+val;
    
    },基础值)
ES6箭头函数:简化一切匿名回调函数的:

固定公式:function去掉.()和{}之间添加=>,如果形参只有一个,那么{}可以省略,如果函数体只有一句话,那么{}也可以省略,如果函数体只有一句话并且是return,那么return和{}都可以省略

4.**********String的概念:

什么是字符串:多个字符组成的【只读】字符【数组】(只读:明天学习的所有字符串的API都不会修改原字符串,都会返回一个新的字符串)

和数组有相同点:

    1.字符串中的个数:str.length;
    2.获取字符串中的某个字符:str[i];
    3.遍历字符串
    4.所有数组不修改原数组的API,字符串也可以使用(concat、slice)
    

和数组也有很多不同的地方:

所有数组的直接修改原数组的API,字符串都不可以使用,比如排序只有数组可以使用,但是字符串也有很多属于自己的API

***引用/对象类型:11个

*String Number Boolean->包装类型
*Array *Function Date(日期) Math(数学) *RegExp(正则:验证)
Error(错误)  *Object(面向对象开发方式)
Global(全局对象)-只有在浏览器中被window对象代替了,自然保存着全局变量和全局函数,只不过window可以省略不写,有一天我们去学习node.js这个后端语言,而在node.js中全局对象就叫做global

***包装类型:抓们用于将原始类型的值封装为一个引用类型的对象的

为什么:原始类型的值原本就是没有任何属性和方法,意味着原始类型本身是不支持去做任何操作的

但是前辈们发现字符串经常会被我们程序员所操作,为了方便我们程序员将这三个提供了包装类型(提供了属性和方法)

何时使用:只要你试图使用原始类型的变量调用属性和方法的时候,自动包装
何时释放:方法调用完毕后,自动释放包装类型,又变成了原始类型

为什么null和undefined不能使用-因为前辈们没有为他们提供包装类型

扩展:

1.周期性定时器:每过一段时间就会自动执行一次
开启:timer=setInterval(function(){
    操作;
},间隔毫秒数)
停止:clearInterval(timer)

2.鼠标移入:onmouseover

3.鼠标移出:onmouseout

Day08

1.*****StringAPI:就是一些只有字符串可以使用的函数,不需要我们创建,直接使用

    1.转义字符串:\
    作用:1.将字符串中和程序冲突的字符转为原文
    "\"" '\"
    2.包含特殊功能的符号:
    换行:\n    制表符:\t->大空格,和tab按键一样
    3.*输出unicode编码的字符:
    \u4e00-ascii码:19968
    \u9fa5-ascii码:40869
2.*大小写转换:将字符串中的每个字符统一转为大写或小写
    何时:只要程序不区分大小写,就要【先统一】的转为大写或小写,再比较(做验证码)
    如何:大写:var upper=str.toUpperCase();
         小写:var upper=str.toLowerCase();

3.获取字符串中指定位置的字符:str.charAt[i]===str[i];

4.*获取字符串中指定的字符的ASCII码:
    var ascii=str.charCodeAt(i);

*通过ascii码转回原文:

var 原文=String.fromCharCode(ascii);
5.******检索字符串:检查索引,检查下标:获取关键字的下标
var i=str/arr.indexOf("关键字",starti);

从starti位置开始,查找右侧第一个关键字的第一个字符的位置

starti可以省略,默认从0位置开始查找

*返回值:找到了,返回第一个关键字的第一个字符的下标位置

没找到,返回-1,其实我们根本不关心下标为多少,我们只关心下标为不为-1

作用:判断有没有!

强调:数组也能使用此方法,数组这个方法是后期加上的,原本此方法只有字符串可用,老版本ie不支持

笔试题:默认只能获取到第一个关键字的下标,如何才能获取到所有关键字的下标?

    var str="no zuo no die no can no bibi no"
    
    varindex=-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);//用法和数组的用法完全一致

str.substring(starti,endi+1);//用法极狐和slice一致,但不支持负数参数

str.substr(starti,n);//n代表的是截取的个数,不必考虑含头不含尾

8.*替换字符串:本生这个方法非常的强大,但是由于我们暂时还不会正则表达式,所以只能替换固定的关键字,暂时比较差

var newStr=str.replace("固定关键字"/正则表达式,"新内容");
9.*******切割/分割/分隔字符串:
作用:将字符串转为数组:
var arr=str.split("自定义切割符");

注意:1.切割后,切割符就不存在了

2.如果你的切割符写的是"",切散每一个字符
   

10.去掉空白字符:str.trim/trimstart/trimEnd()

扩展:js如何创建元素:3步

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

2.为其设置必要的属性和时间:

 elm.属性名="属性值";
 elem.om事件名=function(){操作}
 

3.挂载上树/渲染页面:父元素.appendChild(elem);

Day09

1.Math对象:专门提供了数学计算的API

强调:不需要创建,直接使用

属性:Math有一些属性, 设计到科学计数法,但是几乎用不到,只有Math.PI有可能用到===3.1415926...这一串数字我们不需要自己创建,浏览器自带  

API:

1.取整:3种

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

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

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

var num=Math.floor(num);

3.四舍五入取整:

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

以上方法都是垃圾,都在取整,只能取整

//*num.toFixed(d);优点1.可以四舍五入,并且保留指定小数位数,d其实就是保留小数位数

2.解决浏览器自带的舍入误差,2-1.6=0.3999999这种情况就可以使用toFixed来解决
        
缺点:结果是一个字符串,建议搭配上parseFloat()使用

//笔试题:不允许使用toFixed的情况下,自己封装一个函数,由用户传入数字和保留位数,实现四舍五入操作:

function toFixed(num,d){
    num*=(10**d);
    num=Math.round(num);
    num/=(10**d);
    return num;
}
var reslut=toFixed(Math.PI,2);
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之间取一个随机的小数
搭配上parInt,只能取到0,但是不可能取到1,意味着娶不到最大值
    公式:parseInt(Math.random()*(max-min+1)-min)
    强调:只要以后网页中mou一块有随机的功能,他的底层一定是用到了随机数
    

2.Date对象:日期对象,提供了操作日期和时间的API

创建:4种
1.*创建一个当前日期:
    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代表14.复制一个日期:
为什么:目前的所有API都是直接修改原日期的,无法获得修改之前的日期,所以,在执行API之前先进行复制,然后再操作复制后的日期
var end=Date(start);

使用:21.两个日期对象之间,可以相减(大-小),得到一个毫秒差,换算出自己想要的任何一部分-日期的本质其实就是保存了一个毫秒数-做倒计时关键
创建日期的最后一种方式,绝对没人用:var date=new Date(毫秒数);//计算机元年:1970118点整

2.API:

分量:时间的单位
年月日星期:FullYear Month Date Day
时分秒毫秒:Hours Minutes Seconds Milliseconds
每一个分量都有一对儿getxxx/setxxx的方法
其中getxxx负责获取一个分量的值
其中setxxx负责设置一个分量的值

特殊:
1.取值范围:

FullYear-当前年份的数字
Month-0~31
Date-1~31
Day-0~6:0代表是星期天,外国人的眼里星期天才是一周的开始
Hours-0~23
Minutes、Seconds:0~59
日期你知道了取值范围,如果你还故意设置拆除范围,他很聪明,会自动进制

2.Day 没有set方法

3.如果希望对某个分量进行加减操作:
date.setxxx(date.getxxx()+/-n)

4.格式化日期为本地字符串
date.toLocalString();-垃圾:具有兼容性问题,我们一般会选择自己创建一个格式化方法来格式日期
用了此方法会失去一些东西:日期的自动进制、日期的API
但是你也会获得一些东西,字符串的API

3.定时器:

1.周期性定时器:每过一段时间就会执行一次,先等后做

开启:timer=setInterval(callback,间隔毫秒数);
停止:clearInterval(timer);

2.一次性定时器:等待一段时间,只会做一次就结束

开启:timer=setTimeout(callback,间隔毫秒数);
停止:clearTimeout(timer);

同步技术:代码必须一行一行的执行,前面没做完,后面就等着,定时器是我们第一次见到的异步技术:无论我这块代码多耗时,也不会卡住后续代码

Day10

1.BOM:Browser Object Model-浏览器对象模型:专门用于操作浏览器的,但是它使用的不多,远不如ES和DOM,浏览器很多操作都是自带的,而且BOM没有标准,各个浏览器都有自己的定义,但大部分都是一直的,除了老版的ie8

2.window对象:扮演者两个角色: 1.全局对象:保存着全局变量和全局函数

2.指代当前窗口本身:

属性:
1,获取浏览器的完整大小:outWidth/outerHight
2.*获取浏览器的文档显示区域的大小:innerwidth/innerHeight-获取每台电脑浏览器的文档显示区的大小
3.获取屏幕的完整大小:跟window没关系:screen.width/height;-我们目前学习的都是浏览器应用(网页),并不会去做桌面应用

方法:
1.*打开链接的新方式:
1.当前窗口打开,可以后退:
HTML:<a href="url">内容</a>
JS:open("url","_self");

2.当前窗口打开,禁止后退:使用场景:比如电商网站,结账后不允许后退
HTML做不到了,只有JS可以,也不是window能做到的,而是另一个人

history:当前【窗口的历史记录】,他其实可以做的事儿就是前进后退
location:当前【窗口正在打开的url】,有一个API:
location。replace("新url");//叫做替换,不叫跳转,不会产生历史记录,但是网址替换了,网页必然发生变化

3.新窗口打开,可以打开多个
HTML:<a href="url"target="_blank">内容</a>
JS:open("url","_blank")

4.新窗口打开,只能打开一个:使用场景:比如电商网站,只允许用户打开结账页面
HTML:<a href="url"target="自定义一个name">内容</a>
JS:open("url","自定义一个name");

其实窗口的底层都是一个名字的,如果打开了一个已经开着的名字的窗口,他会把他关掉,再次打开

学完这块,我们了解了两个点: 1.以后的跳转,任何标签都可以 2.提升用户的体验感 3.a标签的其他用途:

1.跳转
2.锚点
3.下载按钮:<a href="xx.exe/rar/zip/7z">下载</a>
4.打开图片和txt文档:<a href="xx.png/jpg/jpeg/gif/txt">打开图片和txt</a>
5.直接书写js-不需要绑定点击事件:<a href="JavaScript:js代码;">打开图片和txt</a>

2.打开窗口/新链接:newW=open("url","target","width=?,height=?,left=?,top=?");

特殊:1.如果没有加第三个参数,那么窗口会和浏览器融为一体
2.如果添加了第三个参数,那么窗口会脱离浏览器独立存在

3.关闭窗口:window/newW.close();

4.改变新窗口的大小:newW.resizeTo(新款,新高);

5.改变窗口的位置:newW.moveTo(新x,新y);

6.*window提供了三个框:

警告框:alert("警告文字");
输入框:var user=prompt("提示文字");
确认框:var bool=confirm("提示文字");

7.******定时器也是window的

8.事件:

 1window.onload事件 - load - 加载:等待其他所有的资源加载完毕后才会执行的代码,放在里面的代码其实要最后才会执行
		
 2、*window.onresize事件 - 窗口如果大小发生了变化,就会触发,搭配上判断innerWidth可以理解为是js版本的css媒体查询
3、***window.onscroll事件 - 滚动事件,一旦滚动就会触发
			1、获取滚动条当前的位置:window.scrollY
			2、获取元素距离页面顶部有多远:elem.offsetTop/offsetLeft

9、*****本地/客户端存储技术:

		cookie:淘汰了,存储的大小只有2kb,而且操作极其麻烦,尤其要到处切割,只能最多保存30天
		webStorage:H5带来了一个新特性,存储的大小有8mb,永久保存,而且非常简单
			分类2种:
			1、sessionStorage - 会话级,只要浏览器一旦关闭,数据就会死亡了
			2、localStorage - 本地即,只要你不清空,就会永久存在
			两者的用法是一模一样的,不用创建,直接可用
			操作:

			1、添加:xxxStorage.属性名="属性值";
			2、读取:xxxStorage.属性名;
			3、删除:xxxStorage.removeItem("属性名");
			4、清空:xxxStorage.clear();

Day11

BOM的常用对象

1.history对象:保存了当前窗口的历史记录(过去的url)

前进:history.go(1);
后退:history.go(-1);
刷新:history.go(0);

2.***location 对象:保存了当前窗口的正在打开的url(现在的url)

***程序员尝试:一个url由几部分组成?分别每个部分有什么用?-以后在学习服务器端和数据库的时候会有很大的帮助

五大部分:

1.协议:*http(加密)/*http(未加密)/ftp(传输文件)/ws(直播)...前两个属于叫做请求-响应模型
2.主机号|IP地址|域名:域名是需要花钱购买的,主机号|IP是免费的,127.0.0.1|才是真正的主机号,只能自己访问自己
3.端口号:http默认端口为443,http默认端口为80,只有默认端口可以省略不写
4.***文件的相对路径|路由:百度加密了
5.***查询字符串|请求消息:前端传输到后端的东西,前端对后端说的话,就是form表单提交带来的东西

http://127.0.0.1:8020/js_core_day06/01history%E5%AF%B9%E8%B1%A1-3.html

https://www.baidu.com/s?wd=%E5%80%9A%E5%A4%A9%E5%B1%A0%E9%BE%99%E8%AE%B0&rsv_spt=1&rsv_iqid=0x960794a60003bf35&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=44004473_20_oem_dg&rsv_enter=1&rsv_dl=tb&rsv_sug3=15&rsv_sug1=8&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&inputT=4197&rsv_sug4=4198

 属性:
 获取url的5个部分的内容,但是不需要记忆,直接输入location对象即可查看
     
     协议:location.protocal;
     域名:location.hostname;
     端口:location.port;
     路由:location.pathname;
     请求消息:location.search;
     
 跳转:location=新"url"-替换当前窗口,可以后退
 跳转后,禁止后退:location.replace("新url")-替换当前窗口,禁止后退
 刷新:location.reload();

DOM:原本是可以操作一切结构化文档的,但是在某一个升级后,为了方便各类程序员将DOM分为了3方面:

1.核心DOM:无敌的,既可以操作HTML又可以操作XML,但是语法相对比较繁琐
2.HTMLDOM:只可以操作HTML,不能访问一切自定义的东西,但是语法简单
3.XML DOM:只可以操作XML,被淘汰了,被JSON数据格式代替了

1.查找元素

1.通过关系找元素

2.直接找元素:

1.document.getElmentsByxxxxx();-返回的是一个动态集合HTMLCollection
2.2个重点:
    
    1.var elem=document.querySelector("任意css选择器的");//query-查询  selector-选择器:查询css选择器
    
    缺陷:只能找到单个元素,如果匹配到了多个,也只会返回第一个,没有找到就是null
         一次只能操作一个元素,不舒服
    2.***var elems=document.querySelectorAll("任意css选择器的");
    优点:
    1.找到了是一个集合,没找到是一个空集合
    2.复杂查找时,非常简单
    3.返回的是一个静态集合NodeList

面试题:document.getxxx和document.queryxxx的区别?

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

2.操作样式:

1.内联样式
2.样式表样式-此生不见
//获取你想要操作的样式表
var sheet=document.styleSheet[i];

//获取此样式表中所有的样式规则
var rules=sheet.cssRules;

//数出你想要操作的那个规则的
var rule=rules[i];

//操作
console.log(rule.style.css属性名)
rule.style.css属性名="css属性值";

3.操作属性:

1.*获取属性值:
核心DOM:elem.getAttributte("属性名");
HTML DOM:elem.属性名;

2.*设置属性值:
核心DOM:elem.setAttribute("属性名","属性值");
HTML DOM:elem.属性名="属性值";

3.删除属性:设置属性值为空字符串,确实某些属性可以算是删除,但是只是删除了属性值,属性名还在,而有的属性哪怕只有一个属性名,也会具有作用(比如:href、disabled、readonly)

核心 DOM:elem.removeAttribute("属性名");
HTML DOM:elem.属性名!="";

以后建议:优先HTML DOM,HTML DOM实现不了的再用核心DOM补充!

缺陷:
    
    1.class必须写className
    2.自定义的东西操作不了
    

4.操作内容-innerHTML/innerText/Value

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

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

2.为其设置必要的属性和时间
elem.属性名="属性值";
elem.on事件名=function(){}

3.上树:3种
    
    *父元素:appendChild(elem);//在父元素末尾处追加一个子元素elem
    父元素.insertBefore(elem,已有子元素);//在父元素追加一个子元素elem,但是放在已有子元素的前面
    父元素.replaceChild(elem,已有子元素);//在父元素追加一个子元素elem,但是会替换掉已有子元素

6.删除元素:elem.remove();

扩展:1.创建变量:新增的一个let关键字:

let 变量名=值;
作用:
1.解决了申明提前
2.带来了块级作用域,一个{}就是一个块,此变量只能在那个{}里面使用
3.如果用let去当做下标绑定事件,那么他会记录着你当前元素的下标,不再需要自定义下标了-其实forEach的那个形参i就是let创建的

2.类数组转变为普通数组:接住=Array.from(类数组对象);

Day12

1.递归:简单来说就是函数之中在一次调用了函数自己,迟早一天会停下来;

何时:专门用于【遍历层级不明确的情况】-DOM树和数据(children只能找到儿子层,找不到孙子层)

如何使用:2function 函数名(root){
    1.第一层要做什么直接做
    2.判断有没有下一层,如果有下一层则再次调用此方法,只不过传入的实参是自己的下一层
    }
    函数名(实际的根)
    
    算法:深度优先!优先遍历当前节点的子节点,子节点遍历完毕才会跳到兄弟节点
    
    缺陷:不要过多使用,性能相对较差,同事开启大量函数调用,浪费内存,我们只在一个情况下使用【层级步明确】

递归vs纯循环 递归:优点:简单易用 缺点:性能较低 纯循环:优点:几乎不占用性能 缺点:难的一批

2.绑定事件:3种方式

1.在HTML上书写实践属性 <elem on 事件名="函数名(实参)">

缺点:1.不符合内容与样式与行为分离原则
2.无法动态绑定,一次只能绑定一个元素
3.不支持绑定多个函数对象

2.*在js中使用事件处理函数属性

elem.on 事件名=function(){操作}

优点:1.符合内容与样式与行为的分离原则
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.符合内容和样式与行为的分离原则
2.动态绑定
3.支持绑定多个函数对象

缺点:有兼容性问题

扩展:1.select&option只有他们可以简化创建元素&上树:

select.add(new Option("innerHTML","value"));
    

Day13

属于BOM:重点只有两个:定时器+event(事件对象);

1.事件周期:从事件发生,到所有事件处理函数执行完毕的全过程

3个阶段:

1.捕获阶段:由外向内,记录要发生的事件有哪些
2.目标优先触发:目标元素->当前点击的实际发生事件的元素
3.冒泡触发:由内向外,依次执行我们之前记录着的要发生的事件

2.*****获取事件对象event:

主流:会自动作为事件处理函数的第一个形参传入
老IE:event;-老IE全局有这个变量
兼容:event

得到了时间对象event可以做什么
1.获取鼠标的坐标:
获取鼠标相对于屏幕的坐标:e.screenX/Y
获取鼠标相对于窗口/客户端/文档显示区域的坐标:e.clientX/Y
*获取鼠标相对于网页的坐标:e.pageX/Y
    
2.阻止事件冒泡-笔试题
主流:e.stopPropagation();
老IE:e.cancelBubble=true;
兼容:e.canceBubble=true;

3.*****利用冒泡/事件委托-开发中常用,提升网页性能,有了它我们的事件函数可以换为箭头函数

    优化:如果多个子元素定义了相同或相似的事件操作,最好只给父元素定义一次
    为什么:每一次绑定一个事件函数,其实都是创建了一个事件对象,创建的事件对象越多,网站性能就越差
    淘汰了this,当前元素
    
    认识一个目标元素target:你点击的是哪一个,他永远是哪一个,不会变化
    
    主流:e.target;
    老IE:e.srcElement;
    兼容:e.srcElement

4.阻止浏览器的默认行为:比如a标签默认就可以跳转,提交按钮可以提交表单,右键自带一个弹出框,F12自带一个控制台,F11自带全屏功能,F5自带刷新功能

    主流:e.preventDefault();
    老IE:e.returnValue=false;
    兼容:e.returnValue=false;
    
    新事件:
    1.右键事件-window.oncontextmenu
    2.键盘事件:一般说用于游戏开发较多+都要搭配上键盘的键码
    window.onkeydown-按下和按住,任何键盘按键都可以触发
    
    window.onkeypress-按住和按下,只有字母、数字、回车、空格可以触发,其他按键不行
    
    window。onkeyup-松开,任何键盘按键都可以触发(比手速游戏)
    

5.获取键盘的键码:

e.keyCode;-可以获取到你按了那个键,每个键都有自己对应的键码,但是不需要要记忆,要么输出看,要么百度搜表

event可以说是BOM之中最最重要的一个点,其他点就算你不用,你也知道,笔试面试只考BOM多半都是event

扩展: 鼠标跟随效果:1.图片的加载速度,比js的执行速度较慢,所以js执行完,图片可能还没有加载完,解决:img.onload事件-图片加载完毕后才会执行的代码 2.图片隐藏了,就应该删除掉

如果事件委托了: 1.如何判断目标元素是什么标签:xx.nodeName;-得到标签名,只不过是全大写组成的 2.事件处理函数可以写为箭头函数->this就会失效,所以必须使用目标元素target 3.this的指向 1.单个元素绑定事件,this->这个元素 2.多个元素绑定事件,this->当前元素 3.箭头函数,this->外部对象

Day14

1.事件的取消绑定

1.如果使用elem.onclick=()=>{},那么elem,onclick=null

2.如果你使用elem,addEventListener("事件名",回调函数);那么:
elem.removeEventListener("事件名",回调函数);-事件名和回调函数,必须和添加时的一模一样

2.***this的指向:非常多

单个元素绑定事件this->这个元素
多个元素绑定事件this->当前元素
箭头函数中的this->外部对象
***函数中的this->当前正在调用函数的这个人
定时器的this->window

3.***ES5强制改变this的指向

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

语法:函数名.call(借用的对象,实参,...);-单独传入每个实参
函数名.apply(借用的对象,arr);-只能传入一个实参要求是一个数组,apply其实会悄悄的打散数组
强调:call/apply:相当于立刻调用函数,立即执行的

bind:永久替换了函数中的this-买
    
    3件事:
    1.创建了一个和原函数功能完全相同的新函数
    2.将新函数的this永久绑定为了指定对象,都借不走
    3.将新函数的部分参数永久固定
    语法:var 新函数=函数名.bind(永久对象,永久实参,...);-不是立刻执行,需要自己调用
    强调:bind绑定的新函数没办法被call/apply再次借走
    

以后不是自己的方法,可以用,推荐:借,白嫖!

三个固定套路:

  1.Math.max/min.apply(Math,arr)-也能支持数组参数:
  2.Object.prototype.toString.call/apply(x)==="[object Array]";//笔试题:判断xxx是不是一个数组
  3.类数组转为普通数组
      1.接住=Array.prototype.slice.call/apply(类数组对象)
      2.接住=Array.from(类数组对象)

4.ES6

1.学过了:let、const关键字、箭头函数

2.模板字符串:可以直接识别变量,不再需要+运算去拼接了,而且实现了一个简单的js环境,甚至支持在里面书写API

`我的名字叫${name}`

3.****解构赋值:

顾明思议:解析结构在进行赋值-赋值的新方式,并且得到了增强

如果赋值符号,左右两边的结构一样的,就会悄悄的解开/脱掉结构在一一进行赋值

语法:
1.类似数组的解构赋值
let[a,b,c]=[1,2,3];
console.log(a);
console.log(b);
console.log(c);

2.类似对象的解构赋值:
let{a,b=默认值,c}={c:3,a:1,b:2};
console.log(a);
console.log(b);
console.log(c);
//形参可以设置默认值,如果自己传入了,就用自己的

3.调用函数时,传递实参的顺序无所谓了
function zwjs{name,age,hobby="女"}{
return`我的名字叫${name},今年${age}岁,喜欢${hobby};
}
console.log(zwjs({hobby:"学习",age:18,name:"袍哥"}));

4.函数的返回的结果,可以有多个
function f1(){
    var a=1;
    var b=2;
    return[a,b];
}
var[a,b]=f1();
console.log(a,b);

只要以后见到:方法名({里面放着键值对就是使用了ES6的解构赋值})

4.Set和Map新的数据类型:

1.*set:类似于数组的一种数据格式-【去重数组,然后在转回数组】
    var s=new Set(arr);
    ...s-三个点扩展运算符,可以脱掉数组的外套
    一句话完成:[..new Set(arr)]-不用记忆任何API

2.Map:类似于对象的一种数据格式
    var m=new Map();
    添加:m.set("键","值");
    获取:m.get("键");
    清空:m.clear();
    删除:m.delete("键");
    

5.新的循环:垃圾

for(var v of arr){
    v;
}
缺陷:
1.没有提供任何过下标,意味着不能修改原数组
2.只能遍历索引数组,不能遍历hash数组,意味着也不能遍历对象
   

Day15

1.*****正则表达式:定义字符串中字符出现规则的表达式

如何使用:切割 替换 【验证】!

如何使用:语法:/正则表达式/

1.最简单的正则就是关键字原文"no"->/no/后缀
    后缀:g:找全部       i:忽略大小写   
2.备选字符集:/^[备选字符集]$/
强调:1.一个中括号,只管一个字符

2.问题:正则表达式默认只要满足就不管后续了,而我们做验证时,希望用户从头到尾按照我们的要求来,希望从头到尾完全匹配:
解决:前加^,后加$,两者同时使用,代表要求从头到尾完全匹配/^[备选字符集]$/-只要做验证必加!
特殊:如果备选字符集中的ascii码是连续的,那么可用-省略中间部分
比如:
一位数字:[0-9];
一位字母:[A-Za-z];
一位数字、字母、下划线:[0-9A-Za-z]
一位汉字:[\u4e00-\u9fa5]
除了xxx之外的:[^0-9] 很少使用,范围太广了

3.预定义字符集:前辈们提前定义了一些字符集,方便我们程序员-简化了备选字符集

    1.一位数字:\d===>[0-9]
    2.一位数字、字母、下划线:\w===>[0-9A-Za-z_]
    3.一位空白字符:\s
    
    一位除了换行外的任意字符:. -很少使用,范围太广了
    
    建议:优先使用预定义字符集,预定义满足不了我们再用备选字符集补充
    
    问题:不管是备选字符集,还是预定义字符集,一个都只管一位
    
 4.量词:规定一个字符集出现的次数:
 
    1.有明确数量:
        字符集{n,m}:前边相邻的字符集,至少n个,最多m个
        字符集{n,}前边相邻的字符集,至少n个,多了不限
        字符集{n}:前边相邻的字符集,至少一个,多了不限
        
    2.无明确数量:
    字符集?:前边相邻的字符集,可有可无,最多1个
    字符集*:前边相邻的字符集,可有可无,多了不限
    字符集+:前边相邻的字符集,至少一个,多了不限
    
5.选择和分组:
    选择:在多个规则中选一个
        规则1|规则2
    分组:将多个字符集临时组成一组子规则
        (规则1|规则2)
        
6.指定匹配位置
    ^:开头
    $:结尾
    特殊:两者同时使用,前加^,后加$,表示从头到尾要求完全匹配-只要你做【验证】

7.密码强度验证:2-4位,可以输入数字、字母,但是必须出现一位大写和一位数字的组合
    /^[0-9A-Za-z]{2,4}$/
    预判公式:
    (?![0-9+$])->不能全由数字组成,可能有大写、小写、汉字、日文、特殊符号...
    
    (?![a-z]+$)->不能全由小写组成,可能有数字、大写、日文、特殊符号...
    
    (?![0-9a-z]+$)->不能全由数字组成、也不能全由小写组成、也不能全由数字和小写的组合组成
    
    比如:
    /(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{2,4};//2-4位,可以输入数字、字母,但是必须出现一位大写和一位数字的组合
    /(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9a-zA-Z]{2,4}/;//必须三者都有
    /(?![0-9A-ZAa-z]+$)[0-9A-Za-z_]{2,4}/;//至少要有下划线

2.******支持正则表达式的字符串API

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

2.******替换:很有可能出现在笔试之中

1.基本替换法:
str=str.replace(/正则表达式/后缀,"新内容");
//replace支持正则,并且搭配上后缀g就可以找到全部
//缺陷:替换的新内容是一个固定的

2.高级替换法:
str=str.replace(/正则表达式/后缀,function(a,b,c){
    console.log(a);//正则匹配到的关键字
    console.log(b);//正则匹配到的关键字的下标
    console.log(c);//原字符串
    return 判断a关键字的长度,而返回不同的星星数量
})

3.格式化:身份证
    var id="500103198602215933";
    var reg=/(\d{6})(/d{4})(\d{2})(\d{2})(\d{4})/;
    
    id.replace(reg,function(a,b,c,d,e,f,g,h){
    //在replace的时候,正则出现了分组,我们会得到更多的形参
    //在形参a的后面就会出现n个形参,就看你有多少个分组
    //第一个分组获得的内容会保存在第2个形参之中
    //第二个分组获得的内容会保存在第3个形参之中
    //...
    //倒数第二个一定是下标
    //倒数第一个一定是原文本身
    })
    
    格式化:手机号
    var phone="13452390312";
    var reg=/(\d{4})\d{4}(\d{3})/;
    
    phone=phone.replace(reg,(a,b,c)=>{
        return b+"****"+c;
    })
    
    console.log(phone);
    
    总结:何时前加^后加$,何时又该添加后缀g?
        1.前加^后加$-验证
        2.替换,你希望替换所有-必须加g
3.正则对象:
创建:
1.直接两:var reg=/正则表达式/后缀;
2.构造函数:var reg=new RegExp("正则表达式","后缀";

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

QQ注册带有验证

1.每个input都应该绑定一个获取焦点事件onfocus-显示提示用户的文字
2.每个input都应该绑定一个失去焦点事件onblur-获取用户输入的东西,用自己的正则去判断,给出一个正确的提示或错误的提示
3.form记得绑定提交时间:阻止提交:return false-但是要考虑何时才阻止提交

Day16

1、animate.css文件是一个动画库:放着很多很多的动画.

如何使用:
1.打开百度:搜索animate.css得到网址
    http://www.animate.net.cn/
    https://animate.style/
    
2.下载-你们不用做这个操作

3.引入此文件

4.挑选你喜欢的动画,把class放到那个元素上

5.并且要记得设置上animation-duration:3s;执行时长

6.还需要根据不同的动画,设置不同的初始效果,才会更好看

2.滚动轮播:如果你会淡入淡出轮播,其实差不多,布局不同,动画方式不同 详见:02.html

3.无缝轮播:详见:03.html

4.swiper插件:专门的一个轮播插件,提供了HTML/CSS/JS,我们只需要复制

1.打开百度:搜索swiper
2.选你自己喜欢的

5.封装一个运动(动画)函数

Day17

1.*****Object:对象-Array/String/RegExp/Date..对象具有属性和方法,都是预定义好的,现在我们可以学习自定义对象-js是基于原型的面向对象语言

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

1.***开发方式
面向过程:过程-开始>结束,其实我们一致的开发方式都是面向过程:先干什么再干什么最后干什么

面相对象:对象(属性和方法),js有一句话万物皆对象,假设一个人是一个对象
    
    属性:身高、体重、姓名、性别、爱好、智商、情商...
    方法:吃饭、睡觉、跑步、拉粑粑、打字、上课...
何时使用面向对象:以后任何操作都要封装在一个对象之中-但是新手不太推荐,难度系数高
为什么要面向对象:现实生活中所有的数据都必须包含在一个事物之中才有意义
2.*****封装/创建/定义:封装自定义对象:3种
      1.*直接量方式::
          var obj={
          "属性名":属性值,...,
          "方法名":function(){操作},//可以简化为箭头函数
          ...
          }
       强调:
       1.其实属性名和方法名的""可以不加-暂时建议加上,以后我们要学一个数据格式JSON,他必须在键上加上""
       2.访问对象的属性和方法:
           obj.属性名;   === obj["属性名"];
           obj.方法名();  ===obj["方法名"]();
       建议使用.去访问对象的属性和方法,更简单
       ***js中一切都是对象,除了undefinednull,一切对象的底层都是hash数组
       3.访问到不存在的属性,返回undefined
       4.可以随时随地的添加新属性和新方法
           obj.属性名=新值;
           obj.方法名=function(){};
       5.如果希望遍历出对象所有的东西,必须使用for in,obj[i]才能拿到不要使用.会出问题
       6.***如果希望在对象的方法里,使用对象自己的属性,写为this.属性名
           
           *****难点:this的指向:
           1.单个元素绑定事件,this->这个元素
           2.多个元素绑定事件,this->当前元素
           3.定时器中的this->window
           4.箭头函数this->外部对象
           5.函数中this->谁在调用此方法,this就是谁
           6.构造函数之中this->当前正在创建的对象
           
        2.预定义构造函数方式:
        var obj=new Object();//空对象
        //需要自己后续慢慢添加属性和方法
        obj.属性名=新值;
        obj.方法名=function(){};
        
        以上两个都有一个缺陷:一次只能创建一个对象,适合创建单个元素的时候(第一种方法),第二种方法完全湿垃圾,如果批量创建多个对象,那么推荐第三个方法
        
3.自定义构造函数方式:2步
1.创建自定义构造函数
function 类名(name,age,hobby){
this.name=name;
this.age=age;
this.hobby=hobby;
}
//千万不要在里面创建方法,每个对象都会创建出一个相同的方法,浪费内存-学习继承后可以解决

2.调用构造函数创建对象
var obj=new 类名(实参,...);

面相对象
优点:
1.逼格高,所有的属性和方法都保存在一个对象之中-更符合现实更有意义
2.每个功能特地分开写-便于以后维护
3.铁索连舟-一个方法触发多个方法联动
缺点:对新手不友好,尤其是this指向问题

2.********继承:父对象的成员(属性和方法),子对象可以直接使用

为什么要继承:代码重用!提高代码的复用性,节约了内存空间!提升了网站的性能!

何时继承:只要多个子对象共用的属性和【方法】,都要集中定义在父对象之中

1.***如何找到原型对象(父对象):保存了一类子对象共有属性和共有方法
    1.对象名.__proto__;//必须现有一个对象
    2.构造函数名.prototype;//构造函数名几乎人人都有,除了Math和Window,
    new 构造函数名();//Array、String、Date、RegExp...
2.*面试题:两链一包:作用域链和【原型链】和闭包
    每个对象都有一个属性:__proto__,可以一层一层的找到每个人的父亲,形成的链式结构,就称之为叫做原型链
    可以找到父对象的成员(属性和方法),作用:找共有属性和共有方法
    最顶层的是Object的原型,上面放着一个我们眼熟的方法toString,怪不得人人都可以使用toString
    JS万物皆对象
3.有了原型对象,可以设置共有属性和共有方法
    1.原型对象.属性名=属性值;
    2.原型对象.方法名=function(){}

Day18

面相对象: 1.*****继承很多的笔试和面试题

1.判断是自有还是共有:

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

2.修改和删除:自有和共有

自有:
    修改:obj.属性名=新属性值;
    删除:delete obj.属性名;
共有:
    修改:原型对象.属性名=新属性值;//千万不要觉得,自己能拿到,就能直接修改,这样很危险,并没有修改原型的东西,而是在本地添加了一个同名属性
    删除:delete原型对象.属性名;//如果你对着本地直接删除,那么此操作直接无效

3.*如何为老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;
    }
}

var arr1=[1,2,3,4,5];
var arr2=[2,4,6,8,10];

4.*如何判断x是不是一个数组:4中方法:千万不要用typeof(),只能检查原始类型,不能检查引用类型,如果检查引用类型得到的结果都是一个object.

1.判断x是否继承自Array.prototype:
    Array.prototype.isPrototypeOf(x);
    结果为true,说明是数组,结果为false,说明不是数组
2.判断x是不是由Array这个构造函数创建的
    x instanceof Array;
    结果为true,说明是数组,结果为false,说明不是数组
3.Array.isArray(x);-ES5新增的方法,只有数组可以这么使用
    结果为true,说明是数组,结果为false,说明不是数组
4.*输出【对象的字符串】形式
    在Object的原型上保存着最原始的toString方法
    原始的toString输出形式:[object 构造函数名]
    
    *****多态:子对象觉得父对象成员不好用,就在本地定义了同名函数,覆盖了父对象的成员,不严格定义:同一个方法,不同的人使用,效果不同,有多种形态
    固定套路:
    Object.prototype.toString.call(x)==="[object Array]"

5.实现自定义继承:

1.两个对象之间设置继承
    子对象.__proto__=父对象
2.多个对象之间设置继承
    构造函数名.prototype=父对象;
    时机:应该在开始创建对象之前就设置好继承关系
    

2.class关键字:简化面相对象(封装、继承、多态)

class 类名 extends 老类{
    constructor(name,age,hobby,...){//放在constructor里面的都是自有属性
        super(name,age);
        this.hobby=hobby;
    }//放在constructor外面的都是共有方法
    //还会继承到老类所有的API,也可以添加新的
}

3.*****Function:闭包

作用域:21.全局:随处可用,可以反复使用,缺点:容易被污染
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.三个事件需要防抖节流-共同点:触发的速度飞快
       1.elem.onmousemove-鼠标移动事件
       2.input.oninput-每次输入/改变都会触发
       3.onresize-每次窗口改变大小都会触发
   防抖节流的公式:
   
       function fdjl(){
           var timer=null;
           return function(){
            if(timer!==null){clearTimerout(timer);timer=null;}
                timer=setTimeout())=>{
                操作
                },500)
           }
       }
       
       var inner=fdjl();
       
       elem.on事件名=function(){
           inner();
       }

总结:两链一包:

1.作用域链:以函数的EC的scope chain 属性为起点,经过AO,逐级引用,形成的一条链式结构,我们就称之为叫做作用域链
作用:查找变量,带来了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错

2.原型链:每个对象都有一个属性叫做.__proto__,可以一层一层的找到每个对象的原型对象,最顶层的就是Object的原型,形成的一条链式结构,我们就称之为叫做原型链
作用:查找属性和方法,哪怕自己没有也会顺着原型链向上找,怪不得人人都能用toString(),因为他在最顶层

3.闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
作用:专门用于防抖节流