JavaScript学习笔记

一、初识JavaScript

解释型语言

JavaScript是一门解释型语言,所谓解释型值语言是指不需要被编译为机器码在执行,而是直接执行。由于少了编译这一步骤,所以解释型语言开发起来尤为轻松,但是解释型语言运行较慢也是它的劣势。不过解释型语言中使用了JIT技术,使得运行速度得以改善。

动态语言

JavaScript是一门动态语言,所谓的动态语言可以暂时理解为在语言中的一切内容都是不确定的。比如一个变量,这一时刻是个整型,下一时刻可能会变成字符串了。当然这个问题我们以后再谈。不过在补充一句动态语言相比静态语言性能上要差一些,不过由于JavaScript中应用的JIT技术,所以JavaScript可能是运行速度最快的动态语言了。

类似于C和Java的语法结构

JavaScript的语法结构与C和Java很像,向for、if、while等语句和Java的基本上是一模一样的。

基于原型的面向对象

Java也是一门面向对象的语言,但是与Java不同JavaScript是基于原型的面向对象。

严格区分大小写

JavaScript是严格区分大小写的,也就是abc和Abc会被解析器认为是两个不同的东西。

二、JavaScript的使用

1、 标签引用

<script>
    alert("Hello,World!");
</script>

2、文件引用

在一个单独的js文件中也可以编写JavaScript代码,然后在HTML文件中使用script标签进行引用。

main.html

<script src="main.js"></script>

main.js

alert("Hello,World!");

三、JavaScript的输出

1、页面输出

<script>
    document.write("Hello,World!");
</script>

2、控制台输出

<script>
    console.log("输出一条日志");//最常用
    console.info("输出一条信息");
    console.warn("输出一条警告");
    console.error("输出一条错误");
</script>

3、弹出窗口输出

<script>
    alert("Hello,World!");
</script>

四、JavaScript的注释

单行注释:// 注释内容

多行注释:/* 注释内容 */

五、JavaScript语法

1、标识符

指给变量、函数、属性或函数的参数起名字。

标识符的命名规范

  • 第一个字符必须是一个字母、下划线( _ )或一个美元符号( $ )。
  • 其它字符可以是字母、下划线、美元符号或数字。
  • 按照惯例,ECMAScript 标识符采用驼峰命名法。
  • 标识符不能是关键字和保留字符。

2、字面量和变量

(1)、字面量
字面量实际上就是一些固定的值,比如:1、2 、3、true、false、null、NaN、“hello”,字面量都是不可以改变的,由于字面量不是很方便使用,所以在JavaScript中很少直接使用字面量,使用的而是变量。

(2)、变量
变量的作用是给某一个值或对象标注名称。比如我们的程序中有一个值123,这个值我们是需要反复使用的,这个时候 我们最好将123这个值赋值给一个变量,然后通过变量去使用123这个值。

变量的声明

var a;

变量的赋值:

a = 123;

声明和赋值同时进行:

var a = 123;

3、数据类型

(1)、类型分类
数据类型决定了一个数据的特征,比如:123和”123”,直观上看这两个数据都是123,但实际上前者是一个数字,而后者是一个字符串。

对于不同的数据类型我们在进行操作时会有很大的不同。

JavaScript中一共有5种基本数据类型:

字符串型(String)
数值型(Number)
布尔型(Boolean)
undefined型(Undefined)
null型(Null)
这5种之外的类型都称为Object,所以总的来看JavaScript中共有六种数据类型。

(2)、typeof运算符

使用typeof操作符可以用来检查一个变量的数据类型。

使用方法

typeof 数据

(3)、String

String用于表示一个字符序列,即字符串。字符串需要使用 单引号双引号 括起来。

注意:使用typeof运算符检查字符串时,会返回"string"。

(4)、Number

Number 类型用来表示整数和浮点数,最常用的功能就是用来表示10进制的整数和浮点数。

Number表示的数字大小是有限的,如果超过了这个范围,则会返回 ±Infinity。

最大值:+1.7976931348623157e+308
最小值:-1.7976931348623157e+308
0以上的最小值:5e-324

特殊的数字:

Infinity:正无穷
-Infinity:负无穷
NaN:非法数字(Not A Number)

其它的进制:

二进制:0b 开头表示二进制,但是,并不是所有的浏览器都支持
八进制:0 开头表示八进制
十六进制:0x 开头表示十六进制

注意:使用typeof检查一个Number类型的数据时(包括NaN 和 Infinity),会返回"number"。

(5)、Boolean

布尔型也被称为逻辑值类型或者真假值类型。

布尔型只能够取真(true)和假(false)两种数值。除此以外, 其它的值都不被支持。

(6)、Undefined

Undefined 类型只有一个值,即特殊的 undefined。

在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。

 注意:使用typeof对没有初始化和没有声明的变量,会返回“undefined”。

(7)、Null

Null 类型是第二个只有一个值的数据类型,这个特殊的值是 null。

undefined值实际上是由null值衍生出来的,所以如果比较undefined和null是否相等,会返回true。

注意:从语义上看null表示的是一个空的对象,所以使用typeof检查null会返回一个Object。

4、强制类型转换

强制类型转换指将一个数据类型强制转换为其它的数据类型。一般是指,将其它的数据类型转换为String、Number、Boolean。

(1)转换为String类型

将其它数值转换为字符串有三种方式:toString()、String()、 拼串。

方式一:调用被转换数据类型的toString()方法,该方法不会影响到原变量,它会将转换的结果返回,但是注意:null和undefined这两个值没有toString()方法,如果调用它们的方法,会报错。

var a = 123;
a = a.toString();
console.log(a);
console.log(typeof a);

方式二:调用String()函数,并将被转换的数据作为参数传递给函数,使用String()函数做强制类型转换时,对于Number和Boolean实际上就是调用的toString()方法,但是对于null和undefined,就不会调用toString()方法,它会将 null 直接转换为 “null”,将 undefined 直接转换为 “undefined”。

var a = 123;
a = String(a);
console.log(a);
console.log(typeof a);

var b = undefined;
b = String(b);
console.log(b);
console.log(typeof b);

var c = null;
c = String(c);
console.log(c);
console.log(typeof c);

方式三:为任意的数据类型 +""

var a = 123;
a = a + "";
console.log(a);
console.log(typeof a);

(2)转换为Number类型

有三个函数可以把非数值转换为数值:Number()、parseInt() 和parseFloat()。Number()可以用来转换任意类型的数据,而后两者只能用于转换字符串。parseInt()只会将字符串转换为整数,而parseFloat()可以将字符串转换为浮点数。

方式一:使用Number()函数

字符串 --> 数字
如果是纯数字的字符串,则直接将其转换为数字
如果字符串中有非数字的内容,则转换为NaN
如果字符串是一个空串或者是一个全是空格的字符串,则转换为0
布尔 --> 数字
true 转成 1
false 转成 0
null --> 数字
null 转成 0
undefined --> 数字
undefined 转成 NaN
方式二:这种方式专门用来对付字符串,parseInt() 把一个字符串转换为一个整数

var a = "123";
a = parseInt(a);
console.log(a);
console.log(typeof a);

方式三:这种方式专门用来对付字符串,parseFloat() 把一个字符串转换为一个浮点数

var a = "123.456";
a = parseFloat(a);
console.log(a);
console.log(typeof a);

注意:如果对非String使用parseInt()或parseFloat(),它会先将其转换为String然后在操作

(3)转换为Boolean类型

将其它的数据类型转换为Boolean,只能使用Boolean()函数。

使用Boolean()函数
数字 —> 布尔
除了0和NaN,其余的都是true
字符串 —> 布尔
除了空串,其余的都是true
null和undefined都会转换为false
对象也会转换为true

5、运算符

运算符也叫操作符,通过运算符可以对一个或多个值进行运算并获取运算结果。

比如:typeof就是运算符,可以来获得一个值的类型,它会将该值的类型以字符串的形式返回(number string boolean undefined object)

(1)算术运算符

(2)关系运算符

(3)赋值运算符

(4)逻辑运算符

&& 与:&&可以对符号两侧的值进行与运算并返回结果,运算规则如下:
·两个值中只要有一个值为false,就返回false,只有两个值都为t rue时,才会返回true
·JS中的“与”属于短路的与,如果第一个值为false,则不会检查第 二个值
·非布尔值时:如果两个都为true,则返回第二个值,如果两个值 中有false,则返回靠前的false的值
|| 或:||可以对符号两侧的值进行或运算并返回结果,运算规则如下:
·两个值中只要有一个true,就返回true,只有两个值都为false, 才会返回false
·JS中的“或”属于短路的或,如果第一个值为true,则不会检查第 二个值
·非布尔值时:如果两个都为false ,则返回第二个值,如果两个 值中有true,则返回靠前的true的值
! 非:!可以用来对一个值进行非运算,所谓非运算就是对一个布尔值 ·进行取反操作,true变false,false变true,运算规则如下:
如果对一个值进行两次取反,它不会变化
·非布尔值时:先会将其转换为布尔值,然后再取反,所以我们可 以利用该特点,来将一个其它的数据类型转换为布尔值,可以为 一个任意数据类型取两次反,来将其转换为布尔值,原理和 Boolean()函数一样

(5)比较运算符

比较运算符用来比较两个值是否相等,如果相等会返回true,否则返回false。

使用 == 来做相等运算
当使用来比较两个值时,如果值的类型不同,则会自动进行类 型转换,将其转换为相同的类型,然后在比较
使用 != 来做不相等运算
不相等用来判断两个值是否不相等,如果不相等返回true,否则 返回false,不相等也会对变量进行自动的类型转换,如果转换后 相等它也会返回false
使用 === 来做全等运算
用来判断两个值是否全等,它和相等类似,不同的是它不会做自 动的类型转换,如果两个值的类型不同,直接返回false
使用 !
来做不全等运算
用来判断两个值是否不全等,它和不等类似,不同的是它不会做 自动的类型转换,如果两个值的类型不同,直接返回true

(6)条件运算符

JavaScript 还包含了基于某些条件对变量进行赋值的条件运算符。

语法:variablename=(condition)?value1:value2;

举例:result=(age<18)?“年龄太小”:“年龄合适”;

执行流程:如果condition为true,则执行语句1,并返回执行结果,如果为false,则执行语句2,并返回执行结果。

(7)逗号运算符

使用逗号可以在一条语句中执行多次操作。

比如:var num1=1, num2=2, num3=3;

使用逗号运算符分隔的语句会从左到右顺 序依次执行。

6、运算符优先级

运算符优先级由上到下依次减小,对于同级运算符,采用从左向右依次执行的方法。

image-20201013115557984

7、代码块

(1)语句

前边我所说表达式和运算符等内容可以理解成是我们一 门语言中的单词,短语。而语句(statement)就是我们这个语言中一句一句完 整的话了。语句是一个程序的基本单位,JavaScript的程序就是由一条一条语句构成的,每一条语句使用;结尾。

JavaScript中的语句默认是由上至下顺序执行的,但是我们也可以通过一些流程控制语句来控制语句的执行顺序。

(2)代码块

代码块是在大括号 {} 中所写的语句,以此将多条语句的集合视为一条语句来使用。

{
    var a = 123;
    a++;
    alert(a);
}

8、条件语句

条件语句是通过判断指定表达式的值来决定执行还是跳过某些语句,最基本的条件语句:

  • if…else
  • switch…case

(1)if…else

if…else语句是一种最基本的控制语句,它让JavaScript可以有条件的执行语句。

第一种形式:

if(expression)
    statement

var age = 16;
if (age < 18) {
    console.log("未成年");
}

第二种形式:

if(expression)
    statement
else
    statement

var age = 16;
if (age < 18) {
    console.log("未成年");
} else {
    console.log("已成年");
}

第三种形式:

if(expression1)
    statement
else if(expression2)
    statement
else
    statement    

var age = 18;
if (age < 18) {
    console.log("小于18岁了");
} else if (age == 18) {
    console.log("已经18岁了");
} else {
    console.log("大于18岁了")
}

(2)switch…case

switch…case是另一种流程控制语句。

switch语句更适用于多条分支使用同一条语句的情况。

语法格式:

switch (语句) {
    case 表达式1:
        语句...
    case 表达式2:
        语句...
    default:
        语句...
}

注意:需要注意的是一旦符合case的条件程序会一直运行到结束,所以我们一般会在case中添加break作为语句的结束。

9、循环语句

循环语句和条件语句一样,也是基本的控制语句,只要满足一定的条件将会一直执行,最基本的循环语句:

  • while
  • do…while
  • for

(1)while

while语句是一个最基本的循环语句,while语句也被称为while循环。

语法格式:

while(条件表达式){
    语句...
}

(2)do…while

do…while和while非常类似,只不过它会在循环的尾部而不是顶部检查表达式的值,因此,do…while循环会至少执行一次。相比于while,do…while的使用情况并不 是很多。

语法格式:

do{
    语句...
}while(条件表达式);

(3)for

for语句也是循环控制语句,我们也称它为for循环。大部分循环都会有一个计数器用以控制循环执行的次数, 计数器的三个关键操作是初始化、检测和更新。for语句 就将这三步操作明确为了语法的一部分。

