唐抉的个人博客

前端三件套之JavaScript(一)

字数统计: 5.5k阅读时长: 24 min
2022/10/28

快速入门

JavaScript代码可以直接嵌在网页的任何地方,通常会把JavaScript代码放在HTML的<head>中。由<script>...</script>包含的代码就是JavaScript代码,它将直接被浏览器执行。

另一种方法就是把JavaScript代码放到一个单独的.js文件中,然后在HTML中通过<script src="/js文件地址">...</script>的方式引入这个文件。

**JavaScript中的注释符号是///**/,*JavaScript中严格区分大小写。**

若需要调试JavaScript代码,可以打开一个网页,然后按下F12打开开发者工具,点击控制台(Console)在>里可以直接输入JavaScript代码,按回车后执行。按F12可关闭开发者工具。

在控制台中输入console.log(a)可以查看变量a的内容,回车后显示的值就是变量内容。

本文中使用的是把JavaScript代码放到一个单独的.js文件中的方法。因此先建立一个test.html文档,其内容如下:

1
2
3
4
5
6
<!DOCTYPE html>
<html>
<head>
<script src="test.js" charset="UTF-8"></script>
</head>
</html>

建好test.html后,在test.html所在文件夹新建一个text.js文件,后续的JavaScript代码都在text.js中编写,控制台的代码在text.js里运行,需要页面显示的代码在test.html里运行。

基本语法

JavaScript的语法与Java类似,每个语句以;结束,语句块用{...}。语句块中具有以4个空格为单位的缩进

练习题

分别利用行注释和块注释把下面的语句注释掉,使它不再执行:

1
2
//alert('我不想执行');
/*alert('我也不想执行');*/

数据类型

Number

JavaScript不区分整数和浮点数,统一用Number表示。

以下是合法的Number类型:

  • 整数:123
  • 浮点数:0.123
  • 科学计数法:1.2345e3(即12345x1000)
  • 复数:-123
  • 无法计算结果时:NaN
  • 无限大:Infinity

JavaScript里的十六进制用0x前缀表示。

字符串

字符串是以单引号''或双引号""括起来的任意文本,引号本身不是字符串的一部分。如:‘abc'、“qwe”。

布尔值

布尔值与布尔代数的表示完全一致,一个布尔值只有truefalse两种值。

&&是与运算,||是或运算,!是非运算。

比较运算符

JavaScript允许对任意数据类型做比较。

注意相等运算符==,JavaScript有两种比较运算符:

  • ==比较运算符:会自动转换数据类型再比较。
  • ===比较运算符:不会自动转换数据类型。若数据类型不一致,返回false,若一直,再进行比较。

NaN与所有值都不相等,包括他自己。

唯一能判断NaN为true的方法是通过isNaN()函数。

JavaScript比较浮点数只能计算它们之差的绝对值,看是否小于某个阈值即:

1
2
1/3===(1-2/3);//false
Math.abs(1/3-(1-2/3))<0.0000001;//true

null和undefined

null表示一个空的值,它和0以及空字符''不同。0是一个数值,''表示长度为0的字符串,null表示空。

undefined表示值未定义,undefined仅在判断函数参数是否传递的情况下有用。

数组

数组是一组按顺序排列的集合,集合的每个值称为元素。JavaScript的数组可以包括任意数据类型,如:

1
[1,2,3.14,'Hello',null,true];

创建数组可以通过Array()函数实现:

1
new Array(1,2,3.14,'Hello',null,true);

数组的元素可以通过索引来访问,起始值是0.

对象

JavaScript的对象是一组由键-值组成的无序集合:

1
2
3
4
5
6
7
8
var person={
name:'Bob',
age:20,
tags:['js','web','java'],
city:'Beijing',
hasCar:true,
zipcode:null
};

JavaScript对象的键都是字符串类型,值可以是任意数据。每个键又称为对象的属性。

获取一个对象的属性,可以用对象变量.属性名的方式,如person.name;

变量

变量在JavaScript中用一个变量名表示,变量名是大小写英文、数字、$_组成,不能以数字开头。变量名也不能是JavaScript的关键字,如if、while等。变量名也可以用中文。

