第 2 章 JavaScript基础

第 2 章 JavaScript 基础

在详述 JavaScript 的语言基础之前,我们先做个简单介绍。本章的目的是使大家对 JavaScript 语言基础有整体性的把握,而严谨详细的说明将留在之后的章节中详述。

2.1 JavaScript 的特点

JavaScript 程序设计语言有如下几个特点:

  • 解释型语言

  • 类似于 C 和 Java 的语法结构

  • 动态语言

  • 基于原型的面向对象

  • 字面量的表现能力

  • 函数式编程

 

  • 解释型语言

    JavaScript 是一种解释型语言,和解释型语言相对的是编译型语言。解释型语言直接在运行环境中执行代码,所以一般来说,与编译型语言相比,解释型语言的开发更为容易。特别是 JavaScript,其运行环境是已经普及的浏览器,所以能够很容易地尝试开发。这是其他程序设计语言所不能比拟的。

    解释型语言的劣势在于,其运行速度通常都会慢于编译型语言,不过这也只是理论上的情况。现在,解释型语言和编译型语言之间的界线正在变得越来越模糊。编译型语言在有了足够快速的编译器和功能强大的开发环境之后,也能实现和解释型语言相匹敌的开发难易度。同时,解释型语言由于使用了 JIT(Just In Time)这种能够在运行中进行编译的技术,使得运行速度得以改善。

    如今,在选择程序设计语言时,比起选择编译型语言还是解释型语言,更重要的是考虑语言的设计目的。是为了使开发过程变得轻松还是为了提高执行效率,语言最初的设计理念不同,其性质自然会有差异。设计 JavaScript 之初,优先考虑的是使开发过程变得轻松,因此提供了多种特性。

  • 类似于 C 和 Java 的语法结构

    JavaScript 的语法结构与 C 和 Java 相似。JavaScript 同样有 if 或 while 这类关键字,其语法结也与 C 和 Java 类似。它们乍一看很像,因此有这些语言开发经验的人很容易就能熟悉 JavaScript。不过需要注意的是,它们之间的相似性其实并不如表面看起来的那么强。

  • 动态语言

    JavaScript 与 C 和 Java 所不同的一点在于,JavaScript 是一种动态语言,将在之后详述。单从代码的角度看,动态语言的变量和函数是不指定返回值类型的。JavaScript 之所以被设计成动态语言,和选择将其设计为解释型语言的理由一样,都是优先考虑了开发难易度的结果。对解释型语言以及动态语言的特性的喜好虽然见仁见智,但语言本身并没有高下优劣之分。

  • 基于原型的面向对象

    解释型动态语言并不少见,现有的较为知名的脚本语言大多都属于这一类型。不过基于原型的面向对象特性,使得 JavaScript 与它们有所不同。基于原型的面向对象特性和基于类的面向对象特性是有所差别的,在此请先了解这一点即可,更为详细的内容将会在之后详述。目前,被称为面向对象语言的程序设计语言,大多提供了基于类的面向对象语言功能。JavaScript 虽然并不是第一个采用基于原型的面向对象特性的语言,不过可以说是这类语言中最为著名的。同样,基于原型与基于类的面向对象语言之间的差异,也主要是个人喜好的区别,而并非是孰优孰劣的问题。

  • 字面量的表现能力

    字面量的表现能力是 JavaScript 开发生产力得以提高的一个重要原因。在 Perl 之后,很多语言都提供了功能强大的字面量功能。虽然其中表现突出的不止 JavaScript 一种,不过由于它的字面量功能相对来说非常优秀,所以作为语言特点之一列举于此。

  • 函数式编程

    最后来介绍一下函数式编程。函数式编程是一种历史悠久,而又在最近颇为热门的话题。函数式编程在面向对象一词诞生以前就已经存在,不过它在很长的一段时间里都被隐藏于过程式编程(面向对象也是过程式编程的一种)的概念之下。然而现在这种状况正在逐步发生改变,JavaScript 正是这一改变过程中的一部分。尽管 JavaScript 能直接支持的程序设计范式在本质上还是过程式的,但由于具备了匿名函数,可以把函数作为对象来使用,所以同时也能够支持函数式编程。

2.2 关于编排格式

