第二周:
位运算:
左移:m<<n:读作m左移了n位,翻译:m*2的n次方
右移:m>>n:读作m右移了n位,翻译:m/2的n次方
垃圾的原因:底数始终只能是2
赋值运算:+= -= *= /= %=
一个操作两件事,先计算,在赋值回去
i++; //递增:每次固定只能+1
i+=1;//累加:每次加几由程序员自己决定
i++ => i+=1 => i=i+1;
鄙视题:++i 和 i++ 的区别?
1、单独使用(独立存在)时,放前放后无所谓,完全一样
2、如果参与了别的表达式,变量中的值都会+1
前++返回的是【加了过后】的新值
后++返回的是【加了之前】的旧值
三目运算:简化【简单的】分支结构if(){}eles{} if(){}else if(){}eles{}
语法:
1、条件?操作1:默认操作;
2、条件1?操作1:条件2?操作2:默认操作;
特殊:
1、【简单的】,操作只能有一句话
2、默认操作必须写出来,不能省略,省略会导致语法错误
总结:
if === &&短路
if...else === 三目
if...else if...else === 三目
2、计算机带有舍入误差 - 解决:num.toFixed(d);//d代表保留的小数位数,会自动四舍五入 此方法有一个唯一的小缺陷:最好搭配上一个parseFloat使用
获取字符串中第一个字符的ascii码:var ascii=str.charCodeAt(0);
自定义Function
什么是函数:需要先定义好,可以反复使用的一个代码段
何时使用:1、不希望打开页面立刻执行 2、以后可以反复使用 3、希望绑定在页面元素上 4、第一等公民地位
如何使用:
1、***创建并且调用:
1、创建:2种方式
1、*【声明方式】创建函数
function 函数名(形参,...){
函数体
return 返回值/结果;
}
2、【直接量方式】
var 函数名=function(形参,...){
函数体
return 返回值/结果;
}
通过第二种创建方式:学到了一个点:函数名其实就是一个变量名
2、调用:var result=函数名(实参,...);
其实return的本意是退出函数,但是如果return后面跟着一个数据
顺便将数据返回到函数作用域的外部,但是return只负责返回,不负责保存
所以调用函数时,如果里面有return操作,记得拿一个变量接住结果
就算你省略return,默认也会返回return undefined;
具体要不要得到函数的结果,看你自己
***作用域:2种
1、全局作用域:全局变量和全局函数,在页面的任何一位置都可以访问到
2、函数作用域:局部变量和局部函数,在【当前函数调用时内部可用】
*变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错
特殊:缺点:
1、千万不要在函数中对着未声明的变量直接赋值 - 全局污染(全局没有的东西,突然被局部添加上了)
解决:使用变量之前记得先var了来在使用
2、局部可以使用全局的,但是全局不能使用局部的 - 解决:看上面的return操作
***声明提前:
在程序正式执行之前
悄悄的,将var声明的变量(轻)和function声明的函数(重)
集中提前到当前作用域的顶部
但是赋值留在原地
声明方式创建的函数会完整的提前(第一种方式)
直接量方式创建的函数不会完整提前,只有变量部分提前(第二种方式)
何时使用:永远不会自己使用,垃圾干扰我们判断 - 只会在鄙视中遇到,为什么平时开发不会遇到呢?
只要遵守一下规则:
1、变量名和函数名尽量的不要重复
2、先创建后使用
3、如果鄙视时碰到了先使用后创建,多半都是在考你声明提前
、***按值传递:两个变量之间进行赋值
如果传递的是【原始类型】的值:
修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方
如果传递的是【引用类型】的对象:- 除了那5个,其余的都是引用类型(Array、Function...)
修改一个变量,另一个变量是会受到影响的,因为大家其实操作的都是同一个地址值
4、预定义全局函数:前辈们提前创建好的方法,我们程序员可以直接使用,任何位置都可以使用 1、编码和解码:-> 玩个悄悄话,你编码发到群里,其他人来解码 问题:url中不允许出现多字节字符的,如果出现会乱码 utf-8编码格式下,一个汉字占3字节 解决:发送前,前端将多字节字符原文编码为单字节字符(数字、字母) 发送后,后端将单字节字符解码为多字节原文
如何:
编码:var code=encodeURIComponent("倚天屠龙记");
解码:var 原文=decodeURIComponent(code);
其实这个东西在某次浏览器更新后,当场就淘汰了,浏览器现在自带此功能
2、isFinite(num);判断num是不是无穷大:true->有效数字 false->无穷大
哪些会为false:NaN,Infinity,分母为0
3、*其实真正有用的反而是代老湿之前就交了你们的
parseInt/Float/eval/isNaN
5、***分支结构:根据条件的不同,选择部分代码执行:if分支、三目、&&短路、switch分支 语法: switch(变量/表达式){ case 值1: 操作1; case 值2: 操作2; case 值3: 操作3; default: 默认操作; }
特殊:
1、case作比较的使用不带有隐式转换
2、问题:默认只要一个case满足后,会将后面所有的操作全部做完
解决:break;
建议:每一个case的操作后面都要跟上一个break;
有的地方也可以不加break:
1、最后的一个操作default可以省略break
2、如果中间多个条件,做的操作是一样的,也不需要break,甚至可以省略掉中间部分
3、default可以省略不写,但是不推荐,如果所有条件都不满足,则什么事都不会发生
if vs switch
1、switch...case:缺点:必须要知道最后的结果才能使用,不能做范围判断
优点:执行效率相对较高
2、if...else:优点:能做范围判断
缺点:执行效率相对较低
建议:开发时,你爱用什么用什么,开发完了,要做代码优化,要尽量的把if考虑能不能换成switch
扩展:1、js动画:几乎和js没有关系,与css有关(transition过渡) js操作样式,瞬间生效的,只要加上过渡就会慢慢生效 - animate.css动画库(库:很多动画) 网址:animate.style/
数组的基础
1、基础概念:
什么是数组:在一个内存(变量)中保存多个数据的一个集合机构
何时:只要保存的多个相关的数据,都要用数组集中保存
为什么:一个好的数据结构,可以极大的提升我们程序员的开发效率
2、创建:2种
1、*直接量:var arr=[值1,...];
2、构造函数:var arr=new Array(值1,...);
第二种方式还有一个坑:var arr=new Array(num);//创建了一个长度为num的空数组
3、访问:数组名[下标]; -- 当前元素
添加/修改:数组名[下标]=新值;
特殊:获取元素,下标越界 - 返回undefined
添加元素,下标越界 - 稀疏数组,导致下标不在连续,导致搭配上for遍历会得到很多很多的undefined
4、数组有的三大不限制:
1、不限制长度
2、不限制类型
3、不限制下标越界 - 不是好事
5、数组唯一的属性:数组名.length - 获取数组的长度
三个固定套路:
1、末尾添加:arr[arr.length]=新值
2、获取倒数第n个元素:arr[arr.length-n]
3、缩容:删除倒数n个元素: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循环 - 专门用于遍历hash数组的
for(var i in arr){
i;//自动得到所有的下标,不需要我们设置从哪里开始到哪里结束,一切都是自动化的
arr[i];//当前次元素,想要干什么看你自己
}
for in循环非常牛逼,不光可以遍历hash数组,也可以遍历索引数组
个人推荐:索引数组依然使用for,hash数组在用for in
4、***面试题:hash数组的原理:
hash算法:将字符串,交给hash算法,计算出一个尽量不重复的数字(地址值)
字符串内容相同,则计算出来的数字也一定是相同的
添加元素:将自定义下标交给hash算法,得到一个数字(地址值),直接将你要保存的数据放到这个地址里面去
获取元素:将指定的自定义下标交给hash,得到一个和当初保存时一样的数字(地址值),通过地址值就能拿到你当初存进去的数据
5、js之中一切东西都是对象,除了undefined和null,【一切对象的底层都是hash数组】
、*****数组的API:前辈们提供好的一些方法,这些方法只有数组可以使用
1、*arr to str:
var str=arr.join("自定义连接符");
固定套路:2个
1、鄙视题:将数组中的每个数据,拼接为一句话/单词
var arr=["h","e","l","l","o"," ","w","o","r","l","d"];
无缝拼接:var str=arr.join("");
2、将数组拼接为DOM页面元素
//以后会从数据库里拿到数据 - 数据渲染
var arr=["-请选择-","北京","南京","西京","东京","重庆"];
//数组转为字符串并且拼接上了应有标签
var str="<开始标签>"+arr.join("<结束标签><开始标签>")+"<结束标签>"
//字符串渲染到DOM树上
sel.innerHTML=str;
2、*数组的拼接:添加元素的新方式
把你传入的实参全部拼接到arr的末尾
var newArr=arr.concat(新值1,...);
特殊:
1、不修改原数组,只会返回一个拼接后的新数组
2、concat支持传入数组参数,悄悄的将你传入的数组打散为单个元素后再拼接
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也有返回值,返回的就是你删除的元素组成的一个新数组,前辈们考虑有可能你删掉的东西,刚好是你需要使用的东西
插入:var dels=arr.splice(starti,0,新值,...);
特殊:原starti位置的元素以及后续元素都会向后顺移
替换:1、var dels=arr.splice(starti,n,新值,...);
2、不推荐插入一个数组,一些是一维,一些是二维
特殊:插入的个数和删除的个数不必相同,随意
*****ArrayAPI:
1、*****排序:2种方式
1、鄙视题:手写冒泡排序:从第一个元素开始,依次比较相邻的两个元素,如果前一个元素>后一个元素,两者互换位置
固定公式:
for(var j=1;j<arr.length;j++){
for(var i=0;i<arr.length-j;i++){
if(arr[i]>arr[i+1]){
var middle=arr[i];
arr[i]=arr[i+1];
arr[i+1]=middle
}
}
}
2、正式开发中:排序API:
arr.sort();
问题1:默认会将元素们转为字符串,按位PK每个字符的unicode号,如果希望按照数字排序
解决:
//我们第一次遇到传入实参,传入的是一个函数,而且是一个没有函数名的函数,我们称呼这种函数叫做匿名回调函数,不需要我们程序员自己调用
//回调函数都是前辈们提供好的,我们只需要学习如何使用的
arr.sort(function(a,b){//a:后一个数 b:前一个数
return a-b;//返回一个正数:后一个数>前一个数
//返回一个负数:后一个数<前一个数
//返回一个0:后一个数==前一个数
//而我们的sort会根据你返回的正数/负数/0,来自动判断要不要交换两者的位置
});
问题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我们学完了,后面ES5还提供了一些新的API
3、二维数组:数组的元素,又引用着另一个数组
何时使用:在一个数组内,希望再一次细分每个分类
创建:
var arr=[
["喻杭",18,3500],
["尹天星",19,4500],
["陈磊",20,1500]
];
访问: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])
}
}
总结:
1、数组的基础(创建、访问、添加、length、遍历)
2、API:10个(转字符串,拼接,截取,删插替,翻转,排序,栈和队列)
3、二维数组
数组暂时告一段落,以后ES5还会有新的API
、*****String的概念/理论:
什么是字符串:多个字符组成的【只读】字符【数组】(只读:明天我们要学习的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(全局对象) -> 在【浏览器】中被window全局对象给代替了 - 有一天我们会学习后端语言Node.js(到时候你就会见到global)
如果有一天你发现有个方法前面没有对象,则一定是window对象,window对象可以省略不写
***包装类型:专门将原始类型的值封装为一个引用类型的对象(属性和方法)的
为什么:原始类型的值原本是没有任何属性和方法的,意味着原始类型本身是不支持.操作
但是前辈们发现字符串经常会被我们程序员所操作
为了方便我们程序员开发,为这三个人提供了包装类型(提供了属性和方法)
何时使用:只要试图用原始类型的变量调用属性或方法时,自动包装
和实施方:方法调用完毕后,自动释放包装类型,并且返回数据(又变回原始类型)
为什么undefined和null不能使用. - 没有包装类型
只读:拿个变量接住结果
*****StringAPI:只有字符串可以使用的函数,无需创建,程序员直接使用
1、转义字符:\
作用:1、将字符串中和程序冲突得分字符转为原文
"\""
2、包含特殊功能的符号
\n - 换行
\t - 制表符,你平时敲一下tab
*3、输出unicode编码的字符
\uXXXX:
第一个汉字:4e00 - 19968
最后一个汉字:9fa5 - 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位置开始,查找右侧的【下一个关键字】的位置
starti可以省略,默认从0位置开始查找
返回值:找到,返回的第一个关键字的第一个字符的下标
*没找大,返回-1:我们根本不关心下标为多少,只关心为不为-1,
作用:【判断有没有】
强调:数组也可以使用此方法,后期才为数组添加的
鄙视题:默认只能获取到第一个关键字的下标,如何才能获取所有的关键字的下标呢?
var str="no zuo no die no can no bibi";
var i=-1;//-1 22
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、*****切割/分割字符串:
作用:将字符串转为数组 arr <=> str
var arr=str.split("自定义切割符")
特殊:
1、切割后,切割符就不存在
2、切割符"",切散每个字符
扩展:3步
1、JS创建空元素: var elem=document.createElement("标签名");
2、为其设置必要的东西 elem.属性名="属性值"; elem.on事件名=function(){}
3、上DOM树 父元素.appendChild(elem);