Files
security-book/02.WEB安全/04.JavaScript基础.md
2025-08-27 14:13:17 +08:00

115 KiB
Raw Permalink Blame History

04.JavaScript基础

1. JavaScript概述

JavaScript是Web开发领域中的一种功能强大的编程语言主要用于开发交互式的Web页面。在计算机、手机等设备上浏览的网页其大多数的交互逻辑几乎都是由JavaScript实现的。 对于制作一个网页而言HTML、CSS和JavaScript分别代表了结构、样式和行为结构是网页的骨架样式是网页的外观行为是网页的交互逻辑

语言 作用 说明
HTML 结构 决定网页的结构和内容,相当于人的身体
CSS 样式 决定网页呈现给用户的模样,相当于给人穿衣服、化妆
JavaScript 行为 实现业务逻辑和页面控制,相当于人的各种动作

JavaScript内嵌于HTML网页中通过浏览器内置的JavaScript引擎进行解释执行把一个原本只用来显示的页面转变成支持用户交互的页面程序。 在1995年时Netscape网景公司现在的Mozilla的布兰登·艾奇Brendan Eich在网景导航者浏览器上首次设计出了JavaScript。Netscape最初将这个脚本语言命名为LiveScript后来Netscape公司与Sun公司2009年被Oracle公司收购合作之后将其改名为JavaScript这是由于当时Sun公司推出的Java语言备受关注Netscape公司为了营销借用了Java这个名称但实际上JavaScript与Java的关系就像“雷锋”与“雷峰塔”它们本质上是两种不同的编程语言。 在设计之初JavaScript是一种可以嵌入到网页中的编程语言用来控制浏览器的行为。例如直接在浏览器中进行表单验证用户只有填写格式正确的内容后才能够提交表单。这样避免了用户因表单填写错误导致的反复提交节省了时间和网络资源。

image-20240127090240149

现在JavaScript的用途已经不仅局限于浏览器了。Node.js的出现使得开发人员能够在服务器端编写JavaScript代码使得JavaScript的应用更加广泛而本书主要针对浏览器端的JavaScript基础进行讲解。学习了JavaScript基础之后读者可以深入学习三大主流框架Vue.js、Angular、React或者进行前端开发、小程序开发或者混合App的开发。推荐读者在掌握JavaScript语言基础后再学习更高级的技术。 JavaScript是由ECMAScript、DOM、BOM三部分组成的

  • ECMAScript是JavaScript的核心。ECMAScript规定了JavaScript的编程语法和基础核心内容是所有浏览器厂商共同遵守的一套JavaScript语法工业标准。
  • DOM文档对象模型是W3C组织推荐的处理可扩展标记语言的标准编程接口通过DOM提供的接口可以对页面上的各种元素进行操作如大小、位置、颜色等
  • BOM浏览器对象模型它提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。通过BOM可以对浏览器窗口进行操作如弹出框、控制浏览器导航跳转等

1.1 代码引入方式

1.1.1 行内式

行内式是指将单行或少量的JavaScript代码写在HTML标签的事件属性中也就是以on开头的属性如onclick。下面通过具体操作步骤进行演示。

1创建一个简单的HTML页面将文件命名为demo01.html。

2编写demo01.html具体代码如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    
</body>
</html>

在上述代码中第4行声明了网页的编码为UTF-8帮助浏览器正确识别网页的编码。在声明编码后还需要确保文件本身的编码也是UTF-8。目前大多数代码编辑器新建的文件编码默认都是UTF-8。另外Windows记事本默认的编码是ANSI在记事本中编写的网页容易出现乱码因此读者应杜绝使用记事本编写代码文件。

3使用行内式编写JavaScript代码实现单击一个按钮后弹出一个警告框显示一些提示信息具体代码如下。

<body>
    <input type="button" value="点我" onclick="alert('Hello World')">
</body>

在上述代码中写在onclick属性里的代码就是JavaScript代码。

4通过浏览器访问demo01.html运行结果如图所示。

image-20240127090250281

以上步骤演示了行内式的使用。在实际开发中使用行内式还需要注意以下4点。 ① 注意单引号和双引号的使用。在HTML中推荐使用双引号而JavaScript推荐使用单引号。 ② 行内式可读性较差尤其是在HTML中编写大量JavaScript代码时不方便阅读。 ③ 在遇到多层引号嵌套的情况时,非常容易混淆,导致代码出错。 ④ 只有临时测试,或者特殊情况下再使用行内式,一般情况下不推荐使用行内式。

1.1.2 内嵌式(嵌入式)

内嵌式是使用<script>标签包裹JavaScript代码<script>标签可以写在<head><body>标签中。通过内嵌式可以将多行JavaScript代码写在<script>标签中。内嵌式是学习JavaScript时最常使用的方式。 下面我们通过具体操作步骤进行演示。 1创建demo02.html用来编写内嵌式JavaScript代码示例代码如下。

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script>
        alert("Hello World")
    </script>
</head>

在上述代码中第4行是一条JavaScript语句其末尾的分号“;”表示该语句结束,后面可以编写下一条语句。<script>标签还有一个type属性在HTML 5中该属性的默认值为“text/javascript”因此在编写时可以省略type属性。

2通过浏览器访问demo02.html页面一打开后就会自动弹出一个警告框提示信息为“Hello World”。

1.1.3 外部式(外链式)

外部式是指将JavaScript代码写在一个单独的文件中一般使用“js”作为文件的扩展名在HTML页面中使用<script>标签进行引入适合JavaScript代码量比较多的情况。 外部式有利于HTML页面代码结构化把大段的JavaScript代码独立到HTML页面之外既美观也方便文件级别的代码复用。需要注意的是外部式的<script>标签内不可以编写JavaScript代码。 下面我们通过具体操作步骤进行演示。

1创建demo03.html用来编写外部式JavaScript代码示例代码如下。

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="test.js"></script>
</head>

2创建test.js文件在文件中编写JavaScript代码如下所示。

alert("Hello World")

3通过浏览器访问demo03.html页面一打开后就会自动弹出一个警告框提示信息为“Hello World”。

1.2 注释

在JavaScript开发过程中使用注释更有利于代码的可读性。注释在程序解析时会被JavaScript解释器忽略。JavaScript支持单行注释和多行注释具体示例如下。

  • 单行注释“//”
  • 多行注释“/* */”

1.3 输入和输出语句

常用的输入和输出语句

语句 说明
alert('msg') 浏览器弹出警告框
console.log('msg') 浏览器控制台输出信息
prompt('msg') 浏览器弹出输入框,用户可以输入内容

2. JavaScript基础

2.1 变量

声明变量

var age;

变量赋值

var age;        // 声明变量
age = 10;        // 为变量赋值

在为变量赋值以后,可以用输出语句输出变量的值

alert(age);       // 使用alert()警告框输出age的值
console.log(age);    // 将age的值输出到控制台中

变量的初始化 声明一个变量并为其赋值,这个过程就是变量的初始化。

var age = 18;

同时声明多个变量

var myName, age, email; // 同时声明多个变量,不赋值
var myName = '小明',
  age = 18,
  email = 'xiaoming@localhost';
// 同时声明多个变量,并赋值 

只声明变量但不赋值则输出变量时结果为undefined

var age;
console.log(age);  // 输出结果undefined

不声明变量,直接输出变量的值,则程序会出错

在对变量进行命名时,需要遵循变量的命名规范,从而避免代码出错,以及提高代码的可读性 ① 由字母、数字、下划线和美元符号($组成如age、num。 ② 严格区分大小写如app和App是两个变量。 ③ 不能以数字开头如18age是错误的变量名。 ④ 不能是关键字、保留字如var、for、while等是错误的变量名。 ⑤ 要尽量做到“见其名知其意”如age表示年龄num表示数字。 ⑥ 建议遵循驼峰命名法首字母小写后面的单词首字母大写如myFirstName。

在JavaScript中关键字分为“保留关键字”和“未来保留关键字”。保留关键字是指在JavaScript语言中被事先定义好并赋予特殊含义的单词不能作为变量名使用。下面我们列举一些常见的保留关键字

break Case catch class
const Continue debugger default
delete Do else export
extends Finally for function
if Import in instanceof
new Return super switch
this Throw try typeof
var Void while with
yield - - -

未来保留关键字是指ECMAScript规范中预留的关键字目前它们没有特殊功能但是在未来的某个时间可能会加上。

enum implements package public
interface private static let
protected - - -

2.2 数据类型

JavaScript是一种弱类型语言不用提前声明变量的数据类型。在程序运行过程中变量的数据类型会被自动确定。 JavaScript中的数据类型分为两大类分别是基本数据类型和复杂数据类型或称为引用数据类型

image-20240127090335016

数字型Number),包含整型值和浮点型值

var num1 = 21;    // 整型值
var num2 = 0.21;   // 浮点型值

布尔型Boolean包含true和false两个布尔值

var bool1 = true ;  // 表示真、1、成立
var bool2 = false;  // 表示假、0、不成立

字符串String用单引号或双引号包裹

var str1 = '';    // 空字符串
var str2 = 'abc';  // 单引号包裹的字符串abc
var str3 = "abc";  // 双引号包裹的字符串abc

Undefined型只有一个值undefined

var a;        // 声明变量"a"未赋值此时a就是undefined
var b = undefined;  // 变量b的值为undefined

Null型只有一个值null

var a = null;    // 变量a的值为null

2.2.1 数值型

JavaScript中的数字型可以用来保存整数或浮点数小数

var age = 18;    // 整数
var pi = 3.14;    // 浮点数(小数)

常见的进制有二进制、八进制、十进制和十六进制。在一般情况下数字都是使用十进制来表示的。在JavaScript中还可以用八进制和十六进制具体如下。 1在数字开头加上0表示八进制数。八进制数由0~7组成逢8进位

var num1 = 07;
console.log(num1);  // 输出结果7
var num2 = 010;
console.log(num2);   // 输出结果8

2在数字开头加上0x表示十六进制数。十六进制数由09af组成

var num1 = 0x9;
console.log(num1);  // 输出结果9
var num2 = 0xa;
console.log(num2);  // 输出结果10

十六进制数中的“x”和“a~f”不区分大小写。

数字型的最大值和最小值可以用如下代码来获取。

console.log(Number.MAX_VALUE);  // 输出结果1.7976931348623157e+308
console.log(Number.MIN_VALUE);  // 输出结果5e-324

在输出结果中使用了科学计数法来表示在JavaScript中可以使用科学计数法来表示数字。

数字型有3个特殊值分别是Infinity无穷大、-Infinity无穷小和NaNNot a Number非数值。下面我们通过代码演示这3种值出现的情况。

console.log(Number.MAX_VALUE * 2);    // 输出结果Infinity

console.log(-Number.MAX_VALUE * 2);   // 输出结果:-Infinity

console.log('abc' - 100);        // 输出结果NaN

若要判断一个变量是否为非数字的类型可以用isNaN()来进行判断它会返回一个布尔值返回true表示非数字返回false表示是数字。示例代码如下。

console.log(isNaN(12));         // 输出结果false

console.log(isNaN('abc'));        // 输出结果true

2.2.2 字符串型

字符串是指计算机中用于表示文本的一系列字符在JavaScript中使用单引号或双引号来包裹字符串 在单引号字符串中可以直接书写双引号,在双引号字符串中也可以直接书写单引号

// 正确的语法

var str1 = 'I am a "programmer"';  // I am a "programmer"

var str2 = "I'm a 'programmer'"; // I'm a 'programmer'

// 常见的错误语法

var str1 = 'I'm a programmer';    // 单引号错误用法

var str2 = "I'm a "programmer""; // 双引号错误用法

var str3 = 'I am a programmer";   // 单双引号混用

在字符串中使用换行、Tab等特殊符号时可以用转义符来进行转义。转义符都是以“\”开始的,常用的转义符如表所示。

