第三章、语言基础
这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
任何语言的核心所描述的都是这门语言在最基本的层面上如何工作,涉及语法、操作符、数据类型以及内置功能,在此基础之上才可以构建复杂的解决方案。如前所述,ECMA-262以一个名为ECMAScript的伪语言的形式,定义了 JavaScript 的所有这些方面。
3.1语法
ECMAScript 的语法很大程度上借鉴了 C 语言和其他类 C 语言,如 Java和 Perl。熟悉这些语言的开发者,应该很容易理解 ECMAScript 宽松的语法。即使小白学习来说,也是非常容易理解的
3.1.1区分大小写
先要知道的是,ECMAScript 中一切都区分大小写。无论是变量、函数名还是操作符,都区分大小写。换句话说,变量 test 和变量 Test 是两个不同的变量。类似地, typeof 不能作为函数名,因为它是一个关键字(后面会介绍)。但 Typeof 是一个完全有效的函数名。
3.1.2标识符
所谓标识符,就是变量、函数、属性或函数参数的名称。标识符可以由一或多个下列字符组成:
- 第一个字符必须是一个字母、下划线( _ )或美元符号( $ )
- 剩下的其他字符可以是字母、下划线、美元符号或数字
标识符中的字母可以是扩展 ASCII(Extended ASCII)中的字母,也可以是 Unicode 的字母字符,如 À 和 Æ(但不推荐使用)。
按照惯例,ECMAScript 标识符使用驼峰大小写形式,即第一个单词的首字母小写,后面每个单词的首字母大写,如:
firstSecond
myCar
doSomethingImportant
虽然这种写法并不是强制性的,但因为这种形式跟 ECMAScript 内置函数和对象的命名方式一致,所以算是最佳实践。(如何让自己的代码写的优雅,非常重要的一点就是代码的易读性,让人一眼就能看明白你写的什么意思,除了命名规范外,写法的规范也尤为重要)
3.1.3注释
ECMAScript 采用 C 语言风格的注释,包括单行注释和块注释。单行注释以两个斜杠字符开头,如:
// 单行注释
块注释以一个斜杠和一个星号( /* )开头,以它们的反向组合( */ )结尾,如:
/* 这是多行
注释 */
3.1.4严格模式
ECMAScript 5 增加了严格模式(strict mode)的概念。严格模式是一种不同的 JavaScript 解析和执行模型,ECMAScript 3 的一些不规范写法在这种模式下会被处理,对于不安全的活动将抛出错误。要对整个脚本启用严格模式,在脚本开头加上这一行:
"use strict";
虽然看起来像个没有赋值给任何变量的字符串,但它其实是一个预处理指令。任何支持的 JavaScript引擎看到它都会切换到严格模式。选择这种语法形式的目的是不破坏 ECMAScript 3语法。
也可以单独指定一个函数在严格模式下执行,只要把这个预处理指令放到函数体开头即可:
function doSomething() {
"use strict";
// 函数体
}
严格模式会影响 JavaScript 执行的很多方面,因此本书在用到它时会明确指出来。所有现代浏览器都支持严格模式。
3.1.5语句
ECMAScript 中的语句以分号结尾。省略分号意味着由解析器确定语句在哪里结尾,如下面的例子所示:
let sum = a + b // 没有分号也有效,但不推荐
let diff = a - b; // 加分号有效,推荐
即使语句末尾的分号不是必需的,也应该加上。记着加分号有助于防止省略造成的问题,比如可以避免输入内容不完整。此外,加分号也便于开发者通过删除空行来压缩代码(如果没有结尾的分号,只删除空行,则会导致语法错误)。加分号也有助于在某些情况下提升性能,因为解析器会尝试在合适的位置补上分号以纠正语法错误。
多条语句可以合并到一个 C 语言风格的代码块中。代码块由一个左花括号( { )标识开始,一个右花括号( } )标识结束:
if (test) {
test = false;
console.log(test);
}
if 之类的控制语句只在执行多条语句时要求必须有代码块。不过,最佳实践是始终在控制语句中使用代码块,即使要执行的只有一条语句,如下例所示:
// 有效,但容易导致错误,应该避免
if (test)
console.log(test);
// 推荐
if (test) {
console.log(test);
}
在控制语句中使用代码块可以让内容更清晰,在需要修改代码时也可以减少出错的可能性。
3.2关键字与保留字
ECMA-262 描述了一组保留的关键字,这些关键字有特殊用途,比如表示控制语句的开始和结束,或者执行特定的操作。按照规定,保留的关键字不能用作标识符或属性名。ECMA-262 第 6 版规定的所有关键字如下:
break do in typeof
case else instanceof var
catch export new void
class extends return while
const finally super with
continue for switch yield
debugger function this
default if throw
delete import try
规范中也描述了一组未来的保留字,同样不能用作标识符或属性名。虽然保留字在语言中没有特定用途,但它们是保留给将来做关键字用的。
以下是 ECMA-262 第 6 版为将来保留的所有词汇。
始终保留:
enum
严格模式下保留:
implements package public
interface protected static
let private
模块代码中保留:
await
这些词汇不能用作标识符,但现在还可以用作对象的属性名。一般来说,最好还是不要使用关键字和保留字作为标识符和属性名,以确保兼容过去和未来的 ECMAScript 版本。
3.3变量
ECMAScript 变量是松散类型的,意思是变量可以用于保存任何类型的数据。每个变量只不过是一个用于保存任意值的命名占位符。有 3 个关键字可以声明变量:var 、 const 和 let 。其中, var 在ECMAScript 的所有版本中都可以使用,而 const 和 let 只能在 ECMAScript 6及更晚的版本中使用。
3.3.1var关键字
要定义变量,可以使用 var 操作符(注意 var 是一个关键字),后跟变量名(即标识符,如前所述):
var message;
这行代码定义了一个名为 message 的变量,可以用它保存任何类型的值。(不初始化的情况下,变量会保存一个特殊值 undefined ,下一节讨论数据类型时会谈到。)ECMAScript 实现变量初始化,因此可以同时定义变量并设置它的值:
var message = "hi";
这里, message 被定义为一个保存字符串值 hi 的变量。像这样初始化变量不会将它标识为字符串类型,只是一个简单的赋值而已。随后,不仅可以改变保存的值,也可以改变值的类型:
var message = "hi";
message = 100; // 合法,但不推荐
在这个例子中,变量 message 首先被定义为一个保存字符串值 hi 的变量,然后又被重写为保存了数值 100。虽然不推荐改变变量保存值的类型,但这在 ECMAScript 中是完全有效的。
-
var 声明作用域
关键的问题在于,使用 var 操作符定义的变量会成为包含它的函数的局部变量。比如,使用 var在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁:
function test() { var message = "hi"; // 局部变量 } test(); console.log(message); // 出错!这里, message 变量是在函数内部使用 var 定义的。函数叫 test() ,调用它会创建这个变量并给它赋值。调用之后变量随即被销毁,因此示例中的最后一行会导致错误。不过,在函数内定义变量时省略 var 操作符,可以创建一个全局变量:
function test() { message = "hi"; // 全局变量 } test(); console.log(message); // "hi"去掉之前的 var 操作符之后, message 就变成了全局变量。只要调用一次函数 test() ,就会定义这个变量,并且可以在函数外部访问到。(一般不这么做)
如果需要定义多个变量,可以在一条语句中用逗号分隔每个变量(及可选的初始化):
var message = "hi", found = false, age = 29;这里定义并初始化了 3 个变量。因为 ECMAScript 是松散类型的,所以使用不同数据类型初始化的变量可以用一条语句来声明。插入换行和空格缩进并不是必需的,但这样有利于阅读理解。
在严格模式下,不能定义名为 eval 和 arguments 的变量,否则会导致语法错误。
-
var 声明提升
使用 var 时,下面的代码不会报错。这是因为使用这个关键字声明的变量会自动提升到函数作用域顶部:
function foo() { console.log(age); var age = 26; } foo(); // undefined之所以不会报错,是因为 ECMAScript 运行时把它看成等价于如下代码:
function foo() { var age; console.log(age); age = 26; } foo(); // undefined这就是所谓的“提升”(hoist),也就是把所有变量声明都拉到函数作用域的顶部。此外,反复多次使用 var 声明同一个变量也没有问题:
function foo() { var age = 16; var age = 26; var age = 36; console.log(age); } foo(); // 36
不知不觉已经写了快三千字了,这个系列的初衷是能够让大家利用碎片的时间来阅读,写太多你们看的也会很疲惫,下篇会讲let和const,还有数据类型和操作符,这些非常基础又零碎的知识.加油!!!