前言

经过前两章的学习,我们知道:

  • HTML是一种标记语言,用来结构化我们的网页内容并赋予内容含义,例如定义段落、标题和数据表,或在页面中嵌入图片和视频。
  • CSS 是一种样式规则语言,可将样式应用于 HTML 内容,例如设置背景颜色和字体,在多个列中布局内容。

而这样的网页仍然是个静态的网页,不能对结果做出反应。而现实中我们希望网站能与用户进行一些交互与反馈,例如输入账号密码后登录,动态检测账号密码正不正确,给出反馈。

于是我们就有一个解决方案,使用JavaScript。JavaScript 是一种脚本语言,可以用来创建动态更新的内容,控制多媒体,制作图像动画,还有很多。

接下来这一章将介绍基础的JavaScript相关知识。

特别参考:MDNW3School


JavaScript基本语法

JavaScript简介

JavaScript(缩写:JS)是一门完备的动态编程语言,可为网站添加交互功能(例如:游戏、动态样式、动画以及在按下按钮或收到表单数据时做出的响应等)。

下面从JavaScript的简单概念介绍开始,认识JavaScript的基本语法。

动态与静态

没有动态更新内容的网页叫做“静态”页面,它是由HTML以及CSS组成的。在访问中,里面的元素不会被改变。

动态”一词既适用于客户端 JavaScript,又适用于描述服务器端语言。是指通过按需生成新内容来更新 web 页面 / 应用,使得不同环境下显示不同内容。服务器端代码会在服务器上动态生成新内容,例如从数据库中提取信息。而客户端 JavaScript 则在用户端浏览器中动态生成新内容,比如说创建一个新的 HTML 表格,用从服务器请求到的数据填充,然后在网页中向用户展示这个表格。两种情况的意义略有不同,但又有所关联,且两者(服务器端和客户端)经常协同作战。

客户端与服务端

JavaScript是一个客户端语言,客户端(client-side)代码是在用户的电脑上运行的代码,在浏览一个网页时,它的客户端代码就会被下载,然后由浏览器来运行并展示。

而服务器端代码在服务器上运行,接着运行结果才由浏览器下载并展示出来。

JavaScript一般作为客户端代码,但是也可以作为服务端代码。

解释代码与编译代码

  • 解释(interpret)代码:在解释型语言中,代码自上而下运行,且实时返回运行结果。代码在由浏览器执行前,不需要将其转化为其他形式。代码将直接以文本格式(text form)被接收和处理。
  • 编译(compile)代码:编译型语言需要先将代码转化(编译)成另一种形式才能运行。比如 C/C++ 先被编译成汇编语言,然后才能由计算机运行。程序将以二进制的格式运行,这些二进制内容是由程序源代码产生的。

JavaScript 是轻量级解释型语言。浏览器接受到 JavaScript 代码,并以代码自身的文本格式运行它。

应用程序接口(API)

应用程序接口(Application Programming InterfacesAPI))是已经建立好的一套代码组件,可以让开发者实现原本很难甚至无法实现的程序。

简单来说,我们可以通过API获取到一些内容,这些也许是一些函数的结果或者获取一些特殊信息。

API一般分为浏览器API与第三方API:

  • 浏览器 API 内建于 web 浏览器中,它们可以将数据从周边计算机环境中筛选出来,还可以做实用的复杂工作。例如通过浏览器获取用户的位置、IP等等信息。
  • 第三方 API 并没有默认嵌入浏览器中,一般要从网上取得它们的代码和信息。例如Hoyue博客中的随机图采用了自建的API,它可以获取一个随机图片的地址,这就是一个第三方API。

JavaScript使用

介绍完了JavaScript,我们还需要知道JavaScript如何在HTML中使用。当然JavaScript不止在HTML中使用,但这里只讨论在web中HTML脚本使用。

内联JavaScript

可以像添加 CSS 那样将 JavaScript 添加到 HTML 页面中。同样的,我们可以给一些标签添加元素引用JavaScript脚本,例如下面的例子:

<button onclick="createParagraph()">点我呀</button>

在html中存在用<script>元素包含JavaScript代码,其中包含了上述函数的定义:

function createParagraph() {
  const para = document.createElement('p');
  para.textContent = '你点击了这个按钮!';
  document.body.appendChild(para);
}

然而请不要这样做。 这将使 JavaScript 污染到 HTML,而且效率低下。对于每个需要应用 JavaScript 的按钮,你都得手动添加 onclick="createParagraph()" 属性。

内部JavaScript

内部JavaScript用得比较多,我们一般在 <head> 元素中插入以下代码:

<script>
  // 在此编写 JavaScript 代码
</script>

接下来我们就可以在<script>元素中添加JavaScript即可给HTML添加脚本内容,例如:

document.addEventListener("DOMContentLoaded", function() {
  function createParagraph() {
    let para = document.createElement('p');
    para.textContent = '你点击了这个按钮!';
    document.body.appendChild(para);
  }
  const buttons = document.querySelectorAll('button');
  for(let i = 0; i < buttons.length ; i++) {
    buttons[i].addEventListener('click', createParagraph);
  }
});

当然这里只是一个例子,关于语法之后会提及。

外部JavaScript

我们使用最多的就是外部 JavaScript。与外部css一样,我们可以把JavaScript代码放在一个单独的文件,而在HTML中引用它。

我们一般有这样的步骤:

  1. 首先,在刚才的 HTML 文件所在的目录下创建一个名为 script.js 的新文件。请确保扩展名为 .js,只有这样才能被识别为 JavaScript 代码。
  2. <head>元素或者<body>元素中添加引用元素<script>,这里是:
    <script src="script.js" async></script>
  3. 在 script.js 文件中,添加之前的例子代码。
  4. 保存并刷新浏览器,即可成功引用JavaScript代码。

当然,和外部CSS一样,我们的src属性处填写的是地址,它可以包括绝对地址与相对地址。

JavaScript语法

知道了如何引入JavaScript后,我们就正式进入JavaScript语法的介绍。

注释与分号

与Java和C++中一样,JavaScript中的注释也是用///* */表示的。

变量与数据类型

一个变量,就是一个用于存放数值的容器。这个数值可能是一个用于累加计算的数字,或者是一个句子中的字符串。变量的独特之处在于它存放的数值是可以改变的。

在JavaScript中,声明一个变量的语法是在 var 或 let 关键字之后加上这个变量的名字

例如:

var myName;
let myAge;

和其他语言一样,我们的变量有一些内置的数据类型,在JavaScript中有六种基本数据类型:

  • 数字类型(Number)
  • 字符串(String)
  • 布尔类型(Boolean (values true and false))
  • 对象(Object)
  • 空(Null (only value of this type is null))
  • 未定义(Undefined)