转义符 解释说明 转义符 解释说明
' 单引号 " 双引号
\n LF换行n表示newline \v 跳格Tab、水平
\t Tab符号 \r CR换行
\f 换页 \ 反斜线(\
\b 退格b表示blank \0 Null字节
\xhh 由2位十六进制数字hh表示的ISO-8859-1字符。如“\x61”表示“a” \uhhhh 由4位十六进制数字hhhh表示的Unicode字符。如“\u597d”表示“好”
var str1 = 'I\'m a programmer';   // I'm a programmer

var str2 = 'I am a\nprogrammer'   // I am a换行programmer

var str3 = 'C:\\JavaScript\\';    // C:\JavaScript\

var str4 = '**\x61bc**';       // abc

var str5 = '\u597d学生';       // 好学生

字符串长度

var str1 = 'I\'m a programmer';

console.log(str1.length);      // 输出结果16

var str2 = '我是程序员';

console.log(str2.length);      // 输出结果5

访问字符串中的字符

var str = 'I\'m a programmer';

console.log(str[0]);     // 输出结果I

console.log(str[1]);     // 输出结果:'

console.log(str[15]);      // 输出结果r

console.log(str[16]);      // 输出结果undefined

字符串拼接

console.log('a' + 'b');     // ab

console.log('a' + 18);      // a18

console.log('_' + true);   // _true

console.log('12' + 14);     // 1214

console.log(12 + 14);      // 两个数字相加结果为26非字符串拼接

var age = 18;
console.log('小明' + age + '岁'); //小明18岁

2.2.3 布尔型

布尔型有两个值true和false表示事物的“真”和“假”通常用于逻辑判断。 当布尔型和数字型相加的时候true会转换为1false会转换为0

console.log(true + 1);    // 输出结果2

console.log(false + 1);   // 输出结果1

2.2.4 undefined和null

如果一个变量声明后没有赋值则变量的值就是undefined。我们也可以给一个变量赋一个null值null一般用来表示空对象指针

var a;

console.log(a);       // 输出结果undefined

console.log(a + '_');    // 输出结果undefined_字符串型

console.log(a + 1);     // 输出结果NaN

var b = null;

console.log(b + '_');    // 输出结果null_字符串型

console.log(b + 1);     // 输出结果1b转换为0

console.log(b + true);    // 输出结果1b转换为0true转换为1

2.2.5 数据类型检测

在开发中当不确定一个变量或值是什么数据类型的时候可以利用typeof运算符进行数据类型检测。

console.log(typeof 12);       // 输出结果number

console.log(typeof '12');      // 输出结果string

console.log(typeof true);      // 输出结果boolean

console.log(typeof undefined);    // 输出结果undefined

console.log(typeof null);      // 输出结果objectjavascript

<script>
    var age = prompt('请输入年龄');
    document.write('你输入的类型是:'+typeof age);
</script>

上述代码执行后如果用户什么都不输入单击“确定”按钮则age的值为空字符串类型为string。如果单击“取消”按钮则的值为null类型为object。如果输入的是一个数字则age的值是用字符串保存的数字类型为string。

typeof运算符的返回结果是一个字符串可以使用比较运算符“==”来判断typeof返回的检测结果是否符合预期

var a = '12';

console.log(typeof a == 'string');    // 输出结果true

console.log(typeof a == 'number');    // 输出结果false

2.2.6 转换为字符串型

在开发中将数据转换成字符串型时有3种常见的方式

// 先准备一个变量

var num = 3.14;

// 方式1利用“+”拼接字符串(最常用的一种方式)

var str = num + '';

console.log(str, typeof str);        // 输出结果3.14 string

// 方式2利用toString()转换成字符串

var str = num.toString();

console.log(str, typeof str);        // 输出结果3.14 string

// 方式3利用String()转换成字符串

var str = String(num);

console.log(str, typeof str);        // 输出结果3.14 string

2.2.7 转换为数字型

将数据转换为数字型有4种常见的方式

// 方式1使用parseInt()将字符串转为整数

console.log(parseInt('78'));     // 输出结果78

// 方式2使用parseFloat()将字符串转为浮点数

console.log(parseFloat('3.94'));   // 输出结果3.94

// 方式3使用Number()将字符串转为数字型

console.log(Number('3.94'));     // 输出结果3.94

// 方式4利用算术运算符-、*、/)隐式转换

console.log('12' - 1);          // 输出结果11

在将不同类型的数据转换为数字型时,转换结果不同

待转数据 Number()和隐式转换 parseInt() parseFloat()
纯数字字符串 转成对应的数字 转成对应的数字 转成对应的数字
空字符串 0 NaN NaN
数字开头的字符串 NaN 转成开头的数字 转成开头的数字
非数字开头字符串 NaN NaN NaN
Null 0 NaN NaN
undefined NaN NaN NaN
False 0 NaN NaN
True 1 NaN NaN

在转换纯数字时会忽略前面的0如字符串“0123”会被转换为123。如果数字的开头有“+”,会被当成正数,“-”会被当成负数。

console.log(parseInt('03.14'));     // 输出结果3

console.log(parseInt('03.94'));     // 输出结果3

console.log(parseInt('120px'));     // 输出结果120

console.log(parseInt('-120px'));   // 输出结果:-120

console.log(parseInt('a120'));      // 输出结果NaN

使用parseInt()还可以利用第2个参数设置转换的进制

console.log(parseInt('F', 16));      // 输出结果15

2.2.8 转换为布尔型

转换为布尔型使用Boolean()在转换时代表空、否定的值会被转换为false如空字符串、0、NaN、null和undefined其余的值转换为true。

console.log(Boolean(''));      // false

console.log(Boolean(0));     // false

console.log(Boolean(NaN));      // false

console.log(Boolean(null));     // false

console.log(Boolean(undefined)); // false

console.log(Boolean('小白'));    // true

console.log(Boolean(12));      // true

2.3 运算符

算术运算符用于对两个变量或值进行算术运算,与数学上的加、减、乘、除类似

运算符 运算 示例 结果
+ 1 + 5 6
- 8 - 4 4
* 3 * 4 12
/ 3 / 2 1.5
% 取余数(取模) 7 % 5 2

算术运算符的使用看似简单但是在实际应用过程中还需要注意以下4点。 1进行四则混合运算时运算顺序要遵循数学中“先乘除后加减”的原则。例如1 + 2 * 3的计算结果是7。 2在进行取模运算时运算结果的正负取决于被模数%左边的数)的符号,与模数(%右边的数)的符号无关。例如,(-8)%7 = -1而8%(-7)= 1。 3在开发中尽量避免利用浮点数进行运算因为有可能会因JavaScript的精度问题导致结果的偏差。例如0.1 + 0.2正常的计算结果应该是0.3但是JavaScript的计算结果却是0.30000000000000004。此时可以将参与运算的小数转换为整数计算后再转换为小数即可。例如将0.1和0.2分别乘以10相加后再除以100即可得到0.3。 4使用“+”和“-”可以表示正数或负数。例如,(+2.1) + (-1.1)的计算结果为1。

2.3.1 递增和递减运算符

var num = 1;

++num;        // 递增运算符

console.log(num);  // 输出结果2

var a = 1, b = 1;

console.log(++a);  // 输出结果2前置递增

console.log(a);   // 输出结果2

console.log(b++);  // 输出结果1后置递增

console.log(b);   // 输出结果2

增和递减运算符的优先级高于“+”“-”等运算符,在一个表达式中进行计算时,应注意运算顺序

var a = 10;

var b = ++a + 2;   // b = 11 + 2a = 11

var c = b++ + 2;   // c = 13 + 2b = 14

var d = c++ + ++a;  // d = 15 + 12c = 16a = 12

2.3.2 比较运算符

比较运算符用于对两个数据进行比较其结果是一个布尔值即true或false。

运算符 运算 示例 结果
> 大于 5 > 5 false
< 小于 5 < 5 false
>= 大于或等于 5 >= 5 true
<= 小于或等于 5 <= 5 true
== 等于 5 == 4 false
!= 不等于 5 != 4 true
=== 全等 5 === 5 true
!== 不全等 5 !== '5' true

需要注意的是,==!=运算符在进行比较时,如果比较的两个数据的类型不同,会自动转换成相同的类型再进行比较。例如,字符串'123'与数字123比较时首先会将字符串'123'转换成数字123再与123进行比较。而===!==运算符在进行比较时,不仅要比较值是否相等,还要比较数据的类型是否相同。

console.log(3 >= 5);   // 输出结果false

console.log(2 <= 4);   // 输出结果true

console.log(5 == 5);   // 输出结果true

console.log(5 == '5');    // 输出结果true

console.log(5 === 5);    // 输出结果true

console.log(5 === '5');   // 输出结果false

2.3.3 逻辑运算符

逻辑运算符用于对布尔值进行运算,其返回值也是布尔值。在实际开发中,逻辑运算符经常用于多个条件的判断。

运算符 运算 示例 结果
&& a && b a和b都为true结果为true否则为false
|| a || b a和b中至少有一个为true则结果为true否则为false
! !a 若a为false结果为true否则相反
// 逻辑“与”

var res = 2 > 1 && 3 > 1;  // true && true

console.log(res);      // 输出结果true

var res = 2 > 1 && 3 < 1;  // true && false

console.log(res);      // 输出结果false

// 逻辑“或”

var res = 2 > 3 || 1 < 2;  // false || true

console.log(res);      // 输出结果true

var res = 2 > 3 || 1 > 2;  // false || false

console.log(res);      // 输出结果false

// 逻辑“非”(取反)

console.log(!res);      // 输出结果true

逻辑运算符在使用时,是从左到右的顺序进行求值,因此运算时需要注意,可能会出现“短路”的情况,具体如下所示。 1使用“&&”连接两个表达式语法为“表达式1 && 表达式2”。如果表达式1的值为true则返回表达式2的值如果表达式1的值为false则返回false。 2使用“||”连接两个表达式语法为“表达式1 || 表达式2”。如果表达式1的值为true则返回true如果表达式1的值为false则返回表达式2的值。

// “短路”效果演示

console.log(123 && 456);         // 输出结果456

console.log(0 && 456);            // 输出结果0

console.log(0 && 1 + 2 && 456 - 56789);   // 输出结果0

console.log(123 || 456);         // 输出结果123

console.log(0 || 456);            // 输出结果456

// “与”运算时表达式1为false则表达式2不执行

var num = 0; 

console.log(123 && num++);          // 输出结果0

console.log(num);              // 输出结果1

console.log(0 && num++);         // 输出结果0

console.log(num);              // 输出结果1

// “或”运算时表达式1为true则表达式2不执行

var num = 0; 

console.log(123 || num++);          // 输出结果123

console.log(num);              // 输出结果0

console.log(0 || num++);         // 输出结果0

console.log(num);              // 输出结果1

2.3.4 赋值运算符

赋值运算符用于将运算符右边的值赋给左边的变量在JavaScript中除了可以使用“=”进行赋值外,还可以使用 “+=”相加并赋值、“-=”相减并赋值、“*=”相乘并赋值等。

运算符 运算 示例 结果
= 赋值 a = 3; a = 3
+= 加并赋值 a = 3; a += 2; a = 5
-= 减并赋值 a = 3; a -= 2; a = 1
*= 乘并赋值 a = 3; a *= 2; a = 6
/= 除并赋值 a = 3; a /= 2; a = 1.5
%= 求模并赋值 a = 3; a %= 2; a = 1
+= 连接并赋值 a = 'abc'; a += 'def'; a = 'abcdef'
<<= 左移位并赋值 a = 9; a <<= 2; a = 36
>>= 右移位并赋值 a = -9; a >>= 2; a = -3
>>>= 无符号右移位并赋值 a = 9; a >>>= 2; a = 2
&= 按位“与”并赋值 a = 3; a &= 9; a = 1
^= 按位“异或”并赋值 a = 3; a ^= 9; a = 10
|= 按位“或”并赋值 a = 3; a |= 9; a = 11

2.3.5 三元运算符

三元运算符是一种需要三个操作数的运算符,运算的结果根据给定条件决定。

var age = prompt('请输入需要判断的年龄:'); 
var status = age >= 18 ? '已成年' : '未成年';
console.log(status);

2.4 流程控制

2.4.1 if

if

