javascript由三部分组成
1、**ECMAScript**(ES3/5/6) - 核心语法:前三天学习的内容 - 内功心法(以后可能做任何操作都离不开他们)
2、DOM - Document Object Model:文档 对象 模型 - 操作文档(HTML) - 外功招式(特效)
3、BOM - Browser Object Model:浏览器 对象 模型 - 操作浏览器 - 有重点但是用处不大
一、js概述
1、含义:js是运行在js解释器中的解释型、弱类型、面向对象的脚本语言
(一)、js解释器
是浏览器自带的不需要安装的,在node.js服务器端,需要安装一个独立的js解释器
(浏览器能识别html和css是因为浏览器也自带html和css解释器)
(二)、js的特点
解释型:不需要检查对错,直接运行,遇到错误就停止执行;(强类型:先检查对错,一旦错误直接就不运行)
弱类型:数据决定数据类型(强类型:由数据类型决定了程序员想要保存的数据是什么)
面向对象语言:万物皆对象(包含属性和方法)===>语法:对象名.属性名/对象名.方法名()
(三)、js的用途
css可以做的js可以做,css不可以做的js也可以做;可以用js中的ajax方法实现前端与服务器端的交互
(四)、js的使用方式(2种)
外部引入script文件====><script src="js文件路径"></script>
直接在html中书写<script >js代码</script>
使用外部引入方式最好:原因==>就算js执行耗时,还可以先看到html和css效果;js会操作html结构,如果js放在head中可能会找不到运算
(五)、js代码注释
作用: 提示自己/别人 2、玩 - 养成一个写注释的好习惯
单行注释://
多行注释:/**/
(六)、js输出方式:
控制台F12.输出方式:console.log()//强推,不会影响用户体验感
页面输出方式:documet.write();//绑定事件会替换页面已有内容
弹出框输出方式:alert();//会卡住页面,需要关闭弹出框后才可能看到页面的内容
(七)、变量
语法:var 变量名=变量值
特殊点:取名要见名知意;
不能以数字开头
连创多个变量可以省略后面的var并把;变为,
=是赋值符号,是把右边的变量值赋值给左边(可以用创建/定义/声明)
(八)、常量
语法:const 常量名=常量值
二、数据类型 (5种基本类型和11种对象类型)
(1)原始类型/值类型/基本类型: 数字,字符串,布尔值,Undefined,Null
Undefined无意义,默认值只有一个,就是undefined,
Null只有一个,就是null,用来释放变量/内存;
(2)引用类型
三、算数运算和表达式
(一)、算数运算
+ - * / %
具有隐式转换,默认会转为数字再运算;
(1)+运算中,遇到字符串会变成拼接,不再是+运算;
(2)在-*/ % 中,字符串也可以进行运算,但前提是纯数字字符串,如果是非纯数字字符串,则结果为NAN;
(3)xx转为数字的话,会转为什么结果
true->1
false->0
undefined->NaN
null->0
"100"->100
"100px"->NaN
NAN:Not A Number 不是一个数字,但确实是一个number类型(不是一个有效的数字)
NAN的缺点:NAN参与任何算术运算,结果仍为NAN;NAN参与任何比较/关系运算,结果都是false,甚至不认识自己
解决方案:!isNAN(x),判断是不是有效数字;
作用:防止用户恶意输入
结果:true是一个有效数字,false就是NAN
(二)、比较/关系运算符:> < >= <= == != === !==
返回的结果都为布尔值:true、false
往往比较运算符出现在分支、循环的判断条件之中
隐式转换:默认,左右两边都会悄悄的转为数字,再比较
特殊:
1、如果左右【两边】参与比较的都是字符串,按位PK每个字符的十六进制的unicode号(十进制ascii码)
0-9<A-Z<a-z<汉字
百度搜索:中文字符集 Unicode 编码范围
汉字的第一个字:一(unicode:4E00)-(ascii:19968)
汉字的最后一个字:龥(unicode:9fa5)-(ascii:40869)
2、NaN参与任何比较运算结果都为false,带了一个问题,没有办法使用普通的比较运算判断一个xx是不是NaN
解决:!isNaN(xx); true->有效数字 false->NaN
3、undefined==null,
问题:==区分不开undefined和null,怎么才能区分开?
undefined===null
全等:===,要求值和数据类型都要相同,就是不再带有隐式转换
!==,不带隐式转换的不等比较
写一个String()方法的底层原理
function String(x){
if(x===null){
return "null";
}else if(x===undefined){
return "undefined";
}else{
return x.toString();//return 返回
}
}
var result=String(x);
console.log(result);
(三)、逻辑运算符
悄悄变为布尔,然后比较得出结论
&&:与(并且)
只有全部条件都为true,最后结果才为true
只要有一个条件为false,最后结果就为false
||:或
只有全部条件都为false,最后结果才为false
只要有一个条件为true,最后结果就为true
!:颠倒布尔值:
!true -> false
!false -> true
特殊用法:短路逻辑
***短路逻辑:只要前一个条件已经可以得出最后结论,则后续条件不再执行!
&&短路:如果前一个条件为true,后一个操作才执行
如果前一个条件为false,后一个操作不执行
简化了【简单的】分支:if(条件){操作}
语法:条件&&(操作);
举例:曾经:if(total>=500){total*=0.8};
现在:total>=500&&(total*=0.8);
特殊:1、【简单的】-> 操作只能有一句话!
建议:能用短路分支绝对不用if分支
||短路:如果前一个条件为true,不需要执行后一个
如果前一个条件为false,需要执行后一个
使用场景:两个值二选一 - 后期做浏览器兼容性(老IE)
btn.onclick=function(e){//event事件对象 | e=e||window.event;//e->undefined |
console.log(e.screenX); |
}
(四)、位运算
左移:m<<n,读作m左移了n位,相当于m*2的n次方
右移:m>>n,读作m右移了n位,相当于m/2的n次方
缺点:底数永远只能是2
(五)、赋值运算
一句话执行了两个操作,先运算,再赋值
+= -= *= /= %= ++ --
何时使用:只要取出变量中的值,在做计算,之后还要在保存回去时,就要试用赋值运算
i=i+1;//比较low的写法
推荐:
i+=1 或者 i++;
递增:i++; 每次只能+1
累加:i+=n; 每次想加几,随便你
***鄙视时:++ 分为 ++i 和 i++
1、单独使用时,没有参与别的表达式,放前放后都一样
2、如果参与了别的表达式:
变量中的值其实都会+1
++i,返回的是递增后的【新值】
i++,返回的是递增前的【旧值】
案例: var i=1;
console.log(i++);//1
console.log(i);//2
var me=1;//5
var sn= me++ + ++me + me++ + ++me;
// 1 3 3 5
console.log(n);//
console.log(i);//
var me=1;//8
var sn= me++ + me++ + ++me + ++me + me++ + ++me + me++;
// 1 2 4 5 5 7 7
console.log(sn);//31
console.log(me);//8
(六)、三目运算:简化分支:if(){}else{} if(){}else if()else{}
语法:
1、条件?操作1:默认操作;
2、条件1?操作1:条件2?操作2:默认操作;
问题:1、只能简化简单的分支 - 三目操作也只能有一句话,不怕,很多情况操作就只有一句话
2、默认操作不能省略 - 哪怕条件都不满足,至少还要做一件事
总结:
if(){} == &&短路
if(){}else == 三目
if(){}else if(){}else == 三目
能用三目/短路尽量不用if分支
案例:var age=17;
if(age>=18){console.log("去网吧")}else{console.log("玩泥巴")};
console.log(age>=18?"去网吧":"玩泥巴");
var score=50;
if(score>=90){console.log("兰博基尼")}else if(score>=60){console.log("奥迪双钻")}else{console.log("钉子皮托")}
console.log(score>=90?"兰博基尼":score>=60?"奥迪双钻":"钉子皮托");
扩展:
用户输入框: var userprompt("提示文字","默认值")===>默认值可省略
查看数据类型:typeof()
四、数据类型转换
(一)、隐式转换(多半都在运算符之中)
1)、算数运算符:+-*/% 具有隐式转换:默认两边都转为数字,再运算
特殊点:见昨天的内容
2)、比较运算符
3)、逻辑运算符
(二)、显示/强制转换
隐式转换出来的结果不是我们想要的,程序员可以手动调用一些方法进行数据类型的转换
【1】、转字符串:2种
xx.toString();不执行undefined和null不能用.点操作
String(xx),万能的,完全等效于隐式转换,还不如直接+"".
【2】、转数字:3种
(1)、parseInt:转整数,只能是str和num
执行原理:从左向右依次读取每一个字符,碰到非数字字符则停止,并不认识小数点,如果一来不认识则为NAN。
console.log(parseInt(35.45));//35
console.log(parseInt("35px"));//35
console.log(parseInt("3hello5"));//3
console.log(parseInt("35.45"));//35
console.log(parseInt("hello35"));//NaN
console.log(parseInt(true));//NaN
(2)parseFloat:转小数,只能是str
执行原理:几乎和parseInt一直,但是认识第一个小数点
console.log(parseFloat("35.45"));//35.45
console.log(parseFloat("35.45.45"));//35.45
console.log(parseFloat("35.4abc5.45"));//35.4
console.log(parseFloat("35.45px"));//35.45
以上两个API作用:强制转为数字的同时并且去掉单位
(3)Number(x);
万能的,任何人都可以转为数字,完全等效与隐式转换,还不如*1 /1 -0
【3】转布尔:1种
Boolean;万能的,任何人都可以转为一个布尔值
只有6个会转为false:0 "" false undefined null NaN,其余的都为true
注意:在【循环或分支的条件判断】中都会带有此隐式转换,隐式转换为布尔值
if(Boolean(条件)){}
例子:(只要判断条件不为false,都会执行操作)
if(undefined){//条件其实不管些什么最后都会变成一个布尔值,悄悄的我们看不见的套上了一个Boolean方法
console.log("啦啦啦啦啦");
}
if([1,2,3,4,5]){
console.log("啦啦啦啦啦");
}
if(function f1(){}){
console.log("啦啦啦啦啦3");
}
if(Array.prototype.indexOf){//函数->true undefined->false
document.write("您的浏览器支持此方法")
}else{
document.write("您的浏览器是个垃圾")
}
五、分支结构
(一)、程序的结构:3种
1、顺序结构:默认结构,从上向下依次执行每一句代码
2、分支结构:根据判断条件,选择一部分代码去执行(只会走一条路)
3、循环结构:根据判断条件,选择是否重复执行某一段代码
(二)、分支结构
if分支:
1、一个条件一件事,满足就做,不满足就不做
if(判断条件){
操作
}
短路逻辑:if条件&&(操作)
2、一个条件两件事,满足就做第一件,不满足就做第二件
if(判断条件){
操作1
}else{
默认操作
}
三目运算:条件1?操作:默认操作;
3、多个条件多件事,满足谁就做谁
if(条件1){
操作1
}else if(条件2){
操作2
}else if(条件3){
操作3
}else{
默认操作
}
三目运算:条件1?操作:条件2?操作2:默认操作;
强调:
1、最后的else是可以省略的,但如果条件都不满足,则分支白写
2、条件有时候写的顺序需要注意,因为分支只会走一条路
switch....case分支
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... 缺点:必须要知道准备的结果才能使用,不能做范围判断
好处:执行效率相对较高
2、if...else 缺点:执行效率相对较低
优点:可以是范围判断
建议:代码优化,尽量将所有的 if...else 换成 switch...case、三目
案例

