1、数据类型转换
1、强制转换:3类
1、转字符串:
1、x.toString();//x不能是undefined和null - undefined和null不能.
2、String(x);//万能的,任何人都可以转为字符串,不重要
不重要的原因:
1、页面上一切的数据都是字符串
2、String(); 完全相当于隐式转换 - 还不如+""
2、*转数字:
1、*parseInt/Float(str);//专门为字符串转数字准备的
原理:从左向右依次读取每个字符,碰到非数字字符就停止,如果一来就不认则为NaN,
Int不认识小数点,Float认识第一个小数点
2、Number(x);//万能的,任何人都可以转为数字,不重要 - 完全相当于隐式转换 - 还不如-0 *1 /1
3、转布尔:
Boolean(x);//万能的,任何人都可以转为布尔,不会手动使用的,还不如:!!x
***只有6个会为false:0,"",undefined,null,NaN,false,其余都为true
在分支、循环的条件之中,以后不管代老师写的是什么,他都会悄悄的隐式转换为布尔值,你只需要考虑为true还是为false
2、隐式转换:都是出现在运算符之中
2、运算符和表达式:
1、*算术运算符:+ - * / %
特殊:1、%:取余,判断奇偶性
2、隐式转换:默认转为数字,在运算
true->1
false->0
undefined->NaN
null->0
特殊:1、+运算,碰到字符串,拼接
2、-*/%:字符串可以转为数字,但是纯数字组成才行,但凡包含一个非数字字符,则为NaN
2、*比较运算符:> < >= <= == != === !==
结果:布尔值
隐式转换:默认,转为数字,再比较大小
特殊:1、如果参与比较的左右两边都是字符串,则是按位PK每个字符的十六进制unicode号(十进制ascii码)
0-9<A-Z<a-z<汉字:常识:汉字的第一个字:一:unicode:4E00 - ascii:19968
最后一个字:龥:unicode:9FA5 - ascii:40869
2、NaN参与任何比较运算结果都是false,所以没有办法用普通的比较运算判断是不是NaN
!isNaN(x);
3、undefined==null;//true
区分:undefined===null;
=== !==:不带隐式转换的等值比较和不等比较,要求数值相同,并且数据类型也要相同
function String(x){
if(x===undefined){
return "undefined";
}else if(x===null){
return "null";
}else{
return x.toString();
}
}
3、逻辑运算:
&&:全部满足,才true
一个不满足,则false
||:全部不满足,才false
一个不满足,则true
!:颠倒布尔值
特殊:短路逻辑:如果前一个条件,已经能够得出最后结论了,则不看后续
&&短路:如果前一个条件满足,才执行后续操作,如果前一个条件不满足,则不管后续操作
目的:简化【简单的】分支:1、一个条件一件事满足就做,不满足就不做 if(){} 2、操作只能有一句话
条件&&(操作);
举例:原来:if(money>=500){money*=0.8};
现在:money>=500&&(money*=0.8);
||短路:浏览器兼容:二选一操作
e=e||window.event;
4、位运算:
左移:m<<n,读作m左移了n位,翻译:m*2的n次方
右移:m>>n,读作m右移了n位,翻译:m/2的n次方
缺点:底数只能2
5、赋值运算:+= -= *= /= %=
一个操作两件事,先计算,在赋值回去。
i++;//递增:每次只会固定+1
i+=1;//累加:每次+几由我们程序员自己决定
i++ === i+=1 === i=i+1;
鄙视题:++i和i++的区别:
单独使用,放前放后无所谓,效果一样。
但是如果参与了别的表达式,变量中的值都会+1
前++返回的是加了过后的新值
后++返回的是加了之前的旧值
6、三目运算:
简化if...else 简化if...else if...else
语法:
1、条件?操作1:默认操作;
2、条件1?操作1:条件2?操作2:条件3?操作3:默认操作;
特殊:
1、只能简化简单的分支 - 操作只能有一句话
2、默认操作不能省略
总结:
if === &&短路
if else === 三目
if else if else === 三目
1、自定义Function:
什么是函数:需要先定义好,可以反复使用的一个代码段
何时使用:1、不希望打开页面立刻执行 2、以后可以反复使用 3、希望绑定在页面元素之上
如何使用:
1、创建并且调用:2种
1、创建
*1、【声明方式】创建函数
function 函数名(形参,...){
函数体;
return 返回值/结果;
}
2、【直接量方式】创建函数 - 无用
var 函数名=function(形参,...){
函数体;
return 返回值/结果;
}
2、调用
var 接住返回的结果=函数名(实参,...);
//其实return的本意退出函数,但是如果return后跟着一个数据,
//顺便将数据返回到函数作用域的外部,但return只负责返回,不负责保存
//就算省略return,默认也会return undefined;
//具体需不要得到函数的结果,看你自己
2、作用域:2种
1、全局作用域:全局变量 和 全局函数,在页面的任何一个位置都可以使用
2、函数/局部作用域:局部变量 和 局部函数,在【当前函数调用时内部可用】
*变量的使用规则:优先使用自己的,自己没有找全局,全局没有报错
特殊:缺点:1、千万不要再函数中对着未声明的变量直接赋值 - 全局污染
2、局部可以用全局的,但是全局不能用局部的 - 解决:看上面return
3、声明提前:
在程序正式执行之前
将var声明的变量(轻)和function声明的函数(重)
都会悄悄集中定义在当前作用域的顶部
但是赋值留在原地
声明方式创建的函数会完整的提前(第一种方式)
直接量方式创建的函数不会完整提前,只有变量部分会提前(第二种方式)
何时使用:永远不会自己使用,垃圾干扰我们判断 - 只会在鄙视中遇到,为什么平时开发根本不可能遇到它?
只要你遵守以下原则:
1、变量名和函数名尽量的不要重复
2、先创建后使用
3、如果鄙视时需要先试用后创建,多半都是在考你声明提前
4、按值传递:两个变量之间赋值,分两种情况
如果传递的是原始类型的值:
修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方
如果传递的是引用类型的对象:
修改一个变量,另一个变量是会受到影响的,引用类型其实根本没有保存到变量中,仅仅只是保存了一个地址值
两者用的是同一个地址值,所以会相互影响
2、预定义全局函数:
前辈们提前定义好的,我们程序员可以直接使用的,在任何位置都可以使用
*parseInt/Float/isNaN/eval... 其实都是预定义全局函数,但是alert/prompt不属于我们现在学的范畴:确实也是全局预定义函数,只不过属于BOM
1、编码和解码
问题:url中不允许出现多字节字符(汉字,utf-8编码格式下,一个汉字占3字节),如果出现会乱码
解决:发送前,前端将多字节字符编码为单字节字符(数字、字母、符号)
发送后,后端将单字节字符在解码为多字节原文
如何:
编码:var 不认识=encodeURIComponent("大梵");
解码:var 原文=decodeURIComponent(不认识);
这个东西没有用,在某一次浏览器更新后,当前就被淘汰了,浏览器自带此功能 - 唯一的用处,现在就是玩了:悄悄话
2、isFinite(num):判断num是不是有效范围 - 垃圾并不能用于判断是不是NaN,因为有三个人会是false
哪些会为false:NaN,Infinity,分母为0
3、分支结构:根据条件的不同,选择部分代码执行
1、if分支
2、三目&短路
3、switch...case...语法
switch(变量/表达式){
case 值1:
操作1;
case 值2:
操作2;
case 值3:
操作3;
default:
默认操作;
}
特殊:1、不具备隐式转换
2、问题:默认只要一个case满足后,就会将后面所有操作全部做一次
解决:break;
建议:每一个case的操作后都要跟上一个break
有的地方可以不加break:
1、最后一个操作default可以省略break
2、如果中间连续的多个操作,是相同的,也可以省略掉中间部分
面试题:if vs switch
1、switch:缺点:必须要知道最后的结果才能使用,不能做范围判断
优点:执行效率相对较高
2、if : 优点:可以做范围判断
缺点:执行效率相对较慢
建议:代码优化:尽量的将if替换为switch或者三目或短路
要求:分支不准使用if,只能使用switch
1、循环结构:宏观几乎是一瞬间执行的,相同 或 相似的 代码
1、var 循环变量=几;
while(循环条件){
操作;
变量变化;
}
2、do...while循环语法:
var 循环变量=几;
do{
操作;
变量变化;
}while(循环条件)
面试题:while和do...while的区别?
只看第一次,如果大家都满足,则没区别
如果不满足,while一次都不执行,而dowhile 至少会执行一次
3、*for(var 循环变量=几;循环条件;变量变化){
操作;
}
4、*退出循环语句:
break - 退出整个循环
continue - 退出本次循环
5、死循环:while(true){} for(;;){}
forEach for in for of - 专门为遍历数组准备的
2、数组的基础:
1、什么是数组:一个集合可以保存多个数据 何时使用:多个相关的数据,都要集中的定义在一个集合中
为什么:因为一个好的数据结构,能够极大的提升我们程序员的开发效率
2、创建:2种
1、*直接量方式:var arr=[值1,...];
2、构造函数方式:var arr=new Array(值1,...);
坑:new Array(num); - 懂不起:以为你是创建了一个长度为num的空数组
3、访问:数组名[下标];
添加/修改:数组名[下标]=新值;
特殊:访问时,下标越界 - 返回undefined
添加时,下标越界 - 变为稀疏数组,导致下标不连续,导致以后遍历一定会得到undefined
4、数组三大不限制
1、不限制类型
2、不限制长度
3、不限制下标越界 - 不推荐
5、数组唯一的一个属性:数组名.length - 获取数组的长度
三个固定套路:
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、创建:2步
1、创建一个空数组:var arr=[];
2、为数组添加自定义下标并且赋值:arr["自定义下标"]=新值
2、访问:arr["自定义下标"]
3、强调:hash数组length永久失效,永久为0!
问题:hash数组不能使用for遍历,必须使用 for in循环遍历数组,语法:
for(var i in 数组名){
console.log(i);//自动获得当前数组的所有的下标,不需要我们去设置从哪里开始到哪里结束
console.log(arr[i]);//当前次元素
}
牛逼:不光可以遍历hash数组,也可以遍历索引数组:
建议:hash用for in,索引用for
4、js中一切东西都是对象,万物皆对象,除了undefined和null,【一切对象的底层都是hash数组】
1、StringAPI:只有字符串可以使用的函数,特点:只读!
1、转义字符:\ 作用:
1、将字符串中和程序冲突的符号编译为原文 """ '''
2、包含特殊功能的符号: 换行:\n 制表符:\t *
3、输出unicode编码的字符: \uXXXX:第一个汉字:4e00 - ascii:19968 最后一个汉字:9fa5 - ascii:40869
2、*大小写转换:将字符串中的每个英文统一的转为大写 或 小写
何时:只要程序不区分大小写,就要【先统一】的转为大写 或 小写,再比较(验证码)
如何:
大写:var upper=str.toUpperCase();
小写:var lower=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位置开始,查找右侧【第一个关键字】的位置
特殊:
1、starti可以省略不写,从0开始查找
2、返回值:找到了,返回的第一个关键字的第一个字符的下标
*没找到,返回-1,我们不关心下标为多少,我只关心下标为不为-1
作用:判断有没有,以后如果不想有重复的,就一定要用上他
3、此方法不光字符串可以使用,数组也可以使用,后期才为数组添加上的,老IE上的数组就没有此方法
4、笔试题:默认只能获取到第一个关键字的下标,如何才能获取到所有的关键字的下标
var str="no zuo no die no can no bibi";
var i=-1;
while((i=str.indexOf("no",i+1))!=-1){
console.log(i);
}
6、*截取字符串:3种
*var subStr=str/arr.slice(starti,endi+1);//用法和数组的slice一摸一样
str.substring(starti,endi+1);//几乎和slice一致,不支持附属参数
*str.substr(starti,n);//n代表截取的个数,不必考虑含头不含尾
7、拼接字符串:var newStr=str.concat(新字符串,...);//还不如+运算
8、*替换字符串:本身强大,但是必须搭配正则表达式
var newStr=str.replace("关键字","新内容");
9、切割/分割字符串:
var arr=str.split("任意切割符");
作用:将字符串=>数组
特殊:1、切割后,切割符就不存在了
2、切割符"",切散每一个字符
1、正则表达式:
什么是:定义字符串中【字符出现规则】的一个表达式
何时使用:切割 替换【验证】
如何使用:语法:/正则表达式/
1、最简单的正则:关键字原文 "no" -> /no/gi 只要用上正则就可以添加后缀 g:全部 i:忽略大小写
2、备选字符集:/[备选字符集]/
强调:1、一个中括号,只管一位数字
2、正则表达式【默认只要满足条件,不管其他了】,解决:前加^,后加$:/^[备选字符集]$/ -
代表要求:用户从头到尾必须完整匹配我们的要求 - 只要是做验证就必然前加^,后加$
特殊:如果备选字符集中的ascii码是连续的,中间的部分可用-代替掉
一位数字:[0-9]
一位字母:[A-Za-z]
一位数字、字母、下划线:[0-9A-Za-z_]
一位汉字:[\u4e00-\u9fa5]
除了数字之外的:[^0-9] - 很少使用,范围太广了
3、预定义字符集:前辈们提前定义好的,我们直接使用的
目的:简化备选字符集
一位数字:\d === [0-9]
一位数字、字母、下划线:\w === [0-9A-Za-z_]
一位空白字符:\s ===包含:空格、换行、制表符
一位除了换行外的任意字符:. - 很少使用,范围太广了
建议:优先使用预定义字符集,满足不了的时候再用备选字符集自定义
问题:不管是备选字符集,还是预定义字符集,一个都只管一位
4、量词:规定一个字符集出现的次数
有明确数量
字符集{n,m}:前边相邻的字符集,最少出现n次,最多出现m次
字符集{n,}:前边相邻的字符集,最少出现n次,多了不限
字符集{n}:前边相邻的字符集,必须出现n次
无明确数量:
?: 前边相邻的字符集,可有可无,最多1次
*: 前边相邻的字符集,可有可无,多了不限
+:前边相邻的字符集,至少一次,多了不限
5、选择和分组
选择:多个规则中选择其中一个
规则1|规则2
分组:将多个字符集临时组成一组子规则
(规则1|规则2)
6、指定匹配位置:
^:开头
$:结尾
特殊:如果两者同时出现,要求从头到尾完全匹配 - 只要是做验证,必须加上
7、密码验证:4位,数字和字母,必须出现一位大写和一位数字
/^[0-9A-Za-z]{4}$/
预判公式:(?![0-9]+$) -> 不能全由数字组成
(?![a-z]+$) -> 不能全由小写组成
(?![0-9a-z]+$) -> 不能全由数字组成,也不能全由小写组成,也不能只由数字和小写的组合组成
/^(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{4}$/; - 4位,数字和字母,必须出现一位大写和一位数字
/^(?![0-9a-z]+$)(?![A-Za-z]+$)(?!
[A-Z0-9]+$)[0-9A-Za-z]{4}$/ - 4位,数字和字母,必须出现一位大写和一位数字和小写
2、字符串中支持正则表达式的API
1、切割: var arr=str.split("固定切割符"/regexp)
2、替换
1、基础替换法:
var newStr=str.replace("固定关键字"/regexp,"新内容");
2、高级替换法:
var newStr=str.replace("固定关键字"/regexp,function(a,b,c){
console.log(a);//第一个形参保存的是正则匹配到的每一个关键字
console.log(b);//第二个形参保存的是正则匹配到的每一个关键字的第一个字符的下标
console.log(c);//原文本身
return a.length==2?"**":"***";
});
3、格式化:在使用replace替换时,如果搭配上了正则,并且正则中加入分组,那么我们的高级替换法会得到更多的形参
有几个分组,就会多出几个形参。
var
newStr=id.replace(reg,function(a,b,c,d,e,f,g,h){
//// console.log(a);//第一个形参保存的是正则匹配到的关键字
//// console.log(b);//第二个形参会第一个分组匹配到的关键字
//// console.log(c);//第三个形参会第2个分组匹配到的关键字
//// console.log(d);//第四个形参会第3个分组匹配到的关键字
//// //....说不清楚有多少个,具体看有多少个分组
//// console.log(e);//关键字下标
//// console.log(f);//原文本身
// return c+"年"+d+"月"+e+"日";
// })
3、正则对象:API
1、创建正则对象:
直接量方式:var reg=/正则表达式/后缀;
构造函数方式:var reg=new RegExp("正则表达式","后缀")
为什么有时候要加 前加^后加$,为什么有时候又要加后缀g
1、验证:前加^后加$ - 用户要和我们的规则完全匹配
2、替换:加后缀g
2、API:var bool=reg.test(用户输入的东西);
返回true,说明用户验证成功,否则验证失败
1、Math对象:提供了一些数学计算的API
强调:不需要创建,直接使用:全局对象window、Math
属性:Math.PI 得到3.1415926
API:
1、取整:3种
1、上取整:超过一点点,就取下一个整数
var result=Math.ceil(num);
2、下取整:哪怕超过的再多,也会省略掉小数部分
var result=Math.floor(num);
3、四舍五入取整
var result=Math.round(num);
//以上三个操作都只能取整:
//取整的方式:以上三个 + *parseInt + *num.toFixed(0)
//个人更推荐:num.toFixed(d):优点:具有四舍五入,并且小数位数可以自己设置
缺点:返回是一个字符串,搭配上一个parseFloat
//*笔试题:要求不允许使用toFixed,自己封装出toFixed的操作
2、乘方和开方
*乘方:var result=Math.pow(底数,幂)
开方:var result=Math.sqrt(num);//只能开平方
3、最大值和最小值:
var max/min=Math.max/min(a,b,c,d,....);
问题:本身不支持数组参数的
解决:固定用法:var max/min=Math.max/min.apply(Math,arr);
apply是ES5才会学习的东西,apply具有打散数组的功能
4、绝对值:把负数变为正数
Math.abs(负数)
5、随机数:Math.random(): 在0-1之间取随机的小数,但是有可能取到0,不可能取到1
有可能取到最小数,但是绝对不可能取到最大数
公式:parseInt(Math.random()*(max-min+1)+min);
强调:只要以后网页中某一块有一个随机的功能,那么一定需要用到随机数
注意:其实Math还提供了三角函数
2、Date对象:提供了操作日期的API
1、创建:4种
1、*创建一个当前时间: var now=new Date();
2、*创建一个自定义时间:
var birth=new Date("yyyy/MM/dd hh:mm:ss");
3、创建一个自定义时间:
var birth=new Date(yyyy,MM-1,dd,hh,mm,ss);
缺点:月份需要进行修正,0 代表 1月
4、复制一份日期:
为什么:日期的所有的API都是直接修改原日期对象的,无法获得修改之前的日期
所以,在执行API之前都要先进行复制,然后在操作复制后的日期
var end=new Date(start);
2、操作:
1、两个日期对象之间可以相减,得到一个毫秒差,换算出你想要的任何一部分 - 日期的本质底层保存的就是一个毫秒
其实创建还有第五种方式:var date=new Date(毫秒)
2、API:
分量:时间的单位
年月日星期:FullYear Month Date Day
时分秒毫秒:Hours Minutes Seconds Milliseconds
每一个分量都有一对儿getXXX/setXXX的API
get用于获取
set用于设置
特殊:
1、取值范围:
年 - 当前年份的数字
月 - 0~11
日 - 1~31
星期 - 0~6:外国人觉得星期天是一周的第一天
小时 - 0~23
分秒 - 0~59
2、唯独星期不允许设置set
3、建议如果你希望对某个分量做加减
date.setXXX(date.getXXX()+/-n);
date.setFullYear(date.getFullYear()+3);//对日期+3年
4、格式化日期对象->字符串:
date.toLocaleString();//locale本地 - 具有兼容性问题,在不同的浏览器显示出来的效果是不一样,一般来说我们都要自定义格式化方法
日期可以用日期自己的API - 日期屌在日期会自动进制
字符串也只可以用字符串自己的API
一旦格式化为字符串则不可用日期的API
1、Function
作用域:
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自动释放,所以局部变量也就释放了
两链一包:
作用域链:以函数的EC中的scope chain属性为起点,经过AO,逐级引用,形成的一条链式结构,就称之为叫做作用域链
作用:变量的使用规则,查找变量
闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实函数一个函数,写法和以后有点不一样
何时使用:希望保护一个可以【反复使用的局部变量】
如何使用:4步
1、两个函数相互嵌套
2、外层函数创建出受保护的变量
3、外层函数要return返回内层函数
4、内层函数要操作受保护的变量
强调:
1、判断是不是闭包,有没有两个函数嵌套,返回内层函数,内层函数在操作受保护的变量
2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
缺点:受保护的变量,永远不会被释放,使用过多,造成内存泄漏
使用场景:防抖节流 - 有三个事件需要做防抖节流:执行的速度非常的快,减少DOM树的渲染
1、elem.onmousemove - 鼠标移动事件
2、input.oninput - 输入内容有变化就会触发
3、window.onresize - 当前窗口的尺寸如果发生了变化就会触发:JS版本的媒体查询
公式:
elem.on事件名=function(){
inner();
}
function fdjl(){
var timer=null;//1 2 3 - 定时器序号
return function(){
if(timer){clearTimeout(timer);timer=null}
timer=setTimeout(function(){
//操作
},1000)
}
}
var inner=fdjl();