目录
第一章 JavaScript简介
1.1、JavaScript的起源
JavaScript诞生于1995年,它的出现主要是用于处理网页中的前端验证。所谓的前端验证,就是指检查用户输入的内容是否符合一定的规则。比如:用户名的长度,密码的长度,邮箱的格式等。但是,有的同学可能会有疑问,这些验证,后端不也可以进行验证吗?确实,后端程序的确可以进行这些验证,但你要清楚,在1995年那个年代,网速是非常慢的,向后端发送一个请求,浏览器很久才能得到响应,那这无疑是一种非常不好的用户体验。
为了解决前端验证的问题,当时的浏览器巨头NetScape(网景)公司就开发出一种脚本语言,起初命名为LiveScript,后来由于SUN公司的介入更名为了JavaScript。但是你要清楚,Java和JavaScript是没有什么关系的,只不过当时Java非常流行,为了蹭热度,才将LiveScript更名为JavaScript,它们的关系就像雷锋和雷峰塔的关系一样,没啥关系。
但是,浏览器开发商不止网景一家,还有一个大家都知道的公司,微软公司,它们的主打产品是IE(Internet Explorer)浏览器,当网景公司的Netscape Navigator浏览器推出JavaScript语言时,微软就急了啊,好家伙,人网景都推出了专门用于前端验证的语言,不仅大大减少了后端程序的压力,还提高了用户的体验。我微软这么大的公司不也得整一个,在1996年,微软公司在其最新的IE3浏览器中引入了自己对JavaScript的实现JScript。
于是在市面上存在两个版本的JavaScript,一个网景公司的JavaScript和微软的JScript,虽然当时浏览器的巨头是网景,但是网景的浏览器是收费的,虽然微软的IE浏览器在全球的市场份额远远不及网景,但是微软的拳头产品是Windows操作系统,每一个操作系统都自带一个IE浏览器并且免费,那么,未来的发展大家可能也想到了,网景让微软给干倒闭了,1998年11月,网景被美国在线(AOL)收购。
老大哥就是老大哥,为了抢先获得规则制定权,网景最先将JavaScript作为草案提交给欧洲计算机制造商协会,也就是ECMA组织,希望能将JavaScript做成行业标准,最终在网景、SUN以及微软等公司的参与下,由一众程序员和相关组织人员组成的第39技术委员会也就是TC39发布了ECMA-262标准,这个标准定义了名为ECMAScript的全新脚本语言,为啥又来了个ECMAScript?
因为Java是SUN的商标,SUN授权了NetScape可以叫JavaScript,但是ECMA没有SUN的授权就不能叫JavaScript,哪怕NetScape成员特别希望ECMA把它叫做JavaScript,但是ECMA也有成员并不希望这个标准就叫JavaScript,总之经过几轮磋商和博弈,ECMAScript这个名字就定下来。
我们可以简单看一下历史事件发展表:
1.2、JavaScript的组成
ECMAScript是一个标准,而这个标准需要由各个厂商去实现,不同的浏览器厂商对该标准会有不同的实现。
我们已经知道ECMAScript是JavaScript标准,所以一般情况下这两个词我们认为是一个意思。但是实际上JavaScript的含义却要更大一些。一个完整的JavaScript实现应该由以下三个部分构成:
由此我们也知道了我们所要学习的内容就是这三部分,它们具体的含义后边章节会具体介绍。
1.3、JavaScript的特点
解释型语言
JavaScript是一门解释型语言,所谓解释型值语言是指不需要被编译为机器码在执行,而是直接执行。由于少了编译这一步骤,所以解释型语言开发起来尤为轻松,但是解释型语言运行较慢也是它的劣势。不过解释型语言中使用了JIT技术,使得运行速度得以改善。
动态语言
JavaScript是一门动态语言,所谓的动态语言可以暂时理解为在语言中的一切内容都是不确定的。比如一个变量,这一时刻是个整型,下一时刻可能会变成字符串了。当然这个问题我们以后再谈。不过在补充一句动态语言相比静态语言性能上要差一些,不过由于JavaScript中应用的JIT技术,所以JavaScript可能是运行速度最快的动态语言了。
类似于 C 和 Java 的语法结构
JavaScript的语法结构与C和Java很像,向for、if、while等语句和Java的基本上是一模一样的。所以有过C和Java基础的同学学习起来会轻松很多。不过JavaScript和与Java的关系也仅仅是看起来像而已。
基于原型的面向对象
JavaScript是一门面向对象的语言。啥是对象?下次聊。
Java也是一门面向对象的语言,但是与Java不同JavaScript是基于原型的面向对象。啥是原型?下次聊。
严格区分大小写
JavaScript是严格区分大小写的,也就是abc和Abc会被解析器认为是两个不同的东西。
1.4、JavaScript的使用
1.4.1、标签引用
在HTML中在script标签中就可以编写JavaScript代码,以下是一个简单演示。
<script>
alert("Hello,World!");
</script>
1
2
3
1.4.2、文件引用
在一个单独的js文件中也可以编写JavaScript代码,然后在HTML文件中使用script标签进行引用,以下是一个简单演示。
main.html
<script src="main.js"></script>
1
main.js
alert("Hello,World!");
1
1.5、JavaScript的输出
1.5.1、页面输出
如何使用JavaScript向页面输出一句话,请参考以下代码。
<script>
document.write("Hello,World!");
</script>
1
2
3
1.5.2、控制台输出
如何使用JavaScript向控制台输出一句话,请参考以下代码。
注意:页面按F12弹出控制台
<script>
console.log("输出一条日志");//最常用
console.info("输出一条信息");
console.warn("输出一条警告");
console.error("输出一条错误");
</script>
1.5.3、弹出窗口输出
如何使用JavaScript向弹出窗口输出一句话,请参考以下代码。
<script>
alert("Hello,World!");
</script>
1.6、JavaScript的注释
注释中的内容不会被解析器解析执行,但是会在源码中显示,我们一般会使用注释对程序中的内容进行解释。
JS中的注释和Java的的一致,分为两种:
单行注释:// 注释内容
多行注释:/* 注释内容 */
1.6.1、单行注释
<script>
// 这是注释内容
console.log("Hello,World!");
</script>
1.6.2、多行注释
<script>
/**
* 这是注释内容
*/
console.log("Hello,World!");
</script>
第二章 JavaScript基础语法
2.1、标识符
所谓标识符,就是指给变量、函数、属性或函数的参数起名字。
标识符可以是按照下列格式规则组合起来的一或多个字符:
第一个字符必须是一个字母、下划线( _ )或一个美元符号( $ )。
其它字符可以是字母、下划线、美元符号或数字。
按照惯例,ECMAScript 标识符采用驼峰命名法。
标识符不能是关键字和保留字符。
关键字:

保留字符:
其它不建议使用的标识符:

单个单词的标识符举例:
name、age、gender、hobby
多个单词的标识符举例:
studentName、studentAge、studentGender、studentHobby
2.2、字面量和变量
2.2.1、字面量
字面量实际上就是一些固定的值,比如:1、2 、3、true、false、null、NaN、“hello”,字面量都是不可以改变的,由于字面量不是很方便使用,所以在JavaScript中很少直接使用字面量,使用的而是变量。
2.2.2、变量
变量的作用是给某一个值或对象标注名称。比如我们的程序中有一个值123,这个值我们是需要反复使用的,这个时候 我们最好将123这个值赋值给一个变量,然后通过变量去使用123这个值。
变量的声明: 使用var关键字声明一个变量。
var a;
变量的赋值: 使用=为变量赋值。
a = 123;
声明和赋值同时进行:
var a = 123;
2.3、数据类型
2.3.1、类型分类
数据类型决定了一个数据的特征,比如:123和”123”,直观上看这两个数据都是123,但实际上前者是一个数字,而后者是一个字符串。
对于不同的数据类型我们在进行操作时会有很大的不同。
JavaScript中一共有5种基本数据类型:
字符串型(String)
数值型(Number)
布尔型(Boolean)
undefined型(Undefined)
null型(Null)
这5种之外的类型都称为Object,所以总的来看JavaScript中共有六种数据类型。
2.3.2、typeof运算符
使用typeof操作符可以用来检查一个变量的数据类型。
使用方式:
typeof 数据
示例代码:
console.log(typeof 123);
console.log(typeof "Hello,World!");
console.log(typeof true);
console.log(typeof undefined);
console.log(typeof null);
运行结果:
2.3.3、String
String用于表示一个字符序列,即字符串。字符串需要使用 单引号 或 双引号 括起来。
转义字符:
注意:使用typeof运算符检查字符串时,会返回"string"。
2.3.4、Number
Number 类型用来表示整数和浮点数,最常用的功能就是用来表示10进制的整数和浮点数。
Number表示的数字大小是有限的,如果超过了这个范围,则会返回 ±Infinity。
最大值:+1.7976931348623157e+308
最小值:-1.7976931348623157e+308
0以上的最小值:5e-324
特殊的数字:
Infinity:正无穷
-Infinity:负无穷
NaN:非法数字(Not A Number)
其它的进制:
二进制:0b 开头表示二进制,但是,并不是所有的浏览器都支持
八进制:0 开头表示八进制
十六进制:0x 开头表示十六进制
注意:使用typeof检查一个Number类型的数据时(包括NaN 和 Infinity),会返回"number"。
2.3.5、Boolean
布尔型也被称为逻辑值类型或者真假值类型。
布尔型只能够取真(true)和假(false)两种数值。除此以外, 其它的值都不被支持。
2.3.6、Undefined
Undefined 类型只有一个值,即特殊的 undefined。
在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。
注意:使用typeof对没有初始化和没有声明的变量,会返回“undefined”。
2.3.7、Null
Null 类型是第二个只有一个值的数据类型,这个特殊的值是 null。
undefined值实际上是由null值衍生出来的,所以如果比较undefined和null是否相等,会返回true。
注意:从语义上看null表示的是一个空的对象,所以使用typeof检查null会返回一个Object。
2.4、强制类型转换
强制类型转换指将一个数据类型强制转换为其它的数据类型。一般是指,将其它的数据类型转换为String、Number、Boolean。
2.4.1、转换为String类型
将其它数值转换为字符串有三种方式:toString()、String()、 拼串。
方式一:调用被转换数据类型的toString()方法,该方法不会影响到原变量,它会将转换的结果返回,但是注意:null和undefined这两个值没有toString()方法,如果调用它们的方法,会报错。
var a = 123;
a = a.toString();
console.log(a);
console.log(typeof a);
方式二:调用String()函数,并将被转换的数据作为参数传递给函数,使用String()函数做强制类型转换时,对于Number和Boolean实际上就是调用的toString()方法,但是对于null和undefined,就不会调用toString()方法,它会将 null 直接转换为 “null”,将 undefined 直接转换为 “undefined”。
var a = 123;
a = String(a);
console.log(a);
console.log(typeof a);
var b = undefined;
b = String(b);
console.log(b);
console.log(typeof b);
var c = null;
c = String(c);
console.log(c);
console.log(typeof c);
方式三:为任意的数据类型 +""
var a = 123;
a = a + "";
console.log(a);
console.log(typeof a);
2.4.2、转换为Number类型
有三个函数可以把非数值转换为数值:Number()、parseInt() 和parseFloat()。Number()可以用来转换任意类型的数据,而后两者只能用于转换字符串。parseInt()只会将字符串转换为整数,而parseFloat()可以将字符串转换为浮点数。
方式一:使用Number()函数
字符串 --> 数字
如果是纯数字的字符串,则直接将其转换为数字
如果字符串中有非数字的内容,则转换为NaN
如果字符串是一个空串或者是一个全是空格的字符串,则转换为0
布尔 --> 数字
true 转成 1
false 转成 0
null --> 数字
null 转成 0
undefined --> 数字
undefined 转成 NaN
方式二:这种方式专门用来对付字符串,parseInt() 把一个字符串转换为一个整数
var a = "123";
a = parseInt(a);
console.log(a);
console.log(typeof a);
方式三:这种方式专门用来对付字符串,parseFloat() 把一个字符串转换为一个浮点数
var a = "123.456";
a = parseFloat(a);
console.log(a);
console.log(typeof a);
注意:如果对非String使用parseInt()或parseFloat(),它会先将其转换为String然后在操作
2.4.3、转换为Boolean类型
将其它的数据类型转换为Boolean,只能使用Boolean()函数。
使用Boolean()函数
数字 —> 布尔
除了0和NaN,其余的都是true
字符串 —> 布尔
除了空串,其余的都是true
null和undefined都会转换为false
对象也会转换为true
2.5、运算符
运算符也叫操作符,通过运算符可以对一个或多个值进行运算并获取运算结果。
比如:typeof就是运算符,可以来获得一个值的类型,它会将该值的类型以字符串的形式返回(number string boolean undefined object)
2.5.1、算术运算符
算术运算符用于表达式计算。
y=5,下面的表格解释了这些算术运算符:
| 运算符 | 描述 | 例子 | x 运算结果 | y 运算结果 | 在线实例 |
| + | 加法 | x=y+2 | 7 | 5 | 实例 |
| - | 减法 | x=y-2 | 3 | 5 | 实例 |
| * | 乘法 | x=y*2 | 10 | 5 | 实例 |
| / | 除法 | x=y/2 | 2.5 | 5 | 实例 |
| % | 取模(求余数) | x=y%2 | 1 | 5 | 实例 |
| ++ | 自增 | x=++y x=y++ | 6 5 | 6 6 | 实例 |
| – | 自减 | x=–y x=y– | 4 5 | 4 4 | 实例 |
2.5.2、关系运算符
关系运算符在逻辑语句中使用,以测定变量或值是否相等。
x=5,下面的表格解释了比较运算符:
| 运算符 | 描述 | 比较 | 返回值 |
| > | 大于 | x>8 | false |
| < | 小于 | x<8 | true |
| >= | 大于或等于 | x>=8 | false |
| <= | 小于或等于 | x<=8 | true |
2.5.3、赋值运算符
赋值运算符用于给 JavaScript 变量赋值。
x=10 和 y=5,下面的表格解释了赋值运算符:
| 运算符 | 例子 | 等同于 | 运算结果 |
| = | x=y | x=5 | |
| += | x+=y | x=x+y | x=15 |
| -= | x-=y | x=x-y | x=5 |
| *= | x*=y | x=x*y | x=50 |
| /= | x/=y | x=x/y | x=2 |
| %= | x%=y | x=x%y | x=0 |
2.5.4、逻辑运算符
逻辑运算符用于测定变量或值之间的逻辑。
给定 x=6 以及 y=3,下表解释了逻辑运算符:
| 运算符 | 描述 | 例子 |
| && | and | (x<10&&y>1)为true |
| || | or | (x==5||y==5)为true |
| ! | not | !(x==y)为true |
关于逻辑运算符我们可以具体探讨一下:
&& 与:&&可以对符号两侧的值进行与运算并返回结果,运算规则如下:
两个值中只要有一个值为false,就返回false,只有两个值都为true时,才会返回true
JS中的“与”属于短路的与,如果第一个值为false,则不会检查第二个值
非布尔值时:如果两个都为true,则返回第二个值,如果两个值中有false,则返回靠前的false的值
|| 或:||可以对符号两侧的值进行或运算并返回结果,运算规则如下:
两个值中只要有一个true,就返回true,只有两个值都为false,才会返回false
JS中的“或”属于短路的或,如果第一个值为true,则不会检查第二个值
非布尔值时:如果两个都为false ,则返回第二个值,如果两个值中有true,则返回靠前的true的值
! 非:!可以用来对一个值进行非运算,所谓非运算就是对一个布尔值进行取反操作,true变false,false变true,运算规则如下:
如果对一个值进行两次取反,它不会变化
非布尔值时:先会将其转换为布尔值,然后再取反,所以我们可以利用该特点,来将一个其它的数据类型转换为布尔值,可以为一个任意数据类型取两次反,来将其转换为布尔值,原理和Boolean()函数一样
2.5.5、比较运算符
比较运算符用来比较两个值是否相等,如果相等会返回true,否则返回false。
使用 == 来做相等运算
当使用==来比较两个值时,如果值的类型不同,则会自动进行类型转换,将其转换为相同的类型,然后在比较
使用 != 来做不相等运算
不相等用来判断两个值是否不相等,如果不相等返回true,否则返回false,不相等也会对变量进行自动的类型转换,如果转换后相等它也会返回false
使用 === 来做全等运算
用来判断两个值是否全等,它和相等类似,不同的是它不会做自动的类型转换,如果两个值的类型不同,直接返回false
使用 !== 来做不全等运算
用来判断两个值是否不全等,它和不等类似,不同的是它不会做自动的类型转换,如果两个值的类型不同,直接返回true
2.5.6、条件运算符
JavaScript 还包含了基于某些条件对变量进行赋值的条件运算符。
语法:variablename=(condition)?value1:value2;
举例:result=(age<18)?"年龄太小":"年龄合适";
执行流程:如果condition为true,则执行语句1,并返回执行结果,如果为false,则执行语句2,并返回执行结果。
2.5.7、逗号运算符
使用逗号可以在一条语句中执行多次操作。
比如:var num1=1, num2=2, num3=3;
使用逗号运算符分隔的语句会从左到右顺 序依次执行。
2.6、运算符优先级
运算符优先级由上到下依次减小,对于同级运算符,采用从左向右依次执行的方法。

