JavaScript
JavaScript 是世界上最流行的脚本语言,在电脑、手机、平板上浏览的所有的网页、以及武术基于HTML5的手机App,交互逻辑都是由JavaScript驱动的。
一、快速入门
1.基本语法
- JavaScript 的语法和 Java 语言类似,每个语句以
;
结束,语句块用{...}
。但是,JavaScript 并不强制要求在每个语句的结尾加;
,浏览器中负责执行 JavaScript 代码的引擎会自动在每个语句的结尾不上;
。 - 让 JavaScript 引擎自动加分号在某些情况下会改变程序的语义,导致运行结果与期望不一致。所以尽量不要省略
;
,所有语句都添加;
- 行注释://
- 块注释:/* */
- JavaScript 严格区分大小写,如果弄错了大小写,程序将会报错或者运行不正常。
2.数据类型和变量
-
Number:JavaScript不区分整数和浮点数,统一用 Number 表示,一下都是合法的 Number 类型:
123; // 整数123 0.456; // 浮点数0.456 1.2345e3; // 科学计数法表示1.2345x1000,等同于1234.5 -99; // 负数 NaN; // NaN表示Not a Number,当无法计算结果时用NaN表示 Infinity; // Infinity表示无限大,当数值超过了JavaScript的Number所能表示的最大值时,就表示为Infinity
Number 可以直接做四则运算,规则和数学一致
-
字符串:以单引号 或 双引号括起来的任意文本,比如:
'abc'
,"xzy"
-
布尔值:一个布尔值只有
true
、false
两种值 -
逻辑运算符:与 Java 一样
-
比较运算符:与 Java 一样
JavaScript 允许对任意数据类型做比较
false == 0; // true false === 0; // false
JavaScript 在设计时,有两种比较运算符:
第一种是
==
比较,他会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;第二种是
===
比较,它不会自动转换数据类型,如果数据类型不一致,返回false
,如果一致,再比较。由于 JavaScript 这个设计缺陷,不要使用
==
比较,始终坚持使用===
比较。另一个例外是
NaN
这个特殊的 Number 与所有其他值都不相等,包括它自己:NaN === NaN; // false
唯一能判断
NaN
的方法是通过isNaN
函数:isNaN(NaN); // true
最后注意浮点数的相等比较:
1 / 3 === (1 - 2 / 3); // false
这不是 JavaScript 的设计缺陷。浮点数在运算过程中国会产生误差,因为计算机无法精确表示无限循环小数,要比较两个浮点数是否相等,只能计算它们只差的绝对值,看是否小于某个阈值:
Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true
-
null 和 undefined
null
表示一个"空"值,它和0
以及空字符串''
不同undefined
表示"未定义",大多数情况下,我们都应该用null
。undefined
仅仅在判断函数参数是否传递的情况下有用。 -
数组
数组是一组按顺序排列的集合,集合的每个值称为元素。JavaScript 的数组可以包括任意数据类型。例如:
[1, 2, 3.14, 'Hello', null, true];
另一种创建数组的方法
new Array(1, 2, 3); // 创建了数组[1, 2, 3]
数组的元素可以通过索引来访问,索引的起始值为
0
-
对象
JavaScript 的对象是一组由键-值组成的无序集合,例如:
var person = { name: 'Bob', age: 20, tags: ['js', 'web', 'mobile'], city: 'Beijing', hasCar: true, zipcode: null };
JavaScript 对象的键都是字符串类型,值可以是任意数据类型。
要获取一个对象的属性,我们用
对象名.属性名
的方式person.name; // 'Bob' person.zipcode; // null
-
变量
变量在 JavaScript 中就是用一个变量名表示,变量名是大小写英文、数字、
$
和_
的组合,且不能用数字开头。变量名也不能是 JavaScript 的关键字声明一个变量用
var
和let
语句,比如:var a; // 申明了变量a,此时a的值为undefined var $b = 1; // 申明了变量$b,同时给$b赋值,此时$b的值为1 var s_007 = '007'; // s_007是一个字符串 var Answer = true; // Answer是一个布尔值true var t = null; // t的值是null let b = 2; //声明另一个变量a,同时给 b 赋值
可以吧任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能声明一次
-
strict 模式
JavaScript设计之初,并不强制要求用
var
、let
声明变量,如果一个变量没有通过var
声明就被使用,那么该变量就自动被声明为全局变量,在同一个页面的不同 JavaScript 文件中,如果都不用var
声明,恰好都是用变量i
,将造成变量i
相互影响,产生那一调试的错误结果启用
strict
严格检查模式,在 JavaScript 代码的第一行写上:<script> 'use strict'; </script>
启用后,未使用关键字声明变量将导致运行错误
不支持 strict 模式的浏览器会把它当做一个字符串语句执行,支持 strict 模式的浏览器将会开启 strict 模式运行 JavaScript
写 JavaScript 代码,建议采用 strict 模式
3.字符串
-
JavaScript 的字符串就是用
''
或""
括起来的字符表示 -
如果
'
本身也是一个字符,那就可以用""
括起来,比如:"I'm OK"
,如果字符串内部既包含'
又包含"
,可以用转义字符\
来标识'I\'m \"OK\"! '; // I'm "OK"!
转义字符可以转义很多字符:
\n // 换行 \t // 制表符 \\ // 转义自己
ASCII 字符可以以
\x##
形式的十六进制表示,例如:'\x41'; // 完全等用于 'A'
还可以用
\u####
表示一个Unicode 字符:'\u4e2d\u6587'; // 完全等同于 '中文'
-
多行字符串
用
tab
键上面的反引号括起来` 这是一个 多行 字符串 `;
-
模板字符串
这个类似 Java 中的 EL 表达式
${变量名}
let name = '小明'; let age = 20; let message = `你好,${name},你今年${age}岁了!`; alert(message); // 可以试一下输出的是什么
-
操作字符串
字符串长度
var s = 'Hello, world!'; s.length; // 13
要获取字符串某个指定位置的字符,使用类似 Array 的下标操作,索引号从 0 开始:
var s = 'Hello, world!'; s[0]; // 'H' s[6]; // ' ' s[7]; // 'w' s[12]; // '!' s[13]; // undefined 超出范围的索引不会报错,但一律返回undefined
需要特别注意的是,字符串时不可变的,如果对字符串的某个索引赋值,不会有任何错误,但是,也没有任何效果:
var s = 'Test'; s[0] = 'X'; alert(s); // s仍然为'Test'
JavaScript为字符串提供了一些常用的方法,注意,调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串
toUpperCase()
把一个字符串全部变为大写:var s = 'hello'; s.toUpperCase(); // 返回 ‘HELLO’
toLowerCase()
把一个字符串全部变为小写:var s = 'Hello'; var lower = s.toLowerCase(); lower; // 'hello'
indexOf()
会搜索指定字符串出现的位置:var s = 'hello, world'; s.indexOf('world'); // 返回 7 s.indexOf('Wordl'); // 没有找到指定的子串,返回 -1
substring()
返回指定索引区间的子串:vat s = 'hello, world'; s.substring(0, 5); // 从索引0开始到5(不包括5),返回'hello' s.substring(7); // 从索引7开始到结束,返回'world'
4.数组
-
JavaScript的 Array 可以包含任意数据类型,并通过索引来访问每个元素
-
要取得 Array 的长度,直接访问
length
属性:var arr = [1, 2, 3.14, 'Hello', null, true]; arr.length; // 6
直接给 Array 的 length 赋一个新的值会导致 Array 大小的变化:
var arr = [1, 2, 3]; arr.length; // 3 arr.length = 6; arr; // arr变为[1, 2, 3, undefined, undefined, undefined] arr.length = 2; arr; // arr变为[1, 2]
Array 可以通过索引把对应的元素修改为新的值,因此,对 Array 的索引进行赋值会直接修改这个 Array :
var arr = ['A', 'B', 'C'] arr[1] = 99; arr; // arr现在变为['A', 99, 'C']
注意
,如果通过索引赋值时,索引超过了范围,同样会引起 Array 大小的变化:var arr = [1, 2, 3]; arr[5] = 'x'; arr; // arr变为[1, 2, 3, undefined, undefined, 'x']
在编写代码时,不建议直接修改 Array 的大小,范文索引时要确保索引不会越界
-
Array 也可以通过
indexOf
来搜索一个指定的元素位置:var arr = [10, 20, '30', 'xyz']; arr.indexOf(10); // 元素10的索引为0 arr.indexOf(20); // 元素20的索引为1 arr.indexOf(30); // 元素30没有找到,返回-1 arr.indexOf('30'); // 元素'30'的索引为2
-
slice()
,他截取 Array 的部分元素,然后返回一个新的 Array:var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; arr.slice(0, 3); // 从索引0开始,到索引3结束,但不包括索引3: ['A', 'B', 'C'] arr.slice(3); // 从索引3开始到结束: ['D', 'E', 'F', 'G']
如果不给
slice()
传递任何参数,它就会从头到尾截取所有元素。利用这一点,我们可以复制一个 Array:var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; var aCopy = arr.slice(); aCopy; // ['A', 'B', 'C', 'D', 'E', 'F', 'G'] aCopy === arr; // false
-
push()
向 Array 的末尾添加若干元素,pop()
则把 Array 的最后一个元素删除掉:var arr = [1, 2]; arr.push('A', 'B'); // 返回Array新的长度: 4 arr; // [1, 2, 'A', 'B'] arr.pop(); // pop()返回'B' arr; // [1, 2, 'A'] arr.pop(); arr.pop(); arr.pop(); // 连续pop 3次 arr; // [] arr.pop(); // 空数组继续pop不会报错,而是返回undefined arr; // []
-
如果要往 Array 的头部添加若干元素,使用
unshift()
方法,shift()
方法则把 Array 的第一个元素删掉:var arr = [1, 2]; arr.unshift('A', 'B'); // 返回Array新的长度: 4 arr; // ['A', 'B', 1, 2] arr.shift(); // 'A' arr; // ['B', 1, 2] arr.shift(); arr.shift(); arr.shift(); // 连续shift 3次 arr; // [] arr.shift(); // 空数组继续shift不会报错,而是返回undefined arr; // []
-
sort()
可以对当前 Array 进行排序,他会直接修改当前 Array 的元素位置,直接调用时,按照默认顺序排序:vat arr = ['B', 'C', 'A']; arr.sort(); arr; //['A', 'B', 'C']
-
reverse()
把整个 Array 数组反转过来:var arr = ['one', 'two', 'three']; arr.reverse(); arr; // ['three', 'two', 'one']
-
splice()
方法是修改 Array 的万能方法,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle']; // 从索引2开始删除3个元素,然后再添加两个元素: arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite'] arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle'] // 只删除,不添加: arr.splice(2, 2); // ['Google', 'Facebook'] arr; // ['Microsoft', 'Apple', 'Oracle'] // 只添加,不删除: arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素 arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
-
concat()
方法把当前的 Array 和另一个 Array 连接起来,并返回一个新的 Array:var arr = ['A', 'B', 'C']; var added = arr.concat([1, 2, 3]); added; // ['A', 'B', 'C', 1, 2, 3] arr; // ['A', 'B', 'C']
注意,concat() 方法并没有修改当前 Array,而是返回了一个新的 Array。
实际上,concat() 方法可以接受任意个元素和 Array,并且自动把 Array 拆开,然后全部添加到新的 Array 里:
var arr = ['A', 'B', 'C']; arr.concat(1, 2, [3, 4]); // ['A', 'B', 'C', 1, 2, 3, 4]
-
join()
方法是一个非常实用的方法,它把当前 Array 的每个元素都用指定的字符串连接起来,然后返回连接后的字符串:var arr = ['A', 'B', 'C', 1, 2, 3]; arr.join('-'); // 'A-B-C-1-2-3'
如果 Array 的元素不是字符串,将自动转换为字符串后再连接。
-
多维数组
如果数组的某个元素又是一个 Array,则可以形成多维数组,例如:
let arr = [[1, 2, 3], [400, 500, 600], '-'];
5.对象
JavaScript 的对象是一种无序的集合数据类型,它又若干键值对组成。
-
JavaScript 的对象用于描述现实世界中的某个对象,例如:小明
var xiaoming = { name: '小明', birth: 1990, school: 'No.1 Middle School', height: 1.70, weight: 65, score: null };
JavaScript 用一个
{...}
表示一个对象,键值对以xxx:xxx
形式声明,用,
隔开,注意,最后一个键值对不需要在末尾加,
可以通过变量
xiaoming
来获取小明的属性:xiaoming.name; // '小明' xiaoming.birth; // 1990
-
访问属性是通过
.
操作符完成,但这要求属性名必须是一个有效的变量名。如果属性名包含特殊字符,就必须用''
括起来:var xiaohong = { name: '小红', 'middle-school': 'No.1 Middle School' };
xiaohong
的属性名middle-school
不是一个有效的变量,就需要用''
括起来。访问这个属性也无法使用.
操作符,必须用['xxx']
来访问:xiaohong['middle-school']; // 'No.1 Middle School' xiaohong['name']; // '小红' xiaohong.name; // '小红'
也可以用
xiaohong['name']
来访问xiaohong
的name
属性,不过xiaohong.name
的写法更简洁。尽量使用标准的变量名,这样就可以直接通过object.prop
的形式访问一个属性了。 -
实际上 JavaScript 对象的所有属性都是字符串,不过属性对应的值可以是任意数据类型。
-
如果访问一个不存在的属性不会报错,而是返回
undefined
:'use strict'; var xiaoming = { name: '小明' }; console.log(xiaoming.age); //undefined
-
由于 JavaScript 的对象是动态类型,可以自由地给一个对象添加或删除属性:
var xiaoming = { name: '小明' }; xiaoming.age; //undefined xiaoming.age = 18; //新增一个 age 属性 xiaoming.age; // 18 delete xiaoming.age; //删除 age 属性 xiaoming.age; //undefined delete xiaoming['name']; //删除 name 属性 xiaoming.name; //undefined delete xiaoming.school; // 删除一个不存在的 school 属性也不会报错
-
如果我们要检查
xiaoming
是否拥有某一属性,可以用in
操作符:var xiaoming = { name: '小明', birth: 1990, school: 'No.1 Middle School', height: 1.70, weight: 65, score: null }; 'name' in xiaoming; //true 'grade' in xiaoming; //false
-
不过要小心,如果
in
判断一个属性存在,这个属性不一定是xiaoming
的,它可能是xiaoming
继承得到的:'toString' in xiaoming; //true
因为
toString
定义在object
对象中,而所有对象最终都会在原型链上指向object
,所以xiaoming
也拥有toString
属性。 -
要判断一个属性是否是
xiaoming
自身拥有的,而不是继承得到的,可以用hasOwnProperty()
方法:var xiaoming = { name: '小明' }; xiaoming.hasOwnProperty('name'); //true xiaoming.hasOwnProperty('toString'); //false
6.条件判断
-
JavaScript 使用
if () {...} else {...}
来进行条件判断var age = 20; if (age >= 18){ alert('adult'); } else { alert('teenager'); }
-
else
语句是可选的 -
if
或者else
下只有一条语句的时候可以省略{}
-
为了良好的编程习惯,建议永远写上
{}
-
多行条件判断
if else
嵌套使用var age = 3; if (age >= 18){ alert('adult'); } else if (age >= 6){ alert('teenager'); } else { alert('kid'); }
-
如果
if
的条件判断语句结果不是true
或false
怎么办?例如:var s = '123'; if (s.length) { //条件计算结果为 3 alert('hello'); }
运行这个代码的结果是会有一个弹窗弹出 ‘hello’
JavaScript 把 null、undefined、0、NaN 和空字符串 ‘’ 视为
false
, 其他值一概视为true
,因此上述代码条件判断的结果是true
7.循环
-
for
循环遍历数组var arr = ['apple', 'google', 'microsoft']; var i, x; for (i = 0; i < arr.length; i++){ x = arr[i]; console.log(x); }
-
for
循环的 3 个条件都是可以省略的,如果没有退出循环的判断条件,就必须使用break
语句退出循环,否则就是死循环 -
for
循环的一个变体是for ... in
循环,它可以把一个对象的所有属性一次循环出来:var o = { name: 'Jack', age: 20, city: 'xian' }; for (var key in o){ console.log(key); //'name', 'age', 'city' }
-
要过滤掉对象继承的属性,用
hasOwnProperty()
来实现:var o = { name: 'Jack', age: 20, city: 'xian' }; for (var key in 0){ if (o.hasOwnProperty(key)){ console.log(key); //'name', 'age', 'city' } }
-
由于 Array 也是对象,而它的每个元素的索引被视为对象的属性,因此,
for ... in
循环可以直接循环出 Array 的索引:var a = ['A', 'B', 'C']; for (var i in a) { console.log(i); // '0', '1', '2' console.log(a[i]); // 'A', 'B', 'C' }
注意,for … in 对 Array 的循环得到的是 String 而不是 Number
-
while
循化只有一个判断条件,条件满足,就不断循环,条件不满足时则退出循环,比如我们要计算 100 以内所有奇数之和,可以用 while 循环实现var x = 0; var n = 99; while (n > 0){ x = x + n; n = n - 2; } x; // 2500
-
do {...} while()
循环,它和while
循环的唯一区别在于,不是在每次循环开始的时候判断条件,而是再每次循环完成的时候判断条件:var n = 0; do { n = n + 1; } while (n < 100); n; // 100
-
do while
循环体会至少执行 1 次,而for
和while
循环则可能一次不执行。
8.Map 和 Set
JavaScript 的默认对象表示方式
{}
可以视为其他语言中的Map
或Dictionary
的数据结构,即一组键值对。但是 JavaScript 的对象有个小问题,就是键必须是字符串。但实际上 Number 或者其他数据类型作为键也是非常合理的。
为了解决这个问题,最新的 ES6 对方引入了新的数据类型
Map
。(需要支持 ES6 的浏览器)
-
Map
是一组键值对的结构,具有几块的查找速度。 -
设计一个"名字-成绩"对照表
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]); m.get('Michael'); // 95
-
初始化
Map
需要一个二维数组,或者直接初始化一个空Map
。Map
具有一下方法:var m = new Map(); // 空 Map m.set('Adam', 64); // 添加新的 key-value m.set('Bob', 89); m.has('Adam'); // 是否存在key 'Adam': true m.get('Adam'); // 64 m.delete('Adam'); // 删除key 'Adam' m.get('Adam'); // undefined
-
由于一个 key 只能对应一个 value,所以,多次对一个 key 放入 value,后面的值会把前面的值冲掉:
var m = new Map(); m.set('Adam', 76); m.set('Adam', 88); m.get('Adam'); // 88
-
Set
和Map
类似,也是一组 key 的集合,但是不存储 value,由于 key 不能重复,所以,在Set
中,没有重复的 key。 -
要创建一个
Set
,需要提供一个Array
作为输入,或者直接创建一个空Set
:var s1 = new Set(); // 空 Set var s2 = new Set([1, 2, 3]);
-
重复元素在
Set
中自动被过滤:var s = new Set([1, 2, 3, 3, 3]); s; // Set [1, 2, 3]
通过
add(key)
方法可以添加元素到Set
中,可以重复添加,但不会有效果:s.add(4); s; // Set {1, 2, 3, 4} s.add(4); s; // 任然是:Set {1, 2, 3, 4}
-
通过
delete(key)
方法可以删除元素:var s = new Set([1, 2, 3]); s; // Set {1, 2, 3} s.delete(3); s; // Set {1, 2}
-
Map
和Set
是 ES6 标准新增的数据类型,请根据浏览器的支持情况决定是否要使用
9.iterable
遍历
Array
可以采用下标循化,遍历Map
和Set
就无法使用下标,为了统一集合类型,ES6标准引入了新的iterable
类型,Array
、Map
和Set
都属于iterable
类型。具有
iterable
类型的集合可以通过新的for ... of
循环来遍历。
for ... of
循环是 ES6 引入的新的语法,请吃你的浏览器是否支持
-
用
for ... of
循环遍历集合,用法如下:var a = ['A', 'B', 'C']; var s = new Set(['A', 'B', 'C']); var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]); for (var x of a){ // 遍历 Array console.log(x); } for (var x of s){ // 遍历 Set console.log(x); } for (var x of m){ // 遍历 Map console.log(x[0] + '=' + x[1]); }