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

var 而不是 let

let 和 var 之间有些微妙的差别。我们将会在 老旧的 "var" 章节中介绍它们。

const 同样后面会讲到。

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= '...'

从技术上讲,这样没问题。这样的命名是完全允许的,但是用英文进行变量命名是国际惯例。哪怕我们正在写一个很小的脚本,它也有可能会被使用很久。某个时候,来自其他国家的人可能会需要阅读它。

保留字

有一张 保留字列表open in new window,这张表中的保留字无法用作变量命名,因为它们被用于编程语言本身了。

比如,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 特性



上次更新:
作者: 洛埋名