整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

HTML ANSI(Windows-1252)参考手册

NSI(Windows-1252)

ANSI 是 Windows 95 及其之前的 Windows 系统中默认的字符集。

ANSI 也称为 Windows-1252。


重要提示

ANSI 和 ISO-8859-1 非常相似,唯一的不同是在 32 个字符上。

在 ANSI 中,从 128 到 159 的字符用于一些有用的字符,比如欧元符号。

在 ISO-8859-1 中,这些字符映射为在 HTML 中不起作用的控制字符。

许多 Web 开发者声明 ISO-8859-1,并使用这 32 个值,就像它们使用的是 Windows-1252。

由于这种常见的误解,当 ISO-8859-1 被声明时,浏览器将更改为 Windows-1252。这对以下文档类型都适用:HTML4、HTML5 和 XHTML。


ANSI 和 ASCII

ANSI 的第一部分(实体编号 0-127)是原来的 ASCII 字符集。它包含数字、大小写英文字母和一些特殊字符。

如需深入了解 ASCII,请查看完整的 ASCII 参考手册。


ANSI 字符集

字符编号实体名称描述
32空格(space)
!33感叹号(exclamation mark)
"34"引号(quotation mark)
#35数字符号(number sign)
$36美元符号(dollar sign)
%37百分比符号(percent sign)
&38&& 符号(ampersand)
'39撇号(apostrophe)
(40左括号(left parenthesis)
)41右括号(right parenthesis)
*42星号(asterisk)
+43加号(plus sign)
,44逗号(comma)
-45连字符(hyphen-minus)
.46句号(full stop)
/47斜线(solidus)
048数字 0(digit zero)
149数字 1(digit one)
250数字 2(digit two)
351数字 3(digit three)
452数字 4(digit four)
553数字 5(digit five)
654数字 6(digit six)
755数字 7(digit seven)
856数字 8(digit eight)
957数字 9(digit nine)
:58冒号(colon)
;59分号(semicolon)
<60&lt;小于号(less-than sign)
=61等于号(equals sign)
>62&gt;大于号(greater-than sign)
?63问号(question mark)
@64@ 符号(commercial at)
A65拉丁文大写字母 A
B66拉丁文大写字母 B
C67拉丁文大写字母 C
D68拉丁文大写字母 D
E69拉丁文大写字母 E
F70拉丁文大写字母 F
G71拉丁文大写字母 G
H72拉丁文大写字母 H
I73拉丁文大写字母 I
J74拉丁文大写字母 J
K75拉丁文大写字母 K
L76拉丁文大写字母 L
M77拉丁文大写字母 M
N78拉丁文大写字母 N
O79拉丁文大写字母 O
P80拉丁文大写字母 P
Q81拉丁文大写字母 Q
R82拉丁文大写字母 R
S83拉丁文大写字母 S
T84拉丁文大写字母 T
U85拉丁文大写字母 U
V86拉丁文大写字母 V
W87拉丁文大写字母 W
X88拉丁文大写字母 X
Y89拉丁文大写字母 Y
Z90拉丁文大写字母 Z
[91左方括号(left square bracket)
\92反斜线(reverse solidus)
]93右方括号(right square bracket)
^94插入符号(circumflex accent)
_95下划线(low line)
`96重音符(grave accent)
a97拉丁文小写字母 a
b98拉丁文小写字母 b
c99拉丁文小写字母 c
d100拉丁文小写字母 d
e101拉丁文小写字母 e
f102拉丁文小写字母 f
g103拉丁文小写字母 g
h104拉丁文小写字母 h
i105拉丁文小写字母 i
j106拉丁文小写字母 j
k107拉丁文小写字母 k
l108拉丁文小写字母 l
m109拉丁文小写字母 m
n110拉丁文小写字母 n
o111拉丁文小写字母 o
p112拉丁文小写字母 p
q113拉丁文小写字母 q
r114拉丁文小写字母 r
s115拉丁文小写字母 s
t116拉丁文小写字母 t
u117拉丁文小写字母 u
v118拉丁文小写字母 v
w119拉丁文小写字母 w
x120拉丁文小写字母 x
y121拉丁文小写字母 y
z122拉丁文小写字母 z
{123左花括号(left curly bracket)
|124竖线(vertical line)
}125右花括号(right curly bracket)
~126波浪线(tilde)
127未使用(NOT USED)
128&euro;欧元符号(euro sign)
129未使用(NOT USED)
130&sbquo;下单引号(single low-9 quotation mark)
ƒ131&fnof;带钩的拉丁文小写字母 f
132&bdquo;下双引号(double low-9 quotation mark)
133&hellip;水平省略号(horizontal ellipsis)
134&dagger;剑号(dagger)
135&Dagger;双剑号(double dagger)
ˆ136&circ;修饰字母抑扬音(modifier letter circumflex accent)
137&permil;千分比符号(per mille sign)
Š138&Scaron;带有 caron 的拉丁文大写字母 S
139&lsaquo;左单角引号(single left-pointing angle quotation mark)
Œ140&OElig;拉丁文大写连字 OE
141未使用(NOT USED)
Ž142&Zcaron;带有 caron 的拉丁文大写字母 Z
143未使用(NOT USED)
144未使用(NOT USED)
'145&lsquo;左单引号(left single quotation mark)
'146&rsquo;右单引号(right single quotation mark)
"147&ldquo;左双引号(left double quotation mark)
"148&rdquo;右双引号(right double quotation mark)
149&bull;着重号(bullet)
150&ndash;短破折号/连字符(en dash)
151&mdash;长破折号(em dash)
˜152&tilde;小波浪线(small tilde)
153&trade;贸易标记符号(trade mark sign)
š154&scaron;带有 caron 的拉丁文小写字母 s
155&rsaquo;右单角引号(single right-pointing angle quotation mark)
œ156&oelig;拉丁文小写连字 oe
157未使用(NOT USED)
ž158&zcaron;带有 caron 的拉丁文小写字母 z
Ÿ159&Yuml;带有分音符(diaeresis)的拉丁文大写字母 Y
160&nbsp;不换行空格(no-break space)
¡161&iexcl;倒置感叹号(inverted exclamation mark)
¢162&cent;美分符号(cent sign)
£163&pound;英镑符号(pound sign)
¤164&curren;货币符号(currency sign)
¥165&yen;日元符号(yen sign)
¦166&brvbar;间断的竖杠(broken bar)
§167&sect;小节号(section sign)
¨168&uml;分音符号(diaeresis)
©169&copy;版权所有(copyright sign)
ª170&ordf;阴性序数记号(feminine ordinal indicator)
«171&laquo;左双角引号(left-pointing double angle quotation mark)
¬172&not;否定符号(not sign)
173&shy;软连字符(soft hyphen)
®174&reg;注册商标(registered sign)
¯175&macr;长音符号(macron)
°176&deg;度符号(degree sign)
±177&plusmn;加减号/正负号(plus-minus sign)
²178&sup2;上标 2(superscript two)
³179&sup3;上标 3(superscript three)
´180&acute;尖音符号(acute accent)
µ181&micro;微米符号(micro sign)
182&para;段落符号(pilcrow sign)
·183&middot;中间点(middle dot)
¸184&cedil;变音符号(cedilla)
¹185&sup1;上标 1(superscript one)
º186&ordm;阳性序数记号(masculine ordinal indicator)
»187&raquo;右双角引号(right-pointing double angle quotation mark)
¼188&frac14;1/4 分数(vulgar fraction one quarter)
½189&frac12;1/2 分数(vulgar fraction one half)
¾190&frac34;3/4 分数(vulgar fraction three quarters)
¿191&iquest;倒置问号(inverted question mark)
À192&Agrave;带有重音符号(grave)的拉丁文大写字母 A
Á193&Aacute;带有尖音符号(acute)的拉丁文大写字母 A
Â194&Acirc;带有抑扬音符号(circumflex)的拉丁文大写字母 A
Ã195&Atilde;带有波浪线的拉丁文大写字母 A
Ä196&Auml;带有分音符(diaeresis)的拉丁文大写字母 A
Å197&Aring;带有上圆圈的拉丁文大写字母 A
Æ198&AElig;拉丁文大写字母 AE
Ç199&Ccedil;带有变音符号(cedilla)的拉丁文大写字母 C
È200&Egrave;带有重音符号(grave)的拉丁文大写字母 E
É201&Eacute;带有尖音符号(acute)的拉丁文大写字母 E
Ê202&Ecirc;带有抑扬符号(circumflex)的拉丁文大写字母 E
Ë203&Euml;带有分音符(diaeresis)的拉丁文大写字母 E
Ì204&Igrave;带有重音符号(grave)的拉丁文大写字母 I
Í205&Iacute;带有尖音符号(acute)的拉丁文大写字母 I
Î206&Icirc;带有抑扬音符号(circumflex)的拉丁文大写字母 I
Ï207&Iuml;带有分音符(diaeresis)的拉丁文大写字母 I
Ð208&ETH;拉丁文大写字母 Eth
Ñ209&Ntilde;带有波浪线的拉丁文大写字母 N
Ò210&Ograve;带有重音符号(grave)的拉丁文大写字母 O
Ó211&Oacute;带有尖音符号(acute)的拉丁文大写字母 O
Ô212&Ocirc;带有抑扬音符号(circumflex)的拉丁文大写字母 O
Õ213&Otilde;带有波浪线的拉丁文大写字母 O
Ö214&Ouml;带有分音符(diaeresis)的拉丁文大写字母 O
×215&times;乘号(multiplication sign)
Ø216&Oslash;带有删除线的拉丁文大写字母 O
Ù217&Ugrave;带有重音符号(grave)的拉丁文大写字母 U
Ú218&Uacute;带有尖音符号(acute)的拉丁文大写字母 U
Û219&Ucirc;带有抑扬音符号(circumflex)的拉丁文大写字母 U
Ü220&Uuml;带有分音符(diaeresis)的拉丁文大写字母 U
Ý221&Yacute;带有尖音符号(acute)的拉丁文大写字母 Y
Þ222&THORN;拉丁文大写字母 Thorn
ß223&szlig;拉丁文小写字母 sharp s
à224&agrave;带有重音符号(grave)的拉丁文小写字母 a
á225&aacute;带有尖音符号(acute)的拉丁文小写字母 a
â226&acirc;带有抑扬音符号(circumflex)的拉丁文小写字母 a
ã227&atilde;带有波浪线的拉丁文小写字母 a
ä228&auml;带有分音符(diaeresis)的拉丁文小写字母 a
å229&aring;带有上圆圈的拉丁文小写字母 a
æ230&aelig;拉丁文小写字母 ae
ç231&ccedil;带有变音符号(cedilla)的拉丁文小写字母 c
è232&egrave;带有重音符号(grave)的拉丁文小写字母 e
é233&eacute;带有尖音符号(acute)的拉丁文小写字母 e
ê234&ecirc;带有抑扬音符号(circumflex)的拉丁文小写字母 e
ë235&euml;带有分音符(diaeresis)的拉丁文小写字母 e
ì236&igrave;带有重音符号(grave)的拉丁文小写字母 i
í237&iacute;带有尖音符号(acute)的拉丁文小写字母 i
î238&icirc;带有抑扬音符号(circumflex)的拉丁文小写字母 i
ï239&iuml;带有分音符(diaeresis)的拉丁文小写字母 i
ð240&eth;拉丁文小写字母 eth
ñ241&ntilde;带有波浪线的拉丁文小写字母 n
ò242&ograve;带有重音符号(grave)的拉丁文小写字母 o
ó243&oacute;带有尖音符号(acute)的拉丁文小写字母 o
ô244&ocirc;带有抑扬音符号(circumflex)的拉丁文小写字母 o
õ245&otilde;带有波浪线的拉丁文小写字母 o
ö246&ouml;带有分音符(diaeresis)的拉丁文小写字母 o
÷247&divide;除号(division sign)
ø248&oslash;带有删除线的拉丁文小写字母 o
ù249&ugrave;带有重音符号(grave)的拉丁文小写字母 u
ú250&uacute;带有尖音符号(acute)的拉丁文小写字母 u
û251&ucirc;带有抑扬音符号(circumflex)的拉丁文小写字母 u
ü252&uuml;带有分音符(diaeresis)的拉丁文小写字母 u
ý253&yacute;带有尖音符号(acute)的拉丁文小写字母 y
þ254&thorn;拉丁文小写字母 thorn
ÿ255&yuml;带有分音符(diaeresis)的拉丁文小写字母 y

ANSI 控制字符

ANSI 控制字符(00-31,加上 127)最初被设计用来控制诸如打印机和磁带驱动器之类的硬件设备。

控制字符(除了水平制表符、换行、回车之外)在 HTML 文档中不起任何作用。

字符编号描述
NUL00空字符(null character)
SOH01标题开始(start of header)
STX02正文开始(start of text)
ETX03正文结束(end of text)
EOT04传输结束(end of transmission)
ENQ05请求(enquiry)
ACK06收到通知/响应(acknowledge)
BEL07响铃(bell)
BS08退格(backspace)
HT09水平制表符(horizontal tab)
LF10换行(line feed)
VT11垂直制表符(vertical tab)
FF12换页(form feed)
CR13回车(carriage return)
SO14不用切换(shift out)
SI15启用切换(shift in)
DLE16数据链路转义(data link escape)
DC117设备控制 1(device control 1)
DC218设备控制 2(device control 2)
DC319设备控制 3(device control 3)
DC420设备控制 4(device control 4)
NAK21拒绝接收/无响应(negative acknowledge)
SYN22同步空闲(synchronize)
ETB23传输块结束(end transmission block)
CAN24取消(cancel)
EM25已到介质末端/介质存储已满(end of medium)
SUB26替补/替换(substitute)
ESC27溢出/逃离/取消(escape)
FS28文件分隔符(file separator)
GS29组分隔符(group separator)
RS30记录分隔符(record separator)
US31单元分隔符(unit separator)
DEL127删除(delete)

如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!

得小时候刚学电脑时还是使用MS-DOS作业系统,家人帮我买了一本厚重的参考书,里头收录所有DOS指令,非常详尽,而这本书也对我产生不小影响,我知道学习任何东西都必须要有正确的参考手册,才能够让学习过程更全面扎实。虽然网路上有许多教学文件,但碍于篇幅都只能够以最短捷径来帮你达到目的,对于一些较为复杂的内容来说,这些快速教学并不能让你「真正」学会,因此最好还是准备参考手册。

本文要推荐的「HTML Reference」网站很不一样,这个线上参考手册收录完整HTML语法教学,依照英文字母顺序排列,使用者可以很在这里检索某个HTML标签详细使用方法,很多可能不在我们平常会经常使用的范围(例如一些语法设定方式),但如果你想知道语法要怎么写才正确,或者想从基础开始学习HTML程式码,请记得将这网站加入书签。

我记得早先在学习制作HTML网页时也有「首页制作百宝箱」可以参考,HTML Reference大概就是比较进阶、内容较新较全面的首页百宝箱吧!不仅如此,网站还提供实际范例供使用者参考,也能一键点击快速复制语法,对学习来说绝对大有帮助。虽然HTML语法不是每个人都会碰到,但书到用时方恨少,假如你的工作与它相关,不妨找时间恶补一下。

网站名称:HTML Reference
网站链结:http://htmlreference.io/

使用教学

第一步

HTMLReference.io网站分成几个部分,最上方Collections收录一些主题,包括常用的基本元素、表单、列表、HTML5语意标签(Semantic)和表格,中间有个搜寻功能可快速查询你要找的语法,最重要的就是底下依照英文开头排序的所有HTML语法,点击可开启详细介绍。

第二步

Collection 会依照语法类别个别汇整,例如说到表单就会用到form、input、textarea、button 这些标签语法,相信大家不陌生,但你可能不知道每个语法有这么多种数值可以设定,这个网站最好的地方就在于它会把所有可以使用的设定参数列出来,并提供你实际使用范例和说明。

如果你已经开始使用HTML5,对于一些语意标签应该不陌生,HTML Reference 也将这些标签说明及范例列在 Semantic Collection。

第三步

回到HTML Reference 首页,前面有提到首页列出所有HTML 语法标签,依照字母排列,当然使用者也能透过搜寻功能来快速查找,例如下图是a 也就是超链结语法的使用教学,可以看到目标链结(href)参数是必须,网站还列出几种不同的写法和用法。

比较可惜的是网站目前只有提供英文版,可能要有一些基础的英文能力才能比较容易理解,但按照网站编排方式,就算没有看说明应该也能从范例略知一二。

第四步

HTML Reference网站提供的范例、语法都能快速一键复制,只要把滑鼠游标移动上去,点选后就能自动复制,非常方便!记得以前在学习写网页时都要去手动复制参考资料上的范例或语法教学,不但很浪费时间,还必须注意有没有弄错段落,在这里就不会遇到相同问题。


avaScript 是世界上最流行的编程语言之一。

我相信这是您第一个绝佳选择的编程语言。

我们主要使用JavaScript来创建:

  • 网站
  • 网络应用
  • 使用 Node.js 的服务器端应用程序

我们的网站:https://www.icoderoad.com

但 JavaScript 不限于这些东西,它还可以用于:

  • 使用 React Native 等工具创建移动应用程序
  • 为微控制器和物联网创建程序
  • 创建智能手表应用程序

它基本上可以做任何事情。它非常流行,以至于出现的所有新事物都会在某个时候能集成某种 JavaScript。

JavaScript 是一种编程语言,它是:

  • 高级:它提供了允许您忽略运行它的机器细节的抽象。它使用垃圾收集器自动管理内存,因此您可以专注于代码而不是像 C 等其他语言那样管理内存,并提供许多允许您处理非常强大的变量和对象的结构。
  • 动态:与静态编程语言相反,动态语言在运行时执行静态语言在编译时所做的许多事情。这有利有弊,它为我们提供了强大的功能,如动态类型、后期绑定、反射、函数式编程、对象运行时更改、闭包等等。如果您不知道这些事情,请不要担心——在课程结束时您会了解全部相关知识。
  • 动态类型:不强制变量类型。您可以将任何类型重新分配给变量,例如,将整数分配给包含字符串的变量。
  • 松散类型:与强类型相反,松散(或弱)类型语言不强制对象的类型,允许更大的灵活性但拒绝我们类型安全和类型检查(TypeScript - 它建立在 JavaScript 之上 - 提供)
  • 解释:它通常被称为解释语言,这意味着它在程序运行之前不需要编译阶段,这与 C、Java 或 Go 等语言不同。实际上,出于性能原因,浏览器会在执行 JavaScript 之前对其进行编译,但这对您来说是透明的——不涉及额外的步骤。
  • 多范式:该语言不强制执行任何特定的编程范式,例如强制使用面向对象编程的 Java 或强制使用命令式编程的 C。您可以使用面向对象的范型、使用原型和新的(从 ES6 开始)类语法来编写 JavaScript。您可以使用函数式编程风格、一流的函数,甚至是命令式风格(类 C)来编写 JavaScript。

如您所知,JavaScript 与 Java 无关,这是一个糟糕的名称选择,但我们必须忍受它。

手册摘要

  1. 历史
  2. JavaScript
  3. 语法简介
  4. 分号
  5. 值域
  6. 变量
  7. 类型
  8. 表达式
  9. 运算符
  10. 优先规则
  11. 比较运算符
  12. 条件语句
  13. 数组
  14. 字符串
  15. 循环
  16. 函数
  17. 箭头函数
  18. 对象
  19. 对象属性
  20. 对象方法
  21. 继承
  22. 异步编程和回调
  23. Promise
  24. 异步和等待
  25. 变量范围
  26. 结论

历史

JavaScript 创建于 1995 年,自其不起眼的开始以来已经走过了很长的路。

它是 Web 浏览器原生支持的第一种脚本语言,因此它获得了优于任何其他语言的竞争优势,今天它仍然是我们可以用来构建 Web 应用程序的唯一脚本语言。

也存在其他语言,但都必须编译为 JavaScript - 或者最近编译为 WebAssembly,但这是另一回事。

最初 JavaScript 还没有今天那么强大,它主要用于花哨的动画和当时被称为动态 HTML奇迹。

随着 Web 平台(并且继续要求)不断增长的需求,JavaScript也有责任发展,以适应世界上使用最广泛的生态系统之一的需求。

JavaScript 现在也在浏览器之外广泛使用。Node.js 在过去几年的兴起开启了后端开发的大门,曾经是 Java、Ruby、Python、PHP 和更传统的服务器端语言的领域。

JavaScript 现在也是支持数据库和更多应用程序的语言,甚至可以开发嵌入式应用程序、移动应用程序、电视应用程序等等。最初是浏览器中的一种小语言,现在是世界上最流行的语言。

JavaScript

有时很难将 JavaScript 与使用它的环境的特性区分开来。

例如,console.log()您可以在许多代码示例中找到的那行代码不是 JavaScript。相反,它是浏览器中提供给我们的庞大 API 库的一部分。

同样,在服务器上,有时很难将 JavaScript 语言功能与 Node.js 提供的 API 分开。

React 或 Vue 是否提供特定功能?还是通常所说的“普通 JavaScript”?

在本手册中,我将讨论 JavaScript 这种语言。

不会因外部生态系统提供的事物而使您的学习过程复杂化。

JavaScript 语法简介

在这个简短的介绍中,我想告诉大家 5 个概念:

  • 空白
  • 区分大小写
  • 文字
  • 身份标识
  • 注释

空白空间

JavaScript 不认为空白是有意义的。至少在理论上,可以以您可能喜欢的任何方式添加空格和换行符。

在实践中,您很可能会保持明确定义的样式并遵守人们常用的样式,并使用 linter 或Prettier等样式工具强制执行此操作。

例如,我总是为每个缩进使用 2 个空格字符。

区分大小写

JavaScript 区分大小写。一个名为something的变量不同于Something.

其它标识符也是如此。

字面量

我们将字面量定义为在源代码中写入的值,例如,数字、字符串、布尔值或更高级的构造,如 Object 字面量 或 Array 字面量:

5
'Test'
true
['a', 'b']
{color: 'red', shape: 'Rectangle'}

标识符

标识符是可用于标识变量、函数或对象的字符序列。它可以以字母、美元符号$或下划线开头_,并且可以包含数字。使用 Unicode,字母可以是任何允许的字符,例如,表情符号 ?。

Test
test
TEST
_test
Test1
$test

美元符号通常用于引用 DOM 元素。

有些名称是为 JavaScript 内部使用而保留的关键字,我们不能将它们用作标识符。

注释

注释是任何编程语言中最重要的部分之一。它们很重要,因为它们让我们可以注释代码并添加重要信息,否则其他人(或我们自己)阅读代码时无法获得这些信息。

在 JavaScript 中,我们可以使用//进行注释. JavaScript 解释器不会将//之后的所有内容视为代码。

像这样:

// a comment
true //another comment

另一种类型的注释是多行注释。它以 /*开头和*/结尾。

两者之间的所有内容均被认为注释:

/* some kind
of 
comment 

*/

分号

JavaScript 程序中的每一行都可选地使用分号终止。

我说可选,因为 JavaScript 解释器足够聪明,可以为您引入分号。

在大多数情况下,您可以在程序中完全省略分号,甚至无需考虑它。

这个事实是非常有争议的。一些开发人员将始终使用分号,而另一些开发人员则从不使用分号,并且您总是会发现使用分号的代码和不使用分号的代码。

我个人的偏好是避免使用分号,所以我在书中的示例不会包含它们。

值类型

字符串hello是一个。像一个数字12是一个

hello和12是值。string 和 number是这些值的类型

类型是值的种类,它的类别。JavaScript 中有许多不同的类型,稍后我们将详细讨论它们。每种类型都有自己的特点。

当我们需要一个值的引用时,我们将它分配给一个变量。变量可以有一个名称,而值是存储在变量中的内容,因此我们稍后可以通过变量名称访问该值。

变量

变量是分配给标识符的值,因此您可以稍后在程序中引用和使用它。

这是因为 JavaScript 是弱类型的,这是您经常听到的概念。

必须先声明变量,然后才能使用它。

我们有两种主要的方式来声明变量。首先是使用const

const a = 0

第二种方法是使用let

let a = 0

有什么不同?

const定义对值的常量引用。这意味着不能更改引用。不能为其重新分配新值。

使用letp声明的变量可以为其分配一个新值。

例如,不能这样做:

const a = 0
a = 1

因为会得到一个错误:TypeError: Assignment to constant variable..

另一方面,可以使用letp声明变量:

let a = 0
a = 1

const并不像 C 等其他语言那样表示“常量”。特别是,这并不意味着该值不能改变 - 这意味着它不能被重新分配。如果变量指向一个对象或数组(我们稍后会看到更多关于对象和数组的内容),那么对象或数组的内容可以自由改变。

const变量必须在声明时初始化:

const a = 0

let值可以稍后初始化:

let a
a = 0

可以在同一语句中一次声明多个变量:

const a = 1, b = 2
let c = 1, d = 2

但是不能多次重新声明同一个变量:

let a = 1
let a = 2

否则会收到“重复声明”错误。

我的建议是始终使用const声明产量,仅在您知道需要为该变量重新分配值时使用let。为什么?因为我们的代码的功能越少越好。如果我们知道一个值不能被重新分配,那么它就少了一个错误来源。

既然我们已经了解了如何使用constlet,我想提一下var

直到 2015 年,var这是我们在 JavaScript 中声明变量的唯一方法。今天,现代代码库很可能只使用constletp声明变量。我在这篇文章中详细介绍了一些基本差异,但如果你刚刚开始学习JavaScript ,可能不会关心它们。只需使用constletp声明变量即可。

类型

JavaScript 中的变量没有附加任何类型。

它们是无类型的。

将具有某种类型的值分配给变量后,可以稍后重新分配该变量以承载任何其他类型的值,而不会出现任何问题。

在 JavaScript 中,我们有 2 种主要类型:原始类型对象类型

原始类型

原始类型是

  • 数字
  • 字符串
  • 布尔值
  • 符号

还有两种特殊类型:nullundefined

对象类型

任何不是原始类型(字符串、数字、布尔值、null 或未定义)的值都是对象

对象类型有属性,也有可以作用于这些属性的方法。

稍后我们将更多地讨论对象。

表达式

表达式是 JavaScript 引擎可以评估并返回值的单个 JavaScript 代码单元。

表达式的复杂性可能会有所不同。

我们从非常简单的开始,称为初级表达式:

2
0.02
'something'
true
false
this //the current scope
undefined
i //where i is a variable or a constant

算术表达式是接受一个变量和一个运算符的表达式(稍后将详细介绍运算符),并产生一个数字:

1 / 2
i++
i -= 2
i * 2

字符串表达式是产生字符串的表达式:

'A ' + 'string'

逻辑表达式使用逻辑运算符并解析为布尔值:

a && b
a || b
!a

更高级的表达式涉及对象、函数和数组,我稍后会介绍它们。

运算符

运算符允许获得两个简单的表达式并将它们组合成一个更复杂的表达式。

我们可以根据它们使用的操作数对运算符进行分类。一些运算符使用 1 个操作数。大多数使用 2 个操作数。只有一个运算符可以处理 3 个操作数。

在对运算符的第一次介绍中,我们将介绍您最可能熟悉的运算符:具有 2 个操作数的运算符。

我在谈论变量时已经介绍了一个:赋值运算符=。您用=为变量赋值:

let b = 2

现在让我们介绍另一组在基础数学中已经熟悉的二元运算符。

加法运算符 (+)

const three = 1 + 2
const four = three + 1

如果使用字符串,+运算符也会进行字符串连接,因此请注意:

const three = 1 + 2
three + 1 // 4
'three' + 1 // three1

减法运算符 (-)

const two = 4 - 2

除法运算符 (/)

返回第一个运算符和第二个运算符的商:

const result = 20 / 5 //result === 4
const result = 20 / 7 //result === 2.857142857142857

如果除以零,JavaScript 不会引发任何错误,但会返回该Infinity值(或者-Infinity如果该值为负数)。

1 / 0 //Infinity
-1 / 0 //-Infinity

余数运算符 (%)

在许多用例中,余数是非常有用的计算:

const result = 20 % 5 //result === 0
const result = 20 % 7 //result === 6

NaN余数总是为零,这是一个表示“非数字”的特殊值:

1 % 0 //NaN
-1 % 0 //NaN

乘法运算符 (*)

将两个数字相乘

1 * 2 //2
-1 * 2 //-2

幂运算符 (**)

将第一个操作数提高到第二个操作数的幂

1 ** 2 //1
2 ** 1 //2
2 ** 2 //4
2 ** 8 //256
8 ** 2 //64

优先规则

在同一行中包含多个运算符的每个复杂语句都会引入优先级问题。

举个例子:

let a = 1 * 2 + 5 / 2 % 2

结果是 2.5,但为什么呢?

哪些操作先执行,哪些需要等待?

某些操作比其他操作具有更高的优先级。下表列出了优先规则:

操作员

描述

同一级别的操作(如+-)按照它们被发现的顺序从左到右执行。

按照这些规则,上面的操作可以这样解决:

let a = 1 * 2 + 5 / 2 % 2
let a = 2 + 5 / 2 % 2
let a = 2 + 2.5 % 2
let a = 2 + 0.5
let a = 2.5

比较运算符

在赋值和数学运算符之后,我要介绍的第三组运算符是条件运算符。

可以使用以下运算符来比较两个数字或两个字符串。

比较运算符总是返回一个布尔值,即truefalse)。

这些是不等式比较运算符

  • <意思是“小于”
  • <=意思是“小于或等于”
  • >意思是“大于”
  • >=意思是“大于或等于”

例子:

let a = 2
a >= 1 //true

除此之外,我们还有 4 个相等运算符。它们接受两个值,并返回一个布尔值:

  • ===检查相等性
  • !==检查不平等

请注意,我们在 JavaScript 中也有==!=,但我强烈建议只使用===和!==,因为它们可以防止一些微妙的问题。

条件语句

有了比较运算符,我们就可以讨论条件语句了。

if语句用于使程序根据表达式求值的结果选择一条路径或另一条路径。

这是最简单的例子,它总是执行:

if (true) {
  //do something
}

相反,这将永远不会执行:

if (false) {
  //do something (? never ?)
}

条件检查传递给它的表达式的真值或假值。如果传递一个数字,除非它是 0,否则它总是计算为真。如果传递一个字符串,它总是计算为真,除非它是一个空字符串。这些是将类型转换为布尔值的一般规则。

注意到花括号了吗?这称为,它用于对不同语句的列表进行分组。

块可以放在任何可以有单个语句的地方。如果在条件句之后有一条语句要执行,可以省略该块,只写语句:

if (true) doSomething()

但我总是喜欢用花括号,这样的语句更清楚。

else可以为if语句提供第二部分。

if如果条件为假,则附加将要执行的语句:

if (true) {
  //do something
} else {
  //do something else
}

由于else接受一个语句,可以在其中嵌套另一个 if/else 语句:

if (a === true) {
  //do something
} else if (b === true) {
  //do something else
} else {
  //fallback
}

数组

数组是元素的集合。

JavaScript 中的数组本身并不是一种类型

数组是对象

我们可以通过以下两种不同的方式初始化一个空数组:

const a = []
const a = Array()

第一种是使用数组文字语法。第二个使用 Array 内置函数。

您可以使用以下语法预填充数组:

const a = [1, 2, 3]
const a = Array.of(1, 2, 3)

数组可以保存任何值,甚至是不同类型的值:

const a = [1, 'Flavio', ['a', 'b']]

由于我们可以将数组添加到数组中,因此我们可以创建多维数组,这些数组有非常有用的应用(例如矩阵):

const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]

matrix[0][0] //1
matrix[2][0] //7

可以通过引用从零开始的索引来访问数组的任何元素:

a[0] //1
a[1] //2
a[2] //3

可以使用以下语法使用一组值初始化一个新数组,该语法首先初始化一个由 12 个元素组成的数组,并用数字0填充每个元素:

Array(12).fill(0)

可以通过检查其length属性来获取数组中元素的数量:

const a = [1, 2, 3]
a.length //3

请注意,可以设置数组的长度。如果分配的数字大于阵列当前容量,则不会发生任何事情。如果分配较小的数字,则在该位置切割数组:

const a = [1, 2, 3]
a //[ 1, 2, 3 ]
a.length = 2
a //[ 1, 2 ]

如何将项目添加到数组

我们可以使用push()方法在数组末尾添加一个元素:

a.push(4)

我们可以使用unshift()方法在数组的开头添加一个元素:

a.unshift(0)
a.unshift(-2, -1)

如何从数组中删除一个项目

我们可以使用pop()方法从数组末尾删除一个项目:

a.pop()

我们可以使用shift()方法从数组的开头删除一个项目:

a.shift()

如何连接两个或多个数组

可以使用concat()方法连接多个数组:

const a = [1, 2]
const b = [3, 4]
const c = a.concat(b) //[1,2,3,4]
a //[1,2]
b //[3,4]

还可以通过使用扩展运算符 ( ...):

const a = [1, 2]
const b = [3, 4]
const c = [...a, ...b]
c //[1,2,3,4]

如何在数组中查找特定项

可以使用数组的find()方法:

a.find((element, index, array) => {
  //return true or false
})

返回第一个返回 true 的项目,如果未找到该元素则返回undefined

一个常用的语法是:

a.find(x => x.id === my_id)

上面的行将返回数组中id === my_id的第一个元素。

findIndex()find()类似,但返回第一个为true 的项目的索引,如果未找到,则返回undefined

a.findIndex((element, index, array) => {
  //return true or false
})

另一种方法是includes()

a.includes(value)

a如果包含则返回真value

a.includes(value, i)

如果在 position 之后包含value,则返回 true 。

字符串

字符串是一个字符序列。

它也可以定义为字符串文字,用引号或双引号括起来:

'A string'
"Another string"

我个人一直更喜欢单引号,只在 HTML 中使用双引号来定义属性。

将字符串值分配给如下变量:

const name = 'Flavio'

length可以使用它的属性来确定字符串的长度:

'Flavio'.length //6
const name = 'Flavio'
name.length //6

''这是一个空字符串. 它的长度属性为 0:

''.length //0

可以使用+运算符连接两个字符串:

"A " + "string"

可以使用+运算符来插入变量:

const name = 'Flavio'
"My name is " + name //My name is Flavio

定义字符串的另一种方法是使用在反引号内定义的模板文字。它们对于使多行字符串更简单特别有用。使用单引号或双引号,您无法轻松定义多行字符串 - 您需要使用转义字符。

使用反引号打开模板文字后,只需按 Enter 键即可创建一个新行,没有特殊字符,并且按原样呈现:

const string = `Hey
this

