JavaScript学习笔记

简介

JavaScript 是互联网上最流行的脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。

JavaScript 是一种轻量级的编程语言。

JavaScript 是可插入 HTML 页面的编程代码。

Javascript用法

<script>标签

HTML 中的脚本必须位于<script></script>标签之间。

脚本可位于 HTML 的 <body><head> 部分中,或者同时存在于两个部分中。

<head>或者<body>的Javascript

脚本可位于 HTML 的 <body><head> 部分中,或者同时存在于两个部分中。

通常的做法是把函数放入 <head> 部分中,或者放在页面底部。这样就可以把它们安置到同一处位置,不会干扰页面的内容。

但是放在<head>里可能会有问题,因为在页面DOM元素加载完之前,js脚本就以及执行完了,如果js脚本里有操作DOM元素的话,可能就没有效果了,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="cn">
<head>
<meta charset="UTF-8">
<title>script标签的位置</title>
<script type="text/javascript">
function btnClick() {
alert('helloWorld!');
};
document.getElementById("btn").onclick = function() {
alert('hello777!');
};
</script>
</head>
<body>
<input type="button" id="btn" value="点击我" onclick="btnClick();">
</body>
</html>

所以放在<body>底部比较好,因为这个位置的js会等页面DOM元素加载完才执行,并且不会阻塞渲染。

如果只想写在<head>里又想让它正常运行,可以把代码写进onload事件里(等页面加载好的一个事件),使其成为内嵌函数,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="cn">
<head>
<meta charset="UTF-8">
<title>script标签的位置</title>
<script type="text/javascript">
function btnClick() {
alert('helloWorld!');
};
onload = function() {
document.getElementById("btn").onclick = function() {
alert('hello777!');
};
}
</script>
</head>
<body>
<input type="button" id="btn" value="点击我" onclick="btnClick();">
</body>
</html>

函数和事件

通常,我们需要在某个事件发生时执行代码,比如当用户点击按钮时。

如果我们把 JavaScript 代码放入函数中,就可以在事件发生时调用该函数。

外部的Javascript

也可以把脚本保存到外部文件中。外部文件通常包含被多个网页使用的代码。

外部 JavaScript 文件的文件扩展名是 .js。

如需使用外部文件,请在<script> 标签的 “src” 属性中设置该 .js 文件

Chrome浏览器执行Javascript

1、Console控制台调试javascript代码

image-20210409170935845

2、Chrome snippets小脚本

可以在sources面板的snippets选项里新建脚本文件并执行

image-20210409171248011

Javascript输出

Javascript没有任何打印或者输出的函数。但是可以通过不同的方式来输出数据:

1、window.alert()弹出警告框。alert() 函数在 JavaScript 中并不常用,但它对于代码测试非常方便。

2、document.write()方法将内容写到HTML文档中。直接写入 HTML 输出流,只能在 HTML 输出中使用 document.write。如果在文档加载后使用该方法,会覆盖整个文档。

3、innerHTML写入到HTML元素。

4、console.log()写入到浏览器的控制台。

通常用法是用javascript写一个函数,通过操作DOM元素来修改内容document.getElementById("id").innerHTML=xxx,最后在页面写一个按钮,通过事件(onclick等等)来触发这个函数

Javascript语法

字面量

固定值称为字面量,如3.14。

数字字面量可以是整数或者是小数,或者是科学计数(e)。

字符串字面量用单引号或双引号包围。

表达式字面量用于计算。

数组字面量定义一个数组。

对象字面量定义一个对象。

函数字面量定义一个函数。

变量

Javascript用var来声明(创建)变量,在 2015 后的 JavaScript 版本 (ES6) 允许我们使用 const 关键字来定义一个常量,使用 let 关键字定义的限定范围内作用域的变量。

变量必须以字母开头

变量名称对大小写敏感(y 和 Y 是不同的变量)

语句

javascript语句用分号分隔。

通常我们在每条可执行的语句结尾添加分号。

使用分号的另一用处是在一行中编写多条语句。

在 JavaScript 中,用分号来结束语句是可选的。

注释

单行注释以双斜杠//开头

多行注释以 /* 开始,以 */ 结尾。

数据类型

JavaScript 有多种数据类型:数字,字符串,数组,对象等等:

1
2
3
4
5
var length = 16;                                  // Number 通过数字字面量赋值
var points = x * 10; // Number 通过表达式字面量赋值
var lastName = "Johnson"; // String 通过字符串字面量赋值
var cars = ["Saab", "Volvo", "BMW"]; // Array 通过数组字面量赋值
var person = {firstName:"John", lastName:"Doe"}; // Object 通过对象字面量赋值

函数

function来定义函数

字母大小写

JavaScript 对大小写是敏感的。 JavaScript 语句和 JavaScript 变量都对大小写敏感。

当编写 JavaScript 语句时,请留意是否关闭大小写切换键。

函数 getElementByIdgetElementbyID 是不同的。

同样,变量 myVariableMyVariable 也是不同的。

字符集

JavaScript 使用 Unicode 字符集。

Unicode 覆盖了所有的字符,包含标点等字符。

命名规则

JavaScript 中,常见的是驼峰法的命名规则,如 lastName (而不是lastname)。

驼峰命名法

骆驼式命名法就是当变量名或函式名是由一个或多个单字连结在一起,而构成的唯一识别字时,第一个单词以小写字母开始;第二个单词的首字母大写或每一个单词的首字母都采用大写字母,例如:myFirstName、myLastName,这样的变量名看上去就像骆驼峰一样此起彼伏,故得名。

Javascript语句

语句标识符

JavaScript 语句通常以一个 语句标识符 为开始,并执行该语句。

语句标识符是保留关键字不能作为变量名使用。

下表列出了 JavaScript 语句标识符 (关键字) :

语句 描述
break 用于跳出循环。
catch 语句块,在 try 语句块执行出错时执行 catch 语句块。
continue 跳过循环中的一个迭代。
do … while 执行一个语句块,在条件语句为 true 时继续执行该语句块。
for 在条件语句为 true 时,可以将代码块执行指定的次数。
for … in 用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作)。
function 定义一个函数
if … else 用于基于不同的条件来执行不同的动作。
return 退出函数
switch 用于基于不同的条件来执行不同的动作。
throw 抛出(生成)错误 。
try 实现错误处理,与 catch 一同使用。
var 声明一个变量。
while 当条件语句为 true 时,执行语句块。

代码行折行

您可以在文本字符串中使用反斜杠对代码行进行换行。下面的例子会正确地显示:

1
2
document.write("你好 \
世界!");

不过,不能像这样折行:

1
2
document.write \ 
("你好世界!");

知识点:JavaScript 是脚本语言,浏览器会在读取代码时,逐行地执行脚本代码。而对于传统编程来说,会在执行前对所有代码进行编译。

Javascript数据类型

值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。

引用数据类型:对象(Object)、数组(Array)、函数(Function)。

注:Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。

Javascript拥有动态类型

JavaScript拥有动态类型,这意味着相同的变量可用作不同的类型:

1
2
3
var x;               // x 为 undefined
var x = 5; // 现在 x 为数字
var x = "John"; // 现在 x 为字符串

数组

下面的代码创建名为 cars 的数组:

1
2
3
4
var cars=new Array();
cars[0]="Saab";
cars[1]="Volvo";
cars[2]="BMW";

或者 (condensed array):

1
var cars=new Array("Saab","Volvo","BMW");

或者 (literal array):

1
var cars=["Saab","Volvo","BMW"];

对象

对象由花括号分隔。在括号内部,对象的属性以名称和值对的形式 (name : value) 来定义。属性由逗号分隔:

1
var person={firstname:"John", lastname:"Doe", id:5566};

上面例子中的对象 (person) 有三个属性:firstname、lastname 以及 id。

空格和折行无关紧要。声明可横跨多行:

1
2
3
4
5
var person={
firstname : "John",
lastname : "Doe",
id : 5566
};

对象属性有两种寻址方式:

1
2
name=person.lastname;
name=person["lastname"];

Undefined和Null

Undefined 这个值表示变量不含有值。

可以通过将变量的值设置为 null 来清空变量。

声明变量类型

用关键词”new”来声明其类型:

1
2
3
4
5
var carname=new String;
var x= new Number;
var y= new Boolean;
var cars= new Array;
var person= new Object;

JavaScript 变量均为对象。当您声明一个变量时,就创建了一个新的对象。

JavaScript对象

JavaScript 对象是拥有属性和方法的数据。(感觉和Java对象有点像)

在 JavaScript中,几乎所有的事物都是对象。

对象也是一个变量,但对象可以包含多个值(多个变量),每个值以 name:value 对呈现。

可以说 “JavaScript 对象是变量的容器”。

但是,我们通常认为 “JavaScript 对象是键值对的容器”。

键值对通常写法为 name : value (键与值以冒号分割)。

键值对在 JavaScript 对象通常称为 对象属性

JavaScript 对象是属性变量的容器。

JavaScript 对象是属性和方法的容器。

访问对象属性

可以通过两种方式访问对象属性:

1
2
name=person.lastname;
name=person["lastname"];

对象方法

对象的方法定义了一个函数,并作为对象的属性存储。

对象方法通过添加 () 调用 (作为一个函数)。

