一:数据类型转换
1:显式转换:程序员主动调用函数完成的类型的转换:
1、转字符串
a、xx.toString(); //xx不能是undefined和null,报错,两者不能使用.去操作
b、String(xx); //万能的,完全等效于隐式转换,但还不如+""
一般来说两个都不用,页面上获取到的数据都是字符串
2、转数字
a、字符串转数字:parseInt/Float(str);
从左向右,依次读取每个字符,碰到非数字字符就停止转换,如果一开始就不认识,则为NaN
parseInt不认识小数点,parseFloat认识第一个小数点
b、Number(xx); //万能的,完全等效于隐式转换,但还不如-0 *1 /1 %1
3、转布尔
Boolean(xx);//万能的,但还不如!!x
只有6个为false:0,"",undefined,null,NaN,false,其余都为true
在某些地方会自动带有此方法:分支条件;循环条件
2:隐式转换:一般都出现在运算符和表达式中
二:运算符和表达式:
1、算术运算符:+ - * / %
具有隐式转换:默认转为数字,再运算
特殊:a、+运算,只要碰上一个字符串,两边都会转为字符串,再拼接
b、- * / %,只要是纯数字组成字符串,也可以转为数字
2、比较/关系运算符:> < >= <= == != === !==
1、返回的结果:布尔值:true、false
具有隐式转换:默认一切都转为数字,再比较大小
特殊:a、如果参与比较【两个都是字符串】,按位pk每个字符的十六进制unicode号(十进制ASCII码)
0-9<A-Z<a-z<汉字
汉字第一个字:一:4e00(ascii码:19968)
最后一个字:龥:9fa5(ascii码:40869)
b、NaN,参与任何比较运算结果都为false,解决:!isNaN(x)
x是一个数字,结果true
x是一个NaN,结果就为false
c、undefined==null;
问题:null==undefined结果为true,区分不开undefined和null
解决:全等===:数值相同 并且 数据类型也要相同,即不再带有隐式转换
2、!==:不带隐式转换的不等比较
3、逻辑运算符:
a、&&:全部条件都为true,结果才为true;只要有一个为false,结果就为false
b、||:全部条件都为false,结果才为false;只要有一个为true,结果就为true
c、 !:颠倒bool值
d、特殊:短路逻辑:只要前一个条件已经可以得出最终结论,则后续条件不会再执行
1、&&:如果前一个条件满足,则后一个操作才执行,如果前一个条件不满足,则后一个操作不执行
实现了一个【简单】的分支:目的:简化if(){},操作只能用一句话
语法:条件&&(操作)
b、||:两个值中二选一 -解决浏览器兼容问题
案例
btn.onclick=function(e){
e=e||window.event;//解决浏览器的兼容性问题:
//e->undefined->false
console.log(e.screenX);
}
4、位运算:
左移:m<<n,读作m左移了n位,m*2的n次方 - 缺点:底数只能是2
右移:m>>n,读作m右移了n位,m/2的n次方 - 缺点:底数只能是2
5、赋值运算:+= -= *= /= %= ++ --
一句话完成了两个操作,先运算再赋值回去 ,比如:i++ => i+=1 => i=i+1;
a、递增 vs 累加
递增:i++,每次只能加1
累加:i+=n,每次加几由我们自己决定
b、笔试题:++i 和 i++ 的区别?
1、单独使用时,放前放后无所谓都一样
2、如果参与了别的表达式,变量中的值都会+1
前++,返回的是加了之后的新值
后++,返回的是加了之前的旧值
案例1
var a=2;//7
var b=a++ + ++a + a++ + ++a + a++;
2 4 4 6 6
// console.log(b);
// console.log(a);
案例2
var a=2;
var b=++a + ++a + a++ + a++ + ++a + a++ + ++a;
// 3 4 4 5 7 7 9
console.log(b);//39
console.log(a);//9
6、三目运算
如何使用:
1、条件?操作1:默认操作;
2、条件1?操作1:条件2?操作2:条件3?操作3:默认操作;
特殊:1、只能完成【简单】的分支 - 操作只能有一句话;2、默认操作不能省略 - 会报错
三:自定义函数:方法,一段提前被定义好的,可以反复使用的代码段
1、如何使用
a、创建/定义/声明函数&返回结果:2种方式
1、*声明方式:用function关键字进行声明,只有变量、常量、函数具有声明方式。
function 函数名(形参,...){
函数体
return 返回值;
}
2、直接量方式:
var 函数名=function(形参,...){
函数体
return 返回值;
}
函数名其实也是一个变量名
b、调用函数&接住结果
var result=函数名(实参,...);
注意:return后面跟着一个数据,可以将数据返回到全局作用域中,但是仅负责返回,不负责保存,所以我们需要自己创建一个变量接住函数调用的结果
return只能写一次,而且最好写在函数体的后面
2、作用域:2个
a、全局作用域:全局变量 和 全局函数,在任何位置都可以访问/使用
b、函数/局部作用域:局部变量 和 局部函数,只能在当前函数调用时内部可用
有了作用域才有变量的使用规则:优先使用自己的,自己没有找全局,全局都没有报错
c、建议:千万不要对未声明的变量直接赋值,导致全局污染
d、哪些属于局部变量:直接在函数作用域中创建的变量;形参
案例1
//全局污染
function f1(){
a=100;
console.log(a);//100
}
f1();
console.log(a);//100
案例2
//修改全局
var a=1;
function f1(){
a=100;
console.log(a);//100
}
f1();
console.log(a);//1
3、声明提前
a、规则:在程序执行之前,悄悄将var声明的变量和function声明的函数集中提前到当前作用域的顶部,但是赋值留在原地,变量比函数更轻
b、笔试题:如果看到先试用后创建,多半都是考声明提前
//案例1
var a=10;
function f1(a,b,c){
console.log(a);//undefined
var a=20;
console.log(a);//20
}
f1();
console.log(a);//10
// 案例2
function fn(){
console.log(1);
}
fn();//2
function fn(){
console.log(2);
}
fn();//2
var fn=100;
fn();//报错:fn不是一个方法
// 案例3
var fn=function(){console.log(1)};
fn();//1
var fn=100;
fn();//报错
var fn=function(){console.log(2)};
function fn(){console.log(3)};
fn();//不执行
// 案例4
function fn(){
console.log(1);
}
fn();//1
var fn=100;
var fn=function(){
console.log(2);
}
fn();//2
4、按值传递:两个变量之间进行赋值
1、如果传递的是原始类型的值:两个变量之间赋值,做操作,互不影响的 - 其实是复制了一个副本给对方
2、如果传递的是引用类型的对象:Array、Function
两个变量之间赋值,做操作,是会相互影响的 - 因为两个用的是同一个地址值
四、预定义的全局函数:可以直接在任何位置使用的函数:
1、编码和解码
编码:var code=encodeURIComponent("str");
解码:var 原文=decodeURIComponent(code);
2、isFinite(num):判断num是不是在有效范围之内
三种情况会为false:分母为0,NaN,Infinity
3、重要的:parseInt/Float()、eval()、isNaN()
五:分支结构:根据条件的不同,执行不同的操作
switch...case结构
语法:
switch(变量/表达式){
case 值1:
操作1;
break;
case 值2:
操作2;
break;
default:
默认操作;
}
注意:
1、默认只要满足一条路,会把后面所有的操作全都做完,解决:break:一般放在操作的后面,但是:a、最后default不需要加break, b、如果连续的多个操作是一样的效果,也可以省略中间部分
2、不带有隐式转换
3、default可以省略不写
六:循环结构:反复执行 相同 或 相似的操作
1、while循环:
语法: var 循环变量=几;
while(循环条件){
循环体;
变量变化;
}
2、do...while循环:
语法: var 循环变量=几;
do{
循环体;
变量变化;
}while(循环条件)
3、面试题:while 和 do...while的区别?
只看第一次,如果第一次大家都满足条件,两者没有区别
如果第一次大家都不满足条件,while一次都不会执行,而do...while至少会执行一次
4、for循环:
for(var 循环变量=几;循环条件;变量变化){
循环体;
}
5、循环终止语句:
break - 退出整个循环
continue - 退出本次循环
七:数组:一个变量可以保存多个数据
1、创建:2种方式
a、直接量:var arr=[值1,....];
b、构造函数:var arr=new Array(值1,....);
//缺陷:面试中:new Array(3);设置一个长度为3的空数组
2、访问:
获取:arr[i]; - 特殊:下标越界:得到undefined
添加/替换:arr[i]=新值; - 特殊:下标越界:得到稀疏数组
3、3大不限制:
不限制长度、类型、下标越界
4、length的三个固定套路:
获取倒数第n个元素:arr[arr.length-n];
向末尾添加元素:arr[arr.length]=新值
缩容:arr.length-=n;
5、遍历数组:
for(var i=0;i<arr.length;i++){
arr[i];//当前次元素
}
6、特殊点:如果要释放一个引用类型:需将每个变量都要释放才能真的释放干净
7、索引数组:下标都是数字组成的数组 - 默认
8、关联(hash)数组:下标是可以自定义的数组
a、创建:2步
1、创建一个空数组:var arr=[];
2、添加自定义下标并且赋值:arr["自定义"]=值;
b、访问:arr["自定义下标"];
c、遍历
问题:不能使用for循环,因为length失效了,关联数组永远为0,而且下标也在是一个数字
解决:for in循环:(不光可以遍历关联数组,也可以遍历索引数组)
语法:for(var i in 数组名){
数组名[i]
}
注意:JS中除了undefined和null不是一个对象,万物皆对象,而且一切对象的底层都是hash数组
9、二维数组:数组中的值再次引用了一个数组
何时使用:在一个数组内在此细分内容
a、创建:
var arr=[ [ ],[ ],[ ] ];
b、访问:arr[r][c];
特殊:面试中:
列下标越界:返回undefined
行下标越界:报错:undefined不能使用[]
c、遍历二维数组:必然需要两个循环嵌套:外层循环控制行,内层循环控制列
公式
for(var r=0;r<arr.length;r++){
for(var c=0;c<arr[r].length;c++){
console.log(arr[r][c]);
}
}
八 :数组的API:函数,但是只有数组可以使用的方法
1、数组 转为 字符串:(不会修改原数组)
var str=arr.join("自定义连接符");
特殊:
a、如果没有传入实参,则和toString/String,完全一样,默认由,分割
b、固定套路:2个
1、笔试题:提供一个数组给你,让你无缝拼接数组里面的内容变为一个字符串
案例
var arr=["h","e","l","l","o"," ","w","o","r","l","d"];
var str=arr.join("")
console.log(str);//"hello world";
2、开发中:将数组中的数据拼接为页面上的元素:初级版数据渲染
案例
var cities=["北京","南京","西京","东京","重庆"];
var str="<option>"+cities.join("</option><option>")+"</option>";
sel.innerHTML=str; //innerHTML可以识别标签
2、拼接数组:添加元素到的末尾的新方式(不会修改原数组)
var newArr=arr.concat(值1,arr1...);
特殊:
a、此方法不修改原数组,只会返回一个新数组
b、此方法传参支持数组参数,并且会悄悄的打散这个数组,单独传入
3、截取子数组:从starti(i指下标)位置截取到endi+1位置的元素,组成一个新数组(不会修改原数组)
var subArr=arr.slice(starti,endi+1)
特殊:
a、此方法不修改原数组,只会返回一个新数组
b、含头不含尾
c、第二实参可以省略:从starti截到末尾
第一实参也可以省略:从头截到尾
d、支持负数参数,-1代表倒数第一个
4、删除、插入、替换:(会修改原数组)
删除:var dels=arr.splice(starti,n);//从starti开始删除n个
特殊:此方法其实也有返回值,所有删除的元素组成的一个新数组
插入:arr.splice(starti,0,值1,...);//从starti开始删除0个,插入了新元素
特殊:原来starti位置的元素以及后续元素都会被向后移动
替换:var dels=arr.splice(starti,n,值1,...);
特殊:插入的个数和删除的个数可以随意
5、翻转数组:arr.reverse();(会修改原数组)
6、排序:两种方式:
a、笔试题:冒泡排序:把数组中的每一个数字取出来,前一个和后一个进行比较,如果前一个>后一个,两者就要交换位置:
案例:
var arr=[13,25,4,3675,12,23,3,215,2,1,42,4,65,473,2431,123];
for(var j=0;j<arr.length-1;j++){
for(var i=0;i<arr.length-(j+1);i++){
if(arr[i]>arr[i+1]){
var m=arr[i];
arr[i]=arr[i+1];
arr[i+1]=m;
}
}
}
console.log(arr);
b、正式开发中:数组API提供的排序 arr.sort();
特殊:1、默认按照字符串按位PK每个字符的unicode号排序
2、按照数字排序:
arr.sort(function(a,b){//a是后一个数,b是前一个数
return a-b;
})
3、降序排列:
arr.sort(function(a,b){
return b-a;
})
注意:JS中只有数组可以排序,以后我们见到网页上任何具有排序功能的案例,底层一定都是一个数组
7、栈和队列:4个API:添加元素和删除元素的新方式
a、栈:一端封闭,只能从另一端进出的操作
开头进:arr.unshift(值1,...);
开头出:var first=arr.shift();//一次只能删掉一个,并且会返回删除的元素
缺点:每一次进出都会修改其他人的下标
结尾进:arr.push(值1,...);
结尾出:var last=arr.pop();//一次只能删掉一个,并且会返回删除的元素
优点:不会影响到其他元素的下标
b、 队列:只能从一端进入,另一端出:
开头进:arr.unshift(值1,...);
结尾出:var last=arr.pop();//一次只能删掉一个,并且会返回删除的元素
结尾进:arr.push(值1,...);
开头出:var first=arr.shift();//一次只能删掉一个,并且会返回删除的元素
8、实现:二级联动:4个重点
1、必须使用二维数组,而且二维数组的数据顺序一定要和一级的对应上
2、select专属事件:select.onchange=function():状态改变事件:选中项发生改变才会触发
3、select具有一个属性:this.selectedIndex;获取选中项的下标 - 只有select不需要自定义下标
4、其实绑定事件的部分就是函数名,也可以拿来调用 select.onchange();
案例
<script type="text/javascript">
var cities=["-请选择-","北京","南京","西京","东京","重庆"];
var qx=[
["-请选择-"],
["-请选择-","北京1","北京2","北京3","北京4"],
["-请选择-","南京1","南京2","南京3","南京4"],
["-请选择-","西京1","西京2","西京3","西京4"],
["-请选择-","东京1","东京2","东京3","东京4"],
["-请选择-","重庆1","重庆2","重庆3","重庆4"],
]
sel1.innerHTML="<option>"+cities.join("</option><option>")+"</option>";
sel1.onchange=function(){
var i=this.selectedIndex;
sel2.innerHTML="<option>"+qx[i].join("</option><option>")+"</option>";
}
sel1.onchange();
</script>
九:String的概念:字符串,多个字符组成的只读字符数组
1、为什么字符串也可以叫数组呢?
和数组有共同点:a、支持下标 - 获取某个字符;b、支持length - 字符的长度;c、遍历;d、数组不修改原数组的API,字符串也可以使用(concat、slice)
不同点:数组修改原数组的API,字符串都不可以使用,但是字符串也有很多属于自己的API
2、只读:字符串中的所有的API都不会修改原字符串,只会返回新字符串
3、引用类型的对象:11个
String(字符串) Number(数字) Boolean(布尔) - 具有包装类型
Array(数组) Function(函数) Math(数学) Date(日期) RegExp(正则) Error(错误) Object(面向对象)
Global - 全局对象:在浏览器端被window对象给代替了:window对象可以省略不写出来
包装类型:将原始类型的值变为一个引用类型的对象
1、为什么undefined和null不能使用.:不具有包装类型,没有任何的属性和方法
十:String API
1、转字符串:\
作用:3个
a、字符串中如果出现了和字符串冲突的符号,可用\将其转义为原文
" "
b、特殊功能: 换行:\n ;制表符:\t
c、可以书写unicode号 表示一个字
\uXXXX 汉字的第一个字:4e00 ;汉字的最后一个字:9fa5
2、转换大小写:统一的转为大写或小写,再比较,忽略大小写: 验证码
大写:var newStr=str.toUpperCase();
小写:var newStr=str.toLowerCase();
3、原文与ascii码的转换
获取字符串中指定位置的字符的ascii码
var ascii=str.charCodeAt(i);
通过ascii码转回原文
var 原文=String.fromCharCode(ascii);
案例:随机验证码
<script type="text/javascript">
var arr=[];//空数组:放着我们可能出现的字符
for(var i=48;i<=52;i++){
arr.push(String.fromCharCode(i));
}
for(var i=65;i<=90;i++){
arr.push(String.fromCharCode(i));
}
for(var i=97;i<=122;i++){
arr.push(String.fromCharCode(i));
}
for(var i=19968;i<=40869;i++){
arr.push(String.fromCharCode(i));
}
var code="";
while(code.length<4){
console.log(1);
var r=parseInt(Math.random()*(arr.length));
if(code.indexOf(arr[r])==-1){
code+=arr[r]
}
}
var user=prompt("请输入验证码"+code);
if(user.toUpperCase()==code.toUpperCase()){
document.write("<h1 style='color:green;'>验证通过</h1>")
}else{
document.write("<h1 style='color:red;'>验证失败</h1>")
}
</script>
4、检索字符串:检查索引/下标:从starti位置开始找右侧的第一个关键字的下标
var i=str/arr.indexOf("关键字",starti);
作用:判断有没有
特殊:a、starti可以省略,如果省略则从0开始
b、返回值:找到了,返回第一个字符的下标;没找到,返回-1
c、数组也可以使用此方法
d、笔试题:找到所有关键字的位置
案例
var str="no zuo no die no can no bibi";
var i=-1;
while((i=str.indexOf("no",i+1))!=-1){
console.log("找到了:"+i);
}
5、拼接字符串:var newStr=str.concat(str1,str2...) 还不如 +运算
6、截取字符串:3个
a、var subStr=str/arr.slice(starti,endi+1);
b、var subStr=str.substring(starti,endi+1);
//不支持负数参数
c、var subStr=str.substr(starti,n);
//截取的个数,不必考虑含头不含尾
7、替换字符串:
var newStr=str.replace("关键字"/正则表达式,"新内容");
8、切割/分割字符串:作用:str 转换为arr
var arr=str.split("自定义切割符");
特殊:
1、切割符可以自定义,切割过后返回一个数组,数组中不再包含切割符
2、如果传入的切割符是一个"",每一个字符都会被切开
十一:其他知识点
1、 计算时会带有舍入误差:
解决:var str=num.toFixed(保留小数位数); - 按小数位四舍五入,但是返回的会是一个字符串
2、获取第一个字的ascii码:
var ascii=str.charCodeAt(0);
3、定时器
a、开启定时器:
timer=setInterval(function(){
操作
},间隔毫秒数)
b、关闭定时器:
clearInterval(timer);
4、鼠标移入/移出
鼠标移入:onmouseover
鼠标移出:onmouseout
案例:简单的轮播
<div id="carousel" class="carousel">
<img src="img/1.jpg"/>
<button><</button>
<button>></button>
</div>
<script type="text/javascript">
var srcs=["img/1.jpg","img/2.jpg","img/3.jpg"],
btns=document.getElementsByTagName("button"),
img=document.getElementsByTagName("img")[0];
for(var i=0;i<btns.length;i++){
btns[i].onclick=function(){
if(this.innerText==">"){
var first=srcs.shift();
srcs.push(first);
img.src=srcs[0];
}else{
var last=srcs.pop();
srcs.unshift(last);
img.src=srcs[0];
}
}
}
timer=setInterval(function(){
var first=srcs.shift();
srcs.push(first);
img.src=srcs[0];
},1000);
carousel.onmouseover=function(){//鼠标移入
clearInterval(timer);
}
carousel.onmouseout=function(){//鼠标移除
timer=setInterval(function(){
var first=srcs.shift();
srcs.push(first);
img.src=srcs[0];
},1000);
}
</script>
案例:复杂的轮播(未封装)
<div id="carousel" class="carousel">
<div class="carousel_img">
<img class="active" src="img/1.jpg"/>
<img src="img/2.jpg"/>
<img src="img/3.jpg"/>
</div>
<img src="img/3.jpg"/>
<button><</button>
<button>></button>
<ul>
<li dy="0" class="active"></li>
<li dy="1"></li>
<li dy="2"></li>
</ul>
</div>
<script type="text/javascript">
var btns=document.getElementsByTagName("button"),
imgs=document.getElementsByTagName("img"),//[img,img,img,img]
lis=document.getElementsByTagName("li"),//[li,li,li]
j=0;
for(var i in btns){
btns[i].onclick=function(){
if(this.innerText==">"){
j++;
j==lis.length&&(j=0);
for(var i=0;i<lis.length;i++){
lis[i].className="";
imgs[i].className="";
}
imgs[j].className="active";
lis[j].className="active";
}else{
j--;
j==-1&&(j=lis.length-1);
for(var i=0;i<lis.length;i++){
lis[i].className="";
imgs[i].className="";
}
imgs[j].className="active";
lis[j].className="active";
}
}
}
for(var i in lis){
lis[i].onclick=function(){
j=this.getAttribute("dy");//修改全局
for(var i=0;i<lis.length;i++){
lis[i].className="";
imgs[i].className="";
}
imgs[j].className="active";
lis[j].className="active";
}
}
timer=setInterval(function(){
j++;
j==lis.length&&(j=0);
for(var i=0;i<lis.length;i++){
lis[i].className="";
imgs[i].className="";
}
imgs[j].className="active";
lis[j].className="active";
},1000)
carousel.onmouseover=function(){
clearInterval(timer);
}
carousel.onmouseout=function(){
timer=setInterval(function(){
j++;
j==lis.length&&(j=0);
for(var i=0;i<lis.length;i++){
lis[i].className="";
imgs[i].className="";
}
imgs[j].className="active";
lis[j].className="active";
},1000)
}
</script>
案例:复杂的轮播(封装版)
<script type="text/javascript">
var btns = document.getElementsByTagName("button"),
imgs = document.getElementsByTagName("img"), //[img,img,img,img]
lis = document.getElementsByTagName("li"), //[li,li,li]
j = 0;
for(var i in btns) {
btns[i].onclick = function() {
if(this.innerText == ">") {
animate(1)
} else {
animate(-1)
}
}
}
for(var i in lis) {
lis[i].onclick = function() {
animate(0, this)
}
}
timer = setInterval(function() {
animate(1);
}, 1000)
carousel.onmouseover = function() {
clearInterval(timer);
}
carousel.onmouseout = function() {
timer = setInterval(function() {
animate(1);
}, 1000)
}
function animate(num, li) {
if(num) {
j += num; //2
if(j == lis.length) {
j = 0
} else if(j == -1) {
j = lis.length - 1;
}
} else {
j = parseInt(li.getAttribute("dy"));
}
for(var i = 0; i < lis.length; i++) {
lis[i].className = "";
imgs[i].className = "";
}
imgs[j].className = "active";
lis[j].className = "active";
}
</script>
5、创建元素并且渲染页面
a、创建空标签
var elem=document.createElement("标签名");
b、设置必要的属性或事件
elem.属性名="属性值";
elem.on事件名=function(){函数体} - 事件都可以在创建时提前绑定上
c、创建好的元素渲染到DOM树上
父元素.appendChild(elem);
案例:根据一个字符串渲染出表格
<script type="text/javascript">
//从数据库获取而来的
var str="李5%刺客%10#王二麻子%奶妈%90";
var table=document.createElement("table");
var arr=["姓名","职业","剩余生命"];
var tr=document.createElement("tr");
table.appendChild(tr);
var peoples=str.split("#");
for(var i=0;i<peoples.length;i++){
var tr=document.createElement("tr");
var people=peoples[i].split("%");
for(var j=0;j<people.length;j++){
var td=document.createElement("td");
td.innerHTML=people[j];
tr.appendChild(td);
}
table.appendChild(tr);
}
bd.appendChild(table);
</script>