var age = prompt('请输入您的年龄');
if (age == '' || age == null) {
  alert('用户未输入');
}

if...else

var year = prompt('请输入年份');
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
 alert('您输入的年份是闰年');
} else {
 alert('您输入的年份是平年');
}

if...else if image-20240127090439032

if (score >= 90) {
  console.log('优秀');
} else if (score >= 80) {
  console.log('良好');  
} else if (score >=  70) {
  console.log('中等');  
} else if (score >=  60) {
  console.log('及格');  
} else {   console.log('不及格');  
}

2.4.2 switch

image-20240127090442914

var score = prompt('请输入0~100范围内的数字');
switch (parseInt(score / 10)) {
 case 10:
  console.log('满分');
 case 9:
  console.log('优');
  break;
 case 8:
  console.log('良');
  break;
 default:
  console.log('差');
}

2.4.3 for

for (var i = 1; i <= 100; i++) {
 console.log(i);
}

2.4.4 while

var num = 1;
while (num <= 100) {
 console.log(num);
 num++;
}

2.4.5 do...while

var num = 1;
do {
 console.log(num);
 num++;
} while (num <= 100);

2.4.6 continue

for (var i = 1; i <= 5; i++) {
 if (i == 3) {
  continue;  // 跳出本次循环直接跳到i++
 }
 console.log('我吃完了第' + i +'个苹果');
}

2.4.7 break

for (var i = 1; i <= 5; i++) {
 if (i == 3) {
  break;
 }
 console.log('我吃完了第' + i +'个苹果');
}

除此之外break语句还可跳转到指定的标签语句处实现循环嵌套中的多层跳转。

outerloop:
for (var i = 0; i < 10; i++) {
 for (var j = 0; j < 1; j++) {
  if (i == 3) {
   break outerloop;
  }
  console.log('i = ' + i + ', j = ' + j);
 }
}

2.5 数组

在JavaScript中创建数组有两种常见的方式一种是使用“new Array()”创建数组,另一种是使用“[ ]”字面量来创建数组。

// 使用new Array()创建数组

var arr1 = new Array();                 // 空数组

var arr2 = new Array('苹果', '橘子', '香蕉', '桃子');   // 含有4个元素

// 使用字面量来创建数组

var arr1 = [];                      // 空数组

var arr2 = ['苹果', '橘子', '香蕉', '桃子'];        // 含有4个元素

上述代码演示了如何创建空数组以及如何创建和含有4个元素的数组。 在数组中可以存放任意类型的元素

// 在数组中保存各种常见的数据类型

var arr1 = [123, 'abc', true, null, undefined];

// 在数组中保存数组

var arr2 = [1, [21, 22], 3];

2.5.1 访问数据元素

在数组中每个元素都有索引或称为下标数组中的元素使用索引来进行访问。数组中的索引是一个数字从0开始

image-20240127090451961

var arr = ['苹果', '橘子', '香蕉', '桃子'];
console.log(arr[0]);  // 输出结果:苹果
console.log(arr[1]);  // 输出结果:橘子
console.log(arr[2]);  // 输出结果:香蕉
console.log(arr[3]);  // 输出结果:桃子
console.log(arr[4]);  // 输出结果undefined数组元素不存在

2.5.2 数组遍历

var arr = [80, 75, 69, 95, 92, 88, 76];
var sum = 0;
for (var i = 0; i < arr.length; i++) {
 sum += arr[i];
}
var avg = sum / arr.length;
console.log(avg.toFixed(2));  // 输出结果82.14

2.5.3 修改数组长度

使用“数组名.length”可以获取或修改数组的长度。数组长度的计算方式为数组中元素的最大索引值加1

var arr = ['a', 'b', 'c'];
console.log(arr.length);    // 输出结果3

在上述代码中数组中最后一个元素是c该元素的索引为2因此数组长度为3。 使用arr.length不仅可以获取数组长度还可以修改数组长度

var arr1 = [1, 2];

arr1.length = 4;     // 大于原有长度

console.log(arr1);    // 输出结果:(4) [1, 2, empty × 2]

var arr2 = [1, 2, 3, 4];

arr2.length = 2;     // 小于原有长度 

console.log(arr2);    // 输出结果:(2) [1, 2]

在console.log()的输出结果中,前面的“(4)”表示数组的长度为4后面显示的是数组中的元素empty表示空元素。若length的值大于数组中原来的元素个数则缺少的元素会占用索引位置成为空元素若length的值小于数组中原来的元素个数多余的数组元素将会被舍弃。 当访问空元素时返回结果为undefined

var arr = [1];

arr.length = 4;      // 修改数组的长度为4

console.log(arr);     // 输出结果:(4) [1, empty × 3]

console.log(arr[1]); // 输出结果undefined

除了上述情况外还有如下3种常见的情况也会出现空元素。

// 情况1在使用字面量创建数组时出现空元素

var arr = [1, 2, , 4];

console.log(arr);    // 输出结果:(4) [1, 2, empty, 4]

// 情况2在new Array()中传入数组长度的参数

var arr = new Array(4);

console.log(arr);    // 输出结果:(4) [empty × 4]

// 情况3为数组添加索引不连续的元素

var arr = [1];

arr[3] = 4;       // 向数组中添加一个元素索引为3

console.log(arr);    // 输出结果:(4) [1, empty × 2, 4]

2.5.4 新增或修改数组元素

通过数组索引可以新增或修改数组元素,如果给定的索引超过了数组中的最大索引,则表示新增元素,否则表示修改元素。

var arr = ['red', 'green', 'blue'];

arr[3] = 'pink';   // 新增元素

console.log(arr);  // (4) ["red", "green", "blue", "pink"]

arr[0] = 'yellow';  // 修改元素

console.log(arr);  // (4) ["yellow", "green", "blue", "pink"]

通过循环语句可以很方便地为数组添加多个元素

var arr = [];
for (var i = 0; i < 10; i++) {
 arr[i] = i + 1;
}
console.log(arr);  // 输出结果:(10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

2.5.5 筛选数组

在开发中经常会遇到筛选数组的需求。例如将一个数组中所有大于或等于10的元素筛选出来放入到新的数组中

var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
var newArr = [];
var j = 0;
for (var i = 0; i < arr.length; i++) {
 if (arr[i] >= 10) {
  newArr[j++] = arr[i];  // 新数组索引号从0开始依次递增
 }
}
console.log(newArr);    // 输出结果:(3) [77, 52, 25]

2.5.6 删除指定的数组元素

在数组中删除指定的数组元素其思路和前面讲解的筛选数组的思路类似。例如将一个数组中所有数值为0的元素删除

var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
 if (arr[i] != 0) {
  newArr[newArr.length] = arr[i];
 }
}
console.log(newArr);  // 输出结果:(7) [2, 6, 1, 77, 52, 25, 7]

2.5.7 反转数组元素顺序

本案例要求将一个数组中所有元素的顺序反过来。例如,有一个数组为['red', 'green', 'blue', 'pink', 'purple'],反转结果为['purple', 'pink', 'blue', 'green', 'red']。若要实现这个效果就需要改变数组遍历的顺序从数组的最后1个元素遍历到第1个元素将遍历到的每个元素添加到新的数组中即可完成数组的反转。

var arr = ['red', 'green', 'blue', 'pink', 'purple'];
var newArr = [];
for (var i = arr.length - 1; i >= 0; i--) {
 newArr[newArr.length] = arr[i];
}
// 输出结果:(5) ["purple", "pink", "blue", "green", "red"]
console.log(newArr);

3. JavaScript函数

函数在使用时分为两步,声明函数和调用函数

function 函数名() {

 // 函数体代码

}

在上述语法中function是声明函数的关键字必须全部使用小写字母。 当函数声明后,里面的代码不会执行,只有调用函数的时候才会执行。调用函数的语法为“函数名()”。

// 声明函数

function sayHello() {

 console.log('hello');

}

// 调用函数

sayHello();

JavaScript函数参数的使用非常灵活它允许函数的形参和实参个数不同。当实参数量多于形参数量时函数可以正常执行多余的实参由于没有形参接收会被忽略除非用其他方式如后面学到的arguments才能获得多余的实参。当实参数量小于形参数量时多出来的形参类似于一个已声明未赋值的变量其值为undefined。

function getSum(num1, num2) {
 console.log(num1, num2);
}
getSum(1, 2, 3);  // 实参数量大于形参数量输出结果1 2
getSum(1);     // 实参数量小于形参数量输出结果1 undefined

3.1 函数返回值

函数的返回值是通过return语句来实现的

function 函数名() {

 return 要返回的值;     // 利用return返回一个值给调用者

}

如果函数没有使用return返回一个值则函数调用后获取到的返回结果为undefined。

function getResult() {
 // 该函数没有return
}
console.log(getResult());  // 输出结果undefined

3.2 函数表达式

函数表达式是将声明的函数赋值给一个变量,通过变量完成函数的调用和参数的传递。

var sum = function(num1, num2) {   // 函数表达式

 return num1 + num2;

};

console.log(sum(1, 2));         // 调用函数输出结果3

从上述代码可以看出函数表达式与函数声明的定义方式几乎相同不同的是函数表达式的定义必须在调用前而函数声明的方式则不限制声明与调用的顺序。由于sum是一个变量名给这个变量赋值的函数没有函数名所以这个函数也称为匿名函数。将匿名函数赋值给了变量sum以后变量sum就能像函数一样调用。

3.3 回调函数

所谓回调函数指的就是一个函数A作为参数传递给一个函数B然后在B的函数体内调用函数A。此时我们称函数A为回调函数。其中匿名函数常用作函数的参数传递实现回调函数。

function cal(num1, num2, fn) {
 return fn(num1, num2);
}
console.log(cal(45, 55, function (a, b) {
 return a + b;
}));
console.log(cal(10, 20, function (a, b) {
 return a * b;
}));

3.4 递归调用

递归调用是函数嵌套调用中一种特殊的调用。它指的是一个函数在其函数体内调用自身的过程,这种函数称为递归函数。需要注意的是,递归函数只有在特定的情况下使用,如计算阶乘。

function factorial(n) {   // 定义回调函数
 if (n == 1) {
  return 1;         // 递归出口
 }
 return n * factorial(n - 1);
}
var n = prompt('求n的阶乘\n n是大于等于1的正整数如2表示求2!。');
n = parseInt(n);
if (isNaN(n)) {
 console.log('输入的n值不合法');
} else {
 console.log(n + '的阶乘为:' + factorial(n));
}

3.5 作用域

变量需要先声明后使用但这并不意味着声明变量后就可以在任意位置使用该变量。例如在函数中声明一个age变量在函数外进行访问就会出现age变量未定义的错误 ① 全局变量不在任何函数内声明的变量显式定义或在函数内省略var声明的变量隐式定义都称为全局变量它在同一个页面文件中的所有脚本内都可以使用。 ② 局部变量在函数体内利用var关键字定义的变量称为局部变量它仅在该函数体内有效。 ③ 块级变量ES 6提供的let关键字声明的变量称为块级变量仅在“{}”中间有效如if、for或while语句等。

// 全局作用域

var num = 10;    // 全局变量

function fn() {

 // 局部作用域

 var num = 20;   // 局部变量

 console.log(num); // 输出局部变量num的值输出结果20

}

fn();

console.log(num);  // 输出全局变量10的值输出结果10

3.6 闭包函数

在JavaScript中内嵌函数可以访问定义在外层函数中的所有变量和函数并包括其外层函数能访问的所有变量和函数。但是在函数外部则不能访问函数的内部变量和嵌套函数。此时就可以使用“闭包”来实现。 所谓“闭包”指的就是有权访问另一函数作用域内变量(局部变量)的函数。它最主要的用途是以下两点: ① 可以在函数外部读取函数内部的变量; ② 可以让变量的值始终保持在内存中。 需要注意的是,由于闭包会使得函数中的变量一直被保存在内存中,内存消耗很大,所以闭包的滥用可能会降低程序的处理速度,造成内存消耗等问题。