在这一部分中,我们将按照以下方式表示 JavaScript 的代码范例。原则上使用 smjs(SpiderMonkey 的 Shell)来确认执行结果,并使用以 ECMAScript 第 5 版为标准的 JavaScript 1.8.5。在需要明确说明执行结果时,在 JavaScript 代码之后附有相应的执行结果。

表达式求值的结果也采用和执行结果相同的方式编排。

js> var s = 'foorbar';           // 用于说明语句含义的注释
运行结果(或是表达式求值的结果)

在 JavaScript 中用于分隔语句的分号是可以省略的,不过在本书的范例代码中,不会省略分号。

如果在运行 smjs 时发生错误,会像下面这样在行首显示“typein: 数字”。这里的数字表示正在运行的代码行号。由于运行环境不同,本书将省略“typein: 数字”这样的错误行号。

js> x;
typein:1 ReferenceError: x is not defined

print 函数

JavaScript 的核心语言中并不包括 print 函数,但是在本书的代码范例中,将会使用到 print。如果要在浏览器中运行相关代码,请改用 alert 或是 document.write ;如果使用 FireBug 或是 Node.js(请参见本书第 6 部分),请改用 console.log 函数。

// 浏览器
var print = alert;

或者

var print = document.write;

// 使用FireBug 或是Node.js 时
var print = console.log;

2.3 变量的基础

2.3.1 变量的使用方法

本节将对 JavaScript 中变量的使用方法进行说明。变量的作用是给某一个值或是对象标注名称。在介绍了对象和函数之后,会再对变量这一主题进行详细说明。本节中先不考虑那些复杂情况,而着重说明变量的使用方法。

像下面这样使用关键字 var 就可以对变量进行声明。

js> var foo;              // 声明变量 foo

在之后还会进一步详述变量名中具体可以使用哪些字符,现阶段请先将其理解为变量名可以使用任意的英文字母即可。通过赋值运算符(=)可以给变量赋值,即在运算符的左侧书写变量,而在其右侧书 写要赋的值。

js> foo = "abc";          // 将字符串 "abc" 赋值给变量 foo

变量的声明和赋值也可以像下面这样同时进行。在声明变量的同时为其赋值是一种较好的编程风格。

js> var foo = "abc";      // 声明一个赋值为字符串 "abc" 的变量 foo

JavaScript 中的变量没有变量类型(之后会说明变量类型的概念)。因为没有变量类型,所以对于同一个变量,既可以赋值为字符串,也可以赋值为数字,就像下面这样。不过通常情况下,以这种方式来使用变量并不是好习惯,所以请尽可能避免出现这样的代码。

js> var foo;
js> foo = "abc";          // 将字符串 "abc" 赋值给变量 foo
js> foo = 123;            // 将数值 123 赋值给变量 foo

在表达式中写上某个变量名之后就能获取该变量的值。

js> var n = 7;            // 将数值 7 赋值给变量 n
js> n + 1;                // 获取变量 n 的值,并加上 1
8

严格来说,语句中的变量,对于左值和右值是有所不同的。左值指的是赋值表达式 = 左侧的变量名,右值指的是赋值表达式 = 右侧或是在赋值表达式外的其他表达式中出现的变量名。右值中的变量是所要用于赋值的值,而左值中的变量则是将被赋值的对象。用这些专业术语来说明可能不太容易理解,其实在这一点上,JavaScript 和大部分支持赋值功能的程序设计语言是完全相同的。对于左值和右值,只要根据直觉,将其理解为被赋值的对象与所赋值的来源即可。

被声明但未进行任何赋值的变量,其值为 undefined(之后会说明 undefined 值的含义)。读取这类变量的值不会引起运行时错误。需要注意的是,在大部分情况下,读取 undefined 值都是产生错误的根源。

js> var foo;
js> print(foo);           // 变量 foo 的值为 undefined
undefined

如果要读取没有被声明的变量(即作为右值使用该变量),就会引发 ReferenceError 异常;如果将其作为左值使用,即作为赋值对象使用,则不会发生错误。

更为详细的内容请参见 2.3.2 节 。

js> print(x);
ReferenceError: x is not defined

2.3.2 省略 var

熟悉 JavaScript 的人也许知道,在 JavaScript 中 var 关键字是可以省略的。尽管此前提到,变量是通过 var 来声明的,但其实不通过 var 来声明也可以对变量进行赋值。这样的变量称为隐式声明变量。采用隐式声明的变量都是全局变量,即使是在函数内部隐式声明的变量也属于全局变量。

