参考资料:https://blog.csdn.net/qq_38490457/article/details/109257751
第一章 JavaScript简介
1.1、JavaScript的起源
略。
1.2、JavaScript的组成
一个完整的JavaScript实现应该由以下三个部分构成:ECMScript + DOM + BOM。
1.3、JavaScript的特点
- JavaScript是一门解释型语言
所谓解释型值语言是指不需要被编译为机器码再执行,而是直接执行。 - JavaScript是一门动态语言
动态语言可以暂时理解为在语言中的一切内容都是不确定的。比如一个变量,这一时刻是个整型,下一时刻可能会变成字符串了。 - JavaScript是一门面向对象的语言。
- JavaScript是严格区分大小写的
也就是abc和Abc会被解析器认为是两个不同的东西。
1.4、JavaScript的使用
-
标签引用:在HTML中在script标签中就可以编写JavaScript代码 <script>
alert("Hello,World!");
</script>
-
文件引用:在一个单独的js文件中也可以编写JavaScript代码,然后在HTML文件中使用script标签进行引用 <script src="main.js"></script>
1.5、JavaScript的输出
1.5.1、页面输出
使用JavaScript向页面输出一句话:
<script>
document.write("Hello,World!");
</script>
1.5.2、控制台输出
使用JavaScript向控制台输出一句话:
<script>
console.log("输出一条日志");
console.info("输出一条信息");
console.warn("输出一条警告");
console.error("输出一条错误");
</script>
1.5.3、弹出窗口输出
使用JavaScript向弹出窗口输出一句话:
<script>
alert("Hello,World!");
</script>
1.6、JavaScript的注释
- 单行注释:
// 注释内容 - 多行注释:
/* 注释内容 */
第二章 JavaScript基础语法
2.1、标识符
标识符,就是指给变量、函数、属性或函数的参数起名字。
标识符命名规则:
- 由字母、数字、下划线、美元符号($)组成,且第一个字符不能是数字
- 按照惯例,ECMAScript 标识符采用驼峰命名法
- 标识符不能是关键字和保留字符
2.2、字面量和变量
2.2.1、字面量(常量)
字面量实际上就是一些固定的值,字面量都是不可以改变的,由于字面量不是很方便使用,所以在JavaScript中很少直接使用字面量,使用的而是变量。
2.2.2、变量
值可以被修改的叫做变量。JavaScript 变量是存储数据值的容器。 变量的声明: 使用var 关键字声明一个变量。
2.3、数据类型
2.3.1、类型分类
JavaScript中一共有5种基本数据类型:
- 字符串型(String)
- 数值型(Number)
- 布尔型(Boolean)
- undefined型(Undefined)
- null型(Null)
这5种之外的类型都称为Object,所以总的来看JavaScript中共有六种数据类型。
2.3.2、typeof运算符
使用typeof操作符可以用来检查一个变量的数据类型。 使用方式:
typeof 数据
2.3.3、String
String用于表示一个字符序列,即字符串。字符串需要使用 单引号 或 双引号 括起来。
转义字符:
2.3.4、Number
Number 类型用来表示整数和浮点数,最常用的功能就是用来表示10进制的整数和浮点数。
特殊的数字:
- Infinity:正无穷
- -Infinity:负无穷
- NaN:非法数字(Not A Number)
其它的进制:
- 二进制:0b 开头表示二进制,但是,并不是所有的浏览器都支持
- 八进制:0 开头表示八进制
- 十六进制:0x 开头表示十六进制
注意:使用typeof检查一个Number类型的数据时(包括NaN 和 Infinity),会返回"number"。
2.3.5、Boolean
布尔型也被称为逻辑值类型或者真假值类型。
布尔型只能够取真(true)和假(false)两种数值。除此以外, 其它的值都不被支持。
2.3.6、Undefined
在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。
2.3.7、Null
undefined值实际上是由null值衍生出来的,所以如果比较undefined和null是否相等,会返回true。
从语义上看null表示的是一个空的对象,所以使用typeof检查null会返回一个Object。
<script>
console.log(undefined == null)
console.log(undefined === null)
console.log(typeof null);
</script>
2.4、强制类型转换
强制类型转换指将一个数据类型强制转换为其它的数据类型。一般是指,将其它的数据类型转换为String、Number、Boolean。
2.4.1、转换为String类型
将其它数值转换为字符串有三种方式:toString()、String()、 拼串。
-
调用被转换数据类型的toString() 方法,该方法不会影响到原变量,它会将转换的结果返回,但是注意:null和undefined这两个值没有toString()方法,如果调用它们的方法,会报错。 <script>
var a = 123;
console.log(a);
console.log(typeof a);
a = a.toString();
console.log(a);
console.log(typeof a);
</script>
-
调用String()函数,并将被转换的数据作为参数传递给函数,使用String()函数做强制类型转换时,对于Number和Boolean实际上就是调用的toString()方法,但是对于null和undefined,就不会调用toString()方法,它会将 null 直接转换为 “null”,将 undefined 直接转换为 “undefined”。 -
为任意的数据类型 +""
2.4.2、转换为Number类型
有三个函数可以把非数值转换为数值:Number()、parseInt() 和parseFloat()。Number()可以用来转换任意类型的数据,而后两者只能用于转换字符串。
1. 使用Number()函数
- 字符串 --> 数字
如果是纯数字的字符串,则直接将其转换为数字 如果字符串中有非数字的内容,则转换为NaN 如果字符串是一个空串或者是一个全是空格的字符串,则转换为0 - 布尔 --> 数字
true 转成 1 false 转成 0 - null --> 数字
null 转成 0 - undefined --> 数字
undefined 转成 NaN
2. parseInt()
这种方式专门用来对付字符串,parseInt() 把一个字符串转换为一个整数
3. parseFloat()
这种方式专门用来对付字符串,parseFloat() 把一个字符串转换为一个浮点数
注意:如果对非String使用parseInt()或parseFloat(),它会先将其转换为String然后在操作
2.4.3、转换为Boolean类型
将其它的数据类型转换为Boolean,只能使用Boolean()函数。
- 数字 —> 布尔
除了0和NaN,其余的都是true - 字符串 —> 布尔
除了空串,其余的都是true - null和undefined都会转换为false
- 对象也会转换为true
2.5、运算符
运算符也叫操作符,通过运算符可以对一个或多个值进行运算并获取运算结果。
比如:typeof就是运算符,可以来获得一个值的类型,它会将该值的类型以字符串的形式返回(number string boolean undefined object)
2.5.1、算术运算符
算术运算符用于表达式计算。
运算符 | 描述 |
---|
+ | 加法 | - | 减法 | * | 乘法 | / | 除法 | % | 取模(求余数) | ++ | 自增 | - - | 自减 |
2.5.2、关系运算符
关系运算符在逻辑语句中使用,以测定变量或值是否相等。
运算符 | 描述 |
---|
> | 大于 | < | 小于 | >= | 大于或等于 | <= | 小于或等于 |
2.5.3、赋值运算符
赋值运算符用于给 JavaScript 变量赋值。 =、+=、-=、*=、/=、%=
2.5.4、逻辑运算符
逻辑运算符用于测定变量或值之间的逻辑。 &&、||、!
1. && 与
&&可以对符号两侧的值进行与运算并返回结果,运算规则如下:
- 两个值中只要有一个值为false,就返回false,只有两个值都为true时,才会返回true
- JS中的“与”属于短路的与,如果第一个值为false,则不会检查第二个值
- 非布尔值时:如果两个都为true,则返回第二个值,如果两个值中有false,则返回靠前的false的值
|| 或
||可以对符号两侧的值进行或运算并返回结果,运算规则如下:
- 两个值中只要有一个true,就返回true,只有两个值都为false,才会返回false
- JS中的“或”属于短路的或,如果第一个值为true,则不会检查第二个值
- 非布尔值时:如果两个都为false ,则返回第二个值,如果两个值中有true,则返回靠前的true的值
! 非
!可以用来对一个值进行非运算,所谓非运算就是对一个布尔值进行取反操作,true变false,false变true,运算规则如下:
- 如果对一个值进行两次取反,它不会变化
- 非布尔值时:先会将其转换为布尔值,然后再取反,所以我们可以利用该特点,来将一个其它的数据类型转换为布尔值,可以为一个任意数据类型取两次反,来将其转换为布尔值,原理和Boolean()函数一样
2.5.5、比较运算符
比较运算符用来比较两个值是否相等,如果相等会返回true,否则返回false。
- 使用
== 来做相等运算 当使用==来比较两个值时,如果值的类型不同,则会自动进行类型转换,将其转换为相同的类型,然后再比较 - 使用
!= 来做不相等运算 不相等用来判断两个值是否不相等,如果不相等返回true,否则返回false,不相等也会对变量进行自动的类型转换,如果转换后相等它也会返回false - 使用
=== 来做全等运算 用来判断两个值是否全等,它和相等类似,不同的是它不会做自动的类型转换,如果两个值的类型不同,直接返回false - 使用
!== 来做不全等运算 用来判断两个值是否不全等,它和不等类似,不同的是它不会做自动的类型转换,如果两个值的类型不同,直接返回true
2.5.6、条件运算符
语法:variablename=(condition)?value1:value2;
执行流程:如果condition为true,则执行语句1,并返回执行结果,如果为false,则执行语句2,并返回执行结果。
2.5.7、逗号运算符
使用逗号可以在一条语句中执行多次操作。
比如:var num1=1, num2=2, num3=3;
使用逗号运算符分隔的语句会从左到右顺 序依次执行。
2.6、运算符优先级
2.7、代码块
2.7.1、语句
语句是一个程序的基本单位,JavaScript的程序就是由一条一条语句构成的,每一条语句使用; 结尾。
2.7.2、代码块
代码块是在大括号 {} 中所写的语句,以此将多条语句的集合视为一条语句来使用。
例如:
{
var a = 123;
a++;
alert(a);
}
我们一般使用代码块将需要一起执行的语句进行分组,需要注意的是,代码块结尾不需要加 分号。
2.8、条件语句
条件语句是通过判断指定表达式的值来决定执行还是跳过某些语句,最基本的条件语句:
-
if…else -
switch…case switch语句更适用于多条分支使用同一条语句的情况。 语法格式: switch (语句) {
case 表达式1:
语句...
case 表达式2:
语句...
default:
语句...
}
注意:需要注意的是一旦符合case的条件程序会一直运行到结束,所以我们一般会在case中添加break作为语句的结束。
2.9、循环语句
循环语句和条件语句一样,也是基本的控制语句,只要满足一定的条件将会一直执行,最基本的循环语句:
-
while while(条件表达式){
语句...
}
-
do…while do{
语句...
}while(条件表达式);
-
for for(初始化表达式 ; 条件表达式 ; 更新表达式){
语句...
}
2.9.4、跳转控制
break :结束最近的一次循环,可以在循环和switch语句中使用。continue :结束本次循环,执行下一次循环,只能在循环中使用。
2.10、对象基础
2.10.1、概述
Object类型,我们也称为一个对象,是JavaScript中的引用数据类型。它是一种复合值,它将很多值聚合到一起,可以通过名字访问这些值。 对象也可以看做是属性的无序集合,每个属性都是一个名/值对。对象除了可以创建自有属性,还可以通过从一个名为原型的对象那里继承属性。 除了字符串、数字、true、false、null和undefined之外,JavaScript中的值都是对象。
2.10.2、创建对象
创建对象有两种方式:
-
第一种方式: var person = new Object();
person.name = "孙悟空";
person.age = 18;
console.log(person);
-
第二种方式: var person = {
name: "孙悟空",
age: 18
};
console.log(person);
2.10.3、访问属性
访问属性的两种方式:
-
第一种方式:使用 . 来访问 对象.属性名
-
第二种方式:使用 [] 来访问 对象[‘属性名’]
2.10.4、删除属性
删除对象的属性可以使用delete关键字,格式如下:
delete 对象.属性名;
2.10.5、遍历对象
枚举遍历对象中的属性,可以使用for … in语句循环,对象中有几个属性,循环体就会执行几次。
语法格式:
for (var 变量 in 对象) {
}
例如:
2.10.6、数据类型梳理
2.10.6.1、基本数据类型
JavaScript中的变量可能包含两种不同数据类型的值:基本数据类型和引用数据类型。
JavaScript中一共有5种基本数据类型:String、Number、 Boolean、Undefined、Null。
基本数据类型的比较是值的比较,也就是只要两个变量的值相等,我们就认为这两个变量相等。
2.10.6.2、引用数据类型
引用类型的值是保存在内存中的对象。
当一个变量是一个对象时,实际上变量中保存的并不是对象本身,而是对象的引用。
当从一个变量向另一个变量复制引用类型的值时,会将对象的引用复制到变量中,并不是创建一个新的对象。
这时,两个变量指向的是同一个对象。 因此,改变其中一个变量会影响另一个。
2.10.7、栈和堆梳理
JavaScript在运行时数据是保存到栈内存和堆内存当中的。
简单来说栈内存用来保存变量和基本类型,堆内存是用来保存对象。
我们在声明一个变量时,实际上就是在栈内存中创建了一个空间用来保存变量。
如果是基本类型则在栈内存中直接保存,如果是引用类型则会在堆内存中保存,但是引用类型的引用还是存在栈内存中的。
2.11、函数
2.11.1、概述
函数是由一连串的子程序(语句的集合)所组成的,可以被外部程序调用,向函数传递参数之后,函数可以返回一定的值。
通常情况下,JavaScript代码是自上而下执行的,不过函数体内部的代码则不是这样。如果只是对函数进行了声明,其中的代码并不会执行,只有在调用函数时才会执行函数体内部的代码。
这里要注意的是JavaScript中的函数也是一个对象,使用typeof检查一个函数对象时,会返回function。
2.11.2、函数创建
-
使用 函数对象 来创建一个函数(几乎不用) var 函数名 = new Function("执行语句");
-
使用 函数声明 来创建一个函数(比较常用) function 函数名([形参1,形参2,...,形参N]) {
语句...
}
-
使用 函数表达式 来创建一个函数(比较常用) var 函数名 = function([形参1,形参2,...,形参N]) {
语句....
}
2.11.3、函数调用
函数名(参数1,参数2...);
2.11.4、函数参数
- JS中的所有的参数传递都是按值传递的,也就是说把函数外部的值赋值给函数内部的参数,就和把值从一个变量赋值给另一个变量是一样的,在调用函数时,可以在()中指定实参(实际参数),实参将会赋值给函数中对应的形参
- 调用函数时,解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能,则需要对参数进行类型的检查,函数的实参可以是任意的数据类型
- 调用函数时,解析器也不会检查实参的数量,多余实参不会被赋值,如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined
2.11.5、函数返回值
可以使用 return 来设置函数的返回值,return后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。
语法格式:return 值
注意:在函数中return后的语句都不会执行,如果return语句后不跟任何值就相当于返回一个undefined,如果函数中不写return,则也会返回undefined,return后可以跟任意类型的值
2.11.6、嵌套函数
在函数中声明的函数就是嵌套函数,嵌套函数只能在当前函数中可以访问,在当前函数外无法访问。
2.11.7、匿名函数
没有名字的函数就是匿名函数,它可以让一个变量来接收,也就是用 “函数表达式” 方式创建和接收。
例如:
var fun = function () {
alert("我是一个匿名函数");
}
fun();
2.11.8、立即执行函数
函数定义完,立即被调用,这种函数叫做立即执行函数,立即执行函数往往只会执行一次.
例如:
(function () {
alert("我是一个匿名函数");
})();
2.11.9、对象中的函数(方法)
对象的属性值可以是任何的数据类型,也可以是个函数。
如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法,调用这个函数就说调用对象的方法(method)。
注意:方法和函数只是名称上的区别,没有其它别的区别
例如:
2.11.10、this对象
解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的上下文对象,根据函数的调用方式的不同,this会指向不同的对象
- 以函数的形式调用时,this永远都是window
- 以方法的形式调用时,this就是调用方法的那个对象
2.12、对象进阶
2.12.1、用工厂方法创建对象
创建多个对象示例:
<script>
function createPerson(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayName = function () {
console.log(this.name);
};
return obj;
}
var person1 = createPerson("孙悟空", 18);
var person2 = createPerson("猪八戒", 19);
var person3 = createPerson("沙和尚", 20);
console.log(person1);
console.log(person2);
console.log(person3);
</script>
上述代码好像已经完美的解决了创建多个对象的难题,那我们是不是可以用循环批量创建1000个对象了呢?那我们就来试试:
<script>
function createPerson(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayName = function () {
console.log(this.name);
};
return obj;
}
for (var i = 1; i <= 1000; i++) {
var person = createPerson("person" + i, 18);
console.log(person);
}
</script>
这样我们就实现了批量创建对象的功能,至于对象的名称和年龄,我们可以通过名称数组和年龄数组来获取,但这并不是我们本小节的重点,我们就忽略了。
2.12.2、用构造函数创建对象
构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写,构造函数和普通函数的还有一个区别就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new 关键字来调用。
<script>
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name);
};
}
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
console.log(person1);
console.log(person2);
console.log(person3);
</script>
那构造函数是怎么执行创建对象的过程呢?我再来解释一下:
- 调用构造函数,它会立刻创建一个新的对象
- 将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
- 逐行执行函数中的代码
- 将新建的对象作为返回值返回
你会发现构造函数有点类似工厂方法,但是它创建对象和返回对象都给我们隐藏了,使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象,称为是该类的实例。
现在,this又出现了一种新的情况,为了不让大家混淆,我再来梳理一下:
- 当以函数的形式调用时,this是window
- 当以方法的形式调用时,谁调用方法this就是谁
- 当以构造函数的形式调用时,this就是新创建的那个对象
我们可以使用 instanceof 运算符检查一个对象是否是一个类的实例,它返回true或false 语法格式:
对象 instanceof 构造函数
2.12.3、原型
在前一节中,我们学习了使用构造函数的方式进行创建对象,但是,它还是存在一个问题,那就是,你会发现,每一个对象的属性不一样这是一定的,但是它的方法似乎好像是一样的,如果我创建1000个对象,那岂不是内存中就有1000个相同的方法,那要是有10000个,那对内存的浪费可不是一点半点的,我们有没有什么好的办法解决,没错,我们可以把函数抽取出来,作为全局函数,在构造函数中直接引用就可以了.
上代码: 但是,在全局作用域中定义函数却不是一个好的办法,为什么呢?因为,如果要是涉及到多人协作开发一个项目,别人也有可能叫sayName这个方法,这样在工程合并的时候就会导致一系列的问题,污染全局作用域,那该怎么办呢?有没有一种方法,我只在Person这个类的全局对象中添加一个函数,然后在类中引用?答案肯定是有的,这就需要原型对象了,我们先看看怎么做的,然后在详细讲解原型对象。
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
console.log(this.name);
};
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
person1.sayName();
person2.sayName();
person3.sayName();
</script>
那原型(prototype)到底是什么呢?
我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象,即显式原型,原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__ (隐式原型)来访问该属性。当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。
以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。
2.12.4、原型链
访问一个对象的属性时,先在自身属性中查找,找到返回, 如果没有,再沿着__proto__这条链向上查找,找到返回,如果最终没找到,返回undefined,这就是原型链,又称隐式原型链,它的作用就是查找对象的属性(方法)。
我们使用一张图来梳理一下上一节原型案例的代码:
2.12.5、toString方法
toString()函数用于将当前对象以字符串的形式返回。该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法,所有主流浏览器均支持该函数。
JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要。
类型 | 行为描述 |
---|
String | 返回 String 对象的值。 | Number | 返回 Number 的字符串表示。 | Boolean | 如果布尔值是true,则返回"true"。否则返回"false"。 | Object(默认) | 返回"[object ObjectName]",其中 ObjectName 是对象类型的名称。(暂未理解 ) | Array | 将 Array 的每个元素转换为字符串,并将它们依次连接起来,两个元素之间用英文逗号作为分隔符进行拼接。 | Date | 返回日期的文本表示。 | Error | 返回一个包含相关错误信息的字符串。 | Function | 返回如下格式的字符串,其中 functionname 是一个函数的名称 此函数的 toString 方法被调用: “function functionname() { [native code] }” |
演示:
<script>
var str = "Hello";
console.log(str.toString());
var num = 15.26540;
console.log(num.toString());
var bool = true;
console.log(bool.toString());
var obj = {
name: "张三",
age: 18
};
console.log(obj.toString());
var array = ["CodePlayer", true, 12, -5];
console.log(array.toString());
var date = new Date(2013, 7, 18, 23, 11, 59, 230);
console.log(date.toString());
var error = new Error("自定义错误信息");
console.log(error.toString());
console.log(Function.toString());
</script>
2.12.6、hasOwnProperty方法
如果判断当前对象是否包含指定的属性或方法可以使用 in 运算符来检查。
如果我只想要检查自身对象是否含有某个方法或属性,我们可以使用Object的hasOwnProperty() 方法,它返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。 hasOwnProperty()是原型中的方法。
二者区别如下代码演示:
<script>
function MyClass() {}
MyClass.prototype.name = "我是原型中的名字";
var mc = new MyClass();
mc.age = 18;
console.log("age" in mc);
console.log("name" in mc);
console.log(mc.hasOwnProperty("age"));
console.log(mc.hasOwnProperty("name"));
</script>
2.12.7、对象继承
实际上,JavaScript语言是通过一种叫做原型(prototype)的方式来实现面向对象编程的。
实现继承有一个最大的好处就是子对象可以使用父对象的属性和方法,从而简化了一些代码。
JavaScript有六种非常经典的对象继承方式,但是我们只学习前三种:
- 原型链继承
- 借用构造函数继承
- 组合继承(重要)
- 原型式继承
- 寄生式继承
- 寄生组合式继承
继承有点难理解,先放一放
2.12.7.1、原型链继承
核心思想: 子类型的原型为父类型的一个实例对象
基本做法:
- 定义父类型构造函数
- 给父类型的原型添加方法
- 定义子类型的构造函数
- 创建父类型的对象赋值给子类型的原型
- 将子类型原型的构造属性设置为子类型
- 给子类型原型添加方法
- 创建子类型的对象: 可以调用父类型的方法
示例:
2.12.7.2、借用构造函数继承
2.12.7.3、组合继承
2.12.8、垃圾回收
垃圾回收(GC:Garbage Collection):就像人生活的时间长了会产生垃圾一样,程序运行过程中也会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢,所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾。
当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作,我们需要做的只是要将不再使用的对象设置null即可。
案例演示:
function Person(name, age) {
this.name = name;
this.age = age;
}
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
person1 = null;
person2 = null;
person3 = null;
其他参考链接:https://blog.csdn.net/qq_21428081/article/details/82465801
2.13、作用域
作用域指一个变量的作用的范围,在JS中一共有两种作用域:
2.13.1、声明提前
变量的声明提前:使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值); 但是如果声明变量时不使用var关键字,则变量不会被声明提前(let、const无;变量赋值无提升)
函数的声明提前:使用函数声明形式创建的函数 function 函数名(){} ,它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数。 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用。
2.13.2、作用域
2.13.2.1、全局作用域
- 直接编写在script标签中的JavaScript代码,都在全局作用域
- 全局作用域在页面打开时创建,在页面关闭时销毁
- 在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建,我们可以直接使用
- 在全局作用域中:
创建的变量都会作为window对象的属性保存 创建的函数都会作为window对象的方法保存 - 全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到
2.13.2.2、函数作用域
- 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
- 每调用一次函数就会创建一个新的函数作用域,它们之间是互相独立的
- 在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
在函数中要访问全局变量可以使用window对象 - 作用域链:当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有则向上一级作用域中寻找,直到找到全局作用域,如果全局作用域中依然没有找到,则会报错ReferenceError
2.13.3、作用域链
多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外),查找变量时就是沿着作用域链来查找的。
查找一个变量的查找规则:
- 在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2
- 在上一级作用域的执行上下文中查找对应的属性,如果有直接返回,否则进入3
- 再次执行2的相同操作,直到全局作用域,如果还找不到就抛出找不到的ReferenceError异常
第三章 JavaScript常用对象
3.1、数组对象
3.1.1、概述
数组也是对象的一种,数组是一种用于表达有顺序关系的值的集合的语言结构,在JavaScript中数组内可以是不同的元素。
数组内的各个值被称作元素,每一个元素都可以通过索引(下标)来快速读取,索引是从零开始的整数。
使用typeof 检查一个数组对象时,会返回object。
3.1.2、创建数组
- 通过new运算符创建数组(参数:传入任意的数据,存储到数组中)
- 省略new运算符创建数组
- 直接通过常量赋值(JS中一般使用中括号[ ]表示数组)
<script>
var arr1 = new Array("hello", 5, 20);
var arr2 = Array("hello", 5, 20);
var arr3 = ["hello", 5, 20];
</script>
3.1.3、遍历数组
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
3.1.4、数组属性
- constructor属性:返回创建数组对象的原型函数
- length属性:设置或返回数组元素的个数
3.1.5、数组方法
1. push( )
功能:给数组的末尾添加一个或多个元素 返回值:添加完新元素以后的数组的长度 直接操作原始数组
<script>
var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.push("唐僧", "蜘蛛精", "白骨精", "玉兔精");
console.log(arr);
console.log(result);
</script>
2. pop( )
功能:从数组末尾取下一个元素 返回值:被取下的元素 直接操作原始数组
3. shift( )
功能:从数组的头部取下一个元素 返回值:取下的元素 直接操作原始数组
4. unshift( )
功能:从数组的头部插入元素 返回值:插入元素后数组的长度 直接操作原始数组
5. concat( )
a)拷贝原数组,生成新数组 b)合并数组 返回值:合并后的新数组,原数组不会改变。 【注】就算合并的是数组,数组中的元素会单独拆出来,再进行合并。
<script>
var arr1 = new Array(30,45,66);
var arr2 = new Array(50,78);
var newArr = arr1.concat(arr2, 'hello')
alert(arr1);
alert(newArr);
alert(newArr.length)
</script>
6. slice( )
格式:数组.slice(start, end); 功能:可以基于当前数组获取指定区域元素 [star, end),提取出元素生成新数组。 【注】start和end指的是数组的下标,并且不包括下标end;如果不写end则默认截取到末尾;如果传入的参数是负数,则从数组的末尾往前数,-1代表倒数第一个,-2代表倒数第二个。 返回值:生成新数组,原数组不会有任何改变
<script>
var arr1 = new Array(76,98,17,28,10,64,564);
var newArr1 = arr1.slice(2,4);
var newArr2 = arr1.slice(2,-4);
var newArr3 = arr1.slice(-4,-2);
console.log(newArr1);
console.log(newArr2);
console.log(newArr3);
</script>
7. splice( )
格式:数组.splice( start, length, 数据1, 数据2, …); 参数:
- start:开始截取的位置的下标
- length:截取的元素的长度,不写则默认删除到最后
- 第三个参数开始:在start位置,插入的元素
功能:增加、删除、修改原数组(即先删除,再增加) 返回值:截取下来的元素组成的数组
<script>
var arr1 = Array(10,20,30,40);
var newArr = arr1.splice(2,0,'hey',88);
alert(arr1);
alert(newArr);
</script>
<script>
var arr1 = Array(10,20,30,40);
var newArr = arr1.splice(2,2);
alert(arr1);
alert(newArr);
</script>
8. join( )
功能:将数组中的元素,用传入的拼接符,拼接成一个字符串;如果不指定连接符,则默认使用,作为连接符 返回值:拼接好的字符串
<script>
var arr = [10,20,30];
var str1 = arr.join('**');
var str2 = arr.join('');
var str3 = arr.join();
console.log(arr);
console.log(str1);
console.log(str2);
console.log(str3);
</script>
9. reverse( )
格式:数组.reverse( ); 功能:该方法用来反转数组(前边的去后边,后边的去前边) 该方法会改变原来的数组,而不会创建新的数组
<script>
var arr = ['hello','hey','hi'];
arr.reverse();
alert(arr);
</script>
10. sort( )方法
格式:数组.sort( ); 功能:sort()会调用toString()方法将数组中的每一项转化成字符串,然后默认会按照Unicode编码进行排序,默认情况下为升序 在原数组上进行排序,不生成副本
【注】即使对于纯数字的数组,使用sort()排序时,也会按照Unicode编码来排序,所以对数字进排序时,可能会得到错误的结果。
<script>
var arr = [15,10,1,5];
arr.sort();
console.log(arr);
</script>
如果想要按照数值的大小进行排序,我们可以在sort()添加一个回调函数,来指定排序规则,回调函数中需要定义两个形参,浏览器将会分别使用数组中的元素作为实参去调用回调函数,使用哪个元素调用不确定,但是肯定的是在数组中a一定在b前边,浏览器会根据回调函数的返回值来决定元素的顺序,如下:
- 如果返回一个大于0的值,则元素会交换位置
- 如果返回一个小于0的值,则元素位置不变
- 如果返回一个等于0的值,则认为两个元素相等,也不交换位置
经过上边的规则,我们可以总结下:
- 如果需要升序排列,则返回 a-b
- 如果需要降序排列,则返回 b-a
<script>
var arr = [15,10,1,5];
arr.sort(function (a, b) {
return a - b;
});
console.log(arr);
</script>
11. forEach()
该方法可以用来遍历数组.
forEach()方法需要一个函数作为参数,像这种函数,由我们创建但是不由我们调用的,我们称为回调函数。数组中有几个元素函数就会执行几次,每次执行时,浏览器会将遍历到的元素,以实参的形式传递进来,我们可以来定义形参,来读取这些内容,浏览器会在回调函数中传递三个参数:
- 第一个参数:就是当前正在遍历的元素
- 第二个参数:就是当前正在遍历的元素的索引
- 第三个参数:就是正在遍历的数组
注:注意:这个方法只支持IE8以上的浏览器,IE8及以下的浏览器均不支持该方法,所以如果需要兼容IE8,则不要使用forEach(),还是使用for循环来遍历数组。
<script>
var arr = ["孙悟空", "猪八戒", "沙和尚"];
arr.forEach(function (value, index, obj) {
console.log(value + " #### " + index + " #### " + obj);
});
</script>
3.2、函数对象
3.2.1、call()和apply()
call()和apply()这两个方法都是函数对象的方法,需要通过函数对象来调用,当对函数调用call()和apply()都会调用函数执行,在调用call()和apply()可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this,call()方法可以将实参在对象之后依次传递,apply()方法需要将实参封装到一个数组中统一传递,如下演示:
call()方法演示:
function fun(a, b) {
console.log("a = " + a);
console.log("b = " + b);
console.log("fun = " + this);
}
var obj = {
name: "obj",
sayName: function () {
console.log(this.name);
}
};
fun(2, 3);
console.log("===============");
fun.call(obj, 2, 3);
注意:默认fun()函数调用,this指向的是window对象,你可以使用call()调用函数,在调用的时候传入一个对象,这个对象就是this所指向的对象,也就是说,可以自己指定this的指向,然后从第二个参数开始,实参将会依次传递
apply()方法演示:
function fun(a, b) {
console.log("a = " + a);
console.log("b = " + b);
console.log("fun = " + this);
}
var obj = {
name: "obj",
sayName: function () {
console.log(this.name);
}
};
fun(2, 3);
console.log("===============");
fun.apply(obj, [2, 3]);
注意:默认fun()函数调用,this指向的是window对象,你可以使用apply()调用函数,在调用的时候传入一个对象,这个对象就是this所指向的对象,也就是说,可以自己指定this的指向,然后从第二个参数开始,需要制定一个实参数组进行参数传递
3.2.2、this指向
- 以函数形式调用时,this永远都是window
- 以方法的形式调用时,this是调用方法的对象
- 以构造函数的形式调用时,this是新创建的那个对象
- 使用call和apply调用时,this是传入的那个指定对象
3.2.3、arguments参数
在调用函数时,浏览器每次都会传递进两个隐含的参数:
- 函数的上下文对象: this
- 封装实参的对象: arguments
arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度,在调用函数时,我们所传递的实参都会在arguments中保存,比如:arguments.length 可以用来获取实参的长度,我们即使不定义形参,也可以通过arguments来使用实参,只不过比较麻烦,例如:
arguments[0]:表示第一个实参 arguments[1]:表示第二个实参 … 它里边有一个属性叫做callee,这个属性对应一个函数对象,就是当前正在指向的函数的对象。
arguments对象演示:
<script>
function fun(a, b) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments.length);
console.log(arguments.callee);
console.log(arguments.callee == fun);
}
fun("Hello", "World");
</script>
3.3、Date对象
在JavaScript中使用Date对象来表示一个时间,如果直接使用构造函数创建一个Date对象,则会封装为当前代码执行的时间。
<script>
var date = new Date();
console.log(date);
console.log(date.getFullYear());
console.log(date.getMonth());
console.log(date.getDate());
console.log(date.getHours());
console.log(date.getMinutes());
console.log(date.getSeconds());
console.log(date.getMilliseconds());
</script>
3.4、Math对象
Math和其它的对象不同,它不是一个构造函数,它属于一个工具类,它里边封装了数学运算相关的属性和方法。
<script>
console.log("PI = " + Math.PI);
console.log("E = " + Math.E);
console.log("===============");
console.log(Math.abs(1));
console.log(Math.ceil(1.1));
console.log(Math.floor(1.99));
console.log(Math.round(1.4));
console.log("===============");
console.log(Math.abs(-1));
console.log(Math.ceil(-1.1));
console.log(Math.floor(-1.99));
console.log(Math.round(-1.4));
console.log("===============");
console.log(Math.round(Math.random() * 10));
console.log(Math.round(Math.random() * (10 - 1) + 1));
console.log("===============");
console.log(Math.pow(12, 3));
console.log(Math.sqrt(4));
</script>
3.5、String对象
3.5.1、概述
在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象
- String():可以将基本数据类型字符串转换为String对象
- Number():可以将基本数据类型的数字转换为Number对象
- Boolean():可以将基本数据类型的布尔值转换为Boolean对象
但是注意:我们在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果,在这一章节中,我们重点介绍String()对象的属性和方法。
3.5.2、字符串属性
- constructor属性:返回创建字符串对象的原型函数
- length属性:可以用来获取字符串的长度
<script>
var str = "Hello World!";
console.log(str.constructor);
console.log(str.length);
</script>
3.5.3、字符串方法
-
charAt()方法 该方法可以根据索引获取指定位置的字符 -
charCodeAt()方法 该方法获取指定位置字符的字符编码(Unicode编码) -
concat()方法 该方法可以用来连接两个或多个字符串 -
indexof()方法 该方法可以检索一个字符串中是否含有指定内容,如果字符串中含有该内容,则会返回其第一次出现的索引,如果没有找到指定的内容,则返回-1,可以指定一个第二个参数,指定开始查找的位置 -
lastIndexOf()方法 该方法的用法和indexOf()一样,不同的是indexOf是从前往后找,而lastIndexOf是从后往前找,也可以指定开始查找的位置 <script>
var str = "Hello World!";
console.log(str.charAt(1));
console.log(str.charCodeAt(1));
console.log(str.concat("你好 ", "世界!"))
console.log(str.indexOf("o"));
console.log(str.indexOf("o", 5));
console.log(str.lastIndexOf("o"));
console.log(str.lastIndexOf("o", 5));
</script>
-
slice()方法 可以从字符串中截取指定的内容,不会影响原字符串,而是将截取到内容返回 参数: 第一个参数:开始位置的索引(包括开始位置) 第二个参数:结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的 注意:也可以传递一个负数作为参数,负数的话将会从后边计算。 <script>
var str = "Hello,World!";
var result = str.slice(1, 4);
console.log(result);
result = str.slice(1);
console.log(result);
result = str.slice(1, -1);
console.log(result);
</script>
-
substring()方法 可以用来截取一个字符串,它和slice()类似 参数: 第一个参数:开始截取位置的索引(包括开始位置) 第二个参数:结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的
注意:不同的是这个方法不能接受负值作为参数,如果传递了一个负值,则默认使用0,而且它还自动调整参数的位置,如果第二个参数小于第一个,则自动交换
<script>
var str = "Hello,World!";
var result = str.substring(1, 4);
console.log(result);
var result = str.substring(4, 1);
console.log(result);
result = str.substring(1);
console.log(result);
result = str.substring(2, -1);
console.log(result);
</script>
-
substr()方法 该方法用来截取字符串 参数: 第一个参数:截取开始位置的索引 第二个参数:截取的长度 -
split()方法 该方法可以将一个字符串拆分为一个数组,需要一个字符串作为参数,将会根据该字符串去拆分数组 <script>
var str = "Hello,World!";
var result1 = str.split(",");
var result2 = str.split("");
var result3 = str.split();
console.log(result1);
console.log(result2);
console.log(result3);
</script>
-
toUpperCase()方法 将一个字符串转换为大写并返回 -
toLowerCase()方法 将一个字符串转换为小写并返回
3.6、RegExp对象
3.6.1、概述
正则表达式用于定义一些字符串的规则,计算机可以根据正则表达式,来检查一个字符串是否符合规则,获取将字符串中符合规则的内容提取出来。
使用typeof检查正则对象,会返回object。
3.6.2、创建正则对象
-
使用对象创建 语法格式: var 变量名 = new RegExp("正则表达式","匹配模式");
匹配模式: ? i:忽略大小写 ? g:全局匹配模式 ? ig:忽略大小写且全局匹配模式 示例: <script>
var reg = new RegExp("ab", "i");
var str = "Abc";
var result = reg.test(str);
console.log(result);
</script>
-
使用字面量创建 语法格式: var 变量名 = /正则表达式/匹配模式;
匹配模式: ? i:忽略大小写 ? g:全局匹配模式 ? m:执行多行匹配
注意:可以为一个正则表达式设置多个匹配模式,且顺序无所谓
示例: <script>
var reg = /a/i;
var str = "Abc";
var result = reg.test(str);
console.log(result);
</script>
3.6.3、正则进阶
-
使用 | 表示或者的意思 -
[ ] 里的内容也是或的关系 常见组合: [a-z]:任意小写字母 [A-Z]:任意大写字母 [A-z]:任意字母 [0-9]:任意数字 -
[^字符序列] 表示除了某些字符序列 常见组合: [^a-z]:除了任意小写字母 [^A-Z]:除了任意大写字母 [^A-z]:除了任意字母 [^0-9]:除了任意数字
3.6.4、正则方法
这些正则方法其实都是字符串的方法,但是它的参数需要传递正则表达式,在这里,我就先称为正则方法。
-
split()方法 该方法可以将一个字符串拆分为一个数组,方法中可以传递一个正则表达式作为参数,这样方法将会根据正则表达式去拆分字符串,这个方法即使不指定全局匹配,也会全都拆分 <script>
var str = "1a2b3c4d5e6f7";
var result = str.split(/[A-z]/);
console.log(result);
</script>
-
search()方法 该方法可以搜索字符串中是否含有指定内容,如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回-1,它可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串,serach()只会查找第一个,即使设置全局匹配也没用 <script>
var str = "hello abc hello aec afc";
var result = str.search(/a[bef]c/);
console.log(result);
</script>
-
match()方法 该方法可以根据正则表达式,从一个字符串中将符合条件的内容提取出来,默认情况下我们的match()只会找到第一个符合要求的内容,找到以后就停止检索,我们可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容,可以为一个正则表达式设置多个匹配模式,且顺序无所谓,match()会将匹配到的内容封装到一个数组中返回,即使只查询到一个结果 <script>
var str = "1a2a3a4a5e6f7A8B9C";
var result = str.match(/[a-z]/ig);
console.log(result);
</script>
-
replace()方法 该方法可以将字符串中指定内容替换为新的内容,默认只会替换第一个,但是可以设置全局匹配替换全部 参数: 第一个参数:被替换的内容,可以接受一个正则表达式作为参数 第二个参数:新的内容 <script>
var str = "1a2a3a4a5e6f7A8B9C";
var result = str.replace(/[a-z]/gi, "@_@");
console.log(result);
</script>
3.6.5、正则量词
通过量词可以设置一个内容出现的次数,量词只对它前边的一个内容起作用,如果有多个内容可以使用 () 括起来,常见量词如下:
-
{n} :正好出现n次 -
{m,} :出现m次及以上 -
{m,n} :出现m-n次 -
+ :至少一个,相当于{1,} -
* :0个或多个,相当于{0,} -
? :0个或1个,相当于{0,1} <script>
var str = "abbc";
reg = /(ab){3}/;
console.log(reg.test(str));
reg = /b{3}/;
console.log(reg.test(str));
reg = /ab{1,3}c/;
console.log(reg.test(str));
reg = /ab{3,}c/;
console.log(reg.test(str));
reg = /ab+c/;
console.log(reg.test(str));
reg = /ab*c/;
console.log(reg.test(str));
reg = /ab?c/;
console.log(reg.test(str));
</script>
3.6.6、正则高阶
如果我们要检查或者说判断是否以某个字符或者字符序列开头或者结尾就会使用^ 和$ 。
^ :表示开头,注意它在[^字符序列] 表达的意思不一样 $ :表示结尾
<script>
var str = "abcabca";
var reg1 = /^a/;
console.log(reg1.test(str));
var str = "abcabca";
var reg2 = /a$/;
console.log(reg2.test(str));
</script>
其他特殊字符:
/ :转义字符\w :任意字母、数字、_,相当于[A-z0-9_] \W :除了字母、数字、_,相当于[^A-z0-9_] \d :任意的数字,相当于[0-9] \D :除了任意的数字,相当于[^0-9] \s :空格\S :除了空格\b :单词边界\B :除了单词边界
第四章 JavaScript DOM
换篇
第五章 JavaScript BOM
换篇
|