与其他语言不同的是,它出现了空类型与未定义的类型。空类型是指空的值,例如空对象、空字符串。

而未定义表示的是定义变量时定义了变量,但没有赋值。此时就是一个未定义的状态。

我们可以使用typeof运算符知晓变量的类型,它的语法格式是,其中operand表示要返回类型的对象或基本类型的表达式。

typeof operand

下面是一个例子:

var myName;
var myAge = 18;
var addRess = "address";

window.alert("myName: " + (typeof myName) + "\nmyAge: " + (typeof myAge) + "\naddRess: " + (typeof addRess));

您可能会想:“为什么我们需要两个关键字来定义变量?”,“为什么有 var 和 let 呢?"。

使用 var 关键字重新声明变量会带来问题,而使用 let 关键字重新声明变量可以解决这个问题。即var和我们平时定义变量规则相同,而let在更小的代码块中可以重新定义。

//使用var
var x = 10;
// 此处 x 为 10
{ 
  var x = 6;
  // 此处 x 为 6
}
// 此处 x 为 6

//使用let
var x = 10;
// 此处 x 为 10
{ 
  let x = 6;
  // 此处 x 为 6
}
// 此处 x 为 10

对于变量名的命名规则,我们有以下规则:

  • 你不应当使用规则之外的其他字符,因为它们可能引发错误,或对国际用户来说难以理解。
  • 变量名不要以下划线开头—— 以下划线开头的被某些 JavaScript 设计为特殊的含义,因此可能让人迷惑。
  • 变量名不要以数字开头。这种行为是不被允许的,并且将引发一个错误。
  • 一个可靠的命名约定叫做 "小写驼峰命名法",用来将多个单词组在一起,小写整个命名的第一个字母然后大写剩下单词的首字符。例如myName
  • 让变量名直观,它们描述了所包含的数据。不要只使用单一的字母/数字,或者长句。
  • 变量名大小写敏感——因此myagemyAge是 2 个不同的变量。
  • 最后也是最重要的一点—— 你应当避免使用 JavaScript 的保留字给变量命名。保留字,即是组成 JavaScript 的实际语法的单词!因此诸如 varfunctionlet 和 for 等,都不能被作为变量名使用。浏览器将把它们识别为不同的代码项,因此你将得到错误。

数据类型转换

JavaScript中有一些数据类型自动转换的功能。例如转换为布尔类型:

同理还有其他类型,例如:

JavaScript输出

和其他语言不同的是,JavaScript 不提供任何内建的打印或显示函数。

于是JavaScript的输出一般用不同方式“显示”数据:

  • 使用 window.alert() 写入警告框
  • 使用 document.write() 写入 HTML 输出
  • 使用 innerHTML 写入 HTML 元素
  • 使用 console.log() 写入浏览器控制台

其中数据的内容可以是表达式也可以是变量。

我们最常用的就是window.alert()它会弹出一个对话框输出文字,例如:

window.alert(5 + 6);

而如果我们的输出要到HTML中的话,一般在HTML中使用 document.write()。但是这样会导致清空文档流内容,重新渲染。例如这是一个内联脚本:

<body>
<h2>我的第一张网页</h2>
<p>我的第一个段落。</p>
<button type="button" onclick="document.write(5 + 6)">试一试</button>
</body>

当我们点击按钮后,就会变成:

此时之前的一个二级标题和段落被清空了,取而代之的是一个表达式的值。

因为document.write()会导致文档清空重新渲染,不推荐使用这种方法。而使用innerHTML 写入 HTML 元素则不会清空其他元素。

因为所以的元素都有一个属性innerHTML,更改它的值将修改该元素的内容。更改 HTML 元素的 innerHTML 属性是在 HTML 中显示数据的常用方法。为了精确定位到某个元素,它的格式一般为id.innerHTML,其中id通过document.getElementById(id)方法获取。例如:

<h1>我是一个标题。</h1>
<p id="text">123456</p>
<script>
document.getElementById("text").innerHTML = 5 + 6;
</script>

此时id为textp元素内容被innerHTML替换了。而其他元素则不变。

最后我们还可以在浏览器中,使用 console.log() 方法在控制台中显示数据。通过 F12 来激活浏览器控制台,并在菜单中选择“控制台”。就可以看到我们的输出了,例如:

console.log("from script file");


JavaScript语句和运算符

JavaScript语句

JavaScript 语句是由 web 浏览器“执行”的“指令”。JavaScript有三种语句,分别为表达式语句、块语句和关键词语句。

JavaScript 语句由这些部分构成:值、运算符、表达式和关键词。在每条JavaScript语句后,使用分号“;”隔开。

表达式语句

完全由表达式组成,最经典的就是算法表达式语句与赋值,例如:

var x, y, z;
x = 22;	
y = 11;
z = x + y;

同样的,JavaScript和C/C++中一样,存在自增和自减运算符,即反复把一个变量加 1 或减 1,使用增量(++)和递减( -- )运算符来完成。下面语句是相同的:

x++;
x=x+1;

块语句

JavaScript 语句可以用花括号({...})组合在代码块中。代码块的作用是定义一同执行的语句

关键词语句

JavaScript 语句常常通过某个关键词来标识需要执行的 JavaScript 动作。我们一般有下面这些关键词:

关键词 描述
break 终止 switch 或循环。
continue 跳出循环并在顶端开始。
debugger 停止执行 JavaScript,并调用调试函数(如果可用)。
do ... while 执行语句块,并在条件为真时重复代码块。
for 标记需被执行的语句块,只要条件为真。
function 声明函数。
if ... else 标记需被执行的语句块,根据某个条件。
return 退出函数。
switch 标记需被执行的语句块,根据不同的情况。
try ... catch 对语句块实现错误处理。try中包含可疑的代码,一旦它报错则立刻到catch中执行。
throw 抛出一个异常对象,可以包含任何有关发生错误的信息。throw语句后面的语句将不被执行。
var 声明变量。

运算符

JavaScript的运算符大多与Java、C/C++相似,相同的就不再赘述。

优先级

一些运算符将在计算算式(在编程中称为表达式)的结果时先于其他运算符被执行。

优先级 运算符 描述
20 () 圆括号 (函数调用、分组)
19 []、. 方括号和点符号 (成员访问)
18 new 对象创建
17 ++、-- 自增/自减
16 !、~、+、-、typeof、void、delete 逻辑非、按位取反、一元加、一元减、类型检测、无操作、对象属性删除
15 *、/、% 乘法、除法、取模
14 +、- 加法、减法
13 <<、>>、>>> 左移、右移 (带符号/不带符号)
12 <、<=、>、>=、in、instanceof 小于、小于等于、大于、大于等于、属性存在性测试、实例关系测试
11 ==、!=、===、!== 相等性测试
10 & 按位与
9 ^ 按位异或
8 | 按位或
7 && 逻辑与
6 || 逻辑或
5 ?: 条件运算符
4 =、+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、^=、|= 赋值运算符
3 yield 生成器函数中的产出操作
2 , 逗号运算符 (多个表达式串联)
1 await 异步函数中的暂停操作

