js初学第二周总结

88 阅读17分钟

***数据类型的转换: 1、强制转换:3类 1、转字符串: 1、x.toString();//x不能是null和undefined,不能用.做操作 2、String(x);//此方法是万能的,任何类型都可以转为字符串,完全相当于隐式转换,绝对不要手动使用此方法,还不如 +"" 不重要:1、页面上的一切数据默认都是字符串类型

*转数字: 1、parseInt/Float(str);//专门为字符串转数字准备的 原理:从左向右,依次读取每个字符,碰到非数字字符则停止,如果一来就不认识,则为NaN,Float认识第一个小数点

2、Number(x);//此方法是万能的,任何类型都可以转为数字,完全相当于隐式转换,绝对不要手动使用此方法,还不如 -0 *1 /1 %1

3、转布尔: Boolean(x);//此方法是万能的,任何类型都可以转为布尔,完全相当于隐式转换,绝对不要手动使用此方法,还不如 !!x ***大部分情况都为true,只有6个为false:0,"",undefined,null,NaN,false 在分支、循环的【条件】之中,以后不管代老师写什么,他都会悄悄的转为一个布尔值,你只需要知道为不为这6个,不为这六个说是true

2、隐式转换:都出现在运算符之中

2、****运算符和表达式 1、算术运算符:+ - * / % 特殊:1、%,取余,俗称模,判断奇偶性 2、隐式转换:默认转为数字,再运算 true->1 false->0 undefined->NaN null->0 特殊:1、+运算,碰上一个字符串,变拼接操作 2、-/%,字符串也可以转为一个数字,前提是纯数字组成的,但凡包含一个非数字字符则为NaN,NaN参与任何算术运算结果都为NaN

*比较运算符:> < >= <= == != === !== 结果:布尔值 隐式转换:默认,转为数字,再比较大小 特殊:1、如果参与比较的左右两边都是字符串,则按位PK每个字符的十六进制unicode号(十进制ascii码) 0-9<A-Z<a-z<汉字

2、NaN参与任何比较运算结果都为false,解决:!isNaN(x);

3、undefined==null;//true; 区分:undefined===null; === !==:不带隐式转换的等值比较和不等比较,要求数值相同,并且数据类型也要相同 function String(x){ // if(x===null){ // return "null"; // }else if(x===undefined){ // return "undefined"; // }else{ // return x.toString(); // } // }

3、*逻辑运算符: &&:全部为true,才为true 一个false,则为false

||:全部为false,才为false 一个true,则为true

!:颠倒布尔值

特殊:短路逻辑:如果前一个条件,已经能够得出最终结论了,则不看后续 &&短路:如果前一个条件满足,则后一个操作执行,如果前一个条件不满足,则后一个操作不执行 目的:简化【简单的】if分支:1、一个条件一件事,满足就做不满足就不做 2、操作只能有一句话 简化:条件&&(操作); 原来:if(total>=500){total*=0.8} 现在:total>=500&&(total*=0.8)

||短路:如果前一个为true,后一个不看 - 实现浏览器的兼容性,二选一操作 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;//最low的写法

鄙视题:++i 和 i++的区别? 单独使用,两者没有区别。 如果参与了别的表达式,变量中的值都会+1 前++,返回的是加了过后的新值 后++,返回的是加了之前的旧值

6、三目运算:简化if...else if...else if...else 语法: 1、条件?操作1:默认操作;

2、条件1?操作1:条件2?操作2:默认操作;

特殊: 1、默认操作不能省略 2、只能简化【简单的分支】 - 操作只能有一句话

汇总:*****自定义Function:函数,也称之为方法,是需要提前预定义好的,以后可以反复使用的代码段 1、***创建函数的方式:2种 1、*声明方式:function 函数名(形参列表){ - 不是人人都有声明方式,但是有声明方式的人一定优先用声明方式,因为最简单,只有三个人具有声明方式:变量、常量、函数 函数体; return 返回值; }

