引言

为什么会有这一篇“重新介绍”呢?因为JavaScript堪称世界上被人误解最深的编程语言。虽然常被嘲为“玩具语言”,但在它看似简洁的外衣下,还隐藏着强大的语言特性。JavaScript目前广泛应用于众多知名应用中,对于网页和移动开发者来说,深入理解JavaScript就尤有必要。
先从这门语言的历史谈起是有必要的。在1995年Netscape一位名为 Brendan Eich 的工程师创造了JavaScript,随后在1996年初,JavaScript首先被应用于Netscape2 浏览器上。最初的JavaScript名为LiveScript,后来因为Sun Microsystem的Java语言的兴起和广泛使用,Netscape出于宣传和推广的考虑,将它的名字从最初的 LiveScript 更改为JavaScript——尽管两者之间并没有什么共同点。这便是之后混淆产生的根源。
几个月后,Microsoft随着IE 3推出了一个与之基本兼容的语言JScript。又几个月后,Netscape将JavaScript提交至Ecma International(一个欧洲标准化组织),ECMAScript标准第一版便在1997年诞生了,随后在1999年以ECMAScript第三版的形式进行了更新,从那之后这个标准没有发生过大的改动。由于委员会在语言特性的讨论上发生分歧,ECMAScript第四版尚未推出便被废除,但随后于2009年12月发布的ECMAScript第五版引入了第四版草案加入的许多特性。第六版标准已经于2015年六月发布。
注意: 为熟悉起见,从这里开始我们将用 “JavaScript” 替代ECMAScript。 与大多数编程语言不同,JavaScript没有输入或输出的概念。它是一个在宿主环境(host environment)下运行的脚本语言,任何与外界沟通的机制都是由宿主环境提供的。浏览器是最常见的宿主环境,但在非常多的其他程序中也包含JavaScript解释器,如Adobe Acrobat、Photoshop、SVG图像、Yahoo!的Widget引擎,以及Node.js之类的服务器端环境。JavaScript的实际应用远不止这些,除此之外还有NoSQL数据库(如开源的Apache CouchDB)、嵌入式计算机,以及包括GNOME(注:GNU/Linux上最流行的GUI之一)在内的桌面环境等等。

概览

JavaScript是一种面向对象的动态语言,它包含类型、运算符、标准内置(built-in)对象和方法。它的语法来源于Java和C,所以这两种语言的许多语法特性同样适用于JavaScript。需要注意的一个主要区别是JavaScript不支持类,类这一概念在JavaScript通过对象原型(object prototype)得到延续(有关ES6类的内容参考这里Classes)。另一个主要区别是JavaScript中的函数也是对象,JavaScript允许函数在包含可执行代码的同时,能像其他对象一样被传递。
先从任何编程语言都不可缺少的组成部分——“类型”开始。JavaScript程序可以修改值(value),这些值都有各自的类型。JavaScript中的类型包括:
Number(数字)String(字符串)Boolean(布尔)Function(函数)Object(对象)Symbol(第六版新增)
…哦,还有看上去有些…奇怪的undefined(未定义)类型和null(空)类型。此外还有Array(数组)类型,以及分别用于表示日期和正则表达式的Date(日期)和RegExp(正则表达式),这三种类型都是特殊的对象。严格意义上说,Function(函数)也是一种特殊的对象。所以准确来说,JavaScript中的类型应该包括这些:
Number(数字)String(字符串)Boolean(布尔)Symbol(符号)(第六版新增)Object(对象)Function(函数)Array(数组)Date(日期)RegExp(正则表达式)Null(空)Undefined(未定义)JavaScript还有一种内置Error(错误)类型,这个会在之后的介绍中提到;现在我们先讨论下上面这些类型。

数字