function fn() {
 var times = 0;
 var c = function () {
  return ++times;
 };
 return c;
}
var count = fn();    // 保存fn()返回的函数此时count就是一个闭包
// 访问测试
console.log(count());  // 输出结果1
console.log(count());  // 输出结果2
console.log(count());  // 输出结果3
console.log(count());  // 输出结果4
console.log(count());  // 输出结果5

3.7 预解析

JavaScript代码是由浏览器中的JavaScript解析器来执行的JavaScript解析器在运行JavaScript代码的时候会进行预解析也就是提前对代码中的var变量声明和function函数声明进行解析然后再去执行其他的代码。

// 以下代码中的var num变量声明会进行预解析
console.log(num);  // 输出结果undefined
var num = 10;
// 以下代码由于不存在var num2所以会报错
console.log(num2); // 报错提示num2 is not definednum2未定义

由此可见由于num的变量声明被预解析所以console.log(num)不会报错并且由于赋值操作num = 10不会被预解析所以此时num的值为undefined。 同样JavaScript中的函数也具有预解析的效果

fn();
function fn() {
 console.log('fn');
}

在上述代码中fn()函数调用的代码写在了函数声明的前面但函数仍然可以正确调用这是因为function函数声明操作也会被预解析。 需要注意的是,函数表达式不会被预解析

fun(); // 报错提示fun is not a functionfun不是一个函数
var fun = function() {
 console.log('fn');
};

上述代码提示fun不是一个函数这是因为var fun变量声明会被预解析预解析后fun的值为undefined此时的fun还不是一个函数所以无法调用只有第2~4行代码执行后才可以通过fun()来调用函数。

4. JavaScript对象

在现实生活中,对象是一个具体的事物,是一种看得见、摸得着的东西。例如,一本书、一辆汽车、一个人,可以看成是“对象”。在计算机中,一个网页、一个与远程服务器建立的连接也可以看成是“对象”。 在JavaScript中对象是一种数据类型它是由属性和方法组成的一个集合。属性是指事物的特征方法是指事物的行为。例如在JavaScript中描述一个手机对象则手机拥有的属性和方法如下所示。

  • 手机的属性:颜色、重量、屏幕尺寸。
  • 手机的方法:打电话、发短信、看视频、听音乐。

在代码中,属性可以看成是对象中保存的一个变量,使用“对象.属性名”,方法可以看成是对象中保存的一个函数,使用“对象.方法名()”进行访问。假设现在有一个手机对象p1则可以用以下代码来访问p1的属性或调用p1的方法。

// 假设现在有一个手机对象p1通过代码创建出来

var p1 = {

 color: '黑色',

 weight: '188g',

 screenSize: '6.5',

 call: function(num) {

  console.log('打电话给' + num);

},

sendMessage: function(num, message) {

 console.log('给' + num + '发短信,内容为:' + message);

},

playVideo: function() {

 console.log('播放视频');

},

playMusic: function() {

 console.log('播放音乐');

}

};

// 访问p1的属性

console.log(p1.color);    // 输出结果:“黑色”,表示手机的颜色为黑色

console.log(p1.weight);   // 输出结果“188g”表示手机的重量为188克

console.log(p1.screenSize); // 输出结果“6.5”表示手机的屏幕尺寸为6.5英寸

// 调用p1的方法

p1.call('123');           // 调用手机的拨打电话方法拨打号码为123

p1.sendMessage('123', 'hello');   // 给电话号码123发短信内容为hello

p1.playVideo();           // 调用手机的播放视频方法

p1.playMusic();           // 调用手机的播放音乐方法

4.1 访问对象的属性和方法

在将对象创建好以后,就可以访问对象的属性和方法

// 访问对象的属性语法1

console.log(stu1.name);   // 输出结果:小明

// 访问对象的属性语法2

console.log(stu1['age']);  // 输出结果18

// 调用对象的方法语法1

stu1.sayHello();       // 输出结果Hello

// 调用对象的方法语法2

stu1['sayHello']();   // 输出结果Hello

如果对象的成员名中包含特殊字符,则可以用字符串来表示

var obj = {

'name-age': '小明-18'

};

console.log(obj['name-age']);  // 输出结果:“小明-18”

JavaScript中的对象具有动态特征如果一个对象没有成员用户可以手动赋值属性或方法来添加成员。

var stu2 = {};            // 创建一个空对象

stu2.name = 'Jack';         // 为对象增加name属性

stu2.introduce = function() {    // 为对象增加introduce方法

 alert('My name is ' + this.name); // 在方法中使用this代表当前对象

};

alert(stu2.name);    // 访问name属性输出结果Jack

stu2.introduce();    // 调用introduce()方法输出结果My name is Jack

在上述代码中在对象的方法中可以用this来表示对象自身因此使用this.name就可以访问对象的name属性。 如果访问对象中不存在的属性时会返回undefined。

var stu3 = {};        // 创建一个空对象

console.log(stu3.name);   // 输出结果undefined

4.2 利用new Object创建对象

前面在学习数组时可以使用new Array创建数组对象而数组是一种特殊的对象如果要创建一个普通的对象则使用new Object进行创建。

var obj = new Object();   // 创建了一个空对象

obj.name = '小明';      // 创建对象后,为对象添加成员

obj.age = 18;

obj.sex = '男';

obj.sayHello = function() {

 console.log('Hello');

};

4.3 利用构造函数创建对象

前面学习的字面量的方式只适合创建一个对象而当需要创建多个对象时还要将对象的每个成员都写一遍显得比较麻烦因此可以用构造函数来创建对象。使用构造函数创建对象的语法为“new 构造函数名()”在小括号中可以传递参数给构造函数如果没有参数小括号可以省略。实际上“new Object()”就是一种使用构造函数创建对象的方式Object就是构造函数的名称但这种方式创建出来的是一个空对象。如果我们想要创建的是一些具有相同特征的对象则可以自己写一个构造函数。

// 编写构造函数

function 构造函数名() {

 this.属性 = 属性;

 this.方法 = function() {

  // 方法体

};

}

// 使用构造函数创建对象

var obj = new 构造函数名();

在上述代码中构造函数中的this表示新创建出来的对象在构造函数中可以通过this来为新创建出来的对象添加成员。需要注意的是构造函数的名称推荐首字母大写。 下面我们通过代码演示如何编写一个Student构造函数并创建对象

// 编写一个Student构造函数

function Student(name, age) {

 this.name = name;

 this.age = age;

 this.sayHello = function() {

  console.log('你好,我叫' + this.name);

};

}

// 使用Student构造函数创建对象

var stu1 = new Student('小明', 18);

console.log(stu1.name);       // 输出结果:小明

console.log(stu1.sayHello());    // 输出结果:你好,我叫小明

var stu2 = new Student('小红', 17);

console.log(stu2.name);       // 输出结果:小红

console.log(stu2.sayHello());    // 输出结果:你好,我叫小红

使用for…in语法可以遍历对象中的所有属性和方法

// 准备一个待遍历的对象

var obj = { name: '小明', age: 18, sex: '男' };

// 遍历obj对象

for (var k in obj) {

 // 通过k可以获取遍历过程中的属性名或方法名

 console.log(k);    // 依次输出name、age、sex

 console.log(obj[k]);  // 依次输出小明、18、男

}

在上述代码中k是一个变量名可以自定义习惯上命名为k或者key表示键名。当遍历到每个成员时使用k来获取当前成员的名称使用obj[k]获取对应的值。另外如果对象中包含方法则可以通过“objk”进行调用。

4.4 Math对象

Math对象用来对数字进行与数学相关的运算该对象不是构造函数不需要实例化对象可以直接使用其静态属性和静态方法。

成员 作用
PI 获取圆周率结果为3.141592653589793
abs(x) 获取x的绝对值可传入普通数值或是用字符串表示的数值
max([value1[,value2, ...]]) 获取所有参数中的最大值
min([value1[,value2, ...]]) 获取所有参数中的最小值
pow(base, exponent) 获取基数base的指数exponent次幂即 baseexponent
sqrt(x) 获取x的平方根
ceil(x) 获取大于或等于x的最小整数即向上取整
floor(x) 获取小于或等于x的最大整数即向下取整
round(x) 获取x的四舍五入后的整数值
random() 获取大于或等于0.0且小于1.0的随机值
Math.PI;        // 获取圆周率

Math.abs(-25);      // 获取绝对值返回结果25

Math.abs('-25');     // 获取绝对值自动转换为数字返回结果25

Math.max(5, 7, 9, 8);  // 获取最大值返回结果9

Math.min(6, 2, 5, 3);  // 获取最小值返回结果2

Math.pow(2, 4);     // 获取2的4次幂返回结果16

Math.sqrt(9);      // 获取9的平方根返回结果为3

Math.ceil(1.1);     // 向上取整返回结果2

Math.ceil(1.9);     // 向上取整返回结果2

Math.floor(1.1);    // 向下取整返回结果1

Math.floor(1.9);    // 向下取整返回结果1

Math.round(1.1);     // 四舍五入返回结果1

Math.round(1.5);     // 四舍五入返回结果2

Math.round(1.9);     // 四舍五入返回结果2

Math.round(-1.5);    // 四舍五入,返回结果:-1取较大值

Math.round(-1.6);    // 四舍五入,返回结果:-2

4.4.1 生成指定范围的随机数

Math.random()用来获取随机数,每次调用该方法返回的结果都不同。该方法返回的结果是一个很长的浮点数 由于Math.random()返回的这个随机数不太常用我们可以借助一些数学公式来转换成任意范围内的随机数公式为“Math.random() * (max - min) + min”表示生成大于或等于min且小于max的随机值。

Math.random() * (3 - 1) + 1;    //  1 ≤ 返回结果 < 3

Math.random() * (20 - 10) + 10;   // 10 ≤ 返回结果 < 20

Math.random() * (99 - 88) + 88;   // 88 ≤ 返回结果 < 99

上述代码的返回结果是浮点数当需要获取整数结果时可以搭配Math.floor()来实现。下面我们通过代码演示如何获取1~3范围内的随机整数返回结果可能是1、2或3。

function getRandom(min, max) {
 return Math.floor(Math.random() * (max - min + 1) + min);
}
console.log(getRandom(1, 3));  // 最小值1最大值3

上述代码中第2行用来生成min到max之间的随机整数包含min和max。另外还可以使用Math.floor(Math.random() * (max + 1))表示生成0到max之间的随机整数使用Math.floor(Math.random() * (max + 1) + 1)表示生成1到max之间的随机整数。 利用随机数,可以实现在数组中随机获取一个元素

var arr = ['apple', 'banana', 'orange', 'pear'];
// 调用前面编写的getRandom()函数获取随机数
console.log(arr[getRandom(0, arr.length - 1)]);

4.4.2 猜数字游戏

function getRandom(min, max) {
 return Math.floor(Math.random() * (max - min + 1) + min);
}
var random = getRandom(1, 10);
while (true) { // 死循环利用第13行的break来跳出循环
 var num = prompt('猜数字范围在1~10之间。');
 if (num > random) {
  alert('你猜大了');
 } else if (num < random) {
  alert('你猜小了')
 } else {
  alert('恭喜你,猜对了');
  break;
 }
}

4.5 日期对象

JavaScript中的日期对象需要使用new Date()实例化对象才能使用Date()是日期对象的构造函数。在创建日期对象时可以为Date()构造函数可以传入一些参数,来表示具体的日期

// 方式1没有参数使用当前系统的当前时间作为对象保存的时间
var date1 = new Date();
// 输出结果Wed Oct 16 2019 10:57:56 GMT+0800 (中国标准时间)
console.log(date1); 

// 方式2传入年、月、日、时、分、秒月的范围是0~11即真实月份-1
var date2 = new Date(2019, 10, 16, 10, 57, 56);
// 输出结果Sat Nov 16 2019 10:57:56 GMT+0800 (中国标准时间)
console.log(date2); 

// 方式3用字符串表示日期和时间
var date3 = new Date('2019-10-16 10:57:56');
// 输出结果Wed Oct 16 2019 10:57:56 GMT+0800 (中国标准时间)
console.log(date3);

日期对象的常用方法分为get和set两大类 Date对象的常用get方法

