2week

117 阅读11分钟
数据类型转换
为什么:页面上的数据都是字符串,有可能需要做一些别的操作(比如+运算),提前先转换,再运算
1、强制类型转换:程序员主动调用方法完成的类型转换
1、转字符串:21var str=xx.toString();//null和undefined不能使用.调用函数,如果使用则会报错

	2var str=String(xx);//万能,完全等效于隐式转换不需要手动使用,还不如+""

	哪个都不重要,页面上的数据本来就是字符串的,而且非要用的话一定是xx.toString();//至少能判断出是undefined或 null 会报错	   

2、***转数字:
	1、*var num=parseInt(str/num);
	   执行原理:从str的左侧开始,依次读取每个字符,碰到第一个不是数字的字符,就退出,
		  一来就碰到了不认识的,则为NaN - 不认识小数点

	2、*var num=parseFloat(str);
	   执行原理:几乎和parseInt一致,唯独不同认识第一个小数点

	   以上两个方法可以去掉后面单位		

	   将页面上获取来的字符串数据,转为数字后在运算

	3Number(xx);//万能,完全等效于隐式转换不需要手动使用,还不如 *1 /1 -0

3、转布尔:
	Boolean(xx);//万能,完全等效于隐式转换不需要手动使用,还不如 !!xx
	***什么情况会为false0,"",NaN,undefined,null,false - 只有这6个
	   其余全部都为true
	其实不管是不是快速转换方式,我们都不会主动手动使用,
	*但是会在某些地方被动用到:
			1if(条件中){}
			2、循环的条件中
			只要是一个条件就会悄悄的转为bool,以后不管条件中我写了什么,你一定要能判断是true还是false

2、隐式类型转换:多半都出现在运算符和表达式之中

运算符和表达式:
 1、*算术运算:+ - * / %
  隐式转换:默认,转为数字,在运算
  特殊:1、+运算,只要碰到一个字符串,则变为拼接
	2、- * / %,纯数字组成的字符串,也可以转为数字,但是非纯数字的字符串转为NaN
		    NaN参与任何的算术运算结果仍为NaN

 *比较运算:> < >= <= == !=     === !==
  结果:布尔值
  隐式转换:默认,左右两边都会悄悄的转为数字,再比较
  特殊:1、如果左右两边参与比较的都是字符串,则是按位PK每个字符的十六进制unicode号(也称之为十进制ascii码)
		不需要记忆unicode号或者是ascii码表:只需要记得一点
		  数字 < 大写 < 小写 < 汉字 - 中间可能还一些特殊符号

	2、NaN参与任何比较运算结果都为false,不大于,不小于,不等于任何值,解决:判断是不是NaN
		!isNaN(x); -- true是数字 false是NaN

	3、undefined == null; -- 结果:true,区分不开
	   解决:=== 全等,要求数据类型要相同,并且数值也要相同
		 也可以理解为,不带隐式转换的等式比较
		 !== - 不带隐式转换的不等比较
	   
	   String()的原理上的就用到了===判断
	
		//String(x)原理:想要区分null和undefined必须使用===
		function String(x){
			if(x===null){
				return "null";
			}else if(x===undefined){
				return "undefined";
			}else{
				return x.toString();
			}
		}

*逻辑运算:
 &&:全部条件都为true,最后才为true
     只要一个为false,最后则为false
 ||:只要一个为true,最后则为true
     全部条件都为false,最后才为false
  !:颠倒布尔值
隐式转换:悄悄把每一个条件都转为一个布尔值过后,再来进行综合比较
特殊:
  ***短路逻辑:只要前一个条件已经可以得出最后结论,则后续条件不需要在执行!
	&&:如果前一个条件为true,则后一个操作才会执行
	    如果前一个条件为false,则后一个操作不会执行
	    固定套路:简化了  简单的(操作只能有一句话)分支  if(){}
	    公式:条件&&(操作);
	    比如:if(total>=500){total*=0.8;};
	    简化:total>=500&&(total*=0.8);

	    总结:简化了简单的分支if(){},但是操作只能写一句话,如果操作有多句还是使用if结构

	||:如果前一个条件为true,则后一个条件不在执行
	    如果前一个条件为false,则后一个条件才会执行
	    固定套路:实现两个值二选一使用 -- 备选值、做老IE兼容
		var 变量=值1||值2;