2.7、代码块
2.7.1、语句
前边我所说表达式和运算符等内容可以理解成是我们一 门语言中的单词,短语。而语句(statement)就是我们这个语言中一句一句完 整的话了。语句是一个程序的基本单位,JavaScript的程序就是由一条一条语句构成的,每一条语句使用;结尾。
JavaScript中的语句默认是由上至下顺序执行的,但是我们也可以通过一些流程控制语句来控制语句的执行顺序。
2.7.2、代码块
代码块是在大括号 {} 中所写的语句,以此将多条语句的集合视为一条语句来使用。
例如:
{
var a = 123;
a++;
alert(a);
}
我们一般使用代码块将需要一起执行的语句进行分组,需要注意的是,代码块结尾不需要加 分号。
2.8、条件语句
条件语句是通过判断指定表达式的值来决定执行还是跳过某些语句,最基本的条件语句:
if…else
switch…case
2.8.1、if…else
if…else语句是一种最基本的控制语句,它让JavaScript可以有条件的执行语句。
第一种形式:
if(expression)
statement
var age = 16;
if (age < 18) {
console.log("未成年");
}
第二种形式:
if(expression){
statement
}else{
statement
}
var age = 16;
if (age < 18) {
console.log("未成年");
} else {
console.log("已成年");
}
第三种形式:
if(expression1){
statement
}else if(expression2){
statement
}else{
statement }
var age = 18;
if (age < 18) {
console.log("小于18岁了");
} else if (age == 18) {
console.log("已经18岁了");
} else {
console.log("大于18岁了")
}
2.8.2、switch…case
switch…case是另一种流程控制语句。
switch语句更适用于多条分支使用同一条语句的情况。
语法格式:
switch (语句) {
case 表达式1:
语句...
case 表达式2:
语句...
default:
语句...
}
注意:需要注意的是一旦符合case的条件程序会一直运行到结束,所以我们一般会在case中添加break作为语句的结束。
案例演示1:根据today的数值,输出今天是星期几。
var today = 1;
switch (today) {
case 1:
console.log("星期一");
break;
case 2:
console.log("星期二");
break;
case 3:
console.log("星期三");
break;
case 4:
console.log("星期四");
break;
case 5:
console.log("星期五");
break;
case 6:
console.log("星期六");
break;
case 7:
console.log("星期日");
break;
default:
console.log("输入错误");
}
案例演示2:根据month的数值,输出对应月份的天数,2月默认28天。
var month = 10;
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
console.log("31天");
break;
case 4:
case 6:
case 9:
case 11:
console.log("30天");
break;
case 2:
console.log("28天");
break;
default:
console.log("输入错误");
}
2.9、循环语句
循环语句和条件语句一样,也是基本的控制语句,只要满足一定的条件将会一直执行,最基本的循环语句:
while
do…while
for
2.9.1、while
while语句是一个最基本的循环语句,while语句也被称为while循环。
语法格式:
while(条件表达式){
语句...
}
案例演示:输出1-10。
var i = 1;
while (i <= 10) {
console.log(i);
i++;
}
2.9.2、do…while
do…while和while非常类似,只不过它会在循环的尾部而不是顶部检查表达式的值,因此,do…while循环会至少执行一次。相比于while,do…while的使用情况并不 是很多。
语法格式:
do{
语句...
}while(条件表达式);
案例演示:输出1-10。
var i = 1;
do {
console.log(i);
i++;
} while (i <= 10);
2.9.3、for
for语句也是循环控制语句,我们也称它为for循环。大部分循环都会有一个计数器用以控制循环执行的次数, 计数器的三个关键操作是初始化、检测和更新。for语句 就将这三步操作明确为了语法的一部分。
语法格式:
for(初始化表达式 ; 条件表达式 ; 更新表达式){
语句...
}
案例演示:输出1-10。
for (var i = 1; i <= 10; i++) {
console.log(i);
}
2.9.4、跳转控制
break:结束最近的一次循环,可以在循环和switch语句中使用。
continue:结束本次循环,执行下一次循环,只能在循环中使用。
那如果我们想要跳出多层循环或者跳到指定位置该怎么办呢?可以为循环语句创建一个label,来标识当前的循环,如下例子:
outer: for (var i = 0; i < 10; i++) {
for (var j = 0; j < 10; j++) {
if (j == 5) {
break outer;
}
console.log(j);
}
}
2.10、对象基础
2.10.1、概述
Object类型,我们也称为一个对象,是JavaScript中的引用数据类型。它是一种复合值,它将很多值聚合到一起,可以通过名字访问这些值。对象也可以看做是属性的无序集合,每个属性都是一个名/值对。对象除了可以创建自有属性,还可以通过从一个名为原型的对象那里继承属性。除了字符串、数字、true、false、null和undefined之外,JavaScript中的值都是对象。
2.10.2、创建对象
创建对象有两种方式:
第一种方式:
var person = new Object();
person.name = "孙悟空";
person.age = 18;
console.log(person);
第二种方式:
var person = {
name: "孙悟空",
age: 18
};
console.log(person);
2.10.3、访问属性
访问属性的两种方式:
第一种方式:使用 . 来访问
对象.属性名
第二种方式:使用 [] 来访问
对象[‘属性名’]
2.10.4、删除属性
删除对象的属性可以使用delete关键字,格式如下:
delete 对象.属性名
案例演示:
var person = new Object();
person.name = "孙悟空";
person.age = 18;
console.log(person);
delete person.name
console.log(person);
2.10.5、遍历对象
枚举遍历对象中的属性,可以使用for … in语句循环,对象中有几个属性,循环体就会执行几次。
语法格式:
for (var 变量 in 对象) {
}
案例演示:
var person = {
name: "zhangsan",
age: 18
}
for (var personKey in person) {
var personVal = person[personKey];
console.log(personKey + ":" + personVal);
}
2.10.6、数据类型梳理
2.10.6.1、基本数据类型
JavaScript中的变量可能包含两种不同数据类型的值:基本数据类型和引用数据类型。
JavaScript中一共有5种基本数据类型:String、Number、 Boolean、Undefined、Null。
基本数据类型的值是无法修改的,是不可变的。
基本数据类型的比较是值的比较,也就是只要两个变量的值相等,我们就认为这两个变量相等。
2.10.6.2、引用数据类型
引用类型的值是保存在内存中的对象。
当一个变量是一个对象时,实际上变量中保存的并不是对象本身,而是对象的引用。
当从一个变量向另一个变量复制引用类型的值时,会将对象的引用复制到变量中,并不是创建一个新的对象。
这时,两个变量指向的是同一个对象。因此,改变其中一个变量会影响另一个。
2.10.7、栈和堆梳理
JavaScript在运行时数据是保存到栈内存和堆内存当中的。
简单来说栈内存用来保存变量和基本类型,堆内存是用来保存对象。
我们在声明一个变量时,实际上就是在栈内存中创建了一个空间用来保存变量。
如果是基本类型则在栈内存中直接保存,如果是引用类型则会在堆内存中保存,变量中保存的实际上对象在堆内存中的地址。
当我们写了下边这几句代码的时候,栈内存和堆内存的结构如下:
var a = 123;
var b = true;
var c = "hello";
var d = {name: 'sunwukong', age: 18};
栈的特点:先进后出,后进先出

2.11、函数
2.11.1、概述
函数是由一连串的子程序(语句的集合)所组成的,可以被外部程序调用,向函数传递参数之后,函数可以返回一定的值。
通常情况下,JavaScript代码是自上而下执行的,不过函数体内部的代码则不是这样。如果只是对函数进行了声明,其中的代码并不会执行,只有在调用函数时才会执行函数体内部的代码。
这里要注意的是JavaScript中的函数也是一个对象,使用typeof检查一个函数对象时,会返回function。
2.11.2、函数创建
使用 函数对象 来创建一个函数(几乎不用)
语法格式:
var 函数名 = new Function("执行语句");
示例代码:
var fun = new Function("console.log('这是我的第一个函数');");
使用 函数声明 来创建一个函数(比较常用)
语法格式:
function 函数名([形参1,形参2,...,形参N]) {
语句...
}
示例代码:
function fun(){
console.log("这是我的第二个函数");
}
使用 函数表达式 来创建一个函数(比较常用)
语法格式:
var 函数名 = function([形参1,形参2,...,形参N]) {
语句....
}
示例代码:
var fun = function() {
console.log("这是我的第三个函数");
}
2.11.3、函数调用
对于无参函数调用:
// 函数声明
var fun = function () {
console.log("哈哈,我执行啦!");
}
// 函数调用
fun();
对于有参函数调用:
// 函数声明
var sum = function (num1, num2) {
var result = num1 + num2;
console.log("num1 + num2 = " + result);
}
// 函数调用
sum(10, 20);
2.11.4、函数参数
JS中的所有的参数传递都是按值传递的,也就是说把函数外部的值赋值给函数内部的参数,就和把值从一个变量赋值给另一个变量是一样的,在调用函数时,可以在()中指定实参(实际参数),实参将会赋值给函数中对应的形参
调用函数时,解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能,则需要对参数进行类型的检查,函数的实参可以是任意的数据类型
调用函数时,解析器也不会检查实参的数量,多余实参不会被赋值,如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined
2.11.5、函数返回值
可以使用 return 来设置函数的返回值,return后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。
注意:在函数中return后的语句都不会执行,如果return语句后不跟任何值就相当于返回一个undefined,如果函数中不写return,则也会返回undefined,return后可以跟任意类型的值
语法格式:return 值
案例演示:
function sum(num1, num2) {
return num1 + num2;
}
var result = sum(10, 20);
console.log(result);
2.11.6、嵌套函数
嵌套函数:在函数中声明的函数就是嵌套函数,嵌套函数只能在当前函数中可以访问,在当前函数外无法访问。
案例演示:
function fu() {
function zi() {
console.log("我是儿子")
}
zi();
}
fu();
2.11.7、匿名函数
匿名函数:没有名字的函数就是匿名函数,它可以让一个变量来接收,也就是用 “函数表达式” 方式创建和接收。
案例演示:
var fun = function () {
alert("我是一个匿名函数");
}
fun();
2.11.8、立即执行函数
立即执行函数:函数定义完,立即被调用,这种函数叫做立即执行函数,立即执行函数往往只会执行一次。
案例演示:
(function () {
alert("我是一个匿名函数");
})();
2.11.9、对象中的函数
对象的属性值可以是任何的数据类型,也可以是个函数。
如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法,调用这个函数就说调用对象的方法(method)。
注意:方法和函数只是名称上的区别,没有其它别的区别
案例演示:
var person = {
name: "zhangsan",
age: 18,
sayHello: function () {
console.log(name + " hello")
}
}
person.sayHello();
2.11.10、this对象
解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的上下文对象,根据函数的调用方式的不同,this会指向不同的对象
以函数的形式调用时,this永远都是window
以方法的形式调用时,this就是调用方法的那个对象
案例演示:
//创建一个全局变量name
var name = "全局变量name";
//创建一个函数
function fun() {
console.log(this.name);
}
//创建一个对象
var obj = {
name: "孙悟空",
sayName: fun
};
//我们希望调用obj.sayName()时可以输出obj的名字而不是全局变量name的名字
obj.sayName();
2.12、对象进阶
2.12.1、用工厂方法创建对象
我们之前已经学习了如何创建一个对象,那我们要是想要创建多个对象又该怎么办?聪明的同学可能会说,直接在写几个对象不就好了吗?比如下边的代码:
var person1 = {
name: "孙悟空",
age: 18,
sayName: function () {
console.log(this.name);
}
};
var person2 = {
name: "猪八戒",
age: 19,
sayName: function () {
console.log(this.name);
}
};
var person3 = {
name: "沙和尚",
age: 20,
sayName: function () {
console.log(this.name);
}
};
console.log(person1);
console.log(person2);
console.log(person3);
的确,上述代码确实可以创建多个对象,但是,这样的解决方案真的好吗?对于少量对象可能使用,我们假设说,要用循环创建1000个对象,那你这种办法似乎就没用了,我们可以这么做,如下代码:
// 使用工厂模式创建对象
function createPerson() {
// 创建新的对象
var obj = new Object();
// 设置对象属性
obj.name = "孙悟空";
obj.age = 18;
// 设置对象方法
obj.sayName = function () {
console.log(this.name);
};
//返回新的对象
return obj;
}
var person1 = createPerson();
var person2 = createPerson();
var person3 = createPerson();
console.log(person1);
console.log(person2);
console.log(person3);
上述代码看起来更加简洁,但是你会发现每一个人都是孙悟空,我们要是想要给每一个人不同的属性值,请参考:
// 使用工厂模式创建对象
function createPerson(name, age) {
// 创建新的对象
var obj = new Object();
// 设置对象属性
obj.name = name;
obj.age = age;
// 设置对象方法
obj.sayName = function () {
console.log(this.name);
};
//返回新的对象
return obj;
}
var person1 = createPerson("孙悟空", 18);
var person2 = createPerson("猪八戒", 19);
var person3 = createPerson("沙和尚", 20);
console.log(person1);
console.log(person2);
console.log(person3);
现在再看上述代码,发现好像已经完美的解决了创建多个对象的难题,那我们是不是可以用循环批量创建1000个对象了呢?那我们就来试试:
// 使用工厂模式创建对象
function createPerson(name, age) {
// 创建新的对象
var obj = new Object();
// 设置对象属性
obj.name = name;
obj.age = age;
// 设置对象方法
obj.sayName = function () {
console.log(this.name);
};
//返回新的对象
return obj;
}
for (var i = 1; i <= 1000; i++) {
var person = createPerson("person" + i, 18);
console.log(person);
}
这样我们就实现了批量创建对象的功能,至于对象的名称和年龄,我们可以通过名称数组和年龄数组来获取,但这并不是我们本小节的重点,我们就忽略了。
2.12.2、用构造函数创建对象
在前一节中,我们学会了使用工厂模式创建对象,但是,你会发现我们所创建的对象类型都是Object,具体代码如下:
// 使用工厂模式创建对象
function createPerson(name, age) {
// 创建新的对象
var obj = new Object();
// 设置对象属性
obj.name = name;
obj.age = age;
// 设置对象方法
obj.sayName = function () {
console.log(this.name);
};
//返回新的对象
return obj;
}
for (var i = 1; i <= 1000; i++) {
var person = createPerson("person" + i, 18);
console.log(typeof person);
}

