一、精通 JavaScript

JavaScript 是一种面向对象的跨平台脚本语言,通常用于使网页具有交互式,包括动画、弹出菜单、可单击按钮等。该语言还有其他版本,例如 Node.js,它是一个服务器端版本,它为您提供了向该网站添加更多功能的工具,而不仅仅是下载一些文件,比如在两台或多台计算机之间提供实时协作的文件。在主机环境(如 web 浏览器)中,您可以使用 JavaScript 连接到环境对象,以便对它们进行更多控制。

在 JavaScript 中,您会发现一个包含日期、数组、数学等对象的标准库,以及该语言的一组核心元素,如控制结构、运算符、语句等。您可以通过添加额外的对象来扩展 JavaScript,以实现多种目的,例如:

  • 客户端 JS–这将提供有助于控制浏览器及其 DOM 或文档对象模型的对象。例如,一些客户端扩展可用于向应用提供向 HTML 表单添加元素的能力,而其他扩展将提供对用户事件的响应—表单上的输入、鼠标单击、页面导航等。
  • 服务器端 JS–这将提供与服务器上运行的 JavaScript 相关的某些对象。例如,其中一些扩展将有助于应用和数据库之间的通信,确保信息在应用调用之间移动时的连续性,并在服务器上执行文件操作。

这一切意味着,当你在浏览器中使用 JavaScript 时,它可以改变网页的外观。

虽然 JavaScript 在某些方面与 Java 相似,但它们之间存在一些基本的区别。就语言而言,JavaScript 与 Java 非常相似,但它没有 Java 所具有的强类型检查和静态类型。对于表达式,它也遵循许多相同的语法,对于控制流,它遵循命名约定和基本构造,这是它的名称从 LiveScript 更改为 JavaScript 的主要原因。

Java 有一个使用声明构建类的编译时系统,JavaScript 有一个运行时系统,它基于较少的数据类型来表示字符串值、布尔值和数值。JavaScript 对象模型是基于原型而不是基于类的,这提供了一个动态继承系统——每个对象可以继承和不能继承的内容是不同的。JavaScript 还支持在声明方面有特殊要求或需要特殊要求的函数,函数也可以是对象属性,可以作为松散类型的方法执行。

与 Java 不同,JavaScript 是一种自由形式的语言,这意味着不需要声明所有的方法、变量和类,不需要担心这些方法是受保护的、私有的还是公共的,也不需要实现接口。除此之外,不需要显式地键入函数的参数、变量和返回类型。

Java 是一种基于类的编程语言,主要用于类型安全和快速执行。类型安全的一个例子是,Java 整数不能转换为对象引用,也不能破坏任何字节码来访问私有内存。在这种基于类的模型中,程序只包含类和相关方法。使 Java 比 JavaScript 复杂得多的一点是,它的继承规则和强类型需要紧密耦合的对象层次结构。

相比之下,JavaScript 是几种动态类型语言的后代,包括 dBase 和 HyperTalk。这些语言提供了更广泛的编程工具受众,因为它们在语法上非常简单,包含专门的内置功能,并且对创建对象的要求很少。

JavaScript 被标准化,以提供一种基于 ECMA international 的 JS 的国际编程语言。这是由欧洲计算机制造商协会制定的,旨在确保信息和通信系统标准化。这个新版本的 JavaScript 被称为 ECMAScript,它在包括标准支持的每个应用中都以相同的方式工作。任何公司都可以使用这种开放语言来开发自己的 JavaScript 实现,并且可以在名为 ECMA-262 的规范中找到完整的文档。

本标准也已获得国际标准化组织(也称为 ISO-16262)的全面批准。本规范没有详细介绍 DOM,DOM 由 W3C(万维网联盟)和/或 WHATWG(也称为 Web 超文本应用技术工作组)标准化。DOM 用于指定如何在脚本中公开 HTML 对象。

开始使用 JavaScript 真正需要的是最新的现代 web 浏览器。我们将使用 Firefox,并将介绍一些只能在 Mozilla Firefox 中找到的 JS 功能。Firefox 中特别包括两个工具,它们对于掌握 JavaScript 非常有用:

网络控制台

Web 控制台非常有用,因为它可以向您显示已加载网页的当前信息,并且在中有一个用于在网页上执行 JS 表达式的命令行。

打开控制台很容易。在 Windows 或 Linux 系统上,只需按 CTRL+SHIFT+I,在 Mac 上,按 CMD+OPTION+K,然后在 Firefox 中选择工具菜单。单击开发者菜单,然后单击 Web 控制台。您将在 Firefox 窗口的底部看到。沿着控制台的底部,您将看到输入 JS 命令的命令行,输出显示在上面的面板中。

控制台的工作方式是最后输入的表达式是返回的表达式。为了简单起见,假设只要有任何东西输入控制台,它就会被 consle.log 和 eval 包围,如下所示:

函数 greetMe(您的姓名){

警报(“你好”+你的名字);

}

控制台日志(eval('4+4'));

草稿行

当您想要执行一行 JavaScript 代码时,控制台非常适合,但对于几行代码,控制台就不太适合了。您也无法在控制台中保存任何代码示例,因此最好的选择是 Scratchpad。

要打开 Scratchpad,请按照上面的步骤操作,当您单击开发者菜单时,单击 Scratchpad。将打开一个新窗口,实际上是一个编辑器;在这种情况下,您可以编写代码并在 Firefox 浏览器中执行,还可以在磁盘上保存和加载脚本。

我们的第一个节目

最简单的入门方法是编写 Hello World 脚本;打开草稿行并输入以下代码:

(功能(){

“严格使用”;

/代码的开头/

函数 greetMe(您的姓名){

警报(“你好”+你的名字);

}

greetMe(“世界”);

/代码结束/

})();

突出显示代码并按 CTRL+R,您将在浏览器中看到所有代码。

在这段代码中,我们将了解 JavaScript 语言的语法和特性,为您提供编写更复杂的应用所需的工具。现在,记住在每个新代码的开头添加(function(){“use strict”;在末尾添加})(),这一点很重要。你很快就会明白这些意味着什么;现在,假设他们做了以下工作:

  • 显著提高性能
  • 防止在 JavaScript 中出现容易使初学者绊倒的愚蠢解释
  • 阻止在控制台内执行的代码片段合并,即在一次执行中创建的代码片段用于另一次执行。

本节的其余部分将介绍 JavaScript 中的基本语法、数据类型、变量声明和文字。

JavaScript 中的大部分语法来自 Java,但在某种程度上也受到 Python、Perl 和 Awk 编程语言的影响。

首先要了解的是 JavaScript 语言是区分大小写的,它使用了一个名为 Unicode 的字符集。例如,类似 First 的单词可以用作变量的名称,如下所示:

JavaScript 区分大小写并使用 Unicode 字符集。例如,单词“First”(在德语中是“early”的意思)可以用作变量名。

var First=“foobar”;

但如果你称之为“第一”,那就不一样了。因为一个以大写字母开头,另一个以小写字母开头,所以它们是两个不同的变量。

语句是 JavaScript 指令的列表,每个指令之间用分号分隔。例外情况是,一行只有一条语句。这不需要分号,但如果一行中有多个语句,则需要分号。然而,最佳实践规定,即使不需要,语句后面也要有一个。这将降低代码中出现错误的风险。

JavaScript 源文本始终从左到右读取,然后转换为输入元素序列,其中包括:

  • 代币
  • 控制字符
  • 线路终端
  • 评论
  • 空白

制表符、空格和换行符位于空格标题下。

保留关键字

与所有编程语言一样,JavaScript 有一组保留关键字;这些不能以任何其他方式使用,特别是不能用于命名标识符或变量。保留字为:

  • 摘要
  • 布尔值
  • 打破
  • 字节
  • 案例
  • 接住
  • 烧焦
  • 常数
  • 持续
  • 调试器
  • 违约
  • 其他的
  • 枚举
  • 出口
  • 延伸
  • 错误的
  • 最终的
  • 最后
  • 浮动
  • 对于
  • 作用
  • 后藤
  • 如果
  • 工具
  • 进口
  • 在里面
  • 运算符
  • int
  • 界面
  • 允许
  • 长的
  • 出生地的
  • 刚出现的
  • 无效的
  • 包裹
  • 私有的
  • 受保护的
  • 平民的
  • 回来
  • 短的
  • 超级的
  • 转换
  • 同步的
  • 投掷
  • 转瞬即逝的
  • 符合事实的
  • 尝试
  • 类型
  • 变量
  • 无效的
  • 不稳定的
  • 虽然
  • 具有

评论

代码中的注释纯粹是为了告诉您和其他查看代码的人正在发生什么。如果你做得对,它们将不会被阅读,也不会被视为代码的一部分。使用的语法与大多数编程语言中的语法基本相同:

//这是一条单行注释,由两个正斜杠表示

/*这要长得多,,

*多行注释,必须以正斜杠和星号开头,以星号和反斜杠结尾

*/

/但是,/注释不可能嵌套/SyntaxError/

在 JavaScript 语言中,有三种不同的声明类型:

  • var-用于声明变量,并带有将变量初始化为值的选项
  • let–用于声明具有块范围的局部值,以及将变量初始化为值的选项
  • const–用于声明具有块范围的只读命名常量。

变量用于提供带有名称的值。变量名也称为标识符,必须遵循特定的命名约定:

  • 名称必须以字母、下划线(\)或$(美元符号)开头。
  • 任何后续字符也可以是 0 到 9 之间的数字。
  • 字符将包括两个大写字母)!到 Z)和小写(a 到 Z)字母。

一些例子包括:

  • 数字钟
  • temp88
  • $name
  • _ 名字

变量声明

变量可以通过两种方式声明:

  • 使用 var 关键字。例如,var x=25。根据执行的上下文,可以使用此语法声明局部变量和全局变量。
  • 使用 let 或 const 关键字。例如,y=9。可以使用此语法声明块范围的局部变量。

也可以为变量赋值,如 x=25。此格式将创建一个未声明的全局变量,并且还将生成一个严格的警告。未声明的变量可能会导致奇怪的行为,因此不鼓励您使用它们。

变量评估

如果您使用 let 或 var 语句声明变量,但不为其赋值,则该变量的值将为“undefined”。如果尝试访问未声明的变量,将看到抛出异常,ReferenceError 异常:

var a;

console.log('a 的值为'+a);//a 的值未定义

console.log('b 的值为'+b);//b 的值未定义

var b;

//在阅读变量提升部分之前,这可能会让您有点困惑

console.log('c 的值是'+c);//未捕获的引用错误:未定义 c

设 x;

console.log('x 的值为'+x);//x 的值未定义