位运算:
 左移:m<<n,读作m左移了n位,相当于:m*2的n次方 - 垃圾:底数始终只能是2,不能修改
 右移:m>>n,读作m右移了n位,相当于:m/2的n次方 - 垃圾:底数始终只能是2,不能修改

***赋值运算:一句话执行两个操作,先运算,在保存回去
  += -= *= /= %=
  何时使用:只要取出变量中的值,做计算,之后还要在保存回去时,就必须使用赋值运算
  比如:total*=0.8; // total=total*0.8;
	m+=n => m=m+n;//累加
	m*=n => m=m*n;//累乘

  递增和递减:
	i++ => i+=1 => i=i+1;
	i-- => i-=1 => i=i-1;

  区别:递增递减每次只会操作1,如果你希望操作的数不是1,那么请使用累加/累减/累乘/累处

  *****鄙视/笔试/面试题:
	递增/减:1、单独使用,放前放后都一样
		 2、参与了其他表达式中,变量中的值都会被+1
		    前++,返回的是递增后的*新*值
		    后++,返回的是递增前的*旧*值
	
*三目运算:简化分支:if(){}else{}	、if(){}else if(){}else{}
语法:
  1、条件?操作1:默认操作;   ===	   if(){}else{}	
	条件满足做操作1,条件不满足做默认操作

  2、条件1?操作1:条件2?操作2:默认操作;    ===    if(){}else if(){}else{}

问题:1、只能简化简单的分支 -- 三目操作只能有一句
      2、默认操作不能省略
          
总结:
	if(){}			==		&&短路(操作只能是一句话)
	if(){}else{}		==		三目(操作只能是一句话)
	if(){}else if(){}else{} ==		三目(操作只能是一句话)


      汉字  19968 40869

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

*****函数
什么是函数:程序中描述一项任务步骤清单的代码段,取了一个名字,可以反复调用
何时使用:1、不希望打开页面立刻执行 2、以后可以反复调用 3、希望绑定在页面元素上 4、独立的功能体
    如何使用:2个点
	1、***创建函数:2种方式
	       *1、【声明方式】创建函数:
			function 函数名(形参,...){
				函数体;
				return 返回值;
			}

		2、直接量方式 创建函数:
			var 函数名=function(形参,...){
				函数体;
				return 返回值;
			}

			通过第二个方式,看出2个点:
				1、第二个方式(直接量)更麻烦
				2、函数名其实就是一个变量名,函数名其实引用着函数对象本身,所以我们说函数是一个引用类型的对象,引用的其实是一个地址

		3、以上两个方式中加入return是什么意思?
			return本意:退出/返回; - 作用退出函数,但是!
			如果return后面跟着一个数据,就可以顺便把这个数据返回到函数的外部,但是只负责返回并不负责保存

	调用函数并且保存接住函数的结果
		var result=函数名();

	强调:1、一个function只能写一个return
	      2、函数是不是一定要写return,不一定的,但是,你会发现前辈们提供的方法都具有return
	      3、到底要不要写return,就看你自己需要不需要得到函数的结果,便于以后操作则加上return
	      4、函数如果没有写return,也有返回值,默认为undefined

	总结:说白了,returnvar result= 不是必须要写的
	      但是如果你希望在函数外部得到函数内部的结果,就需要写上了

	***作用域(scope):一个变量的可用范围:21、全局作用域:全局变量和全局函数,在js的任何一个位置都可以使用

	      2、函数作用域:局部变量(除了函数内创建的变量还有形参也是局部变量)和局部函数,在【函数调用时内部可用】

		 *1、有了作用域就有了变量的使用规则:优先使用自己的,自己没有找全局,全局也没有则报错

		 *2、特殊:全局污染:在任何地方给未声明(未var)的变量直接赋值,都会导致全局污染 -- 浪费的内存
			   一切的变量在使用之前最好都要先var一下在使用

	***声明提前(hoist)
		在程序正式执行之前
		将var声明的变量(小、轻)和function【声明】(大、重)的函数
		集中悄悄提到当前作用域的顶部创建
		但是赋值留在原地
		只有声明方式创建(第一种方式)的函数会完整的提前
		直接量方式创建的函数只有函数名/变量名部分会提前,赋值留在原地

		何时:鄙视面试?
			1、只要遵守先创建后使用的原则
			2、函数名和变量名尽量的不要重复,就不用担心
			一般满足上面两个需要,几乎不可能碰到声明提起
			3、只有笔试面试时,只要你碰到先创建后使用,多半都是考你声明提前

	强调:1、***代表都是考点(面试、鄙视)
	      2、全局污染和声明提前可并不是什么好东西,我们自己写代码绝对不会遇到,但是一定会出现在鄙视面试中

	***按值传递:两个变量之间进行赋值
		如果传递的是原始类型的值(5种):
			修改一个变量,另一个变量是不会受影响的,其实是复制了一个副本给对方。
		如果传递的是引用类型的对象(11种):
			修改一个变量,另一个变量是会受影响的,两个变量使用的其实是同一个地址值。(浅拷贝)

	函数的考点:
		1、创建方式
		2、作用域
		3、声明提前
		4、按值传递
		5、重载?
		6、闭包?
	