在函数外部通过 var 声明的变量也是全局变量,这类全局变量是显式声明的。为了和显式声明的全局变量相区别,那些没有通过 var 声明的变量被称为隐式全局变量。

应当尽可能避免使用全局变量,特别是应该避免使用隐式全局变量。开发者只需做恰当的处理,即在声明变量时总是使用 var,就可以完全避免使用隐式全局变量,从而解决这一问题。

不仅是本书,只要是稍微专业一些的 JavaScript 书籍,都不会推荐使用隐式全局变量。而且在 ECMAScript 第 5 版的 strict mode 中,隐式全局变量已经被判定为一种错误(请参见第 2 部分第 7 章的“专栏”),所以请不要省略 var。

2.3.3 常量

ECMAScript 标准没有规定常量的声明语法。不过在 JavaScript 的自定义增强功能中,是可以对常量进行声明的。由于是自定义的增强功能,因此并没有明确的规范。下面介绍的是 SpiderMonkey 的情况。如果要声明一个常量,需要使用 const 关键字而不是 var。可以作为常量名使用的字符和变量的是相

同的,不过习惯上常量名都以大写字母表示。const 的使用方法如下所示。

js> const FOO = 7;    // 声明常量
js> print(FOO);
7

即使给常量再次赋值,这个常量的值也不会发生改变。其实,对常量再次赋值应该算作一种错误,但在实际中这并不会导致出错,对此请多加注意。

js> const FOO = 7;
js> FOO = 8;          // 对常量再次赋值
js> print(FOO);       // 常量的值不会发生改变
7

如果在声明时没有对常量进行赋值的话,它的值就是 undefined,对其的处理方式和变量相同。由于 const 属于自定义增强功能,所以在本书中不会对此深究。

js> const FOO;
js> print(FOO);
undefined

2.4 函数基础

2.4.1 函数的定义

JavaScript 中的函数是一种类似于 Java 中方法的语言功能,不过它可以独立于类进行定义,所以从表面上来看,反而和 C 语言或是 PHP 的函数、Perl 的子程序更为相似。不过,JavaScript 中的函数和它们在本质上是不同的。它们之间的差异以及其他详细的内容,将在第 6 章中再具体说明。

本节仅对 JavaScript 的函数进行概要性的说明。在基本使用方式上,JavaScript 中的函数和其他程序设计语言中被称为函数或子程序的概念并没有什么不同。函数是由一连串的子程序(语句的集合)所组成的,可以被外部程序调用。向函数传递参数之后,函数可以返回一定的值。

通常情况下,JavaScript 代码是自上而下执行的,不过函数体内部的代码则不是这样。如果只是对函数进行了声明,其中的代码并不会执行。只有在调用函数时才会执行函数体内部的代码(代码清单 2.1)。

代码清单 2.1 包含函数的代码的执行顺序

print('1');
function f() {    // 声明函数
    print('2');
}
print('3');
f();              // 调用函数

 

// 代码清单2.1 的运行结果
1
3
2

2.4.2 函数的声明与调用

可以通过函数声明语句来定义一个函数。函数声明语句以关键字 function 开始,其后跟有函数名、参数列表和函数体。其语法如下所示:

// 函数声明语句的语法
function 函数名 ( 参数, 参数, ……) {
 函数体
}

代码清单 2.2 是个具体例子,其中函数名为 sum,参数名为 ab。函数声明中所写的参数称为形参(形式参数)。代码清单 2.2 中的函数 sum 对两个参数做了加法运算,并通过 return 语句返回结果。

代码清单 2.2 函数 sum 的声明

function sum (a, b) {
    return Number(a) + Number(b);
}

可以像下面这样来调用函数 sum。调用函数时,传递给函数的参数称为实参(实际参数)。下面代码中以 3 和 4 作为实参调用了函数 sum。

// 函数sum 的调用
js> sum (3, 4);
7

函数声明时不必指定形参的类型 1。任何类型的值都可以作为实参传递,因而开发者在设计函数时需要考虑接收错误类型的值的情况。此外,形参的数量和实参的数量可以不一致,这一点将在之后再具体说明。JavaScript 的这些特性,与始终严格检查参数类型的 Java 形成了鲜明的对比。因此,在 JavaScript 中自然也就不存在函数重载这一特性(即可以存在多个参数不同的同名函数)。