该实例访问了 person 对象的 fullName() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
<p id="demo"></p>
<script>
var person = {
firstName: "John",
lastName : "Doe",
id : 5566,
fullName : function()
{
return this.firstName + " " + this.lastName;
}
};
document.getElementById("demo").innerHTML = person.fullName();
</script>

如果你要访问 person 对象的 fullName 属性,它将作为一个定义函数的字符串返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<p id="demo1"></p>
<p id="demo2"></p>
<script>
var person = {
firstName: "John",
lastName : "Doe",
id : 5566,
fullName : function()
{
return this.firstName + " " + this.lastName;
}
};
document.getElementById("demo1").innerHTML = "不加括号输出函数表达式:" + person.fullName;
document.getElementById("demo2").innerHTML = "加括号输出函数执行结果:" + person.fullName();
</script>

访问对象方法

可以使用以下语法创建对象方法:

1
2
3
methodName : function() {
// 代码
}

可以使用以下语法访问对象方法:

1
objectName.methodName()

通常 fullName() 是作为 person 对象的一个方法, fullName 是作为一个属性。

JavaScript函数

函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。

语法

函数就是包裹在花括号中的代码块,前面使用了关键词 function:

1
2
3
4
function functionname()
{
// 执行代码
}

当调用该函数时,会执行函数内的代码。

可以在某事件发生时直接调用函数(比如当用户点击按钮时),并且可由 JavaScript 在任何位置进行调用。 JavaScript 对大小写敏感。关键词 function 必须是小写的,并且必须以与函数名称相同的大小写来调用函数。

调用带参数的函数

在调用函数时,您可以向其传递值

1
2
3
4
5
6
function myFunction(var1,var2)
{
// 代码
}

myFunction(argument1,argument2)

带返回值的函数

有时,我们会希望函数将值返回调用它的地方。

通过使用 return 语句就可以实现。

在使用 return 语句时,函数会停止执行,并返回指定的值。

1
2
3
4
5
function myFunction()
{
var x=5;
return x;
}

在您仅仅希望退出函数时 ,也可使用 return 语句。返回值是可选的:

1
2
3
4
5
6
7
8
9
function myFunction(a,b)
{
if (a>b)
{
return;
}
x=a+b
}
//如果 a 大于 b,则上面的代码将退出函数,并不会计算 a 和 b 的总和。

局部javascript变量

在 JavaScript 函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它。(该变量的作用域是局部的)。

您可以在不同的函数中使用名称相同的局部变量,因为只有声明过该变量的函数才能识别出该变量。

只要函数运行完毕,本地变量就会被删除。

全局javascript变量

在函数外声明的变量是全局变量,网页上的所有脚本和函数都能访问它。

JavaScript变量的生命周期

JavaScript 变量生命周期在它声明时初始化。

局部变量在函数执行完毕后销毁。

全局变量在页面关闭后销毁。

向未声明的JavaScript变量赋值

如果您把值赋给尚未声明的变量,该变量将被自动作为 window 的一个属性。

非严格模式下给未声明变量赋值创建的全局变量,是全局对象的可配置属性,可以删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
var var1 = 1; // 不可配置全局属性
var2 = 2; // 没有使用 var 声明,可配置全局属性

console.log(this.var1); // 1
console.log(window.var1); // 1
console.log(window.var2); // 2

delete var1; // false 无法删除
console.log(var1); //1

delete var2;
console.log(delete var2); // true
console.log(var2); // 已经删除 报错变量未定义

JavaScript作用域

作用域是可访问变量的集合。

HTML中的全局变量

在 HTML 中, 全局变量是 window 对象: 所有数据变量都属于 window 对象。

JavaScript事件

HTML 事件是发生在 HTML 元素上的事情。

当在 HTML 页面中使用 JavaScript 时, JavaScript 可以触发这些事件。

HTML事件

HTML 事件可以是浏览器行为,也可以是用户行为。

以下是 HTML 事件的实例:

HTML 页面完成加载

HTML input 字段改变时

HTML 按钮被点击

通常,当事件发生时,你可以做些事情。

在事件触发时 JavaScript 可以执行一些代码。

HTML 元素中可以添加事件属性,使用 JavaScript 代码来添加 HTML 元素。

1
2
3
4
5
6
7
8
9
10
单引号:
<some-HTML-element some-event='JavaScript 代码'>
双引号:
<some-HTML-element some-event="JavaScript 代码">
实例:
<button onclick="getElementById('demo').innerHTML=Date()">现在的时间是?</button>
<button onclick="this.innerHTML=Date()">现在的时间是?</button>

JavaScript代码通常是几行代码。比较常见的是通过事件属性来调用:
<button onclick="displayDate()">现在的时间是?</button>

常见的HTML事件

下面是一些常见的HTML事件的列表:

事件 描述
onchange HTML 元素改变
onclick 用户点击 HTML 元素
onmouseover 用户在一个HTML元素上移动鼠标
onmouseout 用户从一个HTML元素上移开鼠标
onkeydown 用户按下键盘按键
onload 浏览器已完成页面的加载

JavaScript可以做什么?

事件可以用于处理表单验证,用户输入,用户行为及浏览器动作:

页面加载时触发事件

页面关闭时触发事件

用户点击按钮执行动作

验证用户输入内容的合法性

等等 …

可以使用多种方法来执行 JavaScript 事件代码:

HTML 事件属性可以直接执行 JavaScript 代码

HTML 事件属性可以调用 JavaScript 函数

你可以为 HTML 元素指定自己的事件处理程序

你可以阻止事件的发生。

等等 …

JavaScript字符串

JavaScript 字符串用于存储和处理文本。

字符串可以存储一系列字符,如 “John Doe”。

字符串可以是插入到引号中的任何字符。你可以使用单引号或双引号:

1
2
var carname = "Volvo XC60";
var carname = 'Volvo XC60';

可以使用索引位置来访问字符串中的每个字符:

1
var character = carname[7];

字符串的索引从 0 开始,这意味着第一个字符索引值为 [0],第二个为 [1], 以此类推。

可以在字符串中使用引号,字符串中的引号不要与字符串的引号相同:

1
2
3
var answer = "It's alright";
var answer = "He is called 'Johnny'";
var answer = 'He is called "Johnny"';

也可以在字符串添加转义字符来使用引号:

1
2
var x = 'It\'s alright';
var y = "He is called \"Johnny\"";

字符串长度

可以使用内置属性 length 来计算字符串的长度:

1
2
var txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var sln = txt.length;

特殊字符

在 JavaScript 中,字符串写在单引号或双引号中。

因为这样,以下实例 JavaScript 无法解析:

1
2
"We are the so-called "Vikings" from the north."
字符串 "We are the so-called " 被截断。

如何解决以上的问题呢?可以使用反斜杠 () 来转义 “Vikings” 字符串中的双引号,如下:

1
"We are the so-called \"Vikings\" from the north."

反斜杠是一个转义字符。 转义字符将特殊字符转换为字符串字符:

转义字符 () 可以用于转义撇号,换行,引号,等其他特殊字符。

下表中列举了在字符串中可以使用转义字符转义的特殊字符:

代码 输出
' 单引号
" 双引号
\ 反斜杠
\n 换行
\r 回车
\t tab(制表符)
\b 退格符
\f 换页符

字符串可以是对象

通常, JavaScript 字符串是原始值,可以使用字符创建: var firstName = “John”

但我们也可以使用 new 关键字将字符串定义为一个对象: var firstName = new String(“John”)

1
2
3
4
5
6
7
8
9
10
var x = "John";
var y = new String("John");
typeof x // 返回 String
typeof y // 返回 Object

不要创建 String 对象。它会拖慢执行速度,并可能产生其他副作用:
var x = "John";
var y = new String("John");
(x === y) // 结果为 false,因为 x 是字符串,y 是对象
=== 为绝对相等,即数据类型与值都必须相等。

字符串属性和方法

原始值字符串,如 “John”, 没有属性和方法(因为他们不是对象)。

原始值可以使用 JavaScript 的属性和方法,因为 JavaScript 在执行方法和属性时可以把原始值当作对象。

字符串属性

属性 描述
constructor 返回创建字符串属性的函数
length 返回字符串的长度
prototype 允许您向对象添加属性和方法

字符串方法

方法 描述
charAt() 返回指定索引位置的字符
charCodeAt() 返回指定索引位置字符的 Unicode 值
concat() 连接两个或多个字符串,返回连接后的字符串
fromCharCode() 将 Unicode 转换为字符串
indexOf() 返回字符串中检索指定字符第一次出现的位置
lastIndexOf() 返回字符串中检索指定字符最后一次出现的位置
localeCompare() 用本地特定的顺序来比较两个字符串
match() 找到一个或多个正则表达式的匹配
replace() 替换与正则表达式匹配的子串
search() 检索与正则表达式相匹配的值
slice() 提取字符串的片断,并在新的字符串中返回被提取的部分
split() 把字符串分割为子字符串数组
substr() 从起始索引号提取字符串中指定数目的字符
substring() 提取字符串中两个指定的索引号之间的字符
toLocaleLowerCase() 根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射
toLocaleUpperCase() 根据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射
toLowerCase() 把字符串转换为小写
toString() 返回字符串对象值
toUpperCase() 把字符串转换为大写
trim() 移除字符串首尾空白
valueOf() 返回某个字符串对象的原始值

