JavaScript语法与类型

原创
admin 4个月前 (08-18) 阅读数 25 #JavaScript

语法与类型

本章讨论 JavaScript 的基本语法、变量声明、数据类型和字面量。

基础

JavaScript 的大部分语法从 Java、C 和 C++ 借鉴而来,但同时也受到 Awk、Perl 和 Python 的影响。

JavaScript 是区分大小写的,并使用 Unicode 字符集。举个例子,可以将单词 Früh(在德语中意思是"早")用作变量名。

const Früh = "foobar";

但是,由于 JavaScript 是大小写敏感的,因此变量 frühFrüh 则是两个不同的变量。

在 JavaScript 中,指令被称为语句,并用分号(;)进行分隔。

如果一条语句独占一行的话,那么分号是可以省略的。但如果一行中有多条语句,那么这些语句必须用分号进行分隔。

然而,在一条语句的末尾总是加上分号被认为是最佳实践,即使是在非严格需要的时候。这个习惯可以大大减少代码出问题的可能性。

从左往右扫描 JavaScript 脚本的源文本并将其转换为输入元素(token控制字符行终止符注释空白字符,空白字符指的是空格、制表符和换行符等)序列。

注释

注释语法和 C++ 以及许多其他语言的注释语法一样:

// 单行注释

/* 这是一个更长的,
 * 多行注释
 */

你不能嵌套块注释。这个情况通常是你在注释中意外地包含了 */ 序列,而它会结束这段注释。

在这个例子中,你需要破坏 */ 模式。例如,插入反斜杠:

/* 你可以通过转义正斜杠 /* 嵌套注释 *\/ */

注释的行为类似于空白字符,在脚本执行过程中会被忽略。

声明

JavaScript 有三种变量声明方式。

  • var

  • 声明一个变量,可选择将其初始化为一个值。

  • let

  • 声明一个块级作用域的局部变量,可选择将其初始化为一个值。

  • const

  • 声明一个块级作用域的只读命名常量。

变量

在应用程序中,你将变量用作值的符号名。变量的名字又叫做标识符,其需要遵守一定的规则。

JavaScript 标识符通常以字母、下划线(_)或者美元符号($)开头;后续的字符也可以是数字(0-9)。因为 JavaScript 是区分大小写的,所以字母包含从 AZ 的大写字母和从 az 的小写字母。

你可以在标识符中使用大部分 Unicode 字母,例如 åü。你也可以在标识符中使用 Unicode 转义序列表示字符。

合法的标识符示例:Number_hitstemp99$credit_name

声明变量

你可以用以下两种方式声明变量:

  • 使用关键字 var。例如 var x = 42。这个语法可以用来声明局部变量和全局变量,具体取决于执行上下文

  • 使用关键字 constlet 。例如 let y = 13。这个语法可以用来声明块级作用域的局部变量。

你应该总是在声明变量后使用它们。JavaScript 过去允许给未声明的变量赋值,而这会创建一个**未声明的全局**变量。这在严格模式下是一个错误,应该彻底避免使用它。

声明和初始化

let x = 42 这样的语句中,let x 称作声明= 42称作初始化器。声明允许在后续的代码中访问变量时不会抛出 ReferenceError,而初始化器会给变量赋值。在 varlet 声明中,初始化器是可选的。如果声明变量时没有进行初始化,变量会被赋值为 undefined

let x;
console.log(x); // 输出"undefined"

本质上,let x = 42 等价于 let x; x = 42

const 声明总是需要初始化器,因为它们禁止在声明后进行任何类型的赋值,以及隐式地将其初始化为 undefined 很可能是程序员犯的错。

变量作用域

一个变量可能属于下列作用域之一:

  • 全局作用域:在脚本模式中运行的所有代码的默认作用域。

  • 模块作用域:在模块模式中运行的代码的作用域。

  • 函数作用域:由函数创建的作用域。

此外,用 letconst 声明的变量属于另一个作用域:

  • 块级作用域:用一对花括号创建的作用域()。