语法格式:

for(初始化表达式 ; 条件表达式 ; 更新表达式){
    语句...
}

(4)跳转语句

  • break:结束最近的一次循环,可以在循环和switch语句中使用。
  • continue:结束本次循环,执行下一次循环,只能在循环中使用。

10、对象基础

(1)创建对象

第一种:

var person = new Object();
person.name = "孙悟空";
person.age = 18;
console.log(person);

第二种:

var person = {
    name: "孙悟空",
    age: 18
};
console.log(person);

(2)访问属性

第一种方式:使用 . 来访问

对象.属性名
j

第二种方式:使用 [] 来访问

对象[‘属性名’]

(3)删除属性

delete 对象.属性名

(4)遍历对象

枚举遍历对象中的属性,可以使用for … in语句循环,对象中有几个属性,循环体就会执行几次。

for (var 变量 in 对象) {

}

11、函数

(1)函数创建

使用 函数对象 来创建一个函数(几乎不用)

var 函数名 = new Function("执行语句");

使用 函数声明 来创建一个函数(比较常用)

function 函数名([形参1,形参2,...,形参N]) {
    语句...
}

使用 函数表达式 来创建一个函数(比较常用)

var 函数名  = function([形参1,形参2,...,形参N]) {
    语句....
}

(2)调用函数

对于无参函数调用:

// 函数声明
var fun = function () {
    console.log("哈哈,我执行啦!");
}

// 函数调用
fun();

对于有参函数调用:

// 函数声明
var sum = function (num1, num2) {
    var result = num1 + num2;
    console.log("num1 + num2 = " + result);
}

// 函数调用
sum(10, 20);

(3)函数参数

  • JS中的所有的参数传递都是按值传递的,也就是说把函数外部的值赋值给函数内部的参数,就和把值从一个变量赋值给另一个变量是一样的,在调用函数时,可以在()中指定实参(实际参数),实参将会赋值给函数中对应的形参
  • 调用函数时,解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能,则需要对参数进行类型的检查,函数的实参可以是任意的数据类型
  • 调用函数时,解析器也不会检查实参的数量,多余实参不会被赋值,如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined

(4)函数返回值

可以使用 return 来设置函数的返回值,return后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。

注意:在函数中return后的语句都不会执行,如果return语句后不跟任何值就相当于返回一个undefined,如果函数中不写return,则也会返回undefined,return后可以跟任意类型的值

语法格式:

return

(5)嵌套函数

嵌套函数:在函数中声明的函数就是嵌套函数,嵌套函数只能在当前函数中可以访问,在当前函数外无法访问。

演示:

function fu() {
    function zi() {
        console.log("我是儿子")
    }

    zi();
}

fu();

(6)匿名函数

匿名函数:没有名字的函数就是匿名函数,它可以让一个变量来接收,也就是用 “函数表达式” 方式创建和接收。

演示:

var fun = function () {
    alert("我是一个匿名函数");
}

fun();

(7)立即执行函数

立即执行函数:函数定义完,立即被调用,这种函数叫做立即执行函数,立即执行函数往往只会执行一次。

演示

(function () {
    alert("我是一个匿名函数");
})();

(8)对象中的函数

对象的属性值可以是任何的数据类型,也可以是个函数。

如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法,调用这个函数就说调用对象的方法(method)。

注意:方法和函数只是名称上的区别,没有其它别的区别

演示:

var person = {
    name: "zhangsan",
    age: 18,
    sayHello: function () {
        console.log(name + " hello")
    }
}

person.sayHello();

(9)this对象

解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的上下文对象,根据函数的调用方式的不同,this会指向不同的对象

  • 以函数的形式调用时,this永远都是window
  • 以方法的形式调用时,this就是调用方法的那个对象

演示:

//创建一个全局变量name
var name = "全局变量name";

//创建一个函数
function fun() {
    console.log(this.name);
}

//创建一个对象
var obj = {
    name: "孙悟空",
    sayName: fun
};

//我们希望调用obj.sayName()时可以输出obj的名字而不是全局变量name的名字
obj.sayName();

12、对象进阶

(1)用工厂方法创建对象

// 使用工厂模式创建对象
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);
}

(2)用构造函数创建对象

构造函数:构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写,构造函数和普通函数的还有一个区别就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。

  1. 调用构造函数,它会立刻创建一个新的对象
  2. 将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
  3. 逐行执行函数中的代码
  4. 将新建的对象作为返回值返回

构造函数有点类似工厂方法,但是它创建对象和返回对象都给我们隐藏了,使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象,称为是该类的实例。

  • 当以函数的形式调用时,this是window
  • 当以方法的形式调用时,谁调用方法this就是谁
  • 当以构造函数的形式调用时,this就是新创建的那个对象

我们可以使用 instanceof 运算符检查一个对象是否是一个类的实例,它返回true或false

语法格式:

对象 instanceof 构造函数

案例演示:

console.log(person1 instanceof Person);

(3)原型

​ 创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象,即显式原型,原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

	如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__(隐式原型)来访问该属性。当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。

​ 以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。

(4)原型链

访问一个对象的属性时,先在自身属性中查找,找到返回, 如果没有,再沿着__proto__这条链向上查找,找到返回,如果最终没找到,返回undefined,这就是原型链,又称隐式原型链,它的作用就是查找对象的属性(方法)。

image-20201016092628653

注意:Object对象是所有对象的祖宗,Object的原型对象指向为null,也就是没有原型对象

(5)toString 方法

toString()函数用于将当前对象以字符串的形式返回。该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法,所有主流浏览器均支持该函数。

// 使用构造函数来创建对象
function Person(name, age) {
    // 设置对象的属性
    this.name = name;
    this.age = age;
}

//创建对象的一个实例对象
var p = new Person("张三", 20);
console.log(p.toString());

(6)hasOwnProperty方法

(7)对象继承

面向对象的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是在JavaScript中没有类的概念,前边我们说所的类只是我们自己这么叫,大家要清楚。因此它的对象也与基于类的对象有所不同。实际上,JavaScript语言是通过一种叫做原型(prototype)的方式来实现面向对象编程的。

那实现继承有一个最大的好处就是子对象可以使用父对象的属性和方法,从而简化了一些代码。

JavaScript有六种非常经典的对象继承方式,但是我们只学习前三种:
原型链继承

借用函数构造继承

组合继承

​ 原型式继承

​ 寄生式继承

​ 寄生组合式继承

7.1、原型链继承

核心思想: 子类型的原型为父类型的一个实例对象

基本做法:

  1. 定义父类型构造函数
  2. 给父类型的原型添加方法
  3. 定义子类型的构造函数
  4. 创建父类型的对象赋值给子类型的原型
  5. 将子类型原型的构造属性设置为子类型
  6. 给子类型原型添加方法
  7. 创建子类型的对象: 可以调用父类型的方法

案例演示:

// 定义父类型构造函数
function SupperType() {
    this.supProp = 'Supper property';
}

// 给父类型的原型添加方法
SupperType.prototype.showSupperProp = function () {
    console.log(this.supProp);
};

// 定义子类型的构造函数
function SubType() {
    this.subProp = 'Sub property';
}

// 创建父类型的对象赋值给子类型的原型
SubType.prototype = new SupperType();

// 将子类型原型的构造属性设置为子类型
SubType.prototype.constructor = SubType;

// 给子类型原型添加方法
SubType.prototype.showSubProp = function () {
    console.log(this.subProp)
};

// 创建子类型的对象: 可以调用父类型的方法
var subType = new SubType();
subType.showSupperProp();
subType.showSubProp();

缺点描述:

  1. 原型链继承多个实例的引用类型属性指向相同,一个实例修改了原型属性,另一个实例的原型属性也会被修改
  2. 不能传递参数
  3. 继承单一

7.2、借用构造函数继承

核心思想: 使用.call()和.apply()将父类构造函数引入子类函数,使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类

基本做法:

  1. 定义父类型构造函数
  2. 定义子类型的构造函数
  3. 给子类型的原型添加方法
  4. 创建子类型的对象然后调用

案例演示:

借用构造函数继承的重点就在于SuperType**.call(this, name)**,调用了SuperType构造函数,这样,SubType的每个实例都会将SuperType中的属性复制一份。

// 定义父类型构造函数
function SuperType(name) {
    this.name = name;
    this.showSupperName = function () {
        console.log(this.name);
    };
}

// 定义子类型的构造函数
function SubType(name, age) {
    // 在子类型中调用call方法继承自SuperType
    SuperType.call(this, name);
    this.age = age;
}

// 给子类型的原型添加方法
SubType.prototype.showSubName = function () {
    console.log(this.name);
};

// 创建子类型的对象然后调用
var subType = new SubType("孙悟空", 20);
subType.showSupperName();
subType.showSubName();
console.log(subType.name);
console.log(subType.age);

缺点描述:

  1. 只能继承父类的实例属性和方法,不能继承原型属性和方法
  2. 无法实现构造函数的复用,每个子类都有父类实例函数的副本,影响性能,代码会臃肿

7.3、组合继承

核心思想: 原型链+借用构造函数的组合继承

基本做法:

  1. 利用原型链实现对父类型对象的方法继承
  2. 利用super()借用父类型构建函数初始化相同属性

案例演示:

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.setName = function (name) {
    this.name = name;
};

function Student(name, age, price) {
    Person.call(this, name, age); // 为了得到父类型的实例属性和方法
    this.price = price; // 添加子类型私有的属性
}

Student.prototype = new Person(); // 为了得到父类型的原型属性和方法
Student.prototype.constructor = Student; // 修正constructor属性指向
Student.prototype.setPrice = function (price) { // 添加子类型私有的方法 
    this.price = price;
};

var s = new Student("孙悟空", 24, 15000);
console.log(s.name, s.age, s.price);
s.setName("猪八戒");
s.setPrice(16000);
console.log(s.name, s.age, s.price);

缺点描述:

  1. 父类中的实例属性和方法既存在于子类的实例中,又存在于子类的原型中,不过仅是内存占用,因此,在使用子类创建实例对象时,其原型中会存在两份相同的属性和方法

注意:这个方法是JavaScript中最常用的继承模式

(8)垃圾回收

垃圾回收(GC):就像人生活的时间长了会产生垃圾一样,程序运行过程中也会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢,所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾。

当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。

在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;

13、作用域

作用域指一个变量的作用的范围,在JS中一共有两种作用域:

  • 全局作用域
  • 函数作用域

(1)声明提前

  • 变量的声明提前:使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值),但是如果声明变量时不使用var关键字,则变量不会被声明提前
  • 函数的声明提前:使用函数声明形式创建的函数 function 函数名(){} ,它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数。使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用

(2)作用域

2.1、全局作用域
  • 直接编写在script标签中的JavaScript代码,都在全局作用域
  • 全局作用域在页面打开时创建,在页面关闭时销毁
  • 在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建,我们可以直接使用
  • 在全局作用域中:
    • 创建的变量都会作为window对象的属性保存
    • 创建的函数都会作为window对象的方法保存
  • 全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到
2.2、函数作用域
  • 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
  • 每调用一次函数就会创建一个新的函数作用域,它们之间是互相独立的
  • 在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
  • 在函数中要访问全局变量可以使用window对象
  • 作用域链:当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有则向上一级作用域中寻找,直到找到全局作用域,如果全局作用域中依然没有找到,则会报错ReferenceError

(3)作用域链

多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外),查找变量时就是沿着作用域链来查找的。

查找一个变量的查找规则:

  1. 在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2
  2. 在上一级作用域的执行上下文中查找对应的属性,如果有直接返回,否则进入3
  3. 再次执行2的相同操作,直到全局作用域,如果还找不到就抛出找不到的ReferenceError异常

六、JavaScript常用对象

1、数组对象

(1)创建数组

1.1、使用对象创建

同类型有序数组创建:

var arr = new Array();
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
arr[5] = 6;
arr[6] = 7;
arr[7] = 8;
arr[8] = 9;

不同类型有序数组创建:

var arr = new Array();
arr[0] = 1;
arr[1] = "2";
arr[2] = 3;
arr[3] = "4";
arr[4] = 5;
arr[5] = "6";
arr[6] = 7;
arr[7] = "8";
arr[8] = 9;

1.2、使用字面量创建

同类型有序数组创建:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

不同类型有序数组创建:

var arr = [1, "2", 3, "4", 5, "6", 7, "8", 9];

(2)遍历数组

