一、js基础语法与表达式
初始js:
结构层:html 搭建结构、放置部件、描述语义
样式层:css 美化页面、实现布局
行为层:js 实现交互效果、数据收法、表单验证等
-补充:
1.js在声明变量时不需要考虑变量类型;
1.1认识输出语句:
1.2 aler()语句 -弹出警告框
1.3 console.log()语句 -控制台输出
2.变量(x,y,z):是一个存储数值的容器
2.1定义变量:声明并赋值
eg:var(使用var关键字定义变量) a(变量名) =(赋值) 5;
2.2变量的使用(不能用引号):当变量被赋值后,就可以使用它了
eg:console.log(a);
2.3改变变量的值时,不需要重新写var关键字了
eg:var a = 10;
a = 18;
console.log(a);//18
2.4.变量的命名规范(标识符的命名规则):
只能由字母、数字、下划线、$符号组成,当不能以数字开头;
不能是关键字或保留字;
变量名大小写敏感,a和A是两个不同的变量
注:一个变量只定义而没有被赋值,默认值是undefined;
一个变量只有被var定义,并赋值之后,才算正式初始化完成;
eg:var a;
console.log(a);//undefined
var a = 6;
console.log(a);//6
2.5变量的常见错误:
-不用var定义,直接将值赋予它,虽不引发报错,但是会产生作用域问题:
eg:a = 10;
console.log(a);X
-尝试使用既没有被var定义过,也没有赋过值的字符,就会产生引用错误;
eg:console.log(b);x
2.6等号表示赋值:
var a = 10;
a = a + 1;
console.log(a);//11
-补充:同时声明多个变量,使用逗号同时声明和初始化两个变量
eg:var a = 0, b = 0;
2.7变量声明的提升:你可以提前使用一个稍后才声明的变量,而不会引发异常;
eg:console.log(a);//先使用变量,但是会输出undefined,只提升变量,不会提升值
var a = 12;//后定义变量
注:变量声明的提升是js的特性,面试经常会问;
3.js的基本数据类型
3.1数据类型简介和检测
js分为两大数据类型
-基本数据类型:Number,String,Boolean,Undefined,Null
-复杂数据类型:Object,Array,Funcion,RegExp,Date,Map,Set
typeof运算符:不是内置函数,只是操作符,所以不用写();
使用typeof运算符可以检测值或者变量的类型;
eg:typeof 5;//number
typeof '你好';//string
3.2基本数据类型
-数字类型
所有数字不分大小,不分整浮,不分正负,都是数字类型
eg:typeof 84;//number
typeof 2.3;//number
typeof -5;//number
科学计数法:加大数或较小数可以写成科学计数法
eg:3e8;//300000000=3X10的8次方
3e-8;3x10的-8次方
-不同进制的数字
二进制数值以0b开头
0b10 //2
ob111 //15
八进制数值以0开头
017 //15
十六进制数字以0x开头
0xf //15
-一个特殊的数字型值NaN:是not a nubmer,但它是一个数字类型的值;
eg:typeof NaN;//number
补充:0除以0的结果是NaN,事实上,在数学运算中,若结果不能得到数字,其结果往往都是NaN;
NaN有一个“奇怪”的性质:不自等;
-字符串类型
字符串就是“人来的自然语言”;
字符串要用引号:
eg:typeof '你好';//string
typeof "你好";//string
-字符串和变量的拼接:
要将一个变量的值“插入”到字符串中,要“炸断链接”
var year = 2022;
var str = '北京冬季奥运会子在' + year(斩断链接) + '年召开';
空字符串:一些时候需要使用空字符串,直接书写闭合的引号对即可:
eg:var str = '';
-字符串的length属性(属性:即.+它可以得到它的特质)
eg:'我喜欢js'.length //5
''.length //0
-字符串的常用方法:“方法”就是能打点调用的函数,字符串哟丰富的方法
方法 功能
charAt() 得到指定位置字符 eg:'我喜欢js,我也喜欢hmtl'.charAt(0);//'我'
substring() 提取子串 substring(a,b)方法得到从a开始到b结束(不包括b处)的子串;'我喜欢js,我也喜欢hmtl'.substring(3,5);//js
补充:如果省略第二个参数,会从标注的参数到结尾;eg:'我喜欢js,我也喜欢hmtl'.substring(6);//我也喜欢hmtl;
参数a和b部分大小substring(2,6)与substring(6,2)是一样的效果;
substr() 提取子串 substr(a,b)中,将得到从a开始的长度为b子串;b是a的取值长度,eg:'abcdef'.substr(3,2);//de;
补充:substr(a,b)中,b可以省略,表示到字符串结尾,eg:'abcdefg'.substr(4);//efg
substr(a,b)中,a可以是负数,表示倒数位置;eg:'abcdef'.substr(-2,2);//ef
slice() 提取子串 slice(a,b)方法得到a开始到b结束(不包括b处)的子串;
注:slice(a,b)的参数a可以是负数;eg:'abcdef'.slice(-4,-2);//cd
slice(a,b)中,参数a必须小于参数b
toUpperCase() 将字符串变为大写
toLowerCase() 将字符串变为小写
indexOf() 检索字符串 indexOf()方法返回某个指定的字符串值在字符串中首次出现的位置;
如果要检索的字符串值没有出现,则该返回-1;
eg:'abcdeb'.indexOf('b');//1:表示b字母第一次出现的位置是索引1
'abcdeb'.indexOf('de');//3
'abcdeb'.indexOf('m');//-1:由于字符串中未出现m,所以返回-1;
总结:
substring(a,b)和substr(a,b)功能基本一致,都是得到从a开始到b结束(不包括b)的子串,区别:
-substring()可以自动交换两个参数位置,而slic()不行;
-slice()的参数a可以是负数,而substring()不行;
-substr()中的参数b是字串长度,而不是位置编号;
-布尔类型
布尔是英国19世纪数学家及逻辑学家,
只有两个值:true和false
eg:typeof true;//Boolean
typeof false;//Boolean
-undefined类型
一个没有被赋值的变量的默认值是Undefined,而undefined的类型也是undefined
eg:typeof undefined;//undefined
-null
null表示“空”,它是“空对象”
当我们需要将对象销毁、数组销毁或者是删除事件监听时,通常将它们设置为null.
eg:box.onclick = null;删除点击事件;
注:typeof null;//object
3.3数据类型的转换
-其他值->数字 使用Number()函数
eg:Number('123');//123
Number('123年');//NaN
Number('');//0
Nubmer(true);//1
Number(false);//0
Number(undefined);//NaN
Number(null);//0
-字符串->整数 使用parseInt()函数
parseInt('3.14');//3
parseInt('3.14是圆周率');//3
parseInt('圆周率是3.14');//NaN
parseInt('3.99');//3
-字符串->浮点数 使用parseFloat()函数
parseFloat('3.14');//3.14
parseFloat('3.14圆周率');//3.14
parseFloat('圆周率是3.14');//NaN
parseFloat('3.99');//3.99
parseFloat(true);//NaN--是因为parseFloat会隐式转化为字符串类型
-其他值->字符串 使用String()函数;也可以用toString()方法,方法要用.toString(),eg:(6).toString();
String(123);//'123'
String(123.4);//'123.4'
String(2e3);//2000
String(NaN);//'NaN'
String(Infinty);//'Infinty'
String(0xf);//'15'
-其他值->布尔值 使用Boolean()函数
Boolean(123);//true
Boolean(0);//false
Boolearn(NaN);//false
-小案例:小小假发计算器
使用prompt()函数弹出输入框,让用户输入两个数字
注:用户所输入的数字是字符串类型,所以必须转为数字类型(Number),才能左加法运算;
最后用alert()显示结果;
3.4复杂的数据类型简介
数组、对象、函数function;
复杂数据类型都是“引用数据类型”
4.表达式与操作符(即运算符)
4.1.表达式与运算符
3+5就是一个表达式
表达式的分类:
4.2.js基本表达式
-算术表达+,-,*,/,%
补充:有关IEEE754
在js中,有些小数的数学运算不是很精准
eg;0.1+0.2 //0.3000000000000004
这是由于js使用使用了IEEE754二进制浮点算术标准,这会使一些个别的小数运算产生“丢失精度”的问题;
解决方法:在进行小数运算时,调用数字的toFixed()方法保留指定的小数位数;
eg:Number((0.1+0.2).toFixed(2));//保留两位小数;
js中没有提供幂计算、开根号的运算符,需要使用Math对象的相关方法进行计算。
eg:
Math.pow(2,3);//8 幂运算
Math.sqrt(81);//9 开根号
向上取整和向下取整
Math.ceil();//向上取整 eg:Math.ceil(2.4);//3
Math.floor();//向下取整 eg:Math.floor(2.4);//2
-关系表达
>
<
>=
<=
== 等于,用来比较两个数是否相等,它不比较类型,它会进行隐式转换后比较值是否相等;eg:5 == '5';//ture
!=
=== 全等于,它不仅比较值是否相等,同时也比较类型是否相等;eg:5 === '5';//false
!== 不全等于
补充:NaN是一个特殊的数字类型值,它不自等;eg:Nan == NaN;//false NaN === NaN;//false
如何判断某变量值是否为NaN
可以用isNaN()函数来判断变量值是否为NaN
eg:isNaN(NaN);//true
isNaN(5);//false
注:js中没有连比;
那判断变量a是不是介于3~15之间,应该怎么写?不能这样写2<=a<=15;
要用逻辑运算符来表示;
-逻辑表达
! 非 !true;//false 补充:用!!来显示布尔值
短路计算(假定总结果为真):
a && b 都是真才真
真 b真总结果就是真
b假总结果就假
假 不用看b了被“短路了”
公式:a && b运算中,a真,表达式值为b;a假,表达式值为a;
3 && 6;//6
undefined && 15;//undefined
15 && undefined;//undefined
null && 2;
--------------------------------------------------
a || b有真就真
真 不用看b了被“短路了”
假 b真总结果就是真
b假总结果就是假
3 || 6;//3
0 || 6;//6
null || undefined;//undefined
'a' || 'b';//'a'
NaN || null;//null
&& 与
|| 或
注:逻辑运算的顺序:
逻辑运算的优先级:非->与->或
!ture || true;//true
-赋值表达
赋值 =
快捷赋值 +=,-=,*=,/=,%=
自增运算 ++
自减运算 --
-综合表达
综合运算的运算顺序:非运算-->数学运算(+-*/%)-->关系运算-->逻辑运算(! && ||)
小案例:润年的计算
一下两个条件满足一个即可计算润年:
1.能被4整除且不能被100整除;
2.能被100整除也能被400整除;
-----------------------------------------------------------
5.流程控制语句
5.1条件语句
-if语句的基本使用;
-if else if 多支条件分支
举例:用户输入考试成绩,根据下表输出用户的成绩档次
分数 档次
85~100 优秀
70~84 良好
60~69 及格
0~59 不及格
示例代码:
var score = Number(prompt('请输入您的成绩'));
if(score >= 85){
alert('优秀');
}else if(score >= 70){
alert('良好');
}else if(score >= 60){
alert('及格');
}else{
alert('不及格');
}
举例2:BMI指数,使用体重(公斤)除以身高(米)的平方,是目前国际上常用的衡量人体胖瘦以及是否健康的标准;
BIM值 分档
低于18.5 过瘦
18.5~24(不含) 正常
24~28(不含) 过胖
28~32(不含) 肥胖
大于等于32 非常肥胖
示例代码:
var weight = Number(prompt('请输入您的体重,以公斤为单位'));
var height = Number(prompt('请输入您身高,以米为单位'));
//BMI计算
var bmi =数
alert(bmi); weight / Math.pow(height,2);
//弹出bmi指
//分档
if(bmi < 18.5){
alert('正常');
}else if(bmi < 28){
alert('过胖');
}else if(bmi <32){
alert('肥胖');
}else{
alert('非常肥胖');
}
-if语句算法题
通过案例培养程序感,有了程序感算法就自然会了:
案例一:判断水仙花数
水仙花是这样的一个3位数:把三位数拆开分别立方,再求和等于原来的三位数:
eg:153 ==> 1*1*1 + 5*5*5 + 3*3*3 = 153是水仙花数
拆位的方法:
1.数学方法eg:百位:153/100取整得到,十位:153/10取整,再与10求模;各位153%10
示例代码:
var n = Number(prompt('请输入一个水仙花数'));
//对用户输入的数字进行合法性的验证
if(!isNaN(n) && 100<=n && n<=999){
//数学方法进行拆位
//百位
var a = Math.floor(n / 100);
//十位
var b = Math.floor(n / 10) % 10;
//个位
var c = n % 10;
//根据水仙花数进行判断
if(Math.pow(a,3) + Math.pow(b,3) + Math.pow(c,3) == n){
alert('这个数字是水仙花数');
}else{
alert('这个不是水仙花数');
}
}else{
alert('您输入的数字不合法');
}
2.字符串方法(常用):直接将原来的数字变为字符串,然后使用charAt()方法得到每个数位的值
示例代码:
var n = Number(prompt('请输入一个水仙花数'));
//对用户输入的数字进行合法性的验证
if(!isNaN(n) && 100<=n && n<=999){
//字符串方法:把数字n变为字符串
var n_str = n.toString();
//百位
var a = Number(n_str.charAt(0));
//十位
var b = Number(n_str.charAt(1));
//个位
var c = Number(n_str.charAt(2));
//根据水仙花数进行判断
if(Math.pow(a,3) + Math.pow(b,3) + Math.pow(c,3) == n){
alert('这个数字是水仙花数');
}else{
alert('这个不是水仙花数');
}
}else{
alert('您输入的数字不合法');
}
游乐园门票计算:
某游乐园的门票价格如下表,请用户输入年龄和星期几,弹出对话框显示门票价格,星期几用0,1,2,3,4,5,6表示,0是周天
age>=10 age<10
平日 300 140
周末 500 210
示例代码:
var week = Number(prompt('请输入星期几'));
var age = Number(prompt('请输入年龄'));
if(week == 0 || week == 6){
//周末
if(age >= 10){
alert("500元");
}else{
alert("210元");
}
}else{
//平时
if(age >= 10){
alert("300元");
}else{
alert("140元");
}
}
-switch语句
js除了if语句外还提供了另外一种选择语句:switch
应用场景:当一个变量被分类讨论的情形。
案例:要求用户输入一个1~12中的任意一个数字,显示这个月份的天数。
示例代码:
var month = Number(prompt("请输入月份"));
switch(month){
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
alert('这个月有31天');
break;
case 4:
case 6:
case 9:
case 11:
alert('这个月有30天');
break;
case 2:
alert('这个月有28天或29天');
break;
default:
alert('请输入正确的月份');
}
-三元运算符(二选一情况下)
语法:
条件表达式?表达式1:表达式2----->if条件表达式为true执行表达式1,if为false执行表达式2
应用场景:根据某个条件是否成立,在两个不同值中选择变量的值。
示例代码:
var age = Number(prompt('请输入你的年龄'));
var type = age >= 18 ? '成年人' : '未成年人';
alert(type);
5.2 循环语句
-for循环
从一个例子了解循环语句的功能:
题目:在控制台一次输出1,2,3,4,5,6,……,10;
传统做法:
console.log(1);
console.log(2);
……
console.log(10);
这时循环就起到非常大的作用:
for(var i = 1;i <= 10;i++){
1 2 4
console.log(i);
3
}
for(var i = 2;i < 12;i += 3){
i += 4;
console.log(i);
}
6
13
特殊的:
for(var i = 1;i < 10;i ++){
}
console.log(i);//10
死循环:
for(var i = 1; i < 10;i --){
console.log(i);
}
注:首次会输出循环体(大括号里面的条件),然后再输出括号+循环体的结果
-for循环算法
例子1:
计算1+2+3+……+100
//创建累加器
var sum = 0;
//用for训话遍历所有的数字,每个遍历一个数字要把这个数字加到sum中去
for(var i = 1;i <= 100;i++){
sum += i;
}
console.log(sum);
例子2:
请问1~100中哪个数字除以3余1,除以4余2,除以5余3?(穷举法)
for(var i = 1;i <= 100;i++){
if(i % 3 == 1 && i % 4 == 2 && i % 5 == 3){
console.log(i);
}
}
-while循环语句
while是一种“不定范围”循环,只要测试条件满足,就一直执行循环体。
语法:
while(测试条件){
}
whilt注意事项:while循环没有显示定义循环变量,必须自己在while循环除先定义好循环变量,有时甚至可以没有循环变量。
案例:
var i = 1;
while(i <= 100){
document.write(i)
i++;
}
只能用while循环完成的场景:
寻找最小的满足n^2>456789的整数n;
var n = 1;
while(n * n < 46789){
n++;
}
document.write(n);
小兔子拔萝卜,第1天拔1个,第2天拔2个……一次类推。问小兔子多少天能把500个萝卜拔完。
var n = 1;
//累加器
var sum = 0;
while(sum < 500){
sum += n;
n ++;
}
document.write(n)
-break和continue
break表示立即终止循环,它只能用在循环语句中,在for循环和while循环中都可以使用。
eg:for(var i = 0;i < 10;i ++){
console.log(i);
if(i == 4){
break;
}
}
break用在while语句中,通常和while(true){}搭配使用。
eg:
var n = 1;
while(true){
if(n * n > 456789){
console.log(n);
break;
}
n ++;
}
continue用于跳过循环中的某一个迭代,并继续执循环中的下一个迭代。for循环更经常使用continue。
eg:
for(var i = 0;i < 5;i ++){
if(i == 3){
continue;
}
console.log(i);
}
-do while语句:先执行一次,再测试循环语句。
语法:
do{
循环体
}while(循环执行条件)
输出1~100
var n = 1;
do{
console.log(n);
n++;
}while(n < 101);
补充:随机函数
Math.random();0~1的小数
parseInt(Math.random()*10);
得到[a,b]之间的整数,公式:
parseInt(Math.random()*(b-a+1))+a
do while小案例:
请编写,随机生成两个变量dx和dy,它们均在[-4,4]区间的随机数,但要求dx和dy不能同时为0。
-while语句算法题
猜数字小游戏:
随机生成一个2到99的数字,让用户此猜测这个小游戏是几,用户输入自己的猜测之后,提示“输入的数字太大”或“输入的数字太小”,不断重复这个过程,直到准确次猜到这个数字。
5.3初始算法
-什么是算法
算法就是把一个问题,拆解为计算机能够一步一步执行的步骤。
计算机的流程控制语句:顺序语句、选择语句、循环语句。
优秀算法的要求:
正确性;
健壮性;(各种环境和设备下能够运行)
可读性;(让其他人能够读懂的自己的代码)
补充:
伪代码:不是正真的代码,是用文字来描述程序的执行,但可以清晰的帮助我们写程序。
算法的培养方法:
多敲、多练、多总结。
经典的业务场景要熟记在心。
-累加器和累乘器
案例:
由用户输入数字n,请计算下面算式的值
3/2+4/3+5/4+……n+1/n
示例代码:
var n = Number(prompt(请输入数字n));
var sum = 0;
for(var i = 2 ; i <= n ; i++){
sum += (i+1) / i
}
alert(sum.toFixed(2));//保留两位小数
同时用到累加器和累乘器的题目(大厂面试题)
eg:圆周率Π可以由下面的才菜布尼茨级数公式计算出来,请由用户输入参数n,计算圆周率。
Π/2=1 + 1/3 + 1*2/3*5 + 1*2*3/3*5*7 + 1*2*3/3*5*7*9 +...1*2...*n/3*5*...(2n+1)
-穷举法
是指根据题目的条件确定答案的大致范围,并在此范围内对所有可能的情况逐一验证。
eg:编程寻找100以内的的既能被3整除,也能被5整除的数字。
示例代码:
for(var i = 1; i <= 100; i++){
if(i % 3 == 0 && i % 5 == 0){
console.log(i);
}
}
eg:用户输入一个数字,在控制台显示这个数字的全部约数(该数字范围内全部能被整除的数)。
示例代码:
var n = Number(prompt('请输入一个数字'));
for(var i = 1; i <= n; i++){
if(n % i == 0){
console.log(i);
}
}
eg:寻找全部的水仙花数。
for(var i = 100; i < 1000; i++){
//水仙花数要拆位
//a,b,c分别表示百位,十位,个位
var i_str = i.toString();
var a = i_str.charAt(0);
var b = i_str.charAt(1);
var c = i_str.charAt(2);
//console.log(a,b,c);
if(Math.pow(a,3) + Math.pow(b,3) + Math.pow(c,3) == i){
console.log(i);
}
}
-综合算法题
循环嵌套:
示例代码:
for(var i = 0; i < 3; i++){
for(var j = 0; j < 3; j++){
console.log(i,j);
}
}
eg:
//寻找1~100的所有质数(只能够被1和本身整除的数字,最小的质数是2)
outer : for(var i = 1; i <= 100; i++){
for(var j = 2; j < i; j++){
if(i % j == 0){
//说明数字i不是质数,因为它找到了除1和它自身之外的约数了,就可以筛选下一个数字了
//continue表示放弃这个数字,可是迭代下一个数字,continue它负责的是它所在的最内层的for循环
//要给外层for循环加上label,然后在continue的后面加上这个lebel
//这样就表示立立即开始迭代外层for循环的下一个数字了,而不是内层for循环。
continue outer;
}
}
console.log(i);
}
eg:有鸡兔同笼,上有35个头,下有94脚,问鸡兔共有几个。
示例代码:
//假设小鸡有a只,兔子有b只
for(var a = 0; a <= 35; a++){
for(var b = 0; b <= 35; b++){
if(a + b == 35 && 2*a + 4*b == 94){
console.log('小鸡有'+a+'只'+'兔子有' + b +'只');
}
}
}
-------------------------------------------------------------------
数组
1.数组的定义和基本使用
-数组简介和定义
-数组的定义方法1
var arr = ['A','B','C'];
-数组的定义方法2
var arr = new Array('A','B','C');
-数组的定义方法3
var arr = new Array(4);//表示数组的长度为4,但这4项都是undefined
补充:访问数组:console.log(arr[索引]);
更改数组的项:
eg:var arr = [2,6,7,3];
arr[1]++;
arr[2] = 0;
console.log(arr);//[2,7,0,3];
数组的遍历:数组的最大优势就是方便遍历
eg:
var arr = ['A','B','C','D'];
for(var i = 0;i < arr.length;i++){
console.log(arr[i]);
}
-数组类型的检测
数组用typeof检测结果是object
用Array.isArray()方法可以用来检测是否为数组。
2.数组的常用方法
2.1数组的常用方法1:数组的头尾操作
数组的头尾操作的方法
push()--在尾部插入新项
var arr = [1,2,3,4,5,6];
arr.push(7,8,9);
console.log(arr);
pop()--在尾部删除,不仅会删除而且还会返回删除的项
var arr = ['A','B','C','D'];
var item = arr.pop();
console.log(arr);//['A','B','C']
console.log(item);//D
unshift()--在头插入新项
shift()--在头部删除,不仅会删除头部的项,而且返回删除的项
2.2数组的常用方法2: splice()方法:用来替换数组中的指定的项
eg:var arr = ['A','B','C','D','E','F','G'];
arr.splice(3,2,'X','Y','Z');//数字表示从下标为3开始,连续替换两项。
console.log(arr);//['A','B','C','X','Y','Z','F','G']
spLice()方法也可以用于指定位置出入新项目
eg:var arr = ['A','B','C','D'];
arr.splice(2,0,'X','Y','Z');
console.log(arr);//['A','B','X','Y','Z','C','D']
splice()方法还可以用于删除指定项
var arr = ['A','B','C','D','E','F','G'];
arr.splice(2,4);//从下标为2开始连续删除4项
console.log(arr);// ['A','B','G'];
补充:slice()方法用于得到子数组,类似于字符串的slice()方法。
slice(a,b)截取的子数组从下标为a的项开始,到下标为b但不包括b的项结束)
slice()方法不会改变原有数组
eg:var arr = ['A','B','C','D','E','F'];
var child_arr = arr.slice(2,5);;
console.log(arr);//还是原来的数组
console.log(child_arr);//['C','D','E']
注:slice()方法如果不提供第二个参数,则表示从指定项开始,提取所有后续所有项作为子数组。
slice()方法的参数允许为负数,表示数组的倒数第几项
2.3数组的常用方法3:join()和split()方法。
数组的join()方法可以使数组转为字符串;字符串的split()方法可以使字符串转为数组。
join()方法和toString()方法一样。join()的参数表示以什么字符作为连接符,如果留空则默认以逗号分隔。
splict()的参数表示以什么字符拆分字符串,一般不能留空。
eg:[22,33,44].join();//'22,33,44'--会把其连接成一个字符串
['22,33,44'].split('');//['22','33','44']--会把其拆分成多个字符串
补充:字符串和数组更多相关性:
字符串也可以使用方括号呢写下标的方式,访问某个字符,等价于charAt()方法。eg:['a','b','c'][2];//C
字符串的一些算法问题有时候会转化为数组解决。
concat()方法可以合并连结多个数组。
eg:
var arr1 = [1,2,3,4];
var arr2 = [5,6,7,8];
var arr3 = [9,10,11];
var arr = arr1.concat(arr2,arr3);
console.log(arr);//[1,2,3,4,5,6,,7,8,9,10,11];
concat()方法不会改变原数组。
reverse()方法用来将一个数组的的全部项顺序置反
eg:var arr = ['a','b','c','d'];
arr.reverse();
console.log(arr);//['d','c','b','a']
indexOf()和includes()方法。
indexOf()方法的功能是搜索数组中的元素,并返回它所在位置,如果元素不存在,则返回-1
inlcudes()方法的功能是判断一个数组是否包含一个指定的值,返回布尔值。
['a','b','c','d'].indexOf('c');//2--返回的是数组的下标
['a','b','c','d'].includes('c');//true--用来判断该项是否存在
3.数组的算法
-遍历相关算法
eg:求数组中每一项目的总和、平均数
示例代码:
//求数组的总和
var arr = [3,5,3,2,1];
//累加器
var sum = 0;
//遍历数组,每遍历一个数字,就要把这个数字累加到累加器中
for(var i = 0; i <= arr.length; i++){
sum += arr[i];
}
console.log(sum);
console.log(sum / arr.lenght);
eg:求数组项的最大值和最小值
示例代码:
var arr = [3,5,88,2,1];
/*
定义两个变量,max表示当前寻找到的最大值,默认是arr[0]
===============================最小值,默认是arr[0]
**/
var max = arr[0];
var min = arr[0];
//遍历数组,从下标为1的项开始遍历
for(var i = 1; i < arr.length; i++){
if(arr[i] > max){
max = arr[i];
}else if(arr[i] < min){
min = arr[i];
}
}
console.log(max,min);
-数组去重和随机样本
去掉数组中的重复项
思路:准备一个空结果数组,遍历原数组,如果遍历到的项不再结束数组中,则推入结果数组。
示例代码:
var arr = [1,1,1,1,2,2,2,3,3,4];
//准备结果数组
var result = [];
//遍历数组
for(var i = 0; i < arr.length; i++){
//判断遍历到的这项是否在结果中,如果不在就推入
//includes()方法用来判断某项是否在数组中
if(!result.includes(arr[i])){
result.push[arr[i]];
}
}
console.log(result);
随机样本
题目:请随机从原数组中取3项
思路:准备一个空数组,遍历原数组,随机选择一项,推入结果数组,并且将这项删除
//随机从数组中取3项
var arr = [3,6,10,5,8,9];
//结果数组
var result = [];
for(var i = 0;i < 3; i++){
var n = parseInt(Math.random() * arr.length);
//把这个结果推入结果数组中
result.push(arr[n]);
//删除这项,防止重复项被随机到
arr.splice(n,1);//删除数组中的某一项
}
console.log(result);
-冒泡排序(小的数会浮到上方)
冒泡排序是一个著名的排序算法,也是在面试时非常爱考察的算法,其核心思路是一趟一趟的进行多次项的两两比较,每次都会将最小的元素排好位置,
如同水中的气泡上浮一样。
公式:n个数字,共需要比较n-1趟,比较次数为n(n-1)/2
-二维数组(可以看作是矩阵)
eg:var matrix = [
[11,12,13],
[21,22,23],
[31,32,33],
[41,42,43]
]
console.log(matrix.length);//4
//遍历
for(var i = 0; i < 4; i++){
for(var i = 0;i < 3; i++){
console.log(matrix[i][j]);
}
}
4.认识引用类型
-什么是引用类型(数组,对象等)
基本类型:number,boolean,string,undefined,null
引用类型:array,object,function,regexp……
相等判断时的区别:
基本类型进行相等判断时,会比较值是否相等。
引用类型进行相等时,会比较址是否相等,也就是说它会比较是否为内存中的同一个东西。
-深克隆和浅克隆
使用arr1 = arr2的语法不会克隆数组。
浅克隆:值克隆数组的第一层,如果是对维数组,或者数组中的项是其他引用类型值,则不克隆其他层。
浅克隆的实现方法:
var arr1 = [1,2,3,4];
//结果数组
var result = [];
//遍历原数组中的每一项,把遍历到的项推入到结果数组中
for(var i = 0; i < arr1.length; i++){
result.push(arr1[1]);
}
console.log(result);
console.log(result == arr1);//false,即使连个数组的值一样,但对象不一样。
深克隆:克隆数组的所有层,要使用递归技术。
-------------------------------------------------------------------------
函数
1.函数的基本使用
1.1什么是函数
函数就是语句的封装,可以让这些代码方便的被复用(和数学中的f(x)函数一样);
函数具有“一次定义,多次调用”的优点。
使用函数,可以简化代码,让代码更具有可读性。
1.2函数的定义和调用
和变量类似,函数必须先定义然后才能调用。
使用function关键字定义函数。
语法:
function fun(){
//函数语句体句
}
或者匿名函数:函数表达式
var fun = function(){
}
函数的调用
执行函数语句体中的所有语句称为:调用函数
语法:fun();//调用函数
注:函数只用被调用才能被执行。
和变量一样函数声明可以提升(先调用后声明):
fun();
funciton fun(){
alert('函数被执行');
}
面试题:
fun();//B
var fun() = function(){
alert('A');
}
function fun(){
alert('B');
}
fun();//A
1.3函数的参数的返回值
参数是函数的一些待定值,在调用函数时,必须传入这些参数的具体值,多个参数时要用都好隔开。
eg:function add(a,b){
var sum = a+b;
console.log('两个数的和是:'+sum);
}
add(3,5);
补充:
arguments:作为输出参数会生成数组。
函数内arguments表示它接收到的实参列表,它是一个类数组对象;
类数组对象:所有属性均为从0开始的自然数序列,并且哟length属性,和数组类似可以用方法括号书写下标访问对象的某个属性值,但是不能调用数组的方法。
函数的返回值:函数体可以使用return关键字表示“函数的返回值”。但在调用时要加一个变量来接收返回值
eg:function sum(a,b){
return a+b;
}
var result = sum(4,5);
console.log(result);
返回值的优点:调用一个返回值的函数,可以被当作一个普通值,从而可以出现在任何一个可以书写的地方:
function sum(a,b){
return a + b;
}
var result = sum(3,4) * sum(2,6);
注:调用函数时,一旦遇见return语句则会立即退出函数,将执行权交还给调用者。
2.函数的算法题
2.1函数的算法题1
题目:寻找喇叭花数
喇叭花数:每一位数字的阶乘之和恰好等于它本身。即abc = a! + b! +c!,其中abc表示一个三位数。
示例代码:
//找喇叭花数:每一位数字的阶乘之和恰好等于它本身。
即abc = a! + b! +c!,其中abc表示一个三位数。
//计算一个数字的阶乘
function factorial(n){
//累成器
var result = 1;
for(var i = 1; i <= n; i++){
result *= i;
}
return result;
//factorial(n);//某个数的阶乘
}
//穷举法,从100~999寻找喇叭花数
for(var i = 100; i <= 999; i++){
//转为字符串进行拆分
var i_str = i.toString();
//abc分别表示百位,十位,个位
var a = Number(i_str[0]);
var b = Number(i_str[1]);
var c = Number(i_str[2]);
if(factorial(a) + factorial(b) + factorial(c) == i){
console.log(i)
}
}
2.2函数的算法题2
js内置sort()方法。
数组排序可以使用sort()方法,但需要传入一个参数,这个参数必须是一个函数。
eg:var arr = [33,22,55,11];
arr.sort(function(a,b){
})
3.递归
3.1什么是递归
递归就是函数内部自己调用自己,可以想象成一个画家在不断画自己画画的场景。
递归的要素:
边界条件:确定递归到何时终止,也称为递归出口。
递归模式:大问题如何分解为小问题,也称递归体。
3.2递归常见算法题
斐波那契数列:1,1,2,3,5,8,13,21你找到规律了吗?
示例代码:
//斐波那契数列:1,1,2,3,5,8,13,21
function fib(n){
//数列的下标为0和1的项值是1
if(n == 0 || n == 1){
return 1;
}else{
return fib(n -1) + fib(n-2);
}
}
console.log(fib(6));
//书写一个循环语句,计算斐波那契数列的前15项
for(var i = 0; i < 15; i++){
console.log(fib(i));
}
3.3实现深克隆
复习引用类型和基本类型:
举例 当var a = b变量传值 当用==比较时
---------|--------------------|----------------------------------|-------------------------
基本类型 Number,String, 内存中产生新的副本 比较值是否相等
boolean,undefined
---------|--------------------|----------------------------------|-------------------------
引用类型 Object,Array 内存中不会产生新的副本, 比较内存地址是否相同,
而是让变量指向同一个对象 即比较是否是同一个对象
---------|--------------------|----------------------------------|-------------------------
实现深克隆:
//原数组
var arr1 = [33,44,11,22,[77,88]];
//函数,这个函数会被递归
function deepClone(arr){
var result = [];
//遍历数组的每一项
for(var i = 0; i < arr.length; i++){
//类型判断,如果遍历到的项是数组
if(Array.isArray(arr[i])){
//递归
deepClone(arr[i]);
}else{
//递归出口
result.push(arr[i]);//基本类型值
}
}
return result;
}
//测试
var arr2 = deepClone(arr1);
console.log(arr2);
4.作用域和闭包
4.1全局变量和局部变量
变量的作用域:
javaScript是函数级作用域编程语言:变量旨在其定义时所在的function内部又意义。
eg:funcion fun(){
var a = 56;
}
fun();
console.log(a);//报错
全局变量:如果不将变量定义在任何函数的内部,此时这个变量就是全局变量,它在任何函数内部都可以被访问和更改。
eg:
var a = 10;
function fun(){
a++;
console.log(a);//11
}
fun();
console.log(a);//11
遮蔽效应:如果函数中也定义了和全局同名的变量,则函数内的变量会将全局变量“遮蔽”。
注:形参也是局部变量
eg:var a = 10;
function fun(a){
a++;
console.log(a);//8
}
fun(7);
console.log(a);//10
作用域链:在函数嵌套中,变量会从内到外逐层寻找它的定义。
var a = 10;
var b = 20;
function fun(){
var c = 30;
function inner(){
var a = 40;
var d = 50;
console.log(a,b,c,d);//距自己最近的变量去找,找不到就去外面找,直到找到为止。
}
inner();
}
fun();
不加var关键字定义全局变量:
在初次给变量赋值时,如果没有加var,则将定义全局变量:
eg:function fun(){
a = 3;
}
fun();
console.log(a);//3
4.2闭包
先看以一个程序:
function fun(){
//定义一个局部变量
var name = 'hello';
//返回一个局部函数
return function(){
alert(name);
};
}
//调用外部函数就能得到内部函数,用变量inn来接受
var inn = fun();
//定义一个全局变量
var name = 'ABC';
//执行inn函数,就相当于在fun函数的外部,执行了内部函数
inn();
容易看出:内部函数转到外部来用,形成一个组合就称作闭包(是一种环境状态)。
闭包:就是函数本身和该函数声明时所处的环境状态的组合
闭包的功能:记忆性、模拟私有变量。
记忆性:当闭包产生时,函数所处环境的状态会始终保持在内存中,不会在外层函数调用后自动清除。这就是闭包的记忆性。
举例:创建体温检测函数chekTemp(n),可以检查体温n是否账正常,函数会返回布尔值。
示例代码:
function createTemp(standardTemp){
function checkTemp(n){
if(n <= standardTemp){
alert('你的体温正常');
}else{
alert('你的体温偏高');
}
}
return checkTemp;//返回的是函数
}
var checkTemp_A = createTemp(37.1);//会记住这个区的标准体温
checkTemp_A(37.2);
checkTemp_A(37.0);
闭包的用途-模拟私有变量
题目:请定义一个变量a,要求是能保证这个a只能被进行指定操作(如:加1,乘2),而不能进行其他操作。
补充:在Java,c++等语言中,有私有属性的概念,但是JavaScript中只能用闭包来模拟,从而保证变量的私有属性。
5.立即执行函数
5.1立即执行函数IIFE
IIFE(立即调用函数表达式):是一种特殊的JavaScript函数写法,一旦被定义,就立即被调用。
基本写法:
(function(){
statements;
})();
IIFE的作用:
为变量赋值:当给变量赋值需要一些较为复杂的计算时(如if语句),使用IIFE显得语法更紧凑。
eg:
vvar age = 12;
var sex = '男';
var title = (function(){
if(age < 18){
return '小朋友';
}else{
if(sex == '男'){
return '先生';
}else{
return '女士';
}
}
})();
alert(title);
作用2想看一个条题目:
var arr = [];
for(var i = 0; i < 5; i++){
arr.push(function(){
alert(i);
});
}
arr[2]();//5
IIFE可以在一些场合(如for循环中)将全局变量变为局部变量,语法显得紧凑。
var arr = [];
for(var i = 0; i < 5; i++){
(function (i){
arr.push(function(){
alert(i);
})
})(i);
}
arr[2]();//2
------------------------------------------------------------------------------------
DOM
1.DOM的基本概念
理解:DOM是js操作html和css的桥梁。
1.1DOM的基本概念
DOM(Document Object Model,文档对象模型)是JavaScript操作HTML文档的接口,使文档操作变得非常优雅,简便。
补充nodeType常用属性值
节点的nodeType属性可以显示这个节点具体的类型。
nodeType 节点类型
1 元素节点,列如<p>和<div>
3 文字节点
8 注释节点
9 document节点
10 DTD节点
2.节点的访问和位置关系
2.1访问元素节点:就是“得到”、“获取”页面上的元素节点。
访问元素节点主要是依靠document对象,document.nodeType;//9,document封装了所有节点的方法。
访问元素节点的常用方法
document.getElementById() 通过id获取元素 兼容到IE6
document.getElementByTagName() 通过标签名得到元素素组(批量处理) 兼容到IE6
document.getElementByClassName() 通过类名得到元素数组 兼容到IE9
document.querySelector() 通过选择器得到元素 兼容到IE8
document.querySelectorAll() 通过选择器得到元素数组 兼容到IE9
补充:延迟运行
可以使用window.onload = function{}事件(事件监听),使页面加载完毕后,再执行指定代码。
2.2节点的关系
补充:文本也属于节点
关系 考虑所有节点 只考虑元素节点(常用)
子节点:childNodes children
父节点:parentNode children
第一个子节点:firstChild firstElementChild
最后一个子节点:lastChild lastElementChild
前一个兄弟节点:previousSibling previousElementSibling
后一个兄弟节点:nextSibling nextElementSibling
eg:<div id='box'>
<p>我是段落A</p>
<p id='para'>我是段落B</p>
<p>我是段落C</p>
</div>
var box = document.getElementById('box');
var para = docuemnt.getElementById('para');
//获取所有子节点
console.log(box.childNodes);
//获取所有的元素子节点(IE9开始兼容)
console.log(box.children);//段落A,B,c
补充:解决浏览器兼容性问题,要封装函数。
3.节点操作
3.1节点操作
如何改变元素节点中的内容:
改变元素节点中的内容可以使用两个相关属性:innerHTML,innerText
innerHTML属性能以HTML语法设置节点中的内容。
innerText属性只能以纯文本的形式设置节点中的内容。
示例代码:
<div id="box"></div>
<script>
var oBox = document.getElementById('box');
oBox.innerHTML = '你好';
//html标签
oBox.innerHTML = '<ul><li>牛奶</li><li>咖啡</li></ul>'
//而innerText只能是文本不支持标签
oBox.innerText = '我只支持纯文本';
</script>
如何改变元素节点的css样式
oBox.style.backgroundColor = 'red';
oBox.style.backgroundImage = 'url(images/1.jpg)';
oBox.style.fontSize = '32px';
如何改变元素节点的HTML属性:
标准W3C属性,如src,href等,只需要直接打点进行更改即可。
eg:oImg.src = 'images/2.jpg';
示例代码:
<img src='images/1.jpg' id = 'pc'>
<a href='http://www.baidu.com' id='link'>去百度</a>
<script>
var oPic = document.getElementById('pic');
var oLink = documentById('link');
oPic.src = 'images/2.jpg';
oLink.href = 'http://www.imoc.com';
oLink.innerText = '去慕课网';
</script>
如何改变元素节点的HTML属性
不符合W3C标准的属性,要使用setAttribute()和getAttribute()来设置,读取:
oBox.setAttribute('data-n',10);
var n = oBox.getAttribute('data-n');
alert(n);
示例代码:
<div id="box"></div>
<script>
var box = document.getElementById('box');
box.setAttribute('data-n',10);
var n = box.getAttribute('data-n');
alert(n);
</script>
-节点的创建:
document.createElement()方法用于创建一个指定tagname的HTML元素。
var oDiv = document.createElement('div');
此时并没有被挂载到DOM树上,我们无法看见它,必须继续使用appendChild()或insertBefore()方法将节点插入到DOM树上。
-appendChild():任何已经在DOM树上的节点,都可以调用appendChild()方法,它可以将孤儿节点挂载它的内部,成为它的最后一个子节点。
父节点.appendChild(孤儿节点);
-insertBefore():任何已经在DOM树上的节点,都可以调用inserBefor()方法,它可以将孤儿节点挂载到它的内部,成为它的“标杆子节点”之前的节点。
父节点.insertBefore(孤儿节点,标杆节点),可以指定在哪个之前。
示例代码:
<div id="box">
<p>我是原本的段落0</p>
<p>我是原本的段落1</p>
<p>我是原本的段落2</p>
</div>
<script>
var oBox = document.getElementById('box');
//获取标签
var oPs = document.getElementsByTagName('p');
//创建孤儿节点
var oP = document.createElement('p');
//设置内部文字
oP.innerText = '我是新加入的';
//上树
//oBox.appendChild(op);
oBox.insertBefore(oP,oPs[0]);
</script>
小案例:
请动态创建出要给20行12列的表格
示例代码:
<style>
td{
width: 20px;
height: 20px;
border: 1px solid #000;
}
</style>
</head>
<body>
<table id="mytable"></table>
<script>
//请动态创建出要给20行12列的表格
var mytable = document.getElementById('mytable');
for(var i = 0; i < 20; i++){
//遍历一次就创建一行
var tr = document.createElement('tr');
for(var j = 0; j < 12; j++){
//遍历一次就创建一列
var td = document.createElement('td');
tr.appendChild(td);
}
mytable.appendChild(tr);
}
</script>
移动节点:
如果将已经挂载到DOM树上的节点成为appendChild()或者insertBefore()的参数,这个节点将会被移动。
语法:新父节点.appendChild(已经有父节点的节点);
新父节点.insertBefore(已经有父亲的节点,标杆子节点);
示例代码:
<div id="box1">
<p id="para">我是段落</p>
</div>
<div class="" id="box2">
<p>我是box2的原有标签p标签</p>
<p>我是box2的原有标签p标签</p>
<p>我是box2的原有标签p标签</p>
<p>我是box2的原有标签p标签</p>
</div>
<script>
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var para = document.getElementById("para");
var ps_inbox2 = box2.getElementsByTagName('p');
//box2.appendChild(para);//此时移动到box2中
box2.insertBefore(para,ps_inbox2[0]);
</script>
删除节点:removeChild()方法从DOM中删除一个子节点。
语法:父节点.removeChild(要删除子节点);
注:节点不能主动删除自己,必须由父节点删除它。
示例代码:
<div id="box">
<p>我是p节点</p>
</div>
<script>
var box = document.getElementById('box');
var the_first_p = box.getElementsByTagName('p')[0];
box.removeChild(the_first_p);
</script>
4.节点的创建、移除和克隆
4.1节点的创建、移除和克隆
cloneNode()方法可以克隆节点,克隆节点是“孤儿节点”。
var 孤儿节点 = 老节点.cloneNode();或者
var 孤儿节点 = 老节点.cloneNode(true);
说明:参数是一个布尔值,表示是否采用深度克隆,如果为true,则
该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身。
示例代码:
<div id="box1">
<ul>
<li>牛奶</li>
<li>咖啡</li>
<li>可乐</li>
</ul>
</div>
<div id="box2"></div>
<script>
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var theul = box1.getElementsByTagName('ul')[0];
//克隆节点
var new_ul = theul.cloneNode(true);//加上true子节点也会被克隆
box2.appendChild(new_ul);
</script>
5.DOM事件
5.1事件监听
监听:就是让计算机随时能够发现这个事件发生了,从而执行程序员预先编写的一些程序。
理解:监视某个程序发生时,执行另一个程序。如:点击某个按钮,就会执行一个程序。
设置事件监听的方法主要有onxxx和addEventListener()两种。
eg:oBox.onclick = funciton(){
//点击盒子时,将执行这里的语句。
}
常见的鼠标事件监听
事件名 事件描述
onclick 当鼠标单击某个对象
ondblclick 当鼠标双击某个对象
onmousedown 当某个鼠标按键在某个对象上被按下
onmouseup 当某个鼠标按键在某个对象上被松开
onmousemove 当某个鼠标按键在某个对象上被移动
onmouseenter 当鼠标进入某个对象(相似事件onmouseover)
onmouseleave 当鼠标离开某个对象(相似事件onmouseout)
示例代码:
<style>
div{
width: 200px;
height: 200px;
background-color: pink;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
var oBox = document.getElementById('box');
oBox.onclick = function(){
console.log('我是onclick事件');
}
oBox.onmousedown = function(){
console.log('我是onmousedow事件');
}
</script>
常见的键盘事件监听
事件名 事件描述
onkeypress 当某个键盘的键被按下(系统无法识别是哪个键盘)
onkeydow 当某个键盘的键被按下(系统可以识别是哪个键盘,并优先于onkeypress)
onkdyup 当某个键盘的键被松开时
示例代码:
姓名:
<input type="text" id="nameField">
<script>
var nameField = document.getElementById('nameField');
nameField.onkeydown = function(){
console.log('我是onkeydow');
}
naemField.onkeypress = function(){
console.log('我是onkeypress');
}
</script>
常见的表单事件监听:
事件名 事件描述
onchange 当用户改变域的内容
onfocus 当某个元素获得焦点(比如tab键或鼠标点击)
onblur 当某元素失去焦点
onsubmit 当表单被提交
onreset 当表单被重置
示例代码:
<form id="myform">
<p>
姓名:
<input type="text" name="nameField">
</p>
<p>
年龄:
<input type="text" name="ageField">
</p>
<p>
<input type="submit">
</p>
</form>
<script>
var myform = document.getElementById('myform');
var naemField = myform.nameField;
var ageField = myform.ageField;
nameField.onchange = function(){
console.log('你已经修改姓名');
}
nameField.oninput = function(){
console.log('你正在修改姓名');
}
nameField.onfocus = function(){
console.log('姓名框已经得到了焦点');
}
nameField.onblue = function(){
console.log('姓名框已经失去了焦点');
}
nameField.onsubmit = function(){
console.log('你正在提交表单');
}
</script>
常见的页面事件监听:
事件名 事件描述
onload 当页面或图象被完成加载
onunload 当用户退出页面
5.2事件传播
实际上,事件的传播是:先从外到内(捕获阶段:capturing phase),然后再从内到外(冒泡阶段:Bubbling phase)。
注:onxxx这样的监听事件只能监听冒泡阶段。
DOM0级事件监听:只能监听冒泡阶段。
addEventListener()方法,可以监听到捕获阶段:
DOM1级事件监听:
oBox.adEventListener('click',function(){
//这是事件处理函数
},true);//true:监听捕获阶段,false:监听冒泡阶段
注意事项:
如果有两个同名的盒子,使用DOM0级事件会覆盖前面的,而使用DOM1级则不会覆盖,会按顺序执行。
示例代码:
<style>
#box1{
width: 202px;
height: 202px;
border: 1px solid #000;
padding:50px;
}
#box2{
width: 100px;
height: 100px;
border: 1px solid #000;
padding:50px;
}
#box3{
width: 100px;
height: 100px;
border: 1px solid #000;
padding:50px;
}
</style>
</head>
<body>
<div id="box1">
<div class="box2">
<div class="box3"></div>
</div>
</div>
<script>
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var box3 = document.getElementById('box3');
//DOM0级事件
box1.onclick = function(){
alert('A');
}
box2.onclick = function(){
alert('B');//B会覆盖A
}
//=================================
//DOM1级事件
box1.addEventListener('click',function(){
alert('C');
},true);
box2.addEventListener('click',function(){
alert('D');
},true);
</script>
5.3事件对象
事件对象就是函数提供要给形式参数,它是一个对象,封装了本次事件的细节:
这个参数通常用even或字母e表示:
oBox.onmousemove = function(e){
//对象e就是这次是事件的“事件对象”
};
e的常用属性和方法
属性 属性描述
clientX 鼠标指针相对于浏览器的水平坐标
clientY 鼠标指针相对于浏览器的垂直坐标
pageX 鼠标指针相对于整张网页的水平坐标
pageY 鼠标指针相对于整张网页的垂直坐标
offsetX 鼠标指针事件源元素的水平坐标
offsetY 鼠标指针相对于事件源的垂直坐标
示例代码:
<style>
#box{
width: 200px;
height: 200px;
background-color: aqua;
margin:100px;
}
body{
height: 2000px;
}
#info{
font-size:30px;
}
</style>
</head>
<body>
<div id="box"></div>
<div id="info"></div>
<script>
var oBox = document.getElementById('box');
var oInfo = document.getElementById('info');
oBox.onmousemove = function(e){
oInfo.innerHTML = 'offseX/Y:' + e.offsetX + ',' + e.offsetY;
};
</script>
e.charCode和e.keyCode属性:
e.charCode属性通常用onkeypress事件中(键盘事件),表示用户输入的字符的“字符码”。
e.keyCode属性通常用于onkeydown事件和onkeyup中,表示用户按下的按键的的“键码。
charCode字符码
字符 字符码
数字0~9 48~57
大写字母A~Z 65~90
小写字母a~z 97~122
--------------------------------------------------------------------------
keyCode键码
按键 键码
0-9 48-57(同charCode键码完全相同)
字母不分大小写 68-90(同charCode键码的大写字母A-Z,而keyCode不分大小写,一律为65-90)
四个方向键左,上,右,下 37,38,39,40
回车 13
空格键 32
--------------------------------------------------------------------------
示例代码:
<!-- 显示键盘按键 -->
<input type="text" id="field">
<h1 id="info"></h1>
<input type="text" id="field2">
<h1 id="info2"></h1>
<script>
var oField = document.getElementById('field');
var oInfo = document.getElementById('info');
var oField2 = document.getElementById('field2');
var oInfo2 = document.getElementById('info2');
oField.onkeypress = function(e){
oInfo.innerText = '你输入字符的字符码是'+e.charCode;
}
oField2.onkeydown = function(e){
oInfo2.innerText = '你按下的键盘的字符码是'+e.keyCode;
}
</script>
小案例:制作一个特效
按方向键可以控制页面上的盒子移动。
示例代码:
<style>
#box{
position:absolute;
top:200px;
left:200px;
width: 100px;
height: 100px;
background-color: orange;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
var oBox = document.getElementById('box');
//全局变量t,l分别表示盒子的top属性和left属性
var t = 200;
var l = 200;
//监听document对象的键盘按下事件监听
document.onkeydown = function(e){
switch(e.keyCode){
case 37:
l -= 3;
break;
case 38:
t -= 3;
case 39:
l += 3;
break;
case 40:
t += 3;
break;
}
//更改样式
oBox.style.left = l + 'px';
oBox.style.top = t + 'px';
}
</script>
e.preventDdfault()方法:
e.preventDefault()方法用来阻止事件产生的“默认事件”。
案例1:制作一个文本框,只能让用户在其中输入小写字母和数字,其他字符输入没有效果。
示例代码:
<body>
<p>
<input type="text" id="field">
</p>
<script>
//制作一个文本框,只能让用户在其中输入小写字母和数字,其他字符输入没有效果。
var oField = document.getElementById(field);
oField.onkeypress = function(e){
//根据用户输入的字符的字符码(e.charCode)
//0-9,字符码时48-57
//小写字母a-z,字符码是97-122
if(!(e.charCode >= 48 && e.charCode <= 57 || e.charCode >= 97 && e.charCode <= 122)){
e.preventDefault();
}
};
</script>
案例2:制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字加1,反之,数字减1。
鼠标滚轮事件是onmousewheel,它的事件对象e提供了deltaY属性表示鼠标滚轮方向,向下滚动时返回正值,
向上向上滚动时返回负值。
示例代码:
<style>
#box{
width: 200px;
height: 200px;
background-color: #333;
}
body{
height: 2000px;
}
</style>
</head>
<body>
<div id="box"></div>
<h1 id="info">0</h1>
<script>
var oBox = document.getElementById('box');
var oInfo = document.getElementById('info');
//全局变量就是info显示的数字
var a = 0;
//给box盒子添加鼠标事件监听
oBox.onmousewheel = function(e){
/*
阻止默认事件:当用户在盒子里面滚动的时候,此时不会引发页面的滚动条的滚动。
*/
e.preventDefault();
if(e.deltaY > 0){
a--;
}else{
a++;
}
oInfo.innerText = a;
}
</script>
e.stopPropagation()方法用来阻止事件继续传播。
示例代码:
<div id="box">
<button>按我</button>
</div>
<script>
var oBox = document.getElementById('box');
var oBtn = document.getElementById('btn');
// oBox.onclick = function(){
// console.log('我是盒子');
// };
// oBtn.onclick = function(){
// e.stopPropagation();
// console.log('我是按钮');
// };
oBox.addEventListener('click',function(){
e.stopPropagation();
console.log('我是盒子');
},true);
oBtn.addEventListener('click',function(){
console.log('我是按钮');
},false);
</script>
小案例:
制作一个弹出层:点击按钮显示弹出层,点击网页任意地方,弹出层关闭。
示例代码:
<style>
.modal{
width: 400px;
height: 140px;
background-color: #333;
position:absolute;
top:50%;
left:50%;
margin-top:-70px;
margin-left:-200px;
display:none;
}
</style>
</head>
<body>
<button id="btn">按我弹出弹出层</button>
<div class="modal" id="modal"></div>
<script>
var oBtn = document.getElementById('btn');
var oModal = document.getElementById('modal');
//点击按钮的时候,弹出层显示
oBtn.onclick = function(e){
//阻止事件按继续传播到document身上
e.stopPropagation();
oModal.style.display = 'block';
};
//点击页面任何部分的时候,弹出层关闭
document.onclick = function(){
oModal.style.display = 'none';
};
//点击弹出层内部的时候,在盒子内部不能关闭弹出层,此时要阻止事件的传播
oModal.onclick = function(e){
e.stopPropagation();
}
</script>
5.4事件委托
批量添加事件监听
题目:页面上有一个无序列表<ul>,其内部共有20<li>元素,请批量给它们添加点击事件,实现效果:
点击哪个<li>元素,哪个<li>元素就变红。
示例代码:
<ul id="list">
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
</ul>
<script>
var oList = document.getElementById('list');
var lis = oList.getElementsByTagName('li');
//书写循环语句,批量给元素添加监听
for(var i = 0;i < lis.length; i++){
lis[i].onclick = function(){
//在这个函数中,this表示点击的这个元素
this.style.color = 'red';
}
}
</script>
批量添加事件监听的性能问题
每一个事件监听注册都小号一定的系统内存,而批量添加事件会导致监听数量太多,内存消耗非常大。
问题2:往<ul>中添加<li>,要求每个<li>有点击事件监听,点击哪一个那一个就变红。
示例代码:
<button id="btn">按我添加新的li列表项</button>
<ul id="list"></ul>
<script>
var oBtn = document.getElementById('btn');
var oList = document.getElementById('list');
oBtn.onclick = function(){
//创建一个新的li列表项目是孤儿节点
var oLi = document.createElement('li');
oLi.innerHTML = '我是列表项';
//把它放到ul中
oList.appendChild(oLi);
//给新创建的这个节点添加onclick事件监听
oLi.onclick = function(){
this.style.color = 'red';
};
}
</script>
批量添加事件监听的性能问题
每一个事件监听注册都小号一定的系统内存,而批量添加事件会导致监听数量太多,内存消耗非常大。
以上两个问题可以看出:不能自动获取事件监听,大量监听事件处理函数会产生大量消耗内存。
解决方法用事件委托。
事件委托:利用事件冒泡机制,将后代元素事件委托给祖先元素。
eg:
<ul id="list">
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
</ul>
给list添加onclick监听事件即可,因为不管点击任何一个<li>元素都会通过事件按冒泡传给祖先元素。
e.target和e.currentTarget属性
事件委托通常结合使用e.target属性(事件委托用来节约内存开销)
属性 属性描述
target 触发此事件的最早元素,即“事件源元素”(真正点击的那个)
currentTarget 事件处理程序附加到的元素(即父盒子覆盖到的所有子元素)
示例代码:
<ul id="list">
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
</ul>
<script>
var oList = document.getElementById('list');
oList.onclick = function(e){
//target属性:点击的列表项
e.target.style.color = 'red';
};
</script>
事件委托例子:
示例代码:
<button id="btn">按我创建一个新列表项</button>
<ul id="list">
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
</ul>
<script>
var oList = document.getElementById('list');
var oBtn = document.getElementById('btn');
oList.onclick = function(e){
//e.target表示用户真正点击的那个元素
e.target.style.color = 'red';
};
oBtn.onclick = function(){
//创建一个li元素
var oLi = document.createElement('li');
oLi.innerText = '我是新添加的项';
//把其添加到ul中
oList.appendChild(oLi);
};
</script>
事件委托的使用场景:
当有大量类似元素需要批量添加事件监听时,使用事件委托可以减少内存的开销。
当有动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听。
事件委托时需要注意的事项
onmouseenter和onmouseover都表示“鼠标进入”,它们有什么区别呢?
onmouseenter不冒泡,onmouseover冒泡。(使用冒泡的)
最内层元素不能再有额外的内层元素了,比如:
<ul>
<li><span>我是span</span></li>
<li><span>我是span</span></li>
<li><span>我是span</span></li>
<li><span>我是span</span></li>
</ul>
这样就不适合使用事件委托。
6.实现动画
6.1定时器和延时器
定时器:
setInterval(function(),2000)函数可以重复调用一个函数,再每次调用之间具有固定的时间间隔。
eg:setIntervale(function(){
//这个函数将自动被以固定间隔时间调用
},2000);
setInterval()函数参数:
setInterval(function(a,b){
//形式参数a的值时88,b时66
},2000,88,66)
具名函数也可以传入setInterval:
var a = 0;
funciton fun(){
console.log(++a);
}
setInterval(fun,1000);
清除定时器:
clearInterval()函数额可以清除要一个定时器。
eg:
var timer = setInterval(function(){
},2000);
//点击按钮时,清除定时器
oBtn.onclick = function(){
clearInterval(timer);
}
示例代码:
<h1 id="info">0</h1>
<button id="btn1">开始</button>
<button id="btn2">暂停</button>
<script>
var oInfo = document.getElementById('info');
var oBtn1 = document.getElementById('btn1');
var oBtn2 = document.getElementById('btn2');
var a = 0;
var timer;
oBtn1.onclick = function(){
//为了防止定时器的叠加(越点越快),应该再设置定时器之前先清除定时器
clearInterval(timer);//当叠加时会被清除
//更改全局变量timer的值为一个定时器实体
timer = setInterval(function(){
oInfo.innerText = ++a;
},1000);
};
oBtn2.onclick = function(){
clearInterval(timer);
}
延时器:
setTimeout()函数可以设置一个延时器,当指定时间到了之后,不再重复。
setTimeout(funciton(){
//这个函数会在2秒后执行一次,不会重复执行
},2000)
清除延时器用clearTimeout()函数。
示例代码:
<button id="btn1">2秒后弹出你好</button>
<button id="btn2">取消弹出</button>
<script>
var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');
var timer;
btn1.onclick = function(){
timer = setTimeout(function(){
alert('你好');
},2000);
}
btn2.onclick = function(){
clearTimeout(timer);
}
</script>
初始异步语句
setInterval()和setTimeout()是两个异步语句。
异步:不会阻塞cpu继续执行其他语句,当异步完成时,会执行“回调函数”。
eg:
setTimeout(function(){
console.log('A');
},2000);
console.log('B');
先输出B,再输出A,这就异步而function就是回调函数。
6.2使用定时器实现动画
示例代码:
<style>
#box{
position:absolute;
top:100px;
left:100px;
width: 100px;
height: 100px;
background-color: orange;
}
</style>
</head>
<body>
<button id="btn">开始运动</button>
<div id="box"></div>
<script>
var btn = document.getElementById('btn');
var box = document.getElementById('box');
var left = 100;
btn.onclick = function(){
setInterval(function(){
left += 2;
box.style.left = left + 'px';
},20);
}
</script>
6.3Js和CSS3结合实现动画
优点:
-我们知道css3的transition过渡属性可以实现动画。
-js可以利用css3的transition属性轻松实现元素动画。
-js和css3结合实现动画规避了定时器制作动画的缺点。
示例代码:
<style>
#box{
width: 100px;
height: 100px;
background-color: orange;
position:absolute;
top:100px;
left:100px;
}
</style>
</head>
<body>
<button>按我运动</button>
<div id="box"></div>
<script>
var btn = document.getElementById('btn');
var box = document.getElementById('box');
//标识量,指示当前盒子在左边还是右边
var pos = 1; //左边:1 右边:2
btn.onclick = function(){
box.style.transition = 'all 2s linear 0s';
if(pos == 1){
box.style.left = '1100px';
pos = 2;
}else if(pos ==2){
box.style.left = '100px';
pos = 1;
}
}
</script>
函数节流:一个函数执行一次后,只有大于设定的执行周期后才允许执行第二次。
函数节流非常容易实现,只需要借助setTimeout()延时器。
函数节流的公式:
var lock = true;
funciton 需要节流的函数(){
//如果锁时关闭状态,则不执行
if(!lock) return;
//函数核心语句
//关锁
lock = false;
//指定毫秒数后锁打开
setTimeout(function(){
lock = true;
},2000);
}
示例代码:
<style>
#box{
width: 100px;
height: 100px;
background-color: orange;
position:absolute;
top:100px;
left:100px;
}
</style>
</head>
<body>
<button>按我运动</button>
<div id="box"></div>
<script>
var btn = document.getElementById('btn');
var box = document.getElementById('box');
//标识量,指示当前盒子在左边还是右边
var pos = 1; //左边:1 右边:2
//函数节流锁
var lock = true;
btn.onclick = function(){
//首先检查锁是否关闭
if(!lock) return;
box.style.transition = 'all 2s linear 0s';
if(pos == 1){
box.style.left = '1100px';
pos = 2;
}else if(pos ==2){
box.style.left = '100px';
pos = 1;
}
//关锁
lock = false;
setTimeout(function(){
lock = true;
},2000);
}
</script>
7.常见的动画特效
7.1动画效果开发1
无缝连续滚动特效:
示例代码:
<style>
*{
margin:0;
padding:0;
}
.box{
width: 1000px;
height: 130px;
border: 1px solid #000;
margin:50px auto;
overflow:hidden;
}
.box ul{
list-style:none;
width: 5000px;
position:relative;
}
.box ul li{
float:left;
margin-right:10px;
}
</style>
</head>
<body>
<div id="id" class="box">
<ul id="list">
<li><img src="./images/0.png" alt=""></li>
<li><img src="./images/1.png" alt=""></li>
<li><img src="./images/2.png" alt=""></li>
<li><img src="./images/3.png" alt=""></li>
<li><img src="./images/4.png" alt=""></li>
<li><img src="./images/5.png" alt=""></li>
</ul>
</div>
<script>
var box = document.getElementById('box');
var list = document.getElementById('list');
//(克隆)复制多以便所有的li
list.innerHTML += list.innerHTML;
//全局变量,表示当前list的left值
var left = 0;
var timer;
move();
//动画封装成函数,方便重复调用
function move(){
clearInterval(timer);
timer = setInterval(function(){
left -= 4;
if(left <= -1260){
left = 0;
}
list.style.left = left + 'px';
},20);
}
//鼠标进入事件
list.onmouseenter = function () {
clearInterval(timer);
};
//鼠标离开继续定时器
list.onmouseleave = function(){
move();
};
</script>
7.2动画效果开发2
点击轮播:
示例代码:
*{
margin:0;
padding:0;
}
.carousel{
width: 650px;
height: 360px;
border: 1px solid #000;
margin:50px auto;
position:relative;
overflow:hidden;
}
.carousel ul{
list-style:none;
width: 6000px;
position:relative;
left:0px;
transition:left .5s ease 0s;
}
.carousel ul li{
float:left;
}
.carousel .leftbtn{
position:absolute;
left:20px;
top:50%;
margin-top:-25px;
width: 50px;
height: 50px;
border-radius:50%;
background-color: orange;
}
.carousel .rightbtn{
position:absolute;
right:20px;
top:50%;
margin-top:-25px;
width: 50px;
height: 50px;
border-radius:50%;
background-color: orange;
}
.carousel .btn:hover{
background-color: pink;
}
.carousel a {
text-decoration:none;
}
.carousel a span{
position:absolute;
left:6px;
top:-10px;
font-size:50px;
width: 50px;
height: 50px;
color:blue;
}
</style>
</head>
<body>
<div class="carousel">
<ul id="list">
<li><img src="./images/0.jpg" alt=""></li>
<li><img src="./images/1.jpg" alt=""></li>
<li><img src="./images/2.jpg" alt=""></li>
<li><img src="./images/3.jpg" alt=""></li>
<li><img src="./images/4.jpg" alt=""></li>
<li><img src="./images/5.jpg" alt=""></li>
</ul>
<a href="javascript:;" class="leftbtn btn" id="leftbtn"><span ><</span></a>
<a href="javascript:;" class="rightbtn btn" id="rightbtn"><span>></span></a>
</div>
<script>
var leftbtn = document.getElementById('leftbtn');
var rightbtn = document.getElementById('rightbtn');
var list = document.getElementById('list');
//克隆第一张图片
var cloneli = list.firstElementChild.cloneNode(true);
list.appendChild(cloneli);
//当前ul显示到第几张了,从0开始
var idx = 0;
//节流锁打开
var lock = true;
//右按钮监听
rightbtn.onclick = function(){
//判断锁的状态,如果锁是关闭的就什么也不做
if(!lock) return;
lock = false;
//在取消过渡之后继续使用过渡效果
list.style.transition = 'left .5s ease 0s';
idx ++;
if(idx > 4){
//设置一个延时器,将ul瞬间拉回0的位置
setTimeout(function(){
//瞬间移动就要取消原来的过渡效果
list.style.transition = 'none';
list.style.left = 0;
idx = 0;
},500);
}
list.style.left = -idx * 650 + 'px';
//把锁打开:函数节流
setTimeout(function(){
lock = true;
},500);
}
//左按钮监听
leftbtn.onclick = function(){
if(!lock) return;
lock = false;
//判断是不是第0张,如果是,就要瞬间用假的替换真的
if(idx == 0){
list.style.transition = 'none';
//直接瞬间移动到最后的假图片上
list.style.left = -5 * 650 + 'px';
setTimeout(function(){
//加过渡
list.style.transition = 'left .5s ease 0s';
//idx改为真正的最后一张图片的编号
idx = 4;
list.style.left = -idx * 650 + 'px';
},0);
}else{
idx --;
list.style.left = -idx * 650 + 'px';
}
list.style.left = -idx * 650 + 'px';
//把锁打开:函数节流
setTimeout(function(){
lock = true;
},500);
}
</script>
7.3动画效果开发3
淡入淡出点击轮播:
示例代码:
<style>
*{
margin:0;
padding:0;
}
.carousel{
width: 650px;
height: 360px;
border: 1px solid #000;
margin:50px auto;
position:relative;
}
.carousel ul{
list-style:none;
}
.carousel ul li{
position:absolute;
top:0;
left:0;
/*透明度都是0*/
opacity:0;
transition:opacity .5s ease 0s;
}
/*第一张图片不透明*/
.carousel ul li:first-child{
opacity:1;
}
.carousel .leftbtn{
position:absolute;
left:20px;
top:50%;
margin-top:-25px;
width: 50px;
height: 50px;
border-radius:50%;
background-color: orange;
}
.carousel .rightbtn{
position:absolute;
right:20px;
top:50%;
margin-top:-25px;
width: 50px;
height: 50px;
border-radius:50%;
background-color: orange;
}
.carousel .btn:hover{
background-color: pink;
}
.carousel a {
text-decoration:none;
}
.carousel a span{
position:absolute;
left:6px;
top:-10px;
font-size:50px;
width: 50px;
height: 50px;
color:blue;
}
</style>
</head>
<body>
<div class="carousel">
<ul id="list">
<li><img src="./images/0.jpg" alt=""></li>
<li><img src="./images/1.jpg" alt=""></li>
<li><img src="./images/2.jpg" alt=""></li>
<li><img src="./images/3.jpg" alt=""></li>
<li><img src="./images/4.jpg" alt=""></li>
<li><img src="./images/5.jpg" alt=""></li>
</ul>
<a href="javascript:;" class="leftbtn btn" id="leftbtn"><span ><</span></a>
<a href="javascript:;" class="rightbtn btn" id="rightbtn"><span>></span></a>
</div>
<script>
var leftbtn = document.getElementById('leftbtn');
var rightbtn = document.getElementById('rightbtn');
var list = document.getElementById('list');
var lis = list.getElementsByTagName('li');
//当前的hi第几张图片显示
var idx = 0;
//节流
var lock = true;
//右按钮
rightbtn.onclick = function(){
if(!lock) return;
lock = false;
//还没有改idx,此时的idx图片就是老图,老图淡出
lis[idx].style.opacity = 0;
idx ++;
if(idx > 4){
idx = 0;
}
//改了idx,此时的idx这个图片就是新图,新图淡入
lis[idx].style.opacity = 1;
//动画结束把锁打开
setTimeout(function(){
lock = true;
},500);
}
//左按钮
leftbtn.onclick = function(){
if(!lock) return;
lock = false;
//还没有改idx,此时的idx图片就是老图,老图淡出
lis[idx].style.opacity = 0;
idx --;
if(idx < 0){
idx = 4;
}
//改了idx,此时的idx这个图片就是新图,新图淡入
lis[idx].style.opacity = 1;
//动画结束把锁打开
setTimeout(function(){
lock = true;
},500);
}
</script>
------------------------------------------------------------------------------------
BOM基础(比DOM简单)
BOM(Browser Object Model,浏览器对象模型)是js与浏览器窗口交互的接口。
1.BOM常用对象
1.1Window对象
全局变量是window的属性:
eg:var a = 10;
console.log(window.a == a);//true
这就意味着,多个js文件是共享全局作用域的,即js文件没有作用域隔离功能。
窗口尺寸相关属性
属性 意义
innerHeight 浏览器窗口的内容区域的高度
innerWidth 浏览器窗口的内容区域的宽度
outerHeight 浏览器窗口的外部高度
outerWidth 浏览器窗口的外部宽度
获得不包含滚动条的的窗口宽度,要用
document.documentElement.clientWidth
resize事件:
在窗口大小改变之后,就会触发resize事件,可以使用window.onresize或者
window.addEventListener('resize')来绑定事件处理函数。
已卷动高度:window.scrollY属性表示在垂直方向已滚动的像素值。
document.documentElement.scrollTop属性表示窗口卷动高度:
var scrollTop = window.scrollY || document.documentElement.scrollTop;
document.documentElement.scrollTop不是只读的,而window.scrollY是只读的。
scroll事件:
在窗口被卷动之后,就会触发scroll事件,可以使用window.onscroll或者window.addEventListener('scroll'),来绑定事件处理函数。
1.2Navigator对象
window.navigator属性可以检索navigator对象,它内部含有用户此次活动的浏览器的相关属性和标识。
属性 意义
appName 浏览器官方名称
appVersion 浏览器版本
userAgent 浏览器的用户代理(含有内核信息和封装壳信息)
platform 用户操作系统
示例代码:
console.log('浏览器品牌',navigator.appName);
console.log('浏览器版本',navigator.appVersion);
console.log('用户代理',nvaigator.userAgent);
console.log('操作系统',navigator.platform);
1.3History对象
window.history对象提供了操作浏览器会话历史的接口。
常用操作就是模拟浏览器回退按钮:
history.back();//等同于点击浏览器的回退按钮
history.go(-1);//等同于history.back();
1.4Location对象
window.location标识当前所有的网址,可以通过个这个属性赋值命令浏览器进行页面跳转。
window.location = 'http://www.imooc.com';
window.location.href = 'http://www.imooc.com';
重新加载当前页面
可以调用location的reload方法以重新加载当前页面,参数true表示强制从服务器强制加载
window.location.reload(true);
get请求查询参数
window.location.search属性即为当前浏览器的get请求查询参数
比如https://www.imooc.com/?a=1&b=2
console.lgo(window.location.search);//'?a=1&b=2'
2.BOM特效开发
返回顶部原理:改变document.documentElement.scrollTop属性,
通过定时器逐步改变此值,则将用动画形式返回顶部。
示例代码:
<style>
body{
height:5000px;
background-image: linear-gradient(to bottom,blue,green,yellow);
}
.backtotop{
width: 60px;
height: 60px;
background-color: rgba(255,255,255,.6);
position:fixed;
bottom:100px;
right:100px;
cursor:pointer;
}
</style>
</head>
<body>
<div class="backtotop" id="backtotopBtn">返回顶部</div>
<script>
var backtotopBtn = document.getElementById('backtotopBtn');
var timer;
backtotopBtn.onclick = function(){
clearInterval(timer);
timer = setInterval(function(){
//不断让scrollTop减少
document.documentElement.scrollTop -= 10;
if(document.documentElement.scrollTop <= 0){
clearInterval(timer);
}
},20);
}
</script>
楼层导航小效果
DOM元素都有offsetTop属性,表示此元素大定位祖先元素的垂直距离。
定位祖先元素:在祖先中,离自己最近的且拥有定位属性的元素。
--------------------------------------------------------------------
面向对象
1.认识对象
1.1认识对象js中用{}表示对象。
对象是“键值对”的集合,表示属性和值的映射关系。
var xiaoming = {
属性名(键名,key)--- name:'小明',--属性值(value) k:v对(键值对)
age:12,
sex:'男',
hobbies:['数学','编程']
};
补充:在键名命名时如果不符合js命名规范需要用引号,在访问属性时必须用方括号来访问。
eg:var xiaoming = {
'favorite-book':'拾光忆滴'
};
访问:xiaoming['favorite-book'];
属性的访问:可以用“点语法”访问对象中指定的键的值。
eg:xiaoming.name;//'小明'
xiaoming.age;//12
特别的,如果属性名以变量形式存储,则必须使用方括号形式:
var obj = {
a:1;
b:2;
c:3
}
var key = 'b';
console.log(obj.key);//undefined
console.log(obj[key]);//2
属性值的更改:
直接使用赋值运算符重新对某属性赋值即可更改属性:
var obj = {
a:10
};
obj.a = 30;
aoj.a++;
属性的创建:
如果对象本身没有某个属性值,则用点语法赋值时,这个属性会被创建出来:
var obj = {
a:10
};
obj.b = 40;
属性的删除:
如果要删除某个对象的属性,需要使用delete操作符:
var obj = {
a:1,
b:2
}
delete obj.a;//a这个属性就会被删除
1.2对象的方法
如果某个属性值时函数,则它也被称为对象的“方法”。
eg:
var xiaoming = {
name:'小明',
age:12,
sex:'男',
hobbys:['足球','游泳','编程'],
'favorite-book':'哈利波特',
sayHello:function(){//这个函数就是小明对象的方法
console.log('你好我是小明,今年12岁');
}
}
方法的调用:使用“点语法”可以调用对象的方法。
方法也是函数,只不过方法是对象“函数属性”,它需要用对象打点调用。
xiaoming.sayHello();
1.3对象的遍历
和遍历数组类似,对象也可以被遍历,遍历对象需要使用for...in...循环;
使用for...in...循环可以遍历对象的每个键。
for(var k in obj){
console.log('属性' + k + '的值是' + obj[k]);
}
实例代码:
<script>
var obj = {
a:11,
b:22,
c:88
};
for(var k in obj){
console.log('对象obj的属性' + k + '的值是' + obj[k]);
}
</script>
1.4对象的深浅克隆
对象是引用类型值,这意味着:
不能用var obj2 = obj1这样的语法克隆一个对象。
使用==或者===进行对象的比较时,比较的是它们内存中的同一个对象,而不是比较值是否相同。
对象的浅克隆:
浅克隆:只克隆对象的“表层”,如果对象的某些属性值又是引用类型值,则不进一步克隆它们,只是传递它们的引用。
使用for...in...循环即可实现对象的浅克隆。
实例代码:
<script>
var obj1 = {
a:1,
b:2,
c:[44,55,66]
};
var obj2 = {};
for(var k in obj1){
/**
每遍历一个k属性,就给obj2也添加一个同名的k属性
值和obj1的k属性值相同
*/
obj2[k] = obj1[k];
}
obj1.a++;
console.log(obj2);//虽然obj1变化,obj2是没有变化的
//但是没有实现深克隆,因为
obj1.c.push(77);
console.log(obj2);//此时obj2的数组也会跟着变化
</script>
对象的深克隆:
深克隆:克隆的对象的全貌,不论对象的属性值是否又是引用类型值,都能将它们实现克隆。
和数组的深克隆类似,对象的深克隆需要使用递归(函数内部自己调用自己)。
面试时经常会考察深克隆算法,必须掌握。
实例代码:
<script>
var obj1 = {
a:1,
b:2,
c:[33,44,{
m:55,
n:66,
p:[77,88]
}]
};
function deepClone(o){
//要判断o是对象还是数组
if(Array.isArray(o) == true){
//数组
var result = [];
for(var i = 0;i < o.length; i++){
result.push(deepClone(o[i]));
}
}else if(typeof o == 'object'){
//对象
var result = {};
for(var k in o){
result[k] = deepClone(o[k]);
}
}else{
//基本类型值
var result = o;
}
return result;
}
var obj2 = deepClone(obj1);
console.log(obj2);
console.log(obj1.c == obj2.c);//false:引用类型值是不同内存的对象
</script>
2.认识函数的上下文
函数中的上下文使用this关键字,它表示函数的上下文;
与中文中“这”类似,函数中的this具体指代什么必须通过调用函数时的“前言后语”来判断。
eg:
var xioamaing = {
nickname:'小明',
age:12,
sayHello:funciton(){
console.log('我是'+this.nickname + ',我' + this.age + '岁了');
}
};
特别的:
var xioamaing = {
nickname:'小明',
age:12,
sayHello:funciton(){
console.log('我是'+this.nickname + ',我' + this.age + '岁了');
}
};
var sayHello = xioaming.sayHello;
sayHello();
xiaoming.sayHello();//我是undefined,我undefined岁了。
由例子可以看出函数的上下文是由调用方式决定的:
同一个函数,用不同的形式调用它,则函数的上下文不同。
情形1:对象打点调用函数,函数中的this指代这个打点的对象。
xiaoming.sayHello();//this指向对象本身的属性
情形2:圆括号直接调用函数,函数中的this指代window对象。
var sayHello = xiaoming.sayHello;
sayHello();
注:函数只有被调用时上下文才能被确定。
2.1上下文规则1
规则1:对象打点调用它的方法函数,则函数的上下文是这个打点的对象。
对象.方法();
第一小题:
function fn(){
console.log(this.a + this.b);//99
}
var obj = {
a:66,
b:33,
fn:fn
};
obj.fn();
第二小题:
var obj1 = {
a:1,
b:2,
fn:function(){
console.log(this.a + this.b);
}
};
var obj2 = {
a:3,
b:4,
fn:obj1.fn
};
obj2.fn();//7
第三小题:
function outer(){
var a = 11;
var b = 22;
return{
a:33,
b:44,
fn:function(){
console.log(this.a + this.b);//77
}
};
}
outer().fn();
第四小题:
function fun(){
console.log(this.a + this.b);
}
var obj = {
a:1,
b:2,
c:[{
a:3,
b:4,
c:fun
}]
};
var a = 5;
obj.c[0].c();//7
2.2山下文规则2
规则2:圆括号直接调用函数,则函数的上下文是window对象。
函数();
var obj1 = {
a:1,
b:2,
fn:funciton(){
console.log(this.a + this.b);
}
};
var a = 3;
var b = 4;
var fn = obj1.fn;
fn();//window对象就是全局变量 7
第二小题:
function fun(){
return this.a + this.b;
}
var a = 1;
var b = 2;
var obj = {
a:3,
b:fun(),//规则2:3
fun:fun
};
var result = obj.fun();//规则1
console.log(result);//6
2.3上下文规则3
规则3:数组(类数组对象)枚举出函数进行调用,上下文是这个数组(类数组对象)。
数组[下标]();
第一小题:
var arr = ['A','B','C',function(){
console.log(this[0]);
}];
arr[3]();//规则3:A
类数组对象:所有键名为自然数序列(从0开始),且有length属性的对象。
arguments对象是最常见的类数组对象,它是函数的实参列表。
function fun(){
arguments[3]();规则3:B
}
fun('A','B','C',function(){
console.log(this[1]);//this指向arguments
});
2.4上下文规则4
规则4:IIFE中的函数(立即可执行函数),上下文是window(全局)对象。
(funciton(){
})();
举例:
var a = 1;
var obj = {
a:2,
fun:(funciton(){
var a = this.a;
return funciton(){
console.log(a + this.a);
}
})();//适用规则4
};
obj.fun();//使用规则1 3
2.5上下文规则5
规则5:定时器、延时器调用函数,上下文是window对象。
setIntervale(函数,时间);
setTimeout(函数,时间);
举例:
var obj = {
a:1,
b:2,
fun:function(){
console.log(this.a + this.b);
}
}
var a = 3;
var b = 4;
setTimeout(obj.fun,2000);//是全局变量 7
---------------------------------------
var obj = {
a:1,
b:2,
fun:funciton(){
console.log(this.a + this.b);
}
}
var a = 3;
var b = 4;
setTimeout(function(){
obj.fun();
},2000);//3
2.6上下文规则6
规则6:事件处理函数的上下文是绑定事件DOM元素。
eg:DOM元素.onclick = function(){
};
小案例1:请实现效果:点击哪个盒子,哪个盒子就变红,要求使用同一个事件处理函数实现。
实例代码:
<style>
div{
width: 200px;
height: 200px;
float:left;
border: 1px solid #000;
margin-right:10px;
}
</style>
</head>
<body>
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
<script>
function setColorToRed(){
this.style.backgroundColor = 'red';
}
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var box3 = doucment.getElementById('box3');
box1.onclick = setColorToRed;
box2.onclick = setColorToRed;
box2.onclick = setColorToRed;
</script>
小案例2:请实现效果:点击哪个盒子,哪个盒子在2000毫秒后就变红,要求使用同一个事件处理函数实现。
示例代码:
<style>
div{
width: 200px;
height: 200px;
float:left;
border: 1px solid #000;
margin-right:10px;
}
</style>
</head>
<body>
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
<script>
function setColorToRed(){
//备份上下文
var self = this;
setTimeout(function(){
self.style.backgroundColor = 'red';
})
}
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var box3 = doucment.getElementById('box3');
box1.onclick = setColorToRed;
box2.onclick = setColorToRed;
box2.onclick = setColorToRed;
</script>
2.7call和apply
call和apply能指定函数的山下文
函数.call(上下文);
函数.apply(上下文)
function sum(){
alert(this.chinese + this.math + this.english);
}
var xiaoming = {
chinese:80,
math:95,
english:93
};
//sum.call(xiaoming);
sum.apply(xiaoming);
call和apply的区别(当函数有参数时才被体现出来)
function sum(b1,b2){
alert(this.c + this.m + this.e +b1 + b2);
}
sum.call(xiaoming,5,3);//call要用逗号罗列参数
sum.apply(xiaoming,[5,3]);//apply要把参数写道数组中
到底使用call还是apply?
funciton fun1(){
fun2.apply(this,arguments);
}
function fun2(a,b){
alert(a + b);//77
}
fun1(33,44);
上下文规则作结
规则1 上下文(this)
对象.函数() 对象
函数() window
数组[下标]() 数组
IIFE widnow
定时器 window
DOM事件处理函数 绑定DOM的元素
call和apply 任意指定
3.构造函数
3.1用new调用函数的四步走
3.2构造函数
什么是构造函数
我们将之前的书写的函数进行一下小改进:
function People(name,age,sex){
this.name = name;
this.age = age;//this指向小明,小红,小刚
this.sex = sex;
}
var xiaoming = new People('小明',12,'男');
var xiaoming = new People('小红',10,'女');
var xiaoming = new People('小刚',13,'男');
用new调用一个函数,这个函数就被称为“构造函数”,任何函数都可以是构造函数,只需要用new调用它。
顾名思义,构造函数用来“构造函数对象”,它内部的语句将为新对象添加若干属性和方法,完成对象的初始化。
构造函数数必须用new关键字调用,否则不能正常工作,正因如此,开发者约定构造函数命名时首字母要大写。
构造函数总的this不是函数本身,而是新创建函数的属性和方法。
尝试为对象添加方法
function People(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
this.sayHello = function(){
console.log('我是' + this.name + ',我' + this.age + '岁了');
};
}
var xiaoming = new People('小明',12,'男');
var xiaoming = new People('小红',10,'女');
var xiaoming = new People('小刚',13,'男');
xioaming.sayHello();
xiaohong.sayHello();
xiaogang.sayHello();
3.3类和实例
类:泛指一类东西;
实例:具体的对象。
4.原型和原型连链条
4.1prototype和原型链查找
任何函数都有prototype属性,prototype是“原型”的意思。
prototype属性值是个对象,它默认拥有constructor属性指回函数。
什么时protottype:
普通函数来说的prototype属性没有任何用处,而构造函数的prototype属性非常有用。
构造函数的prototype属性是它的实例原型。
理解:People(构造函数)会自带一个People.prototype属性,而new xiaoming是实例,
此时People.prototype就是xiaoming实例的原型。
eg:function People(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
//实例化
var xiaoming = new People('小明',12,'男');
//测试三者关系是否存在
console.log(xioaming._proto_ === People.prototype);//true
作用:原型链查找:
JS规定:实例可以打点访问它的原型的属性和方法,这被称为“原型链查找”。
function People(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
//添加国籍属性(原型查找的作用)
People.prototype.nationality = '中国';
var xiaoming = new People('小明',12,'男');
console.log(xiaoming.nationality);
hasOwnProperty()方法可以检测对象是否真正“自己拥有”某个属性和方法。
xiaoming.hasOwnProperty('name');//true
xiaoming.hasOwnProperty('nationality');//false
in运算符只能检查某个属性或方法是否可以被对象访问,不能检查是否是自己的属性或方法。
'name' in xiaoming;//true
'nationality' in xiaoming;//true
4.2在prototype上添加方法(保证是同一个函数,节约内存)
function People(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
//方法写到原型上
People.prototype.sayHello = funciton(){
console.log('我是' + this.name);
};
People.prototype.sleep = function(){
console.log(this.name + '开始睡觉,zzzz');
};
var xiaoming = new People('小明',12,'男');
var xiaohong = new People('小红',11,'女');
console.log(xiaoming.sayHello == xioahong.sayHello);//true
xiaoming.sayHello();
xiaohong.sayHello();
4.3原型链的终点
Object.prototype又自带了hasOwnProperty()和toString()方法。
function People(){
}
var xiaoming = new People();
console.log(xiaoming._proto_.proto === Object.prototype);//true
4.4继承
实现继承的关键在于:子类必须拥有父类的全部属性和方法,同时子类还应该定义自己的特有的属性和方法。
使用JavaScript特有的原型链性来实现继承,是普遍的做法。
实例代码:
//父类,人类
function People(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.sayHello = function(){
console.log('你好,我是' + this.name);
};
People.prototype.sleep = function(){
console.log(this.name + '开始睡觉,zzzz');
};
//子类,学生类
function Student(name,age,sex,scholl,studentNumber){
this.name = name;
this.age = age;
this.sex = sex;
this.scholl = scholl;
this.studentNumber = studentNumber;
}
//关键语句,实现继承
Student.prototype = new People();
Student.prototype.study = function(){
console.log(this.name + '正在学习');
};
Student.prototype.exam = function(){
console.log(this.name + '正在考试,加油');
}
//重写父类的sayHello
Student.prototype.sayHello = function(){
console.log('敬礼,我是' + this.name + '我今年' + this.age + '岁了');
}
//实例化
var hanmeimei = new Student('韩梅梅',9,'女','慕课小学',10666);
hanmeimei.study();
hanmeimei.sayHello();
hanmeimei.sleep();
5.上升到面向对象(什么效果就写什么效果的类)
5.1上升到面向对象小案例1
面向对象的本质:定义不同的类,让类的实例工作。
面向对象常用的场合:需要封装和复用性的场合。
红绿灯小案例:页面上做一个红绿灯,点击红灯就变黄,点击黄灯几变绿,点击绿灯就变回红灯。
如果页面上有100个这样的红绿灯呢?
实例代码:
<style>
#box{
width: 80px;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
//定义一个红绿灯类
function TrafficLight(){
//color:red:1,yellow:2,green:3
this.color = 1;
//调用自己的初始化方法
this.init();
//绑定监听
this.bindEvent();
}
//初始化方法
TrafficLight.prototype.init = function(){
//创建自己的DOM
this.dom = document.createElement('img');
//设置src属性
this.dom.src = './images/' + this.color + '.jpg';
//把创建的节点上树
box.appendChild(this.dom);
};
//绑定监听
TrafficLight.prototype.bindEvent = function(){
//备份上下文
var self = this;
//当自己的dom被点击的时候
this.dom.onclick = function(){
self.changeColor();
};
}
//改变颜色的方法
TrafficLight.prototype.changeColor = function(){
this.color ++;
if(this.color == 4){
this.color = 1;
}
//关color属性变化没有用,还要更改自己的dom的src属性
this.dom.src = './images/' + this.color + '.jpg';
}
//获取盒子
var box = document.getElementById('box');
// var t1 = new TrafficLight();
// console.log(t1);
//实例化100个
var count = 100;
while(count--){
new TrafficLight('<br>');
}
</script>
5.2上升到面向对象小案例2
炫彩小球:
Ball类的属性
属性名 意义
x 圆心坐标x
y 圆心坐标y
opacity 透明度
color 颜色
dom DOM元素
Ball类的方法:
属性名 意义
init 初始化
update 更新
如何实现多个小球动画
把每个小球实例都放在同一个数组中
[{小球实例}{小球实例}{小球实例}{小球实例}]
只需要使用1个定时器,在每一帧遍历每个小球,调用它们的updata方法。
实例代码:
<style>
body{
background-color: black;
}
.ball{
position:absolute;
border-radius:50%;
}
</style>
</head>
<body>
<script>
//小球类
function Ball(x,y){
//属性x,y表示的时圆心的坐标
this.x = x;
this.y = y;
//半径
this.r = 20;
//透明度
this.opacity = 1;
//小球背景,从颜色数组中随机选择一个颜色
this.color = colorArr[parseInt(Math.random() * colorArr.length)];
//这个小球的x增量和y的增量
do{
this.dX = parseInt(Math.random() * 20) - 10;
this.dY = parseInt(Math.random() * 20) - 10;
}while(this.dX == 0 && this.dY == 0);
//调用初始化方法
this.init();
//推入数组,this:实例
ballArr.push(this);
}
Ball.prototype.init = function(){
//创建自己的dom
this.dom = document.createElement('div');
this.dom.className = 'ball';
this.dom.style.width = this.r * 2 + 'px';
this.dom.style.height = this.r * 2 + 'px';
this.dom.style.left = this.x - this.r + 'px';
this.dom.style.top = this.y - this.r + 'px';
this.dom.style.backgroundColor = this.color;
//上树
document.body.appendChild(this.dom);
}
//更新
Ball.prototype.update = function(){
this.x += this.dX;
this.y -= this.dY;
this.r += 0.2;
this.opacity -= 0.01;
this.dom.style.width = this.r * 2 + 'px';
this.dom.style.height = this.r * 2 + 'px';
this.dom.style.left = this.x - this.r + 'px';
this.dom.style.top = this.y - this.r + 'px';
this.dom.style.opacity = this.opacity;
//当透明小于0的时候,就需要从数组中删除自己,DOM也要删除自己
if(this.opacity < 0){
for(var i = 0;i < ballArr.length; i++){
if(ballArr[i] == this){
ballArr.splice(i,1);
}
}
document.body.removeChild(this.dom);
}
};
//把所有的小球实例都放到一个数组中
var ballArr = [];
//初始颜色数组
var colorArr = ['#66CCCC','#CCFF66','#FF99CC','#FF6666','#CC3399','#FF6600'];
new Ball(200,600);
//定时器,负责更新所有的小球实例
setInterval(function(){
//遍历数组,调用自己的update方法
for(var i = 0;i < ballArr.length; i++){
ballArr[i].update();
}
},20);
//鼠标指针的监听
document.onmousemove = function(e){
//获取鼠标的位置
var x = e.clientX;
var y = e.clientY;
new Ball(x,y);
};
</script>
6.JS的内置对象
6.1包装类
Number()、String()和Boolean()分别是数字、字符串、布尔值的“包装类”。
很多编程语言都有“包装类”的设计,包装的目的就是为了让基本类型值可以从它们
的构造函数的prototype上获取方法。
eg:
var a = new Number(123);
var b = new String('慕课网');
var c = new Boolean(true);
a,b,c是基本类型值吗?它们和普通的数字,字符串,布尔值有什么区别吗?
console.log(a);
console.log(typeof a);//都是object,包装成一个对象了。
虽然是对象但可以参与计算
console.log(5 + a);//128
console.log(b.slice(0,2));//慕课
console.log(c && true);//true
var d = 123;
console.log(d.__proto__ == Number.prototype);//true
包装类知识总结
Number()、String()和Boolean()的实例都是object类型,它们的PrimitiveValue属性存它们的本身值。
6.2Math对象
幂和开方:Math.pow(),Math.sqrt()
向上取整和向下取整:Math.ceil(),Math.floor()
四舍五入:Math.round()可以将一个数字四舍五入为整数:
console.log(Math.round(3.4));//3
console.log(Math.round(3.5));//4
参数列表的最大值:Math.max()
参数泪飙的最小值:Math.min()
console.log(Math.max(6,2,9,4));//9
console.log(Math.min(6,29,4));//2
如何利用Math.max()求数组最大值?
Math.max()要求参数必须是“罗列出来”,而不能是数组。
还记得apply方法吗?它可以指定函数的上下文,并且以数组的形式传入“零散值”当做函数的参数。
var arr = [3,6,9,2];
var max = Math.max.apply(null,arr);
console.log(max);//9
随机数:Math.random()可以得到[0,1)的小数
为了得到[a,b]区间内的整数,可以使用这个公式:
parseInt(Math.random() * (b - a + 1)) + a
6.3Data对象
使用new Date()即克得到当前的日期对象,它是object类型值。
使用new Date(2022,11,1)即克得到指定的日期对象,注意第二个参数表示月份,从0开始算,11表示12月。
也可以是new Date('2022-12-01')这样的写法。
日期对象的常见方法:
方法 功能
getDate() 得到日期0~31
getDate() 得到星期0~6
getMonth() 得到0~11
getFullYear() 得到年份
getHours() 得到小时0~23
getMinutes() 得到分钟0~59
getSeconds() 得到描述0~59
eg:
var d = new Date();
console.log('日期',d.getDate());
console.log('星期',d.getDay());
console.log('年份',d.getFullYear());
console.log('月份',d.getMonth()+1);
console.log('小时',d.getHours());
console.log('分钟',d.getMinutes());
console.log('秒数',d.getSeconds());
时间戳:表示1970年1月1日零点整距离此时刻的毫秒数;
通过getTime()方法或者Date.parse()函数可以将日期对象变为时间戳。
通过new Date(实践戳)的写法。可以将时间戳变为日期对象。
实例代码:
//日期对象
var d = new Date();
//显示时间戳
var timestamp1 = d.getTime();
var timestamp2 = Date.parse(d);
//把时间戳转为日期对象
var dd = new Date(时间戳);
console.log(dd);
console.log(dd.getFullYear());
倒计时案例:
示例代码:
<h1>2022高考倒计时</h1>
<h2 id="info"></h2>
<script>
var info = document.getElementById('info');
setInterval(function(){
//现在的日期
var nd = new Date();
//目标的日期,5表示6月
var td = new Date(2022,5,7);
//毫秒差
var diff = td - nd;
//任务:就是把diff换算成天,小时,分钟,秒
//换算为多少天,除以一天的总毫秒数,不就是换算成多少天么;1000 * 60 * 60 *24一天的毫秒数
var day = parseInt(diff / (1000 * 60 * 60 *24));
//零多少毫秒?差的总毫秒数于1天的毫秒数的相除的余数,就是零头的毫秒数
var hours = parseInt(diff % (1000*60*60*24)/(1000*60*60));
//分钟
var minutes = parseInt(diff % (1000*60*60*24)%(1000*60*60)/(1000*60));
//秒数
var seconds = parseInt(diff % (1000*60*60*24)%(1000*60*60)%(1000*60)/1000);
info.innerText = day+'天'+hours+'时'+minutes+'分'+seconds+'秒';
},1000);
拓展:
继承:子类让父类更具体,更细化。
通过原型链实现继承:只要People--->就会具有People.prototype属性。
实例代码:
//父类:People类
function People(name,age,sex){
this.name = name;
this.age = name;
this.sex = name;
}
//prototype属性添加方法
People.prototype.sayHello = function(){
console.log('你好,我是' + this.name + '今年' + this.age + '岁了');
}
People.prototype.sleep = function(){
console.log(this.name + '正在睡觉');
}
//子类:Student
function Student(name,age,sex,school,sid){
this.name = name;
this.age = age;
this.sex = sex;
this.school = school;
this.sid = sid;
}
//实现继承非常重要的语句:子类的prototype指父类的一个实例
Student.prototype = new People();
//追加方法
Student.prototype.exam = function(){
console.log(this.name + '正常');
};
Student.prototype.study = function(){
console.log(this.name + '正在学习');
};
//子类可以更改父类的方法,术语叫做override“改写”、“重写”
Student.prototype.sayHello() = function(){
console.log('敬礼,你好,我是'+this.name+',我是'+this.school+'的学生');
}
//实例化
var xiaoming = new Student('小明',12,'男','小慕学校',106666);
xiaoming.exam();
xiaoming.study();
xiaoming.sayHello();
xiaoming.sleep();
存在的问题:
1.如果父类的属性中有引用类型的值,则这个属性会被所有子类的实例共享;
2.子类的构造函数中,往往需要重复定义很多个超类定义过的属性。
借用构造函数:为了解决原型中包含引用类型值所带来的问题和子类构造函数不优雅的问题,开发人员通常使用一种叫做“借助构造函数”
的技术,也被称为“伪造对象”或“经典继承”。
借用构造函数的思想非常简单:在子类构造函数的内部调用超类的构造函数,但是要注意使用call()绑定上下文。
实例代码:
function People(name,sex,age){
this.name = name;
this.sex = sex;
this.age = age;
this.arr = [33,44,55];
}
function Student(name,sex,age,school,sid){
People.call(this,name,sex,age);//调用父类函数避免重写,但要绑定this上下文
this.school = school;
this.sid = sid;
}
var xiaoming = new Student('小明','男',12,'小慕学校',106666);
console.log(xiaoming);
xiaoming.arr.push(77);
var xiaohong = new Student('小红','
组合继承:将借用原型链和借用构造函数的技术组合到一起。也叫做伪经典继承。
组合继承是JavaScript中最常用的继承模式。
实例代码:
//父类
function People(name,sex,age){
this.name = name;
this.sex = sex;
this.age = age;
}
//添加方法
People.prototype.sayHello = function(){
console.log('我是'+this.name+'今年'+this.age+'岁了');
}
People.prototype.sleep = function(){
console.log(this.name+'正在睡觉');
}
//子类
function Student(name,sex,age,school,sid){
//借助构造函数
People.call(this,name,sex,age);//调用父类函数避免重写,但要绑定this上下文
this.school = school;
this.sid = sid;
}
//实现继承,借助原型链
Student.prototype = new People();
Student.prototype.exam = function(){
console.log(this.name + '正在考试');
};
Student.prototype.sayHello = function(){
console.log('你好,我是' + this.name + '今年' + this.age + '岁了' + this.school + '学校的学生');
};
var xiaoming = new Student('小明','男',12,'小慕学校',106666);
xiaoming.sayHello();
xiaoming.sleep();
xiaoming.exam();
组合继承的缺点:
无论是什么情况下,都会调用两次超类的构造函数,一次是在创建子类原型的时候,另一次是在子类构造函数的内部。
原型式继承
认识Object.create():IE+开始支持Object.create()方法,可以根据指定的对象为原型。
var obj2 = Object.create(object1);
实例代码:
var obj1 = {
a:33,
b:45,
c:12
};
var obj2 = Object.create(obj1,{
d:{
value:99
}
});
var obj2 = Object.create(obj1);
console.log(obj2.__proto__ === obj1);//true
console.log(obj2.a);
console.log(obj2.b);
console.log(obj2.c);
console.log(obj2.d);
在没有必要“兴师动众”的创建构造函数,而只是想让新对象与现有对象“类似”的情况下,使用Object.create()即可胜任,称为原型式继承。
Object.create()的兼容性写法。
示例代码:
//道格拉斯.克罗克福德
//函数德功能就是以o为原型,创建新对象
function object(o){
//创建一个临时构造函数
function F(){}
//让这个临时构造函数德prototype指向,这样一来它new出来的对象,__proto__指向了o
F.prototype = o;
//返回F的实例
return new F();
}
var obj1 ={
a:23,
b:5
};
var obj2 = object(obj1);
console.log(obj2.__proto__ == obj1);
console.log(obj2.a);
console.log(obj2.b);
寄生式继承
编写一个函数,它接受一个参数o,返回以o为原型的新对象p,同时给p上添加预置的新方法。
实例代码:
//对象1
var o1 = {
name:'小明',
age:2,
sex:'男'
};
//对象2
var o2 = {
name:'小红',
age:3,
sex:'女'
};
function f(o){
//以o为原型创建出新对象
var p = Object.create(o);
//补充方法
p.sayHello = function(){
console.log('你好,我是' + this.name + '今年' + this.sex + '岁了');
}
p.sleep = function(){
console.log(this.name + '正在睡觉');
}
return p;
}
f(o1);
var p1 = f(o1);
p1.sayHello();
var p2 = (fo2);
p2.sayHello();
寄生式继承的缺点:寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,即“方法没写道prototype上”。
寄生式组合继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
实例代码:
//这个函数接收两个参数,suType是子类的构造函数,superType是父类的构造函数
function inheritPrototype(subType,superType){
var prototype = Object.create(superType.prototype);
subType.prototype = prototype;
};
//父类
function People(name,sex,age){
this.name = name;
this.sex = sex;
this.age = age;
}
People.prototype.sayHello = function(){
console.log('你好,我是'+this.name+'今年'+this.age +'岁了');
}
People.prototype.sleep = function(){
console.log(this.name+'正在睡觉');
}
//子类
function Student(name,sex,age,school,sid){
//借助构造函数
People.call(this,name,sex,age);
this.school = school;
this.sid = sid;
}
//调用自己编写的inheriPrototype函数,这个函数可以让Student类的prototype指向“以People.prototype为原型的新对象”。
inheritPrototype(Student,People);
Student.prototype.exam = function(){
console.log(this.name + '正在考试');
};
Student.prototype.sayHello = function(){
console.log('你好,我是'+this.name+'今年'+this.age+'岁了');
};
//实例化
var xiaoming = new Student('小明','男',12,'小慕学校',103333);
xiaoming.sleep();
xiaoming.exam();
xiaoming.sayHello();
instanceof运算符用来检测“某对象是不是某个类的实例”。
比如:小明是不是Student这个对象的实例
xiaoming instanceof Student;
底层原理:检查Student.prototype属性是否在xiaoming的原型链上。
实例代码:
function People(){
}
function People(){
}
//组合式继承
Student.prototype = new People();
var xiaoming = new Student();
//测试instanceof
console.log(xiaoming instanceof Student);//true
console.log(xiaoming instanceof People);//true
内置构造函数
JavaScript中有很多内置函数,不如Array就是数组类型的构造函数,Function就是函数类型的构造函数,Object就是对象类型的构造函数。
内置函数非常有用,所有该类型的方法都是定义在它的内置构造函数的prototype上的,我们可以给这个对象添加新的方法,从而拓展某类型的功能。
实例代码:
//数组的内置构造函数,任何的数组字面量都可以看做是Array的实例
console.log([1,2] instanceof Array);//true(
console.log([] instanceof Array);//true
var arrv = new Array(5);
console.log(arr);
console.log(arr.length);
//函数的内置构造函数
function fun(){
}
console.log(fun instanceof Function);//true
var sub = new Function('a','b','return a - b');
console.log(8,3);//5
//对象的内置函数
console.log({a:1} instanceof Object);//true
console.log({} instanceof Object);//true
var o = new Object();
o.a =3;
o.b = 4;
console.log(o);
实例代码:
console.log(Array.prototype.hasOwnProperty('push'));//true
console.log(Array.prototype.hasOwnProperty('pop'));//true
console.log(Array.prototype.hasOwnProperty('splice'));//true
//拓展求和的方法
Array.prototype.sum = function(){
//this表示调用sum()方法的数组
var arr = this;
var sum = 0;
for(var i = 0;i < arr.length; i++){
sum += arr[i];
}
return sum;
}
var arr = [3,6,2,1,3];
var rusult = arr.sum();
console.log(result);
var arr2 = [3,5];
var result2 = arr2.sum();
console.log(result2);
基本类型值的“包装类”
Number,String,Boolean是三个基本类型值的包装类,用new调用它们可以生成“对象”版本的基本类型值。
实例代码:
console.log(3);
console.log(typeof 3);//Number
var o = new Number(3);
console.log(o);
console.log(typeof o);//此时通过new创建后就包装成了Object
内置构造函数的关系
Object.prototype是万物原型链的终点
实例代码:
console.log([1,2].__proto__ ===Array.prototype);//true
console.log([1,2].__proto__.__proto__ ===Array.prototype);//true
console.log([] instanceof Object);//true
console.log([] isntanceof Array);//ture
任何函数都可以看做是Function"new 出来的"。
实例代码:
console.log(Object.__proto__ === Function.prototype);//ture
console.log(Funciton.__proto__ === Object.prototype);//true
console.log(Function instanceof Object);//true
console.log(Object instanceof Function);//true
console.log(Function instanceof Function);//true
console.log(Object instanceof Object);//true
------------------------------------------------------------------------------------------
正则表达式:
1.什么是正则表达式
描述了字符串的“构成模式”,经常被用于检查字符串是否符合预定的格式要求。
正则表达式的基本使用方法:检查某个字符串是否是6位数字
实例代码:
//要检查的字符串
var str = '456789';
//正则表达式规则
var regexp = /^\d{6}$/;
//检查
if(regexp.test(str)){//test()方法:可以用来检测字符串是否符合正则表达式规定的规则。
alert('符合规则');
}else{
alert('不符合规则');
}
正则表达“按位”描述规则,是指它是一位一位的描述字符串的构成形式。
比如检查字符串是不是这样的:以字母m开头,然后是否3个数字,最后以字母m结尾。
/^m\d\d\dn$/---> ^表示开头,$表示结尾,\d表示数字,\d\d\d也可以写作\d{3}
开头结尾符号:/^$/
以m开头n结尾,中间有6位数字:/^m\d{6}n$/
2.正则表达式的基本应用
定义正则表达式的实例代码:
//定义正则表达式
var regexp = /^\d{6}$/;//6位数字的字符串
var str1 = '123456';
var str2 = '1234567';
var str3 = '123b56';
//检测用test()方法
console.log(regexp.test(str1));//true
console.log(regexp.test(str2));//false
console.log(regexp.test(str3));//false
2.1正则表达式的创建
使用/内容/的语法形式,可以快速创建正则表达式。
也可以使用new RegExp('内容')的语法形式,可以快速创建正则表达式。
使用typeof运算符检查正则表达式的类型,结果事object.
示例代码:
//创建正则表达式方法1
var regexp1 = /^\d{6}$/;
//方法2
var regexp2 = new RegExp('^\\d{6}$');//注:在字符串中\用\\来代替
console.log(regexp.test(str1));//true
console.log(regexp.test(str2));//true
//正则表达式是一个对象是引用类型值
console.log(typeof regexp1);//object
console.log(typeof regexp2);//object
2.2元字符
是指一位指定类型的字符
元字符 功能
\d 匹配一个数字
\D 匹配一个非数字字符
\w 匹配一个单字字符(字母,数字或下划线)
\W 匹配一个非单字字符
\s 匹配一个空白字符,包括空格,制表符和换行符
. 任意字符
^ 匹配开头
$ 匹配结尾
注:如果使用new RegExp()写法,反斜杆需要多写一个
例子1:
某快递公司运单号形式是这样的:123-4567-890,请使用正则表达式检查字符串是否符合此格式。
示例代码:
var regexp1 = /^/d/d/d-/d/d/d-/d/d/d$/;
var str1 = '444-555-666';
console.log(regexp1.test(str1));
例子2:
某产品的验证密钥形式是遮这样的:框框框-框框框-框框框,其中框表示字母数字或者下划线,请使用正则表达式检查某字符串是否符合此格式。
示例代码:
var regexp2 = /^\w\w\w-\w\w\w-\w\w\w$/;
var str = 'abc-__-ab-123';
console.log(regexp2.test(str2));//true
字符的转义
不管一个符号有没有特殊意义,都可以在其之前加一个\以确保它表达的是这个符合本身。
举例:某产品批号形式位:123.45^67#89,请使用正则表达式检查某字符串是否符合此格式。
示例代码:
var regexp3 = /^\d\d\d\.\d\d\^\d\d\#\d\d$/;
//待测试的字符串
var str3 = '666.66^#66';
console.log(regexp3.test(str3));//ture6
2.3方括号的表示法
使用方括号,比如[xyz],可以创建一个字符集合,表示匹配方括号里的任意字符。
例子:比如某学校的学号规定:第1位是字母,b表示本科生,y表示研究生,后面是7位数字,用正则表示为:/^[by]\d{7}$/
示例代码:
//学号字符串
var str = 'y6666666';
//用正则表达式进行检查
console.log(/^[by]\d{7}$/.test(str));//true
方括号高级表示法:可以使用短横-来指定一个字符串范围,^表示否定
元自符 等价的方括号表示法
\d [0-9]
\D [^0-9] 不能是0-9的数字
\w [A-Za-z0-9]
\w [^A-Za-z0-9]
//题目1:请验证某字符串是否是5位字母,大小写均可
//[a-zA-Z]:从a到z和从A到Z
var str = 'abcde';
console.log(/^[a-zA-Z]{5}$/.test(str));//true
//题目2:请验证某字符串是否是5位,且仅有小写字母、点构成。
var str1 = 'mnp..';
console.log(/^[a-z\.]$/.test(str1));//true
//题目3:请验证某字符串是否是4位小写字母,且最后一位不能是m字母。
var str2 = 'abcm';
console.log(/^[a-z]{3}[a-ln-z]$/)//false
2.4量词
量词 意义
* 匹配前一个表达式0次或多次。等价于{0,}
+ 匹配前面一个表示式1次或者多次。等价{1,}
? 匹配前面一个表达式0次或者1次。等价{0,1}
{n} n是一个正整数,匹配了前面一个字符刚好出现了n次
{n,} n是一个正整数,匹配前一个字符串至少出现了n次
{n,m} n和m都是整数。匹配前面的字符至少n次,最多m次
示例代码:
//题目1:请验证字符串是否符合手号码的规则:11位数字,并且肯定以1开头
var str1 = '18687591186';
var str2 = '186875911535';
//定义正则表达式
var regexp1 = /^1\d{10}$/
console.log(regexp1.test(str1));//true
console.lgo(regexp2.test(str2));//false
//题目2:请验证某字符串是否是这样:以字母开头,中间是任意位数字(最少1位)构成,并以字母结尾
var str3 = 'a123456789b';
var str4 = 'abcde';
var regexp2 = /^[a-zA-Z]\d+[a-zA-Z]$/;// /d+:数字至少一位或多位
console.log(regexp2.test(str3));//true
console.log(regexp2.test(str4));//false
//题目3:请验证某字符串是否符合网址规则:以www.开头,中间是任意位的字符(字母数字下划线,最少一位),最后以.com结尾,也可以以.com.cn结尾
var str5 = 'www.imooc.com';
var str6 = 'www.sina.com.cn';
var str7 = 'abcde';
var regexp3 = /^www\.\w+\.com(\.cn)?$/;// ?:.cn可有可以无
console.log(regexp3.test(str5));//true
console.log(regexp3.test(str6));//true
console.log(regexp3.test(str7));//false
2.5修饰符
修饰符也叫做标志(flags),用于使用正则表达式实现高级搜索。
修饰符 意义
i 不区分大小写搜索
g 全局搜索
eg:
var re = /m/gi;
var re = new RegExp('m','gi');
3.正则表达式和字符串
3.1正则表达式的相关方法
正则表达式可以“打点”调用哪些方法呢?
方法 简介
test() 测试某个字符串是否匹配正则表达式,返回布尔值
exec() 根据正则表达式,在字符串中进行查找,返回结果数组或null
exec()方法功能是:在一个指定字符串中执行一个搜索匹配查找,返回一个结果数组或null
eg:var str = 'abc123def456ghi789';
var regexp = /\d+/g;//有g修饰符
//会逐条遍历
var result1 = regexp.exe(str);
var result2 = regexp.exec(str);
var result3 = regexp.exec(str);
var result4 = regexp.exec(str);
console.log(result1);//123
console.log(result2);//456
console.log(result3);//789
console.log(result4);//null
//还可以这样进行遍历
var result;
while(result = regexp.exec(str)){
console.log(result);
}
3.2字符串的相关方法
字符串有哪些方法可以使用正则表达式呢?
方法 简介
search() 在字符串中根据正则表达式进行查找匹配,返回首次匹配到的位置索引,测试不到返回-1
match() 在字符串中根据正则表达式进行查找匹配,返回一个数组,找不到则返回Null
replace() 使用替换字符串替换掉匹配到的子字符串,可以使用正则表达式
split() 分隔字符串为数组,可以使用正则表达式
示例代码:
var str = 'abc123def4567';
var regexp = /\d+/g;
//search()方法
var result1 = str.search(regexp);
console.log(result1);//首次数字位置是3,没有查到返回-1
//match()方法:检索所有的数字,找不到返回null
var result2 = str.match(regexp);
console.log(result2);
//replace()方法:替换:把所有小写字母替换成*
var result3 = str.replace(/[a-z]+/g,'*');
console.log(result4);
//split()方法:把字符串拆分成数组
var result4 = str.split(/\d+/g);
console.log(result4);
4.正则表达式的应用
用正则表达式进行表单验证是正则表达式最重要的实际应用。
实际上,很多正则表达式不需要我们自己写,可以通过搜索引擎查找,可以拿来即用。
示例代码:
<style>
.warning{
color:red;
display:none;
}
</style>
</head>
<body>
<div>
<p>
请输入姓名:
<input type="text" id="nameField">
<span class="warning" id="nameWarning">输入的姓名不合法</span>
</p>
</div>
<script>
var nameField = document.getElementById('nameField');
var nameWarning = document.getElementById('nameWarning');
//当文本框失去焦点事件监听(光标不在文本框中)
nameField.onblur = function(){
//得到姓名
var name = nameField.value;
if(/^[u4E00-\u9FA5]{2,4}$/.test(name)){
nameWarning.style.display = 'none';
}else{
nameWarning.style.display = 'inline';
}
};
</script>