JavaScript运算符

算数运算符和逻辑运算符基本和C语言一样

用于字符串的+运算符

+ 运算符用于把文本值或字符串变量加起来(连接起来)。

如需把两个或多个字符串变量连接起来,请使用 + 运算符。

1
2
3
txt1="What a very ";
txt2="nice day";
txt3=txt1+txt2; //txt3="What a very nice day"

字符串和数字加法运算

两个数字相加,返回数字相加的和,如果数字与字符串相加,返回字符串,如下实例:

1
2
3
4
5
6
7
8
x=5+5;
y="5"+5;
z="Hello"+5;

x,y,z 输出结果为:
10
55
Hello5

JavaScript比较

比较和逻辑运算符用于测试 true 或者 false

比较运算符

基本也和C语言相同,但是多了===(绝对等于)和!==(不绝对等于)。

运算符 描述
=== 绝对等于(值和类型均相等)
!== 不绝对等于(值和类型有一个不相等,或两个都不相等)

逻辑运算符

同C语言

运算符 描述 例子
&& and (x < 10 && y > 1) 为 true
|| or (x==5 || y==5) 为 false
! not !(x==y) 为 true

条件运算符

JavaScript 还包含了基于某些条件对变量进行赋值的条件运算符。

这个和C语言的三目运算符用法一样。

1
2
3
variablename=(condition)?value1:value2;
例如:
voteable=(age<18)?"年龄太小":"年龄已达到";

JavaScript条件语句

在 JavaScript 中,我们可使用以下条件语句:

if语句

if…else语句

if…else if…else语句

switch语句

用法同C语言

JavaScript循环

JavaScript 支持不同类型的循环:

for - 循环代码块一定的次数

for/in - 循环遍历对象的属性

while - 当指定的条件为 true 时循环指定的代码块

do/while - 同样当指定的条件为 true 时循环指定的代码块

break 语句用于跳出循环。

continue 用于跳过循环中的一个迭代。

基本同C语言,多了个for/in循环。

JavaScript for/in 语句循环遍历对象的属性:

1
2
3
4
5
6
var person={fname:"Bill",lname:"Gates",age:56}; 

for (x in person) // x 为属性名
{
txt=txt + person[x];
}

JavaScript typeof

typeof操作符

你可以使用 typeof 操作符来检测变量的数据类型。

1
2
3
4
5
6
typeof "John"                // 返回 string
typeof 3.14 // 返回 number
typeof false // 返回 boolean
typeof [1,2,3,4] // 返回 object
typeof {name:'John', age:34} // 返回 object
在JavaScript中,数组是一种特殊的对象类型。 因此 typeof [1,2,3,4] 返回 object。

null

在 JavaScript 中 null 表示 “什么都没有”。

null是一个只有一个值的特殊类型。表示一个空对象引用。

用 typeof 检测 null 返回是object。

1
2
3
4
可以设置为 null 来清空对象:
var person = null; // 值为 null(空), 但类型为对象
可以设置为 undefined 来清空对象:
var person = undefined; // 值为 undefined, 类型为 undefined

undefined

在 JavaScript 中, undefined 是一个没有设置值的变量。

1
2
3
4
5
typeof 一个没有值的变量会返回 undefined。
var person; // 值为 undefined(空), 类型是undefined

任何变量都可以通过设置值为 undefined 来清空。 类型为 undefined.
person = undefined; // 值为 undefined, 类型是undefined

undefined和null的区别

null 和 undefined 的值相等,但类型不等:

1
2
3
4
typeof undefined             // undefined
typeof null // object
null === undefined // false
null == undefined // true

1、定义

(1)undefined:是所有没有赋值变量的默认值,自动赋值。

(2)null:主动释放一个变量引用的对象,表示一个变量不再指向任何对象地址。

2、何时使用null?

当使用完一个比较大的对象时,需要对其进行释放内存时,设置为 null。

3、null 与 undefined 的异同点是什么呢?

共同点:都是原始类型,保存在栈中变量本地。

不同点:

(1)undefined——表示变量声明过但并未赋过值。

它是所有未赋值变量默认值,例如:

1
>var a;    // a 自动被赋值为 undefined

(2)null——表示一个变量将来可能指向一个对象。

一般用于主动释放指向对象的引用,例如:

1
2
>var emps = ['ss','nn'];
>emps = null; // 释放指向数组的引用

4、延伸——垃圾回收站

它是专门释放对象内存的一个程序。

(1)在底层,后台伴随当前程序同时运行;引擎会定时自动调用垃圾回收期;

(2)总有一个对象不再被任何变量引用时,才释放。

JavaScript类型转换

Number() 转换为数字, String() 转换为字符串, Boolean() 转换为布尔值。

数据类型

在 JavaScript 中有 6 种不同的数据类型:

  • string
  • number
  • boolean
  • object
  • function
  • symbol

3 种对象类型:

  • Object
  • Date
  • Array

2 个不包含任何值的数据类型:

  • null
  • undefined

类型转换

JavaScript 变量可以转换为新变量或其他数据类型:

通过使用 JavaScript 函数

通过 JavaScript 自身自动转换

一元运算符+

Operator + 可用于将变量转换为数字:

1
2
var y = "5";      // y 是一个字符串
var x = + y; // x 是一个数字

如果变量不能转换,它仍然会是一个数字,但值为 NaN (不是一个数字):

1
2
var y = "John";   // y 是一个字符串
var x = + y; // x 是一个数字 (NaN)

自动转换类型

当 JavaScript 尝试操作一个 “错误” 的数据类型时,会自动转换为 “正确” 的数据类型。

1
2
3
4
5 + null    // 返回 5         null 转换为 0
"5" + null // 返回"5null" null 转换为 "null"
"5" + 1 // 返回 "51" 1 转换为 "1"
"5" - 1 // 返回 4 "5" 转换为 5

自动转换为字符串

当你尝试输出一个对象或一个变量时 JavaScript 会自动调用变量的 toString() 方法:

1
2
3
4
5
6
7
8
9
document.getElementById("demo").innerHTML = myVar;

myVar = {name:"Fjohn"} // toString 转换为 "[object Object]"
myVar = [1,2,3,4] // toString 转换为 "1,2,3,4"
myVar = new Date() // toString 转换为 "Fri Jul 18 2014 09:08:55 GMT+0200"
数字和布尔值也经常相互转换:
myVar = 123 // toString 转换为 "123"
myVar = true // toString 转换为 "true"
myVar = false // toString 转换为 "false"

JavaScript正则表达式

正则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE)使用单个字符串来描述、匹配一系列符合某个句法规则的字符串搜索模式。

搜索模式可用于文本搜索和文本替换。

什么是正则表达式

正则表达式是由一个字符序列形成的搜索模式。

当你在文本中搜索数据时,你可以用搜索模式来描述你要查询的内容。

正则表达式可以是一个简单的字符,或一个更复杂的模式。

正则表达式可用于所有文本搜索和文本替换的操作。

语法

/正则表达式主体/修饰符(可选)

1
2
3
4
5
6
7
实例:
var patt = /runoob/i

实例解析:
/runoob/i 是一个正则表达式。
runoob 是一个正则表达式主体 (用于检索)。
i 是一个修饰符 (搜索不区分大小写)。

使用字符串方法

在 JavaScript 中,正则表达式通常用于两个字符串方法 : search() 和 replace()。

search() 方法 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置。

replace() 方法 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

search()方法使用正则表达式

使用正则表达式搜索 “Runoob” 字符串,且不区分大小写:

1
2
var str = "Visit Runoob!"; 
var n = str.search(/Runoob/i); //输出结果为:6

search()方法使用字符串

search 方法可使用字符串作为参数。字符串参数会转换为正则表达式:

1
2
var str = "Visit Runoob!"; 
var n = str.search("Runoob"); //输出结果为:6

replace()方法使用正则表达式

使用正则表达式且不区分大小写将字符串中的 Microsoft 替换为 Runoob :

1
2
var str = document.getElementById("demo").innerHTML; 
var txt = str.replace(/microsoft/i,"Runoob");

replace()方法使用字符串

replace() 方法将接收字符串作为参数:

1
2
var str = document.getElementById("demo").innerHTML; 
var txt = str.replace("Microsoft","Runoob");

正则表达式参数可用在以上方法中 (替代字符串参数)。
正则表达式使得搜索功能更加强大(如实例中不区分大小写)。

正则表达式修饰符

修饰符 描述
i 执行对大小写不敏感的匹配。
g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m 执行多行匹配。

正则表达式模式

方括号用于查找某个范围内的字符:

表达式 描述
[abc] 查找方括号之间的任何字符。
[^abc] 查找任何不在方括号之间的字符。
[0-9] 查找任何从 0 至 9 的数字。
[a-z] 查找任何从小写 a 到小写 z 的字符。
[A-Z] 查找任何从大写 A 到大写 Z 的字符。
[A-z] 查找任何从大写 A 到小写 z 的字符。
[adgk] 查找给定集合内的任何字符。
[^adgk] 查找给定集合外的任何字符。
(red|blue|green) 查找任何指定的选项。

元字符是拥有特殊含义的字符:

元字符 描述
. 查找单个字符,除了换行和行结束符。
\w 查找数字、字母及下划线。
\W 查找非单词字符。
\d 查找数字。
\D 查找非数字字符。
\s 查找空白字符。
\S 查找非空白字符。
\b 匹配单词边界。
\B 匹配非单词边界。
\0 查找 NULL 字符。
\n 查找换行符。
\f 查找换页符。
\r 查找回车符。
\t 查找制表符。
\v 查找垂直制表符。
\xxx 查找以八进制数 xxx 规定的字符。
\xdd 查找以十六进制数 dd 规定的字符。
\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。

量词:

量词 描述
n+ 匹配任何包含至少一个 n 的字符串。例如,/a+/ 匹配 “candy” 中的 “a”,”caaaaaaandy” 中所有的 “a”。
n* 匹配任何包含零个或多个 n 的字符串。例如,/bo*/ 匹配 “A ghost booooed” 中的 “boooo”,”A bird warbled” 中的 “b”,但是不匹配 “A goat grunted”。
n? 匹配任何包含零个或一个 n 的字符串。例如,/e?le?/ 匹配 “angel” 中的 “el”,”angle” 中的 “le”。
n{X} 匹配包含 X 个 n 的序列的字符串。例如,/a{2}/ 不匹配 “candy,” 中的 “a”,但是匹配 “caandy,” 中的两个 “a”,且匹配 “caaandy.” 中的前两个 “a”。
n{X,} X 是一个正整数。前面的模式 n 连续出现至少 X 次时匹配。例如,/a{2,}/ 不匹配 “candy” 中的 “a”,但是匹配 “caandy” 和 “caaaaaaandy.” 中所有的 “a”。
n{X,Y} X 和 Y 为正整数。前面的模式 n 连续出现至少 X 次,至多 Y 次时匹配。例如,/a{1,3}/ 不匹配 “cndy”,匹配 “candy,” 中的 “a”,”caandy,” 中的两个 “a”,匹配 “caaaaaaandy” 中的前面三个 “a”。注意,当匹配 “caaaaaaandy” 时,即使原始字符串拥有更多的 “a”,匹配项也是 “aaa”。
n$ 匹配任何结尾为 n 的字符串。
^n 匹配任何开头为 n 的字符串。
?=n 匹配任何其后紧接指定字符串 n 的字符串。
?!n 匹配任何其后没有紧接指定字符串 n 的字符串。

使用RegExp对象

在 JavaScript 中,RegExp 对象是一个预定义了属性和方法的正则表达式对象。

使用test()

test() 方法是一个正则表达式方法。

test() 方法用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。

以下实例用于搜索字符串中的字符 “e”:

1
2
3
4
5
var patt = /e/;
patt.test("The best things in life are free!"); //结果为true

可以不用设置正则表达式的变量,以上两行代码可以合并为一行:
/e/.test("The best things in life are free!")

使用exec()

exec() 方法是一个正则表达式方法。

exec() 方法用于检索字符串中的正则表达式的匹配。

该函数返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。

以下实例用于搜索字符串中的字母 “e”:

1
2
/e/.exec("The best things in life are free!");
//字符串中含有 "e",所以该实例输出为:e

JavaScript错误

try 语句测试代码块的错误。

catch 语句处理错误。

throw 语句创建自定义错误。

finally 语句在 try 和 catch 语句之后,无论是否有触发异常,该语句都会执行。

JavaScript错误

当 JavaScript 引擎执行 JavaScript 代码时,会发生各种错误。

可能是语法错误,通常是程序员造成的编码错误或错别字。

可能是拼写错误或语言中缺少的功能(可能由于浏览器差异)。

可能是由于来自服务器或用户的错误输出而导致的错误。

当然,也可能是由于许多其他不可预知的因素。

JavaScript抛出 (throw) 错误

当错误发生时,当事情出问题时,JavaScript 引擎通常会停止,并生成一个错误消息。

描述这种情况的技术术语是:JavaScript 将抛出一个错误。

throw 语句允许我们创建自定义错误。

正确的技术术语是:创建或抛出异常(exception)。

如果把 throw 与 try 和 catch 一起使用,那么您能够控制程序流,并生成自定义的错误消息。

语法:

1
throw exception

异常可以是 JavaScript 字符串、数字、逻辑值或对象。

检测输入变量的值。如果值是错误的,会抛出一个异常(错误)。catch 会捕捉到这个错误,并显示一段自定义的错误消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function myFunction() {
var message, x;
message = document.getElementById("message");
message.innerHTML = "";
x = document.getElementById("demo").value;
try {
if(x == "") throw "值为空";
if(isNaN(x)) throw "不是数字";
x = Number(x);
if(x < 5) throw "太小";
if(x > 10) throw "太大";
}
catch(err) {
message.innerHTML = "错误: " + err;
}
}

JavaScript try 和 catch

try 语句允许我们定义在执行时进行错误测试的代码块。

catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块。

JavaScript 语句 trycatch 是成对出现的。

语法:

1
2
3
4
5
6
7
try {
... //异常的抛出
} catch(err) {
... //异常的捕获与处理
} finally {
... //结束处理
}

实例:

故意在 try 块的代码中写了一个错字。

catch 块会捕捉到 try 块中的错误,并执行代码来处理它。

1
2
3
4
5
6
7
8
9
10
11
12
var txt=""; 
function message()
{
try {
adddlert("Welcome guest!");
} catch(err) {
txt="本页有一个错误。\n\n";
txt+="错误描述:" + err.message + "\n\n";
txt+="点击确定继续。\n\n";
alert(txt);
}
}

finally语句

finally 语句不论之前的 try 和 catch 中是否产生异常都会执行该代码块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function myFunction() {
var message, x;
message = document.getElementById("p01");
message.innerHTML = "";
x = document.getElementById("demo").value;
try {
if(x == "") throw "值是空的";
if(isNaN(x)) throw "值不是一个数字";
x = Number(x);
if(x > 10) throw "太大";
if(x < 5) throw "太小";
}
catch(err) {
message.innerHTML = "错误: " + err + ".";
}
finally {
document.getElementById("demo").value = "";
}
}

JavaScript调试

没有调试工具是很难去编写 JavaScript 程序的。

你的代码可能包含语法错误,逻辑错误,如果没有调试工具,这些错误比较难于发现。

通常,如果 JavaScript 出现错误,是不会有提示信息,这样你就无法找到代码错误的位置。

JavaScript调试工具

浏览器启用调试工具一般是按下 F12 键,并在调试菜单中选择 “Console” 。

如果浏览器支持调试,你可以使用 console.log() 方法在调试窗口上打印 JavaScript 值:

1
2
3
4
a = 5;
b = 6;
c = a + b;
console.log(c);

设置断点

在调试窗口中,你可以设置 JavaScript 代码的断点。

在每个断点上,都会停止执行 JavaScript 代码,以便于我们检查 JavaScript 变量的值。

在检查完毕后,可以重新执行代码(如播放按钮)。

debbuuger关键字

debugger 关键字用于停止执行 JavaScript,并调用调试函数。

这个关键字与在调试工具中设置断点的效果是一样的。

如果没有调试可用,debugger 语句将无法工作。

开启 debugger ,代码在第三行前停止执行。

1
2
3
var x = 15 * 5;
debugger;
document.getElementbyId("demo").innerHTML = x;

JavaScript变量提升

JavaScript声明提升

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。

JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。

以下两个实例将获得相同的结果:

1
2
3
4
5
6
x = 5; // 变量 x 设置为 5

elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x; // 在元素中显示 x

var x; // 声明 x

要理解以上实例就需要理解 “hoisting(声明提升)”。

声明提升:函数声明和变量声明总是会被解释器悄悄地被”提升”到方法体的最顶部。

JavaScript初始化不会提升

JavaScript 只有声明的变量会提升,初始化的不会。

以下两个实例结果结果不相同:

1
2
3
4
5
var x = 5; // 初始化 x
var y = 7; // 初始化 y

elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x + " " + y; // 显示 x 和 y
1
2
3
4
5
6
var x = 5; // 初始化 x

elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x + " " + y; // 显示 x 和 y

var y = 7; // 初始化 y

实例 2 的 y 输出了 undefined,这是因为变量声明 (var y) 提升了,但是初始化(y = 7) 并不会提升,所以 y 变量是一个未定义的变量。

实例 2 类似以下代码:

1
2
3
4
5
6
7
var x = 5; // 初始化 x
var y; // 声明 y

elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x + " " + y; // 显示 x 和 y

y = 7; // 设置 y 为 7

在头部声明变量

对于大多数程序员来说并不知道 JavaScript 声明提升。

如果程序员不能很好的理解声明提升,他们写的程序就容易出现一些问题。

为了避免这些问题,通常我们在每个作用域开始前声明这些变量,这也是正常的 JavaScript 解析步骤,易于我们理解。

JavaScript 严格模式(strict mode)不允许使用未声明的变量。

JavaScript严格模式

JavaScript 严格模式(strict mode)即在严格的条件下运行。

使用”use strict”指令

“use strict” 指令在 JavaScript 1.8.5 (ECMAScript5) 中新增。

它不是一条语句,但是是一个字面量表达式,在 JavaScript 旧版本中会被忽略。

“use strict” 的目的是指定代码在严格条件下执行。

严格模式下你不能使用未声明的变量。

