1、数据转换类型:
1、强制转换:
1、转为字符串:
x.toString(); x不能是undefined和null,因为undivided和null不能使用.操作
String(x);任何东西都可以转为字符串,但是页面上的一切数据都是字符串,不如使用+""运算。
2、转数字:
parseInt(XXX);转为整数,但是不认识小数点,不会四舍五入
parseFloat(XXX);转为小数,认识第一个小数点,不认识第二个小数点。
从左往右一次读取每个字符,碰到非数字字符就停止,如果一开始就不是数字字符就位NaN
Number(xxx); 任何都能转为数字
3、转布尔:
Boolean(xxx); 任何都能转为布尔,转布尔得到的值只有turn和false
只有6个为false: 0," ",undefined,null,NaN,false. 其他都为turn
2、隐式转换:出现在运算符中
2、运算符和表达式:
1、运算符: + - * / %
其中%叫做取余,主要用于判断数字的奇偶性.
隐式转换:默认转为数字在运算,其中+运算在碰到字符串时为拼接效果,所以
-*/%隐式转换时但凡有一个非数字字符结果则为NaN
2、比较运算:NaN参与任何比较运算结果都为false
> < >= <= == != === !==
比较结果都为布尔值
隐式转换:默认转为数字在比较大小
如果参与比较的左右两边都是字符串,那么是按位比较每个字符的十六进制Unicode号
0-9<A-Z<a-z<汉字,其中汉字的第一个字为:一Unicode号为(4E00) 十进制ascii(19968)
最后一个字为:龥:Unicode号为(9FA5) 十进制ascii(40869)
3、 逻辑运算:
&&:全部满足,才true
一个不满足,则false
||:全部不满足,才false
一个不满足,则true
!:颠倒布尔值
短路逻辑:如果前一个条件,已经能够得出最后结论了,则不看后续
&&短路:如果前一个条件满足,才执行后续操作,如果前一个条件不满足,则不管后续操作
目的:简化【简单的】分支:1、一个条件一件事满足就做,不满足就不做 if(){} 2、操作只能有一句话
条件&&(操作);
4、赋值运算: += -= *= /= %=
i+= 同等于 i=i+
笔试题:++i和i++的区别:
单独使用,放前放后无所谓,效果一样。
但是如果参与了别的表达式,变量中的值都会+1
前++返回的是加了过后的新值
后++返回的是加了之前的旧值
5、三目运算:简化if.....else
1、条件?操作1:默认操作;
2、条件1?操作1:条件2?操作2:条件3?操作3:默认操作;
3、分支结构:
1、if分支
2、三目或者短路
3、Switch....case....
语法:
Switch(变量){
case 值1;
default:默认操作
}
特殊:1、不具备隐式转换
2、默认只要一个case满足后,就会将后面所有操作全部做一次
解决:break;
建议:每一个case的操作后都要跟上一个break
有的地方可以不加break:
1、最后一个操作default可以省略break
2、如果中间连续的多个操作,是相同的,也可以省略掉中间部分
面试题:if vs switch
1、switch:缺点:必须要知道最后的结果才能使用,不能做范围判断
优点:执行效率相对较高
2、if : 优点:可以做范围判断
缺点:执行效率相对较慢
4、循环结构:
1、while循环
var 循环变量名=几;
while(循环条件){
操作;
变量变化;
}
2、do....while循环:
var 循环变量名=几;
do{
操作;
变量变化;
}while(循环条件)
面试题:while和do...while的区别?
只看第一次,如果大家都满足,则没区别
如果不满足,while一次都不执行,而dowhile至少会执行一次
for循环:
for(var 循环变量=几;循环条件;变量变化){
操作;
}
退出循环语句:
break - 退出整个循环
continue - 退出本次循环
死循环:while(true){} for(;;){}
5、数组的基础:
1、什么是数组:一个集合可以保存多个数据
2、创建:
直接量方式:var arr=[值1,....]
构造函数方式:var arr=new Array(值1,....);
3、访问: arr[下标];
添加或修改:arr[下标]=新值;
特殊:访问时,下标越界 - 返回undefined
添加时,下标越界 - 变为稀疏数组,导致下标不连续
4、数组三大不限制
不限制类型
不限制长度
不限制下标越界 - 不推荐
5、数组唯一的一个属性:数组名.length - 获取数组的长度
三个固定套路:
末尾添加:arr[arr.length]=新值
获取倒数第n个:arr[arr.length-n];
缩容:arr.length-=n;
6、遍历数组:对数组中的每个元素执行 相同 或 相似的操作
for(var i=0;i<arr.length;i++){
arr[i];//当前次元素
}
7、关联数组(hash):下标可以自定义,便于查找
1、创建:
创建一个空数组:var arr=[];
为数组添加自定义下标并且赋值:arr["自定义下标"]=新值
2、访问:arr["自定义下标"]
3、强调:hash数组length永久失效,永久为0!
问题:hash数组不能使用for遍历,必须使用 for in循环遍历数组,语法:
for(var i in 数组名){
console.log(i);//自动获得当前数组的所有的下标,不需要我们去设置从哪里开始到哪里结束
console.log(arr[i]);//当前次元素
}
6、数组的API:数组的函数,前辈们定义好的,只有数组可以使用
1、数组转为字符串:
var str=arr.join("自定义连接符");
笔试题:给你一个数组,将他无缝拼接在一起:
var arr=["h","e","l","l","o"];
var str=arr.join("");//用空字符把数组里的内容全部连接起来
console.log(str);
拼接为页面元素:
var cities=["-请选择-","北京","南京","西京","东京","重庆"];
//拼接成页面元素后,innerHTML是识别标签的
sel.innerHTML="<option>"+cities.join("</option><option>")+"</option>";
2、拼接数组:添加元素的新方法
var 新的数组=arr.concat(新值1,arr1老数组......);
特殊:1、此方法不修改原数组,只会返回一个新数组
2、支持传入数组参数,悄悄的将我们传入的数组打散,不会变成二维数组
3、截取子数组:取出数组中想要的某一部分组成的一个新数组
var 截取出来的数组=arr.slice(开始的下标,结束的下标);
特殊:1、此方法不修改原数组,只会返回一个新数组
2、含头不含尾
3、第二参数可以省略不写,截取到末尾
4、第一个参数也可以省略不写,如果两个参数都没写,从头截取到尾 - 深拷贝(复制了一份副本)
5、支持负数参数,-1代表倒数第一个
4、*删除、插入、替换:
删除:var dels=arr.splice(starti,n);//n代表删除的个数
特殊:此方法其实也有返回值,返回的就是你删除的元素组成的一个新数组
插入:arr.splice(starti,0,新值1,....);
特殊:1、没有删除,也有返回值,返回的是一个空数组
2、原starti位置的元素以及后续元素都会向后顺移
3、不建议插入数组,会变得不伦不类
替换:var dels=arr.splice(starti,n,新值1,....);
特殊:删除的个数和插入的个数不必相同
5、反转数组:arr.reverse();
6、数组排序:
笔试题:
for(var i=1;i<arr.length;i++){
for(var j=0;j<arr.length;j++){
if(arr[j]>arr[j+1]{
var m=arr[j];
arr[j]=arr[j+1];
arr[j+1]=m;
}}}
排序API:
数组名.sort();
默认:按位PK每个字符的unicode号,默认是按照字符串排序
解决升序:return a-b; 降序:return b-a; a:后一个数字 b:前一个数字
arr.sort(function(a,b){
return a-b;
});
7、栈和队列:
栈:就是一个数组,只不过要求只能从一端进出,另一端是封闭的
前进:arr.unshift(新元素,...) - 添加元素的新方式,建议不要添加数组
前出:var first=arr.shift(); - 删除元素的新方式,一次只能删除一个,而且是第一个
后进:arr.push(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,而且是最后一个
队列:就是一个数组,只不过要求一端进,另一端出
前进:arr.unshift(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,而且是最后一个
后进:arr.push(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,而且是最后一个
8、二维数组:数组的元素,又引用着另一个数组
创建: var arr=[
[数组1]
[数组2]
];
访问:arr[行下标][列下标];
大数组 小数组
遍历二维数组:两层循环,外层循环控制大数组,内层循环控制小数组
for(var r=0;r<arr.length;r++){
for(var c=0;c<arr[r].length;c++){
console.log(arr[r][c]);
}
}
7、String的概念:
1、什么是字符串:多个字符组成的【只读】字符【数组】:
1、只读:我们下周一要学习的所有的字符串的API,都不会修改原字符串,只会返回一个新字符串
2、数组:和数组有相同点:
1、可以使用下标得到某个字符
2、可以使用length获取字符串的长度
3、遍历字符串
4、数组不修改原数组的API,字符串也可以使用(拼接 - 垃圾还不如直接+运算,截取)
当然和数组也有很多的不同点,数组修改原数组的API,字符串一个都用不到,字符串也有十几个API等待我们学习
2、引用/对象类型:11个
*String Number Boolean - > 具有包装类型
*Array *Function Math(数学) Date(日期) *RegExp(正则表达式:验证)
Error(错误)
*Object(面向对象)
Global - 全局对象:在前端/浏览器端/客户端/js中被window代替了:功能:保存着全局变量和全局函数,只有window可以省略不写
3、***包装类型:专门用于将原始类型的值封装为一个引用类型的对象的(带了属性和方法)
为什么:原始类型的值原本是不具备任何属性和方法的,但是前辈们发现我们程序员会经常操作到字符串
前辈们为了方便我们程序员,为三个原始类型提供了包装类型
何时使用:只要试图用原始类型的变量去用.调用属性和方法时,就会悄悄的用上包装类型变为对象
何时释放:方法调用完毕后,自动释放包装类型,并且返回数据(又会变回原始类型)
为什么undefined和null不能用. - 没有提供包装类型
8、String API: 只有字符串可以使用的函数,特点:只读
1、转义字符
将字符串中和程序冲突的编译为原文: "\"" 换行:\n 制表符:\t
输出Unicode编码字符:正则中可以使用
\uXXXX:第一个汉字:4e00 - ascii:19968
最后一个汉字:9fa5 - ascii:40869
2、大小写转换:将字符串中得每个英文统一转为大写或小写
大写:var 转完的结果接住=str.toUpperCase();
小写:var 转完的结果接住=str.toLowerCase();
3、获得字符串中自定位置的字符: str.charAt(i) 不如直接str[i] 因为字符串也可以用下标找到某个字
4、获取字符串中的指定位置字符的ascii码: var ascii=str.charCodeAt(i);
根据ascii在转回原文: var 原文=String.fromCharCode(ascii);
5、检索字符串: 获取关键字的下标
var = str/arr.indexOf("关键字",start);
从start位置开始,查找右侧【第一个关键字】的位置
start可以不写,默认从0开始查找
返回值看找没找到,如果没找到返回值就是-1,可以判断字符串或者数组中有没有重复的
笔试题:
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、截取字符串:
*var subStr=str/arr.slice(starti,endi+1);//用法和数组的slice一摸一样
str.substring(starti,endi+1);//几乎和slice一致,不支持附属参数
***str.sbustr(开始的位置,n)//n代表要截取的个数,不用考虑含头不含尾
7、替换字符串:
*替换字符串:本身强大,但是必须搭配正则表达式
var newStr=str.replace("关键字","新内容");
var newStr=str.replace(/[正则表达式]+/g,function(key){
return key.length==2?:"**":"***":"****";
});
8、切割/分割字符串:
var arr=str.split("任意切割符");
作用:将字符串=>数组
特殊:1、切割后,切割符就不存在了
2、切割符"",切散每一个字符
9、正则表达式:定义字符串中字符出现规则的一个表达式
1、验证:
1、最简单的正则:"关键字" > /关键字/gi 正则后面可以添加后缀
g:全部 i:忽略大小写
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、密码验证: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、验证:var arr=str.split("切割符"/正则表达式);
3、替换:
基础替换法:
var newStr=str.replace("固定关键字"/regexp,"新内容");
高级替换法:
var newStr=str.replace("固定关键字"/regexp,function(a,b,c){
console.log(a);//第一个形参保存的是正则匹配到的每一个关键字
console.log(b);//第二个形参保存的是正则匹配到的每一个关键字的第一个字符的下标
console.log(c);//原文本身
return a.length==2?"**":"***";
});
格式化:在使用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+"日";
})
10、正则对象:API
1、创建正则对象:
*直接量方式:var reg=/正则表达式/后缀;
构造函数方式:var reg=new RegExp("正则表达式","后缀")
2、API:var bool=reg.test(用户输入的东西);
返回true,说明用户验证成功,否则验证失败
11、Math对象:提供了一些数学计算的API
1、取整:
上去整:var 结果接住=Math.celi(num); 不会四舍五入
下去整:var 结果接住=Math.floor(num); 不会四舍五入
四舍五入取整:var 结果接住=Math.round(num);
推荐使用:num.toFixed(d):优点:具有四舍五入,并且小数位数可以自己设置
缺点:返回是一个字符串,搭配上一个parseFloat
笔试题:不用toFixed,封装一个toFixed的操作
function toFixed(num,d){
num*=Math.pow(10,d);
num=Math.round(num);
num/=Math.pow(10,d);
return num.toString();
}
console.log(toFixed(小数,d) d是要保留的位数
2、乘方和开方
*乘方:var result=Math.pow(底数,幂)
开方:var result=Math.sqrt(num);//只能开平方
3、随机数:Math.random(): 在0-1之间取随机的小数,但是有可能取到0,不可能取到1
有可能取到最小数,但是绝对不可能取到最大数
固定公式:parseInt(Math.random()*(max-min+1)+min);
12、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);
13、Error:错误对象
浏览器自带四种错误类型:可以快速找到自己的错误
语法错误:SyntaxError - 符号写错了
引用错误:ReferenceError - 没有创建就去使用了
类型错误:TypeError - 不是自己的方法,你却去使用了,最典型的,就是你们经常会undefined.xxx或null.xxx
范围错误:RangeError - 只有一个API会碰到:num.toFixed(d);//d的取值范围只能是0~100之间
可以用一个技术代替:if...else... 提前预判或者用正则控死
抛出自定义错误:throw new Error("自定义提示");
14、Function:函数对象:提前创建好的,可以反复使用
什么是函数:需要先定义好的,可以反复使用的一个代码段
1、创建和调用:
如何创建:
声明方式创建函数: function 函数名(形参,.....){
函数体;
return 返回结果;
}
直接量方式创建函数: var 函数名=function(形参,....){
函数体;
return 返回结果;
}
构造函数方式:var 函数名=new Function("形参",.....,"函数体;return 返回值");
调用: var 自定义名字接住return返回的结果=函数名(实参,.....);
调用:如果有return,要用变量接住结果;
var 结果=函数名(实参);
2、作用域:
全局作用域: 全局的变量和函数,在页面任何一个位置都可以使用
局部作用域: 局部变量和函数,只能在当前函数调用时内部使用
变量的规则:优先使用局部自己的,局部没有找全局,全部没有就报错。
局部可以用全局的,但是全局不能用局部的
3、声明提前:
在程序执行之前,会把var声明的变量(靠上)和function声明的函数(靠下)集中到当前作用域的顶部(我们看不见)
但是赋值留在原地。
注:声明方式创建的函数会完整的提前(第一种方式)
直接量方式创建的函数不会完整提前,只有变量部分会提前(第二种方式),赋值留在原地
4、函数的执行原理:
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自动释放,所以局部变量也就释放了
5、按值传递:两个变量之间赋值,分两种情况
如果传递的是原始类型的值:
修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方
如果传递的是引用类型的对象:
修改一个变量,另一个变量是会受到影响的,引用类型其实根本没有保存到变量中,仅仅只是保存了一个地址值
两者用的是同一个地址值,所以会相互影响
6、解码和编码:
编码:var 不认识=encodeURIComponent("你输入的文字");
解码:var 原文=decodeURIComponent(不认识);
7、考点:
1、创建的三种方式
2、作用域:变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
3、声明提前:
4、按值传递:
5、重载overload:相同的函数名,根据传入的实参的不同,自动选择对应的函数执行
为什么:减轻程序员负担!
问题:JS不支持重载!
JS不允许多个同名函数同时存在,如果存在,最后的会覆盖之前的所有
解决:在【函数中】有一个对象:arguments对象,不需要我们创建,自带
什么是arguments:是一个类数组对象:但是不是数组,和数组有3个相同点
1、都可以使用下标
2、都可以使用length
3、都可以遍历
作用:***接受住传到函数内部的所有实参,哪怕你不写一个形参
可以做的事儿:
1、实现重载:可以通过在函数内部判断arguments的不同,执行不同的分支操作
2、以后有没有形参无所谓
3、正式开发中,有可能会将多个函数整合为一个函数
6、***匿名函数:没有名字的函数
1、匿名函数自调:
//此函数只会执行一次,执行后会自动释放,优点,自动释放 - 代替全局写法,提升网页的性能
(function(){
var a=2;
console.log(a);
btn.onclick=function(){
console.log("释放了码");
}
//引用类型:在还有人使用的情况下,是不会自动释放的
})();
2、匿名函数回调:将函数作为实参,传递给了其他函数调用 - 没有名字就是匿名函数,匿名函数只有自调和回调,没有自调,则是回调
1、学习回调函数的目的:让你们知道哪些属于回调函数
arr.sort(function(a,b){return a-b;})
str.replace(reg,function(){})
btn.onclick=function(){}
这些东西都是前辈们规定好的,我们只能学习如何使用的
2、ES6 - 一切的回调函数,都可以简化为箭头函数
3、了解了回调函数的原理
7、闭包 :希望保护一个可以【反复使用的局部变量】的一种词法结构,其实函数一个函数,写法和以后有点不一样
使用场景:防抖节流 - 有三个事件需要做防抖节流:执行的速度非常的快,减少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();
15、面向对象:Object:
1、开发方式
面向过程:经过>开始>结束,其实我们一直以来的开发方式都是面向过程:先干什么再干什么最后干什么。
面向对象:属性和方法,js万物皆对象
2、封装对象:
1、直接量方式:
var 对象名={
"属性名":"属性值";
.....
"方法名":function(){},
}
访问对象的属性和方法
*对象名.属性名 === 对象名["属性名"]
*对象名.方法名(); === 对象名["方法名"]();
访问到不存在的属性或者方法,返回undefined
如果希望遍历出对象中的所有东西使用for.....in 循环
for(var i in 对象名){
对象名[i]
}
如果你希望在对象的方法中使用对象自己的属性:写为:this.属性名
难点:this的指向:
1、*单个元素绑定事件:this>这个元素
2、*多个元素绑定事件:this>当前触发事件的元素
3、*函数中使用this:this>谁在调用此方法,this指向就是谁
4、定时器this>window
5、箭头函数this>外部对象
2、预定义构造函数方式:
var 对象名=new Object(); 创建出来的是个空对象,需要自己后续添加属性和方法
对象名.属性名="属性值";
对象名.方法名=function(){}
以上两种方一次只能创建一个对象
3、自定义构造函数方式: 可以创建多个对象
1、创建自定义构造函数
function 类名(name,age,salary){
this.name=name;
this.age=age;
this.salary=salary;
}
2、反复调用自定义构造函数创建出对象
var xxx=new 类名("实参",...)