那这有问题吗?看起来有,看起来好像又没有,每创建一个都是对象,但是在实际生活中,人应该是一个确定的类别,属于人类,对象是一个笼统的称呼,万物皆对象,它并不能确切的指明当前对象是人类,那我们要是既想实现创建对象的功能,同时又能明确所创建出来的对象是人类,那么似乎问题就得到了解决,这就用到了构造函数,每一个构造函数你都可以理解为一个类别,用构造函数所创建的对象我们也成为类的实例,那我们来看看是如何做的:
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
// 设置对象的方法
this.sayName = function () {
console.log(this.name);
};
}
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
console.log(person1);
console.log(person2);
console.log(person3);

那这构造函数到底是什么呢?我来解释一下:
构造函数:构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写,构造函数和普通函数的还有一个区别就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
那构造函数是怎么执行创建对象的过程呢?我再来解释一下:
1、调用构造函数,它会立刻创建一个新的对象
2、将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
3、逐行执行函数中的代码
4、将新建的对象作为返回值返回
你会发现构造函数有点类似工厂方法,但是它创建对象和返回对象都给我们隐藏了,使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象,称为是该类的实例。
现在,this又出现了一种新的情况,为了不让大家混淆,我再来梳理一下:
当以函数的形式调用时,this是window
当以方法的形式调用时,谁调用方法this就是谁
当以构造函数的形式调用时,this就是新创建的那个对象
我们可以使用 instanceof 运算符检查一个对象是否是一个类的实例,它返回true或false
语法格式:
对象 instanceof 构造函数
案例演示:
console.log(person1 instanceof Person);
2.12.3、原型
在前一节中,我们学习了使用构造函数的方式进行创建对象,但是,它还是存在一个问题,那就是,你会发现,每一个对象的属性不一样这是一定的,但是它的方法似乎好像是一样的,如果我创建1000个对象,那岂不是内存中就有1000个相同的方法,那要是有10000个,那对内存的浪费可不是一点半点的,我们有没有什么好的办法解决,没错,我们可以把函数抽取出来,作为全局函数,在构造函数中直接引用就可以了,上代码:
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
// 设置对象的方法
this.sayName = sayName
}
// 抽取方法为全局函数
function sayName() {
console.log(this.name);
}
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
person1.sayName();
person2.sayName();
person3.sayName();

但是,在全局作用域中定义函数却不是一个好的办法,为什么呢?因为,如果要是涉及到多人协作开发一个项目,别人也有可能叫sayName这个方法,这样在工程合并的时候就会导致一系列的问题,污染全局作用域,那该怎么办呢?有没有一种方法,我只在Person这个类的全局对象中添加一个函数,然后在类中引用?答案肯定是有的,这就需要原型对象了,我们先看看怎么做的,然后在详细讲解原型对象。
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
}
// 在Person类的原型对象中添加方法
Person.prototype.sayName = function() {
console.log(this.name);
};
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
person1.sayName();
person2.sayName();
person3.sayName();

那原型(prototype)到底是什么呢?
我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象,即显式原型,原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__(隐式原型)来访问该属性。当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。
以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。
2.12.4、原型链
访问一个对象的属性时,先在自身属性中查找,找到返回, 如果没有,再沿着__proto__这条链向上查找,找到返回,如果最终没找到,返回undefined,这就是原型链,又称隐式原型链,它的作用就是查找对象的属性(方法)。
我们使用一张图来梳理一下上一节原型案例的代码:
注意:Object对象是所有对象的祖宗,Object的原型对象指向为null,也就是没有原型对象
2.12.5、toString方法
toString()函数用于将当前对象以字符串的形式返回。该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法,所有主流浏览器均支持该函数。
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
}
//创建对象的一个实例对象
var p = new Person("张三", 20);
console.log(p.toString());

JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要。
| 类型 | 行为描述 |
| String | 返回 String 对象的值。 |
| Number | 返回 Number 的字符串表示。 |
| Boolean | 如果布尔值是true,则返回"true"。否则返回"false"。 |
| Object | (默认) 返回"[object ObjectName]",其中 ObjectName 是对象类型的名称。 |
| Array | 将 Array 的每个元素转换为字符串,并将它们依次连接起来,两个元素之间用英文逗号作为分隔符进行拼接。 |
| Date | 返回日期的文本表示。 |
| Error | 返回一个包含相关错误信息的字符串。 |
| Function | 返回如下格式的字符串,其中 functionname 是一个函数的名称 此函数的 toString 方法被调用: “function functionname() { [native code] }” |
注意:这里我们只是演示toString()方法,其它的一些没有讲到的知识后边会将,我们只看效果就可。
// 字符串
var str = "Hello";
console.log(str.toString());
// 数字
var num = 15.26540;
console.log(num.toString());
// 布尔
var bool = true;
console.log(bool.toString());
// Object
var obj = {name: "张三", age: 18};
console.log(obj.toString());
// 数组
var array = ["CodePlayer", true, 12, -5];
console.log(array.toString());
// 日期
var date = new Date(2013, 7, 18, 23, 11, 59, 230);
console.log(date.toString());
// 错误
var error = new Error("自定义错误信息");
console.log(error.toString());
// 函数
console.log(Function.toString());
2.12.6、hasOwnProperty方法
前边章节我们学过,如何遍历一个对象所有的属性和值,那我们要是判断当前对象是否包含指定的属性或方法可以使用 in 运算符来检查,如下代码演示:
// 创造一个构造函数
function MyClass() {
}
// 向MyClass的原型中添加一个name属性
MyClass.prototype.name = "我是原型中的名字";
// 创建一个MyClass的实例
var mc = new MyClass();
mc.age = 18;
// 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
console.log("age" in mc);
console.log("name" in mc);

如果我只想要检查自身对象是否含有某个方法或属性,我们可以使用Object的hasOwnProperty()方法,它返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。如下代码演示:
// 创造一个构造函数
function MyClass() {
}
// 向MyClass的原型中添加一个name属性
MyClass.prototype.name = "我是原型中的名字";
// 创建一个MyClass的实例
var mc = new MyClass();
mc.age = 18;
// 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
console.log("age" in mc);
console.log("name" in mc);
// 可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性,使用该方法只有当对象自身中含有属性时,才会返回true
console.log(mc.hasOwnProperty("age"));
console.log(mc.hasOwnProperty("name"));

有同学可能会有疑问,我的这个MyClass类对象中没有hasOwnProperty这个方法啊,它是哪来的?对了,就是原型中的,在执行方法的时候它会通过原型链进行查找,这个方法是Object的特有方法,如下代码演示:
// 创造一个构造函数
function MyClass() {
}
// 向MyClass的原型中添加一个name属性
MyClass.prototype.name = "我是原型中的名字";
// 创建一个MyClass的实例
var mc = new MyClass();
mc.age = 18;
// 检查当前对象
console.log(mc.hasOwnProperty("hasOwnProperty"));
// 检查当前对象的原型对象
console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));
// 检查当前对象的原型对象的原型对象
console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"));

2.12.7、对象继承
前边我们一直在说继承,那什么是继承?它有什么作用?如何实现继承?将会是本章节探讨的问题。
面向对象的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是在JavaScript中没有类的概念,前边我们说所的类只是我们自己这么叫,大家要清楚。因此它的对象也与基于类的对象有所不同。实际上,JavaScript语言是通过一种叫做原型(prototype)的方式来实现面向对象编程的。
那实现继承有一个最大的好处就是子对象可以使用父对象的属性和方法,从而简化了一些代码。
JavaScript有六种非常经典的对象继承方式,但是我们只学习前三种:
原型链继承
借用构造函数继承
组合继承(重要)
原型式继承
寄生式继承
寄生组合式继承
2.12.7.1、原型链继承
核心思想: 子类型的原型为父类型的一个实例对象
基本做法:
定义父类型构造函数
给父类型的原型添加方法
定义子类型的构造函数
创建父类型的对象赋值给子类型的原型
将子类型原型的构造属性设置为子类型
给子类型原型添加方法
创建子类型的对象: 可以调用父类型的方法
案例演示:
// 定义父类型构造函数
function SupperType() {
this.supProp = 'Supper property';
}
// 给父类型的原型添加方法
SupperType.prototype.showSupperProp = function () {
console.log(this.supProp);
};
// 定义子类型的构造函数
function SubType() {
this.subProp = 'Sub property';
}
// 创建父类型的对象赋值给子类型的原型
SubType.prototype = new SupperType();
// 将子类型原型的构造属性设置为子类型
SubType.prototype.constructor = SubType;
// 给子类型原型添加方法
SubType.prototype.showSubProp = function () {
console.log(this.subProp)
};
// 创建子类型的对象: 可以调用父类型的方法
var subType = new SubType();
subType.showSupperProp();
subType.showSubProp();
缺点描述:
原型链继承多个实例的引用类型属性指向相同,一个实例修改了原型属性,另一个实例的原型属性也会被修改
不能传递参数
继承单一
2.12.7.2、借用构造函数继承
核心思想: 使用.call()和.apply()将父类构造函数引入子类函数,使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类
基本做法:
1、定义父类型构造函数
2、定义子类型的构造函数
3、给子类型的原型添加方法
4、创建子类型的对象然后调用
案例演示:
借用构造函数继承的重点就在于SuperType**.call(this, name)**,调用了SuperType构造函数,这样,SubType的每个实例都会将SuperType中的属性复制一份。
// 定义父类型构造函数
function SuperType(name) {
this.name = name;
this.showSupperName = function () {
console.log(this.name);
};
}
// 定义子类型的构造函数
function SubType(name, age) {
// 在子类型中调用call方法继承自SuperType
SuperType.call(this, name);
this.age = age;
}
// 给子类型的原型添加方法
SubType.prototype.showSubName = function () {
console.log(this.name);
};
// 创建子类型的对象然后调用
var subType = new SubType("孙悟空", 20);
subType.showSupperName();
subType.showSubName();
console.log(subType.name);
console.log(subType.age);

缺点描述:
1、只能继承父类的实例属性和方法,不能继承原型属性和方法
2、无法实现构造函数的复用,每个子类都有父类实例函数的副本,影响性能,代码会臃肿
2.12.7.3、组合继承
核心思想: 原型链+借用构造函数的组合继承
基本做法:
1、利用原型链实现对父类型对象的方法继承
2、利用super()借用父类型构建函数初始化相同属性
案例演示:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.setName = function (name) {
this.name = name;
};
function Student(name, age, price) {
Person.call(this, name, age); // 为了得到父类型的实例属性和方法
this.price = price; // 添加子类型私有的属性
}
Student.prototype = new Person(); // 为了得到父类型的原型属性和方法
Student.prototype.constructor = Student; // 修正constructor属性指向
Student.prototype.setPrice = function (price) { // 添加子类型私有的方法
this.price = price;
};
var s = new Student("孙悟空", 24, 15000);
console.log(s.name, s.age, s.price);
s.setName("猪八戒");
s.setPrice(16000);
console.log(s.name, s.age, s.price);

缺点描述:
1、父类中的实例属性和方法既存在于子类的实例中,又存在于子类的原型中,不过仅是内存占用,因此,在使用子类创建实例对象时,其原型中会存在两份相同的属性和方法 。
注意:这个方法是JavaScript中最常用的继承模式。
2.12.8、垃圾回收
垃圾回收(GC):就像人生活的时间长了会产生垃圾一样,程序运行过程中也会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢,所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾。
当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作,我们需要做的只是要将不再使用的对象设置null即可。
案例演示:
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
}
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
person1 = null;
person2 = null;
person3 = null;
2.13、作用域
作用域指一个变量的作用的范围,在JS中一共有两种作用域:
1、全局作用域
2、函数作用域
2.13.1、声明提前
变量的声明提前:使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值),但是如果声明变量时不使用var关键字,则变量不会被声明提前
函数的声明提前:使用函数声明形式创建的函数 function 函数名(){} ,它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数。使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用
2.13.2、作用域
2.13.2.1、全局作用域
直接编写在script标签中的JavaScript代码,都在全局作用域
全局作用域在页面打开时创建,在页面关闭时销毁
在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建,我们可以直接使用
在全局作用域中:
创建的变量都会作为window对象的属性保存
创建的函数都会作为window对象的方法保存
全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到
2.13.2.2、函数作用域
调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
每调用一次函数就会创建一个新的函数作用域,它们之间是互相独立的
在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
在函数中要访问全局变量可以使用window对象
作用域链:当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使 用,如果没有则向上一级作用域中寻找,直到找到全局作用域,如果全局作用域中依然没有找到,则会报错ReferenceError
2.13.3、作用域链
多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外),查找变量时就是沿着作用域链来查找的。
查找一个变量的查找规则:
1、在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2
2、在上一级作用域的执行上下文中查找对应的属性,如果有直接返回,否则进入3
3、再次执行2的相同操作,直到全局作用域,如果还找不到就抛出找不到的ReferenceError异常
第三章 JavaScript常用对象
3.1、数组对象
3.1.1、概述
数组也是对象的一种,数组是一种用于表达有顺序关系的值的集合的语言结构,也就是同类数据元素的有序集合。
数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据。但是在JavaScript中是支持数组可以是不同的元素,这跟JavaScript的弱类型有关,此处不用纠结,我们大多数时候都是相同类型元素的集合。数组内的各个值被称作元素,每一个元素都可以通过索引(下标)来快速读取,索引是从零开始的整数。
使用typeof检查一个数组对象时,会返回object。
3.1.2、创建数组
3.1.2.1、使用对象创建
同类型有序数组创建:
var arr = new Array();
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
arr[5] = 6;
arr[6] = 7;
arr[7] = 8;
arr[8] = 9;
不同类型有序数组创建:
var arr = new Array();
arr[0] = 1;
arr[1] = "2";
arr[2] = 3;
arr[3] = "4";
arr[4] = 5;
arr[5] = "6";
arr[6] = 7;
arr[7] = "8";
arr[8] = 9;
3.1.2.2、使用字面量创建
同类型有序数组创建:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
不同类型有序数组创建:
var arr = [1, "2", 3, "4", 5, "6", 7, "8", 9];
3.1.3、遍历数组
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
3.1.4、数组属性
constructor属性演示:返回创建数组对象的原型函数
var arr = [1,2,3,4];
console.log(arr.constructor);
length属性演示:设置或返回数组元素的个数
var arr = [1,2,3,4];
console.log(arr.length);