1不过 JavaScript 的变量本身就没有类型可言,所以形参没有类型也不奇怪。

2.4.3 匿名函数

还可以通过匿名函数表达式来定义一个函数。其语法形式为在 function 后跟可以省略的函数名、参数列表以及函数体。其语法如下所示:

// 匿名函数的语法
function ( 参数, 参数, ……) {
  函数体
}
function 函数名 ( 参数, 参数, ……) {
  函数体
}

可以看到,函数声明语句和匿名函数表达式在语法上几乎一模一样,唯一的区别仅仅是能否省略函数名称而已。不过,因为匿名函数表达式是一种表达式而非语句,所以也可以在表达式内使用。另外由于它是表达式因此也会有返回值。匿名函数的返回值是一个 Function 对象的引用(关于引用的详细内容将在第 5 章中进行说明)。把它简单理解为返回一个函数也没有问题。

不过请不要因为它们都是表达式,而将匿名函数表达式与函数调用表达式相混淆。函数调用表达式在大部分程序设计语言中都是存在的,而匿名函数表达式在一些程序设计语言中并不存在(至少 Java 中的方法是无法实现这样的功能的)。其实,通过表达式来定义一个函数并不是什么新的功能。早在与 JavaScript 有些类似的 Lisp 语言的时代,这种功能就已经存在,并且在一些比较新的程序设计语言中,这一功能正在变得越来越常见。

匿名函数表达式的使用方式如代码清单 2.3 所示。赋值表达式右侧的就是匿名函数表达式。

代码清单 2.3 匿名函数表达式的例子

var sum2 = function (a, b) {
    return Number(a) + Number(b)
}

sum2 的前面是 var,所以它是一个变量名。以 function 开始的匿名函数表达式将返回一个函数。也就是说,代码清单 2.3 的含义是,将 Function 对象的一个引用赋值给变量 sum2。可以像下面这样来调用 变量 sum2 所引用的函数。

// 调用函数sum2
js> sum2(3, 4);
7

这段代码和代码清单 2.2 中调用函数 sum 的方式没有区别。也就是说,代码清单 2.2 中的函数声明语句,和代码清单 2.3 中赋值表达式的作用是相同的,都会将匿名函数表达式赋值给变量。两者都是在生成一个没有名称的函数体(Function 对象)之后,再赋予其一个名称。目前只要认为代码清单 2.2 和 2.3 中的语句是相同的就可以了。第 6 章会对两者的细微差异进行说明。

还可以像下面这样,在右侧书写通过函数声明语句进行定义的函数,以将其赋值给左值,这样就可以通过被赋值对象的名称来调用该函数。将其理解为 sum 这一名称持有该函数对象的引用即可。至此,读者或许会觉得变量名和函数名之间的分界很模糊,而事实也确实如此,我们之后将会对此进行详述。

// 在表达式右侧书写代码清单2.2 中的函数名
js> var sum3 = sum;
// 调用函数sum3
js> sum3(3, 4);
7

2.4.4 函数是一种对象

JavaScript 中的函数和 Java 中的方法或 C 语言中的函数的最大不同在于,JavaScript 中的函数也是一种对象。下一节将阐述对象的概念,届时可以了解到对象在本质上是没有名称的。而对于函数来说也是如此,因为函数本身也是一种对象。

正如变量存在的意义是为了调用没有名称的对象,函数名存在的意义是为了调用没有名称的函数。因此,变量名和函数名实质上是相同的。这一点在之后会再次说明。虽然有时也需要区别对待变量名和函数名,不过这里为方便起见,暂且认为两者在本质上是相同的。

JavaScript 的函数是一种对象,不过并不是说所有的对象都是函数。函数是一种包含了可执行代码,并能够被其他代码调用的特殊的对象。

2.5 对象的基础

2.5.1 对象的定义

从底层实现来看,JavaScript 的对象和 Java 的对象在基本原则上是相同的。两者都是内存中的实体,保持着某种状态,并且是用于编程操作的目标对象。但是,从高层概念来看的话,就会发现两者有着不小的差别。

Java 中的对象可以认为是类的一种实例化结果,而 JavaScript 中并没有类这样的语言构造。JavaScript 中的对象是一个名称与值配对的集合。这种名称与值的配对被称为属性。这样一来,JavaScript 对象可以定义为属性的集合。