console.log('y 的值为'+y);//未捕获的引用错误:未定义 y

让 y;

“未定义”也可用于查看是否已将值分配给变量。在下一个示例中,我们没有为名为 input 的变量赋值,因此 if 语句将为 true。

var 输入;

如果(输入===未定义){

doThis();

}否则{

doThat();

}

在布尔上下文中使用未定义的值时,它将表现为 false。例如,下一个代码将执行一个名为 myFunction 的函数,因为名为 myArray 的元素未定义:

var myArray=[];

如果(!myArray[0])myFunction();

但是,在数字上下文中使用时,该值将转换为 NaN:

var a;

a+2;//计算结果为 NaN

计算空变量时,在数值上下文中,空值将表现为 0,在布尔上下文中,为 false。请参见下一个示例:

var n=null;

console.log(n*34);//将 0 记录到控制台

可变范围

当在函数外部声明变量时,我们称之为全局变量。这是因为该变量可用于当前代码文档中的所有代码。如果它是在函数内声明的,则称为局部变量,因为它只能在函数内使用。

在 ECMAScript 2015 之前,JavaScript 没有 block 语句作用域。相反,在块内声明的变量将是该块所包含的特定函数的局部变量。在下面的示例中,日志将是 6,因为 x 的作用域是全局上下文,或者,如果此代码是函数的一部分,则作用域将是函数;范围将不是 if 语句块:

如果(真){

var x=6;

}

控制台日志(x);//x 是 6

如果使用 ECMAScript 2015 引入的 let 声明,情况会有所不同:

如果(真){

设 y=6;

}

控制台日志(y);//引用错误:未定义 y

可变起重

JavaScript 变量还有一些不寻常的地方;稍后声明的变量可以在不引发异常的情况下被引用。这被称为变量提升,它意味着,在某种程度上,变量被提升或提升到语句或函数的顶部。但是,已挂起的变量将返回“未定义”值,即使在引用变量后声明并初始化该变量,它仍将返回“未定义”值:

/**

*第一个例子

*/

console.log(x==未定义);//符合事实的

var x=4;

/**

*第二个例子

*/

//返回一个未定义的值

var myvar=‘我的价值’;

(功能(){

console.log(myvar);//未定义

var myvar=‘本地值’;

})();

这两个示例的解释完全相同:

/**

*第一个例子

*/

var x;

console.log(x==未定义);//符合事实的

x=4;

/**

*第二个例子

*/

var myvar=‘我的价值’;

(功能(){

var-myvar;

console.log(myvar);//未定义

myvar=‘本地值’;

})();

提升要求函数中的所有 var 语句必须放在函数顶部附近,以提高代码的清晰度。

在 ECMAScript 2015 中,let(const)声明不会将变量提升到顶部。如果在声明变量之前引用块内的任何变量,则会出现引用错误。这是因为从块开始到变量声明已被处理时,变量处于所谓的“暂时死区”内:

console.log(x);//引用错误

设 x=4;

功能提升

对于一个函数,它将只是被提升的声明,而不是实际的函数表达式。

/函数声明/

foo();//“酒吧”

函数 foo(){

console.log('bar');

}

/函数表达式/

baz();//TypeError:baz 不是一个函数

var baz=函数(){

console.log('bar2');

};

全局变量

全局变量是全局对象的属性。在网页中,全局对象是一个窗口,因此您可以使用语法 window.variable 设置全局变量并访问它们。因此,当您在一个框架或窗口中声明全局变量时,只需指定框架或窗口的名称即可从另一个框架或窗口访问它。例如,如果您在一个文档中声明了一个名为 phoneNumber 的变量,则可以使用 parent.phoneNumber 从 iFrame 引用它。

使用 const 关键字,可以创建只读的命名常量。语法与变量标识符的语法相同–开头必须有字母、美元符号或下划线,还可以包含数字、字母和下划线。例如:

常数 PI=3.14;

常量不能通过赋值来更改值,并且在脚本运行时不能再次声明它们。它们还必须初始化为值。就范围而言,规则与 let 块变量相同;如果省略 const 关键字,标识符将被视为变量。

不能在同一作用域内的变量或函数已有名称的情况下声明常量。例如:

//这将引起一个错误

函数 f(){};

常数 f=8;

//这也会如此

函数 f(){

常数 g=8;

var g;

//语句

}

也就是说,分配给常数的任何对象的属性都没有被归类为受保护的,这意味着下面的示例语句将不会出现任何错误:

const MY_OBJECT={'key':'value'};

MY_OBJECT.key='otherValue';

数组内容也不受保护,因此此语句也将正确执行:

常量 MY_ 数组=['HTML','CSS'];

MY_ARRAY.push('JAVASCRIPT');

log(我的 _ 数组)//日志['HTML'、'CSS'、'JAVASCRIPT'];

最后,ECMAScript 标准定义了七种数据类型,其中六种是基本数据类型:

  1. 布尔值–计算为真或假
  2. null–表示空值的特殊类型的关键字。由于 JS 是一种区分大小写的语言,null,null,null 都有不同的含义
  3. undefined–具有未定义值的属性(顶级)
  4. 数字–浮点(即 2.1987)或整数(即 79)
  5. String–表示文本值的字符序列(即“Hello”)
  6. Symbol–ECMAScript 2015 是一种全新的数据类型,具有不可变的唯一实例
  7. 对象

虽然数据类型不多,但它们都可以用于在应用中执行一些非常有用的功能。功能和对象是语言的另外两个基本要素;一个对象可以被认为是一个容器(命名),用于保存值,而函数是由应用执行的过程。

因为 JavaScript 语言是动态类型化的,所以在声明变量时不需要指定变量的数据类型。执行脚本时,数据类型会自动转换为所需的数据类型。例如,变量可以定义为:

var=79;

稍后,可以为该变量指定一个字符串值,如:

回答=‘谢谢你所有的礼物……’;

JS 是动态类型的这一事实意味着不会有任何由赋值引起的错误消息。

如果表达式使用+运算符同时具有字符串值和数值,则数字将转换为字符串。例如:

x='答案是'+79/'答案是 79“

y=79+“是答案”/“79 是答案”

如果使用了其他运算符,则不会转换数值:

'47' - 7 // 40

'47' + 7 // "477"

将字符串转换为数字

如果您的值表示一个数字,并且它已作为字符串存储在内存中,则有两种转换方法:

第一个,parseInt()将只返回整数,因此它实际上不适合小数或浮点数。除此之外,最佳实践表明,当您使用 parseInt()时,应包含基数参数以指定使用的数字系统。

从字符串中获取数字的另一种方法是使用一元=+(+)运算符:

'2.2' + '2.2' // '2.22.2'

(+'2.2') + (+'2.2') // 4.4  

//注:我加入括号只是为了说明问题;这不是一项要求

文字用于表示固定值,而不是变量。脚本中提供了这些固定值,涵盖了以下文字类型:

数组文字是零个或多个表达式的列表。每个表达式表示一个数组元素,它被括在一组[]方括号中。使用数组文字创建数组时,必须使用元素的指定值对其进行初始化,并且数组的长度将是指定参数的数量。

下一个示例将创建一个名为 teas 的数组;它有三个要素;因此,它有三个长度:

var teas=[大吉岭、格雷伯爵、乌龙];

注意-数组文字是对象初始值设定项和数组对象的类型,这两种类型将在后面讨论。

如果使用文字在顶级脚本中创建数组,则每次计算包含该数组文字的表达式时,都会解释该数组。除此之外,每次调用函数时都会创建函数中使用的文本。

不需要指定数组文本中的每个元素。如果行中放置了两个逗号,则在创建数组时,所有未指定的元素都未定义。看看这个创建大型 cat 阵列的示例:

var 大猫=[老虎,狮子];

这有两个元素有值,一个没有值(大猫[0]是 Tiger,大猫[1]是 undefined,大猫[2]是 Lion)。

如果在元素列表的末尾添加了一个尾随逗号,它将被忽略。在下一个示例中,我们有一个长度为 3 的数组。myList[3]不存在,其他逗号表示其他元素。

注意-如果您使用的是较旧的浏览器,请删除后面的逗号,因为它可能会导致错误。

var myList=['work','home',];

在下一个示例中,数组的长度为四,缺少两个–myList[0]和 myList[2]:

var myList=[,'work','home'];

在本例中,我们还有一个长度为 4 且缺少两个的数组–myList[1]和 myList[3]。最后一个逗号是唯一被忽略的逗号:

var myList=['work','home',];

如果要理解该语言,了解其他逗号的行为非常重要。但是,当涉及到自己编写代码时,您应该明确声明缺少的元素是未定义的,以便您的代码更易于维护和阅读。

布尔文字有两个值–true 和 false。重要的是,不要将基本布尔真值和假值与布尔对象真值和假值混淆。对象是围绕基元数据类型的包装器。

整数可以用四种方式表示:

  • Decimal–base 10–Decimal integer 文字是一个没有前导零(0)的数字序列
  • 十六进制–以 16 为基数–十六进制整数文本的前导为 0x 或 0x,它们可能包含字母 a 到 f、a 到 f 以及数字 0 到 9。该字符的大小写无关;它不会更改该值。
  • 八进制–以 8 为基数–八进制整数文字具有前导 0、0o 或 o0,并且只能具有数字 0 到 7。
  • Binary–base 2–二进制整数文字的前导为 0b 或 0b,并且只能包含数字 o 和 1。

举几个例子:

0、123 和-456(十进制,以 10 为基数)

0160002 和-0o66(八进制,基数 8)

0x1123、0x00111 和-0xF1A7(十六进制、“十六进制”或基数 16)

0b11、0b0011 和-0b11(二进制,基数 2)

浮点文本由以下部分组成:

  • 以+或-开头的十进制整数。
  • 小数点
  • 分数,是另一个十进制数
  • 指数

指数是一个 E 或一个 E,后面有一个整数,前面可以是+或-。浮点文字必须至少包含一位数字和一个 e、e 或小数点。

语法如下所示:

[(+|-)][位数][位数][(E | E)[(+|-)]位数]

例如:

3.1415926

-.123456789

-3.1E+12

.1e-23

对象文字是包含零对或多对的列表,每对都具有属性名称和关联的对象值。该列表包含在一组{}大括号中。决不能在语句的开头使用对象文字,否则会发生以下两种情况之一:抛出错误或事情无法正常工作,因为开头的大括号将被视为块的开头。

下一个示例是对象文字。对象中名为 car 的第一个元素定义名为 myCar 的属性;然后将其分配给一个名为 Jupiter 的新字符串。然后为第二个元素 getCarProperty 分配调用名为 carTypes 的函数的结果。第三个元素是一个特殊属性,它使用一个已经存在的变量 sales。