方法 作用
getFullYear() 获取表示年份的4位数字如2020
getMonth() 获取月份范围0~110表示一月1表示二月依次类推
getDate() 获取月份中的某一天范围1~31
getDay() 获取星期范围0~60表示星期日1表示星期一依次类推
getHours() 获取小时数返回0~23
getMinutes() 获取分钟数范围0~59
getSeconds() 获取秒数范围0~59
getMilliseconds() 获取毫秒数范围0~999
getTime() 获取从1970-01-01 00:00:00距离Date对象所代表时间的毫秒数

Date对象的常用set方法

方法 作用
setFullYear(value) 设置年份
setMonth(value) 设置月份
setDate(value) 设置月份中的某一天
setHours(value) 设置小时数
setMinutes(value) 设置分钟数
setSeconds(value) 设置秒数
setMilliseconds(value) 设置毫秒数
setTime(value) 通过从1970-01-01 00:00:00计时的毫秒数来设置时间
var date = new Date();        // 基于当前日期时间创建Date对象
var year = date.getFullYear();   // 获取年
var month = date.getMonth();     // 获取月
var day = date.getDate();       // 获取日

// 通过数组将星期值转换为字符串
var week = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; 

// 输出date对象保存的时间示例今天是2019年9月16日 星期三
console.log('今天是' + year + '年' + month + '月' + day + '日 ' + week[date.getDay()]);

在开发中,还经常需要将日期对象中的时间转换成指定的格式

// 返回当前时间,格式为:时:分:秒,用两位数字表示
function getTime() {
 var time = new Date();
 var h = time.getHours();
 h = h < 10 ? '0' + h : h;
 var m = time.getMinutes();
 m = m < 10 ? '0' + m : m;
 var s = time.getSeconds();
 s = s < 10 ? '0' + s : s;
 return h + ':' + m + ':' + s;
}
console.log(getTime());   // 输出结果示例10:07:56

4.5.1 统计代码执行时间

通过日期对象可以获取从1970年1月1日0时0分0秒开始一直到当前UTC时间所经过的毫秒数这个值可以作为时间戳来使用。通过时间戳可以计算两个时间之间的时间差还可以用于加密、数字签名等技术中。获取时间戳常见的方式如下。

 // 方式1通过日期对象的valueof()或getTime()方法

var date1 = new Date();

console.log(date1.valueOf());  // 示例结果1571196996188

console.log(date1.getTime());  // 示例结果1571196996188

// 方式2使用“+”运算符转换为数值型

var date2 = +new Date();

console.log(date2);       // 示例结果1571196996190

// 方式3使用HTML5新增的Date.now()方法

console.log(Date.now());    // 示例结果1571196996190

在掌握如何获取到时间戳后,下面我们来完成案例的代码编写

var timestamp1 = +new Date();
for (var i = 1, str = ''; i <= 90000; i++) {
 str += i;
}
var timestamp2 = +new Date();
// 示例结果代码执行时间37毫秒
console.log('代码执行时间:' + (timestamp2 - timestamp1) + '毫秒');

4.5.2 倒计时

function countDown(time) {
 var nowTime = +new Date();
 var inputTime = +new Date(time);
 var times = (inputTime - nowTime) / 1000;
 var d = parseInt(times / 60 / 60 / 24);
 d = d < 10 ? '0' + d : d;
 var h = parseInt(times / 60 / 60 % 24);
 h = h < 10 ? '0' + h : h;
 var m = parseInt(times / 60 % 60);
 m = m < 10 ? '0' + m : m;
 var s = parseInt(times % 60);
 s = s < 10 ? '0' + s : s;
 return d + '天' + h + '时' + m + '分' + s + '秒';
}
// 示例结果05天23时06分10秒
console.log(countDown('2019-10-22 10:56:57'));

4.6 数组对象

在开发中有时候需要检测变量的类型是否为数组。例如在函数中要求传入的参数必须是一个数组不能传入其他类型的值否则会出错所以这时候可以在函数中检测参数的类型是否为数组。数组类型检测有两种常用的方式分别是使用instanceof运算符和使用Array.isArray()方法。

var arr = [];
var obj = {};
// 第1种方式
console.log(arr instanceof Array);   // 输出结果true
console.log(obj instanceof Array);   // 输出结果false
// 第2种方式
console.log(Array.isArray(arr));    // 输出结果true
console.log(Array.isArray(obj));    // 输出结果false

4.6.1 添加或删除数组元素

方法名 功能描述 返回值
push(参数1…) 数组末尾添加一个或多个元素,会修改原数组 返回数组的新长度
unshift(参数1…) 数组开头添加一个或多个元素,会修改原数组 返回数组的新长度
pop() 删除数组的最后一个元素若是空数组则返回undefined会修改原数组 返回删除的元素的值
shift() 删除数组的第一个元素若是空数组则返回undefined会修改原数组 返回第一个元素的值

需要注意的是push()和unshift()方法的返回值是新数组的长度而pop()和shift()方法返回的是移出的数组元素

<script>
 var arr = ['Rose', 'Lily'];
 console.log('原数组:' + arr);
 var last = arr.pop();
 console.log('在末尾移出元素:' + last + ' - 移出后数组:' + arr);
 var len = arr.push('Tulip', 'Jasmine');
 console.log('在末尾添加元素后长度变为:' + len + ' - 添加后数组:' + arr);
 var first = arr.shift();
 console.log('在开头移出元素:' + first + ' - 移出后数组:' + arr);
 len = arr.unshift('Balsam', 'sunflower');
 console.log('在开头添加元素后长度变为:' + len + ' - 添加后数组:' + arr);
</script>

4.6.2 数组排序

方法名 功能描述
reverse() 颠倒数组中元素的位置,该方法会改变原数组,返回新数组
sort() 对数组的元素进行排序,该方法会改变原数组,返回新数组

需要注意的是reverse()和sort()方法的返回值是新数组的长度。

// 反转数组
var arr = ['red', 'green', 'blue'];
arr.reverse();
console.log(arr);  // 输出结果:(3) ["blue", "green", "red"]
// 数组排序
var arr1 = [13, 4, 77, 1, 7];
arr1.sort(function(a, b) {
 return b - a;   // 降序的顺序排列
});
console.log(arr1); // 输出结果:(5) [77, 13, 7, 4, 1]

4.6.3 数组索引

在开发中若要查找指定的元素在数组中的位置则可以利用Array对象提供的检索方法

方法名 功能描述
indexOf() 返回在数组中可以找到给定值的第一个索引,如果不存在,则返回-1
lastIndexOf() 返回指定元素在数组中的最后一个的索引,如果不存在则返回-1

上述方法中,默认都是从指定数组索引的位置开始检索,并且检索方式与运算符“===”相同,即只有全等时才会返回比较成功的结果。

var arr = ['red', 'green', 'blue', 'pink', 'blue'];
console.log(arr.indexOf('blue'));    // 输出结果2
console.log(arr.lastIndexOf('blue'));  // 输出结果4

4.6.4 数组转换为字符串

在开发中若需要将数组转换为字符串可以利用数组对象的join()和toString()方法实现。

方法名称 功能描述
toString() 把数组转换为字符串,逗号分隔每一项
join('分隔符') 将数组的所有元素连接到一个字符串中
// 使用toString()

var arr = ['a', 'b', 'c'];

console.log(arr.toString()); // 输出结果a,b,c

// 使用join()

console.log(arr.join());   // 输出结果a,b,c

console.log(arr.join(''));    // 输出结果abc

console.log(arr.join('-'));   // 输出结果a-b-c

4.6.5 其他方法

方法名 功能描述
fill() 用一个固定值填充数组中指定下标范围内的全部元素
splice() 数组删除参数为splice(第几个开始, 要删除个数),返回被删除项目的新数组
slice() 数组截取参数为slice(begin, end),返回被截取项目的新数组
concat() 连接两个或多个数组,不影响原数组,返回一个新数组

接下来我们以splice()方法为例,演示如何在指定位置添加或删除数组元素

var arr = ['sky', 'wind', 'snow', 'sun'];
// 从索引为2的位置开始删除2个元素
arr.splice(2, 2);
console.log(arr);    // 输出结果:(2) ["sky", "wind"]
// 从索引为1的位置开始删除1个元素后再添加snow元素
arr.splice(1, 1, 'snow');
console.log(arr);    // 输出结果:(2) ["sky", "snow"]
// 从索引为1的位置开始添加数组元素
arr.splice(1, 0, 'hail', 'sun');
console.log(arr);    // 输出结果:(4) ["sky", "hail", "sun", "snow"]

4.7 字符串对象

字符串对象使用new String()来创建在String构造函数中传入字符串就会在返回的字符串对象中保存这个字符串。

var str = new String('apple'); // 创建字符串对象

console.log(str);        // 输出结果String {"apple"}

console.log(str.length);    // 获取字符串长度输出结果5

在前面的学习中可以使用字符串变量.length的方式进行获取这种方式很像是在访问一对象的length属性示例代码如下

var str = 'apple';

console.log(str.length);    // 输出结果5

实际上字符串在JavaScript中是一种基本包装类型。JavaScript中的基本包装类型包括String、Number和Boolean用来把基本数据类型包装成为复杂数据类型从而使基本数据类型也有了属性和方法。

需要注意的是虽然JavaScript基本包装类型的机制可以使普通变量也能像对象一样访问属性和方法但它们并不属于对象类型示例代码如下。

var obj = new String('Hello');
console.log(typeof obj);        // 输出结果object
console.log(obj instanceof String); // 输出结果true
var str = 'Hello';
console.log(typeof str);        // 输出结果string
console.log(str instanceof String); // 输出结果false

4.7.1 根据字符返回位置

成员 作用
indexOf(searchValue) 获取searchValue在字符串中首次出现的位置
lastIndexOf(searchValue) 获取searchValue在字符串中最后出现的位置
var str = 'HelloWorld';

str.indexOf('o');    // 获取“o”在字符串中首次出现的位置返回结果4

str.lastIndexOf('o'); // 获取“o”在字符串中最后出现的位置返回结果6

要求在一组字符串中,找到所有指定元素出现的位置以及次数。字符串为 ' Hello World, Hello JavaScript '。

var str = 'Hello World, Hello JavaScript';
var index = str.indexOf('o');
var num = 0;
while (index != -1) {
 console.log(index);          // 依次输出4、7、17
 index = str.indexOf('o', index + 1);
 num++;
}
console.log('o出现的次数是' + num); // o出现的次数是3

4.7.2 根据位置返回字符

成员 作用
charAt(index) 获取index位置的字符位置从0开始计算
charCodeAt(index) 获取index位置的字符的ASCII码
str[index] 获取指定位置处的字符HTML5新增
var str = 'Apple';
console.log(str.charAt(3));     // 输出结果l
console.log(str.charCodeAt(0));   // 输出结果65字符A的ASCII码为65
console.log(str[0]);        // 输出结果A

4.7.3 字符串操作