表面上看,JavaScript 对象和 Java 的映射(java.util.Map)非常相似。实际上,JavaScript 对象可以用作管理键值对的关联数组 [ 又称映射(Map)或字典(Dictionary)]。JavaScript 对象还有着 Java 映射所没有的两个特点。

其一是 JavaScript 对象的属性值可以由函数指定。

其二是 JavaScript 具备一种称为原型链的构造。通过这一构造,JavaScript 对象实现了类似于类的继承的能力。具体的内容将在第 5 章中进行详细说明。

以上的说法可能有些复杂,简单说来,将对象理解为一种实体即可,程序可以通过它来进行数据处理 2

2更进一步的说明请参见第 5 章。

2.5.2 对象字面量表达式与对象的使用

可以通过对象字面量表达式来生成一个对象。对象字面量表达式由大括号 {} 括起,内部有属性名和属性值,如下所示。

// 对象字面量表达式的语法
{ 属性名: 属性值, 属性名: 属性值, …… }

属性名可以是标识符、字符串值或是数值。属性值则可以是任意的值或对象。具体例子如代码清单 2.4 所示。

代码清单 2.4 对象字面量表达式的例子

{ x: 2, y:1 }                                            // 属性名是标识符
{ "x":2, "y":1 }                                         // 属性名是字符串值
{ 'x':2, 'y':1 }                                         // 属性名是字符串值
{ 1:2, 2:1 }                                             // 属性名是数值
{ x:2, y:1, enable:true, color:{ r:255, g:255, b:255 } } // 各种类型的属性值

在 ECMAScript 第 5 版中,还允许下面这样以逗号结尾的对象字面量,而这在 ECMAScript 第 3 版中是被禁止的。由于会在版本较老的 Internet Explorer 中发生问题,所以应当尽可能避免在对象字面量的最后以逗号结尾。

{ x:2, y:1, }    // ECMAScript 第5 版将会忽略最后一个逗号,所以不会产生问题

对对象字面量表达式求值所得到的结果,是所生成对象的一个引用。

像下面这样,在赋值表达式的右侧书写对象字面量的话,就能够将对象的引用赋值给变量(将会在第 5 章中对引用进行详细说明)。

// 对象字面量表达式与赋值表达式
js> var obj = { x:3, y:4 };    // 所生成对象的引用将被赋值给变量 obj
js> typeof obj;                // 通过 typeof 运算符来判别 obj 的类型,得到的结果是 object
object

为方便起见,我们称变量 obj 所引用的对象是对象 obj。5.2.3 节还将对此详细说明。

2.5.3 属性访问

可以通过点运算符(.)访问对象引用中的属性。只要在点运算符之后书写属性名,就能够读取相应的属性值。

// js> print(obj.x);      // 显示对象obj 的属性x 的值
3

如果属性的值是一个对象,可以像下面这样通过多次点运算来读取其属性。

js> var obj2 = {pos: { x:3, y:4 } };
js> print(obj2.pos.x);
3

在赋值表达式的左侧书写属性访问表达式的话,就可以将相应的值赋给该属性。

js> obj.x = 33;           // 这将覆盖已有的属性值
js> print(obj.x);
33

如果赋值给尚不存在的属性名,则将新建该属性并对其赋值。

js> obj.z = 5;            // 新建属性
js> print(obj.z);
5

2.5.4 属性访问(括号方式)

除了点运算符外,还可以使用中括号运算符 [] 来访问属性。[] 内是需要访问的属性名的字符串值。

js> print(obj['x']);      // 与obj.x 相同
3

[] 里可以是字符字面量,也可以是值为字符串的变量。

js> var name = 'x';
js> print(obj[name]);     // 与obj.x 相同
33

括号运算符也能用于赋值表达式的左侧。

js> obj['5'] = 5;         // 将数值5 赋值给该属性(若该属性不存在则新建属性)

第 5 章将对分别在哪些场合下使用点运算符以及括号运算符进行说明。

2.5.5 方法

可以把任意类型的值、对象或者函数赋值给对象的属性。正如前节所讲,对匿名函数表达式求值所得到的结果是函数对象的引用,所以,也可以像下面这样来书写。

js> obj.fn = function (a, b) {return Number(a) + Number(b); };
                      // 将函数赋值给对象 obj 的属性 fn

可以像下面这样,对被赋值给属性的函数进行调用。