其中,在优先级相同的情况下,大部分JavaScript会按从左到右的顺序依次计算运算符。但是下面类型的语句则是从右到左的顺序执行。

  1. 赋值运算符:赋值运算符的右侧表达式先执行,然后结果被赋值给左侧变量。
    let a, b;
    a = (b = 5); // 先将 5 赋值给 b, 然后 b 的值再赋值给 a
  2. 条件(ternary)运算符 (?:):从右向左计算,在条件为真时执行第一个表达式,否则执行第二个表达式。
    let x = true ? 1 : 2; // x 为 1
    let y = false ? 1 : 2; // y 为 2
  3. 逗号运算符(,):从左到右执行,但返回的是最右边表达式的值。
    let a = (1, 2, 3, 4); // a 的值为 4
  4. 属性访问符(.) 和 数组下标访问符([]): 都是从左到右计算,但是当多次使用时,则是从左到右依次执行,并且数组下标访问符在这种情况下比属性访问符优先级更高。
    const obj = {a: {b: 1}};
    console.log(obj.a.b); // 从左到右,先访问 a 属性再访问 b 属性
    const arr = [1, [2]];
    console.log(arr[1][0]); // 从左到右,先访问下标为 1 的元素(是一个数组),再访问该数组的第一个元素

自动类型转换

在执行运算符时,与其他语言不同的是,JavaScript会执行自动类型转换。例如转换成Number与String类:

x = 7 + 8;        //Number
y = "7" + 8;      //Number
z = "Hello" + 7;  //String

其中y将字符串“7”转换成了number型,进行相加,返回为number型。z则是把数字7转换成了字符型,返回为"Hello7"。一般来说,数字+字符串结果为字符串

而对于比较运算符,比较不同类型的数据也许会出现不可预料的结果。

案例
2 < 12 true
2 < "12" true
2 < "Bill" false
2 > "Bill" false
2 == "Bill" false
"2" < "12" false
"2" > "12" true
"2" == "12" false

当比较两个字符串时,我们一般是字典序比较,同位比较。例如“2”和“12”,在第一位上2>1则“2”>"12"正确。

对于普通的比较运算符外,JavaScript还引入了全等运算符:

运算符 名称 作用 示例
=== 严格等于 测试左右值是否相同 5 === 2 + 4
!== 严格不等于 测试左右值是否相同 5 !== 2 + 3

它们与普通的==!=的区别为后者测试值是否相同,但是数据类型可能不同,而前者的严格版本测试值和数据类型是否相同。返回为布尔类型的true/false。

除了这些外,逻辑运算符|| && !等它们还会默认转换成布尔类型进行比较。

位运算符

位运算符处理 32 位数。该运算中的任何数值运算数都会被转换为 32 位的数。结果会被转换回 JavaScript 数。

运算符 描述 例子 等同于 结果 十进制
& 5 & 1 0101 & 0001 0001 1
| 5 | 1 0101 | 0001 0101 5
~ ~ 5 ~0101 1010 10
^ 异或 5 ^ 1 0101 ^ 0001 0100 4
<< 逻辑左移 5 << 1 0101 << 1 1010 10
>> 逻辑右移 5 >> 1 0101 >> 1 0010 2
>>> 算术右移 5 >>> 1 0101 >>> 1 0010 2

其中逻辑右移和算术右移的区别在于,逻辑右移后左侧补0,适用于无符号。算术右移后左侧补符号位,适用于有符号。


字面量

字面量是由语法表达式定义的常量;或通过由一定字词组成的语词表达式定义的常量。

在 JavaScript 中,你可以使用各种字面量。这些字面量是脚本中按字面意思给出的固定的值,而不是变量。

数字字面量

对于整数,根据数字系统,整数可以用十进制(基数为 10)、十六进制(基数为 16)、八进制(基数为 8)以及二进制(基数为 2)表示。

  • 十进制整数字面量由一串数字序列组成,且没有前缀 0。
  • 八进制的整数以 0(或 0O、0o)开头,只能包括数字 0-7。
  • 十六进制整数以 0x(或 0X)开头,可以包含数字(0-9)和字母 a~f 或 A~F。
  • 二进制整数以 0b(或 0B)开头,只能包含数字 0 和 1。

例如:

0, 117 and -345 (十进制,基数为 10)
015, 0001 and -0o77 (八进制,基数为 8)
0x1123, 0x00111 and -0xF1A7 (十六进制,基数为 16)
0b11, 0b0011 and -0b11 (二进制,基数为 2)

如果数字不是整数的话,我们一般可以用科学计数法和小数形式表示:

3.14
-.2345789 // -0.23456789
-3.12e+12  // -3.12*10^12
.1e-23    // 0.1*10^(-23)=10^(-24)=1e-24

在 JavaScript 中,数字类型使用 64 位双精度浮点数表示。这意味着它可以表示的数字范围大约是 -1.7976931348623157 × 10^308 到 1.7976931348623157 × 10^308。

字符串字面量