var 销售额=‘欧宝’;

函数类型(名称){

如果(名称==‘铃木’){

返回名称;

}其他{

return“对不起,我们没有任何“+name+”;

}

}

var car={myCar:'Jupiter',getCar:carTypes('Suzuki'),special:sales};

console.log(car.myCar);//朱庇特

console.log(car.getCar);//铃木

控制台日志(汽车专用);//欧宝

除此之外,字符串或数字文字还可以用作属性名称,或者可以将一个对象嵌套在另一个对象中。以下是几个例子:

var car={manyCars:{a:'宝马',b:'奔驰'},7:'捷豹'};

控制台日志(car.manyCars.b);//梅赛德斯

控制台日志(car[7]);//美洲虎

对象属性的名称可以是任何类型的字符串,包括空字符串。但是,如果名称不是有效的数字或标识符,则必须包含在一组引号中。无效标识符也不能作为点属性访问,但可以使用[](类似于数组的符号)访问和设置。

var unsualPropertyNames={

'':这是一个空字符串',

'!': '砰

}

console.log(unusualPropertyNames.'');//语法错误:意外的字符串

console.log(异常属性名称['');//空字符串

console.log(unsualPropertyNames.!);//SyntaxError:意外的令牌!

console.log(unusaualPropertyNames['!']);//猛敲

在 ECMAScript2015 中,对象文字支持在构建时设置原型,即 foo 的缩写。i、 例如,foo 赋值、进行超级调用、定义方法、使用表达式计算属性名称。这些工作使对象文字更接近类声明,并允许基于对象的设计从相同的便利性中获得一些好处:

var obj={

//\uu 原版 __

_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,

//handler:handler 的缩写

handler,

//方法

toString(){

//超级呼叫

返回'd'+super.toString();

},

//计算的(动态)属性名称

['prop_'+(()=>79)()]:79

};

另请注意:

var foo={b:'beta',3:'three'};

控制台日志(foo.b);//贝塔

console.log(foo[3]);//三个

//控制台日志(foo.3);//参数列表后的 SyntaxError:缺少)

//console.log(foo[b]);//ReferenceError:尚未定义 b

console.log(foo['b']);//贝塔

console.log(foo['3']);//三

正则表达式文字是斜杠之间的模式,如本例所示:

var re=/ab+c/;

稍后我们将更详细地讨论这些问题。

字符串文字由一组引号内的零个或多个字符组成–双引号(“”)或单引号(“”)。分隔必须使用相同类型的引号,例如,一对双引号或一对单引号。不能使用两种不同的类型。请看以下示例:

“福”

“酒吧”

'5678'

'一行\n 另一行'

“鲍勃的狗”

可以对字符串文本的值调用任何字符串对象方法;JavaScript 将自动将文本转换为临时字符串对象,然后调用该方法并丢弃临时对象。String.length 属性也可以与字符串文字一起使用:

console.log(“鲍勃的狗”。长度)

//这将打印字符串中的所有符号,包括空格。

//该值将为 9。

ECMAScript 2015 中也提供了模板文本。这些字符串包含在一组回号(``)而不是引号之间。模板字符串使字符串结构更易于阅读。您还可以选择添加标记,以便自定义字符串构造、避免注入攻击或使用字符串内容构造更高级别的结构。

//文本字符串的基本创建

在 JavaScript 中,'\n'是换行符

//多行字符串

`模板字符串可以跨越多行

在 JavaScript 中,但其中的行是双行还是单行

引用不能`

//字符串插值

变量名称='Bob',时间='today';

你好${name},你好${time}

//构造 HTTP 请求前缀以解释

替换和构造

职位`http://foo.org/bar?a=${a}&b=${b}

内容类型:application/json

X-Credentials:${Credentials}

{“foo”:${foo},

“bar”:${bar}}`(myOnReadyStateChangeHandler);

除非出于特定原因需要使用字符串对象,否则请尝试使用字符串文字。

除了标准字符外,还有一些特殊字符可以在字符串中使用,例如:

'一行\n 另一行'

以下是可以在 JS 字符串中使用的特殊字符及其含义:

字义

\“单引号还是撇号

\“双引号

\反斜杠

\oNull 字节

\B 背景空间

\饲料

\新线

\回程

\tTab

\u{XXXXX}点转义码(Unicode),即.\u{2F804}表示与标准 Unicode 转义码\uD87E\uDC04 相同

\由十六进制数字(XXXX)指定的 UxxxUnicode 字符。i、 e.\uAooA9 是版权符号的 Unicode 等价物

\垂直选项卡

\XXXLatin-1 编码字符,由 0 到 3777 之间最多三个八进制数字指定。i、 e.\251 是版权符号的八进制等价物

\xXXLatin-1 由从 00 到 FF 的十六进制数字指定的编码字符。i、 e.\xA9 是版权符号的十六进制等价物

如果要在字符串中使用引号,请始终在其前面使用反斜杠;我们称之为“转义”引号。例如:

var quote=“她读了赫尔曼·梅尔维尔的《白鲸》。”;

控制台日志(报价);

结果是

她读了赫尔曼·梅尔维尔的《白鲸》

如果要在字符串中使用文字反斜杠,则必须转义反斜杠字符。例如,要分配文件路径为 c:\temp 的字符串,可以执行以下操作:

var home='c:\temp';

换行符之前使用反斜杠也可以转义。反斜杠和换行符都将从字符串值中删除:

var str=字符串\

故障\

多次以上\

台词。”

console.log(str);//字符串被分解为多行。

JavaScript 不支持 PHP 中使用的带有引号分隔符的大文本块中的“herdoc”语法,您可以通过在每行末尾包含换行符转义和转义换行符来接近它,如下所示:

变奏诗=

草莓是红色的\

蓝莓是蓝色的。\n\

蜂蜜是甜的\

福也是。”

在 ECMAScript 2015 中,我们看到了另一个文本的引入,即模板文本。这些提供了额外功能的加载,包括多行字符串:

变奏诗=

“草莓是红色的,

蓝莓是蓝色的。

蜂蜜是甜的,

福也是。”

JavaScript 提供了对一组简洁语句的支持,特别是那些位于控制流语句标题下的语句。这些语句对于向应用添加交互性非常有用。在我们继续讨论不同类型的语句之前,有两件事需要知道:

  • 语句必须用分号分隔
  • JS 表达式也被归类为语句——稍后将详细介绍表达式。

block 语句是一种非常基本的语句,用于将语句分组在一起。S bock 语句必须用一组花括号{}括起来。例如:

{

报表 1;

报表 2;

.

.

.

声明;;

}

我们通常将块语句与控制流语句一起使用,即 if、while、for:

而(x<15){

x++;

}

这里的 block 语句是{x++;)

重要提示

在 ECMAScript 2015 之前,JavaScript 不支持块语句。相反,在块中引入的任何变量的作用域都是包含它的脚本或函数;设置该变量的效果将在块外继续。简单地说,这意味着 block 语句不会定义范围。独立块的结果与 Java 或 C 产生的结果不同。例如:

var x=2;

{

var x=4;

}

console.log(x);//产出 4

块内的 var x 语句与语句前的 var x 块在同一范围内,这就是为什么输出为 4。在 Java 或 C 中,输出应该是 2。

在 ECMAScript 2015 中,我们看到了常量,并让变量声明成为块范围。

条件语句是仅当给定条件的计算结果为 true 时才会执行的命令集。JavaScript 中支持两个条件语句–if…else 和 switch。

如果……否则

if 语句用于执行逻辑条件计算为 true 的语句。else 子句是可选的,在逻辑条件的计算结果为 false 时使用。if 语句如下所示:

如果(条件){

报表 1;

}否则{

报表 2;

}

条件可以是任何将计算 true 或 false 的语句或表达式。如果条件为 true,则执行语句 _1。如果为 false,则执行语句 _2。这两个语句都可以是任何语句,其中包括嵌套的 if 语句。

如果满足以下条件,还可以使用 else 合成语句,并按顺序测试几个条件:

如果(条件 _1){

报表 1;

}否则如果(条件 2){

报表 2;

}否则如果(条件){

声明;;

}否则{

最后的声明;

}

如果有多个条件,则将执行的唯一条件是第一个计算结果为 true 的条件。如果要执行所有语句,必须将它们分组在{{…}内的块语句中。通常情况下,块语句是最佳做法,特别是如果您有嵌套的 if 语句:

如果(条件){

如果条件为真,则语句 1 执行;

如果条件为真,则语句 2 执行;

}否则{

如果条件为假,则语句 3 执行;

如果条件为 false,则语句 4 执行;

}

尽量不要在条件表达式中使用简单语句;赋值和相等可能会混淆,尤其是当代码只是粗略地看一下时。不应做的一个例子是:

如果(x=y){

/这里有陈述/

}

如果条件表达式中需要赋值,最好在其周围添加另一组括号,如下所示:

如果((x=y)){

/这里有陈述/

}

这些是将评估为 false 的值;你经常会看到这些被称为“虚假价值观”:

  • 0
  • 空字符串(“”)
  • 错误的
  • 无效的
  • 未定义

当它被传递到条件语句时,任何其他值都将计算为 true,其中包括对象。

重要的是,不要被原始布尔值 true 和 false 以及布尔对象 true 和 false 混淆。例如:

var b=新布尔值(false);

if(b)//条件将计算为 true

if(b==true)//条件将计算为 false

在下一个示例中,我们有一个名为 checkData 的函数;假设文本对象中的字符数为 4,则其计算结果为 true。否则,将显示警报并返回 false:

函数 checkData(){

if(document.form1.fourChar.value.length==4){

返回 true;

}其他{

警报('准确输入四个字符。'+

document.form1.fourChar.value+'无效。“);

返回 false;

}

}

我们使用 switch 语句来计算表达式;然后尝试将表达式的值与大小写标签匹配。如果存在匹配项,则执行关联语句。switch 语句如下所示:

开关(表达式){

箱子标签 _1:

声明 1

[中断;]

箱子标签 2:

报表 2

[中断;]

...

默认值:

声明

[中断;]

}

首先,程序将查找具有与表达式值匹配的 case 标签的 case 子句;然后将控制转移到子句,并执行关联的语句。如果没有找到匹配的案例标签,程序将继续查找默认子句(这是可选的)。如果它找到一个默认子句,控制权将转移给它,并执行相关语句。如果程序找不到默认子句,执行将移到紧跟在开关后面的语句。按照惯例,最后一条是默认条款,但情况并非如此。

break 语句(也是可选的)可与每个 case 子句关联,用于确保在找到并执行匹配语句后立即断开开关;执行移动到紧跟在开关后面的语句。如果没有 break 子句,则继续执行开关内的下一条语句。

在下一个示例中,如果 vegtype 的计算结果为“Peppers”,则该值与名为“Peppers”的大小写匹配,并执行相关语句。如果程序遇到 break 子句,switch 语句将终止,控制将移到 switch 后面的第一个语句。如果省略了 break 子句,那么也将执行与案例“土豆”相关的语句:

开关(蔬菜型){

“豌豆”一案:

原木(豌豆每磅 0.49 美元);

中断;

“豆子”一案:

原木('豆子每磅 0.22 美元');

中断;

“辣椒”一案:

原木(“辣椒每磅 0.58 美元”);

中断;

“土豆”一案:

原木(“土豆每磅 3.50 美元”);

中断;

芹菜:

原木(芹菜每磅 0.58 美元);

中断;

“洋葱”一案:

原木(芹菜和洋葱每磅 3.85 美元);

中断;

默认值:

log('抱歉,我们没有任何'+vegtype+');

}

log(“您还想要别的吗?”);

throw 语句用于抛出异常,而 try…catch 语句用于处理异常。

异常类型

几乎可以抛出任何对象,但并非所有抛出的对象都是相等的。数字和字符串通常作为错误抛出,但更有效的方法是使用专门创建的异常类型。

引发异常时,必须指定具有引发值的表达式:

抛出表情;

可以抛出任何表达式;它不必是特定的类型。下一个示例显示了引发的多个异常,每个异常的类型不同:

抛出“错误 3”;//字符串类型

投掷 42 枚;//数字类型

抛假;//布尔类型

抛出{toString:function(){返回“我是一个对象!”;};

抛出异常时可以指定对象,然后可以在 catch 块中引用对象属性:

//创建对象类型 UserException

函数 UserException(消息){

this.message=消息;

this.name='UserException';

}

//当异常用作字符串时,应将其转换为字符串

//(即通过错误控制台)

UserException.prototype.toString=函数(){

返回 this.name+':“'+this.message+'”;

}

//创建对象类型的一个实例,然后将其抛出

抛出新的 UserException(值太高);

此语句用于标记要尝试的特定语句块。在引发异常的情况下,应至少指定一个响应。try…catch 语句将捕获任何抛出的异常。

try…catch 语句有一个 try 块,其中至少有一条语句,还有一个 catch 块,其中的语句指定 try 块中抛出异常时的操作。一般来说,想法是让试块成功;如果不是,则应将控制传递给 catch 块。如果 try 块中的任何语句或从 try 块调用的任何函数引发异常,则控件将立即移动到 catch 块。如果没有抛出异常,则将错过 catch 块。一旦 try 和 catch 块被执行,finally 块将被执行——这发生在 try…catch 块之后的语句被执行之前。

在下一个示例中,调用一个函数从数组中检索月份名称;检索到的名称基于传递给该函数的值。如果没有与该值相对应的月份号(1 到 12),则会抛出一个异常,其中包含一个“InvalidMonthNo”值,catch 块内的所有语句都会将名为 monthName 的变量设置为未知。

函数 getMonthName(mo){

mo=mo-1;//更改数组索引的月份号(1=1 月,12=12 月)

变量月份=['1 月'、'2 月'、'3 月'、'4 月'、'5 月'、'6 月'、'7 月',

“八月”、“九月”、“十月”、“十一月”、“十二月”];

如果(月[mo]){

返回月[mo];

}其他{

扔“残疾的蒙特诺”//我们在这里使用 throw 关键字

}

}

try{//这些是正在尝试的语句

monthName=getMonthName(我的月份);//该函数可能引发异常

}

捕获(e){

monthName=‘未知’;

日志错误(e);//异常对象被传递给错误处理程序->您的函数

}

Catch 块可用于处理 try 块可能生成的任何或所有异常:

catch(catchID){

声明

}

catch 块(上述语法中的 catchID)中指定了一个标识符,该标识符将保存 throw 语句指定的值。此标识符可用于获取有关抛出异常的一些信息。标识符是在首次输入 catch 块时由 JavaScript 创建的,它的持续时间与 catch 块的持续时间相同。一旦 catch 块的执行完成,标识符将变得不可用。

在下一个示例中,将引发异常,发生这种情况时,控件将直接移动到 catch 块:

试一试{

抛出“myException”;//将生成一个异常

}

捕获(e){

//这些语句将处理任何异常

日志错误(e);//异常对象被传递给错误处理程序

}

在 finally 块中,是在 try 和 catch 块完成后但在 catch…try 之后的语句之前执行的语句。无论是否抛出异常,都将执行此块;如果存在异常,无论是否由 catch 块处理异常,都将执行 finally 语句。

finally 块通常用于在抛出异常时使脚本优雅地失败。例如,您的脚本可能占用了特定的资源,您需要将其释放。

在本例中,打开一个文件,执行需要使用该文件的语句。如果在文件打开时引发异常,则在脚本失败之前,finally 块将关闭该文件,从而释放这些资源:

openMyFile();

试一试{

写入文件(数据)//可能会抛出错误

}第(e)款{

手柄错误(e);//我们会处理我们遇到的任何错误

}最后{

closeMyFile();//资源必须始终关闭

}

如果 finally 块返回值,则无论 try 块和 catch 块中的返回语句是什么,它都将成为 entre try…catch…finally 过程的值:

函数 f(){

试一试{

控制台日志(0);

抛"假",;

}捕获(e){

控制台日志(1);

返回 true;//return 语句被挂起

//直到最后一个块完成

控制台日志(2);//无法到达

}最后{

控制台日志(3);

返回 false;//上一个“返回”被覆盖

控制台日志(4);//无法到达

}

//“return false”现在被执行

控制台日志(5);//无法到达

}

f();//控制台 0、1、3;返回 false

当 finally 块覆盖返回值时,它也适用于 catch 块抛出或重试的任何异常:

函数 f(){

试一试{

抛"假",;

}捕获(e){

log('捕获内部“虚构的”');

抛出 e;//throw 语句将暂停,直到

//最后一个块已完成

}最后{

返回 false;//上一个“抛出”被覆盖

}

//“return false”现在执行

}

试一试{

f();

}捕获(e){

//无法到达此位置,因为已插入

//catch 块已被覆盖

//通过 finally 块中的返回

console.log('捕获外部“虚构的”');

}

//输出

//抓住内心的“虚构”

可以在另一个语句中嵌套一个或多个 try…catch 语句。如果内部嵌套语句没有 catch 块,则必须有 finally 块,并在外部 try…catch 块中检查匹配项。

根据错误类型的不同,您可以通过使用两个对象属性——“message”和“name”来获得更精确的错误消息。后者将提供一个通用的错误类,如“error”或“DOMException”,而前者将提供比将对象转换为字符串更清晰的消息。

例如,如果要在抛出赢得的异常时使用这些属性,如果 catch 块没有将系统异常与异常分开,则可以使用错误构造函数,如本例所示:

函数 doSomethingthatscauseSerrors(){

如果(我们的程序 hasmademistake()){

抛出(新错误(“消息”);

}其他{

doSomethingthrowsAjavaScriptError();

}

}

....

试一试{

dosomething 导致错误();

}捕获(e){

console.log(e.name);//日志“错误”

console.log(e.message);//记录“消息”或 JavaScript 错误消息)

}

ECMAScript 2015 还引入了对 Promise 对象的支持。这些允许您控制异步和延迟操作的流程。

承诺必须处于以下状态之一:

  • 未决–初始状态;目标没有实现,也没有被拒绝
  • 已完成–操作已成功
  • 已拒绝–操作已失败
  • 已解决–承诺已履行或拒绝,但未处于待定状态

下面的示例向您介绍了如何使用 XMLHTTPRequest 将图像加载到网站的承诺:

函数 imgLoad(url){

返回新承诺(功能(解决、拒绝){

var request=new XMLHttpRequest();

打开('GET',url);

request.responseType='blob';

request.onload=函数(){

如果(request.status==200){

解决(请求、响应);

}其他{

拒绝(错误('图像未成功加载;错误代码:'

+request.statusText));

}

};

request.onerror=函数(){

拒绝(错误(“出现网络错误”);

};

request.send();

});

}

循环为程序员提供了一种简单的方法来反复做一些事情。你有没有玩过这样一个游戏:一个人告诉另一个人在 X 方向走这么多步,然后在 Y 方向走这么多步?你可以把循环想象成电脑版的游戏。例如,您可以通过以下循环表达向西走六步的想法:

var 阶跃;

对于(步骤=0;步骤<6;步骤++){

//运行 6 次,值为步骤 0 到 5。

console.log(“向西走一步”);

}

有很多类型的循环,但它们都做几乎相同的事情——重复给定的操作指定次数(甚至零次)。循环结构提供了一种不同的方法来确定循环从何处开始和结束,某些情况下,一种类型的循环比另一种类型的循环更适合。

JavaScript 为循环提供以下语句:

for 循环将重复,直到给定条件的计算结果为 false。for 循环语句的语法如下所示:

对于([initialExpression];[condition];[incrementExpression])

声明

在执行 for 循环时,会发生以下情况:

  • 如果有一个初始化表达式(在我们的语法中是 initialExpression),则首先执行它。这通常用于初始化至少一个循环计数器,但如果愿意,可以使用更复杂的表达式。初始化表达式还可以声明一个或多个变量。
  • 接下来,将执行条件表达式。如果该值为 true,则执行相关语句,但如果该值为 false,则终止 for 循环。如果不使用条件表达式,则假定条件为真。
  • 将执行该语句。如果要执行多条语句,必须使用 block 语句–{…}对语句进行分组。
  • 如果使用了 updateExpression(incrementExpression),则将执行它。
  • 控件返回到条件表达式。

在下面的示例中,我们有一个带有 for 语句的函数;for 语句在滚动的列表中计算所选选项的数量。我们使用元素来启用多重选择。名为 i 的变量被声明并初始化为零。函数将检查变量是否小于部分包含的选项数;执行随后的 if 语句,并在每个循环过程后将 i 递增 1:

选择几种类型的音乐,点击下面的按钮: 重金属 岩石 灵魂 大波段 民间 乡村与西部 var header=document.querySelector('header'); var=document.querySelector(“”); 在本练习中,我们将使用 MDN superheros.json 文件,您可以从[这里](https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json)获取该文件 获取 JSON 我们将使用 XMLHttpRequestAPI(通常缩写为 XHR)来获取 JSON。这是 JavaScript 中最有用的对象之一,因为它允许我们向网络发出请求,以使用 JavaScript 从服务器获取资源,即文本、图像、JSON、HTML 片段,这意味着我们可以轻松地更新少量内容,而无需每次重新加载整个页面。其结果是网页响应速度更快,但这是我们将在本书后面深入探讨的内容。 让我们开始吧。 1. 我们需要做的第一件事是存储我们将要检索的 JSON 的 URL,该 URL 存储在一个变量中。将以下内容添加到 JavaScript 代码的底部: var requestURL=https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json'; 2. 接下来,我们创建一个请求对象实例来创建一个新的请求。这是从 XMLHttpRequest 构造函数完成的,我们使用 new 关键字来完成。将这一行添加到代码的底部 var request=new XMLHttpRequest(); 3. 现在我们使用 open()打开一个新请求,因此添加以下行: 打开('GET',requestURL); 这至少需要两个参数;有相当多的可选选项可供选择,但在本例中,强制选项就足够了。他们是: * HTTP 方法–用于向网络发出请求。在我们的示例中,使用 GET 是可以的,因为我们只需要一些简单的数据。 * 正在向其发出请求的 URL。在我们的示例中,该 URL 用于先前存储的 JSON 文件 一旦你做到了这一点 4. 添加这两行以将 responseType 设置为 JSON。这样,XHR 将知道 JSON 将从服务器返回,并将在后台转换为对象。然后我们使用 send()发送请求: request.responseType='json'; request.send(); 5. 最后,我们等待服务器返回响应,然后处理它。在代码底部添加以下行: request.onload=函数(){ var 超级英雄=请求。响应; 大众头目(超级英雄); 表演英雄(超级英雄); } 代码已包装在事件处理程序中,该事件处理程序将在加载事件触发请求对象时运行。之所以会发生这种情况,是因为 load 事件只有在响应成功返回时才会触发,这样做可以确保 request.response 在我们想要处理它时肯定是可用的。 填充标题 我们有 JSON 数据,并将其转换为 JavaScript 对象。现在我们可以通过编写前面引用的函数来使用它。首先,需要将以下行添加到代码底部–这是一个函数定义: 函数 populateHeader(jsonObj){ var myH1=document.createElement('h1'); myH1.textContent=jsonObj['squadName']; 表头.追加子项(myH1); var myPara=document.createElement('p'); myPara.textContent='家乡:'+jsonObj['家乡]+'//格式:'+jsonObj['Formed']; 标题.appendChild(myPara); } 该参数被命名为 jsonObj,以提醒对象最初来自 JSON。首先我们使用 createElement()创建 # 元素;接下来,将其 textContent 设置为等于名为 squadName 的对象属性,然后使用 appendChild()将其追加到标头。然后对段落执行类似操作;它将被创建,其文本内容将被设置并附加到标题中。不同之处在于,段落的文本被设置为一个串联字符串,该字符串同时包含家乡和形成的特性对象。 创建信息卡 要创建和显示信息卡,我们将此函数添加到代码底部: 函数 showHeroes(jsonObj){ var=jsonObj['members']; for(var i=0;i var myArticle=document.createElement('article'); var myH2=document.createElement('h2'); var myPara1=document.createElement('p'); var myPara2=document.createElement('p'); var myPara3=document.createElement('p'); var myList=document.createElement('ul'); myH2.textContent=英雄[i].name; myPara1.textContent='秘密身份:'+英雄[i].秘密性; myPara2.textContent='Age:'+英雄[i].Age; myPara3.textContent='Superpowers:'; 超级大国=英雄[i]。力量; 对于(var j=0;j var listItem=document.createElement('li'); listItem.textContent=超级大国[j]; myList.appendChild(列表项);     } myArticle.appendChild(myH2); myArticle.appendChild(myPara1); myArticle.appendChild(myPara2); myArticle.appendChild(myPara3); myArticle.appendChild(myList); .儿童(我的文章);   } } 首先,objectmembers 属性现在存储在一个新变量中。数组有几个对象,每个对象都包含超级英雄的信息。然后使用 for 循环遍历每个单独的数组对象。对于其中的每一项,我们: * 创建了几个新元素–一个 、一个 ## 、一个 元素 * ## 设置为具有当前英雄的名称 * 这三段( )充满了神秘性、年龄和一行“超级大国”字样。这将介绍一些列表信息 * 名为 powers 的属性存储在名为 superPowers 的新变量中,该变量有一个数组,列出当前英雄的 superPowers * 另一个 for 循环用于循环当前英雄的超级大国;我们为每个元素创建一个 * 元素,将超级能力放入元素中,然后使用 appendChild()将 listItem 放入 * 最后,我们在 (myArticle)中添加了三个 s、 ## 和 添加到<>中。您必须注意的一点是您附加内容的顺序,因为这将是它们在 HTML 中显示的顺序。 笔记 如果您正努力遵循此处使用的点/括号表示法,那么在单独的选项卡或文本编辑器中打开 superheros.json 文件可能会有所帮助,这样您就可以在完成此操作时引用它。您还可以回顾有关对象的部分,以获得有关这种表示法的更多信息。 这是一个显示如何访问 JavaScript 对象的非常简单的示例,之所以这样做是因为 XHR 请求被设置为直接将 JSON 响应转换为 JavaScript 对象,如下所示: request.responseType='json'; 有时候事情并不那么容易;有时,您会得到一个原始 JSON 字符串,需要手动转换为对象。当通过网络发送对象时,需要先将其转换为 JSON 字符串,然后才能发送。这些是 web 开发领域中非常常见的问题,为了帮助解决这些问题,在包含以下两种方法的 web 浏览器中提供了一个 JSON 对象: * parse()-这将获取 JSON 字符串的一个参数,并返回与之对应的 JavaScript 对象 * stringify()–这将获取一个对象的参数,并返回与之对应的 JSON 字符串形式 以我们前面的示例为例,如果我们将 XHR 设置为返回原始文本(JSON),然后 parse()将其转换为 JS 对象。关键代码是: 打开('GET',requestURL); request.responseType='text';//现在我们有一根绳子! request.send(); request.onload=函数(){ var superHeroesText=request.response;//从响应中获取字符串 var superHeroes=JSON.parse(superHeroesText);//将其转换为对象 大众头目(超级英雄); 表演英雄(超级英雄); } 正如您可能猜到的,stringify()方法的工作方式正好相反。在浏览器的 JavaScript 控制台中一次输入一行,然后查看发生了什么: var myJSON={“name”:“Chris”,“age”:“38”}; myJSON var myString=JSON.stringify(myJSON); myString 已经创建了一个 JavaScript 对象;我们检查其中的内容,然后使用 stringify 将其转换为 JSON 字符串。返回值保存在一个新变量中,然后我们再次检查。 这就完成了我们关于 JSON 的基本指南,以及如何在程序中使用它,如何创建 JSON 并解析它,以及如何访问锁定在其中的数据。接下来,我们来看一个简单的弹跳球游戏。 是时候测试一下你新学到的 JavaScript 知识了。您学习了对象和要使用的语法,现在我们将构建一个游戏,一个简单的弹跳球游戏,它将为您提供一些使用 JavaScript 构建自定义对象的实践。 让我们跳起来 我们将为一个经典的弹跳球游戏编写一个演示,演示 JavaScript 对象是多么有用。我们会让球在屏幕上弹跳,每当一个球碰到另一个球,它们都会改变颜色。 我们将使用一个名为 Canvas 的 API 来绘制球,并使用一个名为 requestAnimationFrame 的 API 来为整个显示设置动画。你不需要事先了解任何一个 API,在你构建这个游戏的时候,希望你能对它们进行更多的研究。接下来,我们还将介绍一些巧妙的技术,比如从墙上弹起一个球,看一个球是否击中另一个球——这就是碰撞检测——我们还将使用一些很酷的物体。 首先,您需要制作这些文件的本地副本-[index.html](https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/index.html)、[style.css](https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/style.css)和[main.js](https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/main.js)文件。这些文件包含: * Index.html–一个简单的 html 文档,包含一个 # 元素、一个 元素用于绘制球,以及一些帮助我们将 JavaScript 和 CSS 应用于 html 的元素 * Style.css–一些简单的样式,我们将使用这些样式对 # 进行样式设置和定位,并删除页面边缘的任何边距或滚动条,使其看起来整洁 * Main.js–一些 JavaScript,可以帮助我们设置 元素,并提供我们可以使用的通用函数。 到目前为止,我们的脚本如下所示: var canvas=document.querySelector('canvas'); var ctx=canvas.getContext('2d'); var width=canvas.width=window.innerWidth; var height=canvas.height=window.innerHeight; 在这个脚本中,我们引用了 元素,然后我们调用了 getContext()方法,这样我们就有了某种可以绘制球的上下文。由此产生的变量是 ctx,该对象提供画布绘图区域的直接表示,并使我们能够绘制一些 2D 形状。 接下来,设置宽度和高度变量以及 canvas 元素的高度和宽度,该元素由名为 canvas.height 和 canvas.width 的属性表示,以便它们等于浏览器视口的高度和宽度。这是网页将显示的区域,我们为此使用了名为 Window.innerHight()和 Window.innerWidth()的属性。 您可能已经注意到,我们已经将多个任务链接在一起;这很好,因为它帮助我们更快地设置所有变量。 到目前为止,脚本的最后一部分如下所示: 随机函数(最小值、最大值){ var num=Math.floor(Math.random()*(max-min+1))+min; 返回 num; } 该函数接受两个数字形式的参数,并将从两个参数之间的某个位置返回一个随机数。 我们的节目将有相当多的球在屏幕上弹跳。因为它们的行为方式几乎相同,所以表示它们的最简单方法是使用对象。我们将首先在代码末尾包含此构造函数: 功能球(x、y、velX、velY、颜色、尺寸){ 这个.x=x; 这个。y=y; this.velX=velX; 这是一个很好的例子; 这个颜色=颜色; 这个。大小=大小; } 我们这里有几个参数,用于定义每个球必须在程序中工作的属性: * x 和 y 坐标–这些是球将首先出现在屏幕上的水平和垂直坐标。该范围介于 0(左上角)到浏览器上视口的高度和宽度(右下角)之间。 * velX 和 velY–这是每个球的水平速度和垂直速度。在现实世界中,当球设置动画时,这些值将定期添加到 x 和 y 坐标值中,说明每个球在每个帧上移动的程度。 * 颜色–每个球都有特定的颜色 * 大小–每个球都有一个特定的大小,球的半径以像素为单位 这就是分类出的球的属性,现在我们需要看看这些方法。显然,我们想让球在这个节目中做点什么。 第一步是将下面的 draw()方法包含到 Ball()的原型中: Ball.prototype.draw=函数(){ ctx.beginPath(); ctx.fillStyle=this.color; ctx.arc(this.x,this.y,this.size,0,2*Math.PI); ctx.fill(); } 通过此功能,球被告知在屏幕上绘制自己。为此,我们调用了刚才定义的 2D 画布上下文(ctx)的几个成员。上下文可以看作是一张纸,我们告诉笔开始画图: * 第一步是使用 beginPath()方法;这说明我们想要在纸上画一个形状 * 其次,我们使用 fillStyle()定义形状所需的颜色。这将设置为我们希望球的颜色属性 * 然后使用 arc()方法在纸上绘制一条弧。这有四个参数: * 圆弧中心的 X 和 y 位置–这些是球的 X 和 y 属性 * 圆弧半径–这是球大小的属性 * 最后两个用于指定圆弧围绕圆绘制的开始和结束度数。我们指定了 0 度和 2*PI。这等于 360 度,您必须使用弧度来指定,为我们提供一个完整的圆。如果你用 1*PI,你只能得到半个 180 度的圆。 * 最后,fill()方法用于告诉程序,我们希望用 beginPath()开始的路径完成,并且我们指定的颜色用于填充该区域。 现在可以开始测试对象了。 1. 保存代码,然后在浏览器中打开 HTML 文件 2. 进入浏览器中的 JavaScript 控制台并刷新页面;打开控制台时,画布大小应更改为小视口 3. 键入此选项,以便创建新的 ball 实例: var testBall=新球(50,100,4,4,'蓝色',10); 4. 请各位成员: testBall.x testBall.size testBall.color testBall.draw() 5. 当输入该代码的最后一行时,应该在画布上绘制球。 球可以在起跑位置被拉回,但我们希望它移动。我们通过使用更新函数来实现这一点。以下代码位于 JS 文件的末尾,为 Ball()的原型提供了一个更新方法: Ball.prototype.update=函数(){ 如果((此.x+此.size)>=宽度){ this.velX=-(this.velX);   } 如果((this.x-this.size)<=0){ this.velX=-(this.velX);   } 如果((this.y+this.size)>=高度){ this.velY=-(this.velY);   } 如果((this.y-this.size)<=0){ this.velY=-(this.velY);   } this.x+=this.velX; this.y+=this.y; } 函数开头的四个部分检查球是否到达画布边缘。如果是,则相关速度上的极性相反,因此球将朝相反方向移动。例如,如果球向上移动(正向),则垂直或向下的速度会改变,从而使球向下移动(反向)。 在这四个部分中,我们分别: * 检查 x 坐标是否大于画布宽度,即球在右边缘离开页面 * 检查 x 坐标是否小于 0,即球从页面左边缘离开 * 检查 y 坐标是否大于画布高度,即球在底部边缘离开页面 * 检查 y 坐标是否小于 0,即球从页面顶部边缘离开 在这四种情况下,球的尺寸都包含在计算中。这是因为 x 和 y 坐标位于球的中心,但我们需要球的边缘从画布区域的周长反弹。我们最不希望它在开始反弹之前离开屏幕。 代码的最后两行给出了 x 坐标的 velX 值和 y 坐标的 velY 值,实际上,每次调用该方法时都会移动球。 现在已经足够了,让我们继续看一点动画。 现在我们将有一点乐趣。我们将在画布上添加一些球,并为它们提供一些动画。 1. 第一步是创建一个存储球的位置,我们使用下面的数组来实现这一点,因此将其添加到代码末尾: var=[]; 2. 任何包含动画对象的程序通常都包含某种动画循环。此循环保持程序信息的更新,将更新后的视图渲染到每个动画帧上。这是基本的任何游戏或动画程序。将以下代码添加到程序末尾: 函数循环(){ ctx.fillStyle='rgba(0,0,0,0.25)'; ctx.fillRect(0,0,宽度,高度); 而(球长<25){ 变量大小=随机(10,20); var ball=新球( //球位置始终至少绘制一个球宽 //远离画布边缘,以避免绘制错误 随机(0+尺寸,宽度-尺寸), 随机(0+尺寸,高度-尺寸), 随机(-7,7), 随机(-7,7), "rgb("随机(0255)","随机(0255)","随机(0255)","随机, 大小     ); 推(球);   } 对于(变量 i=0;i 球[i].draw(); balls[i].update();   } 请求动画帧(循环); } 此循环函数执行以下操作: * 画布填充颜色设置为半透明黑色。然后绘制该颜色的矩形以覆盖画布的整个高度和宽度。这是使用 fillRect()完成的,它有四个参数,为矩形提供起始坐标、高度和宽度。这样做的目的是在绘制下一帧之前覆盖上一帧的图形。如果遗漏了这一帧,您将无法在屏幕上看到任何球,只有长虫子在蠕动!填充颜色设置为 rgba(0,0,0,0.25),它是半透明的,可以让最后几帧显示一点;这就是球移动时提供轨迹的原因。如果将 0.25 改为 1,则不会再看到它们。用这个数字玩一玩,看看它有什么效果。 * 将使用 random()函数生成的随机值创建 Ball()的新实例。使用 push()函数将新实例添加到 balls 数组的末尾,但只有当数组中的球少于 25 个时才会发生这种情况。所以,当你在屏幕上看到 25 个球时,就不会有更多的球了。同样,用球的数量进行游戏。长度<25,看看会发生什么-屏幕上的球越多或越少。需要注意的一点是您的计算机和/或浏览器的处理能力;如果指定几千个球,则可能会发现动画速度明显减慢! * 接下来,数组中包含的所有球都循环通过,并为每个球运行 draw()函数和 update()函数,从而在屏幕上绘制球。然后更新位置和速度,为下一帧显示做好准备。 * requestAnimationFrame()方法用于再次运行该函数。当您不断运行此方法并且不断传递相同的函数名时,该函数将每秒运行指定次数,以便创建平滑动画。这通常是递归完成的,这意味着,每次函数运行时,它都会调用自己,从而导致它重复运行。 3. 最后,在 JS 程序的末尾键入此代码,以便调用该函数;这将启动动画: loop(); 这是基本的照顾。保存并刷新以使球反弹! 现在来点更有趣的。我们将在程序中添加一些碰撞检测;每个球都需要知道它何时与另一个球碰撞。 1. 将此方法定义添加到定义 Ball.prototype.update()方法块的位置: Ball.prototype.collisionDetect=函数(){ 对于(var j=0;j 如果(!(this==balls[j])){ var dx=this.x-balls[j].x; var dy=this.y-balls[j].y; 变量距离=数学 sqrt(dx*dx+dy*dy); if(距离 balls[j].color=this.color='rgb('+random(0255)+','+random(0255)+','+random(0255)+');       }     }   } } 这是一种比较复杂的方法,所以如果乍一看对您来说没有多大意义,请不要惊慌。这就是它的工作原理: * 对于每个球,我们需要查看屏幕上的所有其他球,以查看其中一个球是否与球碰撞。为此,我们打开一个 for 循环,该循环将遍历 balls[]数组中的每个球 * 在 for 循环中,if 语句用于查看当前正在循环的球是否就是我们正在检查的球。我们不想做的是检查球是否与自身碰撞!我们通过检查查看当前球(即当前正在检查其 collisionDetect()方法的球)是否与循环通过的球相同(当前 for loop 迭代在 collisionDetect()方法中引用的球)。然后用作取消检查的一种方式;这样,if 语句代码只有在它们不相同时才会运行。 * 接下来,使用通用算法检查两个圆之间的碰撞。我们在这里检查的是是否有任何圆重叠。 * 如果检测到冲突,则运行内部 If 语句代码。我们在这里所做的就是为每个圆设置颜色属性为随机颜色。我们本可以在这里变得更复杂,也许可以让球更真实地相互反弹,但这将是一个更复杂的实现。现在我们保持简单。 2. 此方法还需要为动画中的每一帧调用,因此请将下面的代码添加到显示 balls[i].update()的行中; balls[i].碰撞检测(); 再次保存并刷新,当球发生碰撞时,它们会改变颜色。 希望你在那里玩得开心,写一个随机弹跳球的演示。我们使用了许多对象和面向对象的技术,这应该为您提供一些使用对象和真实环境的良好实践。 在下一节中,我们将进一步向演示中添加更多功能。 我们将继续向 bouncing balls 游戏添加更多功能,作为测试您对 JavaScript 中对象和面向对象构造的理解的一种方法。 开始之前,请制作这些文件的本地副本(新副本): * [index-finished.html](https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/index-finished.html) * [style.css](https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/style.css) * [main-finished.js](https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/main-finished.js) 项目大纲 虽然我们目前的演示本身很有趣,但我们想让它更有趣。我们将添加一些与用户控制的邪恶圈子的互动。这个邪恶的球会吃掉它抓住的任何球。 除此之外,我们还将对您的对象构建技能进行测试;我们将创建一个名为 Shape()的通用对象,邪恶的圆和球都可以从中继承。最后,我们将添加一个记分计数器,以便我们可以跟踪还有多少球需要捕获。 第一步是修改 Ball()构造函数您必须将其转换为 Shape()构造函数,然后我们添加另一个 Ball()构造函数: 1. 确保 Shape()构造函数定义 x、y、velX 和 velY 属性的方式与 Ball()完全相同,但不定义大小和颜色属性。 2. 确保它还定义了一个名为 exists 的全新属性。这将跟踪演示中球的存在,这些球没有被我们的邪恶圈子消耗掉。这必须是返回 True 或 False 的布尔值。 3. 确保 Ball()构造函数可以继承 x、y、velX 和 velY 的属性,以及 Shape()的 exists 属性。 4. 颜色和大小属性的定义方式必须与第一个 Ball()构造函数相同。 5. 确保 Ball()构造函数和构造函数的原型设置正确。 ball for draw()、collisionDetect()和 update()中的方法定义可以保持原样。 新的构造函数调用需要添加一个新参数 Ball()(…),以确保 exists 是第 5 个参数,并且其值为 True。 完成所有这些之后,重新加载代码。它应该像以前一样工作,但需要重新设计对象。 现在我们来见见那个坏男孩——我们的邪恶圈子()。我们将只使用其中一个,但我们将使用从 Shape()继承的构造函数来定义它。这是给你一点练习。 稍后,您可以添加其他玩家可以控制的另一个圆圈,也可以添加由计算机控制的几个圆圈。显然,一个邪恶的圈子不会统治世界,但就我们而言,它会做得很好。 我们的 EvillCircle()构造函数需要从 Shape()继承一些内容–x、y、exist、velX 和 velX。请记住,velY 和 velX 必须始终等于 20,这可以通过这些线来实现–Shape.call(这个,x,y,20,20,存在)。 构造函数还必须定义以下自己的属性: * 尺寸–10 * 颜色-“白色” 不要忘记继承的属性必须定义为构造函数中的参数,并且构造函数和原型属性都设置正确。 循环()中应该有四种方法 画() 这与 Ball()中的 draw()方法的用途完全相同–对象实例绘制在画布上。它的工作方式也大致相同,因此最简单的方法是从 Ball.prototype.draw 复制定义,然后对其进行以下更改: * 我们不希望我们的邪恶圈子被填满,它只需要一条外线或一笔。为此,请将 fill()更新为 stroke(),将 fillStyle()更新为 strokeStyle()。 * 我们还需要更粗的笔划,以便更容易在屏幕上看到邪恶的圆圈。为此,在调用 beginPath()之后的某个地方为 lineWidth()指定一个值 3。 checkBounds() 这一个将执行与 Ball()中 update()函数的第一位相同的操作–它检查我们的邪恶圆圈是否将从屏幕边缘消失,如果是,则更改它,使其无法消失。同样,这与 Ball.prototype.update 的定义基本相同,但有以下更改: 移除末端的两条线,这样邪恶圆圈的位置就不会在每一帧更新。我们不需要这样做,因为它将以不同的方式移动,正如您稍后将看到的那样。 在 if 语句中,如果我们的测试返回的值为 true,我们不希望 velX/velY 更新。相反,x/y 的值需要更改,导致邪恶的圆圈在屏幕上反弹一点。在这里,根据需要添加或减去邪恶圆圈的大小属性是有意义的。 setControls() setControls()方法将名为 onkeydown 的事件侦听器添加到窗口对象。这样,当按下键盘上的特定键时,邪恶的圆圈可以在屏幕上移动。将以下代码添加到方法定义中: var_this=这个; window.onkeydown=函数(e){ 如果(如 keyCode===65){ _this.x-=_this.velX; }else if(e.keyCode==68){ _this.x+=_this.velX; }else if(e.keyCode==87){ _this.y-=_this.ly; }else if(e.keyCode==83){ _this.y+=_this.ly;     }   } 这样,当按下一个键时,会参考事件对象的 keycode 属性;它将检查它是哪个键,如果它是指定的键代码所代表的四个键之一,则圆圈将向上、向下、向左或向右移动。 需要考虑的几件事–指定的代码映射到哪些键?为什么我们要设置 var_this=this;以我们的立场?一个线索,它与函数作用域有关。 碰撞检测() 此方法的工作原理与 Ball()中的 collisionDetect()方法基本相同,因此请复制该方法定义,然后进行以下更改: * 不再需要检查正在迭代的球是否是正在检查的球,因为我们不再有球了——我们有一个邪恶的圆。因此,在 outer if 语句中,您需要包含某种测试,以查看被检查的球是否确实存在——请考虑用于执行此操作的属性。如果没有,邪恶圈已经消耗了它,所以你不需要再次运行检查。 * 我们不再需要使对象在碰撞时更改其颜色。相反,在内部 if 语句中,你可以设置任何与你的邪恶圈碰撞的球,使其不再存在——再一次,仔细想想如何做到这一点。 把这一切结合起来 现在邪恶圈已经被定义,我们希望它出现在游戏中。为此,需要在 loop()函数中更改以下几项: * 首先,我们需要邪恶圆的一个新对象实例,并指定所需的参数。接下来调用 setControls()方法。这些只需要做一次;您不需要在每次循环迭代中重复它们。 * 如果循环穿过每个球,并且为每个球调用函数 draw()、update()和 collisionDetect(),则需要对其进行更改,以便仅在当前球存在时调用函数。 * 最后,应在每次循环迭代中调用邪恶圆实例的 draw()、checkBounds()和 collisionDetect()方法。 最后一件事是实施我们的分数计数器,因此请仔细遵循以下步骤: 1. 打开 HTML 文件,转到包含“Ball count”文本的 # 元素,并在其下方添加一个 元素 2. 打开 CSS 文件并在代码末尾添加此规则: p{ 位置:绝对位置; 保证金:0; 顶部:35px; 右:5px; 颜色:#aaa; } 3. 现在转到 JavaScript 文件并进行以下更改: * 创建将存储段落引用的新变量 * 在某种程度上,记下屏幕上有多少个球 * 计数必须递增,并且每次添加新球时都应显示更新的球数 * 每当邪恶圈消耗一个球时,计数应该减少,更新的球数应该显示出来,这样它就不再存在了。 一定要慢慢地一步一步地完成这项工作。每次你让一个舞台工作时,都要复制一份演示,这样,如果你以后遇到麻烦,你就可以再参考它。 为了结束对 JavaScript 的研究,我们将简要介绍一下客户端 API。 当您开始为应用或网站编写客户端 JavaScript 时,首先会遇到的是 API 或应用编程接口。这些功能允许您操作操作系统的不同部分以及网站或应用将在其上运行的浏览器,或者操作其他服务和网站的数据。我们将简要介绍什么是 API,以及如何利用在开发过程中经常遇到的一些非常常见的 API。 我们花了相当长的时间研究 JavaScript 对象,因为它们是模块,往往涉及相当简单级别的 API 使用,因为没有它们,编写 JavaScript 客户端示例并不容易。 我们将从深入了解 API 开始,了解它们是什么,它们是如何工作的,它们是如何在代码中使用的,以及它们的结构。我们将了解主要的 API 类及其用途。 那么,什么是 API? 应用编程接口是计算机编程语言中可用的一种结构,它允许程序/应用开发人员更好地创建更复杂的功能。API 将抽象出大量复杂的代码,并为您提供更容易使用的语法。 用现实世界的话来说,把你的思想集中在为你的房子、公寓或任何你居住或工作的地方供电的电力上。当你想使用水壶、熨斗、电脑等东西时,你把电器插入插座,它就会工作。你不需要将设备直接写入主电源——这将是一种效率极低的工作方式,如果你不是注册电工,那么这也很困难,更不用说危险了。 同样地,假设您想在 3D 中编程一些图形。使用一个高级语言编写的 API,如 Python 或 JavaScript,将比你试图编写一个低级代码(如 C++)来直接控制计算机中的 GPU 和其他图形功能更容易。 官方术语表条目将 API 描述为应用(软件程序)中存在的一组规则和功能。这些规则和特性使得软件可以使用软件而不是人机界面进行交互。API 可以被视为提供它的应用与第三方软件、硬件或其他项目之间的合同。 就 web 开发而言,API 是一组代码功能,如属性、方法、URL 和事件,开发人员使用这些功能使层应用与用户 web 浏览器的指定部分或系统上的其他硬件或软件,甚至第三方服务和网站交互。 以下是一些 API 的示例: * getUserMedia–此 API 用于从用户系统上的网络摄像头获取视频和音频。然后,无论开发人员如何使用,都可以使用它,即录制音频和视频、通过电话会议广播、从网络摄像头捕获静态图像等等。 * 地理定位–此 API 用于从用户设备上可用的服务(例如 GPS)中检索位置信息。例如,这些信息可以和谷歌地图 API 一起用于在地图上绘制位置、显示路线和显示旅游景点。 * Twitter–这些 API 用于从 Twitter 帐户获取数据,例如,用户的最新推文,然后显示在页面上。 * Web 动画–这些用于设置网页部分的动画,例如使图像移动或旋转。 客户端 API 无处不在,JavaScript 有大量内置 API。这些 API 实际上不是语言的一部分;相反,它们构建在该语言的核心之上,为您编写应用时提供了更强大的功能。通常,这些 API 分为两类: ### • 它们内置于 web 浏览器中,可用于暴露浏览器数据和来自计算机周围环境的数据,从而提供执行复杂和有用操作的能力。例如,Web 音频 API 包含允许在浏览器中操纵音频的结构。例如,采取音轨,改变音量,对它们应用新的和不同的效果等等。浏览器在后台做什么是使用较低级别的代码,相当复杂,例如 Rub 或 C++,用于处理音频。你看不到这一点,因为 API 已经抽象出了复杂性。 ### • 这些在 web 浏览器中不是默认 API;通常,您需要从 internet 上的其他地方获取代码和相关信息。例如,以 twitterapi 为例。你可以用它做很多事情,比如在你的博客或社交媒体页面上显示你的推文。这个第三方 API 为您提供了一组特殊的构造,用于查询 Twitter 和返回指定信息。 因此,我们讨论了什么是 JavaScript 客户端 API 以及它与编程语言的关系。为了让它更清楚一点,我们将简要回顾一下,这样您还可以看到 JavaScript 中的其他工具可能也适用于哪些方面: * JavaScript—内置于 web 浏览器中的高级语言(脚本)。JavaScript 允许您向 web 应用或网页添加不同的特性和功能。JavaScript 也可以在其他环境中使用,比如 Node。 * 浏览器 API–这些结构已经内置到语言之上的浏览器中,允许更轻松地实现功能。 * 第三方 API–这些是内置于第三方服务和平台(如 Twitter、Facebook 等)的结构。这些工具使您能够在自己的网页或应用中使用这些平台的功能,例如,在自己的网站上显示推文或 Facebook 帖子。 * JavaScript 库–这些 JavaScript 文件中包含自定义函数。这些功能可以附加到应用或网页,以帮助您编写通用功能或加速现有功能。库的示例包括 jQuery、React 和 Mootools。 * JavaScript 框架——它们是库的下一步。示例包括 Ember 和 Angular,它们是包含 CSS、HTML、JavaScript 和一些其他技术的包,您需要安装这些技术并使用它们从头开始编写 web 应用。框架和库之间最大的区别是所谓的“控制反转”。当从库中调用一个方法时,开发人员控制过程,而事情是通过框架反转的——开发人员的代码由框架调用。 现代浏览器包含大量 API,允许您对代码进行大量操作。您将使用的最常见浏览器类别有: * 文档操作 API–这些 API 将操作您加载到浏览器中的任何文档。显而易见的一个是 domapi(documentobjectmodel),它允许对 CSS 和 HTML 进行操作,例如,创建 HTML、删除并更改 HTML、将新样式动态应用于 web 页面等等。例如,每当网页上出现弹出窗口或看到新内容时,即是 domapi 在工作。 * 用于从服务器获取数据的 API–这些 API 自己更新网页的小块内容,这些是非常常见的 API。一个微小的细节可以改变网站的行为和表现;例如,如果您希望更新股票列表或书籍列表,最好立即更新,而无需从服务器重新加载整个页面或网站,这样可以使您的网站看起来更快速,对用户的反应更灵敏。此类中的一些 API 包括 Fetch 和 XMLHttpRequest。您可能还将 Ajax 视为一个描述这种技术的术语,我们将在本指南后面的部分中介绍。 * 用于操作图形和绘制图形的 API–这些 API 现在在浏览器中得到了大量支持,最常见的 API 是 WebGL 和 Canvas,这两种 API 都允许您更新 元素(HTML)中的像素数据,以创建 2d 和 3D 场景。例如,可以绘制圆、矩形或其他形状,将图像导入画布,对其应用过滤器,例如使用画布 API 的灰度或深褐色,以及使用 WebGL API,可以在 3D 中创建高度复杂的场景,包括纹理、照明等。这些类型的 API 往往与其他有助于创建动画循环的 API 结合使用,如 window.requestAnimationFrame(),以及有助于对游戏和卡通等场景进行连续更新的 API。 * 视频和音频 API–这些 API 包括 Web 音频 API、HTMLMediaElement 和 WebRTC,允许您使用多媒体做一些很酷的事情。您可以创建自定义的用户界面控件来播放音频和视频,您可以在视频中显示文本,显示曲目名称、字幕、字幕等。您可以从网络摄像机抓取视频,并使用另一个 API 来操作它,使用画布在另一台计算机上的电话会议中显示视频。您甚至可以为音频曲目提供某些效果,如失真、平移、增益等。 * 设备 API–这些 API 用于操作或检索硬件中的数据,使其能够在 web 应用中以有用的方式使用。例如,通过系统通知通知用户有可用的更新,或者,在移动设备的情况下,使用振动 API。 * 用于客户端存储的 API–这些在浏览器中越来越常见。当你想要一个在每次页面加载之间保存自己状态的应用,甚至当设备不在线时,能够在客户端存储数据是非常有用的。有几个选项可供选择,例如使用 Web 存储 API 保存名称和值数据,使用 IndexedDB API 进行稍微复杂一点的数据存储,如表格数据。 第三方 API 种类繁多,有些比其他 API 更受欢迎,您可能会在某些时候使用这些 API: * Twitter API-允许您在网页上显示推文 * 地图 API–如谷歌地图 API 和 MapQuest,让您可以使用地图在网站上做各种事情 * FacebookAPI 套件–这些 API 套件允许您以有益于您的应用的方式使用 Facebook 的一部分,例如允许人们使用 Facebook 登录到您的应用、在应用中付款、有针对性的广告活动,等等。 * YouTube API–这允许您将 YouTube 中的视频嵌入到您的网站中,建立 YouTube 播放列表,搜索 YouTube,等等 * Twilio API–这为您提供了一个框架,可以将视频和语音通话功能构建到应用中,从应用发送彩信和短信,等等 JavaScript 中所有不同的 API 都以不同的方式工作,但一般来说,它们在工作方式上共享相同的主题和特性。 * 基于对象 使用 API 编写的任何代码都将使用至少一个 JavaScript 对象与这些 API 交互。这些对象可以被视为保存 API 使用的数据(对象属性)和保存 API 提供的功能(对象方法)的容器。 为了更好地描述这一点,我们将再次研究 Web 音频 API。这是相当复杂的 API,它有相当多的对象。显而易见的目标是: * AudioContext–这表示一个音频图,您可以使用该图操纵在浏览器中播放的音频。有几种方法和属性可用于音频处理。 * MediaElementAudioSourceNode–这表示一个 元素,该元素包含要播放的声音以及您要在音频上下文中操纵的声音。 * AudioDestinationNode–表示音频目的地,例如,要输出音频的设备,通常是计算机上的耳机或扬声器。 那么,所有这些物体之间的相互作用是什么?请查看以下 HTML 代码: 播放 首先,包含了一个 元素。使用此功能,网页中嵌入了 MP3;我们没有为浏览器添加任何默认控件。接下来,我们添加了一个用于启动和停止音频的,以及一个>=元素。这是一个类型范围,我们使用它来调整播放时的曲目音量。 现在看一下示例的 JavaScript 代码: 我们要做的第一件事是创建一个 AudioContext 实例,我们的曲目将从这个实例中被操纵: const AudioContext=window.AudioContext | | window.webkitadiocontext; const audioCtx=新的 AudioContext(); 接下来,创建用于存储对、 和元素的引用的常数,并使用 AudioContext.createMediaElementSource()方法创建 MediaElementAudioSourceNode–这表示音频源–播放曲目的 元素: const audioElement=document.querySelector('audio'); const playBtn=document.querySelector(“按钮”); const volumeSlider=document.querySelector(“.volume”); const audioSource=audioCtx.createMediaElementSource(audioElement); 接下来,包括两个事件处理程序,每当按下相关按钮时,它们将从播放切换到暂停和返回,并在音频曲目结束时将显示一直重置回开始: //播放/暂停音频 playBtn.addEventListener('click',function(){ //检查上下文是否处于挂起状态(自动播放策略) 如果(audioCtx.state==='suspended'){ audioCtx.resume();     } //如果音轨停止,播放它 if(this.getAttribute('class')='paused'){ audioElement.play(); this.setAttribute('class','playing'); this.textContent='Pause' //如果正在播放曲目,请停止播放 }else if(this.getAttribute('class')='playing'){ audioElement.pause(); this.setAttribute('class','paused'); this.textContent='Play';     } }); //如果轨道结束 audioElement.addEventListener('ended',function(){ playBtn.setAttribute('class','paused'); this.textContent='Play' }); 笔记 您可能已经发现,我们用于播放和暂停曲目的 play()方法和 pause()方法与 Web Audio API 无关;相反,它们是 HTMLMEDIALEMENT 的一部分,该元素虽然关系密切,但却不同。 接下来,使用名为 AudioContext.createGain()的方法创建 GainNode 对象。这用于调整通过它的任何音频的音量。我们还创建了另一个事件处理程序,可以在滑块值更改时更改音频图增益或音量值: const gainNode=audioCtx.createGain(); volumeSlider.addEventListener('input',function(){ gainNode.gain.value=此.value; }); 要使这一切正常工作,最后需要做的事情是将音频图中的所有不同节点连接起来,为此,我们使用 AudioNode.connect()方法,该方法适用于所有节点类型: audioSource.connect(gainNode).connect(audioCtx.destination); 音频将从源开始。然后连接到增益节点,允许根据需要调整音量。该增益节点随后连接到目标节点,允许在设备(您的计算机)上播放声音–名为 AudioContext.destination 的属性代表硬件上可用的任何默认 AudioDestinationNode,即您的计算机扬声器。 * 可识别的入口点 当您使用 API 时,必须始终确保您确切知道它的入口点在哪里。以 Web 音频 API 为例;这个入口点非常简单——AudioContext 对象——无论何时进行任何音频操作,都必须使用它。 另一个是 domapi,或者文档对象模型。这也是一个非常简单的切入点;您通常可以在文档对象或 HTML 元素实例中找到要以某种方式更改的功能。FO=或示例: var em=document.createElement('em');//创建一个新的 em 元素 var para=document.querySelector('p');//引用现有的 p 元素 em.textContent='你好!;//给他们一些文本内容 第 11 段(em);//将 em 嵌入到段落中 Canvas API 只能使用上下文对象来操作对象,尽管这次它不是音频上下文,而是图形上下文。要创建上下文对象,需要获取对要绘制的 元素的引用,然后调用 htmlcanvaseElement.getContext()方法: var canvas=document.querySelector('canvas'); var ctx=canvas.getContext('2d'); 然后,必须通过调用内容对象属性和相关方法来完成对该画布的任何操作。内容对象是 CanvasRenderingContext2D 实例。 例如: Ball.prototype.draw=函数(){ ctx.beginPath(); ctx.fillStyle=this.color; ctx.arc(this.x,this.y,this.size,0,2*Math.PI); ctx.fill(); }; 您可能会认识到这段代码——我们在前面创建的弹跳球游戏中使用了它。 * 事件用于处理状态的更改 事件是在您正在编程的系统中发生的事件或操作。系统将告诉您有关事件或事件的信息,以便您可以在需要时以正确的方式作出响应。例如,如果用户要单击您网页上的按钮,您可能希望通过显示包含某些信息的框来响应。 并非所有 Web API 中都有事件,但大多数 API 中至少有一个事件。事件处理程序及其属性允许函数在事件触发时运行。 我们在前面绘制 Web Audio API 示例时使用了事件处理程序,为了给您提供进一步的示例,一些 XMLHttpRequest 对象实例(每个实例表示发送到服务器以获取某种类型的资源的一个 HTTP 请求)也有多个可用事件。例如,当请求的资源返回成功响应并使其可用时,将触发加载事件。 看看这个简单的例子,看看如何使用它: var requestURL=https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json'; var request=new XMLHttpRequest(); 打开('GET',requestURL); request.responseType='json'; request.send(); request.onload=函数(){ var 超级英雄=请求。响应; 大众头目(超级英雄); 表演英雄(超级英雄); } 在这段代码的前五行中,我们指定要检索的资源的位置,使用 XMLHttpRequest()构造函数创建请求对象的全新实例,打开 HTTP Get 请求以便检索指定的资源,指定响应应使用 JSON 格式,然后发送请求。 接下来,onload 处理程序函数精确地说明响应将发生什么。我们知道它将被成功返回,并且在加载事件完成后将可用,除非发生一些错误,因此响应被保存;该响应应该包含来自名为 Superheros 的变量的 JSON。然后我们将其传递给两个单独的函数进行更多处理。 * 需要额外的安全机制 Web API 中的特性与 JavaScript 以及其他与 Web 相关的技术具有相同的安全性,例如,同源策略。然而,在需要的地方,它们也可能有额外的安全方法。例如,一些现代 Web API 无法在任何未通过 HTTPS 提供服务的页面上工作,因为它们可能包含敏感数据。 除此之外,还有一些 Web API 需要请求用户的许可,以便在代码中进行调用时启用。例如,通知 API 将显示一个弹出对话框以请求用户权限。 HTMLMediaElement 和 Web 音频 API 都受自动播放策略安全机制的约束。这意味着当网页加载时,音频不能自动播放;您的用户必须是使用某种控件(如按钮)启动它的用户。这是因为有音频自动播放可能会非常恼人,用户永远不应该受到它的影响。 笔记 其中一些安全机制甚至可以阻止示例在本地运行,尽管这取决于浏览器的严格程度。例如,如果本地示例文件加载到浏览器中,而不是从 web 服务器运行。 这就结束了我们对客户端 web API 的简要介绍,您现在应该对它们是什么、如何工作以及如何在代码中使用它们有了相当好的了解。 在本指南的下一部分中,我们将深入了解 jQuery。