当你在函数的外部声明变量时,该变量被称作全局变量,因为当前文档中任何其他代码都能使用它。当你在函数内声明变量时,该变量被称作局部变量,因为它仅在那个函数内可用。

letconst 声明也会被限制在声明所在的块语句中。

if (Math.random() > 0.5) {
  const y = 5;
}
console.log(y); // ReferenceError: y is not defined

然而,用 var 创建的变量不是块级作用域的,而只是块所在的*函数(或全局作用域)*的。

变量提升

var 声明的变量会被提升,意味着你可以在该变量所在的作用域的任意地方引用该变量,即使还没有到达变量声明的地方。然而,如果你在声明变量之前访问该变量,其值总是 undefined,因为只有该变量的声明默认初始化(为 undefined被提升,而不是它的值赋值

console.log(x === undefined); // true
var x = 3;

(function () {
  console.log(x); // undefined
  var x = "局部值";
})();

上面的例子可以被解释为:

var x;
console.log(x === undefined); // true
x = 3;

(function () {
  var x;
  console.log(x); // undefined
  x = "局部值";
})();

由于存在变量提升,一个函数中所有的 var 语句应尽可能地放在接近函数顶部的地方。这个最佳实践会提升代码的清晰度。

letconst 是否被提升是个定义争论。在变量声明之前引用块中的变量,将总是抛出 ReferenceError,因为该变量位于从块的开始到声明所在的"暂时性死区"中。

console.log(x); // ReferenceError
const x = 3;

console.log(y); // ReferenceError
let y = 3;

全局变量

实际上,全局变量是全局对象的属性。

在网页中,全局对象是 window,所以你可以用 window.variable 语法读取和设置全局变量。在所有的环境中,globalThis 变量可以被用于读取和设置全局变量。

常量

你可以用 const 关键字创建一个只读命名常量。常量标识符的语法和任何变量标识符的语法相同:必须以字母、下划线或美元符号($)开头并可以包含字母、数字或下划线。

const PI = 3.14;

常量不可以通过赋值来改变其值或在脚本运行时被重新声明。必须为其初始化一个值。常量的作用域规则和 let 块级作用域变量的一致。

然而,const 仅阻止重新赋值,而不阻止修改。被赋值为常量的对象的属性是不受保护的,所以下面的语句执行时不会产生错误。

const MY_OBJECT = { key: "值" };
MY_OBJECT.key = "其他值";

同样的,数组的元素也是不受保护的,所以下面的语句执行时也不会产生错误。

const MY_ARRAY = ["HTML", "CSS"];
MY_ARRAY.push("JAVASCRIPT");
console.log(MY_ARRAY); // ['HTML', 'CSS', 'JAVASCRIPT'];

数据结构和类型

数据类型

最新的 ECMAScript 标准定义了 8 种数据类型:

  • 七种基本数据类型:

    1. Booleantruefalse

    2. null。一个表示空值的特殊关键字。

    3. undefined。一个未定义值的顶级属性。

    4. Number。整数或浮点数。例如,423.14159

    5. BigInt。任意精度的整数。例如,9007199254740992n

    6. String。表示文本值的字符序列。例如,"Howdy"

    7. Symbol。其实例是唯一且不可变的数据类型。

  • 以及 Object

虽然这些数据类型相对来说比较少,但是它们可以让你在程序中开发有用的功能。函数是这门语言的另一个基本元素。

数据类型的转换

JavaScript 是一门动态类型语言。这意味着你在声明变量时可以不必指定该变量的数据类型。这也意味着在脚本执行期间会根据需要自动转换数据类型。

因此,你可以按照如下方式来定义变量:

let answer = 42;

然后,你还可以给同一个变量赋予一个字符串值,例如:

answer = "不客气,感谢所有的鱼!";

因为 JavaScript 是动态类型的,这种赋值方式并不会提示出错。

数字和"+"运算符

在使用 + 运算符的表达式中涉及数字和字符串,JavaScript 会把数字转换成字符串。例如,注意下列语句:

x = "答案是 " + 42; // "答案是 42"
y = 42 + " 是答案"; // "42 是答案"
z = "37" + 7; // "377"

在使用其他运算符时,JavaScript 会把数字转换成字符串。例如:

"37" - 7; // 30
"37" * 7; // 259

字符串转换为数字

有一些方法可以将内存中表示一个数字的字符串转换为数字。

parseInt 只返回整数,因此它在处理小数时用途有限。

将字符串转换为数字的另一种方法是使用 +(一元加)运算符。

"1.1" + "1.1" = "1.11.1"
(+ "1.1") + (+ "1.1") = 2.2

字面量

在 JavaScript 中,字面量可以表示值。这些字面量是脚本中按字面意思给出的固定的值,而不是变量。本节将介绍以下类型的字面量:

  • 数组字面量

  • 布尔字面量

  • 数字字面量

  • 对象字面量

  • RegExp 字面量

  • 字符串字面量

数组字面量

数组字面量是由一对方括号([])括起来的包含零个或多个表达式的列表,其中每个表达式代表一个数组元素。当你使用数组字面量创建数组时,该数组将会以指定的值作为其元素进行初始化,而其 length 被设定为指定的参数的个数。

下面的示例创建了含有 3 个元素的 coffees 数组,它的长度是 3。

const coffees = ["French Roast", "Colombian", "Kona"];

每次字面量被求值时,数组字面量都会创建一个新的数组对象。例如,在全局作用域中用字面量定义的数组在脚本加载后被创建。然而,如果数组字面量位于函数内,每次调用函数时会初始化一个新数组。

数组字面量中的多余逗号

如果你在数组字面量中连续放置两个逗号,数组会为未指定的元素留下一个空槽。以下示例创建了一个名为 fish 的数组:

const fish = ["Lion", , "Angel"];

打印这个数组时,你会看见:

console.log(fish);
// ['Lion',, 'Angel']

注意,第二项是"empty",与实际的 undefined 值完全不同。当使用数组遍历方法(例如,Array.prototype.map)时,空槽会被跳过。然而,索引访问 fish[1] 仍会返回 undefined

如果你在元素列表的尾部添加了一个逗号,它将会被忽略。

布尔字面量

布尔类型有两种字面量值:truefalse

数字字面量

JavaScript 数字字面量包括多种基数的整数字面量和以 10 为基数的浮点数字面量。

整数字面量

整数和 BigInt 字面量可以用十进制(基数 10)、十六进制(基数 16)、八进制(基数 8)和二进制(基数 2)表示。

  • 十进制整数字面量由数字序列组成,且没有前缀 0(零)。

  • 八进制的整数字面量以 0(或 0O0o)开头,只能包括数字 0-7。

  • 十六进制整数字面量以 0x(或 0X)开头,可以包含数字(0-9)和字母 a-fA-F

  • 二进制整数字面量以 0b(或 0B)开头,只能包含数字 01

  • BigInt 由整数字面量和 n 后缀组成。

浮点数字面量

浮点数字面量可以有以下的组成部分:

  • 一个无符号的十进制整数,

  • 小数点("."),

  • 小数部分(另一个十进制数),

  • 指数部分。

指数部分以 eE 开头,后面跟着一个整数,这个整数可以有正负号(即前缀 +-)。浮点数字面量至少有一位数字,而且必须带小数点或者 eE)。