字符串字面量是由双引号(")对或单引号(')括起来的零个或多个字符。字符串被限定在同种引号之间;也即,必须是成对单引号或成对双引号。

"foo"
'bar'
"1234"
"one line \n another line"
"John's cat"

当然还可以使用一些特殊字符:

字符 意思
\0 Null 字节
\b 退格符
\f 换页符
\n 换行符
\r 回车符
\t Tab (制表符)
\v 垂直制表符
\' 单引号
\" 双引号
\\ 反斜杠字符(\)
\uXXXX 由四位十六进制数字 XXXX 表示的 Unicode 字符。

当然,如果要输入未出现在表中的字符,需要使用转义字符\。例如我们要输入引号,通过在引号前加上反斜线'\',可以在字符串中插入引号。

"He read \"The Cremation of Sam McGee\" by R.W. Service."

输出结果为:He read "The Cremation of Sam McGee" by R.W. Service.


JavaScript函数

函数声明

一个函数定义(也称为函数声明,或函数语句)由一系列的function关键字组成:

  • 函数的名称。
  • 函数参数列表,包围在括号中并由逗号分隔。
  • 定义函数的 JavaScript 语句,用大括号{}括起来。

例如:

function square(number) {
  return number * number;
}

其中,不需要给函数返回这和参数定义类型

函数作用域

当同一个作用域下两个参数或者变量同名时,就会产生命名冲突。更近的作用域有更高的优先权,所以最近的优先级最高,最远的优先级最低。例如:

var j=6;    //作用域为当前工作区
function test()
{
    var j;    //作用域为function test()内
    j = 7;
}
test();
window.alert(j);

此时j的命名冲突了,在函数test里面,j具有优先权,则此时j=7。但是它的作用域止步于函数内,而在整个工作区中的变量j仍然是6,故最后输出为6。可以把工作区的j看成是全局变量,而函数中的则为局部变量。

如果想要在函数中改变j的值,我们需要把它的值存储起来,一般当成window的内联对象的属性使用。例如把j=7改为:

window.j = 7;

之后在介绍内联对象时会详细说明。


JavaScript对象

对象和属性

在 JavaScript 中,一个对象可以是一个单独的拥有属性和类型的实体。

一个JavaScript对象有很多属性。一个对象的属性可以被解释成一个附加到对象上的变量。对象的属性和普通的JavaScript变量基本没什么区别,仅仅是属性属于某个对象。属性定义了对象的特征。你可以通过点符号来访问一个对象的属性。

它是形如:objectName.propertyName的调用形式,同时我们还可以使用方括号访问。对象有时也被叫作关联数组(数组表示法),因为每个属性都有一个用于访问它的字符串值。例如,你可以按如下方式访问 myCar 对象的属性:

myCar["make"] = "Ford";
myCar["model"] = "Mustang";
myCar["year"] = 1969;

一个对象的属性名(不是属性值)可以是任何有效的 JavaScript 字符串,或者可以被转换为字符串的任何类型,包括空字符串。然而,一个属性的名称如果不是一个有效的 JavaScript 标识符(例如,一个由空格或连字符,或者以数字开头的属性名),就只能通过方括号标记访问。

接下来就是创建对象了,和申明变量一样,只需要申明为对象即可,我们可以通过new语句来申明。new语句可以添加对象或者属性,已经继承结构。它的格式为:new constructor(),例如:

var obj = new object();
obj.year = 2023;
delete obj.year;

与C/C++不同的是,JavaScript可以随时申明对象,且JavaScript没有类的定义,但是有一些类的特征。

如上例子,我们通过delete语句,它可以删除对象中的属性,在例子中它删除了obj的year属性。

我们默认的构造函数为object()它不会直接向创建的对象添加属性或方法,而是直接修改对象,并继承一些属性与方法,例如toString() and valueOf()

除此之外,JavaScript还提供了一个对象初始化器(Object initializer),可以隐式调用构造函数创建对象的同时也创建属性,例如:

var myHonda = {color: "red", wheels: 4, engine: {cylinders: 4, size: 2.2}};

枚举属性

JavaScript提供了for...in 语句以任意顺序迭代一个对象的可枚举属性,包括继承的可枚举属性。它的语法为:

for (variable in object)
statement

其中,variable为在每次迭代时,variable 会被赋值为不同的属性名。object为非Symbol类型的可枚举属性被迭代的对象。

例如:

var obj = {a:1, b:2, c:3};
for (var i in obj) {
  console.log("obj." + i + " = " + obj[i]);
}

很明显,它的输出就是

"obj.a = 1"
"obj.b = 2"
"obj.c = 3"

对象引用

在JavaScript中,如果变量代表一个对象, 那么存储在该变量中的内容就是一个指向该对象的引用(指针),而不是对象本身。

var o1 = new Object();
o1.data = "Hello";
var o2 = o1;
o2.data += " World!";
console.log("o1.data = " + o1.data + "\no2.data" + o2.data);

在这里o2引用了o1的地址,故此时对o2的数据改变即对o1的数据改变,它们同时都被改变了,因为地址相同。此时的o2相当于o1的引用(Reference)。

同理在作为函数参数时,对象参数也为对象的引用而不是复制一份。

方法

创建与使用方法

一个方法是关联到某个对象的函数,或者简单地说,一个方法是一个值为某个函数的对象属性。实际上,JavaScript在处理函数上都是以方法来处理的,对于一个函数例如function function_name() { },处理它的声明的时候JavaScript做了下面几步:

  1. 创建一个专门的函数对象去表示函数,例如 objectName = new Object();
  2. 创建一个与函数同名的变量,例如objectName.methodname
  3. 为这个变量赋予指向函数对象的引用,例如objectName.methodname = function_name;

在使用时,我们一般通过objectName.methodname(params)的方式引用,接下来通过一个例子来展示:

对象创建:

function makeBTNode(value) {
  var node = new Object();
  node.left = node.right = null;
  node.value = value;
  node.isLeaf = leaf;    //指针指向leaf()变量,此时isLeaf是方法
  return node;
}

创建leaf()变量:

function leaf() {
  return this.left == null && this.right == null;
}

使用方法:

var node1 = makeBTNode(3);
var node2 = makeBTNode(7);
node1.right = node2;
window.alert("node1 is a leaf: " + node1.isLeaf());
window.alert("node2 is a leaf: " + node2.isLeaf());

此时输出为:

函数leaf()被声明为一个函数, 但是打算将其只作为方法(而不是函数)使用。当然我们可以删除声明这个函数, 而代之以直接简单地创建一个方法:

node.isLeaf = 
    function leaf() {
      return this.left == null && this.right == null;
    };

当我们把leaf()看作一个方法的时候,即node.isLeaf =
function ()
后(此时leaf名称可省),leaf()将不能像正常函数一样被直接调用,相反的需要调用的是isLeaf()方法。这个和C++中的成员函数比较相似。

this指针

JavaScript 有一个特殊的关键字 this,它可以在方法中使用以指代当前对象。这个和C++中this指针类似。例如:

myFather.name = function () {
    return this.firstName + " " + this.lastName;
};

因为调用对象的属性需要使用.字符访问,此时在这个函数中,对象就是当前的对象,故用this指代。

构造函数

我们知道,在创建对象的时候会使用默认的构造函数Object(),当然和C++/Java一样,我们也可以自己创建构造函数。我们把构造的对象称为构造函数的一个实例(instance)。

通常,我们自定义的构造函数会初始化一些属性,为对象类型创建一个函数以声明类型的名称、属性和方法。例如,你想为汽车创建一个类型,并且将这类对象称为 car ,并且拥有属性 make, model,和 year,你可以创建如下的函数:

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

此时因为我们不知道对象的名字,在方法中我们通过使用 this 将传入函数的值赋给对象的属性。除此之外,构造函数不需要返回值。

现在可以创建一个myCar对象:

var mycar = new Car("Eagle", "Talon TSi", 1993);

我们通过这个构造函数,创建了 mycar 并且将指定的值赋给它的属性。因而 mycar.make 的值是字符串"Eagle",mycar.model的值是字符串Talon TSi ,mycar.year 的值是整数 1993。

此外,JavaScript还提供了一个instanceof 运算符用于检测对象是否为构造函数生成的。

语法为:object instanceof constructor,返回为布尔类型,例如:

console.log(mycar instanceof Car);

Object.create()

对象也可以用 Object.create() 方法创建。该方法非常有用,因为它允许你为创建的对象选择一个原型对象,而不用定义构造函数。此时要求必须有原型对象,新的对象会继承原型对象的属性与方法。语法为:

newobject = Object.create(origin_object);

例如:

var person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

var me = Object.create(person);
me.name = 'Matthew';
me.isHuman = true;
me.printIntroduction();

此时,me对象是以person对象为原型创造的,继承了person对象的属性与方法,故拥有isHuman属性与printIntroduction()方法。


JavaScript数组

我们之前学习知道,JavaScript对象是可以使用数组表示法来表示的,所以又可以把对象称为关联数组。此外,在 JavaScript 中,数组不是基本类型,而是内置的具有数组特征的 Array 对象。

创建数组

创建数组非常简单,就是使用Array对象的构造函数创建:

var ary1 = Array();

此外还可以给构造函数两个以上参数(这个很重要,因为只有一个参数的情况下效果不一样),用这些值对数组进行初始化。

var ary2 = Array(4, true, "OK");

通过这个例子我们还可以知道,JavaScript的数组中,数据不一定以同一种数据类型存储。

length属性

数组在JavaScript既然是以对象的形式出现的,那么它也自带了一些属性与方法,其中最常用的就是length 属性。

length 属性是 Array 的实例属性。返回或设置一个数组中的元素个数。该值是一个无符号 32-bit 整数,并且总是大于数组最高项的下标。

例如上面例子中,ary1.length = 0; ary2.length = 3; 。这个属性与C/C++中的strlen()函数作用相似,只是它是以属性存储的。

数组下标

JavaScript 数组不是关联数组,因此,不能使用任意字符串作为索引访问数组元素,但必须使用非负整数作为索引访问。引索时从0开始的。

例如之前例子中ary2的引索情况:

  • ary2[0] = 4;
  • ary2[1] = true;
  • ary2[2] = "OK";

JavaScript 语法要求使用方括号表示法而不是点号表示法来访问以数字开头的属性。也可以用引号包裹数组下标(例如,ary2['2']  =  ary2[2])。

数组的对象属性和数组元素列表是分开的,数组的遍历和修改操作不能应用于这些命名属性。因此使用对象属性的方式访问Array对象元素是错误的。

例如这个的例子是错误的:ary2.1,这是因为在对象中,ary2.1将计算ary2的1这个属性的值,但是在点号表示法中,属性不能以数字开头,这个例子是错误的。

数组初始化器

数组初始化器是用数组字面量 [] 代替 new Array()。它隐式地调用数组构造函数,在日常使用中经常使用这种方法代替创建数组。

例如前面例子的ary2,它等价于ary3:

var ary3 = [4, true, "OK"];

而我们还可以使用数组初始化器来创建多维数组,例如下面这个二维数组:

var ary4 = [["00", "01", "02"],
             ["10", "11", "12"],
             ["10", "11", "12"] ]

此时因为多维数组实际是嵌套数组,故可以通过嵌套的方式表示。此时ary4.length == 3,因为它有三个数组元素,每一个都是一个数组。

因此,我们还可以判断每一个数组有多少个元素,例如:ary4[1].length == 3,因为第二个元素ary1[1]有三个数组元素。

我们要访问多维数组也非常简单,与其他语言一样访问即可,例如ary4[1][1] == 11.

动态改变数组

与其他语言不同的是,JavaScript的数组不用通过复杂地分配内容来动态调整数组,而是通过方括号表示法与length属性动态的改变数组。我们同样以ary2为例,ary2的定义如下:

var ary2 = Array(4, true, "OK");

例如我们直接增加元素:

ary2[3] = -12.6;

此时引擎将相应地更新数组的 length 属性,ary2.length == 4
类似的如果我们要直接减少元素,直接减少 length 属性会删除元素。例如:

ary2.length = 2;

那么此时ary2的元素只剩下4与true了,比length大的元素将直接被删除。

此时有一种情况前面没有提到,就是非使用数组初始化器的时候,如果Array()构造函数只有一个Number类型的参数,则此时和C/C++中一样,这是在固定分配数组的大小。例如:

var ary5 = new Array(5);

此时创建了一个长度为5的数组,但是并没有对5个数组元素进行初始化,我们取它的length属性可以知道:ary5.length = 5;

数字方法

在JavaScript中,数组是个数组对象,那么它也有它内置的一些方法。

方法 描述 参数
push() 在数组末尾添加一个或多个元素,并返回新的数组长度。 可以接收任意数量的参数,每个参数都将被添加到数组的末尾。
pop() 删除并返回数组的最后一个元素。 该方法没有参数。
sort() 按字母表顺序对数组排序,也可以传入一个比较函数进行自定义排序。 可选参数“compareFunction”是一个用于定义排序顺序的函数。如果省略该参数,则按字母表顺序对数组排序。
splice() 删除或替换数组中的元素,并可以在指定位置插入新元素。 接收三个或更多个参数:起始索引(必需)、要删除的元素数量(可选)、要插入到数组中的新元素(可选)。如果省略后面两个参数,则只删除起始索引处的元素。
shift() 删除并返回数组的第一个元素。 该方法没有参数。
unshift() 在数组开头添加一个或多个元素,并返回新的数组长度。 可以接收任意数量的参数,每个参数都将被添加到数组的开头。
slice() 返回数组的一个片段,从开始到结束(但不包括结束)。 传入两个参数:起始索引和结束索引(但不包括结束索引)。如果省略结束索引,则将返回从起始索引到数组末尾的所有元素。
concat() 将两个或多个数组合并成一个新数组。 可以接受任意数量的参数,每个参数都是要合并到一个新数组中的数组。
join() 将所有数组元素连接成一个字符串。 可选参数“separator”,指定在每个元素之间使用的分隔符。如果省略该参数,则使用默认逗号作为分隔符。
indexOf() 返回指定元素在数组中第一次出现的位置。 传入一个参数,即要查找的元素的值。
reverse() 反转数组的顺序。 该方法没有参数。

我们对其中的一些方法进行说明。

sort()

sort()方法用原地算法对数组的元素进行排序,并返回数组。它的格式为:

sort()  //or
sort(compareFn)

如果没有指明 compareFn ,那么元素会按照转换为的字符串的诸个字符的 Unicode 位点进行排序。这是因为JavaScript的函数元素是可以允许不同属性的。

这样就会导致元素9 大于元素 80,因为在 Unicode 诸位比较的顺序上 "80" 要比 "9" 要靠前。所以我们一般都会指定排序函数compareFn

如果指明了 compareFn ,compareFn只接受返回值存在<0、>0或===0的情况。数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素:

  • 如果 compareFn(a, b) 大于 0,b 会被排列到 a 之前。
  • 如果 compareFn(a, b) 小于 0,那么 a 会被排列到 b 之前;
  • 如果 compareFn(a, b) 等于 0,a 和 b 的相对位置不变。
  • compareFn(a, b) 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。

那么最简单的比较函数就是:

function compareFn(a, b) {
  if (在某些排序规则中,a 小于 b) {
    return -1;
  }
  if (在这一排序规则下,a 大于 b) {
    return 1;
  }
  // a 一定等于 b
  return 0;
}

要比较数字而非字符串,比较函数可以简单的用 a 减 b,如下的函数将会将数组升序排列:

function compareNumbers(a, b) {
  return a - b;
}

此时是个升序排列,如果为降序排列则参数调整为b,a,或者将比较函数中的判断条件相反即可。

//改变参数位置
function compareNumbersReverse1(b, a) {
  return a - b;
}
//改变判断条件位置
function compareNumbersReverse2(a, b) {
  return b - a;
}

举个例子,例如:

const numArray = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
// 使用比较函数进行数值升序排序
numArray.sort(function compare(first, second) {
  return first - second;
});
console.log(numArray);
// 输出:[1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

splice()

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容(仅返回被修改的内容)。此方法会改变原数组。

它的语法为:

  • splice(start)
  • splice(start, deleteCount)
  • splice(start, deleteCount, item1)
  • splice(start, deleteCount, item1, item2, itemN)

其中的参数分别为:

  • start指定修改的开始位置(从 0 计数)。如果超出了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组末位开始的第几位(从 -1 计数,这意味着 -n 是倒数第 n 个元素并且等价于 array.length-n);如果负数的绝对值大于数组的长度,则表示开始位置为第 0 位。
  • deleteCount(可选):整数,表示要移除的数组元素的个数。如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。如果 deleteCount 被省略了,或者它的值大于等于array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。
  • item1, item2, ...(可选) 要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。

下面通过一些例子来理解这个方法:

插入元素:

var numArray = [1,2,3,4,5,6,7,8,9];
numArray.splice(2, 0, 2.5);
//numArray.toString(): 1,2,2.5,3,4,5,6,7,8,9

相当于从index = 2位置开始,删除0个元素,插入一个元素2.5。这个过程结束后,length属性将会被自动修改,这里的插入导致了右侧数据向右移动。

这个例子中toString() 是 JavaScript 的一个数组方法,用于将数组转换为一个包含数组元素的字符串。它没有参数,可以在任何数组上使用。这里我们用来展示数组与返回值情况。

删除元素(基于上述例子):

numArray.splice(5,3)
//numArray.toString(): 1,2,2.5,3,4,8,9

从index = 5位置开始,删除3个元素。这个过程结束后,length属性将会被自动修改,这里的插入导致了右侧数据向左移动。

在定义处我们注意到,splice() 方法以数组形式返回被修改的内容(仅返回被修改的内容)。我们可以验证这个过程,例如:

var numArray = [1,2,3,4,5,6,7,8,9];
window.alert(numArray.splice(5,3).toString());
// output of following: 5,6,7

我们删除了3个元素,修改的内容就是5,6,7三个元素,故返回5,6,7.

如果是看替换元素的例如:

var numArray = [1,2,3,4,5,6,7,8,9]; 
console.log(numArray.splice(5,3,10,11).toString());
console.log(numArray.toString());

我们此时修改的元素为6,7,8,那么返回的就是6,7,8(而不是被替换后的10,11),最后整个输出为:

如果是只增加而非减少,例如:

var numArray = [1,2,3,4,5,6,7,8,9]; 
console.log(numArray.splice(3,0,10,11).toString());
console.log(numArray.toString());

此时我们没有修改元素,而是增加,那么此时的输出为:

numArray.splice(3,0,10,11).toString()的值为空。

栈与队列实现

push()、pop()与shift()方法常用于使用数组实现栈与队列。

如果用push()与pop()方法,可以实现一个反转的栈,因为它们是在尾部插入删除,而正栈是头部插入删除。例如:

var stack = new Array();

stack.push('H');
stack.push('i');
stack.push('!');

var c3 = stack.pop(); // pops '!'
var c2 = stack.pop(); // pops 'i'
var c1 = stack.pop(); // pops 'H'
window.alert(c1 + c2 + c3);  // displays "Hi!"

我们先从尾部插入数据后得到["H", "i", "!"],再依次从尾部进行删除,这样在尾部同时操作的数据结构是反转的栈,此时栈指针为length。

而实现队列功能,只需要用push()方法在尾部插入,shift()方法在头部删除即可。


JavaScript内置对象

内置对象就是JavaScript中自带的对象,例如Array数组对象。 

以下是几个常用的 JavaScript 内置对象及其描述:

对象 描述
Object 表示 JavaScript 中的对象。可以使用对象字面量或构造函数创建对象,并且可以在运行时添加或删除属性。
Array 表示 JavaScript 中的数组。可以使用数组字面量或构造函数创建数组,并且具有许多有用的方法,如 push()pop()shift()unshift()slice() 等。
String 表示 JavaScript 中的字符串。可以使用字符串字面量或构造函数创建字符串,并且具有许多有用的方法,如 indexOf()slice()concat()replace() 等。
Number 表示 JavaScript 中的数字。可以使用数字字面量或构造函数创建数字,并且具有许多有用的方法,如 toFixed()toPrecision()valueOf() 等。
Boolean 表示 JavaScript 中的布尔值。可以使用布尔字面量或构造函数创建布尔值,并且具有许多有用的方法,如 valueOf()
Date 表示 JavaScript 中的日期和时间。可以使用构造函数创建日期,并且具有许多有用的方法,如 getDate()getMonth()getFullYear()getTime() 等。
Math 提供了许多与数学相关的功能,如三角函数、对数函数、指数函数等。可以通过 Math 对象访问这些功能并使用它们。
RegExp 表示 JavaScript 中的正则表达式,用于在字符串中匹配特定的模式。可以使用字面量或构造函数创建正则表达式,并且具有许多有用的方法,如 test()exec() 等。
Function 表示 JavaScript 中的函数。可以通过函数声明、函数表达式或 Function 构造函数来创建函数,并且可以接受任意数量的参数,并且可以返回值。

除了这些对象以外,在 JavaScript 中,有一个永远被定义的全局对象。我们马上来介绍。

全局对象(Window)

在 JavaScript 中,有一个永远被定义的全局对象。在一个 web 浏览器中,当脚本创建全局变量时,他们作为该全局对象的成员被创建。在 Web 浏览器中,脚本没有专门作为后台任务启动的任何代码都将window 作为其全局对象。

window 对象是浏览器中的全局对象。它具有下面一些性质:

  • 任何全局变量或者内置对象都可以通过 window 的属性来访问。

例如访问全局变量:

var foo = "foobar";
foo === window.foo; // True

访问内置对象,例如函数对象:

function greeting() {
   console.log("Hi!");
}

window.greeting();//window调用函数对象

除此之外,window还有它自己独特的属性与方法,例如:

window.Infinity表示无穷大,window.alert()表示调出警告框。

String、Number与Boolean

我们知道,函数String(), Boolean()和Number() 可以将类型转换。如果将它们作为相应对象的构造函数,它不仅会转换类型,还会把转换后的值包装到一个对象中。

例如:

var wrappedNumber = new Number(5.625);
window.alert(typeof wrappedNumber.valueOf());  //Output is: Number

此时wrappedNumber是Number的一个实例,valueOf()是Number的一个方法,可以取它的值。

除此之外,Number还有其他方法:

方法 描述
Number.toFixed() 返回一个指定小数位数的字符串表示。
Number.toExponential() 返回指数计数法表示的字符串。
Number.toPrecision() 返回一个指定有效位数的字符串表示。
Number.toString() 把一个数字转换为一个字符串,并返回结果。
Number.valueOf() 可以返回 Number 对象的原始值。
Number.isNaN() 判断一个值是否是 NaN
Number.isFinite() 判断一个值是否为有限数。
Number.parseInt() 把字符串转换为整数。
Number.parseFloat() 把字符串转换为浮点数。
Number.MAX_VALUE 返回 JavaScript 中的最大数。
Number.MIN_VALUE 返回 JavaScript 中的最小正数。
Number.NEGATIVE_INFINITY 表示负无穷大的特殊值。
Number.POSITIVE_INFINITY 表示正无穷大的特殊值。

例如:

window.alert(wrappedNumber.toFixed(2));    //取小数位数两位
window.alert(wrappedNumber.toExponential(2));    //取科学计数法
window.alert(wrappedNumber.toString(2));    //取字符串

与Array一样,String对象也有length作为属性。

我们也有一些String方法:

方法 描述
String.charAt() 返回指定索引位置处的字符。
String.charCodeAt() 返回指定索引位置处字符的 Unicode 值。
String.concat() 将两个或多个字符串连接起来,并返回新的字符串。
String.indexOf() 返回字符串中第一个匹配项的索引。
String.lastIndexOf() 返回字符串中最后一个匹配项的索引。
String.slice() 提取字符串中的一部分,并返回新的字符串。
String.substring() 提取字符串中的一部分,并返回新的字符串。
String.substr() 从字符串中提取指定长度的子字符串。
String.split() 将字符串分割成子字符串数组。
String.replace() 替换字符串中的某些字符或子字符串,并返回新的字符串。
String.toLowerCase() 将字符串转换为小写字母。
String.toUpperCase() 将字符串转换为大写字母。
String.trim() 去除字符串两端的空格,并返回新的字符串。
String.match() 在字符串中查找一个或多个匹配项,并返回一个包含匹配项的数组。
String.search() 在字符串中查找匹配项,并返回第一个匹配项的索引。
String.includes() 判断字符串中是否包含指定的文本,并返回布尔值。
String.startsWith() 判断字符串是否以指定的文本开头,并返回布尔值。
String.endsWith() 判断字符串是否以指定的文本结尾,并返回布尔值。

Date

Date对象用于显示计算机的当前日期与时间。它的格式是:

new Date();
new Date(value);
new Date(dateString);
new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]);