严格模式声明

严格模式通过在脚本或函数的头部添加 use strict; 表达式来声明。

“use strict” 指令只允许出现在脚本或函数的开头。

实例中我们可以在浏览器按下 F12 (或点击”工具>更多工具>开发者工具”) 开启调试模式,查看报错信息。

image-20210410164621726

在函数内部声明是局部作用域 (只在函数内使用严格模式):

image-20210410164702737

为什么使用严格模式:

消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;

消除代码运行的一些不安全之处,保证代码运行的安全;

提高编译器效率,增加运行速度;

为未来新版本的Javascript做好铺垫。

“严格模式”体现了Javascript更合理、更安全、更严谨的发展方向,包括IE 10在内的主流浏览器,都已经支持它,许多大项目已经开始全面拥抱它。

另一方面,同样的代码,在”严格模式”中,可能会有不一样的运行结果;一些在”正常模式”下可以运行的语句,在”严格模式”下将不能运行。掌握这些内容,有助于更细致深入地理解Javascript,让你变成一个更好的程序员。

严格模式的限制

不允许使用未声明的变量:

1
2
"use strict";
x = 3.14; // 报错 (x 未定义)

不允许删除变量或对象。

1
2
3
"use strict";
var x = 3.14;
delete x; // 报错

不允许删除函数。

1
2
3
"use strict";
function x(p1, p2) {};
delete x; // 报错

不允许变量重名:

1
2
"use strict";
function x(p1, p1) {}; // 报错

不允许使用八进制:

1
2
"use strict";
var x = 010; // 报错

不允许使用转义字符:

1
2
"use strict";
var x = \010; // 报错

不允许对只读属性赋值:

1
2
3
4
5
"use strict";
var obj = {};
Object.defineProperty(obj, "x", {value:0, writable:false});

obj.x = 3.14; // 报错

不允许对一个使用getter方法读取的属性进行赋值

1
2
3
4
"use strict";
var obj = {get x() {return 0} };

obj.x = 3.14; // 报错

不允许删除一个不允许删除的属性:

1
2
"use strict";
delete Object.prototype; // 报错

变量名不能使用 “eval” 字符串:

1
2
"use strict";
var eval = 3.14; // 报错

变量名不能使用 “arguments” 字符串:

1
2
"use strict";
var arguments = 3.14; // 报错

不允许使用以下这种语句:

1
2
"use strict";
with (Math){x = cos(2)}; // 报错

由于一些安全原因,在作用域 eval() 创建的变量不能被调用:

1
2
3
"use strict";
eval ("var x = 2");
alert (x); // 报错

禁止this关键字指向全局对象。

1
2
3
4
5
6
7
8
9
10
function f(){
return !this;
}
// 返回false,因为"this"指向全局对象,"!this"就是false

function f(){
"use strict";
return !this;
}
// 返回true,因为严格模式下,this的值为undefined,所以"!this"为true。

因此,使用构造函数时,如果忘了加new,this不再指向全局对象,而是报错。

1
2
3
4
5
function f(){
"use strict";
this.a = 1;
};
f();// 报错,this未定义

保留关键字

为了向将来Javascript的新版本过渡,严格模式新增了一些保留关键字:

implements

interface

let

package

private

protected

public

static

yield

1
2
"use strict";
var public = 1500; // 报错

JavaScript使用误区

赋值运算符应用错误

在 JavaScript 程序中如果你在 if 条件语句中使用赋值运算符的等号 (=) 将会产生一个错误结果, 正确的方法是使用比较运算符的两个等号 (==)。

比较运算符常见错误

在常规的比较中,数据类型是被忽略的,以下 if 条件语句返回 true:

1
2
3
var x = 10;
var y = "10";
if (x == y)

在严格的比较运算中,=== 为恒等计算符,同时检查表达式的值与类型,以下 if 条件语句返回 false:

1
2
3
var x = 10;
var y = "10";
if (x === y)

这种错误经常会在 switch 语句中出现,switch 语句会使用恒等计算符(===)进行比较:

以下实例会执行 alert 弹窗:

1
2
3
4
var x = 10;
switch(x) {
case 10: alert("Hello");
}

以下实例由于类型不一致不会执行 alert 弹窗:

1
2
3
4
var x = 10;
switch(x) {
case "10": alert("Hello");
}

加法与连接注意事项

加法是两个数字相加。

连接是两个字符串连接。

JavaScript 的加法和连接都使用 + 运算符。

接下来我们可以通过实例查看两个数字相加及数字与字符串连接的区别:

1
2
var x = 10 + 5;          // x 的结果为 15
var x = 10 + "5"; // x 的结果为 "105"

浮点型数据使用注意事项

JavaScript 中的所有数据都是以 64 位浮点型数据(float) 来存储。

所有的编程语言,包括 JavaScript,对浮点型数据的精确度都很难确定:

1
2
3
4
var x = 0.1;
var y = 0.2;
var z = x + y // z 的结果为 0.30000000000000004
if (z == 0.3) // 返回 false

为解决以上问题,可以用整数的乘除法来解决:

1
var z = (x * 10 + y * 10) / 10;       // z 的结果为 0.3

Undefined不是Null

在 JavaScript 中, null 用于对象, undefined 用于变量,属性和方法。

对象只有被定义才有可能为 null,否则为 undefined。

如果我们想测试对象是否存在,在对象还没定义时将会抛出一个错误。

错误的使用方式:

1
if (myObj !== null && typeof myObj !== "undefined")

正确的方式是我们需要先使用 typeof 来检测对象是否已定义:

1
if (typeof myObj !== "undefined" && myObj !== null)

JavaScript表单

JavaScript表单验证

HTML 表单验证可以通过 JavaScript 来完成。

以下实例代码用于判断表单字段(fname)值是否存在, 如果不存在,就弹出信息,阻止表单提交:

1
2
3
4
5
6
7
function validateForm() {
var x = document.forms["myForm"]["fname"].value;
if (x == null || x == "") {
alert("需要输入名字。");
return false;
}
}

以上 JavaScript 代码可以通过 HTML 代码来调用:

1
2
3
4
<form name="myForm" action="demo_form.php" onsubmit="return validateForm()" method="post">
名字: <input type="text" name="fname">
<input type="submit" value="提交">
</form>

JavaScript验证输入的数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h1>JavaScript 验证输入</h1>
<p>请输入 1 到 10 之间的数字:</p>
<input id="numb">
<button type="button" onclick="myFunction()">提交</button>
<p id="demo"></p>
<script>
function myFunction() {
var x, text;
// 获取 id="numb" 的值
x = document.getElementById("numb").value;
// 如果输入的值 x 不是数字或者小于 1 或者大于 10,则提示错误 Not a Number or less than one or greater than 10
if (isNaN(x) || x < 1 || x > 10) {
text = "输入错误";
} else {
text = "输入正确";
}
document.getElementById("demo").innerHTML = text;
}
</script>
</body>
</html>

E-mail验证

下面的函数检查输入的数据是否符合电子邮件地址的基本语法。

意思就是说,输入的数据必须包含 @ 符号和点号(.)。同时,@ 不可以是邮件地址的首字符,并且 @ 之后需有至少一个点号:

1
2
3
4
5
6
7
8
9
function validateForm(){
var x=document.forms["myForm"]["email"].value;
var atpos=x.indexOf("@");
var dotpos=x.lastIndexOf(".");
if (atpos<1 || dotpos<atpos+2 || dotpos+2>=x.length){
alert("不是一个有效的 e-mail 地址");
return false;
}
}

下面是连同 HTML 表单的完整代码:

1
2
3
4
<form name="myForm" action="demo-form.php" onsubmit="return validateForm();" method="post">
Email: <input type="text" name="email">
<input type="submit" value="提交">
</form>

HTML表单自动验证

HTML 表单验证也可以通过浏览器来自动完成。

如果表单字段 (fname) 的值为空, required 属性会阻止表单提交:

1
2
3
4
<form action="demo_form.php" method="post">
<input type="text" name="fname" required>
<input type="submit" value="提交">
</form>

数据验证

数据验证用于确保用户输入的数据是有效的。

典型的数据验证有:

必需字段是否有输入?

用户是否输入了合法的数据?

在数字字段是否输入了文本?

大多数情况下,数据验证用于确保用户正确输入数据。

数据验证可以使用不同方法来定义,并通过多种方式来调用。

服务端数据验证是在数据提交到服务器上后再验证。

客户端数据验证是在数据发送到服务器前,在浏览器上完成验证。

HTML约束验证

HTML5 新增了 HTML 表单的验证方式:约束验证(constraint validation)。

约束验证是表单被提交时浏览器用来实现验证的一种算法。

HTML 约束验证基于:

HTML 输入属性

CSS 伪类选择器

DOM 属性和方法

约束验证THML输入属性

属性 描述
disabled 规定输入的元素不可用
max 规定输入元素的最大值
min 规定输入元素的最小值
pattern 规定输入元素值的模式
required 规定输入元素字段是必需的
type 规定输入元素的类型

约束验证CSS伪类选择器

选择器 描述
:disabled 选取属性为 “disabled” 属性的 input 元素
:invalid 选取无效的 input 元素
:optional 选择没有”optional”属性的 input 元素
:required 选择有”required”属性的 input 元素
:valid 选取有效值的 input 元素