for (var i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

(3)数组属性

constructor属性演示:返回创建数组对象的原型函数

var arr = [1,2,3,4];
console.log(arr.constructor);

length属性演示:设置或返回数组元素的个数

var arr = [1,2,3,4];
console.log(arr.length);

(4)数组方法

push()方法演示:该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度

var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.push("唐僧", "蜘蛛精", "白骨精", "玉兔精");
console.log(arr);
console.log(result);

pop()方法演示:该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回

var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.pop();
console.log(arr);
console.log(result);

unshift()方法演示:该方法向数组开头添加一个或多个元素,并返回新的数组长度

var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.unshift("牛魔王", "二郎神");
console.log(arr);
console.log(result);

shift()方法演示:该方法可以删除数组的第一个元素,并将被删除的元素作为返回值返回

var arr = ["孙悟空", "猪八戒", "沙和尚"];
var result = arr.shift();
console.log(arr);
console.log(result);

forEach()方法演示:该方法可以用来遍历数组

forEach()方法需要一个函数作为参数,像这种函数,由我们创建但是不由我们调用的,我们称为回调函数。数组中有几个元素函数就会执行几次,每次执行时,浏览器会将遍历到的元素,以实参的形式传递进来,我们可以来定义形参,来读取这些内容,浏览器会在回调函数中传递三个参数:

第一个参数:就是当前正在遍历的元素
第二个参数:就是当前正在遍历的元素的索引
第三个参数:就是正在遍历的数组

注意:这个方法只支持IE8以上的浏览器,IE8及以下的浏览器均不支持该方法,所以如果需要兼容IE8,则不要使用forEach(),还是使用for循环来遍历数组。

slice()方法演示:该方法可以用来从数组提取指定元素,该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回

参数:

第一个参数:截取开始的位置的索引,包含开始索引
第二个参数:截取结束的位置的索引,不包含结束索引,第二个参数可以省略 不写,此时会截取从开始索引往后的所有元素

注意:索引可以传递一个负值,如果传递一个负值,则从后往前计算,-1代表倒数第一个,-2代表倒数第二个。

splice()方法演示:该方法可以用于删除数组中的指定元素,该方法会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回

参数:

第一个参数:表示开始位置的索引
第二个参数:表示要删除的元素数量
第三个参数及以后参数:可以传递一些新的元素,这些元素将会自动插入到开 始位置索引前边

concat()方法演示:该方法可以连接两个或多个数组,并将新的数组返回,该方法不会对原数组产生影响

join()方法演示:该方法可以将数组转换为一个字符串,该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回,在join()中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符,如果不指定连接符,则默认使用,作为连接符

reverse()方法演示:该方法用来反转数组(前边的去后边,后边的去前边),该方法会直接修改原数组

sort()方法演示:该方法可以用来对数组中的元素进行排序,也会影响原数组,默认会按照Unicode编码进行排序

注意:即使对于纯数字的数组,使用sort()排序时,也会按照Unicode编码来排序,所以对数字进排序时,可能会得到错误的结果。

2、 函数对象

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的指向,然后从第二个参数开始,需要制定一个实参数组进行参数传递

2.2、this指向

  • 以函数形式调用时,this永远都是window
  • 以方法的形式调用时,this是调用方法的对象
  • 以构造函数的形式调用时,this是新创建的那个对象
  • 使用call和apply调用时,this是传入的那个指定对象

2.3、arguments参数

在调用函数时,浏览器每次都会传递进两个隐含的参数:

  1. 函数的上下文对象: this
  2. 封装实参的对象: arguments

arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度,在调用函数时,我们所传递的实参都会在arguments中保存,比如:arguments.length 可以用来获取实参的长度,我们即使不定义形参,也可以通过arguments来使用实参,只不过比较麻烦,例如:

  • arguments[0]:表示第一个实参
  • arguments[1]:表示第二个实参

它里边有一个属性叫做callee,这个属性对应一个函数对象,就是当前正在指向的函数的对象。

arguments对象演示:

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");

3、Date对象

在JavaScript中使用Date对象来表示一个时间,如果直接使用构造函数创建一个Date对象,则会封装为当前代码执行的时间。

案例演示:

var date = new Date();
console.log(date);

console.log(date.getFullYear());//获取当前日期对象的年份(四位数字年份)
console.log(date.getMonth());//获取当前日期对象的月份(0 ~ 11)
console.log(date.getDate());//获取当前日期对象的日数(1 ~ 31)
console.log(date.getHours());//获取当前日期对象的小时(0 ~ 23)
console.log(date.getMinutes());//获取当前日期对象的分钟(0 ~ 59)
console.log(date.getSeconds());//获取当前日期对象的秒钟(0 ~ 59)
console.log(date.getMilliseconds());//获取当前日期对象的毫秒(0 ~ 999)

4、Math对象

Math和其它的对象不同,它不是一个构造函数,它属于一个工具类不用创建对象,它里边封装了数学运算相关的属性和方法。

案例演示:

/*固定值*/
console.log("PI = " + Math.PI);
console.log("E  = " + Math.E);
console.log("===============");
/*正数*/
console.log(Math.abs(1));        //可以用来计算一个数的绝对值
console.log(Math.ceil(1.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));  //可以对一个数进行向下取整,小数位只有有值就自动进1
console.log(Math.round(-1.4));   //可以对一个数进行四舍五入取整
console.log("===============");
/*随机数*/
//Math.random():可以用来生成一个0-1之间的随机数
//生成一个0-x之间的随机数:Math.round(Math.random()*x)
//生成一个x-y之间的随机数:Math.round(Math.random()*(y-x)+x)
console.log(Math.round(Math.random() * 10));            //生成一个0-10之间的随机数
console.log(Math.round(Math.random() * (10 - 1) + 1));  //生成一个1-10之间的随机数
console.log("===============");
/*数学运算*/
console.log(Math.pow(12, 3));   //Math.pow(x,y):返回x的y次幂
console.log(Math.sqrt(4));      //Math.sqrt(x) :返回x的平方根

5、String对象

5.1、概述

在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象

  • String():可以将基本数据类型字符串转换为String对象
  • Number():可以将基本数据类型的数字转换为Number对象
  • Boolean():可以将基本数据类型的布尔值转换为Boolean对象
5.2、字符串属性

constructor属性演示:返回创建字符串对象的原型函数

var str = "Hello,World!";
console.log(str.constructor);

length属性演示:可以用来获取字符串的长度

var str = "Hello,World!";
console.log(str.length);

5.3、字符串方法

charAt()方法演示:该方法可以根据索引获取指定位置的字符

var str = "Hello,World!";
console.log(str.charAt(1));

charCodeAt()方法演示:该方法获取指定位置字符的字符编码(Unicode编码)

var str = "Hello,World!";
console.log(str.charCodeAt(1));

concat()方法演示:该方法可以用来连接两个或多个字符串

var str = "Hello,World!";
console.log(str.concat("你好,", "世界!"));

indexof()方法演示:该方法可以检索一个字符串中是否含有指定内容,如果字符串中含有该内容,则会返回其第一次出现的索引,如果没有找到指定的内容,则返回-1,可以指定一个第二个参数,指定开始查找的位置

var str = "Hello,World!";
console.log(str.indexOf("o"));
console.log(str.indexOf("o", 5));

lastIndexOf()方法演示:该方法的用法和indexOf()一样,不同的是indexOf是从前往后找,而lastIndexOf是从后往前找,也可以指定开始查找的位置

var str = "Hello,World!";
console.log(str.lastIndexOf("o"));
console.log(str.lastIndexOf("o", 5));

slice()方法演示:可以从字符串中截取指定的内容,不会影响原字符串,而是将截取到内容返回

参数:

  • 第一个参数:开始位置的索引(包括开始位置)
  • 第二个参数:结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的
注意:也可以传递一个负数作为参数,负数的话将会从后边计算
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);

substring()方法演示:可以用来截取一个字符串,它和slice()类似

参数:

  • 第一个参数:开始截取位置的索引(包括开始位置)
  • 第二个参数:结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的
注意:不同的是这个方法不能接受负值作为参数,如果传递了一个负值,则默认使用0,而且它还自动调整参数的位置,如果第二个参数小于第一个,则自动交换
var str = "Hello,World!";
var result = str.substring(1, 4);
console.log(result);
result = str.substring(1);
console.log(result);
result = str.substring(1, -1);
console.log(result);

substr()方法演示:该方法用来截取字符串

参数:

  • 第一个参数:截取开始位置的索引
  • 第二个参数:截取的长度
var str = "Hello,World!";
var result = str.substr(6, 6);
console.log(result);

split()方法演示:该方法可以将一个字符串拆分为一个数组,需要一个字符串作为参数,将会根据该字符串去拆分数组

var str = "Hello,World!";
var result = str.split(",");
console.log(result);

toUpperCase()方法演示:将一个字符串转换为大写并返回

var str = "Hello,World!";
var result = str.toUpperCase();
console.log(result);

toLowerCase()方法演示:将一个字符串转换为小写并返回

var str = "Hello,World!";
var result = str.toLowerCase();
console.log(result);

6、RegExp对象

6.1、创建正则对象

6.1.1、使用对象创建

语法格式:

var 变量名 = new RegExp("正则表达式","匹配模式");

匹配模式:

  • i:忽略大小写
  • g:全局匹配模式
  • ig:忽略大小写且全局匹配模式

案例演示:

// 这个正则表达式可以来检查一个字符串中是否含有a
var reg = new RegExp("ab", "i");
var str = "Abc";
var result = reg.test(str);
console.log(result);

6.1.2、使用字面量创建

语法格式:

var 变量名 = /正则表达式/匹配模式;

匹配模式:

  • i:忽略大小写
  • g:全局匹配模式
  • m:执行多行匹配
注意:可以为一个正则表达式设置多个匹配模式,且顺序无所谓

案例演示:

// 这个正则表达式可以来检查一个字符串中是否含有a
var reg = /a/i;
var str = "Abc";
var result = reg.test(str);
console.log(result);

6.2、正则进阶

需求信息:创建一个正则表达式,检查一个字符串中是否有a或b

语法格式:使用 | 表示或者的意思

// 这个正则表达式可以来检查一个字符串中是否含有a
var reg = /a|b|c/;
var str = "Abc";
var result = reg.test(str);
console.log(result);

需求信息:创建一个正则表达式,检查一个字符串中是否有字母

语法格式:[ ] 里的内容也是或的关系

// 这个正则表达式可以来检查一个字符串中是否含有字母
var reg = /[A-z]/;
var str = "Abc";
var result = reg.test(str);
console.log(result);

常见组合:

  • [a-z]:任意小写字母
  • [A-Z]:任意大写字母
  • [A-z]:任意字母
  • [0-9]:任意数字

需求信息:创建一个正则表达式,检查一个字符串中是否含有 abc 或 adc 或 aec

// 这个正则表达式可以来检查一个字符串中是否含有abc或adc或aec
var reg = /a[bde]c/;
var str = "abc123";
var result = reg.test(str);
console.log(result);

常见组合:

  • [^a-z]:除了任意小写字母
  • [^A-Z]:除了任意大写字母
  • [^A-z]:除了任意字母
  • [^0-9]:除了任意数字

需求信息:创建一个正则表达式,检查一个字符串中是否除了数字还有其它字母

// 这个正则表达式可以来检查一个字符串中是否除了数字还有其它字母
var reg = /[^0-9]/;
var str = "0123456789";
var result = reg.test(str);
console.log(result);

6.3、正则方法

这些正则方法其实都是字符串的方法,但是它的参数需要传递正则表达式,在这里,我就先称为正则方法。

split()方法演示:该方法可以将一个字符串拆分为一个数组,方法中可以传递一个正则表达式作为参数,这样方法将会根据正则表达式去拆分字符串,这个方法即使不指定全局匹配,也会全都插分

var str = "1a2b3c4d5e6f7";
var result = str.split(/[A-z]/);
console.log(result);

search()方法演示:该方法可以搜索字符串中是否含有指定内容,如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回-1,它可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串,serach()只会查找第一个,即使设置全局匹配也没用

var str = "hello abc hello aec afc";
var result = str.search(/a[bef]c/);
console.log(result);

match()方法演示:该方法可以根据正则表达式,从一个字符串中将符合条件的内容提取出来,默认情况下我们的match()只会找到第一个符合要求的内容,找到以后就停止检索,我们可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容,可以为一个正则表达式设置多个匹配模式,且顺序无所谓,match()会将匹配到的内容封装到一个数组中返回,即使只查询到一个结果

var str = "1a2a3a4a5e6f7A8B9C";
var result = str.match(/[a-z]/ig);
console.log(result);

replace()方法演示:该方法可以将字符串中指定内容替换为新的内容,默认只会替换第一个,但是可以设置全局匹配替换全部

参数:

  • 第一个参数:被替换的内容,可以接受一个正则表达式作为参数
  • 第二个参数:新的内容
var str = "1a2a3a4a5e6f7A8B9C";
var result = str.replace(/[a-z]/gi, "@_@");
console.log(result);

6.4、正则量词

通过量词可以设置一个内容出现的次数,量词只对它前边的一个内容起作用,如果有多个内容可以使用 () 括起来,常见量词如下:

  • {n} :正好出现n次
  • {m,} :出现m次及以上
  • {m,n} :出现m-n次
  • + :至少一个,相当于{1,}
  • * :0个或多个,相当于{0,}
  • ? :0个或1个,相当于{0,1}
var str = "abbc";

reg = /(ab){3}/;
console.log(reg.test(str));
console.log("===============");
reg = /b{3}/;
console.log(reg.test(str));
console.log("===============");
reg = /ab{1,3}c/;
console.log(reg.test(str));
console.log("===============");
reg = /ab{3,}c/;
console.log(reg.test(str));
console.log("===============");
reg = /ab+c/;
console.log(reg.test(str));
console.log("===============");
reg = /ab*c/;
console.log(reg.test(str));
console.log("===============");
reg = /ab?c/;
console.log(reg.test(str));
console.log("===============");