日常使用中将显示日期与时间,例如:

var d = new Date();

我们可以使用它的方法:

方法 描述
Date.now() 返回自 1970 年 1 月 1 日 00:00:00 UTC 起的毫秒数。
Date.parse() 将日期字符串解析为格林威治时间,并返回与之对应的毫秒数。
Date.UTC() 根据 UTC 时间返回一个毫秒数。
Date.getDate() 返回日期中的天数(1 到 31)
Date.getDay() 返回星期几(0 表示周日,1 表示周一,以此类推)。
Date.getFullYear() 返回日期中的年份。
Date.getHours() 返回日期中的小时数(0 到 23)。
Date.getMilliseconds() 返回日期中的毫秒数(0 到 999)。
Date.getMinutes() 返回日期中的分钟数(0 到 59)。
Date.getMonth() 返回日期中的月份(0 到 11)。
Date.getSeconds() 返回日期中的秒数(0 到 59)。
Date.toLocaleDateString() 返回一个表示日期和时间的字符串,该字符串格式由浏览器的语言环境决定。
Date.toLocaleTimeString() 返回一个表示日期和时间的字符串,该字符串格式由浏览器的语言环境决定。
Date.toLocaleString() 返回一个表示日期和时间的字符串,该字符串格式由浏览器的语言环境决定。