2、直接量方式:- 大部分人直接量就是最简单的创建方式了 var 函数名=function(形参列表){//通过此处看出函数名其实也是一个变量名,衍生出,elem.on事件名这一坨其实就是函数名,无非涨得长了点 函数体; return 返回值; }

return的本意,退出函数的意思 只不过如果return后面跟着一个数据,会顺便将其返回到全局作用域之中,但是只负责返回不负责保存! 所以再调用函数时,如果有return,记得拿一个变量接住他

调用函数:var result=函数名(实参列表)

2、***作用域:2种 1、全局作用域:成员:全局变量和全局函数,在任何位置都可以访问|使用

2、函数/局部作用域:成员:局部变量和局部函数,只能在【函数调用时内部可用】

带来了变量得使用规则:优先使用局部的,局部没有找全局,全局没有就报错 特殊: 1、千万不要对着未声明的变量直接赋值:a=1; - 会导致全局污染,全局本来没有,突然就被添加上了一坨内存,建议任何变量使用之前一定要先var 2、儿子不孝啊,局部的东西全局居然不能用,解决:看上面, 3、哪怕没有写return,其实也会悄悄给你return一个undefined,默认 4、return一般只会出现再函数的最后,而且只能出现一次 5、其实前辈们提供给我们的方法,大部分底层都有return操作,因为前辈们考虑到了,调用了他这个方法过后得到的结果可能对我们以后有用

3、***声明提前:鄙视重点 - 祖师爷留下的一个问题 在程序正式执行之前 会将var声明的变量和function声明的函数 集中提前到当前作用域的顶部,但是赋值留在原地 变量比函数轻

我们写代码绝对不会碰到,只要遵守规则:1、先创建后使用 2、变量名和函数名尽量的不要重复 只会在鄙视中碰到:如果以后你碰到先使用后创建,多半都是在靠你声明提前

4、***按值传递: 如果传递的是【原始类型的值】 修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方

如果传递的是【引用类型的对象】:Array、Function 修改一个变量,另一个变量是会受到影响的,因为两者使用的是【同一个地址值】 - 浅拷贝:好像是复制了,但是复制的不全


2、预定义全局函数:前辈们提前创建好的,我们可以直接使用的,在哪里都可以使用 1、编码和解码:- 玩玩悄悄话 问题:url中不允许出现多字节字符,如果出现会乱码 utf-8编码格式下,一个汉字,占3字节 解决:发送前,前端将用户输入的多字节字符编码为单字节字符(符号、字母) 发送后,后端将单字节字符解码为多字节原文

编码:var code=encodeURIComponent("原文"); 解码:var 原文=decodeURIComponent(code); 其实百度根本就没做这个操作,这个东西在某次浏览器更新后,就自带此功能了,当场就淘汰了

2、isFinite(num); 判断num是不是无穷大,true->有效数字 false->无穷大 哪些会为false:NaN,分母为0(java就会错),Infinity

3、牛逼的:parseInt/Float/isNaN/eval()


3、分支结构:根据条件,选择一部分代码执行 switch分支 if分支 语法: switch(变量/表达式){ case 值1: 操作1; case 值2: 操作2; default: 默认操作 } 特殊:1、case 的比较不带隐式转换 2、问题:默认只要一个case满足后,会将后面所有的操作全部做完 解决:break; 建议:每一个case的操作后都跟上一个break 有的地方也可以不加break: 1、最后一个操作default可以省略break 2、如果中间多个操作做的事儿是一样,可以省略中间部分 3、default可以省略不写,但如果条件都不满足,则什么都不会执行

面试题:if vs switch 1、switch...case...,缺点:必须要知道最后的结果是什么才可以使用,在case中不能做范围判断,只能做等值判断 优点:执行效率相对较高

2、if:缺点:执行效率相对较低 优点:可以随意的做范围判断

建议:代码优化时,要尽量的将所有的 if...else... 换成 switch...case... if(){} ===> &&短路 if...else ===> 三目 if...else if...else ===> 三目 *****自定义Function:函数,也称之为方法,是需要提前预定义好的,以后可以反复使用的代码段 1、***创建函数的方式:2种 1、*声明方式:function 函数名(形参列表){ - 不是人人都有声明方式,但是有声明方式的人一定优先用声明方式,因为最简单,只有三个人具有声明方式:变量、常量、函数 函数体; return 返回值; }

2、直接量方式:- 大部分人直接量就是最简单的创建方式了 var 函数名=function(形参列表){//通过此处看出函数名其实也是一个变量名,衍生出,elem.on事件名这一坨其实就是函数名,无非涨得长了点 函数体; return 返回值; }

return的本意,退出函数的意思 只不过如果return后面跟着一个数据,会顺便将其返回到全局作用域之中,但是只负责返回不负责保存! 所以再调用函数时,如果有return,记得拿一个变量接住他

调用函数:var result=函数名(实参列表)

2、***作用域:2种 1、全局作用域:成员:全局变量和全局函数,在任何位置都可以访问|使用

2、函数/局部作用域:成员:局部变量和局部函数,只能在【函数调用时内部可用】

带来了变量得使用规则:优先使用局部的,局部没有找全局,全局没有就报错 特殊: 1、千万不要对着未声明的变量直接赋值:a=1; - 会导致全局污染,全局本来没有,突然就被添加上了一坨内存,建议任何变量使用之前一定要先var 2、儿子不孝啊,局部的东西全局居然不能用,解决:看上面, 3、哪怕没有写return,其实也会悄悄给你return一个undefined,默认 4、return一般只会出现再函数的最后,而且只能出现一次 5、其实前辈们提供给我们的方法,大部分底层都有return操作,因为前辈们考虑到了,调用了他这个方法过后得到的结果可能对我们以后有用

3、***声明提前:鄙视重点 - 祖师爷留下的一个问题 在程序正式执行之前 会将var声明的变量和function声明的函数 集中提前到当前作用域的顶部,但是赋值留在原地 变量比函数轻

我们写代码绝对不会碰到,只要遵守规则:1、先创建后使用 2、变量名和函数名尽量的不要重复 只会在鄙视中碰到:如果以后你碰到先使用后创建,多半都是在靠你声明提前

4、***按值传递: 如果传递的是【原始类型的值】 修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方

如果传递的是【引用类型的对象】:Array、Function 修改一个变量,另一个变量是会受到影响的,因为两者使用的是【同一个地址值】 - 浅拷贝:好像是复制了,但是复制的不全


2、预定义全局函数:前辈们提前创建好的,我们可以直接使用的,在哪里都可以使用 1、编码和解码:- 玩玩悄悄话 问题:url中不允许出现多字节字符,如果出现会乱码 utf-8编码格式下,一个汉字,占3字节 解决:发送前,前端将用户输入的多字节字符编码为单字节字符(符号、字母) 发送后,后端将单字节字符解码为多字节原文

编码:var code=encodeURIComponent("原文"); 解码:var 原文=decodeURIComponent(code); 其实百度根本就没做这个操作,这个东西在某次浏览器更新后,就自带此功能了,当场就淘汰了

2、isFinite(num); 判断num是不是无穷大,true->有效数字 false->无穷大 哪些会为false:NaN,分母为0(java就会错),Infinity

3、牛逼的:parseInt/Float/isNaN/eval()


3、分支结构:根据条件,选择一部分代码执行 switch分支 if分支 语法: switch(变量/表达式){ case 值1: 操作1; case 值2: 操作2; default: 默认操作 } 特殊:1、case 的比较不带隐式转换 2、问题:默认只要一个case满足后,会将后面所有的操作全部做完 解决:break; 建议:每一个case的操作后都跟上一个break 有的地方也可以不加break: 1、最后一个操作default可以省略break 2、如果中间多个操作做的事儿是一样,可以省略中间部分 3、default可以省略不写,但如果条件都不满足,则什么都不会执行

面试题:if vs switch 1、switch...case...,缺点:必须要知道最后的结果是什么才可以使用,在case中不能做范围判断,只能做等值判断 优点:执行效率相对较高

2、if:缺点:执行效率相对较低 优点:可以随意的做范围判断

建议:代码优化时,要尽量的将所有的 if...else... 换成 switch...case... ***循环结构: 1、*var 变量=几;while(循环条件){循环体;变量的变化} 2、var 变量=几; do{ 循环体; 变量的变化 }while(循环条件)

面试题:while和do...while的区别? 只看第一次,如果条件都满足,则没区别 如果条件不满足,则while一次都不会执行,do...while至少会执行一次

3、*for(var 变量=几;循环条件;变量的变化){ 循环体; }

4、*退出循环: break - 退出整个循环 continue - 退出本次循环

5、死循环: while(1){} for(;;){}

2、*****数组的基础: 1、概念:什么是数组:一个内存(变量)中保存多个数据的一个集合结构 何时:只要存储的多个相关的数据,都要用数组集中保存 为什么:一个好的数据结构,可以极大的提升程序员的开发效率

2、创建:2种 *直接量:var arr=[值1,....]; 构造函数:var arr=new Array(值1,....); - 有一个坑new Array(num);//创建了一个长度num的空数组

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

4、数组的三大不限制:长度、类型、下标越界(不好)

5、数组的唯一的属性:长度:arr.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! 遍历数组,不能再使用for循环,必须使用for in循环 - 纯自动化循环,【专门为遍历数组准备的】 for(var i in 数组名){ i;//自动得到下标 数组名[i];//得到当前次元素 } 很棒:不光可以遍历hash数组,还可以遍历索引数组。 个人:遍历索引数组使用for循环,遍历hash数组再使用for in

4、hash数组的原理: hash算法:将字符串,计算出一个尽量不重复的数字(地址值) 字符串的内容相同,则计算出来的数字也是相同的 添加元素:将自定义下标交给了hash算法,得到了一个数字(地址值),直接将你要保存的数据放到了此地址 获取元素:将指定的自定义下标交给hash算法,得到一个和当初添加上完全相同的数字(地址值),通过地址就可以找到你当初保存的数据

5、***js里万物皆对象(除了undefined和null),【一切对象的底层都是hash数组】

3、*****数组的API:数组其实就属于我们11个引用类型对象之一,对象就会有属性和方法,数组有很多很多API(数组的专属方法)等待我们学习 1、*****数组 转为 字符串 var str=arr.join("自定义连接符");

固定套路:2个 1、鄙视题:将数组里面的内容拼接为一句话/单词,无缝拼接; arr.join("");

2、将数组里面的数据拼接为DOM页面元素 //数据 var arr=["-请选择-","北京","南京","西京","东京","重庆"]; //将数组转为字符串,只不过是拼接了标签

//渲染到DOM树上 bd.innerHTML=str

2、*拼接数组:添加元素的新方式 //根据你传入的实参全部拼接到数组的末尾 var newArr=arr.concat(新值1,...); 特殊:1、不修改原数组,只会返回一个拼接后的新数组 2、支持传入一个数组参数进行拼接,悄悄的将你的数组打散放入

3、*截取子数组: //根据你传入的开始下标一直截取到结束下标 var subArr=arr.slice(starti,endi+1); 特殊:1、不修改原数组,只会返回一个截取后的新数组 2、含头不含尾 3、endi可以省略不写,会从starti一直截取到末尾 4、两个实参都可以省略,从头截到尾,复制了一份(深拷贝-两者互不影响) 5、支持负数参数,-1代表倒数第一个

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

以下的API都会修改原数组

4、*删插替: 删除:var dels=arr.splice(starti,n);//n代表删除的个数 特殊:其实splice也有返回值,返回的是你删除的元素组成的一个新数组 插入:arr.splice(starti,0,新值1,...); 特殊:1、原starti位置的元素以及后续元素都会被向后顺移 2、千万不要插入一个数组,会变成到土不洋的数组 替换:var dels=arr.splice(starti,n,新值1,...); 特殊:插入的个数和删除的个数不必相等

5、反转数组:arr.reverse();
*****数组的API: 1、*****排序:2种 1、鄙视题:手写冒泡排序:从第一个元素开始,依次比较相邻的两个元素,如果前一个>后一个,两者就要交换位置 公式: for(var j=1;j<arr.length;j++){ for(var i=0;i<arr.length-j;i++){ console.log(1); if(arr[i]>arr[i+1]){ var m=arr[i]; arr[i]=arr[i+1]; arr[i+1]=m; } } }

2、正式开发中:数组提供的排序API: arr.sort(); 问题1:默认会将元素们转为字符串,按位PK每个字符unicode(ascii),如果希望按数字排序? 解决: arr.sort(function(a,b){//sort传了一个实参:比较奇怪,是一种匿名回调函数,不需要程序员自己调用,前辈们已经帮我们调好了,而且我们不需要管前辈们怎么创建的,只需要记忆固定用法即可 // console.log(a);//后一个数字 // console.log(b);//前一个数字 return a-b;//如果return 是一个正数:说明后一个数>前一个数 //如果return 是一个负数:说明后一个数<前一个数 //如果return 是一个0:说明后一个数==前一个数 //而sort方法会根据你return的正/负数,来自动进行排序操作 })

问题2:希望降序排列 arr.sort(function(a,b){ return b-a; })

强调:排序非常重要,记住:只要以后页面中有排序功能,说明他的底层一定是一个数组,因为js中只有数组才可以排序

2、栈和队列: 栈:其实就是数组,只不过要求是一段封闭,只能从另一端进出的数组 何时:希望优先使用最新的数据时,现实生活中:电梯、旅游公交车...现实生活中少的,代码中用的也少 如何使用: 开头进:arr.unshift(新值1,...);//添加元素的新方式,缺陷:导致其余元素的下标发生变化 开头出:var first=arr.shift();//一次只能删除一个,删除元素的新方式,可以接住你删除的元素,缺陷:导致其余元素的下标发生变化

结尾进:arr.push(新值1,...);//添加元素的新方式 结尾出:var last=arr.pop();//一次只能删除一个,删除元素的新方式,可以接住你删除的元素

队列:其实就是数组,只不过要求是一端进,另一端出 何时:希望按照先来后到的顺序使用数据时,生活中多个一批 开头进:arr.unshift(新值1,...); 结尾出:var last=arr.pop();

结尾进:arr.push(新值1,...); 开头出:var first=arr.shift();

ES3的数组API就到此告一段落了

2、*二维数组:数组的元素,又引用着另一个数组 何时:在一个数组内,希望再次细分每个分类 创建: var arr=[ ["杨鑫",18,3500], ["是新建",19,4500], ["杨婷婷",20,5500] ];

访问:arr[行下标][列下标]; 特殊:列下标越界,得到undefined 行下标越界,会报错,因为只有数组才可以用[下标]。行下标越界已经得到undefined,没有资格再加下标

遍历二维数组:必然需要两层循环,外层循环控制行,内层循环控制列 - 现在都流行扁平化 公式:for(var r=0;r<arr.length;r++){ for(var c=0;c<arr[r].length;c++){ console.log(arr[r][c]) } }

3、*****String的概念: 什么是字符串:多个字符组成的【只读】字符【数组】(只读:明天我们要学习的任何字符串API,都是不会修改原字符串,意味着必须要拿一个变量接住结果) 和数组有相同的点: 1、字符串中个数(长度):str.length 2、获取字符串中的某个字符:str[i]; 3、遍历字符串 4、所有数组不修改原数组的API,字符串都可以使用(concat、slice)

和数组也有很多不同的地方: 所有的数组直接修改原数组的API,字符串都不可以使用!比如排序只有数组可以使用,但是 字符串也有很多很多属于自己的API

***引用/对象类型:11个 *String Number Boolean -> 为什么这三个人即使原始类型又是引用类型?原因是他们三个具有包装类型? *Array *Function Date(日期) Math(数学) *RegExp(正则->表单的验证) Error(错误) *Object(面向对象开发方式) Global(全局对象) -> 特殊:再浏览器中没有global被window(全局对象:保存着全局变量和全局函数)给代替了,只不过window可以省略不写 只有JavaScript中global才被代替为了window,而且node.js后端语言中!全局对象就叫global。

***面试题:什么是包装类型? 包装类型:专门将原始类型的值封装为一个引用类型的对象 为什么:原始类型的值原本是没有任何属性和方法的,意味着原始类型本身就是不支持.做操作的 但是前辈们发现字符串经常会被我们程序员所使用/操作 为了方便我们程序员为这三个人提供了包装类型,从值->对象(提供了很多属性和方法) 何时使用:只要你试图用原始类型的变量去调用属性或方法时,自动包装 何时释放:方法调用完毕后,包装类型就会自动释放,又变回了一个原始类型的值

为什么undefined和null不能使用. - 没有包装类型
只读: 1、StringAPI:只有字符串可以使用的方法,无需创建,直接使用。 1、转义字符:
作用: 1、将字符串和程序冲突的字符转为原文 """

2、包含特殊功能的符号 \n -> js字符串换行 \t -> js字符串中的制表符,就是你平时敲键盘的tab键,大空格

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可以省略,如果省略了,从下标0开始向右查找 返回值:找到了,返回的是第一个关键字的第一个字符的下标 *没找到,返回-1,我们不关心下标为多少,我们只关心下标为不为-1 作用:判断有没有 强调:不光字符串可用,数组也可用!老IE的数组是没有此方法的 鄙视题:此方法默认只能找到第一个关键字的下标,找到所有关键字的下标,如何完成? var str="no zuo no die no can no bibi"; var index=-1;//开始位置 -1 while((index=str.indexOf("no",index+1))!=-1){ console.log(index); }

6、*截取字符串:3个 *var subStr=str/arr.slice(starti,endi+1);//用法和数组一摸一样 str.substring(starti,endi+1);//垃圾:用法几乎和slice一致,但是不支持负数参数,而且只有字符串可用
str.substr(starti,n);//n代表截取的个数,不再需要考虑含头不含尾,也支持负数下标

7、拼接字符串:var newStr=str.concat(新字符串,...);//还不如+运算

8、*替换字符串:本身这个方法非常强大,但是由于我们现在暂时不会正则表达式,所以只能替换固定的关键字(下周一就牛逼了) var newStr=str.replace("关键字"/正则表达式,"新内容");

9、*****切割/分割字符串: 作用:str<=>arr var arr=str.split("自定义切割符"); 特殊:1、切割后,切割符就不存在了,转为一个数组 2、切割符"",切散每一个字符

扩展: 1、JS创建空标签: var elem=document.createElement("标签名");

2、为其设置必要的属性和事件 elem.属性名="属性值"; elem.on事件名=function(){操作}

3、上树 父元素.appendChild(elem);