6.5、正则高阶

如果我们要检查或者说判断是否以某个字符或者字符序列开头或者结尾就会使用^$

  • ^ :表示开头,注意它在[^字符序列]表达的意思不一样
  • $ :表示结尾

需求描述:检查一个字符串中是否以a开头

var str = "abcabca";
var reg = /^a/;
console.log(reg.test(str));

需求描述:检查一个字符串中是否以a结尾

var str = "abcabca";
var reg = /a$/;
console.log(reg.test(str));

​ 想要检查一个字符串中是否含有.\就会使用转义字符

  • \. :表示.
  • \\ :表示\

注意:使用构造函数时,由于它的参数是一个字符串,而\是字符串中转义字符,如果要使用\则需要使用\\来代替

var reg1 = /\./;
var reg2 = /\\/;
var reg3 = new RegExp("\\.");
var reg4 = new RegExp("\\\\");

除了以上两种特殊的字符,其实还有很多如下所示:

\w :任意字母、数字、,相当于[A-z0-9]
\W :除了字母、数字、,相当于[^A-z0-9]
\d :任意的数字,相当于[0-9]
\D :除了任意的数字,相当于[^0-9]
\s :空格
\S :除了空格
\b :单词边界
\B :除了单词边界

需求描述:创建一个正则表达式,去除掉字符串中的前后的空格

var str = "  hello child  "
var reg = /^\s*|\s*$/g;
console.log(str);
str = str.replace(reg, "");
console.log(str);

需求描述:创建一个正则表达式,检查一个字符串中是否含有单词child

var str = "hello child"
var reg = /\bchild\b/;
console.log(reg.test(str));

七、JavaScript DOM

7.1、DOM概述

当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。

HTML DOM 模型被结构化为 对象树

image-20201019104459658

通过这个对象模型,JavaScript 获得创建动态 HTML 的所有力量:

  • JavaScript 能改变页面中的所有 HTML 元素
  • JavaScript 能改变页面中的所有 HTML 属性
  • JavaScript 能改变页面中的所有 CSS 样式
  • JavaScript 能删除已有的 HTML 元素和属性
  • JavaScript 能添加新的 HTML 元素和属性
  • JavaScript 能对页面中所有已有的 HTML 事件作出反应
  • JavaScript 能在页面中创建新的 HTML 事件

换言之:HTML DOM 是关于如何获取、更改、添加或删除 HTML 元素的标准。

7.2、DOM文档节点

节点Node,是构成我们网页的最基本的组成部分,网页中的每一个部分都可以称为是一个节点。

比如:html标签、属性、文本、注释、整个文档等都是一个节点。

虽然都是节点,但是实际上它们的具体类型是不同的。

比如:标签我们称为元素节点、属性称为属性节点、文本称为 文本节点、文档称为文档节点。

节点的类型不同,属性和方法也都不尽相同。

节点:Node——构成HTML文档最基本的单元。

常用节点分为四类:

  • 文档节点:整个HTML文档

  • 元素节点:HTML文档中的HTML标签

  • 属性节点:元素的属性

  • 文本节点:HTML标签中的文本内容

    image-20201019115515595

节点属性:image-20201019115655478

文档节点:

文档节点(Document)代表的是整个HTML文 档,网页中的所有节点都是它的子节点。

document对象作为window对象的属性存在的,我们不用获取可以直接使用。

通过该对象我们可以在整个文档访问内查找节点对象,并可以通过该对象创建各种节点对象。

元素节点:

HTML中的各种标签都是元素节点(Element),这也是我们最常用的一个节点。

浏览器会将页面中所有的标签都转换为一个元素节点, 我们可以通过document的方法来获取元素节点。

例如:document.getElementById(),根据id属性值获取一个元素节点对象。

属性节点:

属性节点(Attribute)表示的是标签中的一个一个的属 性,这里要注意的是属性节点并非是元素节点的子节点,而是元素节点的一部分。可以通过元素节点来获取指定的属性节点。

例如:元素节点.getAttributeNode(“属性名”),根据元素节点的属性名获取一个属性节点对象。

注意:我们一般不使用属性节点。

文本节点:

文本节点(Text)表示的是HTML标签以外的文本内容,任意非HTML的文本都是文本节点,它包括可以字面解释的纯文本内容。文本节点一般是作为元素节点的子节点存在的。获取文本节点时,一般先要获取元素节点,在通过元素节点获取文本节点。

例如:元素节点.firstChild;,获取元素节点的第一个子节点,一般为文本节点。

7.3、DOM文档操作

7.3.1、查找HTML元素

方法介绍:

方法 描述
document.getElementById(id) 通过元素 id 来查找元素。
document.getElementsByTagName(name) 通过标签名来查找元素。
document.getElementsByClassName(name) 通过类名来查找元素。
document.querySelector(CSS选择器) 通过CSS选择器选择一个元素。
document.querySelectorAll(CSS选择器) 通过CSS选择器选择多个元素。

方法演示:

需求描述:创建一个按钮,通过id获取按钮节点对象

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<button id="btn">我是按钮</button>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var btn = document.getElementById("btn");
    console.log(btn);
</script>
</body>
</html>

需求描述:创建一个按钮,通过标签名获取按钮节点对象数组

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<button>我是按钮</button>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var btn = document.getElementsByTagName("button");
    console.log(btn);
</script>
</body>
</html>

需求描述:创建一个按钮,通过类名获取按钮节点对象数组

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<button class="btn">我是按钮</button>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var btn = document.getElementsByClassName("btn");
    console.log(btn);
</script>
</body>
</html>

求描述:创建一个按钮,通过CSS选择器选择该按钮

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<button class="btn">我是按钮</button>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var btn = document.querySelector(".btn");
    console.log(btn);
</script>
</body>
</html>

需求描述:创建一个无序列表,通过CSS选择器选择该列表的所有li

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<ul class="list">
    <li>列表项1</li>
    <li>列表项2</li>
    <li>列表项3</li>
    <li>列表项4</li>
</ul>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var list = document.querySelectorAll(".list li");
    console.log(list);
</script>
</body>
</html>

7.3.2、获取HTML的值

方法介绍

方法 描述
元素节点.innerTex 获取 HTML 元素的 inner Text。
元素节点.innerHTML 获取 HTML 元素的 inner HTML。
元素节点.属性 获取 HTML 元素的属性值。
元素节点.getAttribute(attribute) 获取 HTML 元素的属性值。
元素节点.style.样式 获取 HTML 元素的行内样式值。

方法演示:

需求描述:创建一个按钮,然后获取按钮的文本内容

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<button id="btn">我是按钮</button>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var btn = document.getElementById("btn");
    console.log(btn.innerText);
</script>
</body>
</html>

需求描述:创建一个div,然后在div中插入一个h1标题,获取div中的html代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="box">
    <h1>我是Box中的大标题</h1>
</div>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var box = document.getElementById("box");
    console.log(box.innerHTML);
</script>
</body>
</html>

需求描述:创建一个超链接,默认为空,设置href属性为https://www.baidu.com ,使用JavaScript代码读取href属性

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<a id="a" href="https://www.baidu.com">打开百度,你就知道!</a>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var a = document.getElementById("a");
    console.log(a.href);
</script>
</body>
</html>

需求描述:创建一个超链接,默认为空,设置href属性为https://www.baidu.com ,使用JavaScript代码读取href属性

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<a id="a" href="https://www.baidu.com">打开百度,你就知道!</a>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var a = document.getElementById("a");
    console.log(a.getAttribute("href"));
</script>
</body>
</html>

需求描述:创建一个正方形div,默认颜色为红色,使用JavaScript代码获取div的宽度

注意:如果CSS的样式名中含有-,这种名称在JS中是不合法的比如background-color,需要将这种样式名修改为驼峰命名法,去掉-,然后将-后的字母大写,我们通过style属性设置的样式都是行内样式,同样的获取也是行内样式,而行内样式有较高的优先级,所以通过JS修改的样式往往会立即显示,但是如果在样式中写了!important,则此时样式会有最高的优先级,即使通过JS也不能覆盖该样式,此时将会导致JS修改样式失效,所以尽量不要为样式添加!important
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div style="width: 100px;height: 100px;background: red;" id="box"></div>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var box = document.getElementById("box");
    console.log(box.style.width);
</script>
</body>
</html>

通过style属性设置和读取的都是内联样式,无法读取样式表中的样式或者说正在应用的样式,如果想要读取当前正在应用的样式属性我们可以使用元素.currentStyle.样式名来获取元素的当前显示的样式,它可以用来读取当前元素正在显示的样式,如果当前元素没有设置该样式,则获取它的默认值,但是currentStyle只有IE浏览器支持,其它的浏览器都不支持,在其它浏览器中可以使用getComputedStyle()这个方法来获取元素当前的样式,这个方法是window的方法,可以直接使用,但是需要两个参数:

第一个参数:要获取样式的元素
第二个参数:可以传递一个伪元素,一般都传null

7.3.3、改变HTML的值

方法 描述
元素节点.innerText = new text content 改变元素的 inner Text。
元素节点.innerHTML = new html content 改变元素的 inner HTML。
元素节点.属性 = new value 改变 HTML 元素的属性值。
元素节点.setAttribute(attribute, value) 改变 HTML 元素的属性值。
元素节点.style.样式 = new style 改变 HTML 元素的行内样式值。

7.3.4、修改HTML元素

方法 描述
document.createElement(element) 创建 HTML 元素节点。
document.createAttribute(attribute) 创建 HTML 属性节点。
document.createTextNode(text) 创建 HTML 文本节点。
元素节点.removeChild(element) 删除 HTML 元素。
元素节点.appendChild(element) 添加 HTML 元素。
元素节点.replaceChild(element) 替换 HTML 元素。
元素节点.insertBefore(element) 在指定的子节点前面插入新的子节点。

7.3.5、查找HTML父子

方法 描述
元素节点.parentNode 返回元素的父节点。
元素节点.parentElement 返回元素的父元素。
元素节点.childNodes 返回元素的一个子节点的数组(包含空白文本Text节点)。
元素节点.children 返回元素的一个子元素的集合(不包含空白文本Text节点)。
元素节点.firstChild 返回元素的第一个子节点(包含空白文本Text节点)。
元素节点.firstElementChild 返回元素的第一个子元素(不包含空白文本Text节点)。
元素节点.lastChild 返回元素的最后一个子节点(包含空白文本Text节点)。
元素节点.lastElementChild 返回元素的最后一个子元素(不包含空白文本Text节点)。
元素节点.previousSibling 返回某个元素紧接之前节点(包含空白文本Text节点)。
元素节点.previousElementSibling 返回指定元素的前一个兄弟元素(相同节点树层中的前一个元素节点)。
元素节点.nextSibling 返回某个元素紧接之后节点(包含空白文本Text节点)。
元素节点.nextElementSibling 返回指定元素的后一个兄弟元素(相同节点树层中的下一个元素节点)。

7.4、DOM文档时间

7.4.1、事件概述

HTML事件可以触发浏览器中的行为,比方说当用户点击某个 HTML 元素时启动一段 JavaScript。

7.4.2、窗口事件

由窗口触发该事件 (同样适用于 标签):

属性描述
onblur当窗口失去焦点时运行脚本。
onfocus当窗口获得焦点时运行脚本。
onload当文档加载之后运行脚本。
onresize当调整窗口大小时运行脚本。
onstorage当 Web Storage 区域更新时(存储空间中的数据发生变化时)运行脚本。
7.4.3、表单事件

表单事件在HTML表单中触发 (适用于所有 HTML 元素,但该HTML元素需在form表单内):

属性 描述
onblur 当元素失去焦点时运行脚本。
onfocus 当元素获得焦点时运行脚本。
onchange 当元素改变时运行脚本。
oninput 当元素获得用户输入时运行脚本。
oninvalid 当元素无效时运行脚本。
onselect 当选取元素时运行脚本。
onsubmit 当提交表单时运行脚本。

注意:这里为什么要用this,你不用this也可以,就直接textInput.style.background = "red";也不是不可以的,但是方法的调用规则就是谁调用this,this就指向谁,这样我们就可以简化代码了
7.4.4、键盘事件

通过键盘触发事件,类似用户的行为:

属性描述
onkeydown当按下按键时运行脚本。
onkeyup当松开按键时运行脚本。
onkeypress当按下并松开按键时运行脚本。
7.4.5、鼠标事件

通过鼠标触发事件,类似用户的行为:

属性 描述
onclick 当单击鼠标时运行脚本。
ondblclick 当双击鼠标时运行脚本。
onmousedown 当按下鼠标按钮时运行脚本。
onmouseup 当松开鼠标按钮时运行脚本。
onmousemove 当鼠标指针移动时运行脚本。
onmouseover 当鼠标指针移至元素之上时运行脚本,不可以阻止冒泡。
onmouseout 当鼠标指针移出元素时运行脚本,不可以阻止冒泡。
onmouseenter 当鼠标指针移至元素之上时运行脚本,可以阻止冒泡。
onmouseleave 当鼠标指针移出元素时运行脚本,可以阻止冒泡。
onmousewheel 当转动鼠标滚轮时运行脚本。
onscroll 当滚动元素的滚动条时运行脚本。