例如展示当前本地时间:

var now = new Date();
window.alert("Current date and time: "+ now.toLocaleString());

我们一般使用Date对象是用于计算时间,例如:

var startTime = new Date();

var endTime = new Date();
window.alert("Processing required " +
             (endTime - startTime)/1000 + 
             " seconds.");

其中,startTime和endTime都是到1970 年 1 月 1 日 00:00:00 UTC 起的毫秒数。除1000可以得到秒数。

Math

Math 是一个内置对象,它拥有一些数学常数属性和数学函数方法。与其他全局对象不同的是,Math 不是一个构造函数Math 的所有属性与方法都是静态的。

我们一般使用它的一些方法进行数学计算,例如:

方法 描述
Math.abs(x) 返回数的绝对值。
Math.acos(x) 返回数的反余弦值。
Math.acosh(x) 返回数的反双曲余弦值。
Math.asin(x) 返回数的反正弦值。
Math.asinh(x) 返回数的反双曲正弦值。
Math.atan(x) 返回数的反正切值。
Math.atanh(x) 返回数的反双曲正切值。
Math.atan2(y, x) 返回点 (x,y) 到 x 轴的夹角(以弧度为单位)。
Math.ceil(x) 返回大于或等于数的最小整数。
Math.cos(x) 返回数的余弦值。
Math.cosh(x) 返回数的双曲余弦值。
Math.exp(x) 返回 e 的指数。
Math.floor(x) 返回小于或等于数的最大整数。
Math.log(x) 返回数的自然对数(底为 e)。
Math.max(x, y, z, ..., n) 返回参数中的最大值。
Math.min(x, y, z, ..., n) 返回参数中的最小值。
Math.pow(x, y) 返回基数的指数次幂。
Math.random() 返回一个介于 0 到 1 之间的随机数。
Math.round(x) 返回数四舍五入后的值。
Math.sign(x) 返回一个表示数字符号的值,其中 1 表示正数,-1 表示负数,0 表示零。
Math.sin(x) 返回数的正弦值。
Math.sinh(x) 返回数的双曲正弦值。
Math.sqrt(x) 返回数的平方根。
Math.tan(x) 返回数的正切值。
Math.tanh(x) 返回数的双曲正切值。
Math.trunc(x) 返回删除数的小数部分后的整数。