成员 作用
concat(str1, str2, str3…) 连接多个字符串
slice(start,[ end]) 截取从start位置到end位置之间的一个子字符串
substring(start[, end]) 截取从start位置到end位置之间的一个子字符串基本和slice相同但是不接收负值
substr(start[, length]) 截取从start位置开始到length长度的子字符串
toLowerCase() 获取字符串的小写形式
toUpperCase() 获取字符串的大写形式
split([separator[, limit]) 使用separator分隔符将字符串分隔成数组limit用于限制数量
replace(str1, str2) 使用str2替换字符串中的str1返回替换结果只会替换第一个字符
var str = 'HelloWorld';
str.concat('!');    // 在字符串末尾拼接字符结果HelloWorld!
str.slice(1, 3);    // 截取从位置1开始包括到位置3的范围内的内容结果为el
str.substring(5);   // 截取从位置5开始到最后的内容结果World
str.substring(5, 7); // 截取从位置5开始到位置7范围内的内容结果Wo
str.substr(5);     // 截取从位置5开始到字符串结尾的内容结果World
str.substring(5, 7); // 截取从位置5开始到位置7范围内的内容结果Wo
str.toLowerCase();   // 将字符串转换为小写结果helloworld
str.toUpperCase();   // 将字符串转换为大写结果HELLOWORLD
str.split('l');     // 使用“l”切割字符串结果["He", "", "oWor", "d"]
str.split('l', 3);   // 限制最多切割3次结果["He", "", "oWor"]
str.replace('World', '!'); // 替换字符串,结果:"Hello!"

4.7.4 json格式

var str = '{"name":"张三","age":100}'
var obj = JSON.parse(str)
console.log(obj)
console.log(obj.name)

var obj1 = {"name":"张三","age":100}
var str1 = JSON.stringify(obj1)
console.log(obj1,str1)

4.8 fetch传递post参数

下面是一个使用 fetch 函数发送 POST 请求并传递 usernamepassword 参数的 JavaScript 示例:

const data = {
  username: 'yourUsername',
  password: 'yourPassword'
};

fetch('https://example.com/api/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'  // 指定发送 JSON 格式
  },
  body: JSON.stringify(data)  // 将对象转换为 JSON 字符串
})
.then(response => {
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();  // 如果服务器返回的是 JSON
})
.then(result => {
  console.log('Success:', result);
})
.catch(error => {
  console.error('Error:', error);
});

说明:

  • fetch(url, options) 是现代浏览器中用于发送 HTTP 请求的 API。
  • method: 'POST' 指定使用 POST 请求。
  • headers 中的 'Content-Type': 'application/json' 告诉服务器,发送的是 JSON 数据。
  • body 是要发送的请求体,通常使用 JSON.stringify 将对象序列化成字符串。

如果服务器接受 application/x-www-form-urlencoded 格式:

const params = new URLSearchParams();
params.append('username', 'yourUsername');
params.append('password', 'yourPassword');

fetch('https://example.com/api/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: params.toString()
})
.then(response => response.json())
.then(result => console.log('Success:', result))
.catch(error => console.error('Error:', error));

5. DOM

JavaScript语言由3部分组成分别是ECMAScript、BOM和DOM其中ECMAScript是JavaScript语言的核心它的内容包括前面学习的JavaScript基本语法、数组、函数和对象等。而Web API包括BOM和DOM两部分。 image-20240127090541607 文档对象模型Document Object ModelDOM是W3C组织推荐的处理可扩展标记语言HTML或者XML)的标准编程接口。 W3C定义了一系列的DOM接口利用DOM可完成对HTML文档内所有元素的获取、访问、标签属性和样式的设置等操作。在实际开发中诸如改变盒子的大小、标签栏的切换、购物车功能等带有交互效果的页面都离不开DOM。 DOM中将HTML文档视为树结构被称之为文档树模型把文档映射成树形结构通过节点对象对其处理处理的结果可以加入到当前的页面。 image-20240127090544620 展示了DOM树中各节点之间的关系后接下来我们针对DOM中的专有名词进行解释具体如下。

  • 文档document一个页面就是一个文档。
  • 元素element页面中的所有标签都是元素。
  • 节点node网页中的所有内容在文档树中都是节点元素节点、属性节点、文本节点、注释节点等。在DOM中会把所有的节点都看作是对象这些对象拥有自己的属性和方法。

5.1 获取元素

getElementById()方法是由document对象提供的用于查找元素的方法该方法返回的是拥有指定id的元素如果没有找到指定id的元素则返回null如果存在多个指定id的元素则返回undefined。需要注意的是JavaScript中严格区分大小写所以在书写时一定要遵守书写规范否则程序会报错。

<body>
 <div id="box">你好</div>
 <script>
  var Obox = document.getElementById('box');
  console.log(Obox);       // 结果为:<div id="box">你好</div>
  console.log(typeof Obox);   // 结果为object
  console.dir(Obox);       // 结果为div#box
 </script>
</body>

5.1.1 根据标签获取元素

根据标签名获取元素有两种方式分别是通过document对象获取元素和通过element对象获取元素如下所示。

document.getElementsByTagName('标签名');

element.getElementsByTagName('标签名');

上述代码中的element是元素对象的统称通过元素对象可以查找该元素的子元素或后代元素实现局部查找元素的效果而document对象是从整个文档中查找元素。 由于相同标签名的元素可能有多个上述方法返回的不是单个元素对象而是一个集合。这个集合是一个类数组对象或称为伪数组它可以像数组一样用索引来访问元素但不能使用push()等方法。使用Array.isArray()也可以证明它不是一个数组。

<body>
 <ul>
  <li>苹果</li><li>香蕉</li><li>西瓜</li><li>樱桃</li>
 </ul>
 <ol id="ol">
  <li>绿色</li><li>蓝色</li><li>白色</li><li>红色</li>
 </ol>
 <script>
  var lis = document.getElementsByTagName('li');
  // 结果为HTMLCollection(8) [li, li, li, li, li, li, li, li]
  console.log(lis);

  // 查看集合中的索引为0的元素结果为<li>苹果</li>
  console.log(lis[0]);

  // 遍历集合中的所有元素
  for (var i = 0; i < lis.length; i++) {
   console.log(lis[i]);
  }

  // 通过元素对象获取元素
  var ol = document.getElementById('ol');

  // 结果为HTMLCollection(4) [li, li, li, li]
  console.log(ol.getElementsByTagName('li'));
 </script>
</body>

5.1.2 根据name获取元素

<body>
 <p>请选择你最喜欢的水果多选</p>
 <label><input type="checkbox" name="fruit" value="苹果">苹果</label>
 <label><input type="checkbox" name="fruit" value="香蕉">香蕉</label>
 <label><input type="checkbox" name="fruit" value="西瓜">西瓜</label>
 <script>
  var fruits = document.getElementsByName('fruit');
  fruits[0].checked = true;
 </script>
</body>

在上述代码中getElementsByName()方法返回的是一个对象集合使用索引获取元素。fruits[0].checked为true表示将fruits中的第1个元素的checked属性值设置为true表示将这一项勾选。

5.1.3 HTML5新增的获取方式

HTML5中为document对象新增了getElementsByClassName()、querySelector()和querySelectorAll()方法在使用时需要考虑到浏览器的兼容性问题。接下来我们就来讲解这3种方法的具体使用。

document.getElementsByClassName()方法,用于通过类名来获得某些元素集合。

<body>
 <span class="one">英语</span> <span class="two">数学</span>
 <span class="one">语文</span> <span class="two">物理</span>
 <script>
  var Ospan1 = document.getElementsByClassName('one');
  var Ospan2 = document.getElementsByClassName('two');
  Ospan1[0].style.fontWeight = 'bold';
  Ospan2[1].style.background = 'red';
 </script>
</body>

image-20240127090554110

querySelector()和querySelectorAll() querySelector()方法用于返回指定选择器的第一个元素对象。querySelectorAll()方法返回指定选择器的所有元素对象集合。

<body>
 <div class="box">盒子1</div>
 <div class="box">盒子2</div>
 <div id="nav">
  <ul>
   <li>首页</li>
   <li>产品</li>
  </ul>
 </div>
 <script>
  var firstBox = document.querySelector('.box'); 
  console.log(firstBox); // 获取class为box的第1个div
  var nav = document.querySelector('#nav');
  console.log(nav);    // 获取id为nav的第1个div 
  var li = document.querySelector('li');
  console.log(li);    // 获取匹配到的第一个li
  var allBox = document.querySelectorAll('.box');
  console.log(allBox);  // 获取class为box的所有div
  var lis = document.querySelectorAll('li');
  console.log(lis);    // 获取匹配到的所有li
</script>

从上述代码可以看出在利用querySelector()和querySelectorAll()方法获取操作的元素时直接书写标签名或CSS选择器名称即可根据类名获取元素时在类名前面加上“.”根据id获取元素时在id前面加上“#”。

5.1.4 document对象的属性

document对象提供了一些属性可用于获取文档中的元素。例如获取所有表单标签、图片标签等。

属性 说明
document.body 返回文档的body元素
document.title 返回文档的title元素
document.documentElement 返回文档的html元素
document.forms 返回对文档中所有Form对象引用
document.images 返回对文档中所有Image对象引用

document对象中的body属性用于返回body元素而documentElement属性用于返回HTML文档的根节点html元素。

<body>
 <script>
  var bodyEle = document.body;
  console.dir(bodyEle);
  var htmlEle = document.documentElement;
  console.log(htmlEle);
 </script>
</body>

5.2 操作元素

5.2.1 操作元素内容

在JavaScript中想要操作元素内容首先要获取到该元素前面已经讲解了获取元素的几种方式在本小节中我们将利用DOM提供的属性实现对元素内容的操作。

属性 说明
element.innerHTML 设置或返回元素开始和结束标签之间的HTML包括HTML标签同时保留空格和换行
element.innerText 设置或返回元素的文本内容在返回的时候会去除HTML标签和多余的空格、换行在设置的时候会进行特殊字符转义
element.textContent 设置或者返回指定节点的文本内容,同时保留空格和换行
<body>
 <div id="box">
  The first paragraph...
  <p>
   The second paragraph...
   <a href="http://www.example.com">third</a>
  </p>
 </div>
</body>

按照上述代码设计好HTML文档后在控制台中通过不同的方式获取div中的内容。

image-20250109144037825

5.2.2 操作元素属性

在DOM中HTML属性操作是指使用JavaScript来操作一个元素的HTML属性。一个元素包含很多的属性例如对于一个img图片元素来说我们可以操作它的src、title属性等或者对于input元素来说我们可以操作它的disabled、checked、selected属性等。

img元素的属性操作

<body>
 <button id="flower">鲜花</button>
 <button id="grass">四叶草</button> <br>
 <img src="images/grass.png" alt="" title="四叶草">
 <script>
  // 1. 获取元素
  var flower = document.getElementById('flower');
  var grass = document.getElementById('grass');
  var img = document.querySelector('img');
  // 2. 注册事件处理程序
  flower.onclick = function () {
   img.src = 'images/flower.png';
   img.title = '鲜花';
  };
  grass.onclick = function () {
   img.src = 'images/grass.png';
   img.title = '四叶草';
  };
 </script>
</body>

表单input元素的属性操作

<body>
 <button>按钮</button>
 <input type="text" value="输入内容">
 <script>
  // 1. 获取元素
  var btn = document.querySelector('button');
  var input = document.querySelector('input');
  // 2. 注册事件处理程序
  btn.onclick = function () {
   input.value = '被点击了!'; // 通过value来修改表单里面的值
   this.disabled = true;   // this指向的是事件函数的调用者 btn
  };
 </script>
</body>

5.2.3 操作元素样式

作元素样式有两种方式一种是操作style属性一种是操作className属性

操作style属性 除了前面讲解的元素内容和属性外,对于元素对象的样式,可以直接通过“元素对象.style.样式属性名”的方式操作。样式属性名对应CSS样式名但需要去掉CSS样式名里的半字线“-”并将半字线后面的英文的首字母大写。例如设置字体大小的样式名font-size对应的样式属性名为fontSize。

名称 说明
background 设置或返回元素的背景属性
backgroundColor 设置或返回元素的背景色
display 设置或返回元素的显示类型
fontSize 设置或返回元素的字体大小
height 设置或返回元素的高度
left 设置或返回定位元素的左部位置
listStyleType 设置或返回列表项标记的类型
overflow 设置或返回如何处理呈现在元素框外面的内容
textAlign 设置或返回文本的水平对齐方式
textDecoration 设置或返回文本的修饰
textIndent 设置或返回文本第一行的缩进
transform 向元素应用2D或3D转换
<div id="box"></div>
<script>
 var ele = document.querySelector('#box'); // 获取元素对象
 ele.style.width = '100px';
 ele.style.height = '100px';
ele.style.background = 'red';
 ele.style.transform = 'rotate(7deg)';
</script>

操作className属性 在开发中,如果样式修改较多,可以采取操作类名的方式更改元素样式,语法为“元素对象.className”。访问className属性的值表示获取元素的类名为className属性赋值表示更改元素类名。如果元素有多个类名在className中以空格分隔。 1编写html结构代码具体示例如下。