string
is awesome!`

模板文字也很棒,因为它们提供了一种将变量和表达式插入字符串的简单方法。

您可以使用以下${...}语法来执行此操作:

const var = 'test'
const string = `something ${var}` 
//something test

${}里面可以添加任何东西,甚至是表达式:

const string = `something ${1 + 2 + 3}`
const string2 = `something 
  ${foo() ? 'x' : 'y'}`

循环

循环是 JavaScript 的主要控制结构之一。

使用循环,我们可以自动化并重复一段代码,无论我们希望它运行多少次,甚至无限期地运行。

JavaScript 提供了许多迭代循环的方法。

我想专注于3种方式:

  • while 循环
  • for 循环
  • for..of 循环

while

while 循环是 JavaScript 提供给我们的最简单的循环结构。

我们在关键字之后添加一个条件while,并提供一个运行块,直到条件评估为true

例子:

const list = ['a', 'b', 'c']
let i = 0
while (i < list.length) {
  console.log(list[i]) //value
  console.log(i) //index
  i = i + 1
}

您可以使用关键字中断while循环,如下所示:break

while (true) {
  if (somethingIsTrue) break
}

如果您决定在循环中间跳过当前迭代,则可以使用以下命令跳转到下一个迭代continue

while (true) {
  if (somethingIsTrue) continue

  //do something else
}

非常类似于while,我们有do..while循环。它与 基本相同while,只是在执行代码块之后评估条件。

这意味着块总是至少执行一次

例子:

const list = ['a', 'b', 'c']
let i = 0
do {
  console.log(list[i]) //value
  console.log(i) //index
  i = i + 1
} while (i < list.length)

for

JavaScript 中第二个非常重要的循环结构是for 循环

我们使用for关键字并传递一组 3 条指令:初始化、条件和增量部分。

例子:

const list = ['a', 'b', 'c']

for (let i = 0; i < list.length; i++) {
  console.log(list[i]) //value
  console.log(i) //index
}

就像while循环一样,您可以使用 中断for循环,并且可以使用 快进到循环break的下一次迭代。forcontinue

for...of

这个循环是相对较新的(2015 年引入),它是for循环的简化版本:

const list = ['a', 'b', 'c']

for (const value of list) {
  console.log(value) //value
}

函数

在任何中等复杂的 JavaScript 程序中,一切都发生在函数内部。

函数是 JavaScript 的核心,必不可少的部分。

什么是函数?

函数是一个自包含的代码块。

这是一个函数声明

function getData() {
  // do something
}

一个函数可以通过调用它随时运行,如下所示:

getData()

一个函数可以有一个或多个参数:

function getData() {
  //do something
}

function getData(color) {
  //do something
}

function getData(color, age) {
  //do something
}

当我们可以传递参数时,我们调用传递参数的函数:

function getData(color, age) {
  //do something
}

getData('green', 24)
getData('black')

请注意,在第二次调用中,我将black字符串参数作为color参数传递,但没有传递age. 在这种情况下,age函数内部是undefined.

我们可以使用以下条件检查值是否未定义:

function getData(color, age) {
  //do something
  if (typeof age !== 'undefined') {
    //...
  }
}

typeof是一个一元运算符,它允许我们检查变量的类型。

您也可以通过以下方式检查:

function getData(color, age) {
  //do something
  if (age) {
    //...
  }
}

虽然如果ageis或空字符串null,条件也将为真。0

您可以为参数设置默认值,以防它们未传递:

function getData(color = 'black', age = 25) {
  //do something
}

您可以将任何值作为参数传递:数字、字符串、布尔值、数组、对象以及函数。

一个函数有一个返回值。默认情况下,函数返回undefined,除非您添加return带有值的关键字:

function getData() {
  // do something
  return 'hi!'
}

我们可以在调用函数时将此返回值分配给变量:

function getData() {
  // do something
  return 'hi!'
}

let result = getData()

result现在保存一个带有hi!值的字符串。

您只能返回一个值。

要返回多个值,您可以返回一个对象或数组,如下所示:

function getData() {
  return ['Flavio', 37]
}

let [name, age] = getData()

函数可以在其他函数中定义:

const getData = () => {
  const dosomething = () => {}
  dosomething()
  return 'test'
}

嵌套函数不能从封闭函数的外部调用。

你也可以从一个函数中返回一个函数。

箭头函数

箭头函数是最近对 JavaScript 的介绍。

它们经常被用来代替我在前一章中描述的“常规”函数。您会发现这两种形式随处可见。

从视觉上看,它们允许您使用更短的语法编写函数,来自:

function getData() {
  //...
}

() => {
  //...
}

但是..注意我们这里没有名字。

箭头函数是匿名的。我们必须将它们分配给一个变量。

我们可以将常规函数分配给变量,如下所示:

let getData = function getData() {
  //...
}

当我们这样做时,我们可以从函数中删除名称:

let getData = function() {
  //...
}

并使用变量名调用函数:

let getData = function() {
  //...
}
getData()

这与我们对箭头函数所做的事情相同:

let getData = () => {
  //...
}
getData()

如果函数体只包含一条语句,则可以省略括号并将所有内容写在一行上:

const getData = () => console.log('hi!')

参数在括号中传递:

const getData = (param1, param2) => 
  console.log(param1, param2)

如果您有一个(并且只有一个)参数,则可以完全省略括号:

const getData = param => console.log(param)

箭头函数允许您有一个隐式返回 - 无需使用return关键字即可返回值。

它在函数体中有一行语句时起作用:

const getData = () => 'test'

getData() //'test'

与常规函数一样,我们可以为参数设置默认值,以防它们未传递:

const getData = (color = 'black', 
                 age = 2) => {
  //do something
}

和常规函数一样,我们只能返回一个值。

箭头函数还可以包含其他箭头函数,甚至是常规函数。

这两种函数非常相似,所以你可能会问为什么要引入箭头函数。与常规函数的最大区别在于它们用作对象方法时。这是我们将很快研究的事情。

对象

任何不是原始类型(字符串、数字、布尔值、符号、null 或未定义)的值都是对象

下面是我们定义对象的方式:

const car = {

}

这是对象字面量语法,它是 JavaScript 中最好的东西之一。

您还可以使用以下new Object语法:

const car = new Object()

另一种语法是使用Object.create()

const car = Object.create()

new您还可以在带有大写字母的函数之前使用关键字初始化对象。此函数用作该对象的构造函数。在那里,我们可以初始化我们作为参数接收的参数,以设置对象的初始状态:

function Car(brand, model) {
  this.brand = brand
  this.model = model
}

我们使用以下方法初始化一个新对象:

const myCar = new Car('Ford', 'Fiesta')
myCar.brand //'Ford'
myCar.model //'Fiesta'

对象总是通过引用传递

如果您为一个变量分配与另一个变量相同的值,如果它是像数字或字符串这样的原始类型,则它们是按值传递的:

举个例子:

let age = 36
let myAge = age
myAge = 37
age //36
const car = {
  color: 'blue'
}
const anotherCar = car
anotherCar.color = 'yellow'
car.color //'yellow'

即使是数组或函数,在底层也是对象,因此了解它们的工作原理非常重要。

对象属性

对象具有属性,这些属性由与值关联的标签组成。

属性的值可以是任何类型,这意味着它可以是数组、函数,甚至可以是对象,因为对象可以嵌套其他对象。

这是我们在上一章看到的对象字面量语法:

const car = {

}

我们可以color这样定义一个属性:

const car = {
  color: 'blue'
}

在这里,我们有一个car对象,其属性名为color,其值为blue

标签可以是任何字符串,但要注意特殊字符 - 如果我想在属性名称中包含一个无效的字符作为变量名,我将不得不在它周围使用引号:

const car = {
  color: 'blue',
  'the color': 'blue'
}

无效的变量名字符包括空格、连字符和其他特殊字符。

如您所见,当我们有多个属性时,我们用逗号分隔每个属性。

我们可以使用 2 种不同的语法来检索属性的值。

第一个是点符号

car.color //'blue'

第二个(这是我们唯一可以用于名称无效的属性)是使用方括号:

car['the color'] //'blue'

如果您访问不存在的属性,您将获得以下undefined值:

car.brand //undefined

如前所述,对象可以具有嵌套对象作为属性:

const car = {
  brand: {
    name: 'Ford'
  },
  color: 'blue'
}

在此示例中,您可以使用访问品牌名称

car.brand.name

或者

car['brand']['name']

您可以在定义对象时设置属性的值。

但是您以后可以随时更新它:

const car = {
  color: 'blue'
}

car.color = 'yellow'
car['color'] = 'red'

您还可以向对象添加新属性:

car.model = 'Fiesta'

car.model //'Fiesta'

给定对象

const car = {
  color: 'blue',
  brand: 'Ford'
}

您可以使用从该对象中删除一个属性

delete car.brand

对象方法

我在前一章中谈到了函数。

可以将函数分配给函数属性,在这种情况下,它们称为方法

在这个例子中,start属性分配了一个函数,我们可以使用我们用于属性的点语法来调用它,括号在末尾:

const car = {
  brand: 'Ford',
  model: 'Fiesta',
  start: function() {
    console.log('Started')
  }
}

car.start()

在使用function() {}语法定义的方法中,我们可以通过引用来访问对象实例this

在以下示例中,我们可以使用and访问brandmodel属性值:this.brandthis.model

const car = {
  brand: 'Ford',
  model: 'Fiesta',
  start: function() {
    console.log(`Started 
      ${this.brand} ${this.model}`)
  }
}

car.start()

重要的是要注意常规函数和箭头函数之间的这种区别——this如果我们使用箭头函数,我们就无法访问:

const car = {
  brand: 'Ford',
  model: 'Fiesta',
  start: () => {
    console.log(`Started 
      ${this.brand} ${this.model}`) //not going to work
  }
}

car.start()

这是因为箭头函数没有绑定到对象

这就是为什么经常将常规函数用作对象方法的原因。

方法可以接受参数,如常规函数:

const car = {
  brand: 'Ford',
  model: 'Fiesta',
  goTo: function(destination) {
    console.log(`Going to ${destination}`)
  }
}

car.goTo('Rome')

我们讨论了对象,这是 JavaScript 中最有趣的部分之一。

在本章中,我们将通过介绍类来提升一个层次。

什么是类?它们是一种为多个对象定义通用模式的方法。

让我们看一个人对象:

const person = {
  name: 'Flavio'
}

我们可以创建一个名为Person(注意大写P,使用类时的约定)的类,它有一个name属性:

class Person {
  name
}

现在从这个类中,我们像这样初始化一个flavio对象:

const flavio = new Person()

flavio称为 Person 类的实例

我们可以设置name属性的值:

flavio.name = 'Flavio'

我们可以使用

flavio.name

就像我们对对象属性所做的那样。

类可以保存属性,例如name和方法。

方法是这样定义的:

class Person {
  hello() {
    return 'Hello, I am Flavio'
  }
}

我们可以在类的实例上调用方法:

class Person {
  hello() {
    return 'Hello, I am Flavio'
  }
}
const flavio = new Person()
flavio.hello()

当我们创建一个新的对象实例时,我们可以使用一个特殊的方法 constructor()来初始化类属性。

它是这样工作的:

class Person {
  constructor(name) {
    this.name = name
  }

  hello() {
    return 'Hello, I am ' + this.name + '.'
  }
}

注意我们如何使用this来访问对象实例。

现在我们可以从类中实例化一个新对象,传入一个字符串,当我们调用时,hello我们会得到一条个性化的消息:

const flavio = new Person('flavio')
flavio.hello() //'Hello, I am flavio.'

初始化对象时,将constructor使用传递的任何参数调用该方法。

通常方法是在对象实例上定义的,而不是在类上。

可以定义一个 static 方法以允许它在类上执行:

class Person {
  static genericHello() {
    return 'Hello'
  }
}

Person.genericHello() //Hello

这有时非常有用。

继承

一个类可以扩展另一个类,使用该类初始化的对象继承这两个类的所有方法。

假设我们有一个类Person

class Person {
  hello() {
    return 'Hello, I am a Person'
  }
}

我们可以定义一个新类,Programmer扩展Person

class Programmer extends Person {

}

现在,如果我们用 class 实例化一个新对象Programmer,它就可以访问该hello()方法:

const flavio = new Programmer()
flavio.hello() //'你好, 我是一个人。'

在子类中,可以通过调用 super() 来引用父类方法:

class Programmer extends Person {
  hello() {
    return super.hello() + 
      '我也是一名程序员。'
  }
}

const flavio = new Programmer()
flavio.hello()

上面的程序打印你好,我是一个人。我也是一名程序员。

异步编程和回调

大多数时候,JavaScript 代码是同步运行的。

这意味着执行一行代码,然后执行下一个代码,以此类推。

一切都如您所愿,这是它在大多数编程语言中的工作方式。

但是,有时不能只等待一行代码执行。

不能只等待 2 秒钟来加载一个大文件,然后完全停止程序。

不能只等待下载网络资源后再做其他事情。

JavaScript 通过使用回调解决了这个问题。

如何使用回调的最简单示例之一是使用计时器。计时器不是 JavaScript 的一部分,但它们由浏览器和 Node.js 提供。让我谈谈我们拥有的一个计时器:setTimeout()函数。

setTimeout()函数接受 2 个参数:一个函数和一个数字。该数字是在函数运行之前必须经过的毫秒数。

例子:

setTimeout(() => {
  // 2秒以后执行
  console.log('inside the function')
}, 2000)

包含该console.log('inside the function')行的函数将在 2 秒后执行。

如果在函数之前添加一个 console.log('before') 日志,函数之后添加一个console.log('after')日志:

console.log('before')
setTimeout(() => {
  // runs after 2 seconds
  console.log('inside the function')
}, 2000)
console.log('after')

将在控制台中看到这种情况:

before
after
inside the function

回调函数是异步执行的。

当在浏览器中使用文件系统、网络、事件或 DOM 时,这是一种非常常见的模式。

我提到的所有东西都不是“核心”JavaScript,因此本手册中没有解释它们,但会在https://flaviocopes.com上的其他手册中找到很多示例。

以下是我们如何在代码中实现回调。

我们定义一个接受callback参数的函数,它是一个函数。

当代码准备好调用回调时,我们通过传递结果来调用它:

const doSomething = callback => {
  //do things
  //do things
  const result = /* .. */
  callback(result)
}

使用此函数的代码将像这样使用它:

doSomething(result => {
  console.log(result)
})

Promise

Promise 是处理异步代码的另一种方法。

正如我们在前一章中看到的,通过回调我们将一个函数传递给另一个函数调用,该函数将在函数完成处理时调用。

像这样:

doSomething(result => {
  console.log(result)
})

doSomething()代码结束时,它调用作为参数接收的函数:

const doSomething = callback => {
  //do things
  //do things
  const result = /* .. */
  callback(result)
}

这种方法的主要问题是,如果我们需要在其余代码中使用这个函数的结果,我们所有的代码都必须嵌套在回调中,如果我们必须进行 2-3 次回调,我们输入的是通常定义“回调地狱”,将许多级别的函数缩进到其他函数中:

doSomething(result => {
  doSomethingElse(anotherResult => {
    doSomethingElseAgain(yetAnotherResult => {
      console.log(result)
    })
  }) 
})

Promise 是处理这个问题的一种方法。

而不是这样做:

doSomething(result => {
  console.log(result)
})

我们以这种方式调用基于 Promise 的函数:

doSomething()
  .then(result => {
    console.log(result)
  })

我们首先调用函数,然后我们有一个在函数结束时调用的then()方法。

缩进无关紧要,但为了清晰起见,通常会使用这种样式。

使用以下catch()方法检测错误很常见:

doSomething()
  .then(result => {
    console.log(result)
  })
  .catch(error => {
    console.log(error)
  })

现在,为了能够使用这种语法,doSomething()函数实现必须有点特殊。它必须使用 Promises API。

而不是将其声明为普通函数:

const doSomething = () => {
  
}

我们将它声明为一个 Promise 对象:

const doSomething = new Promise()

我们在 Promise 构造函数中传递一个函数:

const doSomething = new Promise(() => {

})

该函数接收 2 个参数。第一个是我们调用来解析Promise 的函数,第二个是我们调用来拒绝 Promise 的函数。

const doSomething = new Promise(
  (resolve, reject) => {
    
})

解决一个 Promise 意味着成功完成它(这导致then()在任何使用它的地方调用该方法)。

拒绝一个 Promise 味着以一个错误结束它(这导致catch()在任何使用它的地方调用该方法)。

就是这样:

const doSomething = new Promise(
  (resolve, reject) => {
    //some code
    const success = /* ... */
    if (success) {
      resolve('ok')
    } else {
      reject('this error occurred')
    }
  }
)

我们可以将参数传递给我们想要的任何类型的解析和拒绝函数。

异步和等待

异步函数是 Promise 的更高层次的抽象。

一个异步函数返回一个 Promise,如下例所示:

const getData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => 
      resolve('some data'), 2000)
  })
}

任何想要使用此函数的代码都将在函数之前使用 await 关键字:

const data = await getData()

这样做,Promise返回的任何数据都将被分配给data变量。

在我们的例子中,数据是“一些数据”字符串。

有一个特别的警告:每当我们使用await关键字时,我们必须在定义为 async 的函数中这样做。

像这样:

const doSomething = async () => {
  const data = await getData()
  console.log(data)
}

async/await 让我们拥有更简洁的代码和简单的思维模型来处理异步代码。

正如在上面的示例中看到的,我们的代码看起来非常简单。将其与使用Promise或回调函数的代码进行比较。

这是一个非常简单的例子,当代码复杂得多时,主要的好处就会出现。

例如,以下是使用 Fetch API 获取 JSON 资源并使用 Promise 解析它的方法:

const getFirstUserData = () => {
  // get users list
  return fetch('/users.json') 
    // parse JSON
    .then(response => response.json()) 
    // pick first user
    .then(users => users[0]) 
    // get user data
    .then(user => 
      fetch(`/users/${user.name}`)) 
    // parse JSON
    .then(userResponse => response.json()) 
}

getFirstUserData()

这是使用 await/async 提供的相同功能:

const getFirstUserData = async () => {
  // get users list
  const response = await fetch('/users.json') 
  // parse JSON
  const users = await response.json() 
  // pick first user
  const user = users[0] 
  // get user data
  const userResponse = 
    await fetch(`/users/${user.name}`)
  // parse JSON
  const userData = await user.json() 
  return userData
}

getFirstUserData()

变量范围

当我介绍变量时,我谈到了使用constletvar

范围是对程序的一部分可见的变量集。

在 JavaScript 中,我们有全局作用域、块作用域和函数作用域。

如果变量是在函数或块之外定义的,它会附加到全局对象并且它具有全局范围,这意味着它可以在程序的每个部分中使用。

let 、const 和var声明变量之间有一个非常重要的区别。

在函数内部定义的变量var仅在该函数内部可见,类似于函数的参数。

另一方面,定义为const或 let 的变量仅在定义它的内可见。

块是组合成一对花括号的一组指令,就像我们可以在if语句、for循环或函数中找到的指令一样。

重要的是要理解一个块没有为 定义一个新的范围var,但它为let和 const 定义了一个新的范围。

这具有非常实际的意义。

假设在函数的 if 条件内定义了一个 var 变量

function getData() {
  if (true) {
    var data = 'some data'
    console.log(data) 
  }
}

如果调用此函数,ome data打印到控制台。

如果尝试将 console.log(data) 移动到if语句之后,它仍然有效:

function getData() {
  if (true) {
    var data = 'some data'
  }
  console.log(data) 
}

但是如果切换var datalet data

function getData() {
  if (true) {
    let data = 'some data'
  }
  console.log(data) 
}

会得到一个错误:ReferenceError: data is not defined

这是因为var是函数作用域,这里发生了一种特殊的事情,称为提升。简而言之,JavaScript 在运行代码之前将声明var移动到最近的函数的顶部。这或多或少是这个函数在 JS 内部的样子:

function getData() {
  var data
  if (true) {
    data = 'some data'
  }
  console.log(data) 
}

这就是为什么也可以console.log(data)在函数的顶部,甚至在它被声明之前,会得到undefined该变量的值:

function getData() {
  console.log(data) 
  if (true) {
    var data = 'some data'
  }
}

但是如果切换到let,会得到一个错误ReferenceError: data is not defined,因为 let 声明不会发生提升。

const和let 遵循相同的规则:它们是块范围的。

一开始可能会很棘手,但一旦你意识到这种差异,你就会明白为什么var现在被认为是一种不好的做法——它们的活动部件更少,而且它们的范围仅限于块,这也使它们非常好作为循环变量,因为它们在循环结束后不再存在:

function doLoop() {
  for (var i = 0; i < 10; i++) {
    console.log(i)
  }
  console.log(i)
}

doLoop()

当您退出循环时,i将是一个值为 10 的有效变量。

如果切换到let,当你尝试切换到时console.log(i)会导致错误ReferenceError: i is not defined

结论

非常感谢您阅读完本手册。

我希望它能激发您更多地了解 JavaScript。