3.1.5、数组方法
push()方法演示:该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度
var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.push("唐僧", "蜘蛛精", "白骨精", "玉兔精");
console.log(arr);
console.log(result);

pop()方法演示:该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回
var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.pop();
console.log(arr);
console.log(result);

unshift()方法演示:该方法向数组开头添加一个或多个元素,并返回新的数组长度
var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.unshift("牛魔王", "二郎神");
console.log(arr);
console.log(result);

shift()方法演示:该方法可以删除数组的第一个元素,并将被删除的元素作为返回值返回
var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.shift();
console.log(arr);
console.log(result);

forEach()方法演示:该方法可以用来遍历数组
forEach()方法需要一个函数作为参数,像这种函数,由我们创建但是不由我们调用的,我们称为回调函数。数组中有几个元素函数就会执行几次,每次执行时,浏览器会将遍历到的元素,以实参的形式传递进来,我们可以来定义形参,来读取这些内容,浏览器会在回调函数中传递三个参数:
第一个参数:就是当前正在遍历的元素
第二个参数:就是当前正在遍历的元素的索引
第三个参数:就是正在遍历的数组
注意:这个方法只支持IE8以上的浏览器,IE8及以下的浏览器均不支持该方法,所以如果需要兼容IE8,则不要使用forEach(),还是使用for循环来遍历数组。
var arr = ["孙悟空", "猪八戒", "沙和尚"];
arr.forEach(function (value, index, obj) {
console.log(value + " #### " + index + " #### " + obj);
});

slice()方法演示:该方法可以用来从数组提取指定元素,该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回
参数:
第一个参数:截取开始的位置的索引,包含开始索引
第二个参数:截取结束的位置的索引,不包含结束索引,第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
注意:索引可以传递一个负值,如果传递一个负值,则从后往前计算,-1代表倒数第一个,-2代表倒数第二个。
var arr = ["孙悟空", "猪八戒", "沙和尚", "唐僧", "白骨精"];
var result = arr.slice(1, 4);
console.log(result);
result = arr.slice(3);
console.log(result);
result = arr.slice(1, -2);
console.log(result);

splice()方法演示:该方法可以用于删除数组中的指定元素,该方法会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回
参数:
第一个参数:表示开始位置的索引
第二个参数:表示要删除的元素数量
第三个参数及以后参数:可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边
var arr = ["孙悟空", "猪八戒", "沙和尚", "唐僧", "白骨精"];
var result = arr.splice(3, 2);
console.log(arr);
console.log(result);
result = arr.splice(1, 0, "牛魔王", "铁扇公主", "红孩儿");
console.log(arr);
console.log(result);

concat()方法演示:该方法可以连接两个或多个数组,并将新的数组返回,该方法不会对原数组产生影响
var arr = ["孙悟空", "猪八戒", "沙和尚"];
var arr2 = ["白骨精", "玉兔精", "蜘蛛精"];
var arr3 = ["二郎神", "太上老君", "玉皇大帝"];
var result = arr.concat(arr2, arr3, "牛魔王", "铁扇公主");
console.log(result);

join()方法演示:该方法可以将数组转换为一个字符串,该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回,在join()中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符,如果不指定连接符,则默认使用,作为连接符
var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.join("@-@");
console.log(result);

reverse()方法演示:该方法用来反转数组(前边的去后边,后边的去前边),该方法会直接修改原数组
var arr = ["孙悟空", "猪八戒", "沙和尚"];
arr.reverse();
console.log(arr);

sort()方法演示:该方法可以用来对数组中的元素进行排序,也会影响原数组,默认会按照Unicode编码进行排序
var arr = ["b", "c", "a"];
arr.sort();
console.log(arr);

注意:即使对于纯数字的数组,使用sort()排序时,也会按照Unicode编码来排序,所以对数字进排序时,可能会得到错误的结果。
var arr = [1, 3, 2, 11, 5, 6];
arr.sort();
console.log(arr);

我们可以自己来指定排序的规则,我们可以在sort()添加一个回调函数,来指定排序规则,回调函数中需要定义两个形参,浏览器将会分别使用数组中的元素作为实参去调用回调函数,使用哪个元素调用不确定,但是肯定的是在数组中a一定在b前边,浏览器会根据回调函数的返回值来决定元素的顺序,如下:
如果返回一个大于0的值,则元素会交换位置
如果返回一个小于0的值,则元素位置不变
如果返回一个等于0的值,则认为两个元素相等,也不交换位置
经过上边的规则,我们可以总结下:
如果需要升序排列,则返回 a-b
如果需要降序排列,则返回 b-a
var arr = [1, 3, 2, 11, 5, 6];
arr.sort(function (a, b) {
return a - b;
});
console.log(arr);

3.2、函数对象
3.2.1、call()和apply()
call()和apply()这两个方法都是函数对象的方法,需要通过函数对象来调用,当对函数调用call()和apply()都会调用函数执行,在调用call()和apply()可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this,call()方法可以将实参在对象之后依次传递,apply()方法需要将实参封装到一个数组中统一传递,如下演示:
call()方法演示:
function fun(a, b) {
console.log("a = " + a);
console.log("b = " + b);
console.log("fun = " + this);
}
var obj = {
name: "obj",
sayName: function () {
console.log(this.name);
}
};
fun(2, 3);
console.log("===============");
fun.call(obj, 2, 3);

注意:默认fun()函数调用,this指向的是window对象,你可以使用call()调用函数,在调用的时候传入一个对象,这个对象就是this所指向的对象,也就是说,可以自己指定this的指向,然后从第二个参数开始,实参将会依次传递
apply()方法演示:
function fun(a, b) {
console.log("a = " + a);
console.log("b = " + b);
console.log("fun = " + this);
}
var obj = {
name: "obj",
sayName: function () {
console.log(this.name);
}
};
fun(2, 3);
console.log("===============");
fun.apply(obj, [2, 3]);

注意:默认fun()函数调用,this指向的是window对象,你可以使用apply()调用函数,在调用的时候传入一个对象,这个对象就是this所指向的对象,也就是说,可以自己指定this的指向,然后从第二个参数开始,需要制定一个实参数组进行参数传递
3.2.2、this指向
以函数形式调用时,this永远都是window
以方法的形式调用时,this是调用方法的对象
以构造函数的形式调用时,this是新创建的那个对象
使用call和apply调用时,this是传入的那个指定对象
3.2.3、arguments参数
在调用函数时,浏览器每次都会传递进两个隐含的参数:
函数的上下文对象: this
封装实参的对象: arguments
this对象我们已经学习过了,那arguments对象是什么呢?
arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度,在调用函数时,我们所传递的实参都会在arguments中保存,比如:arguments.length 可以用来获取实参的长度,我们即使不定义形参,也可以通过arguments来使用实参,只不过比较麻烦,例如:
arguments[0]:表示第一个实参
arguments[1]:表示第二个实参
…
它里边有一个属性叫做callee,这个属性对应一个函数对象,就是当前正在指向的函数的对象。
arguments对象演示:
function fun(a, b) {
// 通过下标获取第一个参数
console.log(arguments[0]);
// 通过下标获取第二个参数
console.log(arguments[1]);
// 获取实参的个数
console.log(arguments.length);
// 看看它的函数对象
console.log(arguments.callee);
console.log(arguments.callee == fun);
}
fun("Hello", "World");

3.3、Date对象
在JavaScript中使用Date对象来表示一个时间,如果直接使用构造函数创建一个Date对象,则会封装为当前代码执行的时间。
案例演示:
var date = new Date();
console.log(date);
console.log(date.getFullYear());//获取当前日期对象的年份(四位数字年份)
console.log(date.getMonth());//获取当前日期对象的月份(0 ~ 11)
console.log(date.getDate());//获取当前日期对象的日数(1 ~ 31)
console.log(date.getHours());//获取当前日期对象的小时(0 ~ 23)
console.log(date.getMinutes());//获取当前日期对象的分钟(0 ~ 59)
console.log(date.getSeconds());//获取当前日期对象的秒钟(0 ~ 59)
console.log(date.getMilliseconds());//获取当前日期对象的毫秒(0 ~ 999)

3.4、Math对象
Math和其它的对象不同,它不是一个构造函数,它属于一个工具类不用创建对象,它里边封装了数学运算相关的属性和方法。
案例演示:
/*固定值*/
console.log("PI = " + Math.PI);
console.log("E = " + Math.E);
console.log("===============");
/*正数*/
console.log(Math.abs(1)); //可以用来计算一个数的绝对值
console.log(Math.ceil(1.1)); //可以对一个数进行向上取整,小数位只有有值就自动进1
console.log(Math.floor(1.99)); //可以对一个数进行向下取整,小数部分会被舍掉
console.log(Math.round(1.4)); //可以对一个数进行四舍五入取整
console.log("===============");
/*负数*/
console.log(Math.abs(-1)); //可以用来计算一个数的绝对值
console.log(Math.ceil(-1.1)); //可以对一个数进行向上取整,小数部分会被舍掉
console.log(Math.floor(-1.99)); //可以对一个数进行向下取整,小数位只有有值就自动进1
console.log(Math.round(-1.4)); //可以对一个数进行四舍五入取整
console.log("===============");
/*随机数*/
//Math.random():可以用来生成一个0-1之间的随机数
//生成一个0-x之间的随机数:Math.round(Math.random()*x)
//生成一个x-y之间的随机数:Math.round(Math.random()*(y-x)+x)
console.log(Math.round(Math.random() * 10)); //生成一个0-10之间的随机数
console.log(Math.round(Math.random() * (10 - 1) + 1)); //生成一个1-10之间的随机数
console.log("===============");
/*数学运算*/
console.log(Math.pow(12, 3)); //Math.pow(x,y):返回x的y次幂
console.log(Math.sqrt(4)); //Math.sqrt(x) :返回x的平方根

3.5、String对象
3.5.1、概述
在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象
String():可以将基本数据类型字符串转换为String对象
Number():可以将基本数据类型的数字转换为Number对象
Boolean():可以将基本数据类型的布尔值转换为Boolean对象
但是注意:我们在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果,在这一章节中,我们重点介绍String()对象的属性和方法。
3.5.2、字符串属性
constructor属性演示:返回创建字符串对象的原型函数
var str = "Hello,World!";
console.log(str.constructor);
length属性演示:可以用来获取字符串的长度
var str = "Hello,World!";
console.log(str.length);

3.5.3、字符串方法
charAt()方法演示:该方法可以根据索引获取指定位置的字符
var str = "Hello,World!";
console.log(str.charAt(1));

charCodeAt()方法演示:该方法获取指定位置字符的字符编码(Unicode编码)
var str = "Hello,World!";
console.log(str.charCodeAt(1));

concat()方法演示:该方法可以用来连接两个或多个字符串
var str = "Hello,World!";
console.log(str.concat("你好,", "世界!"));

indexof()方法演示:该方法可以检索一个字符串中是否含有指定内容,如果字符串中含有该内容,则会返回其第一次出现的索引,如果没有找到指定的内容,则返回-1,可以指定一个第二个参数,指定开始查找的位置
var str = "Hello,World!";
console.log(str.indexOf("o"));
console.log(str.indexOf("o", 5));

lastIndexOf()方法演示:该方法的用法和indexOf()一样,不同的是indexOf是从前往后找,而lastIndexOf是从后往前找,也可以指定开始查找的位置
var str = "Hello,World!";
console.log(str.lastIndexOf("o"));
console.log(str.lastIndexOf("o", 5));

slice()方法演示:可以从字符串中截取指定的内容,不会影响原字符串,而是将截取到内容返回
参数:
第一个参数:开始位置的索引(包括开始位置)
第二个参数:结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的
注意:也可以传递一个负数作为参数,负数的话将会从后边计算
var str = "Hello,World!";
var result = str.slice(1, 4);
console.log(result);
result = str.slice(1);
console.log(result);
result = str.slice(1, -1);
console.log(result);

substring()方法演示:可以用来截取一个字符串,它和slice()类似
参数:
第一个参数:开始截取位置的索引(包括开始位置)
第二个参数:结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的
注意:不同的是这个方法不能接受负值作为参数,如果传递了一个负值,则默认使用0,而且它还自动调整参数的位置,如果第二个参数小于第一个,则自动交换
var str = "Hello,World!";
var result = str.substring(1, 4);
console.log(result);
result = str.substring(1);
console.log(result);
result = str.substring(1, -1);
console.log(result);

substr()方法演示:该方法用来截取字符串
参数:
第一个参数:截取开始位置的索引
第二个参数:截取的长度
var str = "Hello,World!";
var result = str.substr(6, 6);
console.log(result);

split()方法演示:该方法可以将一个字符串拆分为一个数组,需要一个字符串作为参数,将会根据该字符串去拆分数组
var str = "Hello,World!";
var result = str.split(",");
console.log(result);

toUpperCase()方法演示:将一个字符串转换为大写并返回
var str = "Hello,World!";
var result = str.toUpperCase();
console.log(result);

toLowerCase()方法演示:将一个字符串转换为小写并返回
var str = "Hello,World!";
var result = str.toLowerCase();
console.log(result);

3.6、RegExp对象
3.6.1、概述
正则表达式用于定义一些字符串的规则,计算机可以根据正则表达式,来检查一个字符串是否符合规则,获取将字符串中符合规则的内容提取出来。
使用typeof检查正则对象,会返回object。
3.6.2、创建正则对象
3.6.2.1、使用对象创建
语法格式:
var 变量名 = new RegExp("正则表达式","匹配模式");
匹配模式:
i:忽略大小写
g:全局匹配模式
ig:忽略大小写且全局匹配模式
案例演示:
// 这个正则表达式可以来检查一个字符串中是否含有a
var reg = new RegExp("ab", "i");
var str = "Abc";
var result = reg.test(str);
console.log(result);

3.6.2.2、使用字面量创建
语法格式:
var 变量名 = /正则表达式/匹配模式;
匹配模式:
i:忽略大小写
g:全局匹配模式
m:执行多行匹配
注意:可以为一个正则表达式设置多个匹配模式,且顺序无所谓
案例演示:
// 这个正则表达式可以来检查一个字符串中是否含有a
var reg = /a/i;
var str = "Abc";
var result = reg.test(str);
console.log(result);

3.6.3、正则进阶
需求信息:创建一个正则表达式,检查一个字符串中是否有a或b
语法格式:使用 | 表示或者的意思
// 这个正则表达式可以来检查一个字符串中是否含有a
var reg = /a|b|c/;
var str = "Abc";
var result = reg.test(str);
console.log(result);

试想一下,如果我们现在要是想要检查一个字符串是否含有小写字母,那我们是不是可以reg = /a|b|c|d|e|f|g ... /;这么写,但是你会发现,好啰嗦啊,怎么这么麻烦呢,有没有一种更简单的方式,答案是肯定的。
需求信息:创建一个正则表达式,检查一个字符串中是否有字母
语法格式:[ ] 里的内容也是或的关系
// 这个正则表达式可以来检查一个字符串中是否含有字母
var reg = /[A-z]/;
var str = "Abc";
var result = reg.test(str);
console.log(result);