var user=parseInt(prompt("欢迎使用10086查询系统,\n1、查询话费 \n2、查询流量 \n3、查询套餐 \n4、人工服务"));
switch(user){
case 1:
alert("正在查询话费...");
break;
case 2:
case 3:
alert("正在维护中...");
break;
case 4:
alert("正在转接人工...");
break;
default:
alert("暂无此功能");
}
(三)、循环结构
循环三要素:循环变量;循环条件;循环变量的变化
1、while循环
var 循环变量=几;
while(循环条件){
循环体;
循环变量变化一下;
}
1、执行原理:
先判断循环条件,如果条件为真,则执行【一次】循环体
然后再判断循环条件,如果条件为真,则执行再【一次】循环体
...
直到条件为假,循环才会结束
注意:循环是一次一次执行的 - 并不是同事执行的,只不过计算机的CPU计算速度比较快
2、死循环:
永远不会结束的循环 - 保存死循环也是可以退出循环
何时:不确定要执行的具体次数,但往往死循环会搭配上break进行退出
while(true){
循环体
}
流程控制语句:
循环退出语句:break; - 出现循环之中
continue-退出当前次循环,会继续执行后面的操作
do.... while循环
var 变量=几;
do(循环体){
操作
循环变量变化
}while(循环条件)
while和do...while的区别
只看第一次:如果第一次大家都满足条件,则没有区别;
如果第一次大家都不满足条件,while一次都不会执行,do...while会执行一次操作。
2、for循环:
while能做,for也能做,而且for看上更加的简单/简洁
for(var 循环变量=几;循环条件;变量的变化){
循环体
}
特殊:
1、死循环:for(;;){循环体} - 两个分号一个不能少
2、循环变量处其实可以创建多个变量
3、也支持break
总结:
1、while:一般都不用,何时使用:不确定循环次数的时候
2、for:常用,确定循环次数时
六、函数
[一]、自定义函数
function,也叫方法
(一)、定义:
函数是一个被预定义好的,可以反复使用的代码,是个独立的功能体,可以将若干代码放在里面。
(二)、创建函数
(1)、声明方式
function 函数名(形参,.....){
函数体;
return 返回值/结果
}
(2)、直接量方式
var 函数名=function(形参,.....){
函数体;
return 返回值/结果
}
return本意是指退出函数,但如果后面跟着一个数据,顺便将数据返回到函数作用域的外部,但return只负责返回结果不负责保存,需要自己去创建一个变量接住结果;
return只能写一次,并且最好是写在函数体的最后;同时return可以省略,会返回默认值undefined。
(2)、构造函数方式
var 函数名=new Function("形参1","形参2",...,"函数体;return 返回值;");
何时使用:如果你的函数体不是固定的而是动态拼接的一个字符串
比如:以下案例,我们让用户来参与到底是顺序还是降序排列
var arr=[321,5,43,65,8,213,43,765,12];
var user=prompt("排序数组,如果您输入a-b则为升序排列,如果您输入b-a则为降序排列");//"a-b" "b-a"
var compare=new Function("a","b","return "+user);
arr.sort(compare);
console.log(arr);
(三)、调用
var 接住返回的结果=函数名(实参,...);
绑定在页面元素之上,用户来触发了:<elem onclick="js语法">
函数名(实参)//调用几次执行几次</elem>
(四)、什么东西适合放在函数之中?(何时使用)
1、不希望打开页面立刻执行,等用户来触发
2、不希望只执行一次,可以反复触发
3、希望绑定在页面元素之上时
4、建议:以后每一个作业都要封装为一个函数:函数在js中具有第一等公民的地位,函数中的变量都是会自动释放的
(五)、带有参数的函数:
榨汁机 - function:功能:榨汁
原材料 - 参数:放入一个苹果、梨子、香蕉...
如何创建带参数的函数:
function 函数名(形参,...){//形参(形式参数)其实就是一个变量,只不过不需要var
函数体;
}
调用带参数的函数的时候:
函数名(实参);//实际参数
function zzj(fruit){
console.log("榨"+fruit+"汁");
}
zzj("苹果");
zzj("梨子");
强调:
1、传入实参的顺序一定要和形参的顺序对应上
2、函数不一定非要有参数:1、如果你的操作是固定的,则不需要传递参数
2、根据传入的实参不同,做的操作略微不同,则需要添加参数
(六)、作用域
(1)全局作用域:
全局变量和全局函数,在页面的任何地方都可使用
(2)函数/局部作用域:
局部变量和局部函数,在当前函数调用时内部可以
(3)变量的使用规则:
优先使用自己的,自己没有找全局,全局没有则报错
特殊点:
a、不能对着一个没有声明的变量直接赋值,会导致全局污染
b、局部可以使用全局的,但全局无法使用局本的。解决:return
案例:
1、 function f1(){
a=100;//全局污染 - 直接在全局创建了一个全局没有的变量
console.log(a);//100
}
f1();
console.log(a);//100
2、 var a;
function f1(){
a=100;//修改全局
console.log(a);//100
}
f1();
console.log(a);//100
3、 function f1(){
var a=100;
console.log(a);//100
}
f1();
console.log(a);//报错,全局使用不到局部的
(七)、声明提前(即:先使用后创建)
在程序正式执行之前;
会悄悄地将var声明的变量和function声明的函数,全部集中提前到当前作用域的顶部;
但是赋值会留在原地;
变量比函数轻;
(函数中,声明方式创建的函数具有完整的声明提前,直接量方式具有部分声明提前,赋值留在原地)
案例:
// 鄙视题1
var a=10;
function f1(){
console.log(a);//undefinde
var a=20;
console.log(a);//20
}
f1();
console.log(a);//10
// 鄙视题2:
function fn(){
console.log(1);//2
}
fn();
function fn(){
console.log(2);//2
}
fn();
var fn=100;//报错
fn();
// 鄙视题3:
function fn(){
console.log(1);//1
}
fn();
var fn=100;
var fn=function(){
console.log(2)//2
}
fn();
// 鄙视题4:
var fn=function(){
console.log(1)//1
}
fn();//
function fn(){
console.log(2);//1
}
fn();//
var fn=100;
function fn(){
console.log(3);//报错
}
fn();
// 鄙视题5:
function fn(){
console.log(1);
}
fn();//3
var fn=function(){
console.log(2);
}
function fn(){
console.log(3);
}
var fn=function(){
console.log(4);
}
fn();//4
//鄙视题6:
function f1(){
console.log(a);//10
a=20;
console.log(a);//20
}
console.log(a);//undefined
var a=10;
f1();
console.log(a);//20
a=100;
console.log(a);//100
(八)、按值传递:两个变量之间进行赋值:
如果传递的是原始类型的值:
修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方
如果传递的是引用类型的对象:
修改一个变量,另一个变量是会受到影响的,因为大家操作其实是同一个【地址值】
(九)、重载
相同的函数名,传入不同的实参,可以自动选择对应的函数执行操作
为什么:减轻程序员的负担!
问题:js的语法不支持重载!
js不允许多个同名函数同时存在,如果同时存在,最后的会覆盖之前的所有
解决:在【函数中】有一个对象 - arguments对象
什么是arguments:只能在函数中使用,自动创建,是一个类数组对象(类似数组)
作用:***可以接收所有传入的实参
argument对象是一个类数组对象:长得像一个数组,但是,不是数组!
只有3个点和数组相同:
1、使用下标
2、使用length
3、遍历
***arguments可以做的事:
1、实现重载:通过在函数内部判断arguments,执行不同的操作
2、以后有没有形参都无所谓
3、正式开发中,有可能会将多个函数整合为一个函数 - 代码优化
案例:根据arguments的长度来判断
function zwjs(){
if(arguments.length==1){
return "我的名字叫"+arguments[0];
}else if(arguments.length==2){
return "我叫"+arguments[0]+",今年"+arguments[1]+"岁";
}else if(arguments.length==3){
return arguments[0]+"今年"+arguments[1]+"岁,喜欢"+arguments[2];
}
}
var result=zwjs("代老湿",18,"女");//我的名字叫代老湿,今年18岁,喜欢女
console.log(result)
var result=zwjs("袍哥");//我的名字叫袍哥
console.log(result)
案例:根据arguments的类型来判断
function zwjs(){
if(typeof(arguments[0])=="string"){
return "我的名字叫"+arguments[0];
}else if(typeof(arguments[0])=="number"){
return "我的今年"+arguments[0]+"岁";
}else if(typeof(arguments[0])=="boolean"){
return arguments[0]?"男":"女";
}
}
var result=zwjs(false);
console.log(result)
案例://创建一个函数:如果传入一个实参则求平方,如果传入两个实参则相加
function calc(){
if(arguments.length==1){
return Math.pow(arguments[0],2)
}else{
return arguments[0]+arguments[1];
}
}
console.log(calc(8));
console.log(calc(8,8));
案例:定义calc函数,使用arguments对象计算该方法接收到的所有的参数的算术和
function calc() {
if (arguments.length == 1) {
return Math.pow(arguments[0], 2)
} else {
return arguments[0] + arguments[1]
}
}
var result = calc(7);
var result1 = calc(7,2);
console.log(result)
console.log(result1)
(十)、匿名函数:没有名字的函数
1、匿名函数自调:
为什么:节约内存,因为匿名函数,没有变量引用着,用完,就会立刻释放变量
语法:
(function(){
//以后可以代替全局代码写法,尽量不要再外部再去书写JS(不用担心事件会被释放掉)
})();
2、匿名函数回调:将函数作为实参,传递给其他函数调用
1、*学习回调的目的:让你们知道哪些叫做回调:只要不是自调,就是回调
arr.sort(function(){})
str.replace(reg,function(){})
btn.onclick=function(){}
2、以后ES6技术:箭头函数:简化一切的回调函数
3、了解一下回调函数的原理
(十一)、函数的执行原理
1、程序加载时
创建执行环境栈ECS:保存着函数调用顺序的数组;
先压入全局执行环境栈(全局EC)引用着全局对象window
window里面保存着全局变量
2、定义函数时
创建函数对象:封装代码段(保存着当前函数的代码)
在当前函数对象中有一个scope(作用域)属性,记录着函数来自的作用域
全局函数的scope都是window
3、调用前
在执行环境栈中压入新的EC(函数的EC)
创建活动对象(AO):保存着本次函数调用时用到的局部变量
在函数的EC中有一个scope chain(作用域链)属性,引用着AO
AO里面有一个parent属性是scope引用着的对象
4、调用时
正是因为前面三步,我们才有了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
5、调用完
函数的EC会出栈,AO自动释放,局部变量也就自动释放了
***两链一包:
(1)作用域链:以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一个链式结构
作用:查找变量,带来变量的使用规则
(2)原型链:每个对象都有一个属性:__proto__,可以一层一层的找到每个人的父亲,形参的一条链式结构
可以找到所有父对象的成员(属性和方法),作用:找共有属性和共有方法的
(3)闭包
(十二)、闭包
(1)概念
保护一个可以反复使用的局部变量的一种词法结构。
何时使用:希望保护反复使用的局部变量时
如何使用:
两个函数进行嵌套
外层函数创建出受保护的变量
外层函数return出一个内层函数
内层函数要去操作受保护的变量
强调:
判断是不是闭包,就看有没有两个函数嵌套,返回内层函数,内层函数有没有在操作受保护的变量
外层函数调用几次,就创建了几个闭包,受保护的变量就有了多少个副本
同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
缺点:受保护的变量永远都不会被释放,因此使用过多会导致内存泄漏,不易多用
使用场景:
防抖节流(3个事件需要防抖节流)
1、elem.onmousemove-鼠标移动事件
2、input.oninput-每次输入/改变就会触发
3、window.onresize-每次窗口的大小会改变时就会触发
防抖节流公式:
function f1(){
var timer=null;//3
return function(){//
if(timer){clearTimeout(timer);timer=null;}
timer=setTimeout(function(){
//操作
},1000)
}
}
var fdjl=f1();
案例:
1、简单的闭包
function factory(){
var i=0;
return function(){
i++;
return i;
}
}
var icbc=factory();
console.log(icbc());//1
console.log(icbc());//2
console.log(icbc());//3
console.log(icbc());//4
console.log(icbc());//5
var abc=factory();
console.log(abc());//1
console.log(abc());//2
console.log(abc());//3
console.log(abc());//4
console.log(i);//reference错误
1、简单的闭包
// 汇率转换器
function factory(cate){
return function(money){
return money*cate;
}
}
//人民币转美元
var rmb2$=factory(0.1579);
console.log(rmb2$(1000))
console.log(rmb2$(10000))
//人民币转英镑
var rmb2yb=factory(0.1162);
console.log(rmb2yb(1000))
console.log(rmb2yb(10000))
console.log(rmb2$(1000))
console.log(rmb2$(10000))
2、防抖节流三事件——onmousemove事件(鼠标移入后里面的数字变化)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>防抖节流-onmousemove事件</title>
<style>
div {
width: 100%;
line-height: 200px;
color: #fff;
text-align: center;
background-color: aqua;
font-size: 80px;
}
</style>
</head>
<body>
<div>1</div>
<script>
// 防抖节流前
// var div=document.querySelector("div");
// div.onmousemove=function(){
// div.innerHTML=parseInt(div.innerHTML)+1
// }
// 防抖节流后
var div = document.querySelector("div");
div.onmousemove=function(){
animate()
}
function fdjl() {
var timer = null;
return function () {
if (timer) {
clearTimeout(timer);
timer=null;
}
timer=setTimeout(function(){
div.innerHTML=parseInt(div.innerHTML)+1
},1000)
}
}
var animate=fdjl()
</script>
</body>
</html>
2、防抖节流三事件——input事件
<body>
<input type="text">
<script>
var ipt = document.querySelector("input");
// ipt.oninput=function(){
// console.log(ipt.value)
// }
// 防抖节流后
ipt.oninput = function () {
animate();
}
function fdjl(){
var timer=null;
return function(){
if(timer){
clearTimeout(timer);
timer=null;
}
timer=setTimeout(function(){
console.log(ipt.value);
},1000)
}
}
var animate=fdjl();
</script>
</body>
2、防抖节流三事件——oonresize事件
<body>
<div>1</div>
<script>
// 防抖节流前
// var div = document.querySelector("div");
// window.onresize = function () {
// if (innerWidth >= 1200) {
// div.style.background = "pink";
// } else {
// div.style.background = "yellow";
// }
// }
// 防抖节流后
var div = document.querySelector("div");
window.onresize = function () {
animate();
}
function fdjl(){
var timer=null;
return function(){
if(timer){
clearTimeout(timer);
timer=null;
}
timer=setTimeout(function(){
if(innerWidth>=1200){
div.style.background="pink";
}else{
div.style.background="skyblue";
}
},1000)
}
}
var animate=fdjl();
</script>
</body>
[二]、预定义的全局函数
前辈们提前创建好的方法,我们程序员可以直接使用,在任何位置都可以使用
1、编码和解码: - 玩悄悄话,你编码发到群里,其他人来解码
问题:url中不允许出现多字节字符,如果出现会乱码
utf-8编码格式下,一个汉字,3字节
解决:发送前,前端将多字符原文编码为单字节字符(数字、字母)
发送后,后端将单字节字符解码为多字节原文
如何:
编码:var code=encodeURIComponent("魔兽世界");
解码:var 原文=decodeURIComponent(code);
其实这个东西在某次浏览器更新后,就当场淘汰了,浏览器自带此功能
2、isFinite(num):判断num是不是无穷大,true->有效数字 false->无穷大
哪些会为false:NaN,Infinity,分母为0
3、牛逼的:parseInt/Float、isNaN、eval
eval(str):计算字符串,简单来说就是脱字符串的衣服
案例:
var btns=document.getElementsByTagName("button");
for(var i=0;i<btns.length;i++){
btns[i].onclick=function(){
if(this.innerHTML=="C"){
txt.innerHTML="";
}else if(this.innerHTML=="="){
txt.innerHTML=eval(txt.innerHTML);
}else{
txt.innerHTML+=this.innerHTML;
}
}
}
七、数组
一个变量(内存)保存了多个数据的一个集合结构;当需要保存多个相关数据时就可用数组保存;
(索引数组)
(一)、创建数组
1)直接量方式:
var arr=[];//空数组
var arr=[数据,数据,...];
2)、构造函数方式:
var arr=new Array();//空数组
var arr=new Array(数据,数据,...);
如果传入的值是纯数字,则会得到长度是num的空数组,遍历会得到num个undefined
(二)、获取数据
数组名[下标];
特殊:1、下标越界,获取到的是一个undefined
(三)、添加数据
数组名[下标]=新数据;
特殊:1、如果下标处已有元素,则为替换
2、如果下标越界,会导致你的数组变成一个稀疏数组,中间就会出现很多的undefined,而且下标也不再连续
结论:数组具有3大不限制:
1、不限制元素的长度
2、不限制元素的类型
3、不限制下标越界 - 但是这个操作并不是好事情,强烈不推荐下标越界
(四)、数组对象 - 唯一属性length
因为自己数下标易数错,提供了一个属性:语法:数组名.length - 获取到数组的长度
三个固定套路:
1、向末尾添加元素:arr[arr.length]=新值
2、获取倒数第n个元素:arr[arr.length-n];
3、删除末尾的n个元素:arr.length-=n
(五)、遍历数组
将数组中的每个元素都取出来 执行 相同 或 相似的操作
公式:for(var i=0;i<arr.length;i++){
arr[i];//当前次获取到的元素
}
(六)、如何释放一个引用类型
首先需要看清楚该引用类型的数据由几个变量引用着,只有所有引用着的变量都释放后,该引用类型才会真正的释放。
(解决:将代码都封装在一个函数中,函数中的变量会自动的释放)
(关联数组/hash数组)
创建空数组:var arr=[];//空数组
添加下标并添加元素:arr["自定义下标]=新值
访问元素:arr["自定义下标]
!!!
hash数组由于下标是自定义的,所以length会失效,永远为0,无法使用for循环去遍历,只能用for in循环;
for(var i in 数组名){
操作
}
(一)、hash数组的原理
hash算法:将字符串,计算出一个尽量不重复的数字(地址值)
字符串内容相同,则计算出来的数字也一定是相同的
添加元素:将自定义下标交给hash算法,得到一个数字(地址值),直接将你要保存的数据放到这个地址之中
获取元素:将指定的自定义下标交给hash算法,得到一个和当初保存时一样的数字(地址值),通过地址就找到当前保存的数据
!!!除了undefined和null,js里面的一切东西都是对象(num、boolean、str因为具有包装类型),并且一切对象的底层都是hash数组。
(二)、二维数组
数组中的值再次引用了一个数组
(1)创建
var arr=[
["张三丰",128,3500],
["张翠山",30,4500],
["张无忌",18,5500]
];
(2)访问
arr[r][c]
面试中:
行下标越界:报错(因为根本就没有,得到的是一个undefined,不能使用[]和.操作)
列下标越界:得到undefined
(3)遍厉
for(var r=0;r<arr.length;r++){//外层循环控制行,内层循环控制列
for(var c=0;c<arr[r].length;c++){
console.log(arr[r][c])
}
}
数组的API
前辈们提前定义好的,可以直接使用的
(一)、数组转为字符串 join
xx.toString() String(x)
var str=arr.join("自定义连接符");
固定套路:2个
1、鄙视题:将数组里面的内容拼接为一句话/单词;
无缝拼接:var str=arr.join("");
2、将数组拼接为DOM页面元素
案例:无缝拼接
var arr=["h","e","l","l","o"," ","w","o","r","l","d"];
for(var i=0,str="";i<arr.length;i++){
str+=arr[i];
}
console.log(str);//或者下一种方法
console.log(arr.join(""));
渲染页面
<script type="text/javascript">
var arr=["北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆"];
sel.innerHTML="<option>"+arr.join("</option><option>")+"</option>";
</script>

(二)、拼接数组:concat 添加元素的新方式
会把传入的实参全部拼接到arr的末尾
var newArr=arr.concat(新值1,...)
特殊:1、不会修改原数组,只会返回一个新的数组
2、concat支持传入数组参数,会悄悄的将传入的数组参数自动打散为单个元素后再拼接
(三)、截取子数组:slice
根据你传入的开始下标截取到结尾下标
var subArr=arr.slice(starti,endi+1);
特殊:1、不修改原数组,只会返回一个新子数组
2、含头不含尾
3、endi可以省略不写,会从starti位置一直截取到末尾
4、其实两个实参都可以省略 - 从头截到尾,深拷贝,和以前的按值传递(浅拷贝)不同,一个修改不会影响到另一个
5、支持负数参数,-1代表倒数第一个
案例:
var arr=[1,2,3,4,5,6,7,8];
var narr=arr.slice(1,4)//截取2-4
var narr1=arr.slice(1)//从2截到尾
var narr2=arr.slice()//从头截到尾
console.log(narr)
console.log(narr1)
console.log(narr2)

以上三个API不会修改原数组,只会返回一个新数组
(四)、删除、插入、替换:splice同之前的arr.length-=n
删除:var dels=arr.splice(starti,n);//n代表删除的个数
特殊:其实splice也有返回值,返回的是你删除的元素组成的一个新数组
插入:arr.splice(starti,0,值1,...);
注意:1、原starti位置的元素以及后续元素都会向后移动
2、千万不要插入一个数组,会导致我们的数组一些是一维,一些是二维,遍历时非常的难受
替换:var dels=arr.splice(starti,n,值1,...);
注意:删除的元素个数不必和插入的元素个数相同
案例:
var arr=[1,2,3,4,5,6,7,8];
var del= arr.splice(1,4);//删除2,3,4,5 从开始下标,删除n个
console.log(arr);//[1,6,7,8];
console.log(del);//[2,3,4,5];
var arr=[11,22,33,44];//插入
arr.splice(2,0,"新","来","的")
console.log(arr)//[11,22,"新","来","的",33,44]
var arr=[1,2,3,4,5,6,7,8];//替换
var th=arr.splice(1,3,22,33,44)//替换2,3,4
console.log(arr);//[1,22,33,44,5,6,7,8]
(五)、翻转数组:arr.reverse();
(六)、排序 sort
(一)鄙视题:冒泡排序
从第一个元素开始,依次比较相邻的两个元素,只要前一个元素小于后一个元素,两个元素则交换位置
公式:
var arr=[1,33,5346,65,5767,434,45,544,33]
for(var j=0;j<arr.length;j++){
for(var i=0;i<arr.length-(j+1);i++){
if(arr[i]<arr[i+1]){
var max=arr[i];
arr[i]=arr[i+1];
arr[i+1]=max;
}
}
}
(二)开发中:升降序排序
arr.sort()
特殊:
1、默认会将元素们转换为字符串,然后按位PK字符串的ascii码进行排序
2、按照数字升序排序
arr.sort(function(a,b){//回调函数:不需要我们程序员调用的函数:悄悄的带有循环,提供了两个形参:a是后一个数,b是前一个数
return a-b
})
//return a-b:如果a>b,返回是一个正数
// 如果a<b,返回是一个负数
// 如果a==b,返回是一个0,sort根据你返回的结果,来判断两者要不要交换位置
3、降序排列:
arr.sort(function(a,b){//回调函数:不需要我们程序员调用的函数:悄悄的带有循环,提供了两个形参:a是后一个数,b是前一个数
return b-a;
})
!!!!
js中,只有数组可以排序,一切具有排序功能的案例,底层都是一个数组
案例:
var arr=[1,33,5346,65,5767,434,45,544,33]
arr.sort(function(a,b){
return a-b//升序
})
console.log(arr)
arr.sort(function(a,b){
return a-b//降序
})
console.log(arr)
(七)、栈和队列
添加元素和删除元素的新方式
(一)栈
一端封闭,另一端进出的操作。优先使用最新的数据
开头进:arr.unshift(值,...)//向前添加。
开头出:var first=arr.shift()//向前删除。
以上两个api会导致元素的下标发生改变
结尾近:arr.push(值,...)//完全等效与arr[arr.length]或者arr.concat。向后添加
结尾出:var last=arr.pop()//向后删除
不会导致元素下标发生改变
(二)队列
其实就是数组,一端进,另一端出。根据先来后到的顺序
开头进:arr.unshift(值)
结尾出:var last=arr.pop()
结尾进:arr.push(值)
开头出:var first=arr.shift()
扩展:
周期性定时器:每过一段时间就会执行一次
开启定时器:timer=setInterval(function(){
操作
},间隔毫秒数)
停止:clearInterval(timer)
鼠标移入:onmouseover(触发冒泡)/onmouseenter(不会触发冒泡)
鼠标移出:onmouseout(触发冒泡)/onmouseleave(不会触发冒泡)
八、字符串
(一)、string的概念
多个字符组成的【只读】字符数组
(二)、为何字符串也可以叫数组?
和数组的共同点:
支持下标——可以获取某个字符
支持length——可以获取字符的长度
支持遍历
不修改原数组的API,字符串也可以使用(join、concat、slice),但join不会用,因为本来就是字符串。而用concat还不如直接用+拼接。
不同点:
数组修改原数组的API,字符串都不可以用,有自己的API
(三)、只读
字符串中的所有的API都不会修改原字符串,只会返回新的字符串
(四)、引用类型的对象:11个
String(字符串) Number(数字) Boolean(布尔) - 具有包装类型
Array(数组) Function(函数) Math(数学) Date(日期) RegExp(正则) Error(错误) Object(面向对象)
Global - 全局对象:在浏览器端被window对象给代替了:window对象可以省略不写出来
包装类型:将原始类型的值变为一个引用类型的对象
为什么:前辈们发现字符串/数字/布尔经常都会被拿来使用,所以提前提供了包装类型封装为一个引用类型的对象,提供我们一些属性和方法(便于程序员操作)
何时使用:只要在你试图用原始类型的值去调用属性或者方法时,会自动套上包装类型
何时释放:属性或方法调用完毕后,包装类型自动释放
为什么undefined和null不能使用.:不具有包装类型,没有任何的属性和方法
字符串的API
(一)、转义字符 \
作用:(1)将字符串中和程序冲突的字符转为原文
"\" '\"
案例:
document.write("<h1 style=\"background:green;\">验证通过</h1>")
var str1="我的名字叫\"天棒\"。";
// var str1='我的名字叫\'天棒\'。';
(2)、特殊功能:
换行:\n
制表符:\t 相当于tab键,大空格
(3)、*可以书写unicode号 表示一个字
\uXXXX
汉字的第一个字:4e00
汉字的最后一个字:9fa5
案例:var str="\u4e00\u9fa5"
(二)、*转换大小写 toUpperCase() toLowerCase()
【统一的】转为大写或小写,再比较,忽略大小写:- 验证码
大写:var newStr=str.toUpperCase();
小写:var newStr=str.toLowerCase();
(三)、获取字符串中指定位置的字符的ascii码
var ascii=str.charCodeAt(i);
通过ascii码转回原文
var 原文=String.fromCharCode(ascii);
(四)、*检索字符串:检查索引/下标: indexOf("关键字",starti)
从starti位置开始找右侧的第一个关键字的下标
作用:判断有没有
var i=str/arr.indexOf("关键字",starti);
特殊:1、starti可以省略,如果省略则从0开始
2、返回值:找到了,返回第一个字符的下标
***没找到,返回-1,其实我们根本不关心下标是几,只关心下标是不是-1,-1代表没找到,不是-1代表找到了
3、数组也可以使用此方法
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);
}
案例:
var arr=["倚","天","屠","龙","记"];
var i=arr.indexOf("屠",3);
console.log(i);
(五)、拼接字符串 concat
var newStr=str.concat(str1,str2...) 还不如 +运算
(六)、*截取字符串: slice /substring / substr
1、**var subStr=str/arr.slice(starti,endi+1);
2、var subStr=str.substring(starti,endi+1);//不支持负数参数
3、*var subStr=str.substr(starti,n);//截取的个数,不必考虑含头不含尾
(七)、*替换字符串 replace("关键字"/正则表达式,"新内容")
var newStr=str.replace("关键字"/正则表达式,"新内容");
案例:
var str="卧槽,我操,我艹,我草,我肏,窝草";
str=str.replace(/[我卧窝][槽操艹草肏]/g,"**");
console.log(str);//全部替换为了**
(八)、*****切割/分割字符串 split("自定义切割符")
作用:str <=> arr
var arr=str.split("自定义切割符");
特殊:
1、切割符可以自定义,切割过后返回一个数组,数组中不再包含切割符
2、如果传入的切割符是一个"",每一个字符都会被切开
案例:
var names="tom&jack&rose&jordan&andy";
var arr=names.split("");
扩展
扩展:创建元素并且渲染页面
1、创建空标签
var elem=document.createElement("标签名");
2、设置必要的属性或事件
elem.属性名="属性值";
elem.on事件名=function(){函数体} - 事件都可以在创建时提前绑定上
3、创建好的元素渲染到DOM树上
父元素.appendChild(elem);
九、正则表达式 正则对象
(一)概念和使用场景:
定义字符串中【字符出现规则】的表达式,在切割、替换、验证时使用
(二)、定义及知识点
语法:/正则表达式/
!!正则默认只要满足后,就不再管理后续操作,后续用户可以乱输入,为了防止乱输入:/^[备选字符集]&/——要求用户输入的东西必须从头到尾完全匹配
1、最简单的正则:
关键字原文本身:i:忽略大小写 g:全部
案例:替换(默认一次只能替换一个)
var str="no zuo no die No can no bibi";
// str=str.replace("no","yes");
str=str.replace(/no/ig,"yes");
console.log(str);
2、备选字符集:
语法: /[备选字符集]/
特殊:1、如果备选字符集中,unicode号是连续,那么中间部分可用-代替
一位数字:[0-9]
一位数字、字母、下划线:[0-9A-Za-z_]
一位汉字:[\u4e00-\u9fa5]
2、除了xxx之外,其他都可以: - 很少使用,范围太广了
如:[^0-9] 除了汉字之外其余的都可以
3、预定义字符集:提前定义好的字符集的简化写法
一位数字:\d ==> [0-9]
一位数字、字母、下划线:\w ==> [0-9A-Za-z_]
一位空白字符:\s ==> 空格、制表符、换行
一位除了换行外的任意字符: . - 范围太广了
!!!!!不管是备选字符集还是预定义字符集,一个中括号只管一位
4、量词: 规定了字符集出现的次数
(1)、有明确数量
字符集{n,m}:前边相邻字符集出现的次数,最少出现n此,最多m次
字符集{n,}:前边相邻字符集出现的次数,最少出现n次,多了不限
字符集{n}:前边相邻字符集出现的次数,必须出现n次
(2)、没有明确数量
?:前边相邻字符集出现的次数,可有可无,最多出现一次
*:前边相邻字符集出现的次数,可有可无,多了不限
+:前边相邻字符集出现的次数,最少出现一次,多了不限
5、指定匹配位置:
^:以xxx开头
$:以xxx结尾
特殊:如果^和$同时出现:前加^后加$,代表要求从头到尾完全匹配 - 只要是做验证必须加
6、选择和分组:
选择:规则1|规则2 - 可以再多个规则中选择满足的规则进行执行
分组:添加子规则:(规则1|规则2)
案例:
var reg=/^(\d|\s)$/;
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位密码,数字和字母的组合,三者必须都有
(三)支持正则表达式的字符串API
1、切割:var arr=str.split(reg)
案例:
var str="tom&jerry%andy~邓如宇";
var arr=str.split(/[&%~]/);
console.log(arr);//["tom","jerry","andy","邓如宇"]
2、替换:出现在鄙视题中
var newstr=str。replaec(正则表达式,"新内容")
(1)、基本替换法:
var newStr=str.replace(reg,"新内容");
特殊:1、默认只会替换第一个关键字,想要替换所有记得加上后缀g
2、替换的内容只能是一个固定的新内容
(2)、高级替换法
var newStr=str.replace(/[我卧窝][槽操艹草肏去]+/g,function(a,b,c){
console.log(a);//正则匹配到的关键字
console.log(b);//正则匹配到的关键字的下标
console.log(c);//原文本身
return a.length==2?"**":"***";
});
(3)、格式化:如果替换API使用正则时,并且里面带有分组,那么你会得到更多的形参
var newStr=str.replace(reg,function(a,b,c...){
console.log(a);//正则匹配到的关键字
console.log(b);//第一个分组获取到的内容
console.log(c);//第二个分组获取到的内容
return "格式化的东西"
});
出现:笔试题中
总结:何时前加^后加$,何时又该加后缀gi?
1、前加^后加$ - 验证
2、替换,你希望替换到所有 - 必须加g
案例:
基本替换
var str="那天,我去了她家,我说\n"
+"我草,你家真大\n"
+"她说,卧槽,你怎么开口就是国粹啊\n"
+"我说,我想和你去草地坐一坐\n"
+"她草草的收拾了一下";
// 基本替换法,缺陷:替换的新东西是固定的
str=str.replace(/[我你她卧][去草槽]+/g,"**");
console.log(str) //那天,**了她家,我说 **,你家真大 她说,**,你怎么开口就是国粹啊 我说,我想和**地坐一坐 **的收拾了一下
//高级替换法
str=str.replace(/[我你她卧][去草槽]+/g,function(a){
return a.length==2?"**":"***";
});
console.log(str);//那天,**了她家,我说**,你家真大
她说,**,你怎么开口就是国粹啊 我说,我想和***地坐一坐 ***的收拾了一下
//格式化替换:
var id="500103198602215933";
var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/;
id=id.replace(reg,function(a,b,c,d){
return b+"年"+c+"月"+d+"日";
})
console.log(id);//1986年02月21日
//首字母大写
var str="Lorem ipsum dolor a sit amet, consectetur adipisicing elit. Ducimus obcaecati nemo expedita praesentium rem sequi at aspernatur sed minus adipisci suscipit animi tenetur similique quia amet modi vitae voluptatum? Rerum!"
var reg=/(\w)(\w*)/g;
str=str.replace(reg,function(a,b,c){
return b.toUpperCase()+c;
})
console.log(str);
//去掉开头结尾的空白部分
var str=" hello world ";
str=str.replace(/^\s*|\s*$/g,"");
console.log(str);//hello world
(四)正则对象:
创建:
直接量方式:var reg=/正则表达式/后缀;
构造函数方式:var reg=new RegExp("正则表达式","后缀");
API:
var bool=reg.test(用户输入的内容);
true->验证成功 false->验证失败
****QQ注册案例:
1、绑定事件要一起绑定
2、获取焦点事件 - 提示用户要输入什么
3、失去焦点事件 - 获取用户输入的,再和正则进行验证,不管对错,都要提示用户
4、表单绑定提交事件 - 阻止提交return false;
十、Math 数学对象
专门提供了数学计算的API,不需要去创建,直接就可以使用
唯一的属性:Math.PI,浏览器自带
(一)API
取整
1、上取整:只要超过一点点,就会取下一个整数,此方法小数位数不能超过15位否则会失效:Math.ceil(num);
2、下取整:不管超过多少,都会省略掉小数部分,此方法小数位数不能超过15位否则会失效:Math.floor(num);
3、 四舍五入取整:Math.round(num);
问题:四舍五入虽然不错,但是以上三个API只能取整
解决:棒的:parseFloat(num.toFixed(d));//既有四舍五入功能,又具有保留自定义小数位数的操作,结果是一个字符串
鄙视题:*封装一个函数,实现可以自定义保留小数位数并且四舍五入的功能,但是不允许使用toFixed?
function round(num,d){
num*=Math.pow(10,d);
num=Math.round(num)
num/=Math.pow(10,d);
return num.toString();
}
乘方和开方
*乘方:Math.pow(底数,幂); - 简化连续的乘法
开方:Math.sqrt(num); - 只能开平方
最大值和最小值
语法:Math.max/min(a,b,c,d,e,f....);
获取到最大的一个数或者最小的一个数
问题:不支持数组参数
*解决:Math.max/min.apply(Math,arr);
apply:自己没有的方法可以去借用
可以将数组打散为单个参数悄悄进行传入
案例:
var arr = [2, 4, 5, 6, 8, 10, 33, 454, 958];
var max = arr[0];
for (var i = 1; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
console.log(max)
console.log(Math.max.apply(Math,arr));
绝对值:将负数转为整数
Math.abs(num);
***随机数:只要页面上具有随机的功能,底层一定用到了随机数
Math.random() 已经是一个随机数了,随机的小数0-1,有可能取到0,但是绝对不可能取到1 - 意味着能取到最小值,但是取不到最大值
公式:parseInt(Math.random()*(max-min+1)+min);
十一、Date对象
封装了一个日期对象,提供了对日期事件进行操作的API
何时使用:以后只要网页上跟事件日期相关的,我们都要使用date对象
(一)、创建日期对象
1、*创建当前时间:
var now=new Date();
2、*创建自定义时间:
var birth=new Date("yyyy/MM/dd hh:mm:ss");
3、创建自定义时间:
var birth=new Date(yyyy,MM,dd,hh,mm,ss);
缺点:月份需要修正:计算机中月份是从0开始到11的
4、复制一个日期对象:
为什么:日期对象的API都是直接修改原日期对象,使用API后,无法同时保存住旧的日期对象
何时使用:在调用日期对象的API之前都要先复制,在使用API
语法:var end=new Date(now);
5、var xxx=new Date(毫秒数);
计算器其实保存的就是从1970年1月1日至今的毫秒数
(二)、操作
1、两个日期对象之间可以相减,得到毫秒差,换算出你想要的任何一部分,看出细节:其实日期对象保存的就是一个毫秒数
2、API:
分量:时间单位
FullYear Month Date Day
Hours Minutes Seconds
1、每一个分量都有一对儿方法:getXXX()/setXXX()
特殊:1、取值范围:
年:当前年份
月:0-11
日:1-31
时:0-23
分:0-59
星期:0-6,0代表星期天
2、星期只有get,没有set
2、固定套路:对着某个日期直接做加减
date.setXXX(date.getXXX()+3)
3、格式化为字符串:
国际化日期格式化为本地化日期:date.toLocaleString(); - 垃圾:1、具有浏览器的兼容性问题 2、则不可以再使用日期对象的API,也不会再带有进制操作了
好处:转为了字符串可用字符串的API
解决:自定义format格式化
十二、Number对象 Boolean对象
看出来了一个点:所有的引用类型底层都具有构造函数创建方式,除了Math,直接量简化写法其实是后续追加的操作
十三、Error对象
(一)、***浏览器自带4种错误类型:
可以快速找到自己的错误
语法错误:SyntaxError - 多半都是你的语法符号写错了
引用错误:ReferenceError - 没有创建就直接使用了
类型错误:TypeError - 不是你的方法,你却去使用了
范围错误:RangeError - 只有一个API会碰到:num.toFixed(d);//d取值范围的:0~100之间
(二)、错误处理
语法:
try{
只放入你可能出错的代码
}catch(err){
发生错误后才会执行
console.log(err);//err就是我们的错误提示,英文
console.log("中文错误提示");
}
只要发生错误,就会报错,会导致后续代码终止,我们不希望
错误处理:就算发生错误,我们也不希望报错,而是给一个错误的提示,后续代码可以继续执行
try...catch...的性能非常差,几乎里面的代码效率会被降到最低
*可以用一个技术代替:if...else...
*开发经验:记住一切的客户端输入/用户输入都是坏人 - 你不必担心,只要你做好防护就不会出现错误(!isNaN,比较运算,正则)
案例1:
var num=parseFloat(prompt("请输入一个数字"));
var d=parseInt(prompt("请输入此数字的保留小数位数"));
//try...catch...版本的错误处理
try{
console.log(num.toFixed(d));
}catch(err){
console.log(err);
console.log("傻逼,保留小数位只能是0-100之间")
}
//if...else...版本的错误处理
if(d>=0&&d<=100){
console.log(num.toFixed(d));
}else{
console.log("傻逼,保留小数位只能是0-100之间")
}
console.log("后续代码:轮播、选项卡、购物车、正则验证...");
案例2:
//程序甲:封装了一个方法,但是可能会报错
function round(num,d){
if(!isNaN(num)&&!isNaN(d)){
num*=Math.pow(10,d);
num=Math.round(num);
num/=Math.pow(10,d);
return num;
}else{
throw new Error("傻逼,传入的必须是一个数字");
}
}
//程序员乙:收账系统,用户输入总价,收款金额,收款金额-总价=找零
function checkout(){
// try...catch版本
try{
var total=round(prompt("请输入总价"),2);
var money=round(prompt("请输入收款金额"),2);
var zl=round(money-total,2);
}catch(err){
console.log(err);
}
console.log("找零为:"+zl);
console.log("后续代码");
//if...else版本
// var total=prompt("请输入总价");
// var money=prompt("请输入收款金额");
// if(!isNaN(total)&&!isNaN(money)){
// var zl=round(money-total,2)
// console.log("找零为:"+zl);
// }else{
// console.log("傻逼,传入的必须是一个数字")
// }
// console.log("后续代码");
}
checkout()
(三)、抛出自定义错误
throw new Error("自定义错误信息") - 只要是报错都会卡主后续代码
案例:
function round(num,d){
if(!isNaN(num)&&!isNaN(d)){
num*=Math.pow(10,d);
num=Math.round(num);
num/=Math.pow(10,d);
return num;
}else{
throw new Error("傻逼,传入的必须是一个数字");
}
}
十四、面向对象
面向对象三大特性:封装、继承、多态
开发方式:面向对象 和 面向过程?
面向过程:经过 - 开始->结束,我们一直的开发方式都是面向过程,先干什么在干什么最后干什么
面向对象:对象(属性和方法),js有一句话万物皆对象,假设一个人是一个对象的话:
属性:姓名、性别、身高、体重、爱好、智商、情商...
方法:吃饭、睡觉、拉粑粑、跑步、走路、打字、学习...
为什么要面向对象:现实中所有的数据都必须包含在一个事物中才有意义
何时使用面向对象:以后做任何操作都要封装在一个对象中
(一)、封装/定义/创建
(1)、直接量方式创建
语法:
var obj={
"属性名":属性值,
...
"方法名":function(){},
...
}
强调:1、其实属性名和方法名的""可以不加 - 暂时建议你加上
2、访问对象的属性和方法
*obj.属性名 === obj["属性名"]
*obj.方法名() === obj["方法名"]();
建议使用.去访问属性和方法,更简单
***JS中一切都是对象,一切对象的底层都是hash数组
3、访问到不存在的属性,返回undefined
4、可以随时随地的添加新属性和新方法
5、如果我希望遍历出对象所有的东西,使用for in,obj[i]才能拿到,不要用.会出问题
6、***如果你希望在对象的方法里使用对象自己的属性:写为this.属性名
*****难点:this的指向:
1、单个元素绑定事件:this->单个元素
2、多个元素绑定事件:this->当前触发的元素
3、函数中使用this,this->谁在调用此方法,this指的就是谁
4、定时器中this->window
(2)、预定义构造函数方式
var obj=new Object();//空对象
//需要自己后续慢慢追加属性和方法
obj.属性名=属性值
obj.方法名=function(){}
以上两个方法都有一个缺陷:一次只能创建一个对象,适合创建单个对象的时候(第一种方法),第二种方法了解即可
(3)、自定义构造函数方法
1、创建自定义构造函数
function 类名(name,age,salary){
this.name=name;
this.age=age;
this.salary=salary;
}
//千万不要在里面创建方法,每个对象都会创建一个方法,浪费内存 - 明天,继承将统一的方法放到原型上
2、调用构造函数创建出对象
var obj=new 类名(实参1,...)
案例:
function h52107(name,age,hobby){//h52107类,模板,this出现在构造函数中,this代表的是当前正在创建的对象
this.name=name;//dry.name="邓如宇";
this.age=age;//dry.age=18
this.hobby=hobby;//dry.hobby="抠脚"
this.zwjs=function(){return "我的名字叫"+this.name+",今年"+this.age+"岁,喜欢"+this.hobby}
}
var dry=new h52107("邓如宇",18,"抠脚");
var cf=new h52107("曹芬",18,"穿越火线");
var cmr=new h52107("曹敏茹",18,"穿越人海");
console.log(dry);
console.log(cf);
console.log(cmr);
详见案例:封装对象版轮播、选项卡、开关门
(二)、继承
(1)、定义
父对象的成员(属性和方法),子对象可以直接使用
为什么:代码重用!节约内存空间!提升网站性能!
何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象中
(2)、如何找原型对象(父对象)
保存一类子对象共有属性和共有方法的父对象
原型对象分:隐式原型属性和显示原型属性
隐式原型属性
1、对象名.__proto__;//必须先有一个对象
显示原型属性
2、构造函数名.prototype;//构造函数名,几乎人人都有,除了Math
(3)、有了原型对象,设置共有的属性和方法:
1、原型对象.属性名=属性值;//添加了一个共有属性
2、原型对象.方法名=function(){};//添加了一个共有方法
(三)、继承的鄙视题
1、判断是自有还是共有:
1、判断自有:obj.hasOwnProperty("属性名");
如果结果为true,说明是自有属性,如果结果为false,有两种可能,共有或没有
2、判断共有:
if(obj.hasOwnProperty("属性名")==false && "属性名" in obj){}//obj.hasOwnProperty("属性名")==false && 可以省略
if(obj.hasOwnProperty("属性名")==false && "属性名" in obj){//in 会自动在obj的原型上进行查找
共有
}else{
没有
}
公式:
if(obj.hasOwnProperty("属性名")){//false
console.log("自有")
}else{
if("属性名" in obj){
console.log("共有")
}else{
console.log("没有")
}
}
2、删除自有和共有
自有:修改:obj.属性名=新值;
删除:delete obj.属性名;
共有:修改:obj.共有属性名=新值; - 非常危险的,并没有修改到爸爸,而是在本地添加了一个同名属性
删除:delete obj.属性名; - 无效,本地本来就没有此自有属性
找到原型,修改/删除原型
3、如何为老IE的数组添加indexOf
原理:
if(Array.prototype.indexOf===undefined){//老IE
Array.prototype.indexOf=function(key,starti){
starti==undefined&&(starti=0);
for(var i=starti;i<this.length;i++){
if(this[i]==key){
return i;
}
}
return -1;
}
}
4、如何判断x是不是一个数组:4种方法:千万别用typeof,只能检查原始类型,不能检查引用类型
1、判断x是否继承自Array.prototype
Array.prototype.isPrototypeOf(x);
2、判断x是不是由Array这个构造函数创建
x instanceof Array;
3、Array.isArray(x); - ES5提供的,只是ES5+,老IE不支持,此方法只有数组
4、输出【对象的字符串】形式(用到到了多态)
在Object的原型上保存着最原始的toString方法
原始的toString输出形式:[object 构造函数名]
固定套路:借用:Object.prototype.toString.apply(x);
if(Object.prototype.toString.apply(arr)=="[object Array]"){
console.log("是数组")
}else{
console.log("不是数组")
}
5、实现自定义继承:
1、两个对象之间设置继承:
子对象.__proto__=父对象
2、多个对象之间设置继承:
构造函数名.prototype=父对象
时机:应该在开始创建对象之前设置好继承关系
案例:
//单个对象设置继承
var wsc={
phone:"iphone100",
dog:"阿拉斯加",
gf:"很多",
}
var wjl={
"money":1000000000000000
}
var dy={
"js":150,
}
wsc.__proto__=wjl;
wjl.__proto__=dy;
console.log(wsc.js);
//多个对象设置继承
function h52107(name,age,hobby){
this.name=name;
this.age=age;
this.hobby=hobby;
}
h52107.prototype=dy;
var dry=new h52107("邓如宇",18,"抠脚");
var cf=new h52107("曹芬",18,"穿越火线");
var cmr=new h52107("曹敏茹",18,"穿越人海");
var lmb=new h52107("林明宝",19,"臭宝");
console.log(dry);
console.log(cf);
console.log(cmr);
console.log(lmb);
(四)、多态
***多态:子对象觉得父对象的成员不好,在本地定义了同名成员,覆盖了父对象的成员