根据语言规范,JavaScript采用“IEEE 754标准定义的双精度64位格式”(”double-precision 64-bit format IEEE 754 values“)表示数字。据此我们能得到一个有趣的结论,和其他编程语言(如C和Java)不同,JavaScript不区分整数值和浮点数值,所有数字在JavaScript中均用浮点数值表示,所以在进行数字运算的时候要特别注意。看看下面的例子:
0.1 + 0.2 = 0.30000000000000004
在具体实现时,整数值通常被视为32位整型变量,在个别实现(如某些浏览器)中也以32位整型变量的形式进行存储,直到它被用于执行某些32位整型不支持的操作,这是为了便于进行位操作。
JavaScript支持标准的算术运算符,包括加法、减法、取模(或取余)等等。还有一个之前没有提及的内置对象Math(数学对象),用以处理更多的高级数学函数和常数:
Math.sin(3.5); var d = Math.PI * (r + r);
你可以使用内置函数parseInt()将字符串转换为整型。该函数的第二个参数表示字符串所表示数字的基(进制):
parseInt("123", 10); // 123 parseInt("010", 10); //10
如果调用时没有提供第二个参数(字符串所表示数字的基),2013年以前的JavaScript实现会返回一个意外的结果:
parseInt("010"); // 8 parseInt("0x10"); // 16
这是因为字符串以数字0开头,parseInt()函数会把这样的字符串视作八进制数字;同理,0x开头的字符串则视为十六进制数字。
如果想把一个二进制数字字符串转换成整数值,只要把第二个参数设置为2就可以了:
parseInt("11", 2); // 3
JavaScript还有一个类似的内置函数parseFloat(),用以解析浮点数字符串,与parseInt()不同的地方是,parseFloat()只应用于解析十进制数字。
单元运算符 + 也可以把数字字符串转换成数值:
+ "42"; // 42 + "010"; // 10 + "0x10"; // 16
如果给定的字符串不存在数值形式,函数会返回一个特殊的值NaN(Not a Number的缩写):
parseInt("hello", 10); // NaN
要小心NaN:如果把NaN作为参数进行任何数学运算,结果也会是NaN:
NaN + 5; //NaN
可以使用内置函数isNaN()来判断一个变量是否为NaN:
isNaN(NaN); // true
JavaScript还有两个特殊值:Infinity(正无穷)和-Infinity(负无穷):
1 / 0; // Infinity -1 / 0; // -Infinity
可以使用内置函数isFinite()来判断一个变量是否是一个有穷数, 如果类型为Infinity,-Infinity或NaN则返回false:
isFinite(1/0); // false isFinite(Infinity); // false isFinite(NaN); // false isFinite(-Infinity); // false isFinite(0); // true isFinite(2e64); // true isFinite("0"); // true,如果是纯数值类型的检测,则返回false:Number.isFinite("0");
备注:parseInt()和parseFloat()函数会尝试逐个解析字符串中的字符,直到遇上一个无法被解析成数字的字符,然后返回该字符前所有数字字符组成的数字。使用运算符 “+“ 将字符串转换成数字,只要字符串中含有无法被解析成数字的字符,该字符串都将被转换成NaN。请你用这两种方法分别解析“10.2abc”这一字符串,比较得到的结果,理解这两种方法的区别。

字符串

JavaScript中的字符串是一串Unicode字符序列。这对于那些需要和多语种网页打交道的开发者来说是个好消息。更准确地说,它们是一串UTF-16编码单元的序列,每一个编码单元由一个16位二进制数表示。每一个Unicode字符由一个或两个编码单元来表示。
如果想表示一个单独的字符,只需使用长度为1的字符串。
通过访问字符串的长度(编码单元的个数)属性可以得到它的长度。
"hello".length; // 5
这是我们第一次碰到JavaScript对象。我们有没有提过你可以像objects一样使用字符串?是的,字符串也有methods(方法)能让你操作字符串和获取字符串的信息。
"hello".charAt(0); // "h" "hello, world".replace("hello", "goodbye"); // "goodbye, world" "hello".toUpperCase(); // "HELLO"

其他类型

JavaScript中null和undefined是不同的,前者表示一个空值(non-value),必须使用null关键字才能访问,后者是“undefined(未定义)”类型的对象,表示一个未初始化的值,也就是还没有被分配的值。我们之后再具体讨论变量,但有一点可以先简单说明一下,JavaScript允许声明变量但不对其赋值,一个未被赋值的变量就是undefined类型。还有一点需要说明的是,undefined实际上是一个不允许修改的常量。
JavaScript包含布尔类型,这个类型的变量有两个可能的值,分别是true和false(两者都是关键字)。根据具体需要,JavaScript按照如下规则将变量转换成布尔类型:
false、0、空字符串(“”)、NaN、null和undefined被转换为false所有其他值被转换为true也可以使用Boolean()函数进行显式转换:
Boolean(""); // false Boolean(234); // true
不过一般没必要这么做,因为JavaScript会在需要一个布尔变量时隐式完成这个转换操作(比如在if条件语句中)。所以,有时我们可以把转换成布尔值后的变量分别称为 真值(true values)——即值为true和 假值(false values)——即值为false;也可以分别称为“真的”(truthy)和“假的”(falsy)。
JavaScript支持包括&&(逻辑与)、||(逻辑或)和!(逻辑非)在内的逻辑运算符。下面会有所提到。

变量

在JavaScript中声明一个新变量的方法是使用关键字var:
var a; var name = "simon";
如果声明了一个变量却没有对其赋值,那么这个变量的类型就是undefined。
JavaScript与其他语言的(如Java)的重要区别是在JavaScript中语句块(blocks)是没有作用域的,只有函数有作用域。因此如果在一个复合语句中(如if控制结构中)使用var声明一个变量,那么它的作用域是整个函数(复合语句在函数中)。 但是从ECMAScriptEdition 6开始将有所不同的,let和const关键字允许你创建块作用域的变量。

运算符

JavaScript的算术操作符包括+、-、*、/和%——求余(与模运算不同)。