<style>
 div {
  width: 100px;
  height: 100px;
  background-color: pink;
 } 
</style>
<body>
 <div class="first">文本</div>
</body>

image-20240127090616898 2)单击div元素更改元素的样式示例代码如下。

<script>
 var test = document.querySelector('div');
 test.onclick = function () {
  this.className = 'change';
 };
</script>

3在style中添加change类样式代码如下。

.change {
 background-color: purple;
 color: #fff;
 font-size: 25px;
 margin-top: 100px;
}

4单击div盒子浏览器预览效果 image-20240127090620768

5.3 属性操作

5.3.1 获取属性值

在DOM对象中可以使用“element.属性”的方式来获取内置的属性值但是DOM对象并不能直接使用点语法获取到自定义属性的值那么如何获取自定义属性值呢在DOM中可以使用getAttribute('属性')方法来返回指定元素的属性值。

<body>
 <div id="demo" index="1" class="nav"></div>
 <script>
  var div = document.querySelector('div');
  console.log(div.id);             // 结果为demo
  console.log(div.getAttribute('id'));   // 结果为demo
  console.log(div.getAttribute('index')); // 结果为1
 </script>
</body>

5.3.2 设置属性值

<body>
 <div></div>
 <script>
  var div = document.querySelector('div');
  div.id = 'test';
  div.className = 'navs';
  div.setAttribute('index', 2);
 </script>
</body>

5.3.3 移除属性

<body>
 <div id="test" class="footer" index="2"></div>
 <script>
  var div = document.querySelector('div');  
  div.removeAttribute('id');
  div.removeAttribute('class');
  div.removeAttribute('index');
 </script>
</body>

5.4 节点操作

一般来说节点至少拥有nodeType节点类型、nodeName节点名称和nodeValue节点值这3个基本属性。

  • 元素节点nodeType为1
  • 属性节点nodeType为2
  • 文本节点nodeType为3文本节点包含文字、空格、换行等。

DOM中将HTML文档视为树结构一个HTML文件可以看作是所有元素组成的一个节点树各元素节点之间有级别的划分。具体示例如下。

<!DOCTYPE html>

<html>

 <head>

<meta charset="UTF-8">

  <title>测试</title>

 </head>

 <body>

    <a href="#">链接</a>

    <p>段落...</p>

 </body>

</html>
  • 根节点:<html>标签是整个文档的根节点,有且仅有一个。
  • 父节点:指的是某一个节点的上级节点,例如,<html>元素则是<head><body>的父节点。
  • 子节点:指的是某一个节点的下级节点,例如,<head><body>节点是<html>节点的子节点。
  • 兄弟节点:两个节点同属于一个父节点,例如,<head><body>互为兄弟节点。

获取父级节点

<body>
 <div class="demo">
  <div class="box">
   <span class="child">span元素</span>
  </div>
 </div>
 <script>
  var child = document.querySelector('.child');
  console.log(child.parentNode);
 </script>
</body>

获取子级节点 在JavaScript中可以使用childNodes属性或者children属性两种方式来获得当前元素的所有子节点的集合接下来我们就分别介绍这两种方式的用法。

<body>
 <ul>
  <li>我是li</li>
  <li>我是li</li>
  <li>我是li</li>
  <li>我是li</li>
 </ul>
 <script>
  // DOM 提供的方法API获取
  var ul = document.querySelector('ul');
  var lis = ul.querySelectorAll('li');
   console.log(lis);
  console.log(ul.childNodes);
  console.log(ul.childNodes[0].nodeType);
  console.log(ul.childNodes[1].nodeType);
 </script>
</body>

childNodes属性返回的是NodeList对象的集合返回值里面包含了元素节点、文本节点等其他类型的节点。如果想要获取childNodes里面的元素节点需要做专门的处理。在第15行代码下面编写如下代码获取元素节点。

for (var i = 0; i < ul.childNodes.length; i++) {
 if (ul.childNodes[i].nodeType === 1) {
  console.log(ul.childNodes[i]);
 }
}

children是一个可读的属性返回所有子元素节点。children只返回子元素节点其余节点不返回。目前各大浏览器都支持该属性在实际开发中推荐使用children

<body>
 <ol>
  <li>我是li</li>
  <li>我是li</li>
  <li>我是li</li>
  <li>我是li</li>
 </ol>
 <script>
  var ul = document.querySelector('ol');
  var lis = ul.querySelectorAll('li');
  console.log(ul.children);
 </script>
</body>

获取兄弟节点 在JavaScript中可以使用nextSibling属性来获得下一个兄弟节点使用previousSibling属性来获得上一个兄弟节点。它们的返回值包含元素节点或者文本节点等。如果找不到就返回null。 如果想要获得兄弟元素节点则可以使用nextElementSibling返回当前元素的下一个兄弟元素节点使用previousElementSibling属性返回当前元素的上一个兄弟元素节点。如果找不到则返回null。要注意的是这两个属性有兼容性问题IE 9以上才支持。 实际开发中nextSibling和previousSibling属性返回值都包含其他节点操作不方便而nextElementSibling和previousElementSibling又有兼容性问题。为了解决兼容性问题在实际开发中通常使用封装函数来处理兼容性。

function getNextElementSibling(element) {
 var el = element;
 while (el = el.nextSibling) {
  if (el.nodeType === 1) {
   return el;
  }
 }
 return null;
}

5.4.1 创建节点

动态创建元素节点有3种常见方式具体如下。

  1. document.write() document.write()创建元素,如果页面文档流加载完毕,再调用会导致页面重绘。

  2. element.innerHTML element.innerHTML是将内容写入某个DOM节点不会导致页面全部重绘。

  3. document.createElement() document.createElement()创建多个元素效率稍微低一点,但是结构更加清晰。

5.4.2 添加和删除节点

  1. appendChild() appendChild()方法将一个节点添加到指定父节点的子节点列表末尾类似于CSS中的after伪元素。

  2. insertBefore() insertBefore(child, 指定元素)方法将一个节点添加到父节点的指定子节点前面类似于CSS中的before伪元素。

  3. removeChild(child) removeChild(child)用于删除节点该方法从DOM中删除一个子节点返回删除的节点。

5.4.3 复制节点

在DOM中提供了node.cloneNode()方法,返回调用该方法的节点的一个副本,也称为克隆节点或者拷贝节点。语法为“需要被复制的节点.cloneChild(true/false) ”。如果参数为空或false则是浅拷贝即只复制节点本身不复制里面的子节点如果括号参数为true则是深拷贝即会复制节点本身及里面所有的子节点。

<body>
 <ul id="myList"><li>苹果</li><li>橙子</li><li>橘子</li></ul>
 <ul id="op"></ul>
 <button onclick="myFunction()">点我</button>
 <script>
  function myFunction() {
   var item = document.getElementById('myList').children[0];
   var cloneItem = item.cloneNode(true);
   document.getElementById('op').appendChild(cloneItem);
  }
 </script>
</body>

5.5 事件

5.5.1 事件基础

事件三要素

  • 事件源:触发事件的元素。
  • 事件类型:如 click 单击事件。
  • 事件处理程序:事件触发后要执行的代码(函数形式),也称事件处理函数。
<body>
 <button id="btn">单击</button>
 <script>
  var btn = document.getElementById('btn'); // 第1步获取事件源
  // 第2步注册事件btn.onclick
  btn.onclick = function () { // 第3步添加事件处理程序采取函数赋值形式
    alert('弹出');
  };
 </script>
</body>

5.5.2 注册事件

在JavaScript中注册事件绑定事件有两种方式即传统方式注册事件和事件监听方式注册事件。 传统方式 在JavaScript代码中经常使用on开头的事件如onclick为操作的DOM元素对象添加事件与事件处理程序。 oBtn.onclick = function () { } 在上述语法中元素对象是指使用getElementById()等方法获取到的元素节点。这种方式注册事件的特点在于注册事件的唯一性,即同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数。

事件监听方式

<div id="t">test</div>

<script>

var obj = document.getElementById('t');

// 添加第1个事件处理程序

obj.addEventListener('click', function(){

  console.log('one');

});

// 添加第2个事件处理程序

obj.addEventListener('click', function(){

  console.log('two');

});

</script>

5.5.3 删除事件

DOM对象.onclick = null;               // 传统方式删除事件

DOM对象.removeEventListener(type, callback); // 标准浏览器

5.5.4 事件对象的使用

<button id="btn">获取event对象</button>
<script>
 var btn = document.getElementById('btn');
 btn.onclick = function(e) {
  var event = e || window.event;  // 获取事件对象的兼容处理在老版本的IE中只能使用window.event来获取事件
  console.log(event);
 };
</script>

上述代码中第3行代码根据id属性值获取button按钮的元素对象。第47行代码通过动态绑定式为按钮添加单击事件。其中事件处理函数中传递的参数e参数名称只要符合变量定义的规则即可表示的就是事件对象event。第5行通过“或”运算符实现不同浏览器间获取事件对象兼容的处理。若是标准浏览器则可以直接通过e获取事件对象否则若是早期版本的IE浏览器IE 6IE 8则需要通过window.event才能获取事件对象。

在事件发生后事件对象event中不仅包含着与特定事件相关的信息还会包含一些所有事件都有的属性和方法。

属性 说明 浏览器
e.target 返回触发事件的对象 标准浏览器
e.srcElement 返回触发事件的对象 非标准IE 6~IE 8使用
e.type 返回事件的类型 所有浏览器
e.stopPropagation() 阻止事件冒泡 标准浏览器
e.cancelBubble 阻止事件冒泡 非标准IE 6~IE 8使用
e.preventDefault() 阻止默认事件(默认行为) 标准浏览器
e.returnValue 阻止默认事件(默认行为) 非标准IE 6~IE 8使用

type是标准浏览器和早期版本IE浏览器的事件对象的公有属性通过该属性可以获取发生事件的类型如click、mouseover等不带on

对比e.target和this的区别 在事件处理函数中e.target返回的是触发事件的对象而this返回的是绑定事件的对象。简而言之e.target 是哪个元素触发事件了,就返回哪个元素;而 this 是哪个元素绑定了这个事件,就返回哪个元素。

div.onclick = function(e) {
 e = e || window.event;
 var target = e.target || e.srcElement;
 console.log(target);
};

值得一提的是this和e.currentTarget的值相同但考虑到e.currentTarge有兼容性问题IE 9以上支持所以在实际开发中推荐使用this。

阻止默认行为 在HTML中有些元素标签拥有一些特殊的行为。例如单击<a>标签后会自动跳转到href属性指定的URL链接单击表单的submit按钮后会自动将表单数据提交到指定的服务器端页面处理。因此我们把标签具有的这种行为称为默认行为。 但是在实际开发中为了使程序更加严谨想要确定含有默认行为的标签符合要求后才能执行默认行为时可利用事件对象的preventDefault()方法和returnValue属性禁止所有浏览器执行元素的默认行为。需要注意的是只有事件对象的cancelable属性设置为true才可以使用preventDefault()方法取消其默认行为。

 <body>
  <a href="http://www.baidu.com">百度</a>
  <script>
   var a = document.querySelector('a');
   a.addEventListener('click', function (e) {
    e.preventDefault();   // DOM标准写法早期版本浏览器不支持
   });
   // 推荐使用传统的注册方式
   a.onclick = function (e) {
    e.preventDefault();  // 标准浏览器使用e.preventDefault()方法
    e.returnValue;     // 早期版本浏览器IE 6~IE 8使用returnValue属性
   };
  </script>
 </body>

阻止事件冒泡 如果在开发中想要阻止事件冒泡则可以利用事件对象调用stopPropagation()方法和设置cancelBubble属性实现禁止所有浏览器的事件冒泡行为。

在复杂的DOM结构中一个元素可能嵌套在多个父元素内部。默认情况下事件会从触发元素开始沿着DOM树向上冒泡依次触发各级父元素的事件处理程序。通过阻止事件冒泡可以将事件的影响范围限制在触发元素本身避免不必要的父元素事件被触发。

image-20240127090711149

