JS基础语法与表达式
JS基础语法与表达式
JS的书写位置
1、在body标签中script标签,在内部书写script代码
2、将代码单独保存为js文件,在html中使用<script src=""></script>
映入js文件
认识JS输出语句
alert语句:弹出警告框
alert是js内置函数,函数就是功能的“封装”,调用函数需要使用圆括号。
console.log语句:控制台输出
console是js的内置对象,通过“.”可以调用它的内置的“log”方法。所谓方法就是对象能够调用的函数。
处理报错
Uncaught SyntaxError:lnvalid or unexpected token表示未捕获的语法错误:不合法或错误的符号
引号忘记包裹 Uncaught ReferenceError:慕课网is not defined
函数拼写错误
REPL环境
控制台是一个REPL环境,可以临时测试表达式的值
R(read)读取
E(eval)执行
P(print)打印
L(loop)循环
变量
变量的定义
var关键字定义变量
变量的命名规范
变量的命名方法
变量的默认值是undefined
变量声明提升
先书写输出语句和变量,但是变量没有声明和赋值,在输出语句后定义变量,执行后是输出underfined,因为在执行所有代码前,js有预解析阶段,也就是所js会预先读取好所有定义的变量。
但为什么不是输出12呢,因为变量声明提升只提升定义不会提升值。
数据类型简介和检测
基本数据类型:Number、String、Null、undefined、Boolean、
复杂数据类型:Object、Array、Map、Set、Date、Function、RegExp、Symbol…
Typeof运算符
typeof是一个操作符不是内置函数,但可以加圆括号,但是不规范typeof(a);
Number(数字)类型
number类型:所有数字不分大小、不分整浮、不分正负,都是数字类型。
小数中0是可以忽略
科学计数法:
较大或较小数值可以写成科学计数法
e表示乘以10的多少次方
String(字符串)类型
字符串拼接:可以使用“+”来进行拼接
var a=‘123’;
var b=a+‘456’;
console.log(b);
字符串长度属性length
字符串常用方法:
CharAt()方法:
SubString()方法:
SubString如果省略了第二个参数,那么他会计算到字符串的结尾。
SubString(5,3)第一个参数如果大于第二个参数,那么数字顺序会自动调整小数在前面。
SubStr()方法:
Slice()方法
Slice和SubString方法相同,但是有一点不同,SubString(5,3)参数一大于参数二,但是Slice参数一不能大于参数二。Slice参数一可以负数,SubString不能
toUpperCase小写转大写
toLowerCase大写转小写
indexOf()方法
返回某个字符串值在字符串中首次出现的位置。
Boolean(布尔)类型
Undefined类型
Null类型
Null表示“空”,它是空对象
需要将对象、数组或者删除监听事件,通常将他们设置为Null
undefined和null的异同点
undefined用typeof检测是undefined,变量没有赋值则为undefined,undefined是默认值也是一种类型。
null用typeof检测是object,null值也是null。如果要清除某种方法或者数组已经监听事件要设置值为null。
数据类型转换
字符串转数字需要使用函数Number(),注意N要大写。
使用parseInt()函数
parseint将字符串转换成整数,如果字符串不是数字开头则转换为NAN。
使用parsefloat()函数
parsefloat将字符串转换为浮点数
String()函数
将数字转换为字符串
toString方法()
Boolean()函数
prompt方法定义弹窗
算术表达式
加号的两种作用,有加号和连字符的作用。
如果两边都是数字操作数则为加法,如果有一边是字符串则结果为字符串。
取余运算也叫求模运算,用%表示,a%b表示求a除以b的余数。
隐式类型转换
如果参与运算的某操作数不是数字,那么JavaScript会自动将此操作数转换为数值类型。
注意事项------IEEE754
使用tofixed()方法解决
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>算数运算</title>
</head>
<body>
<script>
var a,b;
a=4,b=5;
var sum=(a+b);
var j=(a-b);
var c=a*b;
var chu=a/b;
var y=b%a;
document.write(sum,j,c,chu,y)
</script>
</body>
</html>
关系表达式
==运算符不会比较值得类型,它会经过隐式转换去比较值是否相等,而全等===不仅比较值是否相同,而且比较类型是否相同。
undefined和null进行比较为true,而进行全等比较为false,因为数据类型不同。
NaN不自等,NaN不论进行相等比较还是全等比较都为false。
如何判断值是否为NaN
需要使用isNaN()函数,可以用来判断值是否为NaN。
isNaN()返回的是false,表示就是数值,返回的是true,表示就是非数值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var a = isNaN(null);
var b = isNaN(10);
var c = isNaN('我是字符串');
var d = isNaN('10');
var e = isNaN(undefined);
document.write(a + " " + b + ' ' + c + ' ' + d + ' ' + e);
//结果为 false false true false true
</script>
</body>
</html>
逻辑表达式
非运算 !
!可以表示为 “非”,也成为置反运算,它是单目运算符,只需要一个操作数,返回结果为布尔型。
!!则表示先取反布尔值,而后在取反的布尔值在取反。
与运算
或运算
短路计算
两个参数进行与运算&&,如果参数1为真,那么参数二为假,js会直接将参数二当做最后结果,也就是说参数一为真,不管参数二是否真假都取参数二。
那么参数一为假,后面这就不执行。js直接去参数一为结果。
逻辑运算顺序:非——>与——>或
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>逻辑操作符</title>
</head>
<body>
<script>
var x=NaN,y=undefined,z=1,m=1,n='0',j=33,a=!(!undefined),b=!0;
document.write(x||y||z);
document.write(m||n||j);
document.write(a||b);
// 结果为 1 1 true
</script>
</body>
</html>
赋值表达式
综合表达式
综合运算的顺序是非运算!->数学运算(加减乘除)->关系运算(大于等于)->(与或非)
非数关逻
JS流程控制语句与数组
if语句的基本使用
if语句是最简单的条件语句,也叫选择语句。通常结合else一起,表示如果…否则…就。
if(测试条件){
//语句块1
//如果测试条件为真时执行语句块1
}else{
//语句块2
//如果测试条件为假执行语句块2
}
else 可以省略
如果语句块就一个可以省略else后面的部分。
if else if多条件分支
if(){
}else if(){
}else if(){
}else{
}
Switch语句
switch(变量){
case XX:
//语句块1
break;
case XX:
//语句块2
break;
case XX:
//语句块3
break;
defalut:
//语句块4
}
switch圆括号中表示的是变量,变量引入后将会和switch语句中的case(情况)进行全等对比,如果对比相同执行,default表示默认情况。多个case可以共用一条语句快
break非常重要,如果switch中有多个case,如果没有在每个case中书写break,当变量跟某个case匹配了并不会执行该条case的语句块结束,而是执行后继续执行后面的case。所以每条case后面都需要书写break,switch不像if语句一样匹配了执行就跳出if语句。switch需要主动去书写break
三元运算符
条件表达式?表达式1:表达式2
问号前的是条件表达式,条件表达式去进行匹配,冒号隔开两个表达式。如果条件表达式为真则执行表达式1,为假执行表达式2。
for循环语句
for(var i=1 i<=10 i++){
console.log(i);
}
for语句括号有三个表达式:
表达式1 定义一个循环变量
表达式2 表示继续循环的条件,如果为真一直执行
表达式3 用来更新循环变量 使循环变量的值越来越趋向终点
for循环的执行机理
for(var i=1 i<=10 i++){
console.log(i);
}
首先执行语句1,接着判断语句2是否满足,如果满足则会执行for循环中的语句块(也就是进入循环体),循环体执行完执行语句3,直到不满足退出循环。
While循环语句
while循环没有显式定义循环变量,需要在外部定义好变量
while是“当”的意思
break和continue
break表示立即终止循环,他只能在循环语句中使用,for和while循环都可以使用。
continue表示跳过循环的一个迭代,并继续执行下一个迭代,常用于for语句。
do while循环
do while是先执行循环体后进行测试循环语句,不同于while和for先测试表达式是否满足。do while先执行循环体在测试条件是否满足。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var n = prompt('请输入一个数值的n');
var sum = 0;
var result = 1;
for (var i = 1; i < n; i++) {
result *= i / (2 * i + 1);
sum += result;
}
document.write(2 * (sum + 1));
</script>
</body>
</html>
给循环语句添加别名
给外层for循环加上label
数组简介和定义
数组(Array)用来存储一组相关的数值,从而发布求和,遍历等操作。
数组的定义方法为3种:
第一种:var arr=[‘A’,‘B’,‘C’,‘D’];
第二种:var arr= new Aaary(‘a’,‘b’,‘c’,‘d’);
第三种:var arr=new Array(4);但是这四项都是undefined;
访问数组项:
数组的每一项都有下标,小标从0开始
下标越界:
访问数组中不存在的项会返回undefined,不会报错
数组的下标是数组的长度减一
更改数组项
数组的内容不是定义后就写死的,我们是可以去修改其中任意一项的值。
如果更改的数组项超过了原本数组的下标,则会去创造这一项。
数组类型检测
数组使用typeOf检测为object类型。
Array.isArray()该方法用于检测数据是否是数组,返回类型是Boolean类型。
数组常用方法
push()在尾部插入新项
pop()在尾部删除
unshift()在头部插入新项
shift()在头部删除
Splice()方法用于替换数组中的指定项。也可以设置不替换增加项。
arr.splice(2,0)表示从第二项开始,但参数二为0则表示不替换下标为2的,在2前面添加新项。
Splice()方法也可以删除指定项。
Splice会以数组的形式返回被删除的项。
Slice()方法
slice用于得到子数组,方法跟字符串的slice方法是一样的,slice(a,b)从下标为a的开始到下标为b的,但是不包含b项。如果没有设置参数b,则表示从下标a开始提取一直到结束。同时也支持参数为负数,表示数组的倒数项。
执行结果为45678,参数有负数,那么用数组的长度加上这个负数,得到的值就是这个位置的索引(下标)。arr.slice(-6,8)第一个参数-6是负数,用数组长度9加上-6,得出的值为3,即开始位置索引为3。第二个参数为8,但是截取时不包含8,即截取到索引为7的位置。所以arr.slice(-6,8)返回的结果是[4,5,6,7,8]。
Join方法和split方法
join方法是数组转换为字符串,字符串的split方法将字符串转换为数组。
join()参数表示已什么样的字符作为连接符,如果参数为空则表示用逗号分割,split()参数是以什么字符拆分字符串,一般不能留空。
concat方法
concat表示合并连接多个数组
下图输出为1,2,3,4,5,6,7,8,9,10,11
reverse方法
reverse()将一个数组的顺序置反。
indexOf方法和includes方法
index()方法选择某一个元素返回该元素的位置,如果不存在则返回-1,includes()方法判断一个数组中是否包含指定的值,返回类型是Boolean类型。
indexOf()有两个参数,第一个参数是要查找的项,第二个是查找项的起点位置的索引。如果查找到该项,则返回它从查找位置起,首次出现的位置。如果没有查找到,则返回-1。arr.indexOf(3,6)意思是从数组arr索引(下标)为6的位置开始查找数字3。这个数组没有索引为6的项,所以返回-1
改变数组和不改变数组的方法:
push()尾部添加改变、pop()尾部删除改变、unshift()头部添加改变、shift()头部删除改变、reverse()倒序改变、splice()指定下标后替换某几项改变、sort()排序改变、
concat() 连接合成新数组 不会改变、indexOf()查找某个数组所在位置不会改变、split()将字符串转数组不会改变、join()将数组转为字符串不会改变、includes()判断一个数组是否是改值不会改变、splice()用于获取子数组不会改变。
数组去重
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var arr = [4, 1, 6, 8, 8, 8, 3, 3, 7, 9, 9, 9, 6, 6, 1, 1, 2, 2, 3, 3];
var result = [];
for (var i = 0; i < arr.length; i++) {
if (!result.includes(arr[i])) {
result.push(arr[i]);
}
}
document.write(result);
</script>
</body>
</html>
数组随机取样
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var arr = [4, 1, 6, 8, 8, 8, 3, 3, 7, 9, 9, 9, 6, 6, 1, 1, 2, 2, 3, 3];
var result = [];
for (var i = 0; i < 3; i++) {
var n = parseInt(Math.random() * arr.length);
result.push(arr[n]);
arr.splice(n, 1);
}
document.write(result);
</script>
</body>
</html>
冒泡排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var arr = [2, 4, 6, 8, 9, 3, 0, 1, 7, 5];
for (var i = 1; i < arr.length; i++) {
for (var j = arr.length - 1; j >= i; j--) {
if (arr[j] < arr[j - 1]) {
var temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
document.write(arr);
</script>
</body>
</html>
二维数组
二维数组即表示数组中存放另一个数组,数组中的数组。
二维数组拼接
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>二维数组</title>
</head>
<body>
<script>
var arr = [
["慕", "课", "网"],
["程", "序", "员"],
["梦", "工", "厂"]
];
// 补充代码
var str = [];
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < 3; j++) {
str.push(arr[i][j]);
if (arr[i][j] == '网') {
str.push(',');
} else if (arr[i][j] == '员') {
str.push('的');
}
}
}
document.write(str.join(''));
</script>
</body>
</html>
基本类型值和引用类型
基本类型进行赋值时创建一个地址来存储数据,当另外一个变量声明后将之前变量的值进行引用,那么会重新克隆之前变量的值选择新的地址创建,那么之前的变量进行修改值则不会影响另外一个变量的值。
var a=3;
var b=a;
a=4;
创建a变量等于3,内存会创建一个地址存放3,那么创建一个b,b的值又是a,那么会克隆3存放在新的一个地址里。那么后面a=4;b不会进行改变。
引用类型变量传值的内存变化
数组存放于一个堆内存中,那么arr1声明一串数组后,会存放在堆内存中,arr1指向这个堆内存,那么arr2的值等于arr1,则将arr2也指向同一个堆内存中,所以后续对其中一个arr进行push等操作,两个都会进行改变,因为他们同事指向一个地址。不跟基本类型一样会进行克隆。
相等判断的区别:基本类型进行对等判断,是比较值是否相等。引用类型进行对等判断时是比较他们的地址是否是同一个地址。
[]==[]: 两个空数组指向的内存地址不一样,比较结果为false。
null==null:null为基本类型,比较结果为true。
undefined==undefined :undefined 为基本类型,比较结果为true。
false==false :false为基本类型
b=a相当于a与b指向同一个地址 。但是a=[4,44]之后改变了a的地址 , 所以b不会受到影响,依旧为[4] 。后面a=b又让a和b指向了同一个地址 ,此时为b添加元素 ,a也会随之改变 ,所以a为[4,44] 。
浅克隆
浅克隆:引用类型是存放在堆内存,前面说到a=b;相当于指向同一个地址,那么去对a和b进行对等比较他们的true 的,因为地址不变。那么要做到浅克隆的话可以使用遍历数组,遍历后将每一个值push到a中,那么就相当于克隆了值在新的数组中,并且进行对等比较为false,false表示不是指向同一个地址,true表示指向同一地址。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var arr = [1, 2, 3, 4, 5, 6];
var arr1 = [];
for (var i = 0; i < arr.length; i++) {
arr1.push(arr[i]);
}
document.write(arr1 + ' ' + arr + '<br/>');
document.write(arr1 == arr);
</script>
</body>
</html>
var arr2=[arr1[0],arr1[1]] 中,并没有直接把arr1赋值给arr2, 它们两个指向的内存地址不一样,所以给arr1添加或者删除元素不会影响到arr2。
但arr2中的元素,是直接复制arr1中的元素。由于arr1[1]是引用类型, 故arr1[1]==arr2[1],那么在arr1[1]删除或者添加元素会影响到arr2的结果。
JS函数与DOM
什么是函数
函数就是语句的封装,可以多次调用简化代码高度复用。
函数的定义和调用
function fun(){
//函数体语句
}
function表示定义函数,fun表示函数的名称,函数名必须符合JS命名规则。()圆括号是行参列表。
匿名表达式
var fun=function (){
//函数体语句
这种定义方法也叫函数表达式
}
函数的调用
执行函数体所有语句也就叫调用函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>什么是函数</title>
</head>
<body>
<script>
function a(){
document.write("你好");
}
var b=function (){
document.write("JavaScript");
}
a();
b();
</script>
</body>
</html>
函数的参数和返回值
参数是函数内的一些待定值,在调用函数时候必须传入这些函数的具体值。
函数的参数可多可少,也可以没有参数,多个参数用逗号隔开。
函数的返回值,函数体可以使用return关键字来表示函数的返回值。
递归
递归是函数内部语句可以调用函数自己本身,从而对函数本身进行一次迭代,在新的迭代中又会执行调用函数自身的语句,又产生迭代,当函数执行到某一次停止迭代,则会一层层返回被递归。
阶乘递归举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 书写一个函数,这个函数内部自己会调用自己,从而形成递归。
function factorial(n) {
// 函数的功能是计算n的阶乘,n!不就是n * (n-1)!么??
// 这个就是递归的出口,如果计算1的阶乘,可以不用递归了,直接告诉你答案就是1
if (n == 1) return 1;
// 如果询问的不是1的阶乘,就返回n * (n-1)!
return n * factorial(n - 1);
}
var result = factorial(6);
alert(result);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>小鲤鱼</title>
</head>
<body>
<script>
function dg(n){
if(n==0)return '我的小鲤鱼';
return "抱着"+dg(n-1)+"的我";
}
document.write(dg(3));
</script>
</body>
</html>
深克隆
实现深克隆要有递归思想,整体过程和浅克隆相似,定义空数组,对数组进行遍历,如果是基本类型值就推入数组中,如果遍历的又是数组,就重复执行浅克隆的操作。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//原数组
var arr = [33, 44, 11, 22, [77, 88, [33, 44]]];
//这个函数会被递归
function deepclone(arr) {
//结果数组,每一层都有一个结果数组
var result = [];
//遍历数组每一项
for (var i = 0; i < arr.length; i++) {
//判断是否是数组,如果遍历到数组
if (Array.isArray(arr[i])) {
//递归
result.push(deepclone(arr[i]));
} else {
//如果是基本类型推入
result.push(arr[i]);
}
}
return result;
}
var arr2 = deepclone(arr);
console.log(arr2);
</script>
</body>
</html>
1、第一次调用deepClone时,创建result = [],循环遍历[33, 44, 11, 22, [77, 88, [33, 44]]] 。依次把33, 44, 11, 22加入到数组中,即result=[33, 44, 11, 22]。等遍历到最后一个元素,发现是数组。那么执行result.push(deepClone(arr[i])),里面调用了函数deepClone,所以需要等待第二次调用结束,第一次调用deepClone才会继续往下执行代码。
2、第二次调用,创建result = [],遍历 [33, 44],发现它里面没有数组,执行else中的result.push(arr[i]),此时result=[33, 44] 。即第二次调用deepClone返回值为[33, 44],第二次调用返回结果数组为result = [33, 44]。
3、上面说了,第二次调用结束,才会继续执行第一次的调用,即第二次deepClone(arr[i])的返回值[33, 44],被添加到了第一次创建的result 中。注意第一次的result=[33, 44, 11, 22],所以result.push(deepClone(arr[i]));执行之后,result=[33, 44, 11, 22, [77, 88, [33, 44]]];
如下相当于 result.push([33, 44]);
最后第一次调用返回结果数组为[33, 44, 11, 22, [77, 88, [33, 44]]]。
全局变量和局部变量
变量作用域:JavaScript是函数级作用域编程语言,变量只在其定义的函数内部有意义。
局部变量:
全局变量
如果变量不是定义在函数内部,而是在外面那它就是全局变量。任何函数都可以对它访问和更改。
不加var将定义为全局变量
遮蔽效应
如果函数中定义了局部变量a,而外部定义了全局变量a,两个变量的值相同,那么函数则会默认使用内部的局部变量a,
注意变量提升的情况
var a=10;
function fun(){
a++;
var a=5;
console.log(a);
}
fun();
console.log(a);
首先需要考虑变量提升的情况,JS预解析会将变量a提升,fun()函数中a被提升定义,那么a的值是undefined,接着a++,那么a被提升了但值没有被提升,所以a的值为undefined,undefined+任何数都是NaN,接着赋值5给a,输出结果则为5和外部a的10。
作用域链
在函数嵌套中,变量会逐层从内到外寻找变量的定义。
程序输出结果为:40、20、30、50。变量会逐层从内部到外部寻找它的定义。
闭包
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。
js语言的特殊之处在于,函数内部能够直接读取全局变量。
var n=999;
function f1(){
alert(n);
}
f1(); // 999 函数内部能够直接读取全局变量,输出结果为999
另一方面,在函数外部自然无法读取函数内的局部变量。
function f1(){
var n=999;
}
alert(n); // error
函数内部声明变量的时候,一定要使用var命令。如果不用的话,实际上声明了一个全局变量。
function f1(){
n=999;
}
f1();
alert(n); // 999
如何从外部读取局部变量?
就是在函数的内部,再定义一个函数。
function f1(){
var n=999;
function f2(){
alert(n); // 999
}
}
上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"作用域链"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,就可以在f1外部读取它的内部变量了。
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
上一节代码中的f2函数,就是闭包。
闭包就是内部函数总是可以访问其所在的外部函数中声明的参数和变量。
闭包特点:记忆性
闭包产生后,函数所处环境的状态会一直保持在内存中,不会被外层函数调用后自动清除。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function createCheckTemp(standardTemp) {
function checkTemp(n) {
if (n <= standardTemp) {
alert('你的体温正常');
} else {
alert('你的体温偏高');
}
}
return checkTemp;
}
// 创建一个checkTemp函数,它以37.1度为标准线
var checkTemp_A = createCheckTemp(37.1);
// 再创建一个checkTemp函数,它以37.3度为标准线
var checkTemp_B = createCheckTemp(37.3);
checkTemp_A(37.2);
checkTemp_A(37.0);
checkTemp_B(37.2);
checkTemp_B(37.0);
</script>
</body>
</html>
createCheckTemp调用之后,传入参数37.1,即参数standardTemp=37.1。返回值是它内部定义的函数checkTemp,即变量checkTemp_A的值就是函数checkTemp,checkTemp_A(37.2)就相当于checkTemp(37.2),然后执行checkTemp函数中的代码,形参n=37.2。此时if判断条件不成立,执行else。
checkTemp_A(37.2)内部设置的返回值是函数checkTemp。所以checkTemp_A(37.2)调用之后,最终把返回值checkTemp赋值给了变量checkTemp_A。
闭包特点:模拟私有变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//封装一个函数,函数功能就是私有化变量
function fun() {
//定义局部变量a
var a = 0;
return {
getA: function() {
alert(a);
},
add: function() {
a++;
},
pow: function() {
a *= 2;
}
};
}
var obj = fun();
//如果想在fun函数外面使用变量a,唯一方法就是调用。
obj.add();
obj.add();
obj.pow();
obj.getA();
</script>
</body>
</html>
使用闭包的注意点
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
运行结果:1122
立即执行函数IIFE
IIFE是立即调用函数表达式,是JavaScript特殊的函数写法,一旦定义就立即被调用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var age = 12;
var sex = '男'
var title = (function() {
if (age < 18) {
return '未成年';
} else {
if (sex == '男') {
return '先生';
} else {
return '女士';
}
}
})();
console.log(title);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var arr = [];
for (var i = 0; i < 5; i++) {
(function(i){
arr.push(function () {
alert(i);
});
})(i);
}
arr[0]();
arr[1]();
arr[2]();
arr[3]();
arr[4]();
</script>
</body>
</html>
DOM基本概念
Nodetype
nodetype常用属性值
Document
getElementById()
document.getElementByld()通过id获得元素
document.getElementsByTagName()通过标签名获得元素
document.getElementsByClassName()通过类名获得元素
document.querySelector()通过选择器得到元素
document.querySelectorAll()通过选择器得到元素数组
延长运行
通常JS代码要写在HTML节点后面的,如果写在HTML节点前面,JS代码无法找到相应的HTML节点。
可以使用window.οnlοad=function(){}事件,让页面加载后,再去执行代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
window.onload = function() {
var box1 = document.getElementsByClassName('box1');
var box2 = document.getElementsByClassName('box2');
console.log(box1, box2);
}
</script>
</head>
<body>
<div class="box1">哈哈</div>
<div class="box2">哈哈哈</div>
</body>
</html>
getElementsByTagName()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box1">
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
</div>
<script>
var box1 = document.getElementById('box1');
var p = box1.getElementsByTagName('p');
console.log(p);
</script>
</body>
</html>
document.querySelector&querySelectorAll()
querySelector只能得到页面上一个元素,如果包含多个则只能获得一个元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box">
<p>哈哈</p>
<p class="spec">哈哈</p>
<p>哈哈</p>
</div>
<script>
var p = document.querySelector('#box .spec');
console.log(p);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- <div id="box">
<p>哈哈</p>
<p class="spec">哈哈</p>
<p>哈哈</p>
</div> -->
<ul id="list1">
<li>哈哈</li>
<li>哈哈</li>
<li>哈哈</li>
<li>哈哈</li>
<li>哈哈</li>
<li>哈哈</li>
</ul>
<script>
// var p = document.querySelector('#box .spec');
// console.log(p);
var p = document.querySelectorAll('#list1 li');
console.log(p);
</script>
</body>
</html>
节点的关系
子节点:childNodes、父节点:parentNode、第一个节点:firstChild、最后一个节点:lastChild
前一个兄弟节点:previousSibling、后一个兄弟节点:nextSibling。
注意点:文本节点也属于节点,空白文本也属于子节点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box">
<p>哈哈哈</p>
<p id="para">嘻嘻嘻</p>
<p>呼呼呼</p>
</div>
<script>
var box = document.getElementById('box');
var para = document.getElementById('para');
//所有子节点
console.log(box.childNodes);
//所有元素的子节点
console.log(box.children);
//第一个子节点
console.log(box.firstChild);
//第一个元素子节点
console.log(box.firstElementChild);
//最后一个子节点
console.log(box.lastChild);
//最后一个元素子节点
console.log(box.lastElementChild);
//para的父节点
console.log(para.parentNode);
//para的前一个兄弟节点 是空白文本节点
console.log(para.previousSibling);
//para的前一个元素兄弟节点
console.log(para.previousElementSibling);
//para的后一个兄弟节点 也是空白文本节点
console.log(para.nextSibling);
//para的后一个元素兄弟节点
console.log(para.nextElementSibling);
</script>
</body>
</html>
封装节点关系函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box">
<p>我是段落a</p>
我是文本
<!-- -->
<p id="para">我是段落b
<span>1</span>
<span>2</span>
<span>3</span>
</p>
<p>我是段落c</p>
<p>我是段落c</p>
<p>我是段落c</p>
</div>
<script>
var box = document.getElementById('box');
var para = document.getElementById('para');
//封装一个函数,这个函数可以返回元素的所有子元素节点(兼容到ie6),类似Childen功能
function getChildren(node) {
//结果数组
var children = [];
//遍历node这个节点的所有子节点,判断每一个子节点的nodetype是不是一,如果是一就推入children数组中
for (var i = 0; i < node.childNodes.length; i++) {
if (node.childNodes[i].nodeType == 1) {
children.push(node.childNodes[i]);
}
}
return children;
}
console.log(getChildren(box));
console.log(getChildren(para));
//封装一个函数,这个函数返回元素的前一个元素兄弟节点(兼容ie6)类似previousElementSibling功能
function getpreviousElementSibling(node) {
var o = node;
while (o.previousElementSibling != null) {
if (o.previousElementSibling.nodeType == 1) {
return o.previousElementSibling;
}
o = o.previousElementSibling;
}
}
console.log(getpreviousElementSibling(para));
//封装一个函数,这个函数返回元素的所有兄弟节点
function getAllSibling(node) {
//前面的元素兄弟节点
var prevs = [];
//后面的元素兄弟节点
var next = [];
var o = node;
while (o.previousElementSibling != null) {
if (o.previousElementSibling.nodeType == 1) {
prevs.unshift(o.previousElementSibling);
}
o = o.previousElementSibling;
}
while (o.nextElementSibling != null) {
if (o.nextElementSibling.nodeType == 1) {
next.push(o.nextElementSibling);
}
o = o.nextElementSibling;
}
return prevs.concat(next);
}
console.log(getAllSibling(para));
</script>
</body>
</html>
innerHTML&innerText
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box"></div>
<script>
var obox = document.getElementById('box');
obox.innerHTML = '哈哈哈';
obox.innerHTML = '<ul><li>哈哈</li><li>嘻嘻</li></ul>'
obox.innerText = '哈哈哈';
obox.innerText = '<ul><li>哈哈</li><li>嘻嘻</li></ul>'
</script>
</body>
</html>
节点操作
如何改变元素节点的CSS样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
border: 1px solid #000;
}
</style>
</head>
<body>
<div class="box" id="box">
你好
</div>
<script>
var box = document.getElementById('box');
// box.style.backgroundColor = 'red';
box.style.fontSize = '50px';
</script>
</body>
</html>
classname属性
1.className属性设置或返回class属性值
element.className="cName"
element表示要设置class属性的元素
className表示要获取或设置指定元素的class属性的值
cName表示元素class属性的值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myDiv"></div>
<script>
var myDiv = document.getElementById("myDiv");
// 设置class属性值
myDiv.className="fixBox"
</script>
</body>
</html>
2.className用于获取元素class属性的值
(1)直接获取html元素的class属性值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myDiv" class="fixBox"></div>
<script>
var myDiv= document.getElementById("myDiv");
// 获取class属性值
console.log(myDiv.className)
</script>
</body>
</html>
(2)先通过className设置class属性的值,在获取该属性的值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myDiv"></div>
<script>
var myDiv = document.getElementById("myDiv");
// 设置class属性值
myDiv.className="fixBox";
// 获取class属性值
console.log(myDiv.className)
</script>
</body>
</html>
(3)元素存在多个类名,通过className获取全部类名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myBox" class="oneBox twoBox threeBox"></div>
<script>
var myBox = document.getElementById("myBox");
console.log(myBox.className)
</script>
</body>
</html>
注意事项
通过className设置元素的class属性的值,如果该元素的class属性中存在类名或者多个类名,都会被替换而不是追加。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myBox" class="oneBox"></div>
<script>
var myBox = document.getElementById("myBox");
// 设置class属性值
myBox.className="fixBox";
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myBox" class="oneBox twoBox threeBox"></div>
<script>
var myBox = document.getElementById("myBox");
myBox.className='fixBox'
</script>
</body>
</html>
如果需要追加多个类名,适用于类名较少的情况下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myBox" class="oneBox"></div>
<script>
var myBox = document.getElementById("myBox");
// 把原本的类名也添加上,使用空格隔开
myBox.className='oneBox fixBox'
</script>
</body>
</html>
当类名较多,追加一个函数设置className属性的效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myDiv" class="oneBox"></div>
<script>
var mydiv=document.getElementById('oneBox');
function addClass(element,value){
var newClassName = "";
if(!element.className){
element.className = value;
}else{
newClassName=element.className;
newClassName+=' ';//这句代码将追加的类名分开
newClassName+=value;
element.className=newClassName;
}
}
// 设置class属性值
addClass(myDiv,"fixBox");
</script>
</body>
</html>
节点的创建、移除、克隆
document.createElement()方法创建一个指定的tagname的HTML元素。
var odiv=document.createElement('div');
注意点:document.createElement()方法创建的节点属于“孤儿节点”,意味着没有挂载在DOM树上,不能被看见。所有要使用appendchild()或者insertBefore()将“孤儿节点”插入DOM节点树上。
创建节点例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
table td {
height: 20px;
width: 100px;
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>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
td {
height: 39px;
width: 90px;
border: 1px solid #000;
}
</style>
</head>
<body>
<table id="t1"></table>
<script>
var otable = document.getElementById('t1');
for (var i = 1; i <= 9; i++) {
var tr = document.createElement('tr');
for (var j = 1; j <= i; j++) {
var td = document.createElement('td');
td.innerText = i + '*' + j + '=' + (i * j);
td.style.textAlign = 'center';
tr.appendChild(td);
}
otable.appendChild(tr);
}
</script>
</body>
</html>
移动节点
如果将已经挂载在DOM树上的节点进行appendchild()或者insertBefore()操作,那么这个节点将会被移动。一个节点不能同时存在于两个DOM树中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box1">
<p id="para">我是1</p>
</div>
<div id="box2">
<p>我是box2原有的p标签</p>
<p>我是box2原有的p标签</p>
<p>我是box2原有的p标签</p>
<p>我是box2原有的p标签</p>
<p>我是box2原有的p标签</p>
<p>我是box2原有的p标签</p>
</div>
<script>
var box1 = document.getElementById('box1');
var para = document.getElementById('para');
var box2 = document.getElementById('box2');
var pbox2 = box2.getElementsByTagName('p');
// box2.appendChild(para);
box2.insertBefore(para, pbox2[0]);
</script>
</body>
</html>
删除节点
removeChild()方法删除Dom中的一个子节点。节点不能主动删除自己,必须由父节点来删除。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box1">
<p>我是p节点</p>
<p>我是p1节点</p>
<p>我是p2节点</p>
<p>我是p3节点</p>
<p>我是p4节点</p>
</div>
<script>
var box1 = document.getElementById('box1');
//删除其中一个
var dp = box1.getElementsByTagName('p')[0];
box1.removeChild(dp);
</script>
</body>
</html>
克隆节点
cloneNode()方法克隆节点,克隆出来的是孤儿节点。
参数后面带有布尔类型,表示是否深度克隆,如果true表示节点的所有后代节点都被克隆,如果false表示只克隆本身。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box1">
<ul>
<li>3</li>
<li>1</li>
<li>2</li>
<li>4</li>
<li>5</li>
<li>7</li>
</ul>
</div>
<div id="box2"></div>
<script>
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var thun1 = box1.getElementsByTagName('ul')[0];
//克隆节点
var newul = thun1.cloneNode();
//克隆出来是孤儿节点
box2.appendChild(newul);
</script>
</body>
</html>
事件监听
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
background-color: gray;
}
</style>
</head>
<body>
<div id="box1"></div>
<script>
var box1 = document.getElementById('box1');
box1.onclick = function() {
alert('哈哈');
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
background-color: gray;
}
</style>
</head>
<body>
<div id="box1"></div>
<script>
var box1 = document.getElementById('box1');
box1.onclick = function() {
console.log('哈哈');
}
box1.ondblclick = function() {
console.log('ondblclick');
}
box1.onmousedown = function() {
console.log('onmousedown');
}
box1.onmouseup = function() {
console.log('onmouseup');
}
box1.onmouseenter = function() {
console.log('onmouseenter');
}
box1.onmousemove = function() {
console.log('onmousemove');
}
box1.onmouseleave = function() {
console.log('onmouseleave');
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="" method="POST" id="myform">
<p>
<input type="text" name="namefield">
</p>
<input type="text" name="agefield">
</form>
<script>
var myform = document.getElementById('myform');
var namefield = myform.namefield;
var agefield = myform.agefield;
namefield.onchange = function() {
console.log('你修改了名字');
}
namefield.onfocus = function() {
console.log('姓名获得焦点');
}
namefield.onblur = function() {
console.log('姓名失去焦点');
}
</script>
</body>
</html>
监听小练习
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>HTML事件</title>
<style>
button{
width:100px;
height:50px;
border:none;
}
</style>
</head>
<body>
<!-- 此处写代码 -->
<input type=button id='btn' value='点击我试试'></input>
<script>
var btn=document.getElementById('btn');
btn.onclick=function(){
this.style.backgroundColor='green';
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>onload</title>
<script type="text/javascript">
//补充代码
window.onload=function(){
var btn=document.getElementsByTagName('input')[0];
btn.onclick=function(){
this.style.color='red';
}
btn.onmouseleave=function(){
this.style.color='gray';
}
}
</script>
</head>
<body>
<input type="button" value="点击改变按钮字体颜色" >
</body>
</html>
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>输入文本</title>
</head>
<body>
<p>字数限制在30字内,<span id="os">您还可以输入<b id="ob">30</b> 字</span></p>
<textarea cols="50" rows="7" id="text"></textarea>
<script type="text/javascript">
//补充代码
var ob = document.getElementById('ob');
var otext = document.getElementById('text');
var os = document.getElementById('os');
otext.onkeyup = function() {
if (otext.value.length <= 30) {
ob.innerText = 30 - otext.value.length;
} else {
ob.innerText = otext.value.length - 30;
os.innerText = '您已超出' + ob.innerText + '字';
}
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>onblur和onfocus</title>
<style type="text/css">
.red {
border: 1px solid red;
}
</style>
</head>
<body>
姓名:<input type="text" id="name"> <span id="ts"></span>
<script type="text/javascript">
// 补充代码
var nametext = document.getElementById('name');
var ts = document.getElementById('ts');
nametext.onfocus = function() {
if (nametext.value == "") {
ts.innerHTML = '请输入您的姓名';
}
}
nametext.onblur = function() {
if (nametext.value == "") {
ts.innerHTML = '用户名不能为空';
nametext.className = 'red';
} else {
ts.innerHTML = '';
this.className = '';
}
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>onchange()</title>
<style type="text/css">
#div {
width: 300px;
height: 300px;
border: 2px solid gray;
margin: 100px 0 0 200px;
}
</style>
<script type="text/javascript">
//补充代码
window.onload = function() {
var xl = document.getElementById('color');
var box = document.getElementById('div');
xl.onchange = function() {
var ys = this.value;
//select复选框若选择了某个option,则那个option的value值会自动传给select节点
if (xl.value == '0') {
box.style.backgroundColor = 'white';
box.innerText = '我没有发生任何变化';
} else {
box.innerText = '我的背景颜色变成了' + ys + '色';
box.style.backgroundColor = ys;
}
}
}
</script>
</head>
<body>
<div>
<span>请选择您喜欢的颜色:</span>
<select id="color">
<option value="0">请选择</option>
<option value="yellow">黄色</option>
<option value="orange">橘色</option>
<option value="pink">粉色</option>
<option value="purple">紫色</option>
</select>
</div>
<div id="div">我是div</div>
</body>
</html>
事件传播
使用onXXX()方法只能是监听冒泡阶段。
注意事项:最内部元素不在区分捕获和冒泡阶段,会先执行写在前面的监听,然后执行后写的监听。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box1 {
height: 202px;
width: 202px;
border: 1px solid #000;
padding: 50px;
}
#box2 {
height: 100px;
width: 100px;
border: 1px solid #000;
padding: 50px;
}
#box3 {
width: 100px;
height: 100px;
border: 1px solid #000;
}
</style>
</head>
<body>
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
<script>
var obox1 = document.getElementById('box1');
var obox2 = document.getElementById('box2');
var obox3 = document.getElementById('box3');
obox1.addEventListener('click', function() {
console.log('box1捕获阶段')
}, true)
obox2.addEventListener('click', function() {
console.log('box2捕获阶段')
}, true);
obox3.addEventListener('click', function() {
console.log('box3冒泡阶段')
}, false)
obox3.addEventListener('click', function() {
console.log('box3捕获阶段')
}, true)
obox3.onclick = function() {
console.log('a');
};
obox1.addEventListener('click', function() {
console.log('box1冒泡阶段')
}, false)
obox2.addEventListener('click', function() {
console.log('box2冒泡阶段')
}, false)
</script>
</body>
</html>
如果给元素设置两个或者相同的多个事件,DOM0级会执行后面写的,前面的会被覆盖。而DOM2级会按顺序执行。
事件对象
e.charCode和e.keyCode属性
e.charCode属性通常用语keypress事件,表示用户输入的‘字符码’。
e.keyCode属性通常用语onkeydown事件和onkeyup事件,表示用户按下按键的‘键码’。
盒子移动小案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
position: absolute;
top: 200px;
left: 200px;
width: 100px;
height: 100px;
background-color: orange;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
var box = document.getElementById('box');
var t = 200,
l = 200;
document.onkeydown = function(e) {
switch (e.keyCode) {
case 37:
l -= 3;
break;
case 38:
t -= 3;
break;
case 39:
l += 3;
break;
case 40:
t += 3;
break;
}
box.style.left = l + "px";
box.style.top = t + "px";
}
</script>
</body>
</html>
e.preventDefault()方法
e.preventDefault()方法用来阻止事件产生的“默认动作”。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>
<input type="text" id="field">
</p>
<script>
var field = document.getElementById('field');
field.onkeypress = function(e) {
if (!(e.charCode >= 48 && e.charCode <= 57 || e.charCode >= 97 && e.charCode <= 122)) {
e.preventDefault();
}
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
width: 220px;
height: 200px;
background-color: orange;
}
body {
height: 2000px;
}
</style>
</head>
<body>
<div id="box"></div>
<h1 id="info">0</h1>
<script>
var box = document.getElementById('box');
var info = document.getElementById('info');
var a = 0;
box.onmousewheel = function(e) {
e.preventDefault();
if (e.deltaY > 0) {
a--;
} else {
a++;
}
info.innerText = a;
};
</script>
</body>
</html>
e.stopPropagation()方法
e.stopPropagation()方法用来阻止事件继续传播。
弹出层小案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<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) {
// 阻止事件继续传播到document身上
e.stopPropagation();
};
</script>
</body>
</html>
事件委托
事件委托通常要结合使用e.target属性
e.target属性:触发此事件的最早元素,即“事件源元素”
currenttarget属性:事件处理附加到的元素,相等于this
target指向的是事件触发时的元素。
currentTarget指向事件绑定的元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn">添加一个试试</button>
<ul id="list"></ul>
<script>
var btn = document.getElementById('btn');
var list = document.getElementById('list');
list.onclick = function(e) {
e.target.style.color = 'red';
}
btn.onclick = function() {
var li = document.createElement('li');
li.innerText = '我是列表项';
list.appendChild(li);
}
</script>
</body>
</html>
事件委托应用场景:
注意事项:
定时器和延时器
定时器:
setInterval()函数可以重复调用一个函数,在每次调用的时候,具有固定的时间间隔。每多少时间调用一次,重复操作。
setInterval(function(){
//这个函数将以自动固定时间调用
})
serInterval()函数可以接收多个参数,传入多个参数按顺序传入
serInterval()方法可以传入匿名函数,也是可以传入具体名称的函数,但是需要注意不能加圆括号。
清除定时器
clearInterval()清除定时器
防止定时器叠加(用户一直点击)需要在定时器开始前先清除定时器。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1 id="info">0</h1>
<button id="btn1">开始</button>
<button id="btn2">暂停</button>
<script>
var info = document.getElementById('info');
var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');
var a = 0,
timer;
btn1.onclick = function() {
//防止定时器叠加(用户一直点击)需要在定时器开始前先清除定时器。
clearInterval(timer);
timer = setInterval(function() {
info.innerText = ++a;
}, 1000);
}
btn2.onclick = function() {
clearInterval(timer);
}
</script>
</body>
</html>
延时器:
setTimeout()函数可以设置延时器,当指定的时间到了,函数会执行一次,执行后就不再执行了。
清除延时器
clearTimeout()清除延时器,和clearInterval功能类似。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<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>
</body>
</html>
JS和CSSS3结合实现动画
左右移动案例:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
#box {
width: 100px;
height: 100px;
position: absolute;
top: 100px;
left: 100px;
background-color: orange;
}
</style>
</head>
<body>
<div id="box"></div>
<button id="btn">按我运动</button>
<script>
var box = document.getElementById('box');
var btn = document.getElementById('btn');
var pos = 1;
btn.onclick = function() {
//过渡动画
if (pos == 1) {
box.style.transition = 'left .5s ease 0s';
box.style.left = '1000px';
pos = 2;
} else if (pos == 2) {
box.style.transition = 'left .5s ease 0s';
box.style.left = '100px';
pos = 1;
}
};
</script>
</body>
</html>
函数节流:
一个函数执行一次后,只有大于设定的执行周期才能执行第二次,要设置函数节流需要用到延时器。
关锁后需要设置延时器开锁,如果没在指定秒数去操作,那么lock为false,去if判断时return的,所以无法操作,因此需要等待延时器结束秒数开启锁才可以继续操作,这样利于动画不累积。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
width: 100px;
height: 100px;
position: absolute;
top: 100px;
left: 100px;
background-color: orange;
}
</style>
</head>
<body>
<div id="box"></div>
<button id="btn">按我运动</button>
<script>
var box = document.getElementById('box');
var btn = document.getElementById('btn');
var pos = 1;
var lock = true;
btn.onclick = function() {
//判断函数节流锁是否关闭,如果关闭return
if (!lock) return;
//过渡动画 锁是true执行
if (pos == 1) {
box.style.transition = 'left .5s ease 0s';
box.style.left = '1000px';
pos = 2;
} else if (pos == 2) {
box.style.transition = 'left .5s ease 0s';
box.style.left = '100px';
pos = 1;
}
//执行后关锁
lock = false;
//设置延时器后打开
setTimeout(function() {
lock = true;
}, 2000);
};
</script>
</body>
</html>
无缝连续滚动特效
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.box {
height: 130px;
width: 1200px;
border: 1px solid #000;
margin: 50px auto;
overflow: hidden;
}
.box ul {
width: 2700px;
list-style: none;
position: relative;
}
.box ul li {
float: left;
margin-right: 10px;
}
</style>
</head>
<body>
<div id="box" class="box">
<ul id="list">
<li><img src="./beijing/0.png" alt=""></li>
<li><img src="./beijing/1.png" alt=""></li>
<li><img src="./beijing/2.png" alt=""></li>
<li><img src="./beijing/3.png" alt=""></li>
<li><img src="./beijing/4.png" alt=""></li>
<li><img src="./beijing/5.png" alt=""></li>
</ul>
<script>
var box = document.getElementById('box');
var list = document.getElementById('list');
//复制一份ul
list.innerHTML += list.innerHTML;
//全局变量 表示当前list的left值
var left = 0;
var timer;
function move() {
//设置清除定时器,防止动画累积
clearInterval(timer);
timer = setInterval(function() {
left -= 6;
if (left <= -1260) {
left = 0;
}
list.style.left = left + 'px';
}, 20);
}
move();
//鼠标进入停止轮播
box.onmouseenter = function() {
clearInterval(timer);
}
//鼠标离开开始轮播
box.onmouseleave = function() {
move();
}
</script>
</div>
</body>
</html>
跑马灯轮播图特效
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.box {
width: 650px;
height: 360px;
border: 1px solid #000;
margin: 50px auto;
position: relative;
overflow: hidden;
}
.box ul {
list-style: none;
width: 6000px;
position: relative;
left: 0px;
transition: left .5s ease 0s;
}
.box ul li {
float: left;
}
.box .leftbtn {
position: absolute;
left: 20px;
top: 50%;
margin-top: -25px;
width: 50px;
height: 50px;
background-color: rgb(28, 180, 226);
border-radius: 50%;
}
.box .rightbtn {
position: absolute;
right: 20px;
top: 50%;
margin-top: -25px;
width: 50px;
height: 50px;
background-color: rgb(28, 180, 226);
border-radius: 50%;
}
</style>
</head>
<body>
<div class="box">
<ul id="list">
<li><img src="./beijing/0.jpg" alt=""></li>
<li><img src="./beijing/1.jpg" alt=""></li>
<li><img src="./beijing/2.jpg" alt=""></li>
<li><img src="./beijing/3.jpg" alt=""></li>
<li><img src="./beijing/4.jpg" alt=""></li>
</ul>
<a href="javascript:;" class="leftbtn" id="leftbtn"></a>
<a href="javascript:;" class="rightbtn" id="rightbtn"></a>
</div>
<script>
// 得到按钮和ul,ul整体进行运动
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加过渡,为什么要加??css中不是已经加了么??这是因为最后一张图片会把过渡去掉
list.style.transition = 'left .5s ease 0s';
idx++;
if (idx > 4) {
//设置一个延时器,将ul瞬间拉回0的位置
setTimeout(function() {
//取消过渡,因为要瞬间移动回去0的位置,如果有过渡显得花里胡哨。
list.style.transition = 'none';
list.style.left = 0 + 'px';
idx = 0;
}, 500);
}
list.style.left = -idx * 650 + 'px';
//函数节流
setTimeout(function() {
lock = true;
}, 500);
}
leftbtn.onclick = function() {
//函数节流
if (!lock) return;
//判断是不是第0张,如果是瞬间移动到假的替换真的第0张,然后假的返回上一张
//取消到过渡动画
if (idx == 0) {
list.style.transition = 'none';
list.style.left = -5 * 650 + 'px';
// 设置一个延时器,这个延时器的延时时间可以是0毫秒,虽然是0毫秒,但是可以让我们过渡先是瞬间取消,然后再加上
setTimeout(function() {
list.style.transition = 'left .5s ease 0s';
idx = 4;
list.style.left = -idx * 650 + 'px';
}, 0)
} else {
idx--;
list.style.left = -idx * 650 + 'px';
}
//函数节流
setTimeout(function() {
lock = true;
}, 500);
}
</script>
</body>
</html>
呼吸轮播特效
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.box {
width: 650px;
height: 360px;
border: 1px solid #000;
margin: 50px auto;
position: relative;
}
.box ul {
list-style: none;
}
.box ul li {
position: absolute;
left: 0;
top: 0;
opacity: 0;
transition: opacity .5s ease 0s;
}
/* 只有第一张透明度是1 */
.box ul li:first-child {
opacity: 1;
}
.box .leftbtn {
position: absolute;
left: 20px;
top: 50%;
margin-top: -25px;
width: 50px;
height: 50px;
background-color: rgb(28, 180, 226);
border-radius: 50%;
}
.box .rightbtn {
position: absolute;
right: 20px;
top: 50%;
margin-top: -25px;
width: 50px;
height: 50px;
background-color: rgb(28, 180, 226);
border-radius: 50%;
}
</style>
</head>
<body>
<div class="box">
<ul id="list">
<li><img src="./beijing/0.jpg" alt=""></li>
<li><img src="./beijing/1.jpg" alt=""></li>
<li><img src="./beijing/2.jpg" alt=""></li>
<li><img src="./beijing/3.jpg" alt=""></li>
<li><img src="./beijing/4.jpg" alt=""></li>
</ul>
<a href="javascript:;" class="leftbtn" id="leftbtn"></a>
<a href="javascript:;" class="rightbtn" id="rightbtn"></a>
</div>
<script>
var list = document.getElementById('list');
var leftbtn = document.getElementById('leftbtn');
var rightbtn = document.getElementById('rightbtn');
//获取li列表项
var lis = document.getElementsByTagName('li');
//当前页数
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;
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;
lis[idx].style.opacity = 1;
//开锁
setTimeout(function() {
lock = true;
}, 500);
}
</script>
</body>
</html>
BOM基础
window对象
BOM(browser object model,浏览器对象模型)是JS与浏览器窗口交互的接口。
windows对象是当前JS运行所处的窗口,而这个窗口包含DOM结构,window.document属性就是document对象,简单来说document是window的一个属性。
我们定义在JS中的全局变量都会变成window对象的属性。
多个js文件共享全局作用域,意思就是在js文件夹中创建两个js文件,两个可以互相调用定义的全局变量。
控制台输入7。
<!DOCTYPE html>
<html lang="en">
<head>
<style>
body {
height: 5000px;
}
</style>
</head>
<body>
<script>
console.log('窗口内宽,包含滚动条' + window.innerWidth);
console.log('窗口外宽' + window.outerWidth);
console.log('窗口内宽,不包含滚动条' + document.documentElement.clientWidth);
</script>
</body>
</html>
resize事件
当窗口改变尺寸大小的时候,就会触发resize事件。
可以使用window.onresize()来监听窗口尺寸或者windows.addEvenListener(‘resize’)来绑定事件处理函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//监听窗口改变尺寸事件
window.onresize = function() {
var root = document.documentElement;
console.log('窗口尺寸改变了', root.clientWidth, root.clientHeight);
}
</script>
</body>
</html>
window.scrolly&scrollTop
二者都是测量浏览器下拉滚动了多少像素值。可以这样设置,这样是短路计算。如果scrolly有值就用。
<!DOCTYPE html>
<html lang="en">
<head>
<style>
body {
height: 5000px;
}
</style>
</head>
<body>
<script>
console.log(window.scrollY);
console.log(document.documentElement.scrollTop);
</script>
</body>
</html>
scrollpTop表示浏览器窗口滚动的高度;
innerHeight表示浏览器窗口内容区域的高度,包含滚动条;
outerHeight表示浏览器窗口外部的高度;
clientHeight表示元素内部的高度,包含内边距,但不包含滚动条、边框、外边距。
Navigator对象
<script>
console.log('浏览器品牌' + navigator.appName);
console.log('版本' + navigator.appVersion);
console.log('用户代理' + navigator.userAgent);
console.log('操作系统' + navigator.platform);
</script>
History对象
window.history对象提供了浏览器操作会话历史的接口,常用在回退按钮。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn1">回到页面1</button>
<button id="btn2">回到页面2</button>
<script>
var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');
btn1.onclick = function() {
history.go(-2);
}
btn2.onclick = function() {
history.go(-1);
}
</script>
</body>
</html>
Location对象
window.location标识浏览器当前所在网址,可以通过属性赋值命名浏览器进行跳转。
面向对象
认识对象
对象的书写:
var obj={
k:v,
k:v,
k:v
};
k和v之间用冒号隔开,每组k和v用逗号隔开,最后一组不用写逗号。
属性的访问:
属性的修改:
属性的创建和删除:
var obj = {
a: 40,
b: 50
};
//如果要创建c
obj.c = 40;
//如果要删除b
delete obj.b;
对象的方法
对象的遍历
for…in…循环
var obj = {
a: 40,
b: 50,
c:60
};
//遍历obj的值
for(var key in obj){
console.log('对象的属性'+key+'的值是'+obj[k]);
}
对象的深浅克隆
浅克隆:只克隆对象的表层,如果对象的某些属性是引用类型值就不会进行浅克隆,只是传递他们的引用。
<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];
}
// 为什么叫浅克隆呢?比如c属性的值是引用类型值,那么本质上obj1和obj2的c属性是内存中的同一个数组,并没有被克隆分开。
obj1.c.push(77);
console.log(obj2); // obj2的c属性这个数组也会被增加77数组
console.log(obj1.c == obj2.c); // true,true就证明了数组是同一个对象
</script>
深克隆:无论对象的属性是否是引用类型都能全部克隆,而不是传递引用。深克隆和数组的深克隆类似需要递归。
<script>
var obj1 = {
a: 1,
b: 2,
c: [33, 44, {
m: 55,
n: 66,
p: [77, 88]
}]
};
//深克隆
function deepClone(obj1) {
// 要判断o是对象还是数组 先判断数组后判断是否对象
if (Array.isArray(obj1)) {
var result = [];
for (var i = 0; i < obj1.length; i++) {
result.push(deepClone(obj1[i]));
}
} else if (typeof obj1 == 'object') {
var result = {};
for (var key in obj1) {
result[key] = deepClone(obj1[key]);
}
} else {
var result = obj1;
}
return result;
}
var obj2 = deepClone(obj1);
console.log(obj2);
console.log(obj1.c == obj2.c); // false
obj1.c.push(99);
console.log(obj2); // obj2不变的,因为没有“藕断丝连”的现象
obj1.c[2].p.push(999);
console.log(obj2); // obj2不变的,因为没有“藕断丝连”的现象
</script>
认识上下文
函数中的上下文
函数中可以使用this关键字表示函数的上下文
函数只有被调用才能确定上下文
上图运行后则是我是undefined我undefined岁了,因为this指向window对象了。
上下文规则
规则一:对象打点调用他的函数,则函数的上下文是这个打点的对象。
<script>
function fn() {
console.log(this.a + this.b);
}
var obj = {
a: 10,
b: 20,
fn: fn
}
obj.fn(); //构成规则一 对象打点调用方法函数() 输出30
</script>
----------------------
<script>
var obj = {
a: 20,
b: 20,
fn: function() {
console.log(this.a + this.b);
}
}
var obj1 = {
a: 10,
b: 20,
fn: obj.fn
}
obj1.fn(); //构成规则一 对象打点调用方法函数() 输出30
</script>
规则二:圆括号直接调用函数,则函数的上下文是window对象。
<script>
var obj1 = {
a: 1,
b: 2,
fn: function() {
console.log(this.a + this.b);
}
};
var a = 3;
var b = 4;
var fn = obj1.fn;
fn(); //使用规则2 输出7
</script>
规则三:
<script>
var arr = ['a', 'b', 'c', function() {
console.log(arr[0]);
}];
arr[3]();
</script>
规则4:
规则5:
规则6:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
border: 1px solid #000;
float: left;
margin-right: 10px;
}
</style>
</head>
<body>
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
<script>
function setColorToRed() {
//备份上下文 this指向的是绑定点击事件的box元素,不是box事件。
var self = this;
setTimeout(function() {
self.style.backgroundColor = 'red';
}, 2000)
}
var box1 = document.getElementById('box1');
var box1 = document.getElementById('box2');
var box1 = document.getElementById('box3');
box1.onclick = setColorToRed;
box2.onclick = setColorToRed;
box3.onclick = setColorToRed;
</script>
</body>
</html>
备份上下文在定时器外面的事件处理函数中,this 就是box1这个元素,此时我们可以备份上下文,把this存为局部变量self,后面的程序就用self 指代box1,还可以用_this 、that等等。
call和apply
call和apply能够指定函数的上下文。
call和apply的区别:
函数中如果带有形参,使用call方法要用逗号罗列参数,apply要用数组包括参数。
用new调用函数四步走
new的调用方式:new 函数()
<script>
// 四步走详解
// 第一步函数体会自动创建出一个空白对象{}
//第二步函数的上下文(this)会指向这个空白对象
//第三步执行函数体中的语句,那么空白对象里面就已经有数据了
//第四步函数会自动返回上下文对象,即是函数没有return语句
function fun() {
this.a = 3;
this.b = 5;
}
var obj = new fun();
console.log(obj);
</script>
构造函数
用new调用的函数,这个函数就被成为“构造函数”,任何函数都可以是构造函数只需要new调用它。构造函数用来完成对象的初始化。构造函数首字母要大写。
构造函数中的ths不是函数本身,是秘密创建的空白对象。
<script>
function People(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
this.sayhello = function() {
console.log('你好,我是' + this.name + '我今年' + this.age + '岁了,我是个' + this.sex + '生');
}
}
var xm = new People('小明', '男', 18);
xm.sayhello();
</script>
类与实例
prototype和原型链查找
任何函数都有prototype属性,prototype属性值是一个对象,他默认拥有constructor属性指回函数。
什么是prototype
普通函数来说的prototype属性没有什么用,而构造函数的prototype属性非常有用。
构造函数的prototype是他的实例的原型
<script>
function People(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
this.sayhello = function() {
console.log('你好,我是' + this.name + '我今年' + this.age + '岁了,我是个' + this.sex + '生');
}
}
console.log(xm.__proto__ === People.prototype); //输出为true,xm的原型是People.prototype
</script>
原型链查找
实例可以打点访问他的原型的属性和方法,被称为“原型链查找”。
<script>
function People(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
this.sayhello = function() {
console.log('你好,我是' + this.name + '我今年' + this.age + '岁了,我是个' + this.sex + '生');
}
}
//构造函数原型添加国籍属性
People.prototype.nationality = '中国';
var xm = new People('小明', '男', 18);
console.log(xm.nationality); //中国
console.log(xm);
xm.sayhello();
</script>
原型链查找:一个对象打点调用一个属性,对象身上并没有这个属性,那么系统会从这个对象的原型上去查找有没有这个属性。
<script>
function People(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
this.sayhello = function() {
console.log('你好,我是' + this.name + '我今年' + this.age + '岁了,我是个' + this.sex + '生');
}
}
//构造函数原型添加国籍属性
People.prototype.nationality = '中国';
var xm = new People('小明', '男', 18);
console.log(xm.nationality); //中国
console.log(xm);
xm.sayhello();
var tom = new People('小刘', '男', 18);
tom.nationality = '福建';
console.log(tom.nationality);//福建,这也是遮蔽效果,会优先采用tom的
</script>
在prototype上添加方法
疑问:三个实例的sayhello函数是内存中的t同一个函数嘛?**不是。**三个实例都有sayhello函数,那么去new这个People构造函数后,会创造一个新的函数,并且绑定到实例身上。
把方法添加到实例的身上的缺点,每个实例和实例的方法函数都是内存中不同的函数会造成内存的浪费。解决方法是将方法写到实例的原型上。
<script>
function People(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
People.prototype.sayhello = function() {
console.log('你好,我是' + this.name + '我今年' + this.age + '岁了,我是个' + this.sex + '生');
}
var xm = new People('小明', 18, '男');
xm.sayhello();
</script>
原型链的终点
原型链终点是object.prototype,终点内置了许多方法,xioaming要调用通过原型链查找到终点进行调用。
<script>
function People() {}
var xiaoming = new People();
console.log(xiaoming.__proto__.__proto__ === Object.prototype); //true xiaoming的原型的原型是否等于原型的终点
</script>
数组的原型链
数组中的push、pop、unshift、shift都是Array.prototype的内置函数。
继承
创建了people类和student类,student类继承了people类,people类也被称为父类,student类继承了父类,所以student类被称为子类,子类继承父类,同时子类比父类多出更多方法更加具体。
使用js的原型链特性来实现继承。
1.首先创建people类,在people类的原型创建两个方法,同时new出people类的实例。
2.创建student类,student类的原型指向new出来的people类的实例(student.prototype=new people()😉,student类new出‘hanmeimei’的实例,那么‘hanmeimei’的原型可以调用到people实例的方法和people原型的方法。
<script>
//人类
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//创建人类原型的方法
People.prototype.sayhello = function() {
console.log('你好,我是' + this.name + '我今年' + this.age + '岁了,我是个' + this.sex + '生');
};
People.prototype.sleep = function() {
console.log(this.name + '开始睡觉,zzzzzz');
};
//创建学生类
function Student(name, age, sex, school, studentNumber) {
this.name = name;
this.age = age;
this.sex = sex;
this.school = school;
this.studentNumber = studentNumber;
}
//创建学生类原型 实现继承
Student.prototype = new People();
//创建学生类的方法
Student.prototype.study = function() {
console.log(this.name + '正在学习');
};
Student.prototype.exam = function() {
console.log(this.name + '正在考试,加油!');
};
//重写(override)父类的方法
Student.prototype.sayhello = function() {
console.log('敬礼你好,我是' + this.name + '我今年' + this.age + '岁了,我是个' + this.sex + '生');
};
//实例化
var hanmeimei = new Student('韩梅梅', 18, '女', '清华大学', '666666');
hanmeimei.sayhello();
hanmeimei.sleep();
hanmeimei.study();
hanmeimei.exam();
</script>
上身到面向对象-红绿灯实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box img {
width: 80px;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
// 定义红绿灯类
function TrafficLight() {
// 颜色属性,一开始都是红色
// 红色1、黄色2、绿色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() {
// 备份上下文,这里的this指的是JS的实例
var self = this;
// 当自己的dom被点击的时候
this.dom.onclick = function() {
// 当被点击的时候,调用自己的changeColor方法
self.changeColor();
};
}
// 改变颜色
TrafficLight.prototype.changeColor = function() {
// 改变自己的color属性,从而有一种“自治”的感觉,自己管理自己,不干扰别的红绿灯
this.color++;
if (this.color == 4) {
this.color = 1;
}
// 光color属性变化没有用,还要更改自己的dom的src属性
this.dom.src = 'images/' + this.color + '.jpg';
};
// 得到盒子
var box = document.getElementById('box');
// 实例化100个
var count = 100;
while (count--) {
new TrafficLight();
}
</script>
</body>
</html>
包装类
Number()、String()、Boolean()分别是数字、字符串、布尔值的包装类。包装类的目的就是让基本类型的值可以从他们的构造函数的prototype上获得方法。
<script>
var a = new Number(123);
var b = new String('我爱你');
var c = new Boolean(true);
console.log(a);
console.log(typeof a); //object
console.log(b);
console.log(typeof b); //object
console.log(c);
console.log(typeof c); //object
console.log(5 + a); //128
console.log(b.slice(0, 1));//爱你
console.log(c && true);//true
</script>
普通的定义变量abc那么他们的值是基本类型值。但是通过new出来的他们的类型是object。但是它可以像普通的类型值进行计算。
Math(数字)对象
Math.round()四舍五入方法
Math.max()得到参数列表最大值
Math.min()得到参数列表最小值
<script>
console.log(Math.max(44, 55, 33, 22)); // 55
console.log(Math.min(44, 55, 33, 22)); // 22
var arr = [3, 4, 4, 3, 2, 2, 1, 3, 5, 7, 4, 3];
console.log(Math.max.apply(null, arr)); // 7
// 在今后学习ES6之后,求数组最大值还可以
console.log(Math.max(...arr)); // 7
</script>
Date日期对象
<script>
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());
</script>
<script>
// 日期对象
var d = new Date();
// 显示时间戳的两种方法。时间戳表示1970年1月1日距离此时的毫秒数
var timestamp1 = d.getTime(); // 精确到毫秒
var timestamp2 = Date.parse(d); // 精确到秒,也是毫秒数,只不过最后三位一定是000
console.log(timestamp1);
console.log(timestamp2);
// 如何把时间戳,比如1601536565000变回日期对象呢?
var dd = new Date(1601536565000);
console.log(dd);
console.log(dd.getFullYear());
</script>