在深入学习各种数据结构和算法前,让我们先大概了解一下JavaScript。本节教大家一些相关的基础知识,有利于学习后面各章。
首先来看在HTML中编写JavaScript的两种方式:
<!DOCTYPE html><html> <head> <meta charset=/"UTF-8/"> </head> <body> <script> alert(/'Hello, World!/'); </script> </body></html>
第一种方式如上面的代码所示。创建一个HTML文件,把代码写进去。在这个例子里,我们在HTML文件中声明了script
标签,然后把JavaScript代码都写进这个标签。
第二种方式,我们需要创建一个JavaScript文件(比如01-HelloWorld.js),在里面写入如下代码:
alert(/'Hello, World!/');
然后,我们的HTML文件看起来如下:
<!DOCTYPE html><html> <head> <meta charset=/"UTF-8/"> </head> <body> <script src=/"01-HelloWorld.js/"> </script> </body></html>
第二个例子展示了如何将一个JavaScript文件引入HTML文件。
这两个例子,无论执行哪个,输出都是一样的。但第二个例子是最佳实践。
可能你在网上的一些例子里看到过JavaScript的
include
语句,或者放在head
标签中的JavaScript代码。作为最佳实践,我们会在关闭body
标签前引入JavaScript代码。这样浏览器就会在加载脚本之前解析和显示HTML,有利于提升页面的性能。
1.3.1 变量
变量保存的数据可以在需要时设置、更新或提取。赋给变量的值都有对应的类型。JavaScript的类型有数字、字符串、布尔值、函数和对象。还有undefined
和null
,以及数组、日期和正则表达式。
尽管JavaScript有多种变量类型,然而不同于C/C++、C#或Java,它并不是一种强类型语言。在强类型语言中,声明变量时需要指定变量的类型(例如,在Java中声明一个整型变量,使用
int num = 1;
)。在JavaScript中,我们只需要使用关键字var
,而不必指定变量类型。因此,JavaScript不是强类型语言。
下面的例子介绍如何在JavaScript里使用变量。
var num = 1; //{1}num = 3; //{2}var price = 1.5; //{3}var name = /'Packt/'; //{4}var trueValue = true; //{5}var nullVar = null; //{6}var und; //{7}
在行
{1}
,我们展示了如何声明一个JavaScript变量(声明了一个数字类型)。虽然关键字var
不是必需的,但最好每次声明一个新变量时都加上。在行
{2}
,我们更新了已有变量。JavaScript不是强类型语言。这意味着你可以声明一个变量并初始化成一个数字类型的值,然后把它更新成字符串或者其他类型的值,不过这并不是一个好做法。在行
{3}
,我们又声明了一个数字类型的变量,不过这次是十进制浮点数。在行{4}
, 声明了一个字符串;在行{5}
,声明了一个布尔值;在行{6}
,声明了一个null
;在行{7}
, 声明了undefined
变量。null
表示变量没有值,undefined
表示变量已被声明,但尚未赋值。看看下面的例子:console.log(/"num: /"+ num);console.log(/"name: /"+ name);console.log(/"trueValue: /"+ trueValue);console.log(/"price: /"+ price);console.log(/"nullVar: /"+ nullVar);console.log(/"und: /"+ und);
如果想看我们声明的每个变量的值,可以用console.log
来实现,就像上面代码片段中那样。
书中示例代码会使用三种方式输出JavaScript的值。第一种是
alert
(/'My text here/'
),将输出到浏览器的警示窗口;第二种是console.log
(/'My text here/'
),将把文本输出到调试工具的Console标签(谷歌开发者工具或是Firebug,根据你使用的浏览器而定);第三种方式是通过document.write
(/'My text here/'
)直接输出到HTML页面里并被浏览器呈现。可以选择你喜欢的方式来调试。
console.log
方法也不止接收参数,除了console.log(/"num: /"+ num)
还可以写成console.log(/"num: /", num)
。
稍后我们会讨论函数和对象。
变量作用域
作用域指在编写的算法函数中,我们能访问的变量(在使用时,函数作用域也可以是一个函数)。有本地变量和全局变量两种。
让我们看一个例子:
var myVariable = /'global/';myOtherVariable = /'global/';function myFunction { var myVariable = /'local/'; return myVariable;}function myOtherFunction { myOtherVariable = /'local/'; return myOtherVariable;}console.log(myVariable); //{1}console.log(myFunction); //{2}console.log(myOtherVariable); //{3}console.log(myOtherFunction); //{4}console.log(myOtherVariable); //{5}
行
{1}
输出global
,因为它是一个全局变量。行
{2}
输出local
,因为myVariable
是在myFunction
函数中声明的本地变量,所以作用域仅在myFunction
内。行
{3}
输出global
,因为我们引用了在第二行初始化了的全局变量myOtherVariable
。行
{4}
输出local
。在myOtherFunction
函数里,因为没有使用var
关键字修饰,所以这里引用的是全局变量myOtherVariable
并将它赋值为local
。因此,行
{5}
会输出local
(因为在myOtherFunction
里修改了myOtherVariable
的值)。
你可能听其他人提过在JavaScript里应该尽量少用全局变量,这是对的。通常,代码质量可以用全局变量和函数的数量来考量(数量越多越糟)。因此,尽可能避免使用全局变量。
1.3.2 操作符
编程语言里都需要操作符。在JavaScript里有算数操作符、赋值操作符、比较操作符、逻辑操作符、位操作符、一元操作符和其他操作符。我们来看一下这些操作符:
var num = 0; //{1}num = num + 2;num = num * 3;num = num / 2;num++;num--;num += 1; //{2}num -= 2;num *= 3;num /= 2;num %= 3;console.log(/'num == 1 : /' + (num == 1)); // {3}console.log(/'num === 1 : /' + (num === 1));console.log(/'num != 1 : /' + (num != 1));console.log(/'num > 1 : /' + (num > 1));console.log(/'num < 1 : /' + (num < 1));console.log(/'num >= 1 : /' + (num >= 1));console.log(/'num <= 1 : /' + (num <= 1));console.log(/'true && false : /' + (true && false)); // {4}console.log(/'true || false : /' + (true || false));console.log(/'!true : /' + (!true));
在行{1}
,我们用了算数操作符。在下面的表格里,列出了这些操作符及其描述。
算数操作符
描述
+
加法
-
减法
*
乘法
/
除法
%
取余
++
递增
--
递减
在行{2}
,我们使用了赋值操作符,在下面的表格里,列出了赋值操作符及其描述。
赋值操作符
描述
=
赋值
+=
加/赋值 (x += y) == (x = x + y)
-=
减/赋值 (x -= y) == (x = x - y)
*=
乘/赋值 (x *= y) == (x = x * y)
/=
除/赋值 (x /= y) == (x = x / y)
%=
取余/赋值 (x %= y) == (x = x % y)
在行{3}
,我们使用了比较操作符。在下面的表格里,列出了比较操作符及其描述。
比较操作符
描述
==
相等
===
全等
!=
不等
>
大于
>=
大于等于
<
小于
<=
小于等于
在行 {4}
,我们使用了逻辑操作符。在下面的表格里,列出了逻辑操作符及其描述。
逻辑操作符
描述
&&
与
||
或
!
非
JavaScript也支持位操作符,如下所示:
console.log(/'5 & 1:/', (5 & 1));console.log(/'5 | 1:/', (5 | 1));console.log(/'~ 5:/', (~5));console.log(/'5 ^ 1:/', (5 ^ 1));console.log(/'5 << 1:/', (5 << 1));console.log(/'5 >> 1:/', (5 >> 1));
下面的表格对位操作符做了更详细的描述。
位操作符
描述
&
与
|
或
~
非
^
异或
<<
左移
>>
右移
typeof
操作符可以返回变量或表达式的类型。我们看下面的代码:
console.log(/'typeof num:/', typeof num);console.log(/'typeof Packt:/', typeof /'Packt/');console.log(/'typeof true:/', typeof true);console.log(/'typeof [1,2,3]:/', typeof [1,2,3]);console.log(/'typeof {name:John}:/', typeof {name:/'John/'});
输出如下:
typeof num: numbertypeof Packt: stringtypeof true: booleantypeof [1,2,3]: objecttypeof {name:John}: object
JavaScript还支持delete
操作符,可以删除对象里的属性。看看下面的代码:
var myObj = {name: /'John/', age: 21};delete myObj.age;console.log(myObj); // 输出对象{name: /"John/"}
这些操作符在后面的算法学习中可能会用到。
1.3.3 真值和假值
在JavaScript中,true和false有些复杂。在大多数编程语言中,布尔值true
和false
仅仅表示true/false。在JavaScript中,如/"Packt/"
这样的字符串值,也可以看作true
。
下面的表格能帮助我们更好地理解true和false在JavaScript中是如何转换的。
数值类型
转换成布尔值
undefined
false
null
false
布尔值
true
是true
,false
是false
数字
+0
、-0
和NaN
都是false
,其他都是true
字符串
如果字符串是空的(长度是0)就是false
,其他都是true
对象
true
我们来看一些代码,用输出来验证上面的总结:
function testTruthy(val){ return val ? console.log(/'truthy/') : console.log(/'falsy/');}testTruthy(true); //truetestTruthy(false); //falsetestTruthy(new Boolean(false)); //true (对象始终为true)testTruthy(/'/'); //falsetestTruthy(/'Packt/'); //truetestTruthy(new String(/'/')); //true (对象始终为true)testTruthy(1); //truetestTruthy(-1); //truetestTruthy(NaN); //falsetestTruthy(new Number(NaN)); //true (对象始终为true)testTruthy({}); //true (对象始终为true)var obj = {name:/'John/'};testTruthy(obj); //truetestTruthy(obj.name); //truetestTruthy(obj.age); //false (年龄不存在)
1.3.4 相等操作符(==
和===
)
当使用这两个相等操作符时,可能会引起一些困惑。
使用==时,不同类型的值也可以被看作相等。这样的结果可能会使那些资深的JavaScript开发者都感到困惑。我们用下面的表格给大家分析一下不同类型的值用相等操作符比较后的结果。
类型(x)
类型(y)
结果
null
undefined
true
undefined
null
true
数字
字符串
x == toNumber(y)
字符串
数字
toNumber(x) == y
布尔值
任何类型
toNumber(x) == y
任何类型
布尔值
x == toNumber(y)
字符串或数字
对象
x == toPrimitive(y)
对象
字符串或数字
toPrimitive(x) == y
如果 x 和 y 是相同类型,JavaScript会比较它们的值或对象值。其他没有列在这个表格中的情况都会返回false
。
toNumber
和toPrimitive
方法是内部的,并根据以下表格对其进行估值。
toNumber
方法对不同类型返回的结果如下:
值类型
结果
undefined
NaN
null
+0
布尔值
如果是true
,返回1
;如果是false
,返回+0
数字
数字对应的值
字符串
将字符串解析成数字。如果字符串中包含字母,返回NaN
;如果是由数字字符组成的,转换成数字
对象
Number(toPrimitive(vale))
toPrimitive
方法对不同类型返回的结果如下:
值类型
结果
对象
如果对象的valueOf
方法的结果是原始值,返回原始值。如果对象的toString
方法返回原始值,就返回这个值;其他情况都返回一个错误
用例子来验证一下表格中的结果。首先,我们知道下面的代码输出true
(字符串长度大于1):
console.log(/'packt/' ? true : false);
那么这行代码的结果呢?
console.log(/'packt/' == true);
输出是false
,为什么会这样呢?
首先,布尔值会被
toNumber
方法转成数字,因此得到packt == 1
。其次,用
toNumber
转换字符串值。因为字符串包含有字母,所以会被转成NaN
,表达式就变成了NaN == 1
,结果就是false
。
那么这行代码的结果呢?
console.log(/'packt/' == false);
输出也是false
。步骤如下所示。
首先,布尔值会被
toNumber
方法转成数字,因此得到packt == 0
。其次,用
toNumber
转换字符串值。因为字符串包含有字母,所以会被转成NaN
,表达式就变成了NaN == 0
,结果就是false
。
那么===
操作符呢?简单多了。如果比较的两个值类型不同,比较的结果就是false
。如果比较的两个值类型相同,结果会根据下表判断。
类型(x)
值
结果
数字
x 和 y 数值相同(但不是NaN
)
true
字符串
x 和 y 是相同的字符
true
布尔值
x 和 y 都是true
或false
true
对象
x 和 y 引用同一个对象
true
如果 x 和 y 类型不同,结果就是false
。
我们来看一些例子:
console.log(/'packt/' === true); //falseconsole.log(/'packt/' === /'packt/'); //truevar person1 = {name:/'John/'};var person2 = {name:/'John/'};console.log(person1 === person2); //false,不同的对象