数据类型转换
为什么:页面上的数据都是字符串,有可能需要做一些别的操作(比如+运算),提前先转换,再运算
1、强制类型转换:程序员主动调用方法完成的类型转换
1、转字符串:2种
1、var str=xx.toString();//null和undefined不能使用.调用函数,如果使用则会报错
2、var str=String(xx);//万能,完全等效于隐式转换不需要手动使用,还不如+""
哪个都不重要,页面上的数据本来就是字符串的,而且非要用的话一定是xx.toString();//至少能判断出是undefined或 null 会报错
2、***转数字:
1、*var num=parseInt(str/num);
执行原理:从str的左侧开始,依次读取每个字符,碰到第一个不是数字的字符,就退出,
一来就碰到了不认识的,则为NaN - 不认识小数点
2、*var num=parseFloat(str);
执行原理:几乎和parseInt一致,唯独不同认识第一个小数点
以上两个方法可以去掉后面单位
将页面上获取来的字符串数据,转为数字后在运算
3、Number(xx);//万能,完全等效于隐式转换不需要手动使用,还不如 *1 /1 -0
3、转布尔:
Boolean(xx);//万能,完全等效于隐式转换不需要手动使用,还不如 !!xx
***什么情况会为false:0,"",NaN,undefined,null,false - 只有这6个
其余全部都为true
其实不管是不是快速转换方式,我们都不会主动手动使用,
*但是会在某些地方被动用到:
1、if(条件中){}
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
总结:说白了,return 和 var result= 不是必须要写的
但是如果你希望在函数外部得到函数内部的结果,就需要写上了
***作用域(scope):一个变量的可用范围:2种
1、全局作用域:全局变量和全局函数,在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(变量/表达式){
case 值1:
操作1;
case 值2:
操作2;
case 值3:
操作3;
default:
默认操作;
}
强调:1、case的比较不会带有隐式转换
2、问题:默认只要满足一个case过后,会将后面所有的操作全部做完!
解决:break
注意:有的地方可以不写break
1、最后的默认操作不用加
2、如果中间多个操作,做的是相同的事情,也不需要加
3、default默认操作可以省略
面试题:if vs switch
1、switch...case - 必须知道准确的结果才能使用,不能做范围判断,效率相对较高
2、if...else - 可以实现范围判断,效率相对较低
代码优化:尽量的将 if...else 换成 短路/三目/switch...case
+++++++++++++++++++++++++++++++++++++++++++++
循环结构
while(){}
do{循环体}while(循环条件)
面试题:while 和 do...while的区别?
仅看第一次,如果第一次都满足,两者没区别,
如果第一次不满足,while一次都不做,do...while至少会执行一次
***for(循环变量;循环条件;变量的变量){
循环体;
}
流程控制语句:退出循环:*1、break; //退出整个循环
2、continue; //退出本次循环
****数组的基础:
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();
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++