常见组合:
[a-z]:任意小写字母
[A-Z]:任意大写字母
[A-z]:任意字母
[0-9]:任意数字
需求信息:创建一个正则表达式,检查一个字符串中是否含有 abc 或 adc 或 aec
// 这个正则表达式可以来检查一个字符串中是否含有abc或adc或aec
var reg = /a[bde]c/;
var str = "abc123";
var result = reg.test(str);
console.log(result);
那现在我们已经学会了判断是否包含,那要是判断除了某些字符序列该咋整,只需要这么写[^字符序列]
常见组合:
[^a-z]:除了任意小写字母
[^A-Z]:除了任意大写字母
[^A-z]:除了任意字母
[^0-9]:除了任意数字
需求信息:创建一个正则表达式,检查一个字符串中是否除了数字还有其它字母
// 这个正则表达式可以来检查一个字符串中是否除了数字还有其它字母
var reg = /[^0-9]/;
var str = "0123456789";
var result = reg.test(str);
console.log(result);
3.6.4、正则方法
这些正则方法其实都是字符串的方法,但是它的参数需要传递正则表达式,在这里,我就先称为正则方法。
split()方法演示:该方法可以将一个字符串拆分为一个数组,方法中可以传递一个正则表达式作为参数,这样方法将会根据正则表达式去拆分字符串,这个方法即使不指定全局匹配,也会全都插分
var str = "1a2b3c4d5e6f7";
var result = str.split(/[A-z]/);
console.log(result);

search()方法演示:该方法可以搜索字符串中是否含有指定内容,如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回-1,它可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串,serach()只会查找第一个,即使设置全局匹配也没用
var str = "hello abc hello aec afc";
var result = str.search(/a[bef]c/);
console.log(result);

match()方法演示:该方法可以根据正则表达式,从一个字符串中将符合条件的内容提取出来,默认情况下我们的match()只会找到第一个符合要求的内容,找到以后就停止检索,我们可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容,可以为一个正则表达式设置多个匹配模式,且顺序无所谓,match()会将匹配到的内容封装到一个数组中返回,即使只查询到一个结果
var str = "1a2a3a4a5e6f7A8B9C";
var result = str.match(/[a-z]/ig);
console.log(result);

replace()方法演示:该方法可以将字符串中指定内容替换为新的内容,默认只会替换第一个,但是可以设置全局匹配替换全部
参数:
第一个参数:被替换的内容,可以接受一个正则表达式作为参数
第二个参数:新的内容
var str = "1a2a3a4a5e6f7A8B9C";
var result = str.replace(/[a-z]/gi, "@_@");
console.log(result);

3.6.5、正则量词
通过量词可以设置一个内容出现的次数,量词只对它前边的一个内容起作用,如果有多个内容可以使用 () 括起来,常见量词如下:
{n} :正好出现n次
{m,} :出现m次及以上
{m,n} :出现m-n次
+ :至少一个,相当于{1,}
* :0个或多个,相当于{0,}
? :0个或1个,相当于{0,1}
var str = "abbc";
reg = /(ab){3}/;
console.log(reg.test(str));
console.log("===============");
reg = /b{3}/;
console.log(reg.test(str));
console.log("===============");
reg = /ab{1,3}c/;
console.log(reg.test(str));
console.log("===============");
reg = /ab{3,}c/;
console.log(reg.test(str));
console.log("===============");
reg = /ab+c/;
console.log(reg.test(str));
console.log("===============");
reg = /ab*c/;
console.log(reg.test(str));
console.log("===============");
reg = /ab?c/;
console.log(reg.test(str));
console.log("===============");

3.6.6、正则高阶
如果我们要检查或者说判断是否以某个字符或者字符序列开头或者结尾就会使用^和$。
^ :表示开头,注意它在[^字符序列]表达的意思不一样
$ :表示结尾
需求描述:检查一个字符串中是否以a开头
var str = "abcabca";
var reg = /^a/;
console.log(reg.test(str));

需求描述:检查一个字符串中是否以a结尾
var str = "abcabca";
var reg = /a$/;
console.log(reg.test(str));

那如果我们想要检查一个字符串中是否含有.和\就会使用转义字符
\. :表示.
\\ :表示\
注意:使用构造函数时,由于它的参数是一个字符串,而\是字符串中转义字符,如果要使用\则需要使用\\来代替
var reg1 = /\./;
var reg2 = /\\/;
var reg3 = new RegExp("\\.");
var reg4 = new RegExp("\\\\");
除了以上两种特殊的字符,其实还有很多如下所示:
\w :任意字母、数字、_,相当于[A-z0-9_]
\W :除了字母、数字、_,相当于[^A-z0-9_]
\d :任意的数字,相当于[0-9]
\D :除了任意的数字,相当于[^0-9]
\s :空格
\S :除了空格
\b :单词边界
\B :除了单词边界
这里边我们就演示最后四个,其它的都比较简单,后边的案例也会涉及,这里就不演示了
需求描述:创建一个正则表达式,去除掉字符串中的前后的空格
var str = " hello child "
var reg = /^\s*|\s*$/g;
console.log(str);
str = str.replace(reg, "");
console.log(str);

需求描述:创建一个正则表达式,检查一个字符串中是否含有单词child
var str = "hello child"
var reg = /\bchild\b/;
console.log(reg.test(str));

3.6.7、正则案例
3.6.7.1、检查手机号
var phoneStr = "15131494600";
var phoneReg = /^1[3-9][0-9]{9}$/;
console.log(phoneReg.test(phoneStr));

3.6.7.2、检查邮箱号
var emailStr = "abc.def@163.com";
var emailReg = /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/;
console.log(emailReg.test(emailStr));

第四章 JavaScript DOM
4.1、DOM概述
当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。
HTML DOM 模型被结构化为 对象树 :

通过这个对象模型,JavaScript 获得创建动态 HTML 的所有力量:
JavaScript 能改变页面中的所有 HTML 元素
JavaScript 能改变页面中的所有 HTML 属性
JavaScript 能改变页面中的所有 CSS 样式
JavaScript 能删除已有的 HTML 元素和属性
JavaScript 能添加新的 HTML 元素和属性
JavaScript 能对页面中所有已有的 HTML 事件作出反应
JavaScript 能在页面中创建新的 HTML 事件
换言之:HTML DOM 是关于如何获取、更改、添加或删除 HTML 元素的标准。
4.2、DOM文档节点
4.2.1、节点概述
节点Node,是构成我们网页的最基本的组成部分,网页中的每一个部分都可以称为是一个节点。
比如:html标签、属性、文本、注释、整个文档等都是一个节点。
虽然都是节点,但是实际上它们的具体类型是不同的。
比如:标签我们称为元素节点、属性称为属性节点、文本称为 文本节点、文档称为文档节点。
节点的类型不同,属性和方法也都不尽相同。
节点:Node——构成HTML文档最基本的单元。
常用节点分为四类:
文档节点:整个HTML文档
元素节点:HTML文档中的HTML标签
属性节点:元素的属性
文本节点:HTML标签中的文本内容

4.2.2、节点属性

4.2.3、文档节点
文档节点(Document)代表的是整个HTML文 档,网页中的所有节点都是它的子节点。
document对象作为window对象的属性存在的,我们不用获取可以直接使用。
通过该对象我们可以在整个文档访问内查找节点对象,并可以通过该对象创建各种节点对象。
4.2.4、元素节点
HTML中的各种标签都是元素节点(Element),这也是我们最常用的一个节点。
浏览器会将页面中所有的标签都转换为一个元素节点, 我们可以通过document的方法来获取元素节点。
例如:document.getElementById(),根据id属性值获取一个元素节点对象。
4.2.5、属性节点
属性节点(Attribute)表示的是标签中的一个一个的属 性,这里要注意的是属性节点并非是元素节点的子节点,而是元素节点的一部分。可以通过元素节点来获取指定的属性节点。
例如:元素节点.getAttributeNode("属性名"),根据元素节点的属性名获取一个属性节点对象。
注意:我们一般不使用属性节点。
4.2.6、文本节点
文本节点(Text)表示的是HTML标签以外的文本内容,任意非HTML的文本都是文本节点,它包括可以字面解释的纯文本内容。文本节点一般是作为元素节点的子节点存在的。获取文本节点时,一般先要获取元素节点,在通过元素节点获取文本节点。
例如:元素节点.firstChild;,获取元素节点的第一个子节点,一般为文本节点。
4.3、DOM文档操作
文档对象代表您的网页,,如果您希望访问 HTML 页面中的任何元素,那么您总是从访问 document 对象开始。
下面是一些如何使用 document 对象来访问和操作 HTML 的实例。
4.3.1、查找 HTML 元素
4.3.1.1、方法介绍
| 方法 | 描述 |
| document.getElementById(id) | 通过元素 id 来查找元素。 |
| document.getElementsByTagName(name) | 通过标签名来查找元素。 |
| document.getElementsByClassName(name) | 通过类名来查找元素。 |
| document.querySelector(CSS选择器) | 通过CSS选择器选择一个元素。 |
| document.querySelectorAll(CSS选择器) | 通过CSS选择器选择多个元素。 |
4.3.1.2、方法演示
.
需求描述:创建一个按钮,通过id获取按钮节点对象
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementById("btn");
console.log(btn);
</script>
</body>
</html>

需求描述:创建一个按钮,通过标签名获取按钮节点对象数组
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button>我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementsByTagName("button");
console.log(btn);
</script>
</body>
</html>

需求描述:创建一个按钮,通过类名获取按钮节点对象数组
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button class="btn">我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementsByClassName("btn");
console.log(btn);
</script>
</body>
</html>

需求描述:创建一个按钮,通过CSS选择器选择该按钮
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button class="btn">我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.querySelector(".btn");
console.log(btn);
</script>
</body>
</html>

需求描述:创建一个无序列表,通过CSS选择器选择该列表的所有li
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul class="list">
<li>列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li>列表项4</li>
</ul>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var list = document.querySelectorAll(".list li");
console.log(list);
</script>
</body>
</html>

4.3.2、获取 HTML 的值
4.3.2.1、方法介绍
| 方法 | 描述 |
| 元素节点.innerText | 获取 HTML 元素的 inner Text。 |
| 元素节点.innerHTML | 获取 HTML 元素的 inner HTML。 |
| 元素节点.属性 | 获取 HTML 元素的属性值。 |
| 元素节点.getAttribute(attribute) | 获取 HTML 元素的属性值。 |
| 元素节点.style.样式 | 获取 HTML 元素的行内样式值。 |
4.3.2.2、方法演示
需求描述:创建一个按钮,然后获取按钮的文本内容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementById("btn");
console.log(btn.innerText);
</script>
</body>
</html>

需求描述:创建一个div,然后在div中插入一个h1标题,获取div中的html代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box">
<h1>我是Box中的大标题</h1>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
console.log(box.innerHTML);
</script>
</body>
</html>

需求描述:创建一个超链接,默认为空,设置href属性为https://www.baidu.com ,使用JavaScript代码读取href属性
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a id="a" href="https://www.baidu.com">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
console.log(a.href);
</script>
</body>
</html>

需求描述:创建一个正方形div,默认颜色为红色,使用JavaScript代码获取div的宽度
注意:如果CSS的样式名中含有-,这种名称在JS中是不合法的比如background-color,需要将这种样式名修改为驼峰命名法,去掉-,然后将-后的字母大写,我们通过style属性设置的样式都是行内样式,同样的获取也是行内样式,而行内样式有较高的优先级,所以通过JS修改的样式往往会立即显示,但是如果在样式中写了!important,则此时样式会有最高的优先级,即使通过JS也不能覆盖该样式,此时将会导致JS修改样式失效,所以尽量不要为样式添加!important
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div style="width: 100px;height: 100px;background: red;" id="box"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
console.log(box.style.width);
</script>
</body>
</html>

拓展知识1:
通过style属性设置和读取的都是内联样式,无法读取样式表中的样式或者说正在应用的样式,如果想要读取当前正在应用的样式属性我们可以使用元素.currentStyle.样式名来获取元素的当前显示的样式,它可以用来读取当前元素正在显示的样式,如果当前元素没有设置该样式,则获取它的默认值,但是currentStyle只有IE浏览器支持,其它的浏览器都不支持,在其它浏览器中可以使用getComputedStyle()这个方法来获取元素当前的样式,这个方法是window的方法,可以直接使用,但是需要两个参数:
- 第一个参数:要获取样式的元素
- 第二个参数:可以传递一个伪元素,一般都传null
该方法会返回一个对象,对象中封装了当前元素对应的样式,可以通过 对象.样式名 来读取样式,如果获取的样式没有设置,则会获取到真实的值,而不是默认值,比如:没有设置width,它不会获取到auto,而是一个长度,但是该方法不支持IE8及以下的浏览器。通过currentStyle和getComputedStyle()读取到的样式都是只读的,不能修改,如果要修改必须通过style属性,因此,我们可以写一个适配各个浏览器的读取元素样式的方法。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
/*样式表的样式*/
#box {
width: 200px;
height: 200px;
background-color: green;
}
</style>
</head>
<body>
<div style="width: 100px;height: 100px;" id="box"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
/*通用的获取元素样式的方法*/
function getStyle(obj, name) {
if (window.getComputedStyle) {
//正常浏览器的方式,具有getComputedStyle()方法
return getComputedStyle(obj, null)[name];
} else {
//IE8的方式,没有getComputedStyle()方法
return obj.currentStyle[name];
}
}
var box = document.getElementById("box");
console.log(getStyle(box, "width"));
console.log(getStyle(box, "height"));
console.log(getStyle(box, "background-color"));
</script>
</body>
</html>
谷歌浏览器:

火狐浏览器:

IE9-IE11 浏览器:

IE5-IE8 浏览器:

有同学会说,在IE5-IE8 浏览器中,老师这不是还不一样吗?因此我们建议设置颜色数值一般采用rgb或者rgba,最好不要采用英文单词的这种颜色值,这样最终展示出来的效果就一致了。
拓展知识2:编写一段兼容性代码,用来获取任意标签的文本内容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a href="https://www.baidu.com" id="a">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
console.log(getInnerText(a));
/*获取任意标签的内容*/
function getInnerText(element) {
// 判断浏览器是否支持textContent,如果支持,则使用textContent获取内容,否则使用innerText获取内容。
if(typeof element.textContent == "undefined") {
return element.innerText;
} else {
return element.textContent;
}
}
</script>
</body>
</html>
谷歌浏览器:

火狐浏览器:

IE5-IE11浏览器:

4.3.3、改变 HTML 的值
4.3.3.1、方法介绍
| 方法 | 描述 |
|---|---|
| 元素节点.innerText = new text content | 改变元素的 inner Text。 |
| 元素节点.innerHTML = new html content | 改变元素的 inner HTML。 |
| 元素节点.属性 = new value | 改变 HTML 元素的属性值。 |
| 元素节点.setAttribute(attribute, value) | 改变 HTML 元素的属性值。 |
| 元素节点.style.样式 = new style | 改变 HTML 元素的行内样式值。 |
4.3.3.2、方法演示
需求描述:创建一个按钮,然后改变按钮的文本内容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">我是按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementById("btn");
btn.innerText = "我是JavaScript的按钮";
console.log(btn);
</script>
</body>
</html>

需求描述:创建一个div,然后在div中插入一个h1标题
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
box.innerHTML = "<h1>我是Box中的大标题</h1>";
console.log(box);
</script>
</body>
</html>

**需求描述:创建一个超链接,默认为空,使用JavaScript代码设置href属性为https://www.baidu.com **
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a id="a" href="">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
a.href="https://www.baidu.com";
console.log(a);
</script>
</body>
</html>