4.4.6、媒体事件

通过视频(videos),图像(images)或音频(audio) 触发该事件。

属性 描述
onabort 当发生中止事件时运行脚本。
oncanplay 当媒介能够开始播放但可能因缓冲而需要停止时运行脚本。
oncanplaythrough 当媒介能够无需因缓冲而停止即可播放至结尾时运行脚本。
ondurationchange 当媒介长度改变时运行脚本。
onemptied 当媒介资源元素突然为空时(网络错误、加载错误等)运行脚本。
onended 当媒介已抵达结尾时运行脚本。
onerror 当在元素加载期间发生错误时运行脚本。
onloadeddata 当加载媒介数据时运行脚本。
onloadedmetadata 当媒介元素的持续时间以及其它媒介数据已加载时运行脚本。
onloadstart 当浏览器开始加载媒介数据时运行脚本。
onpause 当媒介数据暂停时运行脚本。
onplay 当媒介数据将要开始播放时运行脚本。
onplaying 当媒介数据已开始播放时运行脚本。
onprogress 当浏览器正在取媒介数据时运行脚本。
onratechange 当媒介数据的播放速率改变时运行脚本。
onreadystatechange 当就绪状态(ready-state)改变时运行脚本。
onseeked 当媒介元素的定位属性不再为真且定位已结束时运行脚本。
onseeking 当媒介元素的定位属性为真且定位已开始时运行脚本。
onstalled 当取回媒介数据过程中(延迟)存在错误时运行脚本。
onsuspend 当浏览器已在取媒介数据但在取回整个媒介文件之前停止时运行脚本。
ontimeupdate 当媒介改变其播放位置时运行脚本。
onvolumechange 当媒介改变音量亦或当音量被设置为静音时运行脚本。
onwaiting 当媒介已停止播放但打算继续播放时运行脚本。

7.4.7、其它事件
属性描述
onshow 元素在上下文显示时触发。
ontoggle当用户打开或关闭 元素时触发。
7.4.8、事件冒泡

事件的冒泡(Bubble):所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发,在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡。

7.4.9、事件委派

我们希望只绑定一次事件,即可应用到多个的元素上,即使元素是后添加的,我们可以尝试将其绑定给元素的共同的祖先元素,也就是事件的委派。事件的委派,是指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。事件委派是利用了事件冒泡,通过委派可以减少事件绑定的次数,提高程序的性能。

7.4.10、事件绑定
7.4.10、事件绑定

八、JavaScript BOM

8.1、Window对象

8.1.1、弹出框

JavaScript 有三种类型的弹出框:警告框、确认框和提示框。

8.1.1.1、警告框

如果要确保信息传递给用户,通常会使用警告框。当警告框弹出时,用户将需要单击“确定”来继续。

语法

window.alert("sometext");

注意:window.alert() 方法可以不带 window 前缀来写。
8.1.1.2、确认框

如果您希望用户验证或接受某个东西,则通常使用“确认”框。

当确认框弹出时,用户将不得不单击“确定”或“取消”来继续进行。

如果用户单击“确定”,该框返回 true。如果用户单击“取消”,该框返回 false。

语法

window.confirm("sometext");

注意:window.confirm() 方法可以不带 window 前缀来编写。
8.1.1.3、提示框

如果您希望用户在进入页面前输入值,通常会使用提示框。

当提示框弹出时,用户将不得不输入值后单击“确定”或点击“取消”来继续进行。

如果用户单击“确定”,该框返回输入值。如果用户单击“取消”,该框返回 NULL。

语法

window.prompt("sometext","defaultText");

window.prompt() 方法可以不带 window 前缀来编写。

8.1.2、定时事件

JavaScript 可以在时间间隔内执行,这就是所谓的定时事件( Timing Events)。

window 对象允许以指定的时间间隔执行代码,这些时间间隔称为定时事件。

通过 JavaScript 使用的有两个关键的方法:

​ 1、 setTimeout(function, milliseconds)

​ 在等待指定的毫秒数后执行函数。

​ 2、setInterval(function, milliseconds)

​ 等同于 setTimeout(),但持续重复执行该函数。

setTimeout() 和 setInterval() 都属于 window 对象的方法。

1、延时器

setTimeout() 方法:延时器

window.setTimeout(function, milliseconds);

注意:window.setTimeout() 方法可以不带 window 前缀来编写。
  • 第一个参数是要执行的函数。
  • 第二个参数指示执行之前的毫秒数。
2、定时器

setInterval() 方法:定时器

setInterval() 方法在每个给定的时间间隔重复给定的函数。

window.setInterval(function, milliseconds);
注意:window.setInterval() 方法可以不带 window 前缀来写。
  • 第一个参数是要执行的函数。
  • 第二个参数每个执行之间的时间间隔的长度。

8.1.3、常用窗口属性

两个属性可用用于确定浏览器窗口的尺寸。

这两个属性都以像素返回尺寸:

​ window.innerHeight - 浏览器窗口的内高度(以像素计)
​ window.innerWidth - 浏览器窗口的内宽度(以像素计)
浏览器窗口(浏览器视口)不包括工具栏和滚动条。

对于 Internet Explorer 8, 7, 6, 5:

​ document.documentElement.clientHeight
​ document.documentElement.clientWidth

​ document.body.clientHeight
​ document.body.clientWidth
一个实用的 JavaScript 解决方案(包括所有浏览器):该例显示浏览器窗口的高度和宽度(不包括工具栏和滚动条)

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    var w = window.innerWidth
        || document.documentElement.clientWidth
        || document.body.clientWidth;

    var h = window.innerHeight
        || document.documentElement.clientHeight
        || document.body.clientHeight;

    console.log(w);
    console.log(h);
</script>
</body>
</html>

8.1.4、其他窗口方法

window.open() :打开新的窗口

语法介绍:

window.open(URL,name,specs,replace);
1

参数介绍:image-20201022094734710

window.close() :关闭当前窗口

window.close();

window.moveTo() :移动当前窗口

window.moveTo(x,y);

window.resizeTo() :调整当前窗口

window.resizeTo(width,height);

8.2、Navigator对象

注意:在IE11中已经将微软和IE相关的标识都已经去除了,所以我们基本已经不能通过UserAgent来识别一个浏览器是否是IE了,如果通过UserAgent不能判断,还可以通过一些浏览器中特有的对象,来判断浏览器的信息,比如:ActiveXObject

8.3、Location对象

8.3.1、常用属性

常用属性:

console.log(location);          //输出location对象
console.log(location.href);     //输出当前地址的全路径地址
console.log(location.origin);   //输出当前地址的来源
console.log(location.protocol); //输出当前地址的协议
console.log(location.hostname); //输出当前地址的主机名
console.log(location.host);     //输出当前地址的主机
console.log(location.port);     //输出当前地址的端口号
console.log(location.pathname); //输出当前地址的路径部分
console.log(location.search);   //输出当前地址的?后边的参数部分

修改地址:

location = "https://www.baidu.com";
location.href = "https://www.baidu.com";

8.3.2、常用方法

assign():用来跳转到其它的页面,作用和直接修改location一样

location.assign("https://www.baidu.com");

reload():用于重新加载当前页面,作用和刷新按钮一样,如果在方法中传递一个true,作为参数,则会强制清空缓存刷新页面

location.reload(true);

replace():可以使用一个新的页面替换当前页面,调用完毕也会跳转页面,它不会生成历史记录,不能使用回退按钮回退

location.replace("https://www.baidu.com");

8.4、History对象

History对象可以用来操作浏览器向前或向后翻页

8.4.1、常用属性
console.log(history);           //输出history对象
console.log(history.length);    //可以获取到当成访问的链接数量
12

image-20201022114945012

5.5.2、常用方法

back():可以回退到上一个页面,作用和浏览器的回退按钮一样

history.back();

forward():可以跳转到下一个页面,作用和浏览器的前进按钮一样

history.forward();

go():可以用来跳转到指定的页面,它需要一个整数作为参数

​ 1:表示向前跳转一个页面,相当于forward()
​ 2:表示向前跳转两个页面
​ -1:表示向后跳转一个页面,相当于back()
​ -2:表示向后跳转两个页面

history.go(-2);

8.5、Screen对象

Screen 对象包含有关客户端显示屏幕的信息。

注意:没有应用于 screen 对象的公开标准,不过所有浏览器都支持该对象。

8.5.1、Screen对象描述

每个 Window 对象的 screen 属性都引用一个 Screen 对象。Screen 对象中存放着有关显示浏览器屏幕的信息。JavaScript 程序将利用这些信息来优化它们的输出,以达到用户的显示要求。例如,一个程序可以根据显示器的尺寸选择使用大图像还是使用小图像,它还可以根据显示器的颜色深度选择使用 16 位色还是使用 8 位色的图形。另外,JavaScript 程序还能根据有关屏幕尺寸的信息将新的浏览器窗口定位在屏幕中间。

5.6.2、Screen对象属性

属性 描述
availHeight 返回显示屏幕的高度 (除 Windows 任务栏之外)。
availWidth 返回显示屏幕的宽度 (除 Windows 任务栏之外)。
bufferDepth 设置或返回调色板的比特深度。
colorDepth 返回目标设备或缓冲器上的调色板的比特深度。
deviceXDPI 返回显示屏幕的每英寸水平点数。
deviceYDPI 返 回显示屏幕的每英寸垂直点数。
fontSmoothingEnabled 返回用户是否在显示控制面板中启用了字体平滑。
height 返回显示屏幕的高度。
logicalXDPI 返回显示屏幕每英寸的水平方向的常规点数。
logicalYDPI 返回显示屏幕每英寸的垂直方向的常规点数。
pixelDepth 返回显示屏幕的颜色分辨率(比特每像素)。
updateInterval 设置或返回屏幕的刷新率。
width 返回显示器屏幕的宽度。

九、JavaScript高级语法

9.1、Exception

9.1.1、异常概述

在ES3之前JavaScript代码执行的过程中,一旦出现错误,整个JavaScript代码都会停止执行,这样就显的代码非常的不健壮。

在Java或C#等一些高级语言中,都提供了异常处理机制,可以处理出现的异常,而不会停止整个应用程序。

从ES3开始,JavaScript也提供了类似的异常处理机制,从而让JavaScript代码变的更健壮,即使执行的过程中出现了异常,也可以让程序具有了一部分的异常恢复能力。

当错误发生时,JavaScript 提供了错误信息的内置 error 对象。

error 对象提供两个有用的属性:name 和 message 。

Error 对象属性

属性描述
name设置或返回错误名
message设置或返回错误消息(一条字符串)

Error Name Values

error 的 name 属性可返回六个不同的值:

错误名 描述
EvalError 已在 eval() 函数中发生的错误
RangeError 已发生超出数字范围的错误
ReferenceError 已发生非法引用
SyntaxError 已发生语法错误
TypeError 已发生类型错误
URIError 在 encodeURI() 中已发生的错误

9.1.2、异常捕捉

ES3开始引入了 try-catch 语句,是 JavaScript 中处理异常的标准方式。

语法格式:

try {
    // 可能发生异常的代码
} catch (error) {
    // 发生错误执行的代码
} finally {
    // 无论是否出错都会执行的代码
}

在 try…catch 中,try 中一旦出现错误则其它语句不能执行,如果不出现错误则 catch 中的语句不会执行。

JavaScript 参考其它编程语言,也提供了一种 finally 语句:不管 try 中的语句有没有错误,在最后都会执行 finally 中的语句。也就是说,try 中语句不发生错误执行完毕后会执行 finally 中的语句,try 中的语句发生错误,则执行 catch中的语句,catch 中的语句执行完毕后也会执行 finally 中的语句。

在JavaScript中,如果添加了 finally 语句,则 catch 语句可以省略。但是如果没有 catch 语句,则一旦发生错误就无法捕获这个错误,所以在执行完 finally 中的语句后,程序就会立即停止了。所以,在实际使用中,最好一直带着 catch 语句。如果你写了 catch 语句,则finally 语句也是可以省略的。

9.1.3、异常演示

1、Eval 错误

EvalError 指示 eval() 函数中的错误。更新版本的 JavaScript 不会抛出任何 EvalError,请使用 SyntaxError 代替。

2、范围错误

RangeError 会在您使用了合法值的范围之外的数字时抛出。

3、引用错误

假如您使用(引用)了尚未声明的变量,则 ReferenceError 会被抛出:

4、语法错误

假如您计算带语法错误的代码,会 SyntaxError 被抛出:

5、类型错误

假如您使用的值不在期望值的范围之内,则 TypeError 被抛出:

6、URI错误

假如您在 URI 函数中使用非法字符,则 URIError 被抛出:

9.1.4、异常抛出

在大部分的代码执行过程中,都是出现错误的时候,由浏览器(javascript引擎)抛出异常,然后程序或者停止执行或被try…catch 捕获。

然而有时候我们在检测到一些不合理的情况发生的时候也可以主动抛出错误,请使用 throw 关键字抛出来主动抛出异常。

