JavaScript是世界上最流行的脚本语言,因为你在电脑、手机、平板上浏览的所有的网页,以及无数基于HTML5的手机App,交互逻辑都是由JavaScript驱动的。
简单地说,JavaScript是一种运行在浏览器中的解释型的编程语言。
基本语法规则
JavaScript 的语法和 Java 类似,每个语句以 ; 结束,语句块用 {...} 。但是,JavaScript 并不强制要求在每个语句结尾加 ; ,JavaScript 引擎会自动在每个语句的结尾补上 ; 。
示例:
1 | // 一个完整的赋值语句 |
以//开头直到行末的字符被视为单行注释,注释是给开发人员看到,JavaScript引擎会自动忽略:
1 | // 这是一行注释 |
用/*...*/把多行字符包裹起来,为一个多行注释:
1 | /* 从这里开始是块注释 |
数据类型和变量
数据类型
在JavaScript中定义了以下几种数据类型:
Number
JavaScript不区分整数和浮点数,统一用Number表示,以下都是合法的Number类型:
1 | 123; // 整数123 |
计算机由于使用二进制,所以,有时候用十六进制表示整数比较方便,十六进制用0x前缀和0-9,a-f表示,例如:0xff00,0xa5b4c3d2,等等,它们和十进制表示的数值完全一样。
Number可以直接做四则运算,规则和数学一致:
1 | 1 + 2; // 3 |
注意%是求余运算。
字符串
字符串是以单引号’或双引号”括起来的任意文本,比如'abc',"xyz"等等。请注意,''或""本身只是一种表示方式,不是字符串的一部分,因此,字符串'abc'只有a,b,c这3个字符。
布尔值
布尔值和布尔代数的表示完全一致,一个布尔值只有true、false两种值,要么是true,要么是false,可以直接用true、false表示布尔值,也可以通过布尔运算计算出来:
1 | true; // 这是一个true值 |
逻辑运算符
与(&&)运算,只有所有都为true,结果才是true:
1 | true && true; // 这个&&语句计算结果为true |
或(||)运算,只要其中有一个为true,结果就是true:
1 | false || false; // 这个||语句计算结果为false |
非(!)运算,它是一个单目运算符,把true变成false,false变成true:
1 | ! true; // 结果为false |
布尔值经常用在条件判断中,比如:
1 | var age = 15; |
比较运算符
当我们对Number做比较时,可以通过比较运算符得到一个布尔值:
1 | 2 > 5; // false5 >= 2; // true7 == 7; // true |
实际上,JavaScript允许对任意数据类型做比较:
1 | false == 0; // truefalse === 0; // false |
要特别注意相等运算符==。JavaScript在设计时,有两种比较运算符:
第一种是==比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;
第二种是===比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。
由于JavaScript这个设计缺陷,不要使用==比较,始终坚持使用===比较。
另一个例外是NaN这个特殊的Number与所有其他值都不相等,包括它自己:
1 | NaN === NaN; // false |
唯一能判断NaN的方法是通过isNaN()函数:
1 | isNaN(NaN); // true |
最后要注意浮点数的相等比较:
1 | 1 / 3 === (1 - 2 / 3); // false |
这不是JavaScript的设计缺陷。浮点数在运算过程中会产生误差,因为计算机无法精确表示无限循环小数。要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值:
1 | Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true |
null和undefined
null表示一个“空”的值,它和0以及空字符串''不同,0是一个数值,''表示长度为0的字符串,而null表示“空”。
在其他语言中,也有类似JavaScript的null的表示,例如Java也用null,Swift用nil,Python用None表示。但是,在JavaScript中,还有一个和null类似的undefined,它表示“未定义”。
大多数情况下,我们都应该用null。undefined仅仅在判断函数参数是否传递的情况下有用。
数组
数组是一组按顺序排列的集合,集合的每个值称为元素。JavaScript的数组可以包括任意数据类型。例如:
1 | [1, 2, 3.14, 'Hello', null, true]; |
另一种创建数组的方法是通过Array()函数实现:
1 | new Array(1, 2, 3); // 创建了数组[1, 2, 3] |
然而,出于代码的可读性考虑,强烈建议直接使用[]。
数组的元素可以通过索引来访问。请注意,索引的起始值为0:
1 | var arr = [1, 2, 3.14, 'Hello', null, true]; |
对象
JavaScript的对象是一组由键-值组成的无序集合,例如:
1 | var person = { |
JavaScript对象的键都是字符串类型,值可以是任意数据类型。上述person对象一共定义了6个键值对,其中每个键又称为对象的属性,例如,person的name属性为'Bob',zipcode属性为null。
要获取一个对象的属性,我们用对象变量.属性名的方式:
1 | person.name; // 'Bob' |
变量
变量在JavaScript中就是用一个变量名表示,变量名是大小写英文、数字、$和_的组合,且不能用数字开头。变量名也不能是JavaScript的关键字,如if、while等。申明一个变量用var语句,比如:
1 | var a; // 申明了变量a,此时a的值为undefined |
在JavaScript中,使用等号=对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var申明一次,例如:
1 | var a = 123; // a的值是整数123 |
字符串
JavaScript的字符串就是用''或""括起来的字符表示。
如果'本身也是一个字符,那就可以用""括起来,比如"I'm OK"包含的字符是I,',m,空格,O,K这6个字符。
如果字符串内部既包含'又包含"怎么办?可以用转义字符\来标识,比如:
1 | var a = 'I\'m \"OK\"!'; |
转义字符\可以转义很多字符,比如\n表示换行,\t表示制表符,字符\本身也要转义,所以\\表示的字符就是\。
ASCII字符可以以\x##形式的十六进制表示,例如:
1 | '\x41'; // 完全等同于 'A' |
还可以用\u####表示一个Unicode字符:
1 | '\u4e2d\u6587'; // 完全等同于 '中文' |
多行字符串
由于多行字符串用\n写起来比较费事,所以最新的ES6标准新增了一种多行字符串的表示方法,用反引号 ` 表示:
1 | `这是一个 |
模板字符串
要把多个字符串连接起来,可以用+号连接:
1 | var name = '小明'; |
如果有很多变量需要连接,用+号就比较麻烦。ES6新增了一种模板字符串,表示方法和上面的多行字符串一样,但是它会自动替换字符串中的变量:
1 | var name = '小明'; |
操作字符串
字符串常见的操作如下:
1 | var s = 'Hello, world!'; |
要获取字符串某个指定位置的字符,使用类似Array的下标操作,索引号从0开始:
1 | var s = 'Hello, world!'; |
需要特别注意的是,字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,但是,也没有任何效果:
1 | var s = 'Test'; |
JavaScript为字符串提供了一些常用方法,注意,调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串:
toUpperCase
toUpperCase()把一个字符串全部变为大写:1
2var s = 'Hello';
s.toUpperCase(); // 返回'HELLO'toLowerCase
toLowerCase()把一个字符串全部变为小写:1
2
3var s = 'Hello';
var lower = s.toLowerCase(); // 返回'hello'并赋值给变量lower
lower; // 'hello'indexOf
indexOf()会搜索指定字符串出现的位置:1
2
3var s = 'hello, world';
s.indexOf('world'); // 返回7
s.indexOf('World'); // 没有找到指定的子串,返回-1substring
substring()返回指定索引区间的子串:1
2
3var s = 'hello, world'
s.substring(0, 5); // 从索引0开始到5(不包括5),返回'hello'
s.substring(7); // 从索引7开始到结束,返回'world'数组
JavaScript的Array可以包含任意数据类型,并通过索引来访问每个元素。
要取得Array的长度,直接访问length属性:
1 | var arr = [1, 2, 3.14, 'Hello', null, true]; |
注意,直接给Array的length赋一个新的值会导致Array大小的变化:
1 | var arr = [1, 2, 3]; |
Array可以通过索引把对应的元素修改为新的值,因此,对Array的索引进行赋值会直接修改这个Array:
1 | var arr = ['A', 'B', 'C']; |
注意,如果通过索引赋值时,索引超过了范围,同样会引起Array大小的变化:
1 | var arr = [1, 2, 3]; |
大多数其他编程语言不允许直接改变数组的大小,越界访问索引会报错。然而,JavaScript的Array却不会有任何错误。在编写代码时,不建议直接修改Array的大小,访问索引时要确保索引不会越界。
indexOf
与String类似,Array也可以通过indexOf()来搜索一个指定的元素的位置:
1 | var arr = [10, 20, '30', 'xyz']; |
slice
slice()就是对应String的substring()版本,它截取Array的部分元素,然后返回一个新的Array:
1 | var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; |
push 和 pop
push()向Array的末尾添加若干元素,pop()则把Array的最后一个元素删除掉:
1 | var arr = [1, 2]; |
unshift和shift
如果要往Array的头部添加若干元素,使用unshift()方法,shift()方法则把Array的第一个元素删掉:
1 | var arr = [1, 2]; |
sort
sort()可以对当前Array进行排序,它会直接修改当前Array的元素位置,直接调用时,按照默认顺序排序:
1 | var arr = ['B', 'C', 'A']; |
reverse
reverse()把整个Array的元素给调个个,也就是反转:
1 | var arr = ['one', 'two', 'three']; |
splice
splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:
1 | var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle']; |
concat
concat()方法把当前的Array和另一个Array连接起来,并返回一个新的Array:
1 | var arr = ['A', 'B', 'C']; |
注意,concat()方法并没有修改当前Array,而是返回了一个新的Array。
join
join()方法是一个非常实用的方法,它把当前Array的每个元素都用指定的字符串连接起来,然后返回连接后的字符串:
1 | var arr = ['A', 'B', 'C', 1, 2, 3]; |
如果Array的元素不是字符串,将自动转换为字符串后再连接。
多维数组
如果数组的某个元素又是一个Array,则可以形成多维数组,例如:
1 | var arr = [[1, 2, 3], [400, 500, 600], '-']; |
条件判断
JavaScript使用if () { ... } else { ... }来进行条件判断。例如,根据年龄显示不同内容,可以用if语句实现如下:
1 | var age = 20; |
多行条件判断
如果还要更细致地判断条件,可以使用多个if...else...的组合:
1 | var age = 3; |
注意,if...else...语句的执行特点是二选一,在多个if...else...语句中,如果某个条件成立,则后续就不再继续判断了。
练习
小明身高1.75,体重80.5kg。请根据BMI公式(体重除以身高的平方)帮小明计算他的BMI指数,并根据BMI指数:
- 低于18.5:过轻
- 18.5-25:正常
- 25-28:过重
- 28-32:肥胖
- 高于32:严重肥胖
用if...else...判断并显示结果:
1 | var height = parseFloat(prompt('请输入身高(m):')); |
循环
for 循环
for循环,通过初始条件、结束条件和递增条件来循环执行语句块:
1 | var x = 0; |
- i=1 这是初始条件,将变量i置为1;
- i<=10000 这是判断条件,满足时就继续循环,不满足就退出循环;
- i++ 这是每次循环后的递增条件,由于每次循环后变量i都会加1,因此它终将在若干次循环后不满足判断条件
i<=10000而退出循环。
练习
利用for循环计算1 * 2 * 3 * ... * 10的结果:
1 | var x = 1; |
for循环最常用的地方是利用索引来遍历数组:
1 | var arr = ['Apple', 'Google', 'Microsoft']; |
for循环的3个条件都是可以省略的,如果没有退出循环的判断条件,就必须使用break语句退出循环,否则就是死循环:
1 | var x = 0; |
for … in
for循环的一个变体是for ... in循环,它可以把一个对象的所有属性依次循环出来:
1 | var o = { |
要过滤掉对象继承的属性,用hasOwnProperty()来实现:
1 | var o = { |
由于Array也是对象,而它的每个元素的索引被视为对象的属性,因此,for ... in循环可以直接循环出Array的索引:
1 | var a = ['A', 'B', 'C']; |
注意,for ... in对Array的循环得到的是String而不是Number。
while
while循环只有一个判断条件,条件满足,就不断循环,条件不满足时则退出循环。比如我们要计算100以内所有奇数之和,可以用while循环实现:
1 | var x = 0; |
在循环内部变量n不断自减,直到变为-1时,不再满足while条件,循环退出。
do … while
最后一种循环是do { ... } while()循环,它和while循环的唯一区别在于,不是在每次循环开始的时候判断条件,而是在每次循环完成的时候判断条件:
1 | var n = 0; |
用do { ... } while()循环要小心,循环体会至少执行1次,而for和while循环则可能一次都不执行。
练习
请利用循环遍历数组中的每个名字,并显示Hello, xxx!:
1 | var arr = ['Bart', 'Lisa', 'Adam']; |
Map和Set
JavaScript的默认对象表示方式{}可以视为其他语言中的Map或Dictionary的数据结构,即一组键值对。
Map
Map是一组键值对的结构,具有极快的查找速度。
假设要根据同学的名字查找对应的成绩,如果用Array实现,需要两个Array:
1 | var names = ['Michael', 'Bob', 'Tracy']; |
给定一个名字,要查找对应的成绩,就先要在names中找到对应的位置,再从scores取出对应的成绩,Array越长,耗时越长。
如果用Map实现,只需要一个“名字”-“成绩”的对照表,直接根据名字查找成绩,无论这个表有多大,查找速度都不会变慢。用JavaScript写一个Map如下:
1 | var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]); |
初始化Map需要一个二维数组,或者直接初始化一个空Map。Map具有以下方法:
1 | var m = new Map(); // 空Map |
由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:
1 | var m = new Map(); |
Set
Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。
要创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set:
1 | var s1 = new Set(); // 空Set |
重复元素在Set中自动被过滤:
1 | var s = new Set([1, 2, 3, 3, '3']); |
注意数字3和字符串'3'是不同的元素。
通过add(key)方法可以添加元素到Set中,可以重复添加,但不会有效果:
1 | s.add(4); |
通过delete(key)方法可以删除元素:
1 | var s = new Set([1, 2, 3]); |
iterable
遍历Array可以采用下标循环,遍历Map和Set就无法使用下标。为了统一集合类型,ES6标准引入了新的iterable类型,Array、Map和Set都属于iterable类型。
具有iterable类型的集合可以通过新的for ... of循环来遍历。
用for ... of循环遍历集合,用法如下:
1 | var a = ['A', 'B', 'C']; |
for ... in循环将把name包括在内,但Array的length属性却不包括在内。
for ... of循环则完全修复了这些问题,它只循环集合本身的元素:
1 | var a = ['A', 'B', 'C']; |
直接使用iterable内置的forEach方法,它接收一个函数,每次迭代就自动回调该函数。以Array为例:
1 | var a = ['A', 'B', 'C']; |
Set与Array类似,但Set没有索引,因此回调函数的前两个参数都是元素本身:
1 | var s = new Set(['A', 'B', 'C']); |
Map的回调函数参数依次为value、key和map本身:
1 | var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]); |
如果对某些参数不感兴趣,由于JavaScript的函数调用不要求参数必须一致,因此可以忽略它们。例如,只需要获得Array的element:
1 | var a = ['A', 'B', 'C']; |
@o@ 本系列笔记是 记录学习 JavaScript 学习的点点滴滴,参考资料 廖雪峰 - JavaScript 教程