js> obj.fn2(3, 4);     // 调用函数
7

回顾一下之前章节的说明可以发现,在代码清单 2.2 之后还可以像下面这样书写。

js> obj.fn2 = sum;    // sum 是在代码清单2.2 中定义的函数
js> obj.fn(3, 4);     // 调用函数
7

从该调用表达式可以看出,通过点运算符来调用函数,和其他语言中的方法调用十分相似。事实上,不仅在表面上很像,其内部原理也和方法调用如出一辙。在 JavaScript 中并没有方法这种语言特性,不过实际上作为对象属性的函数也可称为一种方法 3

3在第 5 章 中将会对方法进行详细说明。

2.5.6 new 表达式

JavaScript 中 new 表达式的作用是生成一个对象。可以像下面这样使用该表达式。

// new 表达式的例子
js> var obj = new Object();
js> typeof obj;       // 通过typeof 运算符来判别obj 的类型,得到的结果是object
object

和之前说明的通过对象字面量表达式生成的对象一样,通过 new 表达式生成的对象,其属性能够被读取。

以直观的方式来理解的话,关键词 new 之后所写的是类名。不过正如此前说明,JavaScript 中没有类的概念,所以,根据 JavaScript 的语法规则,new 之后所写的是函数名。在 new 之后写函数名的话,就会把该函数作为构造函数来进行调用。

2.5.7 类与实例

再次强调一下,在 JavaScript 的语言特性中没有“类”的概念。但是在本书中为了便于理解,将用类 这个词来称呼那些可以被视作“类”的概念。也就是说,在本书中将用“类”,来称呼那些实际上将会调 用构造函数的 Function 对象。此外,在强调对象是通过调用构造函数而生成的时候,会将这些被生成的 对象称作对象实例以示区别。

综上所述,虽然在 JavaScript 中没有类的概念,但将 new 之后所写的标识符(函数名)看作是类名, 也并没有什么概念上的问题。也就是说,完全可以认为,上文中代码 new Object() 的作用是生成一个 Object 类的实例。

2.5.8 对类的功能的整理

在这一部分中,将集中对一些标准内置类(Object 类或是 String 类)的功能进行说明。现在将类的功能整理在表 2.1 中。

表 2.1 对类的功能的整理

接口

说明

函数或是构造函数的调用

-

类的属性

相当于Java 中的static 方法或是static 域

prototype 对象的属性

相当于Java 中的实例方法

实例属性

相当于Java 中的实例域

可能大家阅读了第 5 章之后,才能彻底理解表 2.1 的含义。在这里,先对此做一些简单的说明。

类的属性是一个类自身的属性,例如,String 类的属性是 String 类的对象自身的属性。如果是函数的话,则可以像 String.fromCharCode(0x41) 这样来使用。如果用更加直观一些的说法来讲,这就相当于 Java 或 C++ 中的 static 方法。

prototype 对象的属性和实例属性,都是以对象实例的形式来进行访问的。以 String 类为例,可以以 str.trim() 或是 str.length 的方式,来使用引用了 String 对象(对象实例)的变量 str。

prototype 对象的属性与实例属性之间的不同点在于是否进行了继承。例如,String 对象的 trim 方法,其实是 String.prototype 对象的属性。这种以实例来继承属性的方式被称为原型继承。

2.5.9 对象与类型

Java 中对象分为类与接口等不同类型。在 JavaScript 中不存在这样的类型区分。不过如果把对象的行为方式定义为其类型的话,JavaScript 的对象也可说是有类型的,只不过并不是 Java 那样的严格的类型。

在 Java 中,需要事先进行严格的类型定义(通常以层级方式进行管理),然后将对象置于类型层级中进行分类。然而在 JavaScript 中,则是根据不同对象的不同行为方式,在事实上对其类型做出了分类。在 Java 中,由于语法规则的需要,强制使用了基于类型层级的编程风格。与此相对,在 JavaScript 中虽然也能够定义(类似于)类(的实体),并以类型层级的方式对对象进行分类,但这仅仅是可供选择的一种风格,没有作强制性的要求。

2.6 数组的基础

数组是一种用于表达有顺序关系的值的集合的语言结构。在 JavaScript 中,数组并非是一种内建类型。相对地,JavaScript 支持 Array 类,所以数组能够以 Array 类的实例的形式实现。不过,由于有数组字面量的表达方式,所以在一般情况下,只需将其作为内建类型使用即可。