内置对象的字面量

总结上文,JavaScript 提供用于原始对象的构造器:

var x1 = new Object();    // 一个新的 Object 对象
var x2 = new String();    // 一个新的 String 对象
var x3 = new Number();    // 一个新的 Number 对象
var x4 = new Boolean();   // 一个新的 Boolean 对象
var x5 = new Array();     // 一个新的 Array 对象
var x6 = new RegExp();    // 一个新的 RegExp 对象
var x7 = new Function();  // 一个新的 Function 对象
var x8 = new Date();      // 一个新的 Date 对象

Math() 对象不再此列。Math类似于全局对象,new 关键词不可用于 Math

我们可以像对象和数组一样,使用初始化器来代替这种写法:

  • 使用对象字面量 {} 代替 new Object()
  • 使用字符串字面量 "" 代替 new String()
  • 使用数值字面量代替 Number()
  • 使用布尔字面量代替 new Boolean()
  • 使用数组字面量 [] 代替 new Array()
  • 使用模式字面量/ /代替 new RexExp()
  • 使用函数表达式 () {} 代替 new Function()

例如:

var x1 = {};            // 新对象
var x2 = "";            // 新的原始字符串
var x3 = 0;             // 新的原始数值
var x4 = false;         // 新的原始逻辑值
var x5 = [];            // 新的数组对象
var x6 = /()/           // 新的正则表达式对象
var x7 = function(){};  // 新的函数对象