需求描述:创建一个正方形div,默认颜色为红色,使用JavaScript代码改变为绿色
注意:如果CSS的样式名中含有-,这种名称在JS中是不合法的比如background-color,需要将这种样式名修改为驼峰命名法,去掉-,然后将-后的字母大写,我们通过style属性设置的样式都是行内样式,同样的获取也是行内样式,而行内样式有较高的优先级,所以通过JS修改的样式往往会立即显示,但是如果在样式中写了!important,则此时样式会有最高的优先级,即使通过JS也不能覆盖该样式,此时将会导致JS修改样式失效,所以尽量不要为样式添加!important
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div style="width: 100px;height: 100px;background: red;" id="box"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
box.style.background = "green";
console.log(box);
</script>
</body>
</html>

拓展知识1:
修改节点的内容除了常用的innerHTML和innerText之外,还有insertAdjacentHTML和insertAdjacentText方法,可以在指定的地方插入内容。insertAdjacentText方法与insertAdjacentHTML方法类似,只不过是插入纯文本,参数相同。
语法说明:
object.insertAdjacentHTML(where,html);
object.insertAdjacentText(where,text)
参数说明:
-
where:
- beforeBegin:插入到开始标签的前面
- beforeEnd:插入到结束标签的前面
- afterBegin:插入到开始标签的后面
- afterEnd:插入到结束标签的后面

-
html:一段html代码
-
text:一段文本值
注意事项:
- 这两个方法必须等文档加载好后才能执行,否则会出错。
- insertAdjacentText只能插入普通文本,insertAdjacentHTML插入html代码。
- 使用insertAdjacentHTML方法插入script脚本文件时,必须在script元素上定义defer属性。
- 使用insertAdjacentHTML方法插入html代码后,页面上的元素集合将发生变化。
- insertAdjacentHTML方法不适用于单个的空的元素标签(如img,input等)
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="insert">
<p>你是我的小丫小苹果</p>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var div = document.getElementById("insert");
div.insertAdjacentHTML('beforeBegin', '你是我的小丫小苹果');
</script>
</body>
</html>

拓展知识2:编写一段兼容性代码,用来设置任意标签的文本内容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a href="https://www.baidu.com" id="a">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
setInnerText(a, "你要打开百度吗?");
console.log(getInnerText(a));
/*获取任意标签的内容*/
function getInnerText(element) {
// 判断浏览器是否支持textContent,如果支持,则使用textContent获取内容,否则使用innerText获取内容。
if (typeof element.textContent == "undefined") {
return element.innerText;
} else {
return element.textContent;
}
}
/*设置任意标签的内容*/
function setInnerText(element, text) {
// 判断浏览器是否支持textContent,如果支持,则使用textContent设置内容,否则使用innerText设置内容。
if (typeof element.textContent == "undefined") {
return element.innerText = text;
} else {
return element.textContent = text;
}
}
</script>
</body>
</html>
谷歌浏览器:

火狐浏览器:

IE5-IE11浏览器:

4.3.4、修改 HTML 元素
4.3.4.1、方法介绍
| 方法 | 描述 | |
|---|---|---|
| document.createElement(element) | 创建 HTML 元素节点。 | |
| document.createAttribute(attribute) | 创建 HTML 属性节点。 | |
| document.createTextNode(text) | 创建 HTML 文本节点。 | |
| 元素节点.removeChild(element) | 删除 HTML 元素。 | |
| 元素节点.appendChild(element) | 添加 HTML 元素。 | |
| 元素节点.replaceChild(element) | 替换 HTML 元素。 | |
| 元素节点.insertBefore(element) | 在指定的子节点前面插入新的子节点。 |
4.3.4.2、方法演示
案例演示1:创建一个ul列表,然后在该列表中追加4个li标签
第一种方法:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.createElement("ul");
var li1 = document.createElement("li");
var text1 = document.createTextNode("列表项1");
li1.appendChild(text1);
ul.appendChild(li1);
var li2 = document.createElement("li");
var text2 = document.createTextNode("列表项2");
li2.appendChild(text2);
ul.appendChild(li2);
var li3 = document.createElement("li");
var text3 = document.createTextNode("列表项3");
li3.appendChild(text3);
ul.appendChild(li3);
var li4 = document.createElement("li");
var text4 = document.createTextNode("列表项4");
li4.appendChild(text4);
ul.appendChild(li4);
document.getElementsByTagName("body")[0].appendChild(ul);
</script>
</body>
</html>

第二种方法:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.createElement("ul");
var li1 = document.createElement("li");
li1.innerHTML = "列表项1";
ul.appendChild(li1);
var li2 = document.createElement("li");
li2.innerHTML = "列表项2";
ul.appendChild(li2);
var li3 = document.createElement("li");
li3.innerHTML = "列表项3";
ul.appendChild(li3);
var li4 = document.createElement("li");
li4.innerHTML = "列表项4";
ul.appendChild(li4);
document.getElementsByTagName("body")[0].appendChild(ul);
</script>
</body>
</html>

第三种方法:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.createElement("ul");
var li1 = "<li>列表项1</li>";
var li2 = "<li>列表项2</li>";
var li3 = "<li>列表项3</li>";
var li4 = "<li>列表项4</li>";
ul.innerHTML = li1 + li2 + li3 + li4;
document.getElementsByTagName("body")[0].appendChild(ul);
</script>
</body>
</html>

案例演示2:创建一个ul列表,里边有四个li子元素,删除第一个li,替换最后一个li
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="ul">
<li id="first">列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li id="last">列表项4</li>
</ul>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.getElementById("ul");
var first = document.getElementById("first");
var last = document.getElementById("last");
/*删除第一个*/
ul.removeChild(first);
/*替换最后一个*/
var replaceLi = document.createElement("li");
replaceLi.innerHTML = "列表4的替换";
ul.replaceChild(replaceLi, last);
</script>
</body>
</html>

案例演示3:创建一个ul列表,里边有四个li子元素,在第一个li前边插入一个id为zero的li
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="ul">
<li id="first">列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li>列表项4</li>
</ul>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.getElementById("ul");
var first = document.getElementById("first");
var zero = document.createElement("li");
zero.innerHTML = "列表0的新增";
ul.insertBefore(zero, first);
</script>
</body>
</html>

拓展知识:
动态判断、添加、删除、切换样式,支持IE5-IE11,谷歌浏览器、火狐浏览器等
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
.b1 {
width: 100px;
height: 100px;
background-color: red;
}
.b2 {
width: 300px;
height: 300px;
background-color: yellow;
}
</style>
</head>
<body>
<button id="btn0">判断b2样式</button>
<button id="btn1">添加b2样式</button>
<button id="btn2">删除b2样式</button>
<button id="btn3">切换b2样式</button>
<br>
<br>
<div id="box" class="b1"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn0 = document.getElementById("btn0");
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var btn3 = document.getElementById("btn3");
var box = document.getElementById("box");
btn0.onclick = function () {
alert(hasClass(box, "b2"));
};
btn1.onclick = function () {
addClass(box, "b2");
};
btn2.onclick = function () {
removeClass(box, "b2");
};
btn3.onclick = function () {
toggleClass(box, "b2");
};
/*
* 判断一个元素中是否含有指定的class属性值
* 如果有该class,则返回true,没有则返回false
*/
function hasClass(obj, cn) {
var reg = new RegExp("\\b" + cn + "\\b");
return reg.test(obj.className);
}
/*
* 向一个元素中添加指定的class属性值
* 参数:
* obj 要添加class属性的元素
* cn 要添加的class值
*/
function addClass(obj, cn) {
// 检查obj中是否含有cn
if (!hasClass(obj, cn)) {
obj.className += " " + cn;
}
}
/*
* 删除一个元素中的指定的class属性
*/
function removeClass(obj, cn) {
var reg = new RegExp("\\b" + cn + "\\b");
obj.className = obj.className.replace(reg, "");
}
/*
* toggleClass可以用来切换一个类
* 如果元素中具有该类,则删除
* 如果元素中没有该类,则添加
*/
function toggleClass(obj, cn) {
// 判断obj中是否含有cn
if (hasClass(obj, cn)) {
// 存在,则删除
removeClass(obj, cn);
} else {
// 没有,则添加
addClass(obj, cn);
}
}
</script>
</body>
</html>

4.3.5、查找 HTML 父子
4.3.5.1、方法介绍
| 方法 | 描述 |
|---|---|
| 元素节点.parentNode | 返回元素的父节点。 |
| 元素节点.parentElement | 返回元素的父元素。 |
| 元素节点.childNodes | 返回元素的一个子节点的数组(包含空白文本Text节点)。 |
| 元素节点.children | 返回元素的一个子元素的集合(不包含空白文本Text节点)。 |
| 元素节点.firstChild | 返回元素的第一个子节点(包含空白文本Text节点)。 |
| 元素节点.firstElementChild | 返回元素的第一个子元素(不包含空白文本Text节点)。 |
| 元素节点.lastChild | 返回元素的最后一个子节点(包含空白文本Text节点)。 |
| 元素节点.lastElementChild | 返回元素的最后一个子元素(不包含空白文本Text节点)。 |
| 元素节点.previousSibling | 返回某个元素紧接之前节点(包含空白文本Text节点)。 |
| 元素节点.previousElementSibling | 返回指定元素的前一个兄弟元素(相同节点树层中的前一个元素节点)。 |
| 元素节点.nextSibling | 返回某个元素紧接之后节点(包含空白文本Text节点)。 |
| 元素节点.nextElementSibling | 返回指定元素的后一个兄弟元素(相同节点树层中的下一个元素节点)。 |
4.3.5.2、方法演示
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box">
<ul id="ul">
<li><a href="https://www.baidu.com">我是超链接1</a></li>
<li id="two"><a href="https://www.baidu.com">我是超链接2</a></li>
<li><a href="https://www.baidu.com">我是超链接3</a></li>
<li><a href="https://www.baidu.com">我是超链接4</a></li>
</ul>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
var ul = document.getElementById("ul");
var two = document.getElementById("two");
console.log(ul.parentNode);
console.log(ul.parentElement);
console.log("===============");
console.log(box.childNodes);
console.log(box.children);
console.log("===============");
console.log(ul.firstChild);
console.log(ul.firstElementChild);
console.log(ul.lastChild);
console.log(ul.lastElementChild);
console.log("===============");
console.log(two.previousSibling);
console.log(two.previousElementSibling);
console.log(two.nextSibling);
console.log(two.nextElementSibling);
</script>
</body>
</html>

兼容性方法:
/*获取任意一个父级元素的第一个子元素*/
function getfirstElementChild(element) {
if(element.firstElementChild) {
return element.firstElementChild;
} else {
var node = element.firstChild;
while(node && node.nodeType != 1) {
node = node.nextSibling;
}
return node;
}
}
/*获取任意一个父级元素的最后一个子元素*/
function getLastElementChild(element) {
if(element.lastElementChild) {
return element.lastElementChild;
} else {
var node = element.lastChild;
while(node && node.nodeType != 1) {
node = node.previousSibling;
}
return node;
}
}
/*获取任意一个子元素的前一个兄弟元素*/
function getPreviousElementSibling(element) {
if(element.previousElementSibling) {
return element.previousElementSibling;
} else {
var node = element.previousSibling;
while(node && node.nodeType != 1) {
node = node.previousSibling;
}
return node;
}
}
/*获取任意一个子元素的后一个兄弟元素*/
function getNextElementSibling(element) {
if(element.nextElementSibling) {
return element.nextElementSibling;
} else {
var node = element.nextSibling;
while(node && node.nodeType != 1) {
node = node.nextSibling;
}
return node;
}
}
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="container">
<p>前面的P标签</p>
<b>加粗文本</b>
<a href="https://www.baidu.com" id="a">百度一下</a>
<i>斜体文本</i>
<p>最后的P标签</p>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
/*第一个子元素*/
var firstNode = getfirstElementChild(document.getElementById("container"));
console.log(firstNode.innerHTML);
/*最后一个子元素*/
var lastNode = getLastElementChild(document.getElementById("container"));
console.log(lastNode.innerHTML);
/*指定元素的前一个子元素*/
var node1 = getPreviousElementSibling(document.getElementById("a"));
console.log(node1.innerHTML);
/*指定元素的后一个子元素*/
var node2 = getNextElementSibling(document.getElementById("a"));
console.log(node2.innerHTML);
/*获取任意一个父级元素的第一个子元素*/
function getfirstElementChild(element) {
if (element.firstElementChild) {
return element.firstElementChild;
} else {
var node = element.firstChild;
while (node && node.nodeType != 1) {
node = node.nextSibling;
}
return node;
}
}
/*获取任意一个父级元素的最后一个子元素*/
function getLastElementChild(element) {
if (element.lastElementChild) {
return element.lastElementChild;
} else {
var node = element.lastChild;
while (node && node.nodeType != 1) {
node = node.previousSibling;
}
return node;
}
}
/*获取任意一个子元素的前一个兄弟元素*/
function getPreviousElementSibling(element) {
if (element.previousElementSibling) {
return element.previousElementSibling;
} else {
var node = element.previousSibling;
while (node && node.nodeType != 1) {
node = node.previousSibling;
}
return node;
}
}
/*获取任意一个子元素的后一个兄弟元素*/
function getNextElementSibling(element) {
if (element.nextElementSibling) {
return element.nextElementSibling;
} else {
var node = element.nextSibling;
while (node && node.nodeType != 1) {
node = node.nextSibling;
}
return node;
}
}
</script>
</body>
</html>
谷歌浏览器:

火狐浏览器:

IE5-IE11浏览器:

4.4、DOM文档事件
4.4.1、事件概述
HTML事件可以触发浏览器中的行为,比方说当用户点击某个 HTML 元素时启动一段 JavaScript。
4.4.2、窗口事件
由窗口触发该事件 (同样适用于 <body> 标签):
| 属性 | 描述 |
|---|---|
| onblur | 当窗口失去焦点时运行脚本。 |
| onfocus | 当窗口获得焦点时运行脚本。 |
| onload | 当文档加载之后运行脚本。 |
| onresize | 当调整窗口大小时运行脚本。 |
| onstorage | 当 Web Storage 区域更新时(存储空间中的数据发生变化时)运行脚本。 |
案例演示1:当窗口失去焦点时,输出“窗口失去焦点”
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
window.onblur = function () {
console.log("窗口失去焦点");
};
</script>
</body>
</html>

案例演示2:当窗口获取焦点时,输出“窗口获取焦点”
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
window.onfocus = function () {
console.log("窗口获取焦点");
};
</script>
</body>
</html>

案例演示3:当页面文档加载完成后,输出"Hello, World"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
window.onload = function () {
console.log("Hello,World");
};
</script>
</body>
</html>

案例演示4:当调整窗口大小时,输出"窗口大小正在改变"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
window.onresize = function () {
console.log("窗口大小正在改变");
};
</script>
</body>
</html>

