摘要:本文主要介绍了JS中常用的概念,以及基本语法。
1 简介
1.1 是什么
JS全称:JavaScript。
JS是一种脚本语言,命名和Java没有任何关系,用于给网页设置行为,比如点击、弹窗等等。
官方文档地址:
- W3C官网:https://www.w3.org/
- W3School:https://www.w3school.com.cn
1.2 发展历史
JavaScript是由网景公司在1995年发明的,起初命名为LiveScript,后来由于SUN公司的介入更名为了JavaScript。
在1996年,微软公司在其最新的IE3浏览器中引入了自己对JavaScript的实现JScript。
于是在市面上存在两个版本的JavaScript,网景公司的JavaScript和微软的JScript,为了确保不同的浏览器上运行的JavaScript标准一致,几个公司定制了通用标准,名命为ECMAScript。
在一般情况下,会将这ECMAScript和JavaScript认作同一个意思,实际上JavaScript的含义要大一些,一个完整的JavaScript实现应该由以下三个部分构成:
- ECMAScript:通用的标准,通常简写为ES。
- DOM:文档对象模型,操作网页。
- BOM:浏览器对象模型,操作浏览器。
2 基础
2.1 编写位置
因为JS是运行在浏览器中的,因此需要在网页中编写JS代码。
和CSS类似,JS也有三种位置可以选择。
2.1.1 行内脚本
直接写在元素内部,通过一些特殊的交互属性设置交互行为。
示例:
1 | <button onclick="alert('点击测试');">按钮</button> |
这种方式使用简单,但是功能比较少,并且和网页的结构耦合,不能体现出结构和行为分离的思想,也不利于维护,不建议使用这种方式。
说明:alert()
是一个函数,可以将传入的参数通过弹窗显示在页面上。
2.1.2 内部脚本
将JS提取出来,写在页面内部的script
元素中。
示例:
1 | <script type="text/javascript"> |
理论上可以将script
元素放在页面的任何位置,建议放到head
元素中。另外,也可以省略type
属性。
和行内样式相比,这种方式编写的结构和行为分离,样式也能复用,但也没有做到完全分离。
2.1.3 外部脚本
将JS进一步提取,写在单独的JS文件中,在HTML页面中使用元素引用外部的JS文件。
创建JS文件,后缀为.js
格式。示例:
1 | alert('测试'); |
在HTML页面引用CSS文件。示例:
1 | <script src="./hello.js"></script> |
这种方式可维护高,并且做到了结构和行为分离,也能被其他页面复用,建议使用这种方式。
2.2 大小写敏感
JavaScript是严格区分大小写的,在编写代码是要注意。
2.3 注释
JavaScript的注释分为单行注释和多行注释:
- 单行注释:
js 1
2// 注释内容
alert('测试'); - 多行注释:
js 1
2/* 注释内容 */
alert('测试');
3 语法
3.1 标识符
所谓标识符,就是指变量、函数、属性的名字,以及函数的参数。
标识符可以是按照下列格式规则组合起来的一或多个字符:
- 第一个字符必须是字母、下划线
_
或美元符号$
。 - 其他字符可以是字母、下划线、美元符号或数字。
按照惯例,ECMAScript标识符采用驼峰命名法,但是JavaScript中的标识符不能是关键字和保留字符。
关键字:
function | void | return | this | new | with |
var | instanceof | typeof | switch | case | default |
if | else | do | while | for | in |
continue | break | try | catch | throw | finally |
delete | debugger | true | false | null |
保留字符:
import | export | package | public | protected | private |
interface | class | enum | extends | implements | super |
let | yield | static | const |
其他不建议使用的标识符:
synchronize | volatile | abstract | final | native | transient |
float | double | long | int | short | byte |
char | boolean | throws | arguments | eval | goto |
undefined | encodeURI | decodeURI | decodeURIComponent | encodeURICOmponent | isFinite |
isNaN | parseFloat | parseInt | NaN | Number | String |
Boolean | Object | Date | Array | JSON | RegExp |
Infinity | Function | Math | Error | RangeError | SyntaxError |
ReferenceError | TypeError | EvalError | URIError |
3.2 数据类型
数据类型决定了一个数据的特征,比如123
和'123'
,直观上看这两个数据都是123,但实际上前者是一个数字,而后者是一个字符串。
对于不同的数据类型我们在进行操作时会有很大的不同。JavaScript中一共有五种基本数据类型:
- 字符串型(String)
- 数值型(Number)
- 布尔型(Boolean)
- null型(Null)
- undefined型(Undefined)
这五种之外的类型都称为Object,所以总的来看JavaScript中共有六种数据类型。
使用typeof
关键字检查数据可以得到数据的类型,返回结果是字符串类型:
- 使用
typeof 字符串
判断会返回string
- 使用
typeof 数值
判断会返回number
- 使用
typeof 布尔型
判断会返回boolean
- 使用
typeof null
判断会返回object
- 使用
typeof undefined
判断会返回undefined
3.2.1 String
在JavaScript中字符串使用String类型表示,都需要使用引号引起来,可以使用单引号,也可以使用双引号,但必须成对匹配,不能混搭使用。
在某些情况下,如果想要将特殊的字符作为字符串,需要使用转义字符\
,也可以使用HTML中的实体:
- 使用
<
表示<
,使用>
表示>
- 使用
\'
表示'
,使用\"
表示"
- 使用
\n
表示换行符 - 使用
\t
表示制表符,可以实现缩进的效果 - 使用
\\
表示\
转换为String有三种方式:
- 使用
toString()
方法:js 1
2
3
4
5
6
7
8
9var i = 1;
alert(typeof i);// number
alert(typeof i.toString());// string
var m = null;
alert(m.toString());// 页面报错,Null类型的变量不能调用方法
alert(typeof m.toString());// 页面报错,Null类型的变量不能调用方法
var n = undefined;
alert(n.toString());// 页面报错,Undefined类型的变量不能调用方法
alert(typeof n.toString());// 页面报错,Undefined类型的变量不能调用方法 - 使用
String()
方法:js 1
2
3
4
5
6
7
8
9var i = 1;
alert(typeof i);// number
alert(typeof String(i));// string
var m = null;
alert(String(m));// null
alert(typeof String(m));// string
var n = undefined;
alert(String(n));// undefined
alert(typeof String(n));// string - 拼接字符串:
js 1
2
3
4
5
6
7
8
9var i = 1;
alert(typeof i);// number
alert(typeof i + '1');// string
var m = null;
alert(m + '');// null
alert(typeof m + '');// string
var n = undefined;
alert(n + '');// undefined
alert(typeof n + '');// string
3.2.2 Number
在JavaScript中所有的数值都是Number类型,包括整数和浮点数:
- 使用
Number.MAX_VALUE
表示数值的最大值,使用Number.MIN_VALUE
表示数值大于0的最小值。 - 使用
Infinity
表示超出数值最大值的正无穷,使用-Infinity
表示负无穷。 - 使用
NaN
表示非数字的数值,即Not a number
。
在对Infinity
和-Infinity
以及NaN
使用typeof
判断时,也能得到number
的结果。
有三个方法可以把变量转换为数值:
- 使用
Number()
方法可以用来将变量转换为数值:js 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var i = '1';
alert(typeof i);// string
alert(typeof Number(i));// number
var a = true;
alert(Number(a));// 1 true会返回1,false会返回0
alert(typeof Number(a));// number
var x = '1x';
alert(Number(x));// NaN 非数值字符串会返回NaN
alert(typeof Number(x));// number
var m = null;
alert(Number(m));// 0 null会返回0
alert(typeof Number(m));// number
var n = undefined;
alert(Number(n));// NaN undefined会返回NaN
alert(typeof Number(n));// number - 使用
parseInt()
方法可以用来将字符串转换为整数:js 1
2
3
4
5
6
7
8
9
10
11
12var i = '1';
alert(typeof i);// string
alert(typeof parseInt(i));// number
var f = '1.5';
alert(parseInt(f));// 1 开头为数值的字符串会返回开头的整数部分,开头非数值的字符串会返回NaN
alert(typeof parseInt(f));// number
var x = '1x';
alert(parseInt(x));// 1 开头为数值的字符串会返回开头的整数部分,开头非数值的字符串会返回NaN
alert(typeof parseInt(x));// number
var b = false;
alert(parseInt(b));// NaN 非字符串会先转为字符串,开头非数值的字符串会返回NaN
alert(typeof parseInt(b));// number - 使用
parseFloat()
方法可以用来将字符串转换为浮点数:js 1
2
3
4
5
6
7
8
9var i = '1.5';
alert(typeof i);// string
alert(typeof parseFloat(i));// number
var x = '1.5x';
alert(parseFloat(x));// 1.5 开头为数值的字符串会返回开头的浮点数部分,开头非数值的字符串会返回NaN
alert(typeof parseFloat(x));// number
var b = false;
alert(parseFloat(b));// NaN 非字符串会先转为字符串,开头非数值的字符串会返回NaN
alert(typeof parseFloat(b));// number
在对浮点数进行计算时,可能会得到一个不精确的结果,所以在处理浮点数的运算时,需要使用特殊的方式保证得到的结果尽量精确,这里以后再说。
3.2.3 Boolean
在JavaScript中使用Boolean类型表示布尔型的数据,其取值只有true
和false
两种。
使用Boolean()
方法可以用来将变量转换为布尔值:
1 | var i = NaN; |
3.2.4 Null
使用Null表示空对象。
可以显示的将对象赋值为null
来强制将对象设置为Null类型:
1 | var person = null; |
3.2.5 Undefined
使用Undefined表示声明但未赋值的变量。
1 | var person; |
3.3 变量
变量的作用是给某一个值或对象标注名称。
使用var
关键字声明变量:
1 | var i; |
对声明的变量进行赋值:
1 | i = 1; |
在声明变量时赋值:
1 | var i = 1; |
支持对多个变量同时声明和赋值:
1 | var x, y, z = 'z'; |
3.3.1 重复声明
JavaScript允许对变量进行重复声明和赋值,也允许声明为不同的数据类型:
1 | var i = 1; |
在ES6以后,还可以使用let
声明变量:
1 | let i = 1; |
通过var
声明的变量称为全局变量,支持修改值和类型,支持重复声明。通过let
声明的变量称为局部变量,局部变量支持修改值和类型,但不支持重复声明,建议在代码块中使用。
3.3.2 常量
在ES6以后,使用const
声明常量:
1 | const i = 1; |
和var
与let
声明的变量相比,常量一经声明和赋值,就不再支持修改值和类型,也不支持重复声明。
3.4 运算符
通过运算符可以对一个或多个值进行运算,并获取运算结果。
优先级:
. [] new |
() |
++ -- |
! ~ |
* / % |
+ - |
<< >> >>> |
< <= > >= |
== != === !=== |
& |
^ |
| |
& |
^ |
| |
&& |
|| |
?: |
= += -= *= /= %= <<= >>= >>>= &= ^= |= |
, |
3.4.1 算数运算符
在JavaScript中提供了以下几种算数运算符:
- 使用
+
进行加法或拼接运算,如果拼接的是一个字符串,会将计算结果转为字符串 - 使用
-
进行减法运算,如果涉及数值,会将结果转为数值 - 使用
*
进行乘法运算,如果涉及数值,会将结果转为数值 - 使用
/
进行除法运算,如果涉及数值,会将结果转为数值 - 使用
%
进行取模运算
自增和自减运算符:
- 使用
++
进行自增运算,根据运算符位置分为前置++i
和后置i++
:js 1
2
3
4var i = 0;
alert(++i);// 1 前置获取的是自增后的值
alert(i++);// 1 后置获取的是自增前的值
alert(i);// 2 - 使用
--
进行自增运算,根据运算符位置分为前置--i
和后置i--
:js 1
2
3
4var i = 2;
alert(--i);// 1 前置获取的是自减后的值
alert(i--);// 1 后置获取的是自减前的值
alert(i);// 0
3.4.2 逻辑运算符
在JavaScript中提供了以下三种逻辑运算符:
- 使用
!
表示逻辑非,用于对布尔值进行取反,对于非布尔值的变量会先将其转换为布尔值再取反。 - 使用
&&
表示逻辑与,只有两个值都为true时,整个运算为true,返回第二个值。该运算符属于短路与,当第一个值为false时,整个运算为false,返回第一个值,不会判断第二个值。 - 使用
||
表示逻辑或,只有两个值都为false时,整个运算为false,返回第二个值。该运算符属于短路或,当第一个值为true时,整个运算为true,返回第一个值,不会判断第二个值。
3.4.3 赋值运算符
在JavaScript中使用=
进行赋值运算,可以使用简写:
- 使用
+=
先计算后赋值,i += 5
等同i = i + 5
- 使用
-=
先计算后赋值,i -= 5
等同i = i - 5
- 使用
*=
先计算后赋值,i *= 5
等同i = i * 5
- 使用
/=
先计算后赋值,i /= 5
等同i = i / 5
- 使用
%=
先计算后赋值,i %= 5
等同i = i % 5
3.4.4 比较运算符
通过比较运算符可以比较两个变量的大小关系:
- 使用
>
大于号判断左侧的变量是否大于右侧的变量,成立返回true,否则返回false。 - 使用
<
小于号判断左侧的变量是否小于右侧的变量,成立返回true,否则返回false。 - 使用
==
等号判断两侧的变量是否相等,如果类型不一样,会先进行自动类型转换再比较,相等返回true,否则返回false。 - 使用
===
全等号判断两侧的变量是否相等,相等返回true,否则返回false。不会进行自动类型转换。 - 使用
!=
不等号判断两侧的变量是否不等,如果类型不一样,会先进行自动类型转换再比较,不等返回true,否则返回false。 - 使用
!==
不全等号判断两侧的变量是否不等,不等返回true,否则返回false。不会进行自动类型转换。
对于非数值的变量进行比较判断时,会将变量转换为数值再比较,但当两侧的变量都是字符串时,会根据字符串的Unicode编码进行比较。
3.4.5 条件运算符
条件运算符也称为三元运算符,语法:
1 | 条件表达式?语句1:语句2; |
运算逻辑:
- 条件运算符在执行时,首先对条件表达式进行求值。
- 如果该值为true,则执行语句1,并返回执行结果。如果该值为false,则执行语句2,并返回执行结果。
- 如果条件表达式的求值结果是一个非布尔值,会将其转换为布尔值然后再运算。
3.4.6 移位运算符
移位运算符一般用于数值类型的变量,作用在数值补码的所有位,按位运算:
- 使用
&
进行按位与运算,两个数值的相对位都为1,结果的对应位为1,否则结果的对应位为0 - 使用
|
进行按位或运算,两个数值的相对位都为0,结果的对应位为0,否则结果的对应位为1 - 使用
^
进行按位异或运算,两个数值的相对位不相同,结果的对应位为1,否则结果的对应位为0 - 使用
~
进行按位非运算,对数值的每位都进行反转 - 使用
<<
进行带符号左移运算,每位左移,右边补0。 - 使用
>>
进行带符号右移运算,每位右移,左边补0,符号位使用原来的。 - 使用
>>>
进行无符号右移运算,每位右移,左边补0,符号位补0。
移位运算改变的是数值的补码。对于正数来说,数值的原码是其补码。对于负数来说,数值的反码+1是其补码。
3.5 语句和代码块
表达式和运算符类似于单词和短语,语句是一条完整的命令,通常使用;
结尾。
可以使用{}
将多条语句包裹起来,称为代码块,只具有分组和排版的作用,没有其他意义。
3.6 条件语句
3.6.1 if…else语句
如果if中的条件成立,返回布尔值为true,则执行if后面代码块里的操作语句,否则执行else后面代码块里的操作语句:
1 | if (条件表达式) { |
在if和else后面的代码块中,支持嵌套其他语句,如果嵌套的还是if…else语句,可以简写:
1 | if (条件表达式1) { |
3.6.2 switch…case语句
也称为条件分支语句,会将switch后面的值同每个case后面的值相比较,相等就执行case后面的操作语句:
1 | switch (值) { |
其中的break用于终止判断,如果没有break会在执行完当前case的操作语句后,继续判断后面case的值,直到遇到break或者判断结束。
如果所有switch的值和case后面的值都不相等,则执行default后面的操作语句。
3.7 循环语句
不管使用那种循环语句,都需要在适当位置跳过循环和终止循环:
- 跳过循环:跳过本次循环,不执行后面的操作语句,直接进行条件判断,进行下一次循环,使用continue实现,一般在条件语句中使用:
js 1
2
3
4
5
6
7循环操作语句代码块 {
操作语句1
if (条件表达式) {
continue;
}
操作语句2
} - 终止循环:跳出循环,终止整个循环的执行,执行循环后面的代码,使用break实现,一般在条件语句中使用:
js 1
2
3
4
5
6
7循环操作语句代码块 {
操作语句1
if (条件表达式) {
break;
}
操作语句2
}
3.7.1 while语句
如果while后面的条件成立,则执行代码块中的操作语句,执行后重复判断是否成立,如果成立,则重复执行操作语句,只有当不成立时才终止循环:
1 | while (条件表达式) { |
还可以使用do…while语句,这种方式会先执行操作语句,然后再循环判断条件是否成立,以及循环执行操作语句,最终终止循环:
1 | do { |
相比较而言,while语句会先判断再执行,do…while语句会先执行再判断,能够保证至少执行一次操作语句。
但不管使用那种循环语句,都需要做好终止循环的判断逻辑,否则会导致死循环,严重情况会导致系统没有足够的资源而崩溃。
3.7.2 for语句
使用for语句可以更加明显的设置循环终止条件,但因此也显得比较繁琐:
1 | for(初始化语句; 条件表达式; 更新语句) { |
先执行初始化语句,可以省略,将初始化语句放在for循环前面执行。然后判断条件是否成立,可以省略,但需要在操作语句中控制终止循环的条件,类似while语句,否则会导致死循环。如果条件成立,会执行操作语句,否则终止循环。在操作语句执行结束后,执行更新语句,对循环条件更新,并循环判断条件是否成立,如果成立则循环执行操作语句,否则终止循环。
除了for语句代码块中的操作语句外,其他三个部分都是可以省略的,省略后就类似while语句了,需要手动控制循环。
3.8 嵌套循环
循环语句支持嵌套,在一个循环语句中支持嵌套另一个循环语句。
使用continue和break可以处理当前循环语句的循环逻辑,如果想处理上层循环语句的循环逻辑,需要配合label标签使用:
1 | label1: |
使用label给循环打标签,在continue和break后面使用标签控制要操作的循环。示例:
1 | // 大学数组 |
说明:console.log()
是一个函数,可以将传入的参数打印到页面的控制台上。
4 对象
4.1 定义
JavaScript中的数据类型有六种,包括五种基本数据类型和一种引用数据类型。
五种基本数据类型:
- String字符串
- Number数值
- Boolean布尔值
- Null空值
- Undefined未定义
使用Object对象来表示引用类型的数据,这是一种复合的数据类型,可以保存多个不同数据类型的属性。
4.2 使用
创建对象有两种方式:
- 先创建,然后对属性赋值:
js 1
2
3
4
5
6
7var student = new Object();
student.name = '张三';
student.sex = '男';
// 不能赋值特殊的属性名
// student.'1 2 3' = 123;
// 可以赋值特殊的属性名
student['1 2 3'] = 123; - 在创建时对属性赋值:
js 1
2
3
4
5var student = {
name:'张三',
sex:'男',
'1 2 3':123
};
访问对象的属性:
1 | var student = { |
删除对象属性:
1 | var student = { |
在JavaScript中,对象的属性可以是任意类型的数据,包括对象的属性时另一个对象:
1 | var student = { |
可以通过in
关键字判断对象是否存在指定属性:
1 | var student = { |
可以使用for…in语句循环遍历对象的属性:
1 | var student = { |
4.3 堆栈
JavaScript在运行时数据是保存到栈内存和堆内存当中的。简单来说栈内存保存堆内存中对象的引用和基本类型数据,堆内存保存引用类型数据,也就是对象。
基本数据类型和引用数据类型的区别:
- 对于基本数据类型的变量来说,在栈上创建栈内存用于存储数据,每个变量都是独立的栈内存,变量的修改互不影响:
js 1
2
3
4var m = 100;
var n = 100;
m = 200;
console.log(n);// 100 - 对于引用数据类型的变量来说,在堆上创建堆内存用于存储对象,在栈上创建栈内存用于存储堆内存中对象的引用,对具有相同引用的对象的修改会互相影响:
js 1
2
3
4
5
6
7var zs = {
name:'张三',
age:'18'
};
var student = zs;
student.age = 20;
console.log(zs.age);// 20
在进行比较运算时的区别:
- 在比较基本类型数据的时候,比较的是栈内存中的值。
- 在比较引用类型数据的时候,比较的是栈内存中的引用地址。即使两个对象有完全相同的属性,但因为在堆内存是两个对象,就导致引用地址不同,两个对象也不同:
js 1
2
3
4
5
6
7
8
9var m = {
name:'张三',
age:'18'
};
var n = {
name:'张三',
age:'18'
};
console.log(m == n);// false
4.4 垃圾回收
垃圾回收(GC,Garbage Collection),是一种回收不再使用的对象内存的机制。
当一个对象没有任何的变量或属性对它进行引用,会导致无法操作该对象,这种对象就是一个垃圾,垃圾对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
JavaScript拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,开发人员不需要也不能进行垃圾回收的操作,只需要将不再使用的对象设置为null即可:
1 | var obj = new Object(); |
4.5 分类
在JavaScript中可以将对象分为内部对象、宿主对象和自定义对象三种。
4.5.1 内部对象
内部对象包括String、Number、Boolean、Object、Function、Array、Date、Math、RegExp、Global,以及各种错误类对象,包括Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError。
其中Global和Math这两个对象又被称为内置对象,这两个对象在脚本程序初始化时被创建,不必实例化这两个对象。
4.5.2 宿主对象
宿主对象就是执行JavaScript的环境提供的对象。
对于嵌入到网页中的JavaScript来说,其宿主对象就是浏览器提供的对象,所以又称为浏览器对象,如IE、Firefox等浏览器提供的对象。不同的浏览器提供的宿主对象可能不同,即使提供的对象相同,其实现方式也大相径庭,这会带来浏览器兼容问题,增加开发难度。
浏览器对象有很多,如Window和Document等等。
4.5.3 自定义对象
顾名思义,就是开发人员自己定义的对象。JavaScript允许使用自定义对象,使应用及功能得到扩充。
5 内部对象
5.1 函数
5.1.1 定义
函数是封装了多条语句的代码块,这段代码块会让函数具有某种功能。
函数中的语句不会自动执行,需要在有需要的地方显示调用函数,在被调用时才会执行函数中的语句。
函数是一种Function类型的对象,使用typeof
检查:
1 | var fun = new Function(); |
当将函数赋值给对象的某个属性时,将这个属性称为方法,即对象的方法对应的类型是函数。
5.1.2 使用
创建函数的方式:
- 使用普通函数,支持多个参数:
js 1
2
3
4function func(name) {
console.log('test ' + name);
}
func('hello'); - 使用函数表达式,创建匿名函数,并将函数赋值给变量:
js 1
2
3
4var func = function(name) {
console.log('test ' + name);
};
func('hello'); - 使用函数的构造方法,支持多个参数:
js 1
2var func = new Function('name',"console.log('test ' + name);");
func('hello'); - 使用构造函数,支持多个参数:
js 1
2
3
4function Func(name) {
console.log('test ' + name);
}
new Func('hello');
函数的组成部分:
- 函数名:用于显示调用函数,可以省略函数名使用变量名调用,也可以同时省略函数名和变量名使用函数自调用:
js 1
2
3(function(name) {
console.log(name);
})('hello'); - 入参:用于将数据传入到函数中进行处理,支持无参和有参,多个参数用
,
分隔,参数类型支持任意类型。在执行函数时会从左到右自动匹配参数,缺少的参数按照未定义处理:js 1
2
3
4var fun = function(name) {
console.log(name);
};
fun();// undefined - 返回值:用于获取函数处理的结果。使用
return
关键字返回结果,返回结果支持任意类型,无返回结果按照未定义处理:js 1
2
3
4var fun = function(name) {
console.log(name);
};
console.log(fun('hello'));// undefined
使用匿名函数自调用(IIFE,Immediately Invoked Function Expression)的好处是隐藏了内部实现,不污染外部命名空间。
5.1.3 作用域
作用域通常用于形容变量,指的是变量的有效范围。
在JavaScript中一共有两种作用域:
- 全局作用域,在
script
元素内部声明的变量,而不是在某个方法内部声明的变量,都在全局作用域,这种变量称为全局变量。 - 局部作用域,在方法内部创建的变量,只在方法内部有效,这种变量称为局部变量。
全局变量的特点:
- 在页面打开时创建,在页面关闭时销毁。
- 可以直接使用由浏览器创建的window全局对象,代表浏览器的窗口,可以直接使用其属性和方法。
- 全局变量在页面的任意地方都可以访问。
局部变量的特点:
- 在方法调用时创建,在方法执行结束后销毁。
- 只能在方法内部使用。
只有在方法内部使用关键字声明变量,该变量才是局部变量,否则就是全局变量:
1 | var name = '张三'; |
当全局变量和局部变量名称相同时,在方法中使用的是局部变量:
1 | var name = '张三'; |
常用的全局函数:
1 | // 对URI进行编码 |
5.1.4 声明提升
在JavaScript中,函数及变量的声明都将被提升到函数的最顶部,变量可以先使用再声明。
在ES6引入let和const的声明方式后,只有var声明的变量才支持声明提升,并且使用函数表达式声明的函数变量也不支持声明提升。
声明提升只是将声明提前了,并没有将初始化的操作提前:
1 | console.log(name);// undefined |
5.1.5 内部属性
在函数内部,有两个特殊的对象:
- arguments:该对象实际上是一个类数组对象,有数组的特性,但本质上是Object对象,用于保存函数的参数,同时该对象还有一个属性callee来表示当前函数。
- this:引用的是一个对象,用来表示函数执行的上下文,一般是函数的调用者。根据函数调用方式的不同,引用对象也会有所不同。
通过arguments对象可以获取传入的参数,即使函数没有定义入参:
1 | function test() { |
说明:instanceof
关键字可以用来判断左侧的变量是否属于右侧的类型。
5.1.6 构造函数
构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是,构造函数习惯上首字母大写:
1 | function Student() { |
在调用函数时,普通函数可以直接调用,构造函数需要使用new
关键字调用:
1 | function Student(name, sex) { |
也可以通过instanceof
关键字判断是不是构造函数调用:
1 | function Student(name, sex) { |
使用同一个构造函数创建的对象,称为一类对象。所以也将构造函数称为类,同时将通过构造函数创建的对象,称为该类的实例。
5.1.7 原型对象
JavaScript是一门面向对象的语言,而且它还是一个基于原型的面向对象的语言。
原型:
- 所有函数都有
prototype
属性的对象,称为原型。 - 所有对象(包括原型和函数,函数也是对象)都有
__proto__
属性的对象,可以通过这个对象访问其构造方法的原型。 - 通过函数
prototype
属性获取原型的方式称为显示调用,通过对象__proto__
属性获取原型的方式称为隐式调用。 - 默认情况下,原型是一个Object对象,该对象只包含
constructor
属性和__proto__
属性:
说明:
- 原型的
constructor
属性指向函数:js 1
2function func() {}
console.log(func.prototype.constructor === func);// true - 原型的
__proto__
属性在浏览器里显示为[[Prototype]]
属性,对应Object的prototype
属性:js 1
2function func() {}
console.log(func.prototype.__proto__ === Object.prototype);// true
原型链:
- 原型链又称为隐式原型链,隐式调用的原型会形成原型链,顶端是Object显示调用的原型,该原型如果继续通过隐式调用获取原型会返回null:
js 1
console.log(Object.prototype.__proto__);// null
- 所有对象(包括原型和函数,函数也是对象)都是Object的实例,任何对象的原型链都能追溯到Object显示调用的原型:
js 1
2
3function func() {}
console.log(func.__proto__ === Function.prototype);// true
console.log(func.__proto__.__proto__ === Object.prototype);// true - 所有方法(包括Object和Function)都是Function的实例,任何方法隐式调用的原型都是Function显示调用的原型:
js 1
2
3function func() {}
console.log(func.__proto__ === Function.prototype);// true
console.log(Object.__proto__ === Function.prototype);// true
当访问对象的属性和方法时,首先在对象自身空间中寻找,如果没有,会在原型链上寻找,如果找不到会返回undefined:
1 | function Student() { |
在调用toString()
方法时,在没有重写的情况下,调用的实际上是Object的原型对象的方法:
1 | function Student() { |
在使用in
关键字和for…in语句循环遍历对象的属性时,也会访问原型对象中的属性。
可以使用hasOwnProperty()
方法判断对象自身是否包含指定属性:
1 | function Student() { |
5.1.8 函数对象方法
当将函数作为对象赋值给变量时,这个对象即使函数对象,函数对象可以通过自身调用函数,也可以通过call()
方法和apply()
方法调用。
通过方法调用时,传入的第一个参数会被作为函数的调用者,即作为函数里的this对象:
1 | var test = function() { |
两个方法的区别在于对入参上的处理,第二个参数会作为入参:
1 | var test = function(first, last) { |
5.1.9 箭头函数
箭头函数是在ES6中添加的一种规范:
- 省略了
function
关键字,箭头左侧表示入参,只有当入参只有一个时,才可以省略小括号 - 省略了
return
关键字,箭头右侧是返回语句,只有当函数直接返回表达式时,才可以省略大括号
使用:
1 | // 使用箭头函数 |
如果返回值是对象,需要使用括号包裹:
1 | var func = (x) => ({result: x * x}); |
5.2 数组
5.2.1 定义
数组也是对象的一种,数组是一种用于表达有顺序关系的数据的集合的语言结构。
数组和普通对象功能类似,也用来存储多个值。不同的是普通对象使用字符串作为属性名,而数组使用数字来作为索引操作元素。
索引是从0开始的整数,用于获取数组对应位置的数据。
数组可以存放任意类型的数据,可以在一个数组中存放不同类型的数据。
5.2.2 使用
创建数组有两种方式:
- 先创建,然后插入数据:
js 1
2
3
4
5
6var arr = new Array();
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
console.log(arr);// [1, 2, 3]
console.log(typeof arr);// object - 在创建时插入数据:
js 1
2
3
4
5var arr = new Array(1, 2, 3);
// 也可以使用这种方式
// var arr = [1, 2, 3];
console.log(arr);// [1, 2, 3]
console.log(typeof arr);// object
删除数组数据:
1 | var arr = [1, 2, 3]; |
访问数组数据:
1 | var arr = [1, 2, 3]; |
遍历数组数据:
- 使用for…in循环遍历:
js 1
2
3
4var arr = [1, 2, 3];
for (var i in arr) {
console.log(arr[i]);
} - 使用forEach循环遍历:
js 1
2
3
4var arr = [1, 2, 3];
arr.forEach(function(i, e) {
console.log(i + ' > ' + e);
});
数组的属性:
1 | var arr = [1, 2, 3]; |
数组的方法:
1 | var arr = [1, 2, 3]; |
5.3 日期
在JavaScript中使用Date对象表示日期:
1 | var now = new Date(); |
日期的属性:
1 | var now = new Date(); |
日期的方法:
1 | var now = new Date(); |
5.4 数学相关
JavaScript为数学计算封装了一个Math对象,和其他的对象不同,Math不是一个构造函数,属于一个工具类,不用创建对象,里边封装了数学运算相关的属性和方法。
Math的属性:
1 | console.log(typeof Math);// object |
Math的方法:
1 | console.log(Math.abs(-1));// 1 获取绝对值 |
5.5 包装类
JavaScript提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象:
- String:可以将基本数据类型字符串转换为String对象。
- Number:可以将基本数据类型的数字转换为Number对象。
- Boolean:可以将基本数据类型的布尔值转换为Boolean对象。
在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,因为对象在栈中存储的是对象的引用,所以在进行比较时会导致与预期不一致的结果:
1 | console.log(1 == 1);// true 基本数据类型之间的比较使用栈上的值 |
5.5.1 布尔值
布尔值的属性:
1 | var boo = new Boolean(true); |
布尔值的方法:
1 | var boo = new Boolean(true); |
5.5.2 数字
数字的属性:
1 | var num = new Number(0); |
数字的方法:
1 | console.log(Number.isNaN('1'));// false 判断指定参数是否为NaN,只有当参数是数字且为NaN才会返回true,参数非数字和非NaN的数字会返回false |
5.5.3 字符串
字符串的属性:
1 | var str = new String('hello'); |
字符串的方法:
1 | var str = new String('hello'); |
5.6 正则表达式
5.6.1 定义
在JavaScript中使用RegExp对象表示正则表达式,正则表达式是用于从字符串中选择特定字符串的文本。
5.6.2 创建
创建正则表达式有两种方式:
- 使用构造方法创建,第一个参数是表达式,第二个参数是可选的修饰符,如果表达式有特殊字符,需要使用
\
进行转义:js 1
2
3// var patt = new RegExp(pattern, modifiers);
var reg = new RegExp('123');
console.log(typeof reg);// object - 直接创建,同样需要两个参数,但是使用
/
分隔:js 1
2
3// var reg = /pattern/modifiers;
var reg = /123/;
console.log(typeof reg);// object
如果需要动态创建正则表达式,需要使用构造方法创建。
在创建正则表达式以后,其内部维护了一个lastIndex
索引,记录下一次匹配从哪个位置开始。
5.6.3 方法
使用test()
方法判断是否匹配内容:
1 | var reg = /123/; |
使用exec()
方法获取匹配内容:
1 | var reg = /123/; |
使用字符串的match()
方法获取匹配内容,和正则表达式的exec()
方法相同:
1 | var reg = /123/; |
5.6.4 修饰符
修饰符用于对匹配方式进行说明,修饰符可以省略,但修饰符之前的/
不能省略:
- i:执行对大小写不敏感的匹配,默认大小写敏感
- g:执行全局匹配,默认匹配到第一个停止
- m:执行多行匹配,默认匹配第一行
如果没有使用g
全局修饰,会在匹配到第一个停止,返回包含匹配信息的数组对象,并且exec()
方法和match()
方法获取的结果相同。
如果使用了g
全局修饰,会匹配所有满足的内容,但是exec()
方法和match()
方法获取的结果不同。
使用exec()
方法执行有全局修饰的正则表达式,返回从lastIndex
开始匹配到的内容数组,匹配一次后返回匹配的内容并暂停:
1 | var reg = /123/g; |
使用test()
方法也会更新lastIndex
的值,如果不注意这一点,可能会出问题:
1 | var reg = /123/g; |
使用字符串的match()
方法执行有全局修饰的正则表达式,返回匹配到的内容数组,匹配全部后结束:
1 | var reg = /123/g; |
5.6.5 字符串方法
字符串中可以使用正则表达式的方法:
1 | var str = new String('hello'); |
5.6.6 量词
表达式中可以使用量词,使用n
表示任意字符:
1 | var str = 'hello'; |
在具有量词匹配的表达式中,使用贪婪模式和非贪婪模式:
1 | var str = '123123'; |
5.6.7 范围
表达式中可以使用方括号限制查找某个范围的字符串,实际上比较的是ASCII码:
1 | var str = 'ab12CD'; |
5.6.8 特殊字符
表达式中可以使用元字符替代特殊字符进行查找:
1 | console.log('ab12_!+'.match(/./g));// ['a', 'b', '1', '2', '_', '!', '+'] 查找单个字符,除了换行和行结束符 |
5.6.9 捕获分组
在正则表达式中使用()
有两个作用:
- 将被包裹的内容进行单独匹配,用于形成捕获分组
- 括号后的量词会将括号视为整体
在未使用全局修饰的情况下,使用exec()
方法获取匹配内容:
1 | var reg = /(123)(a.a)/; |
在未使用全局修饰的情况下,使用字符串的match()
方法获取匹配内容,和正则表达式的exec()
方法相同:
1 | var reg = /(123)(a.a)/; |
在使用全局修饰的情况下,使用exec()
方法获取匹配内容:
1 | var reg = /(123)(a.a)/g; |
在使用全局修饰的情况下,使用字符串的match()
方法获取匹配内容:
1 | var reg = /(123)(a.a)/g; |
6 文档对象模型
6.1 定义
文档对象模型(DOM,Document Object Model),在JavaScript中通过DOM操作HTML页面。
DOM将HTML页面映射为一个多节点模型,每个元素和属性都是节点:
- 文档本身就是文档节点,使用document对象表示,作为window对象的属性存在的,不用获取可以直接使用。
- 注释是注释节点。
- 元素是元素节点。
- 元素内的属性是属性节点。
- 元素内的文本是文本节点。元素之间的空白也是文本节点。
当网页被加载时,浏览器会创建页面的DOM树:
节点的通用属性:
- nodeName:代表当前节点的名字,只读属性。元素节点返回大写的标签名,属性节点返回属性名,文本节点返回
#text
字符串。 - nodeType:返回一个整数,这个数值代表着给定节点的类型,只读属性。元素节点返回1,属性节点返回2,文本节点返回3。
- nodeValue:返回给定节点的当前值,可读写的属性。元素节点返回null,属性节点返回属性值,文本节点返回文本节点的内容。
6.2 节点
6.2.1 文档节点
查询元素节点的方法,需要使用文档节点document对象调用:
1 | var home = document.getElementById('home');// 通过元素的id属性查找元素节点 |
除了通过id属性查找返回的是对象外,其余三个返回的均为伪数组:
1 | var home = document.getElementById('home'); |
文档节点的属性:
1 | console.log(document.title);// 获取文档标题 |
使用文档节点操作节点:
1 | console.log(document.querySelector('#home'));// 使用CSS选择器查找元素节点,返回第一个元素节点 |
6.2.2 元素节点
元素节点的属性:
1 | var home = document.getElementById('home'); |
通过元素节点获取其他节点:
- 通过firstChild属性获取的第一个节点通常是文本节点
- 通过getAttributeNode(属性名)方法获取指定属性节点,通常不使用属性节点
获取和修改元素节点的属性:
1 | var home = document.getElementById('home'); |
操作元素节点:
1 | var home = document.getElementById('home'); |
操作元素样式:
1 | var home = document.getElementById('home'); |
6.2.3 属性节点
属性节点的属性:
1 | var home = document.getElementById('home'); |
6.3 事件
6.3.1 使用
事件就是用户和浏览器之间的交互行为,比如点击、移动。
事件可以写在HTML的元素内,也可以写在JavaScript里:
- 写在HTML的元素里,不建议使用这种方式:
html 1
<span id="home" onclick="alert('span');">
- 写在JavaScript里,建议使用这种方式:
js 1
2
3
4var home = document.getElementById('home');
home.onclick = function() {
alert('span');
}
当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递给响应函数,在事件对象中封装了当前事件相关的一切信息。
在使用IE浏览器时,如果版本小于IE8,响应函数被触发时,浏览器不会传递事件对象,而是将事件对象作为window对象的属性保存。
兼容IE和其他浏览器的方式:
1 | // 获取事件对象,兼容写法 |
常用事件:
1 | var home = document.getElementById('home'); |
使用target
属性获取触发事件的元素:
1 | // 获取事件对象,兼容写法 |
6.3.2 事件的传播
关于事件的传播网景公司和微软公司有不同的理解:
- 微软公司认为事件应该是由内向外传播,也就是当事件触发时,应该先触发当前元素上的事件,然后再向当前元素的祖先元素上传播,也就说事件应该在冒泡阶段执行。
- 网景公司认为事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素的事件,然后在向内传播给后代元素。
W3C综合了两个公司的方案,将事件传播分成了三个阶段:
- 捕获阶段:在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件。
- 目标阶段:事件捕获到目标元素,捕获结束开始在目标元素上触发事件。
- 冒泡阶段:事件从目标元素向他的祖先元素传递,依次触发祖先元素上的事件。
IE8及以下的浏览器中没有捕获阶段,其他浏览器在调用方法时处理。
6.3.3 事件的冒泡
事件的冒泡,指的是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发。
如果不希望发生事件冒泡可以取消冒泡:
1 | // 获取事件对象,兼容写法 |
取消冒泡并不会取消元素的默认动作,比如超链接的默认跳转,可以使用preventDefault()
方法取消默认动作:
1 | // 获取事件对象,兼容写法 |
既可以取消冒泡,又可以取消默认动作:
1 | // 获取事件对象,兼容写法 |
6.3.4 事件的绑定
给元素绑定事件有两种方式:
- 通过
元素.事件 = 函数
的方式绑定,一个事件只支持绑定一个函数,后面的会覆盖前面的:js 1
2
3
4var home = document.getElementById('home');
home.onclick = function() {
alert('span');
} - 通过方法绑定,一个事件可以绑定多个函数,但是需要兼容不同的浏览器:
js 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
26var home = document.getElementById('home');
// 在页面加载后绑定
window.onload = function() {
bindClick(home, 'click', 'first');
bindClick(home, 'click', 'last');
};
// 通用的绑定方法,将handle()方法绑定到传入的对象上
function bindClick(obj, eventStr, name) {
if (obj.addEventListener) {
// 大部分浏览器兼容的方式,需要使用没有on前缀的事件
obj.addEventListener(eventStr, function() {
console.log(this);// 在addEventListener()方法中,this对象是绑定的事件对象
handle(name);
}, false);// 如果希望在捕获阶段就触发事件,可以设置为true,一般为false
} else {
// IE8及以下,需要使用有on前缀的事件
obj.attachEvent("on" + eventStr, function() {
console.log(this);// 在attachEvent()方法中,this对象是window对象
handle.call(obj, name);// 调用回调函数,强制将this对象设为绑定的事件对象
});
}
}
// 通用的执行方法
function handle(name) {
console.log(name);
}
7 浏览器对象模型
7.1 定义
浏览器对象模型(BOM,Browser Object Model),允许JavaScript与浏览器交互。
BOM提供了一组对象用于对浏览器进行操作:
- Window:代表的是整个浏览器的窗口,同时window也是网页中的全局对象。
- Navigator:代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器。
- Location:代表当前浏览器的地址栏信息,通过Location可以获取地址栏信息,或者操作浏览器跳转页面。
- History:代表浏览器历史记录,可以通过该对象来操作浏览器的历史记录。由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页。而且该操作只在当次访问时有效。
- Screen:代表用户的屏幕的信息,通过该对象可以获取到用户的显示器的相关的信息。
这些BOM对象(全局对象)在浏览器中都是作为window对象的属性保存的,可以通过window对象来使用,也可以直接使用。
7.2 使用
7.2.1 Window对象
Window对象的属性和方法可以省略直接使用。
Window对象属性:
1 | console.log(window.document);// 返回Document对象的只读引用 |
Window对象方法:
1 | // 关闭浏览器窗口 |
7.2.2 Navigator对象
Navigator对象属性:
1 | console.log(navigator.appName);// 返回浏览器的名称 |
由于历史原因,Navigator对象中的大部分属性都已经不能识别浏览器了,一般只使用userAgent来判断浏览器的信息。
7.2.3 Location对象
Location对象属性:
1 | console.log(location.hash);// 返回URL的锚部分 |
Location对象方法:
1 | location.assign();// 载入新的文档,相当于给location对象赋值 |
7.2.4 History对象
History对象属性:
1 | console.log(location.length);// 返回历史列表中的网址数 |
History对象方法:
1 | location.back();// 加载历史列表中的前一个URL |
7.2.5 Screen对象
Screen对象属性:
1 | console.log(screen.height);// 返回屏幕的总高度 |
条