正则表达式

正则表达式的创建与测试

RegExp (Regular Expression,正则表达式)对象用于将文本与一个模式匹配。

正则表达式是构成搜索模式的字符序列。该搜索模式可用于文本搜索和文本替换操作

本部分引用:正则表达式30分钟入门教程

很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是*?。如果你想查找某个目录下的所有的Word文档的话,你会搜索*.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求。

JavaScript中,正则表达式往往用于测试在 HTML 表单中输入的字符串是否具有某种格式, 或者用正则表达式的术语讲, 测试它是否属于具有正确格式的字符串集合。

例如我们需要匹配三个连续的数字字符集合,正则表达式中应该写作:

\d\d\d

这里的\d表示字符为数字(digite)字符,大小为字符'0'~'9'。它是正则表达式规定的一个特殊代码,称为元字符(metacharacter)。这个正则表达式匹配的是字符中有连续三个数字的情况,例如000, 111这类。我们在JavaScript中插入正则表达式进行匹配测试:

var acTest = new RegExp("^\\d\\d\\d$");
if (!acTest.test(areaCode)) {
  window.alert(areaCode + " is not a valid area code.");
}

我们此时测试areaCode字符串中,是否存在三个连续整数。此时acTestRegExp对象的一个实例,test() 方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 true 或 false

RegExp()构造函数中的是正则表达式的字符形式,此时需要转义(escape)后才能写在JavaScript中,故在\前还有一个\作为转义标志符。^是匹配输入的开始,$是匹配输入的结束。这个表达式仅仅匹配连续三个整数的情况。

如果没有^,则只要是\d结尾的,满足\d\d\d的均会被匹配到;同理如果没有$,只要是以\d开头的,满足\d\d\d的均会被匹配到。

在前面的例子中可以知道,构造函数的写法可以替换为字面量写法,同样的RegExp对象也可以,这种形式称为正则表达式字面量:使用/ /代替 new RexExp()。例如:

var acTest= /^\d\d\d$/;

此时不需要转义。

元字符

我们知道,刚才例子中的\d是一个元字符,以下是一些常用的正则表达式元字符及其描述:

元字符 描述
. 匹配除换行符外的任何单字符。
\b 匹配单词的开始或结束。
\w 匹配字母、数字、下划线。等同于[A-Za-z0-9_]
\W 匹配非字母、数字、下划线。等同于 [^A-Za-z0-9_]
\d 匹配数字。等同于 [0-9]
\D 匹配非数字。等同于 [^0-9]
\s 匹配空白字符,包括空格、制表符、换行符等。等同于 [\t\n\r\f]
\S 匹配非空白字符。等同于 [^\t\n\r\f]
[abc] 匹配 a、b、c 中的一个字符。
[^abc] 匹配不是 a、b、c 中的任意一个字符。
[a-z] 匹配 a 到 z 中的任意一个小写字母。
[A-Z] 匹配 A 到 Z 中的任意一个大写字母。
[0-9] 匹配 0 到 9 中的任意一个数字。
^ 匹配字符串的开始。
$ 匹配字符串的结尾。
() 捕获子表达式。
\n 匹配换行符。

和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^$的意义就变成了匹配行的开始处和结束处。

例如一段连接的匹配,hi后面不远处跟着一个Lucy,你应该用\bhi\b.*\bLucy\b

这里相当于两个正则表达式,一个是\bhi\b,另一个是\bLucy\b,中间用.隔开,表示中间匹配到除换行符外任意字符都行,*则表示匹配多次,之后会在限定符中提到。那么这样的正则表达式就能匹配到以hi开头,中间不管什么,后面跟着一个Lucy的字符了。

特殊符号

这些符号在正则表达式中有它的特殊作用,在字符中使用时必须使用\转义:

特殊符号 描述
\ 转义字符,在某些特定字符前面使用可以取消特殊含义。
^ 匹配字符串的开始,如果不放在字符组中则无需转义。
$ 匹配字符串的结尾,如果不放在字符组中则无需转义。
. 匹配除换行符外任意单个字符。
{} 匹配前一个元素指定次数。
() 分组,捕获匹配的子串供以后使用。
[] 字符集合,匹配括号内任意一个字符。
| 或运算,匹配两个表达式中的任意一个。

我们用或运算,或者说合并(Union)举个例子:\d|\s,表示一组字符串要么是数字,要么是空白格符。

限定符

限定符可以用来限制匹配的次数,有助于更精确地匹配文本。

以下是一些常用的正则表达式限定符及其描述:

限定符 描述
* 匹配前一个元素 0 次或多次。
+ 匹配前一个元素 1 次或多次。
? 匹配前一个元素 0 次或 1 次。
{n} 匹配前一个元素恰好出现 n 次。
{n,} 匹配前一个元素至少 n 次。
{n,m} 匹配前一个元素至少 n 次,不超过 m 次。

例如之前的\d\d\d,你会觉得重复的很麻烦,我们可以使用限定符来修改成:\d{3}

再例如,密码是5-12位的,此时匹配为\d{5,12}


后记

以上为JavaScript的基础学习笔记部分。

这里的一切都有始有终,却能容纳所有的不期而遇和久别重逢。
最后更新于 2023-05-17