注意事项:

  1. thow后面就是我们要抛出的异常对象,在以前的时候都是出现错误的时候浏览器抛出异常对象,只是现在是我们自己主动抛出的异常对象。
  2. 只要有异常对象抛出,不管是浏览器抛出的,还是代码主动抛出,都会让程序停止执行。如果想让程序继续执行,则有也可以用try…catch来捕获。
  3. 每一个错误类型都可以传入一个参数,表示实际的错误信息。
  4. 我们可以在适当的时候抛出任何我们想抛出的异常类型。throw new SyntaxError("语法错误...");
9.1.4.1、主动抛出内置异常
/*该函数接收一个数字,返回它的平方。*/
function foo(num) {
    if (typeof num == "number") {
        return num * num;
    } else {
        throw new TypeError("您输入的是一个非法数字!")
    }
}

console.log(foo(4));
console.log(foo("abc"));

image-20201023082421480

9.1.4.2、主动抛出自定义异常

如果要自定义错误类型,只需要继承任何一个自定义错误类型都可以,一般直接继承Error即可。

/*自定义错误*/
function MyError(message) {
    this.message = "注意:这是自定义的错误"
    this.name = "自定义错误";
}
MyError.prototype = new Error();

try {
    throw new MyError("注意:这是自定义错误类型")
} catch (error) {
    console.log(error.message)
}

image-20201023082719743

9.2、JSON

9.2.1、JSON语法

在json中,每一个数据项,都是由一个键值对(或者说是名值对)组成的,但是键必须是字符串,且由双引号包围,而值必须是以下数据类型之一:

  • 字符串(在 JSON 中,字符串值必须由双引号编写)
  • 数字
  • 对象(JSON 对象)
  • 数组
  • 布尔
  • null

JSON 的值不可以是以下数据类型之一:

  • 函数
  • 日期
  • undefined

因为 JSON 语法由 JavaScript 对象标记法衍生而来,所以很少需要其它额外的软件来处理 JavaScript 中的 JSON。

9.2.2、JSON数据类型

1、JSON字符串

JSON 中的字符串必须用双引号包围。

{"name": "John"}

2、JSON数字

JSON 中的数字必须是整数或浮点数。

{"age": 30}

3、JSON对象

JSON 中的值可以是对象,JSON 中作为值的对象必须遵守与 JSON 对象相同的规则。

{
    "employee": {"name": "Bill Gates", "age": 62, "city": "Seattle"}
}

4、JSON数组

JSON 中的值可以是数组。

{
    "employees": ["Bill", "Steve", "David"]
}

5、JSON布尔

JSON 中的值可以是 true/false。

{"sale": true}

6、JSON null

JSON 中的值可以是 null。

{"middlename": null}

9.2.3、JSON字符串转JS对象

JSON.parse():可以将以JSON字符串转换为JS对象,它需要一个JSON字符串作为参数,会将该字符串转换为JS对象并返回

案例演示:

var jsonStr = '{"name":"孙悟空","age":18,"gender":"男"}';
var obj = JSON.parse(jsonStr);
console.log(obj);

image-20201023093124404

注意 :JSON这个对象在IE7及以下的浏览器中不支持,所以在这些浏览器中调用时会报错

9.2.4、JS对象转JSON字符串

JSON.stringify():可以将一个JS对象转换为JSON字符串,需要一个js对象作为参数,会返回一个JSON字符串

案例演示:

var obj = {name: "猪八戒", age: 28, gender: "男"};
var jsonStr = JSON.stringify(obj);
console.log(jsonStr);


image-20201023093346336

注意 :JSON这个对象在IE7及以下的浏览器中不支持,所以在这些浏览器中调用时会报错

9.3、AJAX

1、 AJAX的XMLHttpRequest对象

AJAX 的核心是 XMLHttpRequest 对象。 所有现代浏览器都支持 XMLHttpRequest 对象。

XMLHttpRequest 对象用于幕后同服务器交换数据,这意味着可以更新网页的部分,而不需要重新加载整个页面。

所有现代浏览器(Chrom、IE7+、Firefox、Safari 以及 Opera)都有内建的 XMLHttpRequest 对象。

创建 XMLHttpRequest 的语法是:

variable = new XMLHttpRequest();

老版本的 Internet Explorer(IE5 和 IE6)使用 ActiveX 对象:

variable = new ActiveXObject("Microsoft.XMLHTTP");

为了应对所有浏览器,包括 IE5 和 IE6,请检查浏览器是否支持 XMLHttpRequest 对象。如果支持,创建 XMLHttpRequest 对象,如果不支持,则创建 ActiveX 对象:

var xhttp;
if (window.XMLHttpRequest) {
    xhttp = new XMLHttpRequest();
} else {
    // code for IE6, IE5
    xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}

但是需要注意的是,出于安全原因,现代浏览器不允许跨域访问,这意味着尝试加载的网页和 XML 文件都必须位于相同服务器上。

2、AJAX的XMLHttpRequest对象方法

方法 描述
new XMLHttpRequest() 创建新的 XMLHttpRequest 对象
abort() 取消当前请求
getAllResponseHeaders() 返回头部信息
getResponseHeader() 返回特定的头部信息
open(method, url, async, user, psw) 规定请求method:请求类型 GET 或 POST
url:文件位置
async:true(异步)或 false(同步)
user:可选的用户名称
psw:可选的密码
send() 将请求发送到服务器,用于 GET 请求
send(string) 将请求发送到服务器,用于 POST 请求
setRequestHeader() 向要发送的报头添加标签/值对

3、AJAX的XMLHttpRequest对象属性

属性 描述
onreadystatechange 定义当 readyState 属性发生变化时被调用的函数
readyState 保存 XMLHttpRequest 的状态。
0:请求未初始化
1:服务器连接已建立
2:请求已收到
3:正在处理请求
4:请求已完成且响应已就绪
responseText 以字符串返回响应数据
responseXML 以 XML 数据返回响应数据
status 返回请求的状态号
200: “OK”
403: “Forbidden”
404: “Not Found”
如需完整列表请访问 Http 消息参考手册
statusText 返回状态文本(比如 “OK” 或 “Not Found”)

4、AJAX的GET请求

工程结构:

image-20201023095114457

users.json

[
  {"name":"孙悟空","age":18,"gender":"男"},
  {"name":"猪八戒","age":19,"gender":"男"},
  {"name":"唐僧","age":20,"gender":"男"},
  {"name":"沙和尚","age":21,"gender":"男"}
]

index.html

//步骤一:创建异步对象
var ajax = new XMLHttpRequest();
//步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url
ajax.open("get", "users.json");
//步骤三:发送请求
ajax.send();
//步骤四:注册事件 onreadystatechange 状态改变就会调用
ajax.onreadystatechange = function () {
    if (ajax.readyState == 4 && ajax.status == 200) {
        //步骤五:如果能够进到这个判断,说明数据完美的回来了,并且请求的页面是存在的
        console.log(ajax.responseText);//输入响应的内容
    }
};

控制台:

image-20201023095301891

5、AJAX的POST请求

工程结构:

image-20201023095114457

users.json

[
  {"name":"孙悟空","age":18,"gender":"男"},
  {"name":"猪八戒","age":19,"gender":"男"},
  {"name":"唐僧","age":20,"gender":"男"},
  {"name":"沙和尚","age":21,"gender":"男"}
]

index.html

//步骤一:创建异步对象
var ajax = new XMLHttpRequest();
//步骤二:设置请求的类型及url,注意:post请求一定要添加请求头才行不然会报错
ajax.open("post", "users.json");
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//步骤三:发送请求
ajax.send();
//步骤四:注册事件 onreadystatechange 状态改变就会调用
ajax.onreadystatechange = function () {
    //步骤五:如果能够进到这个判断,说明数据完美的回来了,并且请求的页面是存在的
    if (ajax.readyState == 4 && ajax.status == 200) {
        console.log(ajax.responseText);//输入响应的内容
    }
};

控制台:

image-20201023095301891

6、AJAX的请求整合

工程结构:

image-20201023095114457

users.json

[
  {"name":"孙悟空","age":18,"gender":"男"},
  {"name":"猪八戒","age":19,"gender":"男"},
  {"name":"唐僧","age":20,"gender":"男"},
  {"name":"沙和尚","age":21,"gender":"男"}
]

index.html

var Ajax = {
    get: function (url, fn) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4 && xhr.status == 200 || xhr.status == 304) {
                fn.call(this, xhr.responseText);
            }
        };
        xhr.send();
    },
    post: function (url, data, fn) {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", url, true);
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
                fn.call(this, xhr.responseText);
            }
        };
        xhr.send(data);
    }
};

// 演示GET请求
Ajax.get("users.json", function (response) {
    console.log(response);
});

// 演示POST请求
Ajax.post("users.json", "", function (response) {
    console.log(response);
});

控制台:

image-20201023100152816

9.4、Cookie

1、Cookie创建

JavaScript 中,创建 cookie 如下所示:

document.cookie = "username=zhangsan";

您还可以为 cookie 添加一个过期时间(以 UTC 或 GMT 时间)。默认情况下,cookie 在浏览器关闭时删除。

document.cookie = "username=zhangsan; expires=Thu, 18 Dec 2043 12:00:00 GMT";

您可以使用 path 参数告诉浏览器 cookie 的路径。默认情况下,cookie 属于当前页面。

document.cookie = "username=zhangsan; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/"

2、Cookie读取

avaScript 中,读取 cookie 如下所示:

document.cookie 将以字符串的方式返回所有的 cookie,类型格式: cookie1=value; cookie2=value; cookie3=value;

document.cookie = "username=zhangsan";
var cookies = document.cookie;
console.log(cookies);

image-20201023124113003

3、Cookie修改

avaScript 中,修改 cookie 如下所示:

使用 document.cookie 将旧的 cookie 将被覆盖就是修改。

document.cookie = "username=zhangsan";
document.cookie = "username=lisi";
var cookies = document.cookie;
console.log(cookies);

image-20201023124645689

4、Cookie删除

JavaScript 中,删除 cookie 如下所示:

删除 cookie 非常简单,您只需要设置 expires 参数为以前的时间即可,如下所示,设置为 Thu, 01 Jan 1970 00:00:00 GMT:

document.cookie = "username=zhangsan";
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
var cookies = document.cookie;
console.log(cookies);

image-20201023124936436

5、Cookie值设置函数

/**
 * Cookie值设置函数
 * @param cname     cookie名称
 * @param cvalue    cookie值
 * @param exdays    过期天数
 */
function setCookie(cname, cvalue, exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
    var expires = "expires=" + d.toGMTString();
    document.cookie = cname + "=" + cvalue + "; " + expires;
}

6、Cookie值获取函数

/**
 * Cookie值获取函数
 * @param cname     cookie名称
 * @returns {string}
 */
function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i].trim();
        if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
    }
    return "";
}

9.5、WebStorage

1、WebStorage分类

Web Storage又分为两种: sessionStorage 和localStorage ,即这两个是Storage的一个实例。从字面意思就可以很清楚的看出来,sessionStorage将数据保存在session中,浏览器关闭也就没了;而localStorage则一直将数据保存在客户端本地; 不管是sessionStorage,还是localStorage,使用的API都相同。

localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理,低版本IE可以使用json2.js

2、localStorage方法

对象介绍:

localStorage在本地永久性存储数据,除非显式将其删除或清空。

常见方法:

  • 保存单个数据:localStorage.setItem(key,value);
  • 读取单个数据:localStorage.getItem(key);
  • 删除单个数据:localStorage.removeItem(key);
  • 删除所有数据:localStorage.clear();
  • 获取某个索引的key:localStorage.key(index);

案例类型:

// 保存数据
localStorage.setItem("username", "zhangsan");

// 读取单个数据
console.log(localStorage.getItem("username"));
console.log("===============");

// 删除单个数据
localStorage.removeItem("username");
console.log(localStorage.getItem("username"));
console.log("===============");

// 保存两个数据
localStorage.setItem("age", 18);
localStorage.setItem("sex", "男");
console.log("age=" + localStorage.getItem("age"));
console.log("sex=" + localStorage.getItem("sex"));
console.log("===============");

// 使用for-in循环来迭代localStorage中的键值对、属性和方法:
for (var key in localStorage) {
    console.log(key + "=" + localStorage[key]);
}
console.log("===============");

// 使用for循环来迭代localStorage中的键值对:
for (var i = 0; i < localStorage.length; i++) {
    var key = localStorage.key(i);
    var value = localStorage.getItem(key);
    console.log(key + "=" + value);
}
console.log("===============");

// 删除所有数据
localStorage.clear();

控制台:

image-20201023220221557

3、sessionStorage方法

对象介绍:

sessionStorage对象存储特定于某个对话的数据,也就是它的生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。存储在sessionStorage中的数据可以跨越页面刷新而存在,同时如果浏览器支持,浏览器崩溃并重启之后依然可以使用(注意:Firefox和Weblit都支持,IE则不行)。

因为sessionStorage对象绑定于某个服务器会话,所以当文件在本地运行的时候是不可用的。存储在sessionStorage中的数据只能由最初给对象存储数据的页面访问到,所以对多页面应用有限制。

不同浏览器写入数据方法略有不同。Firefox和Webkit实现了同步写入,所以添加到存储空间中的数据是立刻被提交的。而IE的实现则是异步写入数据,所以在设置数据和将数据实际写入磁盘之间可能有一些延迟。