例如:

3.1415926
.123456789
3.1E+12
.1e-23

对象字面量

对象字面量是由一对花括号({})括起来的包含零个或多个属性名和相关值的列表。

以下是一个对象字面量的例子。car 对象的第一个元素定义了属性 myCar,被赋值为一个新字符串("Saturn");第二个元素,属性 getCar,立即被赋值为函数调用 (carTypes("Honda")) 的结果;第三个元素,属性 special,使用了一个已有的变量(sales)。

const sales = "Toyota";

function carTypes(name) {
  return name === "Honda" ? name : `对不起,我们不售卖 ${name}。`;
}

const car = { myCar: "Saturn", getCar: carTypes("Honda"), special: sales };

console.log(car.myCar); // Saturn
console.log(car.getCar); // Honda
console.log(car.special); // Toyota

增强的对象字面量

对象字面量支持一组简写语法,包括在创建时设置原型、foo: foo 赋值的简写、定义方法、支持 super 调用以及使用表达式计算属性名。

const obj = {
  // __proto__
  __proto__: theProtoObj,
  // "handler: handler"的简写
  handler,
  // 方法
  toString() {
    // Super 调用
    return "d " + super.toString();
  },
  // 计算(动态的)属性名
  ["prop_" + (() => 42)()]: 42,
};