预定义全局函数:前辈们提前定义好的,在任何一个位置都可以使用的函数
1、编码和解码 - 玩一玩简单的悄悄话
   问题1:url中不允许出现多字节字符,如果出现会乱码
	  utf-8编码格式下,一个汉字,占3字
   解决:发送前,将多字节字符编码为单字节内容
	 收到后,将单字节内容解码为原文

	 如何
	   编码:var code=encodeURI("千锋");
	   解码:var 原文=decodeURI(code);

   问题2:url中不允许出现保留字符:比如: : /
	 加强版语法:
	   编码:var code=encodeURIComponent("千锋");
	   解码:var 原文=decodeURIComponent(code);

   以上4个方法是垃圾了,现在浏览器自带编码解码功能不需要我们程序员操作

2、isFinite(num):判断一个数字是否在有效范围内
	有三种情况为false:NaN/Infinity/分母为0

3、eval(str):计算字符串,脱掉字符串的衣服
分支结构:
if(){}	===		&&短路
if(){}else{} ===		三目
if..else if..else ===        三目

switch...case分支结构:
语法:
  switch(变量/表达式){
	case1:
	操作1;
	case2:
	操作2;
	case3:
	操作3;
	default:
	默认操作;
  }
强调:1case的比较不会带有隐式转换
      2、问题:默认只要满足一个case过后,会将后面所有的操作全部做完!
	 解决:break
	 注意:有的地方可以不写break
		1、最后的默认操作不用加
		2、如果中间多个操作,做的是相同的事情,也不需要加
      3default默认操作可以省略

面试题:if vs switch
1switch...case - 必须知道准确的结果才能使用,不能做范围判断,效率相对较高
2if...else     - 可以实现范围判断,效率相对较低

代码优化:尽量的将 if...else 换成 短路/三目/switch...case

+++++++++++++++++++++++++++++++++++++++++++++

循环结构
while(){}
do{循环体}while(循环条件)
面试题:whiledo...while的区别?
	仅看第一次,如果第一次都满足,两者没区别,
	如果第一次不满足,while一次都不做,do...while至少会执行一次
***for(循环变量;循环条件;变量的变量){
	循环体;
  }

流程控制语句:退出循环:*1break; //退出整个循环
		   2continue; //退出本次循环
****数组的基础:
1、基础概念
什么是数组:在一个内存(变量名)中保存了多个数据的一个集合结构
  	何时:只要存储的多个相关的数据,都要用数组集中保存
为什么:一个好的数据结构,可以极大的提升程序员的执行效率

2、创建:
1、直接量:var arr=[值1,...];
2、构造函数:var arr=new Array(值1,...);
       特殊:new Array(只放了一个数字num) - 创建长度为num的一个空数组,相当于设置length

3、访问:数组名[下标] -- 元素
  添加:数组名[下标]=新值;
  特殊:读取元素,下标越界 - 返回undefined
    添加元素,下标越界 - 下标不在连续,导致数组变成一个稀疏数组

4、三不限制:
1、不限制元素的个数
2、不限制元素的类型
3、不限制下标越界 - 不是好东西