常见方法:

  • 保存单个数据:sessionStorage.setItem(key,value);

  • 读取单个数据:sessionStorage.getItem(key);

  • 删除单个数据:sessionStorage.removeItem(key);

  • 删除所有数据:sessionStorage.clear();

  • 获取某个索引的key:sessionStorage.key(index);

案例演示:

// 保存数据
sessionStorage.setItem("username", "zhangsan");

// 读取单个数据
console.log(sessionStorage.getItem("username"));
console.log("===============");

// 删除单个数据
sessionStorage.removeItem("username");
console.log(sessionStorage.getItem("username"));
console.log("===============");

// 保存两个数据
sessionStorage.setItem("age", 18);
sessionStorage.setItem("sex", "男");
console.log("age=" + sessionStorage.getItem("age"));
console.log("sex=" + sessionStorage.getItem("sex"));
console.log("===============");

// 使用for-in循环来迭代sessionStorage中的键值对、属性和方法:
for (var key in sessionStorage) {
    console.log(key + "=" + sessionStorage[key]);
}
console.log("===============");

// 使用for循环来迭代sessionStorage中的键值对:
for (var i = 0; i < sessionStorage.length; i++) {
    var key = sessionStorage.key(i);
    var value = sessionStorage.getItem(key);
    console.log(key + "=" + value);
}
console.log("===============");

// 删除所有数据
sessionStorage.clear();

控制台:

image-20201023220326391

9.6、Closure

1、闭包引入

需求信息:点击某个按钮,提示"点击的是第n个按钮"

第一种解决方法:将btn所对应的下标保存在btn上

var btns = document.getElementsByTagName('button');

//将btn所对应的下标保存在btn上
for (var i = 0, length = btns.length; i < length; i++) {
    var btn = btns[i];
    btn.index = i;
    btn.onclick = function () {
        alert('第' + (this.index + 1) + '个');
    }
}

第二种解决方法:利用闭包延长局部变量的生命周期

var btns = document.getElementsByTagName('button');

// 利用闭包延长局部变量的生命周期
for (var i = 0, length = btns.length; i < length; i++) {
    (function (j) {
        var btn = btns[j];
        btn.onclick = function () {
            alert('第' + (j + 1) + '个');
        }
    })(i);
}

2、闭包概念

如何产生闭包?

  • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包

什么才是闭包?

  • 理解一:闭包是嵌套的内部函数(绝大部分人认为)
  • 理解二:包含被引用变量(函数)的对象(极少部分人认为)

闭包的作用?

  • 它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中

3、闭包演示

function fun1() {
    var a = 2;
    function subFun() {
        a++;
        console.log(a);
    }
    return subFun;
}

var f1 = fun1();
f1();
f1();
console.log("===============");

function fun2() {
    var a = 2;
    function subFun() {
        a--;
        console.log(a);
    }
    return subFun;
}

var f2 = fun2();
f2();
f2();
console.log("===============");

image-20201023225902988

4、闭包生命周期

生命周期:

  1. 产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
  2. 死亡:在嵌套的内部函数成为垃圾对象时就死亡了

演示:

function fn1() {
    //此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
    var a = 2;

    function fn2() {
        a++;
        console.log(a);
    }

    return fn2;
}

var f = fn1();
f(); // 3
f(); // 4
f = null; //闭包死亡(包含闭包的函数对象成为垃圾对象)

5、闭包应用

闭包应用: 定义JS模块

  • 具有特定功能的js文件
  • 将所有的数据和功能都封装在一个函数内部(私有的)
  • 只向外暴露一个包含n个方法的对象或函数
  • 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能

案例:

第一种格式:myModule.js

function myModule() {
    //私有数据
    var msg = 'Hello, World';

    //操作数据的函数
    function doSomething() {
        console.log('doSomething() ' + msg.toUpperCase());
    }

    function doOtherthing() {
        console.log('doOtherthing() ' + msg.toLowerCase());
    }

    //向外暴露对象(给外部使用的方法)
    return {
        doSomething: doSomething,
        doOtherthing: doOtherthing
    }
}

第一种使用:index.html

var module = myModule();
module.doSomething();
module.doOtherthing();

image-20201023231502831

第二种格式:myModule.js

(function (window) {
    //私有数据
    var msg = 'Hello, World';

    //操作数据的函数
    function doSomething() {
        console.log('doSomething() ' + msg.toUpperCase());
    }

    function doOtherthing() {
        console.log('doOtherthing() ' + msg.toLowerCase());
    }

    //向外暴露对象(给外部使用的方法)
    window.myModule = {
        doSomething: doSomething,
        doOtherthing: doOtherthing
    }
})(window);

第二种使用:index.html

myModule.doSomething();
myModule.doOtherthing();

image-20201023231821301

十、JavaScript新特性

1、ECMAScript6新特性

1.1、let 关键字

let 关键字用来声明变量,使用 let 声明的变量有几个特点:

  • 不允许重复声明
  • 块儿级作用域
  • 不存在变量提升
  • 不影响作用域链
注意:以后声明变量使用 let 就对了

案例:创建四个div,单机每一个div让其变色

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .item {
            width: 100px;
            height: 50px;
            border: solid 1px rgb(42, 156, 156);
            float: left;
            margin-right: 10px;
        }
    </style>
</head>
<body>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>

<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    // 获取div元素对象
    let items = document.getElementsByClassName('item');

    // 遍历并绑定事件
    for (let i = 0; i < items.length; i++) {
        items[i].onclick = function () {
            // 以前的做法:this.style.background = "pink";
            items[i].style.background = "pink";
        };
    }
</script>
</body>
</html>

image-20201025151025642

1.2、const 关键字

const 关键字用来声明常量,const 声明有以下特点:

  • 不允许重复声明
  • 块儿级作用域
  • 声明必须赋初始值
  • 值不允许修改
  • 标识符一般为大写
注意:声明对象类型使用 const,非对象类型声明选择 let
// 声明常量
const MAX = 100;
console.log(MAX);

// 对于数组和对象的元素修改, 不算做对常量的修改, 不会报错
const TEAM1 = [1, 2, 3, 4];
const TEAM2 = [1, 2, 3, 4];
// 但是不能修改地址指向
// TEAM2 = TEAM1;

1.3、变量的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。

注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式

数组的解构赋值:

//数组的解构赋值
const arr = ["张学友", "刘德华", "黎明", "郭富城"];
let [zhang, liu, li, guo] = arr;
console.log(zhang);
console.log(liu);
console.log(li);
console.log(guo);

image-20201025152604811

简单对象的解构赋值:

//对象的解构赋值
const lin = {
    name: "林志颖",
    tags: ["车手", "歌手", "小旋风", "演员"]
};
let {name, tags} = lin;
console.log(name);
console.log(tags);

image-20201025152700504

复杂对象的解构赋值:

//复杂对象的解构赋值
let wangfei = {
    name: "王菲",
    age: 18,
    songs: ["红豆", "流年", "暧昧"],
    history: [
        {name: "窦唯"},
        {name: "李亚鹏"},
        {name: "谢霆锋"}
    ]
};
let {name, age, songs: [one, two, three], history: [first, second, third]} = wangfei;
console.log(name);
console.log(age);
console.log(one);
console.log(two);
console.log(three);
console.log(first);
console.log(second);
console.log(third);

image-20201025152830276

1.4、模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:

  • 字符串中可以出现换行符
  • 可以使用 ${xxx} 形式输出变量
注意:当遇到字符串与变量拼接的情况使用模板字符串

字符串中可以出现换行符:

//定义字符串
let str = `<ul>
               <li>沈腾</li>
               <li>玛丽</li>
               <li>魏翔</li>
               <li>艾伦</li>
           </ul>`;
console.log(str);

image-20201025153705017

变量拼接:

//变量拼接
let name = '小可爱';
let result = `欢迎${name}访问我的文章`;
console.log(result);

image-20201025153746643

1.5、简化对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法,这样的书写更加简洁。

注意:对象简写形式简化了代码,所以以后用简写就对了

let name = "张三";
let age = 18;
let speak = function () {
    console.log(this.name);
};

//属性和方法简写
let person = {
    name,
    age,
    speak
};

console.log(person.name);
console.log(person.age);
person.speak();

image-20201025154304885

1.6、箭头函数

ES6 允许使用「箭头」(=>)定义函数,通用写法如下:

let fn = (arg1, arg2, arg3) => {
    return arg1 + arg2 + arg3;
}

箭头函数的注意点:

  • 如果形参只有一个,则小括号可以省略

  • 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果

  • 箭头函数 this 指向声明时所在作用域下 this 的值,箭头函数不会更改 this 指向,用来指定回调函数会非常合适

  • 箭头函数不能作为构造函数实例化

  • 不能使用 arguments 实参

省略小括号的情况:

let fn = num => {
    return num * 10;
};

省略花括号的情况:

let fn = score => score * 20;

this 指向声明时所在作用域中 this 的值:

// this 指向声明时所在作用域中 this 的值
let fn = () => {
    console.log(this);
}
fn();

let school = {
    name: "张三",
    getName() {
        let subFun = () => {
            console.log(this);
        }
        subFun();
    }
};
school.getName();

image-20201025155420505

1.7、rest 参数

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments 参数。

注意:rest 参数非常适合不定个数参数函数的场景

// 作用与 arguments 类似
function add(...args) {
    console.log(args);
}
add(1, 2, 3, 4, 5);

// rest 参数必须是最后一个形参
function minus(a, b, ...args) {
    console.log(a, b, args);
}
minus(100, 1, 2, 3, 4, 5, 19);

image-20201025160054563

1.8、spread 扩展运算符

扩展运算符(spread)也是三个点(…),它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。

展开数组:

// 展开数组
let tfboys = ["德玛西亚之力", "德玛西亚之翼", "德玛西亚皇子"];
function fn() {
    console.log(arguments);
}
fn(...tfboys);

image-20201025161321223

展开对象:

// 展开对象
let skillOne = {
    q: "致命打击"
};
let skillTwo = {
    w: "勇气"
};
let skillThree = {
    e: "审判"
};
let skillFour = {
    r: "德玛西亚正义"
};
let gailun = {...skillOne, ...skillTwo, ...skillThree, ...skillFour};
console.log(gailun);

image-20201025161335012

1.9、Symbol类型

1、Symbol的使用

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值,它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型,Symbol 特点如下:

  • Symbol 的值是唯一的,用来解决命名冲突的问题
  • Symbol 值不能与其它数据进行运算
  • Symbol 定义的对象属性不能使用 for…in 循环遍 历 ,但是可以使用 Reflect.ownKeys 来获取对象的所有键名
//创建 Symbol
let s1 = Symbol();
console.log(s1);
console.log(typeof s1);

//添加标识的 Symbol
let s2 = Symbol("张三");
let s2_2 = Symbol("张三");
console.log(s2);
console.log(s2_2);
console.log(s2 === s2_2);

//使用 Symbol for 定义
let s3 = Symbol.for("张三");
let s3_2 = Symbol.for("张三");
console.log(s3);
console.log(s3_2);
console.log(s3 === s3_2);

//在方法中使用 Symbol
let game = {
    name: "狼人杀",
    [Symbol('say')]: function () {
        console.log("我可以发言")
    },
    [Symbol('zibao')]: function () {
        console.log('我可以自爆');
    }
};
console.log(game);

image-20201025173541630

注意:遇到唯一性的场景时要想到 Symbol
2、Symbol内置值

除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。

可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。

内置值 描述
Symbol.hasInstance 当其它对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法
Symbol.isConcatSpreadable ** 对象的 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于A rray.prototype.con cat()时, 是否可以展开 **
Symbol.species 创建衍生对象时,会使用该属性
Symbol.match 当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值
Symbol.replace 当该对象被 str.replace(myObject)方法调用时,会返回该方法的返回值
Symbol.search 当该对象被 str.search (myObject)方法调用时,会返回该方法的返回值
Symbol.split 当该对象被 str.split(myObject)方法调用时,会返回该方法的返回值
Symbol.iterator 当对象进行 for…of 循环时,会调用 Symbol.iterator 方法, 返回该对象的默认遍历器
Symbol.toPrimitive 当对象被转为原始类型的值时,会调用这个方法,返 回该对象对应的原始类型值
Symbol. toStringTag 当对象上面调用 toString 方法时,返回该方法的返 回值
Symbol. unscopables 当对象指定了使用 with 关键字时,哪些属性会被 with 环境排除

Symbol.hasInstance演示:

class Person {
    static [Symbol.hasInstance](param) {
        console.log("我被用来检测类型了");
    }
}
let o = {};
console.log(o instanceof Person);

image-20201025173815146

Symbol.isConcatSpreadable演示:

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr2[Symbol.isConcatSpreadable] = true;
console.log(arr1.concat(arr2));

const arr3 = [1, 2, 3];
const arr4 = [4, 5, 6];
arr4[Symbol.isConcatSpreadable] = false;

image-20201025174006835

1.10、迭代器

遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费,原生具备 iterator 接口的数据:

  • Array
  • Arguments
  • Set
  • Map
  • String
  • TypedArray
  • NodeList