数组字面量的书写方式为,在方括号内列出所需的值。通过数组字面量就能够生成数组。

// 数组字面量的例子
js> var arr = [1, 100, 7];

数组内的各个值被称作元素。每一个元素都可以通过索引(下标)来快速读取。索引是从零开始的整数。对于上面的数组,可以像下面的代码这样,通过在方括号中书写索引值 1 来读取其第 2 个元素。

// 接之前的代码
js> print(arr[1]);         // 读取索引值为 1 的元素
100
js> arr[1] = 200;          // 为索引值为 1 的元素赋值
js> print(arr[1]);
200

在括号中不仅可以直接写某个数值,还可以写具有某一特定值的变量或表达式。

// 接之前的代码
js> var n = 1;
js> print(arr[n]);         // 与 a[1] 含义相同
200
js> print(arr[n + 1]);     // 与 a[2] 含义相同
7

JavaScript 的数组支持同时包含不同类型的元素。以下是一个同时包含数值和字符串的例子。同样, 对象以及数列也能够作为数组元素。

// 包含不同类型元素的数组的例子
js> var arr = [1, 'foo', 7];

专栏

代码书写风格

无论是哪种程序设计语言,都有其代码书写风格。虽然不遵循代码书写风格也能写出可以运行的代码,但是这会为之后阅读代码时增加不必要的麻烦。因此遵循代码书写风格是非常重要的。

JavaScript 由于其特殊的历史与定位,有着略显独特的代码书写风格。造成这一局面的背景之一,和客户端 JavaScript 的历史有关。对于客户端代码来说,代码体积的大小不但会影响运行速度,还会直接影响网络传输的性能。在 Web 的发展历史中,尤其是早期,如何减少网络数据传输量是一个重要的课题(至今仍然非常重要)。因此,JavaScript 中有着大量以减少代码书写量为目的的代码书写风格。乍一看,简洁书写似乎是一件好事,但事实上,也存在着一些看似取巧实则糟糕的做法。说到底,代码书写风格是历史的产物,不能仅仅根据是否正确来判断代码书写风格的好坏,而应该去尝试接受这些既定事实。

另一背景则和 JavaScript 普及的历史有关。其他很多程序设计语言在被广泛普及之前,往往都会有少数优秀的开发者首先使用。通常在这一时期,该语言的代码书写风格会初步成形,然后在随后更为广泛的普及过程中发生一些变化。而 JavaScript 的情况并非如此。在其普及初期,对 JavaScript 感兴趣的主要是那些书写 HTML 的网页设计师,或者一些主用其他语言的开发者,他们仅仅把 JavaScript 作为一种临时的替代品来使用。因此,JavaScript 没有属于自己的核心代码书写风格,却有着很多模仿其他语言而来的代码书写风格。比如,类似于 Java 代码风格的 JavaScript 代码,又或是类似于 PHP 代码风格的 JavaScript 代码,诸如此类。不过最近几年,具有 JavaScript 自身特点的代码书写风格正在逐渐普及。

目录

  • 版权声明
  • 注意
  • 前言
  • 第 1 部分 JavaScript 概要
  • 第 1 章 JavaScript概要
  • 第 2 部分 JavaScript 的语言基础
  • 第 2 章 JavaScript基础
  • 第 3 章 JavaScript的数据类型
  • 第 4 章 语句、表达式和运算符
  • 第 5 章 变量与对象
  • 第 6 章 函数与闭包
  • 第 7 章 数据处理
  • 第 3 部分 客户端 JavaScript
  • 第 8 章 客户端JavaScript与HTML
  • 第 9 章 DOM
  • 第 10 章 事件
  • 第 11 章 客户端JavaScript实践
  • 第 12 章 库
  • 第 4 部分 HTML5
  • 第 13 章 HTML5概要
  • 第 14 章 Web应用程序
  • 第 15 章 与桌面应用的协作
  • 第 16 章 存储
  • 第 17 章 WebSocket
  • 第 18 章 Web Workers
  • 第 5 部分 Web API
  • 第 19 章 Web API的基础
  • 第 20 章 Web API的实例
  • 第 6 部分 服务器端 JavaScript
  • 第 21 章 服务器端JavaScript与Node.js
  • 第 22 章 Node.js程序设计实践
  • 后记