JavaScript验证API

约束验证DOM方法

Property Description
checkValidity() 如果 input 元素中的数据是合法的返回 true,否则返回 false。
setCustomValidity() 设置 input 元素的 validationMessage 属性,用于自定义错误提示信息的方法。使用 setCustomValidity 设置了自定义提示后,validity.customError 就会变成true,则 checkValidity 总是会返回false。如果要重新判断需要取消自定义提示,方式如下:setCustomValidity('') setCustomValidity(null) setCustomValidity(undefined)

以下实例如果输入信息不合法,则返回错误信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
<input id="id1" type="number" min="100" max="300" required>
<button onclick="myFunction()">验证</button>

<p id="demo"></p>

<script>
function myFunction() {
var inpObj = document.getElementById("id1");
if (inpObj.checkValidity() == false) {
document.getElementById("demo").innerHTML = inpObj.validationMessage;
}
}
</script>

setCustomValidity 的用法:

1
2
3
4
5
6
7
8
9
10
11
12
var inpObj = document.getElementById("id1");
inpObj.setCustomValidity(''); // 使用前先取消自定义,否则下次点击checkValidity总返false
if (inpObj.checkValidity() == false) {
if(inpObj.value==""){
inpObj.setCustomValidity("不能为空!");
}else if(inpObj.value<100 || inpObj.value>300){
inpObj.setCustomValidity("请重新输入数值(100~300之间)!");
}
document.getElementById("demo").innerHTML = inpObj.validationMessage;
} else {
document.getElementById("demo").innerHTML = "输入正确";
}

约束验证DOM属性

属性 描述
validity 布尔属性值,返回 input 输入值是否合法
validationMessage 浏览器错误提示信息
willValidate 指定 input 是否需要验证

Validity属性

input 元素的 validity 属性包含一系列关于 validity 数据属性:

属性 描述
customError 设置为 true, 如果设置了自定义的 validity 信息。
patternMismatch 设置为 true, 如果元素的值不匹配它的模式属性。
rangeOverflow 设置为 true, 如果元素的值大于设置的最大值。
rangeUnderflow 设置为 true, 如果元素的值小于它的最小值。
stepMismatch 设置为 true, 如果元素的值不是按照规定的 step 属性设置。
tooLong 设置为 true, 如果元素的值超过了 maxLength 属性设置的长度。
typeMismatch 设置为 true, 如果元素的值不是预期相匹配的类型。
valueMissing 设置为 true,如果元素 (required 属性) 没有值。
valid 设置为 true,如果元素的值是合法的。

JavaScript this

this关键字

面向对象语言中 this 表示当前对象的一个引用。

但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。

  • 在方法中,this 表示该方法所属的对象。
  • 如果单独使用,this 表示全局对象。
  • 在函数中,this 表示全局对象。
  • 在函数中,在严格模式下,this 是未定义的(undefined)。
  • 在事件中,this 表示接收事件的元素。
  • 类似 call() 和 apply() 方法可以将 this 引用到任何对象。
1
2
3
4
5
6
7
8
var person = {
firstName: "John",
lastName : "Doe",
id : 5566,
fullName : function() {
return this.firstName + " " + this.lastName;
}
};

方法中的this

在对象方法中, this 指向调用它所在方法的对象。

在上面一个实例中,this 表示 person 对象。

fullName 方法所属的对象就是 person。

单独使用this

单独使用 this,则它指向全局(Global)对象。

在浏览器中,window 就是该全局对象为 [object Window]:

1
var x = this;

严格模式下,如果单独使用,this 也是指向全局(Global)对象。

1
2
"use strict";
var x = this;

函数中使用this(默认)

在函数中,函数的所属者默认绑定到 this 上。

在浏览器中,window 就是该全局对象为 [object Window]:

1
2
3
function myFunction() {
return this; //实例中,this 表示 myFunction 函数的所有者:[object Window]
}

函数中使用this(严格模式)

严格模式下函数是没有绑定到 this 上,这时候 this 是 undefined

1
2
3
4
5
"use strict";
function myFunction() {
return this;
}
/* 函数中,默认情况下,this 指向全局对象。严格模式下,this 为 undefined,因为严格模式下不允许默认绑定:undefined */

事件中的this

在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素:

1
<button onclick="this.style.display='none'">点我后我就消失了</button>

对象方法中绑定

下面实例中,this 是 person 对象,person 对象是函数的所有者:

1
2
3
4
5
6
7
8
var person = {
firstName : "John",
lastName : "Doe",
id : 5566,
myFunction : function() {
return this;
}
};

显式函数绑定

在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。

在下面实例中,当我们使用 person2 作为参数来调用 person1.fullName 方法时, this 将指向 person2, 即便它是 person1 的方法:

1
2
3
4
5
6
7
8
9
10
var person1 = {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var person2 = {
firstName:"John",
lastName: "Doe",
}
person1.fullName.call(person2); // 返回 "John Doe"

JavaScript let和const

ECMAScript 2015(ECMAScript 6)
ES2015(ES6) 新增加了两个重要的 JavaScript 关键字: let 和 const。

let 声明的变量只在 let 命令所在的代码块内有效。

const 声明一个只读的常量,一旦声明,常量的值就不能改变。

在 ES6 之前,JavaScript 只有两种作用域: 全局变量 与 函数内的局部变量。

JavaScript块级作用域(Block Scope)

使用 var 关键字声明的变量不具备块级作用域的特性,它在 {} 外依然能被访问到。

1
2
3
4
{ 
var x = 2;
}
// 这里可以使用 x 变量

在 ES6 之前,是没有块级作用域的概念的。

ES6 可以使用 let 关键字来实现块级作用域。

let 声明的变量只在 let 命令所在的代码块 {} 内有效,在 {} 之外不能访问。

1
2
3
4
{ 
let x = 2;
}
// 这里不能使用 x 变量

重新定义变量

使用 var 关键字重新声明变量可能会带来问题。

在块中重新声明变量也会重新声明块外的变量:

1
2
3
4
5
6
7
var x = 10;
// 这里输出 x 为 10
{
var x = 2;
// 这里输出 x 为 2
}
// 这里输出 x 为 2

let 关键字就可以解决这个问题,因为它只在 let 命令所在的代码块 {} 内有效。

1
2
3
4
5
6
7
var x = 10;
// 这里输出 x 为 10
{
let x = 2;
// 这里输出 x 为 2
}
// 这里输出 x 为 10

循环作用域

使用 var 关键字:

1
2
3
4
5
var i=5;
for (var i = 0; i < 10 ; i++){
// 一些代码...
}
// 这里输出i为10

使用 let 关键字:

1
2
3
4
5
let i = 5;
for (let i = 0; i < 10; i++) {
// 一些代码...
}
// 这里输出 i 为 5

在第一个实例中,使用了 var 关键字,它声明的变量是全局的,包括循环体内与循环体外。

在第二个实例中,使用 let 关键字, 它声明的变量作用域只在循环体内,循环体外的变量不受影响。

局部变量

在函数体内使用 varlet 关键字声明的变量有点类似。

它们的作用域都是 局部的:

1
2
3
4
5
6
7
8
9
// 使用 var
function myFunction() {
var carName = "Volvo"; // 局部作用域
}

// 使用 let
function myFunction() {
let carName = "Volvo"; // 局部作用域
}

全局变量

在函数体外或代码块外使用 varlet 关键字声明的变量也有点类似。

它们的作用域都是 全局的:

1
2
3
4
5
// 使用 var
var x = 2; // 全局作用域

// 使用 let
let x = 2; // 全局作用域

THML代码中使用全局变量

在 JavaScript 中, 全局作用域是针对 JavaScript 环境。

在 HTML 中, 全局作用域是针对 window 对象。

使用 var 关键字声明的全局作用域变量属于 window 对象:

1
2
var carName = "Volvo";
// 可以使用 window.carName 访问变量

使用 let 关键字声明的全局作用域变量不属于 window 对象:

1
2
let carName = "Volvo";
// 不能使用 window.carName 访问变量

重置变量

使用 var 关键字声明的变量在任何地方都可以修改:

1
2
3
4
5
6
7
var x = 2;

// x 为 2

var x = 3;

// 现在 x 为 3

在相同的作用域或块级作用域中,不能使用 let 关键字来重置 var 关键字声明的变量:

1
2
3
4
5
6
7
var x = 2;       // 合法
let x = 3; // 不合法

{
var x = 4; // 合法
let x = 5 // 不合法
}

在相同的作用域或块级作用域中,不能使用 let 关键字来重置 let 关键字声明的变量:

1
2
3
4
5
6
7
let x = 2;       // 合法
let x = 3; // 不合法

{
let x = 4; // 合法
let x = 5; // 不合法
}

在相同的作用域或块级作用域中,不能使用 var 关键字来重置 let 关键字声明的变量:

1
2
3
4
5
6
7
let x = 2;       // 合法
var x = 3; // 不合法

{
let x = 4; // 合法
var x = 5; // 不合法
}

let 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:

1
2
3
4
5
6
7
8
9
let x = 2;       // 合法

{
let x = 3; // 合法
}

{
let x = 4; // 合法
}

变量提升

JavaScript 中,var 关键字定义的变量可以在使用后声明,也就是变量可以先使用再声明。

let 关键字定义的变量则不可以在使用后声明,也就是变量需要先声明再使用。

const关键字

const 用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改:

1
2
3
const PI = 3.141592653589793;
PI = 3.14; // 报错
PI = PI + 10; // 报错

const定义常量与使用let 定义的变量相似:

  • 二者都是块级作用域
  • 都不能和它所在作用域内的其他变量或函数拥有相同的名称

两者还有以下两点区别:

  • const声明的常量必须初始化,而let声明的变量不用
  • const 定义常量的值不能通过再赋值修改,也不能再次声明。而 let 定义的变量值可以修改。
1
2
3
4
5
6
7
var x = 10;
// 这里输出 x 为 10
{
const x = 2;
// 这里输出 x 为 2
}
// 这里输出 x 为 10

const 声明的常量必须初始化:

1
2
3
4
5
6
// 错误写法
const PI;
PI = 3.14159265359;

// 正确写法
const PI = 3.14159265359;

并非真正的常量

const 的本质: const 定义的变量并非常量,并非不可变,它定义了一个常量引用一个值。使用 const 定义的对象或者数组,其实是可变的。下面的代码并不会报错:

1
2
3
4
5
6
7
8
// 创建常量对象
const car = {type:"Fiat", model:"500", color:"white"};

// 修改属性:
car.color = "red";

// 添加属性
car.owner = "Johnson";

但是我们不能对常量对象重新赋值:

1
2
const car = {type:"Fiat", model:"500", color:"white"};
car = {type:"Volvo", model:"EX60", color:"red"}; // 错误

以下实例修改常量数组:

1
2
3
4
5
6
7
8
// 创建常量数组
const cars = ["Saab", "Volvo", "BMW"];

// 修改元素
cars[0] = "Toyota";

// 添加元素
cars.push("Audi");

但是我们不能对常量数组重新赋值:

1
2
const cars = ["Saab", "Volvo", "BMW"];
cars = ["Toyota", "Volvo", "Audi"]; // 错误

重置变量

使用 var 关键字声明的变量在任何地方都可以修改:

1
2
3
var x = 2;    //  合法
var x = 3; // 合法
x = 4; // 合法

在相同的作用域或块级作用域中,不能使用 const 关键字来重置 varlet关键字声明的变量:

1
2
3
4
5
6
var x = 2;         // 合法
const x = 2; // 不合法
{
let x = 2; // 合法
const x = 2; // 不合法
}

在相同的作用域或块级作用域中,不能使用 const 关键字来重置 const 关键字声明的变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
const x = 2;       // 合法
const x = 3; // 不合法
x = 3; // 不合法
var x = 3; // 不合法
let x = 3; // 不合法

{
const x = 2; // 合法
const x = 3; // 不合法
x = 3; // 不合法
var x = 3; // 不合法
let x = 3; // 不合法
}

const 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:

1
2
3
4
5
6
7
8
9
const x = 2;       // 合法

{
const x = 3; // 合法
}

{
const x = 4; // 合法
}

变量提升

const 关键字定义的变量则不可以在使用后声明,也就是变量需要先声明再使用。

1
2
carName = "Volvo";    // 在这里不可以使用 carName 变量
const carName = "Volvo";

JavaScript JSON

JSON 是用于存储和传输数据的格式。

JSON 通常用于服务端向网页传递数据 。

什么是JSON?

JSON 英文全称 JavaScript Object Notation

JSON 是一种轻量级的数据交换格式。

JSON是独立的语言。

JSON 易于理解。

JSON 使用 JavaScript 语法,但是 JSON 格式仅仅是一个文本。
文本可以被任何编程语言读取及作为数据格式传递。

JSON实例

以下 JSON 语法定义了 sites 对象: 3 条网站信息(对象)的数组:

1
2
3
4
5
{"sites":[
{"name":"Runoob", "url":"www.runoob.com"},
{"name":"Google", "url":"www.google.com"},
{"name":"Taobao", "url":"www.taobao.com"}
]}

JSON格式化后为JavaScript对象

JSON 格式在语法上与创建 JavaScript 对象代码是相同的。

由于它们很相似,所以 JavaScript 程序可以很容易的将 JSON 数据转换为 JavaScript 对象。

JSON语法规则

数据为 键/值 对。

数据由逗号分隔。

大括号保存对象

方括号保存数组

JSON数据 - 一个名称对应一个值

JSON 数据格式为 键/值 对,就像 JavaScript 对象属性。

键/值对包括字段名称(在双引号中),后面一个冒号,然后是值:

1
"nickname":"Tianze"

JSON对象

JSON 数组保存在中括号内。

就像在 JavaScript 中, 数组可以包含对象:

1
{"name":"Runoob", "url":"www.runoob.com"}

JSON数组

JSON 数组保存在中括号内。

就像在 JavaScript 中, 数组可以包含对象:

1
2
3
4
5
"sites":[
{"name":"Runoob", "url":"www.runoob.com"},
{"name":"Google", "url":"www.google.com"},
{"name":"Taobao", "url":"www.taobao.com"}
]

在以上实例中,对象 “sites” 是一个数组,包含了三个对象。

每个对象为站点的信息(网站名和网站地址)。

JSON字符串转换为JavaScript对象

通常我们从服务器中读取 JSON 数据,并在网页中显示数据。

简单起见,我们网页中直接设置 JSON 字符串:

首先,创建 JavaScript 字符串,字符串为 JSON 格式的数据:

1
2
3
4
var text = '{ "sites" : [' +
'{ "name":"Runoob" , "url":"www.runoob.com" },' +
'{ "name":"Google" , "url":"www.google.com" },' +
'{ "name":"Taobao" , "url":"www.taobao.com" } ]}';

然后,使用 JavaScript 内置函数 JSON.parse() 将字符串转换为 JavaScript 对象:

1
var obj = JSON.parse(text);

最后,在你的页面中使用新的 JavaScript 对象:

1
2
3
4
5
6
7
var text = '{ "sites" : [' +
'{ "name":"Runoob" , "url":"www.runoob.com" },' +
'{ "name":"Google" , "url":"www.google.com" },' +
'{ "name":"Taobao" , "url":"www.taobao.com" } ]}';

obj = JSON.parse(text);
document.getElementById("demo").innerHTML = obj.sites[1].name + " " + obj.sites[1].url;

相关函数

函数 描述
JSON.parse() 用于将一个 JSON 字符串转换为 JavaScript 对象。
JSON.stringify() 用于将 JavaScript 值转换为 JSON 字符串。

JavaScript void

JavaScript:void(0) 含义

我们经常会使用到 javascript:void(0) 这样的代码,那么在 JavaScript 中 javascript:void(0) 代表的是什么意思呢?

javascript:void(0) 中最关键的是 void 关键字, void 是 JavaScript 中非常重要的关键字,该操作符指定要计算一个表达式但是不返回值。

语法格式如下:

1
2
void func()
javascript:void func()

或者

1
2
void(func())
javascript:void(func())

下面的代码创建了一个超级链接,当用户点击以后不会发生任何事。

1
<a href="javascript:void(0)">单击此处什么也不会发生</a>

当用户链接时,void(0) 计算为 0,但 Javascript 上没有任何效果。

以下实例中,在用户点击链接后显示警告信息:

1
2
<p>点击以下链接查看结果:</p>
<a href="javascript:void(alert('Warning!!!'))">点我!</a>

以下实例中参数 a 将返回 undefined :

1
2
3
4
5
function getValue(){
var a,b,c;
a = void ( b = 5, c = 7 );
document.write('a = ' + a + ' b = ' + b +' c = ' + c );
}

href=”#”与href=”javascript:void(0)”的区别

# 包含了一个位置信息,默认的锚是#top 也就是网页的上端。

而javascript:void(0), 仅仅表示一个死链接。

在页面很长的时候会使用 # 来定位页面的具体位置,格式为:# + id

如果你要定义一个死链接请使用 javascript:void(0) 。

1
2
3
4
5
6
<a href="javascript:void(0);">点我没有反应的!</a>
<a href="#pos">点我定位到指定位置!</a>
<br>
...
<br>
<p id="pos">尾部定位点</p>

JavaScript 异步编程

异步的概念

异步(Asynchronous, async)是与同步(Synchronous, sync)相对的概念。

在我们学习的传统单线程编程中,程序的运行是同步的(同步不意味着所有步骤同时运行,而是指步骤在一个控制流序列中按顺序执行)。而异步的概念则是不保证同步的概念,也就是说,一个异步过程的执行将不再与原有的序列有顺序关系。

简单来理解就是:同步按你的代码顺序执行,异步不按照代码顺序执行,异步的执行效率更高。

以上是关于异步的概念的解释,接下来我们通俗地解释一下异步:异步就是从主线程发射一个子线程来完成任务。

img

什么时候用异步编程

在前端编程中(甚至后端有时也是这样),我们在处理一些简短、快速的操作时,例如计算 1 + 1 的结果,往往在主线程中就可以完成。主线程作为一个线程,不能够同时接受多方面的请求。所以,当一个事件没有结束时,界面将无法处理其他请求。

现在有一个按钮,如果我们设置它的 onclick 事件为一个死循环,那么当这个按钮按下,整个网页将失去响应。

为了避免这种情况的发生,我们常常用子线程来完成一些可能消耗时间足够长以至于被用户察觉的事情,比如读取一个大文件或者发出一个网络请求。因为子线程独立于主线程,所以即使出现阻塞也不会影响主线程的运行。但是子线程有一个局限:一旦发射了以后就会与主线程失去同步,我们无法确定它的结束,如果结束之后需要处理一些事情,比如处理来自服务器的信息,我们是无法将它合并到主线程中去的。

为了解决这个问题,JavaScript 中的异步操作函数往往通过回调函数来实现异步任务的结果处理。

回调函数

回调函数就是一个函数,它是在我们启动一个异步任务的时候就告诉它:等你完成了这个任务之后要干什么。这样一来主线程几乎不用关心异步任务的状态了,他自己会善始善终。

1
2
3
4
function print() {
document.getElementById("demo").innerHTML="RUNOOB!";
}
setTimeout(print, 3000);

这段程序中的 setTimeout 就是一个消耗时间较长(3 秒)的过程,它的第一个参数是个回调函数,第二个参数是毫秒数,这个函数执行之后会产生一个子线程,子线程会等待 3 秒,然后执行回调函数 “print”,在命令行输出 “Time out”。

当然,JavaScript 语法十分友好,我们不必单独定义一个函数 print ,我们常常将上面的程序写成:

1
2
3
setTimeout(function () {
document.getElementById("demo").innerHTML="RUNOOB!";
}, 3000);

既然 setTimeout 会在子线程中等待 3 秒,在 setTimeout 函数执行之后主线程并没有停止,所以:

1
2
3
4
5
setTimeout(function () {
document.getElementById("demo1").innerHTML="RUNOOB-1!";
}, 3000);
document.getElementById("demo2").innerHTML="RUNOOB-2!";
console.log("2");

这段程序的执行结果是:

1
2
RUNOOB-1!
RUNOOB-2!

异步AJAX

除了 setTimeout 函数以外,异步回调广泛应用于 AJAX 编程。有关于 AJAX 详细请参见:https://www.runoob.com/ajax/ajax-tutorial.html

XMLHttpRequest 常常用于请求来自远程服务器上的 XML 或 JSON 数据。一个标准的 XMLHttpRequest 对象往往包含多个回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var xhr = new XMLHttpRequest();

xhr.onload = function () {
// 输出接收到的文字数据
document.getElementById("demo").innerHTML=xhr.responseText;
}

xhr.onerror = function () {
document.getElementById("demo").innerHTML="请求出错";
}

// 发送异步 GET 请求
xhr.open("GET", "https://www.runoob.com/try/ajax/ajax_info.txt", true);
xhr.send();

XMLHttpRequest 的 onload 和 onerror 属性都是函数,分别在它请求成功和请求失败时被调用。如果你使用完整的 jQuery 库,也可以更加优雅的使用异步 AJAX:

1
2
3
$.get("https://www.runoob.com/try/ajax/demo_test.php",function(data,status){
alert("数据: " + data + "\n状态: " + status);
});

JavaScript Promise

Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务。

由于 Promise 是 ES6 新增加的,所以一些旧的浏览器并不支持,苹果的 Safari 10 和 Windows 的 Edge 14 版本以上浏览器才开始支持 ES6 特性。

构造Promise

现在我们新建一个 Promise 对象:

1
2
3
new Promise(function (resolve, reject) {
// 要做的事情...
});

通过新建一个 Promise 对象好像并没有看出它怎样 “更加优雅地书写复杂的异步任务”。我们之前遇到的异步任务都是一次异步,如果需要多次调用异步函数呢?例如,如果我想分三次输出字符串,第一次间隔 1 秒,第二次间隔 4 秒,第三次间隔 3 秒:

1
2
3
4
5
6
7
8
9
setTimeout(function () {
console.log("First");
setTimeout(function () {
console.log("Second");
setTimeout(function () {
console.log("Third");
}, 3000);
}, 4000);
}, 1000);

这段程序实现了这个功能,但是它是用 “函数瀑布” 来实现的。可想而知,在一个复杂的程序当中,用 “函数瀑布” 实现的程序无论是维护还是异常处理都是一件特别繁琐的事情,而且会让缩进格式变得非常冗赘。

现在我们用 Promise 来实现同样的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("First");
resolve();
}, 1000);
}).then(function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("Second");
resolve();
}, 4000);
});
}).then(function () {
setTimeout(function () {
console.log("Third");
}, 3000);
});