5、数组名.length:可以获得数组的长度,长度 == 最大下标+1
三个固定套路:
	1、末尾添加元素:arr[arr.length]=新值
	2、获取倒数第n个:arr[arr.length-n];
	3、缩容:删除元素:从末尾删除元素:arr.length-=n;

6、遍历数组:将数组中每个元素都取出来执行相同或者相似的操作
for(var i=0;i<arr.length;i++){arr[i]//当前次元素}

7、如果一个引用类型你想要释放,请你一定要释放干净

  索引数组:下标都是由数字组成的数组
8、关联数组 - 也称之为hash数组
什么是:下标是可以自定义的
为什么:索引数组的下标无具体意义,不便于查找
如何使用:
	1、先创建一个空数组
		var arr=[];

	2、为数组添加下标添加元素
		arr["自定义下标"]=新值

	3、访问元素非常简单,不需要数下标,只需要直接写入下标:
		arr["自定义下标"];

	4、强调:hash数组的length永远失效!永远为0!

	遍历hash数组:必须使用for in循环遍历 - for in是专为遍历准备的
		公式:for(var 下标名 in 数组名){
				数组名[下标名];
		      }
	for in 很牛逼:既可以遍历索引数组 也可以 遍历 hash数组
	个人建议:索引数组还是使用for,关联数组才使用for in

***hash数组的原理:
   hash算法:将字符串,计算出一个尽量不重复的数字(地址值)
	     字符串内容相同,则计算出的数字也一定是相同的
   添加元素:将自定义下标交给了hash算法,得到了一个数字(地址值),直接加你要保存的数据放到了这个地址之中
   读取元素:将指定的自定义下标交给hash算法,得到了一个和添加时完全一样的地址,通过地址就能找到你保存的数据
	     
***学习的javascript所有的对象(引用类型)的底层都是hash数组!!!
   万物皆对象

    上午练习:把没见过的新内容都尝试一下
数组的API:前辈们已经提前创建好的函数,我们程序员可以直接调用使用,但是这些API只有数组可以使用
*1、arr 转 string:
var str=arr.join("连接符");
	可以自定义连接符,也可以省略不写等效于String(arr);

固定套路:
	1、鄙视时,将数组里面的内容拼接为一个句话(无缝拼接)
	   语法:var str=arr.join("");

       *2、将数组拼接为页面元素
		//数据
		var cities=["-请选择-","北京","南京","重庆","天津","上海"];
		//数组的API join拼接上了标签
		var str="<option>"+cities.join("</option><option>")+"</option>";
		//渲染到页面元素之上
		sel.innerHTML=str;

	实现:二级联动:今天的难点:
		关键要点:1、两个数组:一个普通数组做一级,一个二维做二级
			  2、select专属事件onchange
			  3、事件中:this.selectedIndex - 获取到当前选中项的下标
*2、拼接数组:添加数组元素的新方式:将concat后面的参数,拼接到arr之后
var newArr=arr.concat(值1,...);
强调:1、concat不修改原数组,只能返回一个新数组
      2、concat可以直接传入数组参数,悄悄的把数组打散为单个元素
*3、截取(获取)子数组:将arr从starti位置开始,一直截取到endi - 删除元素的新方式/复制
var subArr=arr.slice(starti,endi+1);
强调:1、slice不修改原数组,只能返回一个新数组
      2、含头不含尾
      3、endi可以省略,截取到末尾
      4、两个参数都可以省略,从头到尾复制一份 - 深拷贝(复制了一个副本)

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

以下的API都是修改原数组的

*4、arr.splice():删除/添加/替换的新方式
删除:var dels=arr.splice(starti,n); n - 删除个数 
      强调:其实此方法也有返回值,返回的是你删除的元素组成的一个新数组

添加:arr.splice(starti,0,值1,...);
      强调:1、原starti位置的元素以及后续元素都会向后移动
	    2、插入数组也行,但是不推荐,因为不会帮我们打散数组,会导致一些部分是一维,一些部分是二维

替换:var dels=arr.splice(starti,n,值1,...);
      强调:删除的个数不必等于插入的个数

5、翻转数组:arr.reverse();

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++