4.4.3、表单事件
表单事件在HTML表单中触发 (适用于所有 HTML 元素,但该HTML元素需在form表单内):
| 属性 | 描述 |
|---|---|
| onblur | 当元素失去焦点时运行脚本。 |
| onfocus | 当元素获得焦点时运行脚本。 |
| onchange | 当元素改变时运行脚本。 |
| oninput | 当元素获得用户输入时运行脚本。 |
| oninvalid | 当元素无效时运行脚本。 |
| onselect | 当选取元素时运行脚本。 |
| onsubmit | 当提交表单时运行脚本。 |
案例演示1:当文本框获取焦点,文本框背景为红色,当文本框失去焦点,文本框背景为黄色
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text"></input>
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var textInput = document.getElementById("text");
/* 当文本框获取焦点,文本框背景为红色 */
textInput.onfocus = function () {
this.style.background = "red";
};
/* 当文本框失去焦点,文本框背景为绿色 */
textInput.onblur = function () {
this.style.background = "green";
};
</script>
</body>
</html>
注意:这里为什么要用this,你不用this也可以,就直接
textInput.style.background = "red";也不是不可以的,但是方法的调用规则就是谁调用this,this就指向谁,这样我们就可以简化代码了
案例演示2:当文本框内容改变时,鼠标离开文本框,自动将文本框的内容输出到控制台
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text"></input>
</form>
<script>
var textInput = document.getElementById("text");
/* 当文本框内容改变时,鼠标离开文本框,自动将文本框的内容输出到控制台 */
textInput.onchange = function(){
console.log(this.value)
};
</script>
</body>
</html>

案例演示3:当文本框内容改变时,立即将改变的内容输出到控制台
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text"></input>
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var textInput = document.getElementById("text");
/* 当文本框内容改变时,立即将改变的内容输出到控制台 */
textInput.oninput = function () {
console.log(this.value);
};
</script>
</body>
</html>

案例演示4:如果单击“submit”,则不填写文本字段,将发生警报消息
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text" required></input>
<input type="submit" value="submit"></input>
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var textInput = document.getElementById("text");
/* 如果单击“submit”,则不填写文本字段,将发生警报消息 */
textInput.oninvalid = function () {
console.log("请您完善表单内容!");
};
</script>
</body>
</html>

案例演示5:当选中文本框的内容时,输出“您已经选择了文本框内容!”
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text"></input>
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var textInput = document.getElementById("text");
/* 当选中文本框的内容时,输出“您已经选择了文本框内容!” */
textInput.onselect = function () {
console.log("您已经选择了文本框内容!");
};
</script>
</body>
</html>

案例演示6:当提交表单的时候,在控制台输出“表单提交”
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form id="myform">
<input type="submit" id="submit"></input>
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var myform = document.getElementById("myform");
/* 当提交表单的时候,在控制台输出“表单提交” */
myform.onsubmit = function () {
console.log("表单提交");
return false;/* 用来阻止表单提交的,你不写它会跳转请求 */
};
</script>
</body>
</html>

4.4.4、键盘事件
通过键盘触发事件,类似用户的行为:
| 属性 | 描述 |
|---|---|
| onkeydown | 当按下按键时运行脚本。 |
| onkeyup | 当松开按键时运行脚本。 |
| onkeypress | 当按下并松开按键时运行脚本。 |
案例演示1:当键盘按下判断当前的按键是不是 a ,如果是就输出true,否则输出false
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
/* 当键盘按下判断当前的按键是不是 a ,如果是就输出true,否则输出false */
window.onkeydown = function (event) {
/* 解决兼容性问题 */
event = event || window.event;
if (event.keyCode == 65) {
console.log("true");
} else {
console.log("false");
}
};
</script>
</body>
</html>

案例演示2:使div可以根据不同的方向键向不同的方向移动
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box" style="width: 100px;height: 100px;background: red;position: absolute;"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
//为document绑定一个按键按下的事件
document.onkeydown = function (event) {
event = event || window.event;
// 定义移动速度
var speed = 10;
// 选择移动方向
switch (event.keyCode) {
case 37:
box.style.left = box.offsetLeft - speed + "px";
break;
case 39:
box.style.left = box.offsetLeft + speed + "px";
break;
case 38:
box.style.top = box.offsetTop - speed + "px";
break;
case 40:
box.style.top = box.offsetTop + speed + "px";
break;
}
};
</script>
</body>
</html>

拓展知识:
当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数。
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标的状态。
在IE8中,响应函数被触发时,浏览器不会传递事件对象,在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的。
解决事件对象的兼容性问题:event = event || window.event;
键鼠属性:
| 属性 | 描述 |
|---|---|
| ctrlKey | 返回当事件被触发时,“CTRL” 键是否被按下。 |
| altKey | 返回当事件被触发时,“ALT” 是否被按下。 |
| shiftKey | 返回当事件被触发时,“SHIFT” 键是否被按下。 |
| clientX | 返回当事件被触发时,鼠标指针的水平坐标。 |
| clientY | 返回当事件被触发时,鼠标指针的垂直坐标。 |
| screenX | 返回当某个事件被触发时,鼠标指针的水平坐标。 |
| screenY | 返回当某个事件被触发时,鼠标指针的垂直坐标。 |
4.4.5、鼠标事件
通过鼠标触发事件,类似用户的行为:
| 属性 | 描述 |
|---|---|
| onclick | 当单击鼠标时运行脚本。 |
| ondblclick | 当双击鼠标时运行脚本。 |
| onmousedown | 当按下鼠标按钮时运行脚本。 |
| onmouseup | 当松开鼠标按钮时运行脚本。 |
| onmousemove | 当鼠标指针移动时运行脚本。 |
| onmouseover | 当鼠标指针移至元素之上时运行脚本,不可以阻止冒泡。 |
| onmouseout | 当鼠标指针移出元素时运行脚本,不可以阻止冒泡。 |
| onmouseenter | 当鼠标指针移至元素之上时运行脚本,可以阻止冒泡。 |
| onmouseleave | 当鼠标指针移出元素时运行脚本,可以阻止冒泡。 |
| onmousewheel | 当转动鼠标滚轮时运行脚本。 |
| onscroll | 当滚动元素的滚动条时运行脚本。 |
案例演示1:创建一个正方形div,默认颜色为黑色,当鼠标移入div,背景颜色变为红色,当鼠标移出div,背景颜色变为绿色
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box" style="width: 100px;height: 100px;background: black;"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
/* 当鼠标移入div,背景颜色变为红色 */
box.onmouseenter = function () {
this.style.background = "red";
};
/* 当鼠标移出div,背景颜色变为绿色 */
box.onmouseleave = function () {
this.style.background = "green";
};
</script>
</body>
</html>

案例演示2:编写一个通用的拖拽元素函数,创建两个div,进行拖拽演示,要求兼容IE8、火狐、谷歌等主流浏览器
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box1" style="width: 100px;height: 100px;background: red;position: absolute;"></div>
<div id="box2" style="width: 100px;height: 100px;background: green;position: absolute;"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
drag(box1);
drag(box2);
/*
* 提取一个专门用来设置拖拽的函数
* 参数:开启拖拽的元素
*/
function drag(obj) {
//当鼠标在被拖拽元素上按下时,开始拖拽
obj.onmousedown = function (event) {
// 解决事件的兼容性问题
event = event || window.event;
// 设置obj捕获所有鼠标按下的事件
/**
* setCapture():
* 只有IE支持,但是在火狐中调用时不会报错,
* 而如果使用chrome调用,它也会报错
*/
obj.setCapture && obj.setCapture();
// obj的偏移量 鼠标.clentX - 元素.offsetLeft
// obj的偏移量 鼠标.clentY - 元素.offsetTop
var ol = event.clientX - obj.offsetLeft;
var ot = event.clientY - obj.offsetTop;
// 为document绑定一个鼠标移动事件
document.onmousemove = function (event) {
// 解决事件的兼容性问题
event = event || window.event;
// 当鼠标移动时被拖拽元素跟随鼠标移动
// 获取鼠标的坐标
var left = event.clientX - ol;
var top = event.clientY - ot;
// 修改obj的位置
obj.style.left = left + "px";
obj.style.top = top + "px";
};
// 为document绑定一个鼠标松开事件
document.onmouseup = function () {
// 取消document的onmousemove事件
document.onmousemove = null;
// 取消document的onmouseup事件
document.onmouseup = null;
// 当鼠标松开时,取消对事件的捕获
obj.releaseCapture && obj.releaseCapture();
};
/*
* 当我们拖拽一个网页中的内容时,浏览器会默认去搜索引擎中搜索内容,
* 此时会导致拖拽功能的异常,这个是浏览器提供的默认行为,
* 如果不希望发生这个行为,则可以通过return false来取消默认行为,
* 但是这招对IE8不起作用
*/
return false;
};
}
</script>
</body>
</html>

4.4.6、媒体事件
通过视频(videos),图像(images)或音频(audio) 触发该事件。
| 属性 | 描述 |
|---|---|
| onabort | 当发生中止事件时运行脚本。 |
| oncanplay | 当媒介能够开始播放但可能因缓冲而需要停止时运行脚本。 |
| oncanplaythrough | 当媒介能够无需因缓冲而停止即可播放至结尾时运行脚本。 |
| ondurationchange | 当媒介长度改变时运行脚本。 |
| onemptied | 当媒介资源元素突然为空时(网络错误、加载错误等)运行脚本。 |
| onended | 当媒介已抵达结尾时运行脚本。 |
| onerror | 当在元素加载期间发生错误时运行脚本。 |
| onloadeddata | 当加载媒介数据时运行脚本。 |
| onloadedmetadata | 当媒介元素的持续时间以及其它媒介数据已加载时运行脚本。 |
| onloadstart | 当浏览器开始加载媒介数据时运行脚本。 |
| onpause | 当媒介数据暂停时运行脚本。 |
| onplay | 当媒介数据将要开始播放时运行脚本。 |
| onplaying | 当媒介数据已开始播放时运行脚本。 |
| onprogress | 当浏览器正在取媒介数据时运行脚本。 |
| onratechange | 当媒介数据的播放速率改变时运行脚本。 |
| onreadystatechange | 当就绪状态(ready-state)改变时运行脚本。 |
| onseeked | 当媒介元素的定位属性不再为真且定位已结束时运行脚本。 |
| onseeking | 当媒介元素的定位属性为真且定位已开始时运行脚本。 |
| onstalled | 当取回媒介数据过程中(延迟)存在错误时运行脚本。 |
| onsuspend | 当浏览器已在取媒介数据但在取回整个媒介文件之前停止时运行脚本。 |
| ontimeupdate | 当媒介改变其播放位置时运行脚本。 |
| onvolumechange | 当媒介改变音量亦或当音量被设置为静音时运行脚本。 |
| onwaiting | 当媒介已停止播放但打算继续播放时运行脚本。 |
4.4.7、其它事件
| 属性 | 描述 |
|---|---|
| onshow | 当 <menu> 元素在上下文显示时触发。 |
| ontoggle | 当用户打开或关闭 <details> 元素时触发。 |
4.4.8、事件冒泡
事件的冒泡(Bubble):所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发,在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡。
案例演示1:创建两个div,叠放在一起,分别绑定单击事件,点击最里边的div,会触发两个div的单击事件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {
width: 200px;
height: 200px;
background: pink;
}
#div2 {
width: 100px;
height: 100px;
background: coral;
}
</style>
</head>
<body>
<div id="div1">
我是DIV1
<div id="div2">
我是DIV2
</div>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
// 为div1绑定单击事件
div1.onclick = function () {
console.log("div1 的单击事件触发了!");
};
// 为div2绑定单击事件
div2.onclick = function () {
console.log("div2 的单击事件触发了!");
};
</script>
</body>
</html>