注意:需要自定义遍历数据的时候,要想到迭代器

工作原理:

  1. 创建一个指针对象,指向当前数据结构的起始位置
  2. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  4. 每调用 next 方法返回一个包含 value 和 done 属性的对象

案例演示:遍历数组

//声明一个数组
const xiyou = ["唐僧", "孙悟空", "猪八戒", "沙僧"];
//使用 for...of 遍历数组
for (let v of xiyou) {
    console.log(v);
}
console.log("===============");

//获取迭代器对象
let iterator = xiyou[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

image-20201025174729990

案例演示:自定义遍历数据

//声明一个对象
const banji = {
    name: "五班",
    stus: [
        "张三",
        "李四",
        "王五",
        "小六"
    ],
    [Symbol.iterator]() {
        //索引变量
        let index = 0;
        let _this = this;
        return {
            next: function () {
                if (index < _this.stus.length) {
                    const result = {value: _this.stus[index], done: false};
                    //下标自增
                    index++;
                    //返回结果
                    return result;

image-20201025181339376

1.11、生成器

生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

1.11.1、生成器函数使用

代码说明:

  • * 的位置没有限制
  • 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到 yield 语句后的值
  • yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next 方法,执行一段代码
  • next 方法可以传递实参,作为 yield 语句的返回值
function * gen() {
    /*代码1开始执行*/
    console.log("代码1执行了");
    yield "一只没有耳朵";
    /*代码2开始执行*/
    console.log("代码2执行了");
    yield "一只没有尾巴";
    /*代码3开始执行*/
    console.log("代码3执行了");
    return "真奇怪";
}

let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log("===============");

//遍历
for (let v of gen()) {
    console.log(v);
}


image-20201025194703172

1.11.2、生成器函数参数
function * gen(arg) {
    console.log(arg);
    let one = yield 111;
    console.log(one);
    let two = yield 222;
    console.log(two);
    let three = yield 333;
    console.log(three);
}

//执行获取迭代器对象
let iterator = gen('AAA');
console.log(iterator.next());

//next方法可以传入实参
console.log(iterator.next('BBB'));
console.log(iterator.next('CCC'));
console.log(iterator.next('DDD'));

image-20201025194822072

1.12、Promise

1.12.1、Promise基本使用
//实例化 Promise 对象
const p = new Promise(function (resolve, reject) {
    setTimeout(function () {

        // 成功调用resolve()处理
        let data = "数据读取成功";
        resolve(data);

        // 失败调用reject()处理
        let err = "数据读取失败";
        reject(err);

    }, 1000);
});

//调用 promise 对象的 then 方法
p.then(function (value) {
    console.log(value);
}, function (reason) {
    console.error(reason);
});
1.12.2、Promise案例演示
// 接口地址: https://api.apiopen.top/getJoke
const p = new Promise((resolve, reject) => {
    //1. 创建对象
    const xhr = new XMLHttpRequest();
    //2. 初始化
    xhr.open("GET", "https://api.apiopen.top/getJoke");
    //3. 发送
    xhr.send();
    //4. 绑定事件, 处理响应结果
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            //判断响应状态码 200-299
            if (xhr.status >= 200 && xhr.status < 300) {
                //表示成功
                resolve(xhr.response);
            } else {
                //如果失败
                reject(xhr.status);
            }
        }
    }
});

//指定回调
p.then(function (value) {
    console.log(value);
}, function (reason) {
    console.error(reason);
});

image-20201025203323920

1.12.3、Promise-then方法

调用 then 方法,then 方法的返回结果是 Promise 对象,对象状态由回调函数的执行结果决定,如果回调函数中返回的结果是 非 promise 类型的属性,状态为成功,返回值为对象的成功的值

//创建 promise 对象
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("用户数据");
    }, 1000)
});

//链式调用+箭头函数
p.then(value => {
    console.log(value);
    return value;
}).then(value => {
    console.log(value);
});

image-20201025203756949

1.12.4、Promise-catch方法

如果只想处理错误状态,我们可以使用 catch 方法

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        //设置 p 对象的状态为失败, 并设置失败的值
        reject("出错啦!");
    }, 1000);
});

p.catch(function (reason) {
    console.error(reason);
});

1.13、Set

ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法:

  • size:返回集合的元素个数
  • add():增加一个新元素,返回当前集合
  • delete():删除元素,返回 boolean 值
  • has():检测集合中是否包含某个元素,返回 boolean 值
  • clear():清空集合,返回 undefined
//创建一个空集合
let s = new Set();
//创建一个非空集合
let s1 = new Set([1, 2, 3, 1, 2, 3]);
//集合属性与方法
//返回集合的元素个数
console.log(s1.size);
//添加新元素
console.log(s1.add(4));
//删除元素
console.log(s1.delete(1));
//检测是否存在某个值
console.log(s1.has(2));
//清空集合
console.log(s1.clear());

image-20201025204233138

1.14、Map

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。Map 的属性和方法:

  • size:返回 Map 的元素个数

  • set():增加一个新元素,返回当前 Map

  • get():返回键名对象的键值

  • has():检测 Map 中是否包含某个元素,返回 boolean 值

  • clear():清空集合,返回 undefined

1.15、class 类

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是 一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已,它的一些如下:

  • class:声明类
  • constructor:定义构造函数初始化
  • extends:继承父类
  • super:调用父级构造方法
  • static:定义静态方法和属性
//父类
class Phone {
    //构造方法
    constructor(brand, color, price) {
        this.brand = brand;
        this.color = color;
        this.price = price;
    }

    //对象方法
    call() {
        console.log("我可以打电话!!!")
    }
}

//子类
class SmartPhone extends Phone {
    constructor(brand, color, price, screen, pixel) {
        super(brand, color, price);
        this.screen = screen;
        this.pixel = pixel;
    }

    //子类方法
    photo() {
        console.log("我可以拍照!!");
    }

    playGame() {
        console.log("我可以玩游戏!!");
    }

    //方法重写
    call() {
        console.log("我可以进行视频通话!!");
    }

    //静态方法
    static run() {
        console.log("我可以运行程序")
    }

    static connect() {
        console.log("我可以建立连接")
    }
}

//实例化对象
const Nokia = new Phone("诺基亚", "灰色", 230);
const iPhone6s = new SmartPhone("苹果", "白色", 6088, "4.7inch", "500w");
//调用子类方法
iPhone6s.playGame();
//调用重写方法
iPhone6s.call();
//调用静态方法
SmartPhone.run();

image-20201025205027699

1.16、数值扩展

1.16.1、二进制和八进制

ES6 新增了二进制和八进制的表示方法

let b = 0b1010//二进制
let o = 0o777;//八进制
let d = 100;//十进制
let x = 0xff;//十六进制
console.log(b);
console.log(o);
console.log(d);
console.log(x);

image-20201025205402446

1.16.2、Number.EPSILON

Number.EPSILON:它是 JavaScript 表示的最小精度,EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16

function equal(a, b) {
    if (Math.abs(a - b) < Number.EPSILON) {
        return true;
    } else {
        return false;
    }
}
console.log(0.1 + 0.2 === 0.3);
console.log(equal(0.1 + 0.2, 0.3));

image-20201025205537398

1.16.3、Number.isFinite

Number.isFinite:检测一个数值是否为有限数

console.log(Number.isFinite(100));
console.log(Number.isFinite(100 / 0));
console.log(Number.isFinite(Infinity));
console.log(Number.isFinite(-Infinity));

image-20201025205822198

1.16.4、Number.isNaN

Number.isNaN:检测一个数值是否为 NaN

console.log(Number.isNaN(123));

image-20201025205950242

1.16.5、Number.parseInt

Number.parseInt:将一个字符串转换为整数

console.log(Number.parseInt("123abc"));

image-20201025210139403

1.16.6、Number.parseFloat

Number.parseFloat:将一个字符串转换为浮点数

console.log(Number.parseFloat("3.1415926神奇"));

image-20201025210334463

1.16.7、Number.isInteger

Number.isInteger:判断一个数是否为整数

console.log(Number.isInteger(5));
console.log(Number.isInteger(2.5));

image-20201025210435568

1.16.8、Math.trunc

Math.trunc:将数字的小数部分抹掉

console.log(Math.trunc(3.5));

image-20201025210605825

1.16.9、Math.sign

Math.sign:判断一个数到底为正数、负数、还是零

console.log(Math.sign(100));
console.log(Math.sign(0));
console.log(Math.sign(-20000));

image-20201025210642315

1.17、对象扩展

ES6 新增了一些 Object 对象的方法,例如:

  • Object.is:比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
  • Object.assign:对象的合并,将源对象的所有可枚举属性,复制到目标对象
  • proto、setPrototypeOf、 setPrototypeOf可以直接设置对象的原型
1.17.1、Object.is

Object.is:判断两个值是否完全相等

console.log(Object.is(120, 120));// ===
console.log(Object.is(NaN, NaN));// ===
console.log(NaN === NaN);// ===

image-20201025211007712

1.17.2、Object.assign

Object.assign:对象的合并,后边的对象会把前边对象的相同属性和方法覆盖,没有的属性和方法会合并

const config1 = {
    host: "localhost",
    port: 3306,
    name: "zhangsan",
    pass: "root",
    test1: "test1"
};
const config2 = {
    host: "127.0.0.1",
    port: 3309,
    name: "lisi",
    pass: "root",
    test2: "test2"
}
console.log(Object.assign(config1, config2));

image-20201025221354637

1.18、模块化

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。

1.18.1、模块化的好处
  • 防止命名冲突
  • 代码复用
  • 高维护性
1.18.2、模块化的产品

CommonJS => NodeJS、Browserify

AMD => requireJS

CMD => seaJS

1.18.3、模块化的语法

模块功能主要由两个命令构成:export 和 import。

  • export 命令用于规定模块的对外接口
  • import 命令用于输入其它模块提供的功能
1.18.4、模块化的暴露
1.18.5、模块化的导入
1.18.6、解构赋值形式
注意:针对默认暴露还可以直接 import m3 from "./m3.js"

1.19、浅拷贝和深拷贝

1.19.1、浅拷贝
1.19.2、深拷贝
1.19.2.1、自带的

Array:slice()、concat()、Array.from()、… 操作符:只能实现一维数组的深拷贝

Object:Object.assign()、… 操作符:只能实现一维对象的深拷贝

JSON.parse(JSON.stringify(obj)):可实现多维对象的深拷贝,但会忽略 undefined任意的函数Symbol 值

2、ECMAScript7新特性

2.1、数组方法扩展
2.2、幂运算

3、ECMAScript8新特性

3.1、async 函数
3.2、await 表达式
3.3、对象方法拓展
  • Object.keys()方法返回一个给定对象的所有可枚举键值的数组
  • Object.values()方法返回一个给定对象的所有可枚举属性值的数组
  • Object.entries()方法返回一个给定对象自身可遍历属性 [key,value] 的数组

4、ECMAScript9新特性

4.1、对象拓展

Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,在 ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符

4.1.1、对象展开
4.1.2、对象合并
4.2、正则表达式拓展
4.2.1、命名捕获分组
4.2.2、正向断言
4.2.3、反向断言
4.2.4、dotAll模式

5、ECMAScript10新特性

5.1、对象方法拓展

Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。

5.2、字符串方法拓展
5.3、数组方法拓展
5.4、Symbol属性拓展

6、ECMAScript11新特性

6.1、class 私有属性
6.2、Promise.allSettled

该Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时,通常使用它。

相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject时立即结束。

6.3、字符串方法扩展

String.prototype.matchAll() 方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。

6.4、可选链操作符

当我们要使用传进来的一个属性值的时候,我们不知道这个属性值到底有没有传,我们可以使用&&运算符一级一级判断,就像这样 const dbHost = config && config.db && config.db.host;但是这样会显得很麻烦,所以在ES11 中就提供了可选链操作符,它就简化了代码,变成了这样 const dbHost = config?.db?.host; 另一方面,即使用户没有传入这个属性,我们用了也不会报错,而是undefined

6.5、动态 import
6.6、BigInt类型

BigInt数据类型的目的是比Number数据类型支持的范围更大的整数值。在对大整数执行数学运算时,以任意精度表示整数的能力尤为重要。使用BigInt,整数溢出将不再是问题。

此外,可以安全地使用更加准确时间戳,大整数ID等,而无需使用变通方法。 它就是JS 第二个数字数据类型,也将是 JS 第8种基本数据类型:

Boolean
Null
Undefined
Number
BigInt
String
Symbol
Object
对于学过其它语言的程序员来说,JS中缺少显式整数类型常常令人困惑。许多编程语言支持多种数字类型,如浮点型、双精度型、整数型和双精度型,但JS却不是这样。在JS中,按照IEEE 754-2008标准的定义,所有数字都以双精度64位浮点格式表示。

在此标准下,无法精确表示的非常大的整数将自动四舍五入。确切地说,JS 中的Number类型只能安全地表示-9007199254740991 (-(2^53-1)) 和9007199254740991(2^53-1)之间的整数,任何超出此范围的整数值都可能失去精度。

6.7、globalThis

全局属性 globalThis 包含全局的 this 值,类似于全局对象(global object)。

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值