声明变量用var语句,如:

1
2
var a;
var $b=1

=号为赋值号,同一个变量可以反复赋值,可以是不同类型的变量,但只能声明一次:

1
2
var a=123;
a='ABC'//a的值由整数变为字符串

要在控制台上显示变量的内容,用console.log(x)语句。使用console.log()代替alert()的好处是避免弹出烦人的对话框。

strict模式

使用var声明的变量不是全局变量,它的范围被限制在该变量被声明的函数体内,同名变量在不同的函数体内互不冲突。

在strict模式下运行的JavaScript代码,强制通过var声明变量,未使用var声明变量就使用的,运行会报错。

启用strict模式的方法是在JavaScript代码的第一行写上:

1
'use strict';

这是一个字符串,不支持strict模式的浏览器会把它当做一个字符串语句执行,支持strict模式的浏览器将会开启strict模式运行JavaScript。

字符串

转义字符\可以转义很多字符,比如\n表示换行,\t表示制表符,\\表示\字符。

ASCII字符可以以\x##形式的十六进制表示,如\x41;便等同于A。

还可以用\u####表示一个Unicode字符,如\u4e2d\u6587;便等同于‘中文’。

多行字符串

由于多行字符串用\n写起来比较麻烦,因此最新的ES6标准新增了一种多行字符串的方法,用反引号`表示,反引号里面的内容每次回车就会自动换行:

1
2
3
`这是一个
多行
字符串`;

模板字符串

要把多个字符串连接起来,可以用+号连接:

1
2
3
4
var name="张三";
var age=22;
var message='你好,'+name+',你今年'+age+'岁了';
alert(message);

当由很多变量需要连接时,用+号比较麻烦,ES6新增了一种模板字符串,表示方法和多行字符串一样,但它会自动替换字符串中的变量:

1
2
3
4
var name="张三";
var age=22;
var message=`你好,${name},你今年${age}岁了`;
alert(message);

操作字符串

字符串是不可变的,若对字符串的某个索引赋值,不会报错,但也没有任何效果。

toUpperCase

toUpperCase()把一个字符串全部变为大写:

1
2
var s="Hello";
console.log(s.toUpperCase());//返回'HELLO'

toLowerCase

toLowerCase把一个字符串全部变为小写:

1
2
var s="Hello";
console.log(s.toLowerCase());//返回'hello'

indexOf

indexof()会搜索指定字符串出现的位置:

1
2
3
var s='hello,world';
console.log(s.indexOf('world'));//返回6
console.log(s.indexOf('World'));//没有找到指定的子串,返回-1

substring

substring()返回指定索引区间的子串:

1
2
3
var s='hello,world';
console.log(s.substring(0,5));//从索引0开始到5结束(不包括5),返回'hello'
console.log(s.substring(6));//从索引6开始到结束,返回'world'

练习题

测试你的浏览器是否支持ES6标准,如果不支持,请把多行字符串用\n重新表示出来:

1
2
3
4
5
6
7
console.log(`多行
字符串
测试`);
/*运行结果如下:
多行
字符串
测试*/

测试你的浏览器是否支持ES6模板字符串,如果不支持,请把模板字符串改为+连接的普通字符串:

1
2
3
4
5
var name = '小明';
var age = 20;
console.log(`你好, ${name}, 你今年${age}岁了!`);
/*运行结果如下:
你好, 小明, 你今年20岁了!*/

数组

要取得数组的长度,可直接访问length属性:

1
2
var arr=[1,2,3.14,'Hello',null,true];
console.log(arr.length);//长度为6,返回6

注意:直接给Array的length赋一个新值会导致Array大小的变化:

1
2
3
4
5
6
7
var arr=[1,2,3];
console.log(arr.length);//长度为3,返回3
arr.length=6;
console.log(arr);//arr变为[1,2,3,undefined,undefined,undefined]
//返回(6) [1, 2, 3, …]
arr.length=2;
console.log(arr);//arr变为[1,2],返回(6) [1, 2, 3, …]

Array可以通过索引把对应的元素修改为新的值,通过索引赋值会直接修改Array。若索引超过了范围,同样会引起Array大小的变化:

1
2
3
4
var arr=[1,2,3];
arr[5]='x';
console.log(arr);//arr变为[1,2,3,undefined,undefined,'x']
//返回(6) [1, 2, 3, …, 'x']

indexOf

与String类似,Array也可以通过indexof()来搜索一个指定的元素位置:

1
2
3
var arr=[10,20,'30','xyz'];
console.log(arr.indexOf(10));//元素10的索引为0,返回0
console.log(arr.indexOf(30));//元素30没有找到,返回-1

slice

slice对应String的substring(),截取Array的部分元素,然后返回一个新的Array:

1
2
3
var arr=['A','B','C','D','E','F','G'];
console.log(arr.slice(0,3));//从索引0开始到3结束(不包括3),返回(3) ['A', 'B', 'C']
console.log(arr.slice(3));//从索引3开始到结束,返回(4) ['D', 'E', 'F', 'G']

若不给slice()传递任何参数,它会从头到尾截取所有元素。

push和pop

push()向Array的末尾添加元素,pop()则把Array的最后一个元素删掉:

1
2
3
4
5
6
7
8
9
var arr=[1,2];
arr.push('A','B');
console.log(arr); // 返回(4)[1, 2, 'A', 'B']
arr.pop();
console.log(arr); // 返回(3)[1, 2, 'A']
arr.pop(); arr.pop(); arr.pop(); // 连续pop 3次
console.log(arr); // 返回(0)[]
arr.pop(); // 空数组继续pop不会报错,而是返回undefined
console.log(arr);// 返回(0)[]

unshift和shift

若要往Array的头部添加若干元素,使用unshift()方法,shift()方法则是把Array的第一个元素删掉:

1
2
3
4
5
6
7
8
9
var arr=[1,2];
arr.unshift('A','B');
console.log(arr);// 返回(4) ['A', 'B', 1, 2]
arr.shift();
console.log(arr);// 返回(3) ['B', 1, 2]
arr.shift();arr.shift();arr.shift();
console.log(arr);// 返回(0) []
arr.shift();// 空数组继续shift不会报错,而是返回undefined
console.log(arr);// 返回(0) []

sort

sort()可以对当前Array进行排序,它会直接修改当前Array的元素位置,直接调用是,按照默认顺序排序:

1
2
3
var arr=['B','C','A'];
arr.sort();
console.log(arr);//返回(3) ['A', 'B', 'C']

reverse

reverse()可以反转Array内的元素:

1
2
3
var arr=[1,2,3];
arr.reverse();
console.log(arr);//返回(3) [3, 2, 1]

splice

splice方法可以从指定的索引开始删除元素,然后再从该位置添加元素:

1
2
3
4
5
6
7
var arr=[1,2,3,4,5,6];
arr.splice(2,3,'A','B');//从索引2开始删除3个元素,然后再添加2个元素
console.log(arr);//返回(5) [1, 2, 'A', 'B', 6]
arr.splice(2,2)//从索引2开始删除2个元素,不添加元素
console.log(arr);//返回(3) [1, 2, 6]
arr.splice(2,0,'A','B');//从索引2开始添加2个元素,不删除元素
console.log(arr);//返回(5) [1, 2, 'A', 'B', 6]

concat

concat()方法把当前的Array和另一个Array连接起来,并返回一个新的Array:

1
2
3
4
var arr=['A','B','C'];
var added=arr.concat([1,2,3]);
console.log(added);//返回(6) ['A', 'B', 'C', 1, 2, 3]
console.log(arr);//返回(3) ['A', 'B', 'C']

concat()方法可以接收任意个元素和Array,并自动把Array拆开,然后全部添加到新的Array里:

1
2
3
var arr=['A','B','C'];
console.log(arr.concat(1,2,[3,4]));
//返回(7) ['A', 'B', 'C', 1, 2, 3, 4]

join

join()方法把当前Array的每个元素都用指定的字符串连接起来,然后返回连接后的字符串:

1
2
var arr=['A','B','C',1,2,3];
console.log(arr.join('-'))//返回A-B-C-1-2-3

若Array的元素不是字符串,将自动转换为字符串后再连接。

多维数组

若数组的某个元素又是一个Array,便形成了多维数组。

练习题

如何通过索引取到500这个值:

1
2
3
4
5
var arr = [[1, 2, 3], [400, 500, 600], '-'];
var x = arr[1][1];
console.log(x);
/*运行结果如下:
500*/

在新生欢迎会上,你已经拿到了新同学的名单,请排序后显示:欢迎XXX,XXX,XXX和XXX同学!

1
2
3
4
5
var arr = ['小明', '小红', '大军', '阿黄'];
arr.sort();
console.log('欢迎'+arr[0]+','+arr[1]+','+arr[2]+'和'+arr[3]+'同学!');
/*运行结果如下:
欢迎大军,小明,小红和阿黄同学!*/

对象

JavaScript的对象是一种由若干键值组成的无序集合数据类型。

JavaScript用一个{...}表示一个对象,键值对以xxx:xxx形式声明,用,隔开:

1
2
3
4
5
6
7
8
var xiaoming={
name:'小明',
birth:1999,
school:'No.1 Middle School',
height:1.70,
weight:65,
score:null
};

可以通过对象.属性来获取小明的属性:xiaoming.name;。若属性名包括特殊字符,就必须使用''括起来:

1
2
3
4
var zhangsan={
name:'张三',
'middel-school':'No.1 Middle School'
};

访问这一特殊属性无法使用.操作符,必须使用['xxx']访问:

1
2
3
zhangsan['middel-school'];
zhangsan['name'];//name也可以用['xxx']访问
zhangsan.name;

对象的所有属性都是字符串,但属性值可以是任意数据类型。若访问不存在的属性会返回undefined

由于JavaScript的对象是动态类型,因此可以自由给对象添加或删除属性:

1
2
3
4
5
6
7
8
var xiaoming={
name:'小明'
};
console.log(xiaoming.age);//返回undefined
xiaoming.age=18;//
console.log(xiaoming.age);//返回18
delete xiaoming.age;//删除age属性
console.log(xiaoming.age);//返回undefined

in操作符可以检查对象是否拥有某个属性(包括继承属性):

1
2
3
4
5
var xiaoming={
name:'小明'
};
console.log('name'in xiaoming);//返回true
console.log('age' in xiaoming);//返回false

hasOwnProperty()方法可以可以检查对象自身是否拥有某个属性:

1
2
3
4
5
var xiaoming={
name:'小明'
};
console.log(xiaoming.hasOwenProperty('name'));//返回true
console.log(xiaoming.hasOwenProperty('age'));//返回false

条件判断

JavaScript使用if(){...}else{...}来进行条件判断,如:

1
2
3
4
5
6
var age=20;
if(age>=18){
alert('adult');
}else{
alert('teenager');
}

多行条件判断

使用多个if...else if...else...组合可以进行更细致的判断:

1
2
3
4
5
6
7
8
var age=3;
if (age>=18){
alert('adult');
}else if(age>=6){
alert('teenager');
}else{
alert('kid');
}

JavaScript把nullundefined0NaN和空字符串''视为false,其他值一概视为true。

练习题

小明身高1.75,体重80.5kg。请根据BMI公式(体重除以身高的平方)帮小明计算他的BMI指数,并根据BMI指数:

  • 低于18.5:过轻
  • 18.5-25:正常
  • 25-28:过重
  • 28-32:肥胖
  • 高于32:严重肥胖

if...else...判断并显示结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var height=parseFloat(prompt('请输入身高(m):'));
var weight=parseFloat(prompt('请输入体重(kg):'));
var bmi=weight/(height*height);
if(bmi<18.5){
console.log('过轻');
}else if(bmi>=18.5&&bmi<25){
console.log('正常');
}else if(bmi>=25&&bmi<28){
console.log('过重');
}else if(bmi>=28&&bmi<32){
console.log('肥胖');
}else{
console.log('严重肥胖');
}
/*运行结果如下:
过重*/

循环

JavaScript的循环有两种,一种是for循环,另一种是while循环。

for循环

1
2
3
4
5
6
var x=0;
var i;
for(i=1;i<=10000;i++){
x=x+i;
}
console.log(x);//返回50005000

练习题

利用for循环计算1 * 2 * 3 * ... * 10的结果:

1
2
3
4
5
6
7
8
9
var x=1;
var i;
for (i=1;i<=10;i++){
x=x*i;
}if (x === 3628800) {
console.log('1 x 2 x 3 x ... x 10 = ' + x);
}else {
console.log('计算错误');
}

for循环的3个条件是可以省略的,for(;;)将无限循环下去,为了避免死循环,此时可以使用break语句退出循环。

for...in

for循环的一个变体是for...in循环,它可以把一个对象的所有属性依次循环出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
var a={
name:'Jack',
age:20,
city:'Beijing'
}
for(var key in a){
console.log(key);
}
/*运行结果如下:
name
age
city
*/

可用hasOwnProperty()过滤掉继承属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var a={
name:'Jack',
age:20,
city:'Beijing'
}
for(var key in a){
if(a.hasOwnProperty(key)){
console.log(key);
}
}
/*运行结果如下:
name
age
city
*/

for...in循环也可以直接循环出Array的索引:

1
2
3
4
5
6
7
8
9
10
11
12
13
var a=['A','B','C'];
for(var i in a){
console.log(i);
console.log(a[i]);
}
/*运行结果如下:
0
A
1
B
2
C
*/

while

while循环只有一个判断条件,条件满足就不断循环,条件不满足则退出循环:

1
2
3
4
5
6
7
8
9
10
var x=0;
var n=99;
while(n>0){
x=x+n;
n=n-2;
}
console.log(x);
/*运行结果如下:
2500
*/

do...while

do...while与while循环的唯一区别在于,do...while在每次循环完成时判断条件:

1
2
3
4
5
6
7
8
var n=0;
do{
n=n+1;
}while(n<100);
console.log(n);
/*运行结果如下:
100
*/

练习题

请利用循环遍历数组中的每个名字,并显示Hello, xxx!。请尝试for循环和while循环,并以正序、倒序两种方式遍历:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var arr = ['Bart', 'Lisa', 'Adam'];
//for循环正序
for(var i in arr){
console.log('Hello, '+arr[i]+'!');
}
console.log('for循环正序');

//for循环倒序
for(var i=arr.length-1;i>=0;i--){
console.log('Hello, '+arr[i]+'!');
}
console.log('for循环倒序');

//while循环正序
var t=0;
while(t<=arr.length-1){
console.log('Hello, '+arr[t]+'!');
t=t+1;
}
console.log('while循环正序');
//while循环倒序
var n=arr.length-1
do{
console.log('Hello, '+arr[n]+'!');
n=n-1;
}while(n>=0);
console.log('while循环倒序');

Map和Set

JavaScript的默认对象表示方式{}可以视为其他语言中的Map的数据结构。但JavaScript的对象,其键必须是字符串,实际上Number或其他数据类型作为键也是合理的。

为解决这种情况,ES6规范引入了新的数据类型Map。

以下代码可以测试浏览器是否支持ES6规范,若报错则不支持:

1
2
3
var m = new Map();
var s = new Set();
console.log('你的浏览器支持Map和Set!');

Map

Map是一组键值对的结构,具有极快的查找速度:

1
2
3
4
var m=new Map([['Michael',95],['Bob',75],['Tracy',85]]);
console.log(m.get('Michael'));
/*运行结果如下:
95*/

初始化Map需要两个二维数组,或者直接初始化一个空Map。Map具有以下方法:

1
2
3
4
5
6
var m=new Map();//空Map
m.set('Adam',67);//添加一个新的键值
console.log(m.has('Adam'));//是否存在key'Adam'
console.log(m.get('Adam'));//获取key'Adam'的值,返回67
m.delete('Adam');//删除key'Adam'
console.log(m.get('Adam'));//返回undefined

由于一个key只能对应一个value,依次多次对一个key放入value,后面的值会把前面的值顶掉。

Set

Set和Map类似,也是一组key的集合,但Set不存储value。Set中没有重复的key。

创建一个Set,需要提供一个Array作为输入,或者直接初始化一个空Set:

1
2
var s=new Set();//空Set
var ss=new Set([1,2,3]);//含1,2,3

Set具有以下方法:

1
2
3
4
5
6
7
var a=new Set([1,2,3]);
a.add(4);//添加元素4到set中
console.log(a);//返回Set{4, 1, 2, 3}
a.add(4);//重复添加元素
console.log(a);//自动过滤重复元素。返回Set{4, 1, 2, 3}
a.delete(3);//删除set中的元素3
console.log(a);//Set{1, 2, 4}

iterable

遍历Array可以采用下标循环,而Set和Map无法使用 下标。为了统一集合类型,ES6引入了iterable类型。Array、Map和Set都属于iterable类型。

具有iterable类型的集合可以通过for...of循环来遍历,可以通过以下代码测试浏览器是否支持for...of循环:

1
2
3
4
var a = [1, 2, 3];
for (var x of a) {
}
console.log('你的浏览器支持for ... of');

for...of循环遍历集合,用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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){//遍历数组
console.log(x);
}
for(var x of s){//遍历Set
console.log(x);
}
for(var x of m){//遍历Map
console.log(x[0]+'='+x[1]);
}
/*运行结果如下:
A
B
C
A
B
C
1=x
2=y
3=z*/

当给Array对象添加了额外的属性后,使用for ... in循环会有意想不到的意外效果:

1
2
3
4
5
6
7
8
9
10
11
var a = ['A', 'B', 'C'];
a.name = 'Hello';
for (var x in a) {
console.log(x);
}
/*运行结果如下:
0
1
2
name
*/

for ... in循环将把name包括在内,但Arraylength属性却不包括在内。

for ... of循环则完全修复了这些问题,它只循环集合本身的元素:

1
2
3
4
5
6
7
8
9
10
var a = ['A', 'B', 'C'];
a.name = 'Hello';
for (var x of a) {
console.log(x);
}
/*运行结果如下:
A
B
C
*/

还可以使用ES5.1引入的iterable内置forEach方法,它接收一个函数,每次迭代就自动回调该函数:

1
2
3
4
5
var a=['A','B','C'];
a.forEach(function(element,index,array){
console.log(element+', index='+index);
});
//element指向当前元素的值,index指向当前索引,array指向array对象本身

Set与Array类似,但Set没有索引,因此回调函数的前两个参数都是元素本身。

Map的回调函数参数依次为valuekeymap本身。

由于JavaScript的函数调用不要求参数必须一致,因此可以忽略部分参数,如只需要获得Array的element

CATALOG
  1. 1. 快速入门
    1. 1.1. 基本语法
      1. 1.1.1. 练习题
    2. 1.2. 数据类型
      1. 1.2.1. Number
      2. 1.2.2. 字符串
      3. 1.2.3. 布尔值
      4. 1.2.4. 比较运算符
      5. 1.2.5. null和undefined
      6. 1.2.6. 数组
      7. 1.2.7. 对象
    3. 1.3. 变量
      1. 1.3.1. strict模式
  2. 2. 字符串
    1. 2.1. 多行字符串
    2. 2.2. 模板字符串
    3. 2.3. 操作字符串
      1. 2.3.1. toUpperCase
      2. 2.3.2. toLowerCase
      3. 2.3.3. indexOf
      4. 2.3.4. substring
    4. 2.4. 练习题
  3. 3. 数组
    1. 3.1. indexOf
    2. 3.2. slice
    3. 3.3. push和pop
    4. 3.4. unshift和shift
    5. 3.5. sort
    6. 3.6. reverse
    7. 3.7. splice
    8. 3.8. concat
    9. 3.9. join
    10. 3.10. 多维数组
    11. 3.11. 练习题
  4. 4. 对象
  5. 5. 条件判断
    1. 5.1. 多行条件判断
    2. 5.2. 练习题
  6. 6. 循环
    1. 6.1. for循环
    2. 6.2. 练习题
    3. 6.3. for...in
    4. 6.4. while
    5. 6.5. do...while
    6. 6.6. 练习题
  7. 7. Map和Set
    1. 7.1. Map
    2. 7.2. Set
  8. 8. iterable