这段代码较长,所以还不需要完全理解它,我想引起注意的是 Promise 将嵌套格式的代码变成了顺序格式的代码。

Promise的使用

下面我们通过剖析这段 Promise “计时器” 代码来讲述 Promise 的使用:

Promise 构造函数只有一个参数,是一个函数,这个函数在构造之后会直接被异步运行,所以我们称之为起始函数。起始函数包含两个参数 resolve 和 reject。

当 Promise 被构造时,起始函数会被异步执行:

1
2
3
new Promise(function (resolve, reject) {
console.log("Run");
});

这段程序会直接输出 Run

resolve 和 reject 都是函数,其中调用 resolve 代表一切正常,reject 是出现异常时所调用的:

1
2
3
4
5
6
7
8
9
10
11
12
new Promise(function (resolve, reject) {
var a = 0;
var b = 1;
if (b == 0) reject("Diveide zero");
else resolve(a / b);
}).then(function (value) {
console.log("a / b = " + value);
}).catch(function (err) {
console.log(err);
}).finally(function () {
console.log("End");
});

这段程序执行结果是:

1
2
a / b = 0
End

Promise 类有 .then() .catch() 和 .finally() 三个方法,这三个方法的参数都是一个函数,.then() 可以将参数中的函数添加到当前 Promise 的正常执行序列,.catch() 则是设定 Promise 的异常处理序列,.finally() 是在 Promise 执行的最后一定会执行的序列。 .then() 传入的函数会按顺序依次执行,有任何异常都会直接跳到 catch 序列:

1
2
3
4
5
6
7
8
9
10
11
12
new Promise(function (resolve, reject) {
console.log(1111);
resolve(2222);
}).then(function (value) {
console.log(value);
return 3333;
}).then(function (value) {
console.log(value);
throw "An error";
}).catch(function (err) {
console.log(err);
});

执行结果:

1
2
3
4
1111
2222
3333
An error

resolve() 中可以放置一个参数用于向下一个 then 传递一个值,then 中的函数也可以返回一个值传递给 then。但是,如果 then 中返回的是一个 Promise 对象,那么下一个 then 将相当于对这个返回的 Promise 进行操作,这一点从刚才的计时器的例子中可以看出来。

reject() 参数中一般会传递一个异常给之后的 catch 函数用于处理异常。

但是请注意以下两点:

  • resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列;
  • resolve 和 reject 并不能够使起始函数停止运行,别忘了 return。

Promise函数

上述的 “计时器” 程序看上去比函数瀑布还要长,所以我们可以将它的核心部分写成一个 Promise 函数:

1
2
3
4
5
6
7
8
function print(delay, message) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(message);
resolve();
}, delay);
});
}

然后我们就可以放心大胆的实现程序功能了:

1
2
3
4
5
print(1000, "First").then(function () {
return print(4000, "Second");
}).then(function () {
print(3000, "Third");
});

这种返回值为一个 Promise 对象的函数称作 Promise 函数,它常常用于开发基于异步操作的库。

异步函数

异步函数(async function)是 ECMAScript 2017 (ECMA-262) 标准的规范,几乎被所有浏览器所支持,除了 Internet Explorer。

在 Promise 中我们编写过一个 Promise 函数:

1
2
3
4
5
6
7
8
function print(delay, message) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(message);
resolve();
}, delay);
});
}

然后用不同的时间间隔输出了三行文本:

1
2
3
4
5
print(1000, "First").then(function () {
return print(4000, "Second");
}).then(function () {
print(3000, "Third");
});

我们可以将这段代码变得更好看:

1
2
3
4
5
6
async function asyncFunc() {
await print(1000, "First");
await print(4000, "Second");
await print(3000, "Third");
}
asyncFunc();

异步函数 async function 中可以使用 await 指令,await 指令后必须跟着一个 Promise,异步函数会在这个 Promise 运行中暂停,直到其运行结束再继续运行。

异步函数实际上原理与 Promise 原生 API 的机制是一模一样的,只不过更便于程序员阅读。

处理异常的机制将用 try-catch 块实现:

1
2
3
4
5
6
7
8
9
10
11
async function asyncFunc() {
try {
await new Promise(function (resolve, reject) {
throw "Some error"; // 或者 reject("Some error")
});
} catch (err) {
console.log(err);
// 会输出 Some error
}
}
asyncFunc();

如果 Promise 有一个正常的返回值,await 语句也会返回它:

1
2
3
4
5
6
7
8
9
async function asyncFunc() {
let value = await new Promise(
function (resolve, reject) {
resolve("Return value");
}
);
console.log(value);
}
asyncFunc();

程序会输出:

1
Return value

参考文章

https://www.runoob.com/js/js-tutorial.html

https://blog.csdn.net/Richard1997/article/details/87350174

https://www.huaweicloud.com/articles/f98d0e64fb1b281f60b7209c750528fc.html

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2020-2021 Blog of Tianze

请我喝杯咖啡吧~

支付宝
微信