<div id="parent">
        <button id="child">Click me</button>
    </div>
    <script>
        document.getElementById('parent').addEventListener('click', function (e) {
            console.log('Parent element clicked');
        });
        document.getElementById('child').addEventListener('click', function (e) {
            console.log('Button clicked');
            if (window.event) {   // 早期版本的浏览器
                window.event.cancelBubble = true;
            } else {         // 标准浏览器
                e.stopPropagation();
            }
        });
    </script>

上述第1行代码用于判断当前是否为早期版本的IE浏览器如果是则利用事件对象调用cancelBubble属性阻止事件冒泡否则利用事件对象e调用stopPropagation()方法完成事件冒泡的阻止设置。

事件委托 在现实生活中有时快递员为了节省时间会把快递放到某快递代收机构然后让客户自行去领取这种把事情委托给别人的方式就是代为处理。事件委托或称为事件代理也是如此。事件委托的原理是不给每个子节点单独设置事件监听器而是把事件监听器设置在其父节点上让其利用事件冒泡的原理影响到每个子节点。简而言之就是不给子元素注册事件给父元素注册事件让处理代码在父元素的事件中执行。这样做的优点在于只操作了一次DOM ,提高了程序的性能。

<body>
 <ul>
  <li>我是第 1 个li</li>
  <li>我是第 2 个li</li>
  <li>我是第 3 个li</li>
  <li>我是第 4 个li</li>
  <li>我是第 5 个li</li>
 </ul>
 <script>
  var ul = document.querySelector('ul');
  ul.addEventListener('click', function (e) {
   e.target.style.backgroundColor = 'pink';
  });
 </script>
</body>

上述代码中第10~13行代码采用事件委托原理首先获取到ul父元素并且在第11行给父元素绑定单击事件实现单击子元素li时给当前项改变背景色。

5.5.5 鼠标事件

事件触发时机 事件名称
单击鼠标左键时触发 onclick
获得鼠标指针焦点触发 onfocus
失去鼠标指针焦点触发 onblur
鼠标指针经过时触发 onmouseover
鼠标指针离开时触发 onmouseout
当按下任意鼠标按键时触发 onmousedown
当释放任意鼠标按键时触发 onmouseup
在元素内当鼠标指针移动时持续触发 onmousemove

禁止鼠标右击菜单

document.addEventListener('contextmenu', function (e) {

 e.preventDefault();

});

禁止鼠标选中

document.addEventListener('selectstart', function (e) {

 e.preventDefault();

});

在项目开发中还经常涉及一些常用的鼠标属性,用来获取当前鼠标的位置信息。

位置属性(只读) 描述
clientX 鼠标指针位于浏览器页面当前窗口可视区的水平坐标X轴坐标
clientY 鼠标指针位于浏览器页面当前窗口可视区的垂直坐标Y轴坐标
pageX 鼠标指针位于文档的水平坐标X轴坐标IE 6~IE 8不兼容
pageY 鼠标指针位于文档的垂直坐标Y轴坐标IE 6~IE 8不兼容
screenX 鼠标指针位于屏幕的水平坐标X轴坐标
screenY 鼠标指针位于屏幕的垂直坐标Y轴坐标

从表1可知IE 6IE 8浏览器中不兼容pageX和pageY属性。因此项目开发时需要对IE 6IE 8浏览器进行兼容处理具体示例如下。

var pageX = event.pageX || event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft);

var pageY = event.pageY || event.clientY + (document.body.scrollTop || document.documentElement.scrollTop);

5.5.6 键盘事件

事件名称 事件触发时机
keypress 某个键盘按键被按下时触发。不识别功能键如Ctrl、Shift、箭头等
keydown 某个键盘按键被按下时触发
keyup 某个键盘按键被松开时触发

为了让大家更好地理解键盘事件的使用下面我们通过案例的形式进行展示。检测用户是否按下了s键如果按下s 键,就把光标定位到搜索框里面,示例代码如下。

<body>
 <input type="text">
 <script>
  var search = document.querySelector('input');
  document.addEventListener('keyup', function (e) {
   if (e.keyCode === 83) {
    search.focus();
   }
  });
 </script>
</body>

6. BOM

浏览器对象模型Brower Object ModelBOM提供了独立于内容而与浏览器窗口进行交互的对象其核心对象是window。 BOM由一系列相关的对象构成并且每个对象都提供了很多方法和属性。但是BOM缺乏标准JavaScript语法的标准化组织是ECMADOM的标准化组织是W3C而BOM最初是Netscape网景浏览器标准的一部分。 DOM是文档对象模型把文档当作一个对象来看待它的顶级对象是document我们主要学习的是操作页面元素。DOM是W3C标准规范。 BOM是浏览器对象模型是把浏览器当做一个对象来看待它的顶级对象是window我们主要学习的是浏览器窗口交互的一些对象。BOM是浏览器厂商在各自浏览器上定义的兼容性较差。 image-20240127090720821 BOM比DOM更大它包含DOMdocument)。BOM的核心对象是window其他的对象称为window的子对象它们是以属性的方式添加到window对象的。 window对象是浏览器顶级对象具有双重角色既是JavaScript访问浏览器窗口的一个接口又是一个全局对象定义在全局作用域中的变量、函数都会变成window对象的属性和方法。

<script>
 // 全局作用域中的变量是window对象的属性
 var num = 10;
 console.log(num);     // 结果为10
 console.log(window.num); // 结果为10
 // 全局作用域中的函数是window对象的方法
 function fn() {
  console.log(11);
 }
 fn();           // 结果为11
 window.fn();       // 结果为11
</script>

6.1 常见事件

window.onload window.onload是窗口页面加载事件当文档内容包括图像、脚本文件、CSS文件等完全加载完成会触发该事件调用该事件对应的事件处理函数。 JavaScript代码是从上往下依次执行的如果要在页面加载完成后执行某些代码又想要把这些代码写到页面任意的地方可以把代码写到window.onload事件处理函数中。因为onload事件是等页面内容全部加载完毕再去执行处理函数的。 onload页面加载事件有两种注册方式

// 方式1

window.onload = function () {};

// 方式2

window.addEventListener('load', function () {});

需要注意的是window.onload注册事件的方式只能写一次如果有多个会以最后一个window.onload为准。如果使用addEventListener则没有限制。

document.DOMContentLoaded document.DOMContentLoaded加载事件会在DOM加载完成时触发这里所说的加载不包括CSS样式表、图片和flash动画等额外内容的加载因此该事件的优点在于执行的时机更快适用于页面中图片很多的情况。当页面图片很多时从用户访问到onload事件触发可能需要较长的时间交互效果就不能实现这样会影响到用户的体验效果在这时使用document.DOMContentLoaded事件更为合适只要DOM元素加载完即可执行。需要注意的是该事件有兼容性问题IE 9以上才支持。

6.2 调整窗口大小

当调整window窗口大小的时候就会触发window.onresize事件调用事件处理函数。该事件有两种注册方式

// 方式1

window.onresize = function () {};

// 方式2

window.addEventListener('resize', function () {});

接下来我们通过案例进行演示。利用页面加载事件和调整窗口大小事件完成响应式布局

<body>
 <script>
  window.addEventListener('load', function () {
   var div = document.querySelector('div');
   window.addEventListener('resize', function () {
    if (window.innerWidth <= 800) {
     div.style.display = 'none';
    } else {
     div.style.display = 'block';
    }
   });
  });
 </script>
 <div style="width:200px;height:100px;background-color:pink;"></div>
</body>

6.3 定时器

在JavaScript中提供了两组方法用于定时器的实现

方法 说明
setTimeout() 在指定的毫秒数后调用函数或执行一段代码
setInterval() 按照指定的周期(以毫秒计)来调用函数或执行一段代码
clearTimeout() 取消由setTimeout()方法设置的定时器
clearInterval() 取消由setInterval()设置的定时器

setTimeout()和setInterval()方法都可以在一个固定时间段内执行代码,不同的是前者只执行一次代码,而后者会在指定的时间后自动重复执行代码。 在实际开发中我们可以通过setTimeout()方法实现函数的一次调用并且可以通过clearTimeout()来清除setTimeout()定时器。

setTimeout(调用的函数, [延迟的毫秒数])

setInterval(调用的函数, [延迟的毫秒数])

下面我们以setTimeout()为例进行代码演示,具体代码如下。

// 参数形式1用字符串表示一段代码

setTimeout('alert("JavaScript");', 3000); 

// 参数形式2传入一个匿名函数

setTimeout(function () {

alert('JavaScript');

}, 3000);

// 参数形式3传入函数名

setTimeout(fn, 3000);

function fn() {

 console.log('JavaScript');

}

在上述代码中,当参数为一个函数名时,这个函数名不需要加()小括号否则就变成了立即执行这个函数将函数执行后的返回值传入。如果延迟的毫秒数省略时默认为0。

在实际开发中考虑到一个网页中可能会有很多个定时器所以建议用一个变量保存定时器的id唯一标识。若想要在定时器启动后取消该定时器操作可以将setTimeout()的返回值定时器id传递给clearTimeout()方法。示例代码如下。

// 在设置定时器时,保存定时器的唯一标识

var timer = setTimeout(fn, 3000);

// 如果要取消定时器将唯一标识传递给clearTimeout()方法

clearTimeout(timer);

本案例将会使用setTimeout()实现3秒后自动关闭广告的效果。具体代码如下。

 <body>
  <script>
   console.log('广告显示');
   var timer = setTimeout(fn, 3000);
   function fn() {
    console.log('广告关闭了');
   }
  </script>
 </body>

6.4 location对象

在URL中包含了网络协议、服务器的主机名、端口号、资源名称字符串、参数以及锚点

// 示例1

protocol://host[:port]/path/[?query]#fragment

// 示例2

http://www.example.com:80/web/index.html?a=3&b=4#res
各部分 说明
protocol 网络协议常用的如httpftpmailto等
host 服务器的主机名,如www.example.com
port 端口号可选省略时使用协议的默认端口如http默认端口为80
path 路径,如“/web/index.html”
query 参数,键值对的形式,通过“&”符号分隔如“a=3&b=4”
fragment 锚点,如“#res”表示页面内部的锚点

location常用属性

属性 说明
location.search 返回或设置当前URL的查询部分“?”之后的部分)
location.hash 返回一个URL的锚部分从“#”开始的部分)
location.host 返回一个URL的主机名和端口
location.hostname 返回URL的主机名
location.href 返回完整的URL
location.pathname 返回URL的路径名
location.port 返回一个URL服务器使用的端口号
location.protocol 返回一个URL协议

location常用方法

方法 说明
assign() 载入一个新的文档
reload() 重新加载当前文档
replace() 用新的文档替换当前文档,覆盖浏览器当前记录
  • assign()方法是比较常用的方式使用location.assign()就可以立即打开一个新的浏览器位置并生成一条新的历史记录。接收的参数为URL地址。
  • reload()方法的唯一参数是一个布尔类型值将其设置为true时它会绕过缓存从服务器上重新下载该文档类似于浏览器中的“刷新页面”按钮。
  • replace()方法作用就是会使浏览器位置改变并且禁止在浏览器历史记录中生成新的记录它只接受一个要导航到的URL参数而且在调用replace()方法后,用户不能返回到前一个页面。

6.5 navigator对象

navigator对象包含有关浏览器的信息但是每个浏览器中的navigator对象中都有一套自己的属性。下面列举了主流浏览器中存在的属性和方法

分类 名称 说明
属性 appCodeName 返回浏览器的内部名称
appName 返回浏览器的完整名称
appVersion 返回浏览器的平台和版本信息
cookieEnabled 返回指明浏览器中是否启用Cookie的布尔值
platform 返回运行浏览器的操作系统平台
userAgent 返回由客户端发送到服务器的User-Agent头部的值
方法 javaEnabled() 指定是否在浏览器中启用Java

最常用的属性是userAgent

<script>
 var msg = window.navigator.userAgent;
 console.log(msg);
</script>

6.6 history对象

分类 名称 说明
属性 length 返回历史列表中的网址数
方法 back() 加载history列表中的前一个URL
forward() 加载history列表中的下一个URL
go() 加载history列表中的某个具体页面