RegExp 字面量

一个正则表达式字面量是字符被正斜杠围成的表达式。下面是正则表达式字面量的一个示例。

const re = /ab+c/;

字符串字面量

字符串字面量是由一对双引号(")或单引号(')括起来的零个或多个字符。字符串被限定在同种引号之间(也即,必须是成对单引号或成对双引号)。

下面是字符串字面量的示例:

'foo'
"bar"
'1234'
'一行\n另一行'
"Joyo 的猫"

你应该使用字符串字面量,除非你特别需要使用 String 对象。想要了解有关 String 对象的细节,参见 String

你可以在字符串字面量值上使用 String 对象的所有方法。JavaScript 会自动将字符串字面量转换为一个临时字符串对象,调用该方法,然后废弃掉那个临时的字符串对象。你也可以使用字符串字面量的 length 属性。

// 将打印字符串中的字符个数(包括空格)
console.log("John 的猫".length); // 结果为:7

模板字面量也可用。模板字面量由一对反引号(`)包围,而不是双引号或单引号。

模板字面量为构建字符串提供了语法糖。

// 创建基本的字符串字面量
`在 JavaScript 中,"\n" 是换行符。`

// 多行字符串
`在 JavaScript 中,模板字符串可以
 跨越行,但是由双引号和单引号
 包裹的字符串不行。`

// 字符串插值
const name = 'Lev', time = 'today';
`你好 ${name},${time} 过得怎么样?`

在字符串中使用的特殊字符

除了普通字符,你也可以在字符串中使用特殊字符,如下例所示。

"一行\n另一行";

以下表格列举了你能在 JavaScript 的字符串中使用的特殊字符。

字符意思
\0空字节
\b退格符
\f换页符
\n换行符
\r回车符
\t制表符
\v垂直制表符
\'撇号或单引号
\"双引号
\\反斜杠字符
\XXX由从 0377 最多三位八进制数 XXX 表示的 Latin-1 字符。
\xXX由从 00FF 的两位十六进制数字 XX 表示的 Latin-1 字符。
\uXXXX由四位十六进制数字 XXXX 表示的 Unicode 字符。
\u{XXXXX}Unicode 码位转义。

转义字符

对于那些未出现在表格中的字符,其所带的前导反斜杠将被忽略。但是,这一用法已被废弃,应当避免使用。

通过在引号前加上反斜杠,可以在字符串中插入引号,这就是引号转义。例如:

const quote = "He read \"The Cremation of Sam McGee\" by R.W. Service.";
console.log(quote);

要在字符串中插入反斜杠字面量,必须转义反斜杠。例如,要把文件路径 c:\temp 赋值给一个字符串,可以采用如下方式:

const home = "c:\\temp";

更多信息

本章重点包括声明和类型的基本语法。想要学习有关 JavaScript 的语言结构更多的信息,也可以参见本指南中的这些文章:

在下一章中,我们将会学习控制流结构与错误处理。

版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权本站发表,未经许可,不得转载。

上一篇:JavaScript介绍 下一篇:循环与迭代
作者文章
热门
最新文章
标签列表