2.1 Hello, world!
提示
本文章是关于 JavaScript 的,但是为了演示效果,会用到一些浏览器的命令,如:alert
“script” 标签
我们几乎可以使用 script 标签将 JavaScript 程序插入到 HTML 文档的任何位置。
Demo
也可 F12 查看 控制台
<!DOCTYPE HTML>
<html>
<body>
<p>script 标签之前...</p>
<script>
alert('Hello, world!');
console.log('你好,VuePress!')
</script>
<p>...script 标签之后</p>
</body>
</html>
你可以通过点击“运行”按钮来运行这个例子。
script 标签中包裹了 JavaScript 代码,当浏览器遇到 script 标签,代码会自动运行。
外部脚本
如果有大量的 JavaScript 代码,我们可以将它放入一个单独的文件。
脚本文件可以通过 src 特性(attribute)添加到 HTML 文件中。
<script src="/path/to/script.js"></script>
这里,/path/to/script.js 是脚本文件从网站根目录开始的绝对路径。当然也可以提供当前页面的相对路径。例如,src ="script.js",就像 src="./script.js",表示当前文件夹中的 "script.js" 文件。
我们也可以提供一个完整的 URL 地址,例如:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
要附加多个脚本,请使用多个标签:
<script src="/js/script1.js"></script>
<script src="/js/script2.js"></script>
…
Tips:
一般来说,只有最简单的脚本才嵌入到 HTML 中。更复杂的脚本存放在单独的文件中。
使用独立文件的好处是浏览器会下载它,然后将它保存到浏览器的 缓存 中。
之后,其他页面想要相同的脚本就会从缓存中获取,而不是下载它。所以文件实际上只会下载一次。
这可以节省流量,并使得页面(加载)更快。
Tips:如果设置了 src 特性,script 标签内容将会被忽略。
一个单独的 script 标签不能同时有 src 特性和内部包裹的代码。
这将不会工作:
<script src="file.js">alert(1); </script>
// 此内容会被忽略,因为设定了 src
我们必须进行选择,要么使用外部的 script src="…",要么使用正常包裹代码的 script。
为了让上面的例子工作,我们可以将它分成两个 script 标签。
<script src="file.js"></script>
<script>
alert(1);
</script>
2.2 代码结构
语句
语句是执行行为(action)的语法结构和命令。
我们已经见过了 alert('Hello, world!') 这样可以用来显示消息的语句。
我们可以在代码中编写任意数量的语句。语句之间可以使用分号进行分割。
例如,我们将 “Hello World” 这条信息一分为二:
Demo
也可 F12 查看 控制台
alert('Hello');alert('World!')
通常,每条语句独占一行,以提高代码的可读性:
alert('Hello');
alert('World!');
分号
当存在换行符(line break)时,在大多数情况下可以省略分号。
下面的代码也是可以运行的:
alert('Hello')
alert('World!')
在这,JavaScript 将换行符理解成“隐式”的分号。这也被称为 自动分号插入。
在大多数情况下,换行意味着一个分号。但是“大多数情况”并不意味着“总是”!
有很多换行并不是分号的例子,例如:
Demo
也可 F12 查看 控制台
alert(3 +
1 +
2);
代码输出 6,因为 JavaScript 并没有在这里插入分号。显而易见的是,如果一行以加号 "+" 结尾,那么这是一个“不完整的表达式”,不需要分号。所以,这个例子得到了预期的结果。
但存在 JavaScript 无法确定是否真的需要自动插入分号的情况。
这种情况下发生的错误是很难被找到和解决的。
Demo 正常的例子
也可 F12 查看 控制台
alert("Hello");
[1, 2].forEach(alert);
运行结果:先显示 Hello,然后显示 1,然后 2。
Demo 错误的例子
也可 F12 查看 控制台
alert("Hello")
[1, 2].forEach(alert)
运行结果:只有第一个 Hello 会被显示出来(并且有一个报错,你可能需要打开控制台才能看到它)。并且不会再有数字被显示出来。
这是因为,JavaScript 引擎并没有假设在方括号 [...] 前有一个分号。因此,最后一个示例中的代码被视为了单个语句。
对于引擎来说,它是这样的:
alert("Hello")[1, 2].forEach(alert)
注释
TIP
注意:一定要写注释
可以在脚本的任何地方添加注释,它们并不会影响代码的执行,因为引擎会直接忽略它们。
单行注释以两个正斜杠字符 // 开始。
// 这行注释独占一行
console.log('Hello')
console.log('World') // 这行注释跟随在语句后面
多行注释以一个正斜杠和星号开始 “/*” 并以一个星号和正斜杠结束 “*/”。
/* 注释代码
console.log('Hello');
*/
console.log('World')
不支持注释嵌套!
不要在 /*...*/ 内嵌套另一个 /*...*/
/*
/* 嵌套注释 ?!? */
*/
console.log( 'World' );
2.3 现代模式,"use strict"
ES5 规范增加了新的语言特性并且修改了一些已经存在的特性。为了保证旧的功能能够使用,大部分的修改是默认不生效的。你需要一个特殊的指令 —— "use strict" 来明确地激活这些特性。
当它处于脚本文件的顶部时,则整个脚本文件都将以“现代”模式进行工作。
比如:
"use strict";
// 代码以现代模式工作
...
TIP
确保 "use strict" 出现在最顶部
TIP
没有办法取消 "use strict"
2.4 变量
变量 是数据的“命名存储”。我们可以使用变量来保存商品、访客和其他信息。
变量声明
在 JavaScript 中创建一个变量,我们需要用到 let/var/const 关键字。
下面的语句创建(也可以称为 声明 或者 定义)了一个名称为 "message" 的变量:
let message
Demo
也可 F12 查看 控制台
可以通过赋值运算符 = 为变量添加一些数据
let message
message = 'Hello!'
alert(message) // 显示变量内容
简洁一点,我们可以将变量定义和赋值合并成一行:
Demo
也可 F12 查看 控制台
let message = 'Hello!' // 定义变量,并且赋值
alert(message) // 显示变量内容
也可以在一行中声明多个变量:
let user = '洛埋名', age = 25, message = 'Hello';
看上去代码长度更短,但并不推荐这样。为了更好的可读性,请一行只声明一个变量。
多行变量声明有点长,但更容易阅读:
let user = '洛埋名'
let age = 25
let message = 'Hello'
一些程序员采用下面的形式书写多个变量:
let user = '洛埋名',
age = 25,
message = 'Hello'
或者这样
let user = '洛埋名'
, age = 25
, message = 'Hello';
TIP
这些变体可行,但不推荐
TIP 变量是可以被改变的
也可 F12 查看 控制台
let message
message = 'Hello!'
message = 'World!' // 值改变了
alert(message)
我们还可以声明两个变量,然后将其中一个变量的数据拷贝到另一个变量。
TIP
也可 F12 查看 控制台
let hello = 'Hello world!'
let message
message = hello // 将字符串 'Hello world' 从变量 hello 拷贝到 message
// 现在两个变量保存着相同的数据
alert(hello) // Hello world!
alert(message) // Hello world!
声明两次会触发 error
let message = 'This'
// 重复 'let' 会导致 error
let message = 'That'
// SyntaxError: 'message' has already been declared
变量的命名
JavaScript 的变量命名有两个限制:
- 变量名称必须仅包含字母、数字、符号 $ 和 _ 。
- 首字符必须非数字。
有效命名
let userName
let test123
无效命名
let 1a; // 不能以数字开始
let my-name; // 连字符 '-' 不允许用于变量命名
TIP
区分大小写
TIP
允许非英文字母,但不推荐
let имя = '...'
let 我 = '...'
从技术上讲,这样没问题。这样的命名是完全允许的,但是用英文进行变量命名是国际惯例。哪怕我们正在写一个很小的脚本,它也有可能会被使用很久。某个时候,来自其他国家的人可能会需要阅读它。
保留字
有一张 保留字列表,这张表中的保留字无法用作变量命名,因为它们被用于编程语言本身了。
比如,let、class、return、function 都被保留了。
下面的代码将会抛出一个语法错误:
let let = 5; // 不能用 "let" 来命名一个变量,错误!
let return = 5; // 同样,不能使用 "return",错误!
未采用 use strict 下的赋值
一般,我们需要在使用一个变量前定义它。但是在早期,我们可以不使用 let 进行变量声明,而可以简单地通过赋值来创建一个变量。现在如果我们不在脚本中使用 use strict 声明启用严格模式,这仍然可以正常工作,这是为了保持对旧脚本的兼容。
// 注意:这个例子中没有 "use strict"
num = 5 // 如果变量 "num" 不存在,就会被创建
alert(num) // 5
上面这是个糟糕的做法,严格模式下会报错。
'use strict'
num = 5 // 错误:num 未定义
TIP
一个变量名应该有一个清晰、明显的含义,对其存储的数据进行描述。
不该使用诸如 a、b、c 这种缩写和短名称,除非你真的知道你在干什么。
常量
声明一个常数(不变)变量,可以使用 const 而非 let:
const myBirthday = '2000.10.10'
使用 const 声明的变量称为“常量”。它们不能被修改,如果你尝试修改就会发现报错:
const myBirthday = '2000.10.10'
myBirthday = '2000.10.11' // 错误,不能对常量重新赋值
TIP
一般情况下 我们习惯 用大写形式表示常数
const COLOR_RED = '#F00'
2.5 数据类型
JavaScript 中的值都具有特定的类型。例如,字符串或数字。
TIP
在 JavaScript 中有 8 种基本的数据类型
- 7 种原始类型
- Number 用于任何类型的数字:整数或浮点数,在 ±(2^53-1) 范围内的整数。
- BigInt 用于任意长度的整数。
- String 用于字符串:一个字符串可以包含 0 个或多个字符,所以没有单独的单字符类型。
- Boolean 用于 true 和 false。
- null 用于未知的值。
- undefined 用于未定义的值。
- Symbol 用于唯一的标识符。
- 1 种引用类型
- object 用于更复杂的数据结构。 - (其中 array、function、data、Math 等归属于 object)
基本数据类型和引用数据类型的区别
TIP
基本数据类型是直接存放在栈中的简单数据段,占用的空间大小固定且较小
引用数据类型存放在堆内存中,占据空间大,在栈中存储指针,指向该数据在堆中的内存地址
Number 类型
let n = 1
n = 1.45
number 类型代表整数和浮点数。
数字可以有很多操作,比如,乘法 *、除法 /、加法 +、减法 - 等等。
除了常规的数字,还包括所谓的“特殊数值”也属于这种类型:
Infinity
无穷大 ∞。是一个比任何数字都大的特殊值。
可以通过除以 0 来得到它:
console.log(1 / 0) // Infinity
或者在代码中直接使用它
console.log(Infinity) // Infinity
-Infinity
同 Infinity
NaN
NaN 代表一个计算错误。它是一个不正确的或者一个未定义的数学操作所得到的结果,比如:
console.log('not a number' / 2) // NaN,这样的除法是错误的
任何对 NaN 的进一步数学运算都会返回 NaN:
console.log(NaN + 1) // NaN console.log(3 * NaN) // NaN console.log('not a number' / 2 - 1) // NaN
例外
也可 F12 查看 控制台
alert(NaN ** 0) // 1 console.log(NaN ** 0) // 结果为 1
BigInt 类型
在 JavaScript 中,“number” 类型无法安全地表示大于 (2^53-1)(即 9007199254740991),或小于 -(2^53-1) 的整数。
更准确的说,“number” 类型可以存储更大的整数(最多 1.7976931348623157 * 10308),但超出安全整数范围 ±(253-1) 会出现精度问题,因为并非所有数字都适合固定的 64 位存储。因此,可能存储的是“近似值”。
例如,这两个数字(正好超出了安全整数范围)是相同的:
console.log(9007199254740991 + 1) // 9007199254740992
console.log(9007199254740991 + 2) // 9007199254740992
在大多数情况下,±(253-1) 范围就足够了,但有时候我们需要整个范围非常大的整数,例如用于密码学或微秒精度的时间戳。
BigInt 类型是最近被添加到 JavaScript 语言中的,用于表示任意长度的整数。
可以通过将 n 附加到整数字段的末尾来创建 BigInt 值。
// 尾部的 "n" 表示这是一个 BigInt 类型
const bigInt = 1234567890123456789012345678901234567890n
String 类型
JavaScript 中的字符串必须被放在引号里。
let name = '洛埋名'
let str = '晚上好'
let phrase = `你好,我是 ${name}`
在 JavaScript 中,有三种包含字符串的方式。
- 双引号:"Hello"
- 单引号:'Hello'
- 反引号:
Hello
双/单引号都是“简单”引用,在 js 中几乎没有什么差别。
反引号是 功能扩展 引号。它们允许我们通过将变量和表达式包装在 ${…} 中,来将它们嵌入到字符串中。
Demo
也可 F12 查看 控制台
let name = '洛埋名'
// 嵌入一个变量
alert(`你好, 我叫${name}!`) // 你好, 我叫洛埋名!
// 嵌入一个表达式
alert(`结果是: ${1 + 2}`) // 结果是: 3
${…} 内的表达式会被计算,计算结果会成为字符串的一部分。可以在 ${…} 内放置任何东西。
Boolean 类型(逻辑类型)
boolean 类型仅包含两个值:
- true
- false。
null 值
特殊的 null 值不属于上述任何一种类型。
它构成了一个独立的类型,只包含 null 值:
let age = null
undefined 值
undefined 的含义是 未被赋值。
如果一个变量已被声明,但未被赋值,那么它的值就是 undefined:
let age
alert(age) // 弹出 "undefined"
Symbol 类型
symbol 类型用于创建对象的唯一标识符。
Object 类型
object 类型是一个特殊的类型。
其他所有的数据类型都被称为“原始类型”,因为它们的值只包含一个单独的内容(字符串、数字或者其他)。相反,object 则用于储存数据集合和更复杂的实体。
typeof 运算符
typeof 运算符返回参数的类型
typeof undefined // "undefined"
typeof 0 // "number"
typeof 10n // "bigint"
typeof true // "boolean"
typeof 'foo' // "string"
typeof Symbol('id') // "symbol"
typeof Math // "object"
typeof null // "object"
typeof alert // "function"
TIP
typeof null 的结果为 "object"。这是官方承认的 typeof 的错误,这个问题来自于 JavaScript 语言的早期阶段,并为了兼容性而保留了下来。null 绝对不是一个 object。null 有自己的类型,它是一个特殊值。typeof 的行为在这里是错误的。
typeof alert 的结果是 "function",因为 alert 在 JavaScript 语言中是一个函数。我们会在下一章学习函数,那时我们会了解到,在 JavaScript 语言中没有一个特别的 “function” 类型。函数隶属于 object 类型。但是 typeof 会对函数区分对待,并返回 "function"。这也是来自于 JavaScript 语言早期的问题。从技术上讲,这种行为是不正确的,但在实际编程中却非常方便。
2.6 类型转换
大多数情况下,运算符和函数会自动将赋予它们的值转换为正确的类型。
比如,alert 会自动将任何值都转换为字符串以进行显示。算术运算符会将值转换为数字。
字符串转换
除去类似 alert(value) 将 value 转换为字符串类型,然后显示这个值。
我们还可以显式的转换它: String(value)
Demo
也可 F12 查看 控制台
let value = true
alert(typeof value) // boolean
value = String(value) // 现在,值是一个字符串形式的 "true"
alert(typeof value) // string
字符串转换最明显。false 变成 "false",null 变成 "null" 等。
数字型转换
在算术函数和表达式中,会自动进行 number 类型转换。
比如,当把除法 / 用于非 number 类型:
Demo
也可 F12 查看 控制台
alert('6' / '2') // 3, string 类型的值被自动转换成 number 类型后进行计算
2.7 基础运算符,数学运算
2.8 值的比较
2.9 条件分支:if 和 '?'
2.10 逻辑运算符
2.11 空值合并运算符 '??'
2.12 循环:while 和 for
2.13 "switch" 语句
2.14 函数
2.15 函数表达式
2.16 箭头函数,基础知识
2.17 JavaScript 特性