案例演示2:创建两个div,叠放在一起,分别绑定单击事件,点击最里边的div,不会触发两个div的单击事件,只会触发自己的单击事件,这时候我们可以取消事件冒泡
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {
width: 200px;
height: 200px;
background: pink;
}
#div2 {
width: 100px;
height: 100px;
background: coral;
}
</style>
</head>
<body>
<div id="div1">
我是DIV1
<div id="div2">
我是DIV2
</div>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
// 为div1绑定单击事件
div1.onclick = function () {
console.log("div1 的单击事件触发了!");
stopBubble();
};
// 为div2绑定单击事件
div2.onclick = function () {
console.log("div2 的单击事件触发了!");
stopBubble();
};
// 取消事件冒泡
function stopBubble(event) {
// 如果提供了事件对象,则这是一个非IE浏览器
if (event && event.stopPropagation) {
// 因此它支持W3C的stopPropagation()方法
event.stopPropagation();
} else {
// 否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
</script>
</body>
</html>

案例演示3:当点击a标签的时候,阻止a标签的默认跳转事件,采用事件阻止
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {
width: 200px;
height: 200px;
background: pink;
}
#div2 {
width: 100px;
height: 100px;
background: coral;
}
</style>
</head>
<body>
<a href="https://www.baidu.com" id="a">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
// 为a绑定单击事件
a.onclick = function () {
stopDefault();
};
// 阻止浏览器的默认行为
function stopDefault(event) {
if (event && event.preventDefault) {
// 阻止默认浏览器动作(W3C)
event.preventDefault();
} else {
// IE中阻止函数器默认动作的方式
window.event.returnValue = false;
}
return false;
}
</script>
</body>
</html>
4.4.9、事件委派
我们希望只绑定一次事件,即可应用到多个的元素上,即使元素是后添加的,我们可以尝试将其绑定给元素的共同的祖先元素,也就是事件的委派。事件的委派,是指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。事件委派是利用了事件冒泡,通过委派可以减少事件绑定的次数,提高程序的性能。
案例演示:为ul列表中的所有a标签都绑定单击事件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="u1">
<li><a href="javascript:;" class="link">超链接一</a></li>
<li><a href="javascript:;" class="link">超链接二</a></li>
<li><a href="javascript:;" class="link">超链接三</a></li>
</ul>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var u1 = document.getElementById("u1");
// 为ul绑定一个单击响应函数
u1.onclick = function (event) {
event = event || window.event;
// 如果触发事件的对象是我们期望的元素,则执行,否则不执行
if (event.target.className == "link") {
console.log("我是ul的单击响应函数");
}
};
</script>
</body>
</html>

4.4.10、事件绑定
我们以前绑定事件代码只能一个事件绑定一个函数,那我们要是想一个事件对应多个函数,并且不存在兼容性的问题该如何解决呢?
接下来,我会直接提供两个已经编写好的事件绑定和事件解绑的兼容性代码,如下:
/*为元素绑定事件兼容性代码*/
function addEventListener(element, type, fn) {
if(element.addEventListener) {
element.addEventListener(type, fn, false);
} else if(element.attachEvent) {
element.attachEvent("on" + type, fn);
} else {
element["on" + type] = fn;
}
}
/*为元素解绑事件兼容性代码*/
function removeEventListener(element, type, fnName) {
if(element.removeEventListener) {
element.removeEventListener(type, fnName, false);
} else if(element.detachEvent) {
element.detachEvent("on" + type, fnName);
} else {
element["on" + type] = null;
}
}
案例演示:为按钮1的单击事件绑定两个函数,然后点击按钮2取消按钮1的单机事件绑定函数f1
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function f1() {
console.log("output1 ...");
};
function f2() {
console.log("output2 ...");
};
// 为按钮1的单击事件绑定两个函数
addEventListener(document.getElementById("btn1"), "click", f1);
addEventListener(document.getElementById("btn1"), "click", f2);
// 点击按钮2取消按钮1的单机事件绑定函数f1
document.getElementById("btn2").onclick = function () {
removeEventListener(document.getElementById("btn1"), "click", f1);
};
/*为元素绑定事件兼容性代码*/
function addEventListener(element, type, fn) {
if (element.addEventListener) {
element.addEventListener(type, fn, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, fn);
} else {
element["on" + type] = fn;
}
}
/*为元素解绑事件兼容性代码*/
function removeEventListener(element, type, fnName) {
if (element.removeEventListener) {
element.removeEventListener(type, fnName, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, fnName);
} else {
element["on" + type] = null;
}
}
</script>
</body>
</html>
谷歌浏览器:

火狐浏览器:

IE8-IE11浏览器:

4.4.11、事件传播
事件的传播:关于事件的传播网景公司和微软公司有不同的理解
微软公司认为事件应该是由内向外传播,也就是当事件触发时,应该先触发当前元素上的事件,然后再向当前元素的祖先元素上传播,也就说事件应该在冒泡阶段执行。
网景公司认为事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素的事件,然后在向内传播给后代元素。
W3C综合了两个公司的方案,将事件传播分成了三个阶段:
- 捕获阶段:在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
- 目标阶段:事件捕获到目标元素,捕获结束开始在目标元素上触发事件
- 冒泡阶段:事件从目标元素向它的祖先元素传递,依次触发祖先元素上的事件

注意:如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true,一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false,并且注意,IE8及以下的浏览器中没有捕获阶段,我们可以使用
event.stopPropagation();取消事件传播。
5.1、BOM概述
浏览器对象模型(BOM)使 JavaScript 有能力与浏览器"对话"。
浏览器对象模型(Browser Object Model (BOM))尚无正式标准。
由于现代浏览器已经(几乎)实现了 JavaScript 交互性方面的相同方法和属性,因此常被认为是BOM的方法和属性。
浏览器对象模型(BOM)可以使我们通过JS来操作浏览器,在BOM中为我们提供了一组对象,用来完成对浏览器的操作,常见的BOM对象如下:
- Window:代表的是整个浏览器的窗口,同时window也是网页中的全局对象
- Navigator:代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器
- Location:代表当前浏览器的地址栏信息,通过Location可以获取地址栏信息,或者操作浏览器跳转页面
- History:代表浏览器的历史记录,可以通过该对象来操作浏览器的历史记录,由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页,而且该操作只在当次访问时有效
- Screen:代表用户的屏幕的信息,通过该对象可以获取到用户的显示器的相关的信息
这些BOM对象在浏览器中都是作为window对象的属性保存的,可以通过window对象来使用,也可以直接使用。
5.2、Window对象
5.2.1、弹出框
JavaScript 有三种类型的弹出框:警告框、确认框和提示框。
如果要确保信息传递给用户,通常会使用警告框。当警告框弹出时,用户将需要单击“确定”来继续。
语法
window.alert("sometext");
注意:window.alert() 方法可以不带 window 前缀来写。
实例
alert("我是一个警告框!");
5.2.1.2、确认框
如果您希望用户验证或接受某个东西,则通常使用“确认”框。
当确认框弹出时,用户将不得不单击“确定”或“取消”来继续进行。
如果用户单击“确定”,该框返回 true。如果用户单击“取消”,该框返回 false。
语法
window.confirm("sometext");
注意:window.confirm() 方法可以不带 window 前缀来编写。
实例
var r = confirm("请按按钮");
if (r == true) {
x = "您按了确认!";
} else {
x = "您按了取消!";
}
5.2.1.3、提示框
如果您希望用户在进入页面前输入值,通常会使用提示框。
当提示框弹出时,用户将不得不输入值后单击“确定”或点击“取消”来继续进行。
如果用户单击“确定”,该框返回输入值。如果用户单击“取消”,该框返回 NULL。
语法
window.prompt("sometext","defaultText");
window.prompt() 方法可以不带 window 前缀来编写。
实例
var person = prompt("请输入您的姓名", "比尔盖茨");
if (person != null) {
console.log(person);
}
5.2.2、定时事件
JavaScript 可以在时间间隔内执行,这就是所谓的定时事件( Timing Events)。
window 对象允许以指定的时间间隔执行代码,这些时间间隔称为定时事件。
通过 JavaScript 使用的有两个关键的方法:
-
setTimeout(function, milliseconds)
在等待指定的毫秒数后执行函数。
-
setInterval(function, milliseconds)
等同于 setTimeout(),但持续重复执行该函数。
setTimeout() 和 setInterval() 都属于 window 对象的方法。
setTimeout() 方法:延时器
window.setTimeout(function, milliseconds);
注意:window.setTimeout() 方法可以不带 window 前缀来编写。
- 第一个参数是要执行的函数。
- 第二个参数指示执行之前的毫秒数。
案例演示:单击按钮,等待 3 秒,然后控制台会输出 "Hello"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementById("btn");
btn.onclick = function () {
// 创建延时器
var timer = setTimeout(function () {
console.log("Hello");
}, 3000);
// 清除延时器
// clearTimeout(timer);
};
</script>
</body>
</html>
5.2.2.2、定时器
setInterval() 方法:定时器
setInterval() 方法在每个给定的时间间隔重复给定的函数。
window.setInterval(function, milliseconds);
注意:window.setInterval() 方法可以不带 window 前缀来写。
- 第一个参数是要执行的函数。
- 第二个参数每个执行之间的时间间隔的长度。
案例演示:单击按钮,每隔一秒向控制台输出 "Hello"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn">按钮</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var btn = document.getElementById("btn");
btn.onclick = function () {
// 创建定时器
var timer = setInterval(function () {
console.log("Hello");
}, 1000);
// 清除定时器
// clearInterval(timer);
};
</script>
</body>
</html>
拓展知识:
做一个通用移动函数来实现小汽车(黑色方块)移动的效果
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
/*控制器样式*/
.controller {
width: 600px;
height: 50px;
line-height: 50px;
}
.controller button {
outline: none;
border: none;
margin: 0px;
padding: 0px;
width: 200px;
height: 50px;
font-size: 16px;
line-height: 50px;
text-align: center;
background-color: #E9E9E9;
cursor: pointer;
float: left;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
border: 2px solid #F0F0F0;
}
.controller button:hover {
background-color: #F9F9F9;
}
/*公路样式*/
.road {
width: 100%;
height: 100px;
position: relative;
margin-top: 50px;
background: #3DB1FF;
opacity: .90;
}
.road800 {
width: 800px;
height: 100px;
background: pink;
position: absolute;
top: 0px;
left: 0px;
z-index: 1000;
opacity: .75;
}
.road1200 {
width: 1200px;
height: 100px;
background: orange;
position: absolute;
top: 0px;
left: 0px;
z-index: 500;
}
/*小汽车样式*/
div#car {
width: 135px;
height: 100px;
display: block;
background: black;
position: absolute;
top: 0px;
left: 0px;
z-index: 1500;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
/*border: 1px solid #F0F0F0;*/
}
</style>
</head>
<body>
<div class="controller">
<button id="btn1">移动到800PX</button>
<button id="btn2">移动到1200PX</button>
<button id="btn3">回家</button>
</div>
<div class="road">
<div class="road800"></div>
<div class="road1200"></div>
<div id="car"></div>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
document.getElementById("btn1").onclick = function () {
move(document.getElementById("car"), 800);
};
document.getElementById("btn2").onclick = function () {
move(document.getElementById("car"), 1200);
};
document.getElementById("btn3").onclick = function () {
move(document.getElementById("car"), 0);
};
/*移动函数*/
function move(element, target) {
// 先清理定时器
clearInterval(element.timeId);
// 一会要清理定时器(只产生一个定时器)
element.timeId = setInterval(function () {
// 获取对象当前的位置
var current = element.offsetLeft;
// 每次移动多少像素
var step = 10;
// 判断是往正方向走还是往相反方向走
step = current < target ? step : -step;
// 每次移动后的距离
current += step;
// 判断当前移动后的位置是否到达目标位置
if (Math.abs(target - current) > Math.abs(step)) {
element.style.left = current + "px";
} else {
// 清理定时器
clearInterval(element.timeId);
element.style.left = target + "px";
}
}, 20);
}
</script>
</body>
</html>

5.2.3、常用窗口属性
两个属性可用用于确定浏览器窗口的尺寸。
这两个属性都以像素返回尺寸:
- window.innerHeight - 浏览器窗口的内高度(以像素计)
- window.innerWidth - 浏览器窗口的内宽度(以像素计)
浏览器窗口(浏览器视口)不包括工具栏和滚动条。
对于 Internet Explorer 8, 7, 6, 5:
- document.documentElement.clientHeight
- document.documentElement.clientWidth
或
- document.body.clientHeight
- document.body.clientWidth
一个实用的 JavaScript 解决方案(包括所有浏览器):该例显示浏览器窗口的高度和宽度(不包括工具栏和滚动条)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var w = window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var h = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
console.log(w);
console.log(h);
</script>
</body>
</html>
5.2.4、其它窗口方法
-
window.open() :打开新的窗口
语法介绍:
-
window.open(URL,name,specs,replace);参数介绍:

案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button onclick="openWin()">打开窗口</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function openWin() {
myWindow = window.open('', '', 'width=200,height=100');
myWindow.document.write("<p>这是新建窗口</p>");
}
</script>
</body>
</html>
window.close() :关闭当前窗口
语法介绍:
window.close();
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button onclick="openWin()">打开窗口</button>
<button onclick="closeWin()">关闭窗口</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function openWin() {
myWindow = window.open('', '', 'width=200,height=100');
myWindow.document.write("<p>这是新建窗口</p>");
}
function closeWin() {
myWindow.close();
}
</script>
</body>
</html>
window.moveTo() :移动当前窗口
语法介绍:
window.moveTo(x,y);
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button onclick="openWin()">打开窗口</button>
<button onclick="moveWin()">移动窗口</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function openWin() {
myWindow = window.open('', '', 'width=200,height=100');
myWindow.document.write("<p>这是新建窗口</p>");
}
function moveWin() {
myWindow.moveTo(300, 300);
myWindow.focus();
}
</script>
</body>
</html>
window.resizeTo() :调整当前窗口
语法介绍:
window.resizeTo(width,height);
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button onclick="openWin()">打开窗口</button>
<button onclick="resizeWin()">调整窗口</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function openWin() {
myWindow = window.open('', '', 'width=200,height=100');
myWindow.document.write("<p>这是新建窗口</p>");
}
function resizeWin() {
myWindow.resizeTo(300, 300);
myWindow.focus();
}
</script>
</body>
</html>
5.3、Navigator对象
Navigator代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器,由于历史原因,Navigator对象中的大部分属性都已经不能帮助我们识别浏览器了,一般我们只会使用userAgent来判断浏览器的信息,userAgent是一个字符串,这个字符串中包含有用来描述浏览器信息的内容,不同的浏览器会有不同的userAgent,如下代码:
var ua = navigator.userAgent;
console.log(ua);
谷歌浏览器:
- Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
火狐浏览器:
- Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0
IE11浏览器:
- Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; rv:11.0) like Gecko
IE10浏览器:
- Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
IE9浏览器:
- Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
IE8浏览器:
- Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
IE7浏览器:
- Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
于是乎,我们就可以实现对浏览器类型的判断:
var ua = navigator.userAgent;
if (/firefox/i.test(ua)) {
alert("你是火狐浏览器");
} else if (/chrome/i.test(ua)) {
alert("你是谷歌浏览器");
} else if (/msie/i.test(ua)) {
alert("你是IE5-IE10浏览器");
} else if ("ActiveXObject" in window) {
alert("你是IE11浏览器");
}
注意:在IE11中已经将微软和IE相关的标识都已经去除了,所以我们基本已经不能通过UserAgent来识别一个浏览器是否是IE了,如果通过UserAgent不能判断,还可以通过一些浏览器中特有的对象,来判断浏览器的信息,比如:ActiveXObject
5.4、Location对象
Location对象中封装了浏览器的地址栏的信息,如果直接打印location,则可以获取到地址栏的信息(当前页面的完整路径)
5.4.1、常用属性
常用属性:
console.log(location); //输出location对象
console.log(location.href); //输出当前地址的全路径地址
console.log(location.origin); //输出当前地址的来源
console.log(location.protocol); //输出当前地址的协议
console.log(location.hostname); //输出当前地址的主机名
console.log(location.host); //输出当前地址的主机
console.log(location.port); //输出当前地址的端口号
console.log(location.pathname); //输出当前地址的路径部分
console.log(location.search); //输出当前地址的?后边的参数部分
修改地址:
location = "https://www.baidu.com";
location.href = "https://www.baidu.com";
5.4.2、常用方法
assign():用来跳转到其它的页面,作用和直接修改location一样
location.assign("https://www.baidu.com");
reload():用于重新加载当前页面,作用和刷新按钮一样,如果在方法中传递一个true,作为参数,则会强制清空缓存刷新页面
location.reload(true);
replace():可以使用一个新的页面替换当前页面,调用完毕也会跳转页面,它不会生成历史记录,不能使用回退按钮回退
location.replace("https://www.baidu.com");
5.5、History对象
History对象可以用来操作浏览器向前或向后翻页
5.5.1、常用属性
console.log(history); //输出history对象
console.log(history.length); //可以获取到当成访问的链接数量

5.5.2、常用方法
back():可以回退到上一个页面,作用和浏览器的回退按钮一样
history.back();
forward():可以跳转到下一个页面,作用和浏览器的前进按钮一样
history.forward();
go():可以用来跳转到指定的页面,它需要一个整数作为参数
- 1:表示向前跳转一个页面,相当于forward()
- 2:表示向前跳转两个页面
- -1:表示向后跳转一个页面,相当于back()
- -2:表示向后跳转两个页面
history.go(-2);
5.6、Screen对象
Screen 对象包含有关客户端显示屏幕的信息。
注意:没有应用于 screen 对象的公开标准,不过所有浏览器都支持该对象。
5.6.1、Screen对象描述
每个 Window 对象的 screen 属性都引用一个 Screen 对象。Screen 对象中存放着有关显示浏览器屏幕的信息。JavaScript 程序将利用这些信息来优化它们的输出,以达到用户的显示要求。例如,一个程序可以根据显示器的尺寸选择使用大图像还是使用小图像,它还可以根据显示器的颜色深度选择使用 16 位色还是使用 8 位色的图形。另外,JavaScript 程序还能根据有关屏幕尺寸的信息将新的浏览器窗口定位在屏幕中间。
5.6.2、Screen对象属性
| 属性 | 描述 |
|---|---|
| availHeight | 返回显示屏幕的高度 (除 Windows 任务栏之外)。 |
| availWidth | 返回显示屏幕的宽度 (除 Windows 任务栏之外)。 |
| bufferDepth | 设置或返回调色板的比特深度。 |
| colorDepth | 返回目标设备或缓冲器上的调色板的比特深度。 |
| deviceXDPI | 返回显示屏幕的每英寸水平点数。 |
| deviceYDPI | 返回显示屏幕的每英寸垂直点数。 |
| fontSmoothingEnabled | 返回用户是否在显示控制面板中启用了字体平滑。 |
| height | 返回显示屏幕的高度。 |
| logicalXDPI | 返回显示屏幕每英寸的水平方向的常规点数。 |
| logicalYDPI | 返回显示屏幕每英寸的垂直方向的常规点数。 |
| pixelDepth | 返回显示屏幕的颜色分辨率(比特每像素)。 |
| updateInterval | 设置或返回屏幕的刷新率。 |
| width | 返回显示器屏幕的宽度。 |

1989

被折叠的 条评论
为什么被折叠?



