JavaScript笔记

330 阅读1小时+

**

第一部分 基础

JavaScript 概述

① JavaScript 的特点

JavaScript 是一门动态的弱类型的,解释型的,基于对象脚本语言。

动态: 程序执行的时候才会确定数据类型。 静态: 书写代码的时候提前确定数据类型。

弱类型: 数据类型可以自动转换。 强类型: 数据类型无法自动转换。

解释型: 边编译,边运行,开发效率更高。 编译型: 先编译,后运行,运行效率更高。

脚本: 一般都是可以嵌在其它编程语言当中执行。

② javaScript 的运行环境

所有的高级编程语言都无法直接在电脑上运行,需要安装运行环境,编译型语言的运行环境称之为编译器,解释型语言的运行环境称之为解释器

运行环境负责把编程语言编译为机器码。

JavaScript 的运行环境是浏览器或者Node.js

② 客户端浏览器的 JavaScript 组成部分

  • ECMAScript, JavaScript 的基本语法。
  • BOM 浏览器对象模型, 浏览器提供给 JavaScript 的接口。
  • DOM 文档对象模型, 文档页面(HTML和CSS)提供给 JavaScript 的接口。

第一章 JavaScript 基本语法

1.1 JavaScript 在 HTML 中使用的三种方式

① 行内式

配合属性,把JS代码写在HTML标签里面。

<button onclick="JS代码..."></button>
<button ondblclick="JS代码..."></button>

② 内嵌式

使用 script 标签,把JS代码写在 script 双标签之间。

<script>
	JS 代码...
</script>

注意: 建议CSS写在 head 标签内,而 JS 代码建议写在其他HTML元素的最后面。

③ 外链式

js 代码写在独立的 js 文件中,在 html 中使用 script 标签的 src 属性引入 js 文件。

<script src="js文件的地址"></script>

注意: 外链式与内嵌式不要共用一个 script 标签。

1.2 JavaScript 注释

// 单行注释
// alert('ok');

/*
 多行注释
 多行注释
 多行注释
*/

1.3 JavaScript 语法特点

1. JavaScript 中严格区分大小写。
2. 每条JavaScript语句结束需要语句结束符,分号或者换行都可以作为语句结束符。

1.4 JavaScript 中输出内容

① 输出到弹框

alert(内容);

② 输出到页面中

document.write(内容)

③ 输出到控制台

console.log(内容);

第二章 变量

2.1 数据、直接量、变量

数据: 计算机计算的就是数据,运算的结果也是数据。

直接量: 直接使用数据就是直接量。

变量: 给数据取个名字,使用该名字就可以使用数据了。

2.2 变量的意义

  1. 用变量给数据取了名字,通过变量就可以使用数据,方便对数据的多次重复使用。
  2. 使用变量可以把数据存储下来,保证后续使用的还是原来的数据。

2.3 变量的语法

1、把数据给变量的过程称之为给变量赋值, 存储到变量中的数据称之为变量的值

2、如果要创建新的变量,需要使用关键字 var,如果使用的变量已经存在不需要关键字 var

// 创建一个变量
var username;

// 创建变量并且赋值
var age = 1000;
var address = age;

// 修改变量的值(给变量赋新的值)
username = '芳芳';
address = '上海'

// 同时创建多个变量
var a,b,c;

// 同时创建多个变量并且赋值
var a = 100, b = 200, c = 300;

2.4 变量的命名规范(标识符的命名规范)

① 强制规范

1、变量名必须由数字、字母、下划线、$ 组成并且不能以数字开头。

2、变量名不能是关键字或保留字。

关键字: 在 JS 中具有特殊意义的词。

保留字: 目前在 JS 中还没有特殊意义,但是将来可能会具有特殊意义的词。

② 建议规范

1、变量名应该是由有意义的单词组成。

2、如果变量名是由多个单词组成,建议使用小驼峰命名法,如 homeAddressuserHomeAddress

第三章 数据类型

1 数据类型的划分

① 原始类型

number		数值类型
string		字符串类型
boolean		布尔类型
undefined	未定义类型
null		空类型

② 对象类型

Array    数组类型
Object   对象类型
Function 函数类型
...

2 判断数据的类型

typeof()
Object.prototype.toString.call() 万能

3 原始类型介绍

3.1 number 数值类型

① 整型

var n1 = 89;  // 十进制表示
var n2 = 010; // 八进制表示
var n3 = 0x10;  // 十六进制表示

② 浮点型

var n4 = 34.009;
var n5 = 78.0;
var n6 = 45e-2;  // 科学计数法 

注意:

浮点数的计算存在精度问题!原因是计算机需要转为二进制数再进行计算。

③ NaN

NaN 全称 Not a Number, 是一个特殊的 number 类型的值。

NaN 这个值具有两个特点:

  1. NaN 与任何数进行任何运算,计算结果还是 NaN。
  2. NaN 与任何数都不相等,包括自己。

使用 isNaN() 函数可以判断一个数是不是 NaN。

null+1 = null

undefined+1 =NaN+1 = NaN

④ JavaScript 中数字的有效范围

JavaScript 可以表示的数字存在有效范围,5e324 ~ 1.7976931348623157e+308 。

如果超出有效范围,会用 Infinity(正无穷) 或者 -Infinity(负无穷) 来表示,Infinity 和 -Infinity 是特殊的 number 类型值。

使用 isFinite() 函数来判断一个数字是否在有效范围内。

3.2 string 字符串类型

① 字符串的表示方式

// 使用单引号
'字符串内容'

// 使用双引号
"字符串内容"

注意:

  1. 使用单引号定义字符串与使用双引号定义字符串没有任何区别。
  2. 单引号定义的字符串内容中中不允许写单引号;双引号定义的字符串内容中不允许写双引号。

② 转义字符

\n		表示换行符
\\		表示\本身
\'		用于在单引号定义的字符串中使用单引号
\"		用于在双引号定义的字符串中使用双引号

3.3 boolean 布尔类型

布尔类型的数据有两个值,分别是 true 和 false。

3.4 null 和 undefined

undefined 类型的数据表示没有定义,都是被动产生的,定义变量没有赋值以及对象中不存在的属性和数组中不存在的元素,这些情况会自动得到 undefined 类型的数据。

null 表示存在但是内容是空,一般需要 null 的时候,会主动给变量赋值为 null。

注意: 使用 typeof 判断 null 的数据类型,得到是 object。

4 数据类型转换

4.1 数据类型转换的规则

① 其他类型转为 number 类型

1. string 转为 number 类型的规则:
   ① 纯数字字符串会转为对应的数字,十六进制表示以及科学计数法表示的数字都是纯数字。
   ② 空字符串转为数字 0。
   ③ 其他字符串转为 NaN。
   注意: string 类型数据在转为 number 类型数据的时候会自动去掉两端的空格,剩下部分按照以上规则进行转换
   
2. boolean 转为 number 类型的规则:
   true -> 1
   false -> 0
   
3. null 转为 number 类型的规则:
   null -> 0
   
4. undefeind 转为 number 类型的规则:
   undefined -> NaN

② 其他类型转为 string 类型

数据=>字符串

③ 其他类型转为 boolean 类型

1. number 转为 boolean 类型的规则:
   0NaN 转为 false,其他数字都转为 true
   
2. string 转为 boolean 类型的规则:
   空字符串转为 false,其他字符串都转为 true
   
3. null 转为 boolean 类型的规则:
   null -> false
   
4. undefeind 转为 boolean 类型的规则:
   undefiend -> false

总结: 0、NaN、空字符串、null、undefined 转为布尔值会转为 false,其他数据转为布尔值都是 true。

4.2 强制类型转换(显示转换)

① 强制把其他类型转为 number 类型的函数

Number()
parseInt()
parseFloat() 

parseFloat('') = NaN
Number('') = 0

parsentInt()、parsentFloat 与 Number 的区别:

  1. parseInt() 和 parseFloat() 用于提取字符串的数字,所有非字符串类型得到的都是 NaN
  2. 纯数字字符串转为对应的数字;以数字开头的字符串转为开头的数字;其他字符串都是 NaN(空字符也是NaN)

parseInt() 和 parsentFloat() 的区别:

  1. parseInt() 只能提取字符串中的整数部分。
  2. parentFloat() 可以提取字符串中的小数部分.

注意:

  1. Number() 的转换规则此时标准的转换规则。
  2. 可以使用 parsetInt() 来对某个数字进行取整(会把小数部分舍去)。

② 强制把其他类型转为 string 类型的函数

String()

③ 强制把其他类型转为 boolean 类型的函数

Boolean()

4.3 自动类型转换(隐式转换)

  1. JavaScript 作为弱类型编程语言,支持数据类型自动转换。

  2. 数据参与运算,数据不符合运算所要求的类型,数据会根据运算要求的类型自动转换。

  3. 运算所要求的数据类型由运算符绝对。

  4. 自动类型转换的规则与强制类型(Number、String、Boolean)转换的规则是一样的。

第四章 运算符和表达式

1 运算符和表达式

① 运算符

运算符就是参与运算的符号,像+-*/ 等。

与运算符一起运算的变量、直接量、表达式称之为操作数

② 表达式

  1. 表达式是变量或者直接量与运算符组成的式子,表达式会有一个计算结果,称之为表达式的值,也就是说表达式是有值的。

  2. 最简单的表达式是"原始表达式", 如 直接量、 变量名、 关键字等。

  3. 复杂表达式由简单表达式组成, 运算符何以将简单表达式组合成复杂表达式。

  4. 注意带有副作用的表达式,这样的表达式除了有表达式的计算结果之外,还会对表达式中的变量产生影响。

2 运算符的分类

① 按照运算符需要的操作数的个数

一元运算符/一目运算符
二元运算符/二目运算符
三元运算符/三目运算符

② 按照运算符的功能

算术运算符
关系运算符
逻辑运算符
位运算符
赋值运算符
其他运算符

3 运算符讲解(按照功能)

① 算术运算符

运算符含义操作数个数操作数的类型要求组成的表达式的值的类型有无副作用
+加号2numbernumber
-减号2numbernumber
*乘号2numbernumber
/除号2numbernumber
%取余(取模)2numbernumber
+正号1numbernumber
-负号1numbernumber
++累加1numbernumber
--累减1numbernumber

正号运算符:

通常会使用正号运算符来把其他类型的数据转为 number 类型,利用运算符自动类型转换。

累加运算符和累减运算符:

累加和累减都是一元运算符,操作数必须以变量的形式表示,具有副作用。

累加和累减的符号可以在操作数的前面也可以在操作数的后面,对操作数产生的副作用是一样的,但是所组成的表达式的值不同。

++a=自增后;运算符在操作前面: 表单式取操作数累加或累减之后的值作为表达式的值。

a++=自增前;运算符在操作数后面:表达式取操作数累加或累减之前的值作为表达式的值。

② 关系运算符(比较运算符)

运算符含义操作数个数操作数的类型要求组成的表达式的值的类型有无副作用
大于2number、stringboolean
>=大于等于2number、stringboolean
<小于2number、stringboolean
<=小于等于2number、stringboolean
==相等2number、其他类型boolean
!=不相等2number、其他类型boolean
===全等2所有类型boolean
!==不全等2所有类型boolean

两个操作数比较大小的规则:

  1. 如果两个操作的类型不一致,各自自动转为 number 类型进行比较。
  2. 如果两个操作数都是 string 类型,按照 string 类型比较大小的规则进行比较。

string 类型的数据比较大小的规则:

  1. 比较的是字符对应的 unicode 编码,unicode 编码是数字,可以比较大小。
  2. 字符串比较是按位一位一位的比较,如果一个操作数第一位的字符比另外一个操作第一位字符的unicode编码大,该操作整体都大。

相等和不相等的判断规则:

  1. 如果两个操作数类型不一致,自动转为 number 之后进行比较。
  2. 如果两个操作数类型一致,直接看是不是一样的。

全等和不全等的判断规则:

  1. 如果两个操作数类型不一致,直接返回 false,不会进行自动类型转换的。
  2. 如果两个操作数类型一致,比较是否一样。

null 进行相等(==)判断的时候,非常特殊:

console.log(null == null);  	 // true
console.log(null == 0);     	 // false 
console.log(null == false); 	 // false
console.log(null == '');    	 // false
console.log(null == undefined);  // true 

③ 逻辑运算符

运算符含义操作数个数操作数的类型要求组成的表达式的值的类型有无副作用
&&逻辑与2任何类型取其中一个操作数作为表达式的值
||逻辑或2任何类型取其中一个操作数作为表达式的值
!逻辑非1booleanboolean

逻辑与组成的表达式的值的计算规则:

两个值中,只要有一个值为false,就返回false。

  1. 如果第一个值为false,不会看第二个值,返回第一个值。
  2. 如果第一个值为true,要看第二个,取第二个操作数作为表达式的值。

逻辑或组成的表达式的值的计算规则:

  1. 如果第一个操作数不成立,取第二个操作数作为表达式的值。
  2. 如果第一个操作数成立,取第一个操作数作为表达式的值。

④ 赋值运算符

运算符含义操作数个数操作数的类型要求组成的表达式的值的类型有无副作用
=赋值2无要求,左边的操作数必须是变量变量(左边操作数)的值作为表达式的值
+=赋值求和2无要求,左边的操作数必须是变量变量(左边操作数的值作为表达式的值
-=赋值求差2无要求,左边的操作数必须是变量变量(左边操作数的值作为表达式的值
*=赋值求积2无要求,左边的操作数必须是变量变量(左边操作数的值作为表达式的值
/=赋值求商2无要求,左边的操作数必须是变量变量(左边操作数的值作为表达式的值
%=赋值求余2无要求,左边的操作数必须是变量变量(左边操作数的值作为表达式的值
+=赋值连接2string,左边的操作数必须是变量变量(左边操作数的值作为表达式的值

总结:

  1. 赋值运算符要求左边的操作数必须是变量,右边的操作数变量、直接量、表达式都可以。
  2. 赋值运算符都有副作用,使用的就是赋值运算符的副作用,左边操作数会被修改。
  3. 赋值运算符组成的表达式以左边操作数为值(修改过后的)。

⑤ 其他运算符

运算符含义操作数个数操作数的类型要求组成的表达式的值的类型有无副作用
typeof判断数据的类型1任意类型string
,逗号2
+字符串连接符2stringstring
?:比较运算符3任意类型取第二个操作数或者第三个操作数作为表达式的值

+ 符合具有3个含义:

  1. 如果操作数只有一个,+ 表示正号运算符。
  2. 如果操作数两个,且没有任何一个操作数是字符串,+ 表示加号。
  3. 如果操作数连个,只要有一个操作数是字符串,+ 表示字符串连接符。

?:三元运算符组成的表示的值的规则:

  1. 如果第一个操作数成立,取第二个操作数作为表达式的值。
  2. 如果第一个操作数不成立,取第三个操作数作为表达式的值。

4 运算符优先级

1、 一元运算符: ++ -- ! typeof 
2、 乘除取余、加减
3、 关系运算符
4、 逻辑运算符
5、 赋值运算符
6、 ?:

第五章 流程控制语句

1 分支结构(条件语句)

1.1 单向分支

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

1.2 双向分支

if (条件表达式) {
    语句 ...;
} else {
    语句 ...;
}

1.3 多向分支 else if

if (条件表达式) {

} else if (条件表达式) {

} else if (条件表达式) {

} else if (条件表达式) {

} else {

}

1.4 多向分支 switch case

switch (表达式) {
    case 表达式可能的值: 
    	语句...;
    	break;
    case 表达式可能的值: 
    	语句...;
    	break;
    case 表达式可能的值: 
    	语句...;
    	break;
    case 表达式可能的值: 
    	语句...;
    	break;
	defalut:
    	语句
}

总结:

  1. 判断条件是判等的多向分享,适合使用 switch case 结构。
  2. switch case 判等是按照全等(===)的规则进行判等。
  3. break 结束本 case 中代码的执行,如果没有 break,会一直向下执行,直到执行 break。

1.5 嵌套分支

if (条件表达式) {
    if (条件表达式) {
        
    }    
} else {
    if (条件表达式) {
        
    } else {
        
    }
}

2 循环语句

2.1 while 循环

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

正常的循环:

  1. 循环条件不能永远都成立,否则会变为死循环
  2. 随着循环次数的增加,循环条件应该越来越趋近于不成立。

2.2 do while 循环

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

while 循环和 do-while 循环的区别:

  1. 第一次循环,while 先判断再执行; do while 先执行一次。
  2. 第二次以及往后的循环,二者都一样都是先判断再执行。
  3. 循环同样的次数,二者循环条件表达式是一样的,do while 只比 while 少判读了一次。

2.3 for 循环

for (循环标记变量初始化; 循环条件; 循环标记变量的变化) {
    语句...
}

总结:

  1. 循环标记变量初始化只在正式循环之前执行一次。
  2. 每次循环先判断循环条件,成立才能执行,与 while 一致。
  3. 循环标记变量的变化在每次循环体语句执行结束后才执行。

3 跳转语句

① break

在 switch case 中使用: 结束当前 case。

在循环(for、while、do while)中使用: 跳出并结束循环。

① continue

在循环(for、while、do while)中使用:跳出本次循环,下次循环继续。

第六章 数组

1 数组

1.1 什么是数组

1)数组是值的有序集合

2)每一个值叫做一个元素

3)每个元素在数组中有一个位置,用数字表示,称为 索引(有时也叫 下标)

4)数组的元素可以是任何类型的数据。

5)数组索引从0开始,最大为2^32-2,数组最大能容纳4294967294个元素。

1.2 声明数组的方式

1⃣️直接量方式

[元素1,元素2,元素3。。。];

2⃣️Array函数方式

Array(元素1,元素2,元素3...)
Array10); //声明一个拥有10个元素的数组

注意:Array如果只有一个参数,且该参数是number,该参数会被当作数组的个数。

3⃣️Array构造函数方式

new Array(元素1,元素2,元素3...)
new Array10);

注释:new Array()和Array()相关规则完全一致。

1.3 稀疏数组

  • 数组要求元素的索引必须是连续的数字
  • 如果出现有些元素没有值,是空的,这样的数组称为 稀疏数组
  • 如果读取空的元素的值,会得到 undefined。(注意:空的元素值就是空的,只是读取该元素自动的到undefined)
  • 产生稀疏数组的情况:1⃣️使用索引赋值,与前面的索引不连续;2⃣️修改数组的length,给了比较大的值;
  • 注意:要尽量避免稀疏数组。

1.4 数组的遍历(迭代)

// 使用for 循环遍历数组  推荐方式
for(var i = 0; i < 数组.length ; i++){
  	数组[i]; //读取到数组的每个元素
}
//使用for in 结构遍历数组。 不推荐
for(var i in 数组){
  	数组[i]; //读取到数组的每个元素
}

1.5 数组元素的添加和删除

1⃣️ 添加元素

1,指定下一个索引,给数组添加元素,可以利用 数组.length
	arr[arr.length] = '值';

2,使用 push()方法在数组的后面追加一个或多个元素。
	arr.push(值);
	arr.push(值1,值2...);
	
3,使用 unshift()方法在数组的最前面添加一个或多个元素
	arr.unshift(值);
	arr.unshift(值1,值2...);
	
4,使用 splice() 方法在指定的位置插入一个或多个元素
	arr.splice(位置,0,值);
	arr.splice(位置,0,值1,值2);

2⃣️删除元素

1,利用 length 删除数组的后几个元素
	arr.length -= n; //删除后几个元素
	
2,使用pop() 删除数组最后一个元素
	arr.pop();
	
3,使用shift()删除数组的第一个元素
	arr.shift()
	
4,使用splice()删除指定位置指定数量的元素
	arr.splice(位置,要删除的数量)

1.6 多维数组

[
    ['11111', '222222', '33333333'], 
    [100, 200, 300, 400, 500], 
    ['阿波罗', '门捷列夫'],
    [
        ['a', 'b', 'c', 'd'],
        ['A', 'B', 'C', 'D'],
    ]
];

1.7 字符串的数组特性

字符串与数组有相似的结构,字符串是由字符组成的串,每个字符类似于数组的元素。

字符串具备如下数组特性:
1. 字符串具有 length 属性,可以获取字符串长度(字符串中字符的个数)
2. 使用 [] 指定索引可以读取字符串中的某个字符。
3. 使用 for 循环遍历字符串中的每个字符。

字符串只是具备部分数组的特性:
1. 字符串无法像数组那样修改其中的某个字符。
2. 字符串的 length 也不能修改。

第七章 函数

1 函数概述

1⃣️ 什么是函数

  • 函数是具有特定功能的代码块。
  • 在JavaScript,函数也是一种数据类型,属于对象类型,使用typeof判断函数返回function。

2 函数的组成

  • 函数名
  • 参数
  • 函数体
  • 返回值

2 声明函数的三种方式

1⃣️ function 关键字方式

function 函数名([参数列表...]){
  函数体语句...;
}

2⃣️表达式方式

var 函数名 = function([参数列表...]){
  函数体语句...;
}

3⃣️Function构造函数方式

var 函数名 = new Function('函数体语句');
var 函数名 = new Function('参数1','参数2','参数3','函数体语句...');
var 函数名 = Function('函数体语句...');
var 函数名 = Function('参数1','参数2','参数3','函数体语句...');

3 函数的调用和返回值

① 函数调用

  • 函数名的后面加括号(),才是函数的调用,函数中的语句才可以执行。

② 返回值

  • 函数调用表达式(函数名+括号)的值是函数的返回值。

  • 在函数体内部,使用 return 关键字可以设置返回值,需要在 return 的右边写个表达式(表达式的值就是函数返回值)。

  • 如果函数中没有 return 或者 return 的右边没有表达式,函数默认返回 undefined。

  • return 关键字除了可以设置函数的返回值,还可以结束函数的执行,执行了 return 之后,函数体内 return 后面的代码就不会执行。

4 函数的参数

① 形参和实参

形参: 定义函数的时候所设置的参数,形参就像没有赋值的变量, 形参的形式必须以变量名形式给出。

实参: 调用函数给的参数,用于给实参赋值,实参的形式可以是变量、直接量、表达式。

② 形参和实参的数量问题

  1. 设置了多个形参,调用函数的时候就给多少个实参。
  2. 如果实参的个数比形参多,实参按照顺序依次给形参赋值,多余的实参就没有用了。
  3. 如果实参的个数比形参少,实参按照顺序依次给形参赋值,后面的形参没有对应的实参,默认赋值为 undefined。

③ 形参的默认值(可选参数)

ES5 中给形参设置默认值的方式:

function 函数名(参数1,参数2) {
    if (参数2 === undefined) {
        参数2 = 默认值;
    }
}

ES6 中给形参设置默认值的方式:

function 函数名(参数1,参数2=默认值) {
    
}

注意: 有默认值的形参一定要放在后面。

④ arguments

  1. arguments 只能在函数内使用,arguements 是系统定义好的变量。
  2. arguments 可以得到调用函数时给的所有的实参。
  3. arguments 是一个类数组(不是真正的的数组),可以通过索引读取其中的成员,具有 length 属性得到长度,可以使用 for 循环遍历里面的成员。
  4. JavaScript 函数中获取实参,可以通过设置形参,也可以通过 arguments。

5 作用域

① 变量的作用域

变量的作用域: 变量的可作用范围。根据变量的作用域,可以把变量分为全局变量局部变量

全局变量: 在函数外边定义的变量就是全局变量,全局变量的作用域是全局。

局部变量: 在函数内定义的变量就是局部变量,局部变量的作用域是所在的函数。

注意实现:

  1. 形参是局部变量,作用域是所在的函数。
  2. 函数名本质上是变量名,所以说函数本身也具有作用域。

② 作用域链

  1. 什么是作用域链: 函数的嵌套可以形成作用域链。
  2. 作用域链的作用: 作用域链描述变量的查找过程。 使用变量的时候,先从本作用域查找有没有声明该变量,如果没有去上层作用域查找,有就停止查找,没有继续向上层作用域查找,一直到全局作用域,如果都没有报错。
  3. 作用域链只与函数声明的位置有关系,与函数调用的位置无关!

6 变量提升

① 变量提升

JavaScript 中会把变量提升到本作用域的最前面。

只提升了变量的声明,没有提升变量的赋值

在正式执行代码之前,变量就已经提升了,正式执行到变量声明语句的时候,仅仅是进行赋值操作。

② 函数提升

function 关键字方式创建的函数: 不但会提升函数的声明,连同函数一起提升到作用域的最前面。当正式执行代码执行到函数声明语句,直接跳过。

表达式方式或者 Function 构造函数方式创建的函数: 与变量提交的规则一致!

7 匿名函数

匿名函数就是没有名字的函数; 匿名函数其实就是用直接量方式表示函数。

匿名函数通常作为自调用函数和回调函数使用。

// 匿名函数
        // 把一个匿名函数赋值给变量 fn
        var fn = function() {
            console.log('我是匿名函数');
            return 100;
        };

8 自调用函数

简称 IIFE,英文全称 Immediately Invoked Function Expression

自调用函数也叫立即调用的函数,函数声明完立即被调用了。

自调用函数通常会使用匿名函数。

自调用函数的意义: 产生作用域,避免全局变量污染

注意: 连续写多个自调用函数的时候,可能会出现执行错误,需要写完一个自调函数之后用分号结束,或者在自调函数的前面任意写个一元运算符。

9 回调函数

① 什么是回调函数

把满足以下三个条件的函数称为回调函数:

  1. 函数是我定义的
  2. 我没有调用函数(没有直接调用)
  3. 函数却被执行了

注意: 回调函数的形式大部分都是作为其他函数的参数。

// 声明函数
        // 要求参数 cb 是个function类型的数据
        function func(cb) {
            cb();
        }

        // 调用函数 设置一个匿名函数作为func的参数
        func(function() {
            console.log('hello, 我是 cb');
        });

        // 再次调用 func, 把 demo 作为 func 的参数
        func(demo);

        function demo() {
            console.log('hello 我是demo');
        }
// ------------------------------------------------------

        
        // 定义函数,参数要求是 function
        function func01(cb) {
            cb(100, 200);
        }
        // 调用 func01
        func01(function(a, b) {
            console.log('两个参数的和:', a+b);
        });//300
        


 // -----------------------------------------------------
        // 声明函数 要求参数是 function
        function func02(cb, num1, num2) {
            cb(num1, num2);
        }
        // 调用 func02
        func02(function(a, b) {
            console.log('两个参数的和:', a + b);
        }, 400, 700);//1100
        



   // ----------------------------------------------
        // 声明函数 要求参数是 function
        function func03(cb, num1, num2) {
            return cb(num1, num2);
        }
        // 调用 func03()
        var res = func03(function(num1, num2){
            return num1 + num2;
        }, 800, 200);

        console.log(res); // 1000

② 回调函数的使用场景

  1. DOM事件的回调函数
  2. 定时器的回调函数
  3. Ajax的回调函数
  4. 生命周期的钩子函数
  5. 其他需要回调函数作为参数的系统函数(方法)

10 递归函数

① 什么是递归函数

函数内部可以调用自己,称之为函数的递归调用,这种函数叫递归函数.

② 递归函数成功的条件

  1. 要有结束递归的条件。
  2. 随着递归调用次数的增加,应该越来越趋近于递归结束的条件。

③ 递归函数的缺点

1)函数递归调用很容易发生灾难(内存泄漏)而调用失败。

2)函数递归调用效率不高,能不用就不用。

④ 递归函数应用场景

后端场景: 目录的删除、复制、移动等操作。

前端场景: 处理从后端获取的 json 数据。

// 定义函数,实现某个数字的阶乘
        function fn(n) {
            // 如果 n 是小于等于 2
            if (n <= 2) {
                return n;
            }
            return n * fn(n - 1);
        }

        console.log('8的阶乘:', fn(8));
        console.log('10的阶乘:', fn(10));
        console.log('3的阶乘:', fn(3));
        console.log('5的阶乘:', fn(5));
        console.log('4的阶乘:', fn(4));

第八章 对象

1 Object对象

1.1 什么是Object

  • Object 是值的无序集合。
  • Object 由属性组成,属性有属性名和属性值。
  • 属性的值可以是任意类型的数据,属性值如果 function 类型的数据,这种属性可以成为方法。

1.2 如何声明Object对象

① 第一种 直接量方式

// 创建一个空的的对象
var obj1 = {};

// 创建一个带有属性的 object 类型的数据
var obj2 = {
    name: '俄格', 
    age: 18,
    getInfo: function() {
        console.log('get info 函数');
    },
    users: [100, 200, 300, 400],
    'address': '上海',
    'user-info': 'hello'
};

注意: 属性名的形式是字符串,属性名的格式没有任何要求; 如果属性名满足标识符的命名规范(变量的命名规范),可以省略引号。

② 使用 Object 函数

var obj = Object();

③ 使用 Object 构造函数

var obj = new Object();

1.3 Object 对象属性的读写

① 语法

// 点语法
obj.属性名;
obj.属性名 = 值;

// [] 语法
obj['属性名'];
obj['属性名']();

② 什么情况下必须使用 [] 语法读写属性

1. 属性名不满足标识符命名规范
2. 使用变量表示属性名

1.4 遍历对象的属性

// for in 遍历 Object 所有的属性
for (var prop in obj) {
    console.log(prop, obj[prop]);
}

1.5 删除对象中的属性

使用 delete 运算符,删除对象中的属性。

delete 对象.属性名;
delete 对象['属性名'];

1.6 判断对象中是否存在某个属性

使用 in 运算符,这是二元运算符,组成的表达式返回布尔值。

'属性名' in 对象

2 构造函数

2.1 什么是构造函数?

JS 中的构造函数相当于其他编程语言中的类。

构造函数用于产生创造一个对象。

相同数据类型的对象,构造函数必定是一致的。 数据类型等价于构造函数。

2.2 构造函数和对象的关系

构造函数是对象的抽象(描述),对象是构造函数的实例。

一个构造函数可以对应多个对象,一个对象只能有一个构造函数。

2.3 判断对象的构造函数

① 运算符 instanceof

判断一个对象是否是某个构造函数的实例,这是一个二元运算符,所组成的表达式的值是布尔值

对象 instanceof 构造函数;

② constructor 属性

所有的对象都具有 constructor 属性,通过该属性可以得到对象的构造函数。

对象.constructor

2.4 自定义构造函数

// 自定义构造函数
function User(username, pwd, address) {
    // 给对象设置属性
    this.username = username;
    this.pwd = pwd
    this.address = address;

    // 给对象设置方法
    this.getInfo = function() {
        console.log('我叫' + this.username + ',我的密码是' + this.pwd + ',我的地址是' + this.address);
    };
}

构造函数特点总结:

  1. 函数还是构造函数取决于怎么去用,如果实例化就是构造函数,调用就是函数。
  2. 实例化构造函数的时候,函数中的语句都会被执行到。
  3. 构造函数函数中的 return 语句对实例化结果的影响:① 如果构造函数没有返回值或者返回原始类型的数据,对实例化结果毫无影响。 ② 如果构造函数返回一个对象类型的数据,实例化的结果就是该返回值。

2.5 实例化

使用 new 关键字实例化构造函数,可以创建出该构造函数的实例(对象)。

new 构函数函数()

3 this

3.1 this 的含义

1) this 是 JS 内置的一个变量,本质上是一个对象

2)通常在函数或方法当中使用,代表这个函数的调用者。

3.2 this 的指向

this 的指向就是 this 的值, this 在不同的地方使用,this 的指向是不同的。

this 的指向分为两种情况:

1 在构造函数中使用 this: this 指向构造函数的实例。

2 在方法(函数)中使用 this: 谁调用了该方法,this 指向谁。

3.3 window 介绍

  • js中有个一全局对象,运行在浏览器上的js,全局对象是 window。

  • 全局变量(包括全局函数)其实本质上是全局对象的属性(或方法)。

  • 使用全局对象的属性或方法,可以省略 window.

var length = 10;
function fn() {
    console.log(this.length);
}
 
var obj = {
  length: 5,
  method: function(fn) {
    fn();
    arguments[0]();
  }
};
 
obj.method(fn, 1);	//10 2 
//fn()是全局调用
//arguments[0] 是 返回arguments的实参个数
function C2(){
  this.a = 37;
  return {
    a:38
  };
}

var o = new C2();
console.log(o.a);  // 38

4 原型

4.1 原型的概念

  • 任何一个对象,都有原型。 原型也是个对象。
  • 对象可以继承原型上的属性(方法)。

4.2 如何获取对象的原型

① 隐式原型方式

对象.__proto__

② 显示原型方式

对象的构造函数.prototype

注意: 具有相同构造函数的两个对象,原型也是一致的。

4.3 对象、构造函数、原型之间的关系

① 对象和构造函数

  • 构造函数是对象的抽象(描述),对象是构造函数的实例。
  • 一个构造函数可以创建多个对象,但是一个对象只能有一个构造函数。

② 对象和原型

  • 每个对象都有原型。
  • 对象可以使用原型上的属性(方法)。

③ 构造函数和原型

  • 构造函数通过属性 prototype 可以获取构造函数的实例的原型。
  • 具有相同构造函数的对象,原型也是一致的。

4.4 自定义构造函数时原型的应用

// 自定义构造函数
function User(name, age, address) {
    // 设置对象的属性
    this.name = name;
    this.age = age;
    this.address = address;
}

// 把对象上的方法设置到对象的原型
User.prototype.getInfo = function() {
    console.log('我叫'+this.name+',我的年龄'+this.age+',我住在'+this.address);
};

User.prototype.addShopcart = function() {
    console.log(this.name + '向购物车添加了商品');
}

4.5 判断属性是否属于对象本身

对象.hasOwnProperty('属性名');   

规则:

  1. hasOwnProperty() 判断,只有在对象本身上的属性才返回 true,在原型上的和不存在的属性,返回false。
  2. in 运算符,对象本身和原型上的属性都返回 true,只有不存在的属性才返回 false.

4.6 创建对象的同时指定原型

// 在对象的原型上设置方法,并没有改变对象的原型
对象.__proto__.add = function() {
    
};

// 修改了对象的原型的指向
对象.__proto__ = 新的对象;


// 给构造函数实例的原型添加方法,不改变原型的指向
构造函数.prototype.add = function() {
    
}

// 修改构造函数实例的原型的指向
构造函数.prototype = 新的对象;

// 创建对象的同时,指定原型
Object.create(对象);

// 创建一个没有原型的对象
Object.create(null);

5 原型链

5.1 原型链的概念

每个对象都有原型,原型还是对象,所以原型也有原型,一直到一个没有原型的对象(顶层原型),组成了一个原型链。

5.2 属性查找过程

当使用对象中某个属性的时候:

  1. 先从对象自身查找,如果由该属性直接返回,如果没有该属性从原型上进行查找。
  2. 如果原型上定了该属性,直接返回;如果原型也没有该属性,继续从原型的原型上进行查找。
  3. 一直查找到顶层原型对象,哪里找到哪里停止,最后顶层原型对象也没有属性,返回 undefined

5.3 构造函数和原型链

  1. 数组的原型、数字的原型、字符粗的原型、自定义构造函数创建的实例的原型,都是 Object 的实例。
  2. 最顶层的原型对象是 Object.prototype
  3. 原型对象上constructor属性指向的并不是自己的构造函数,指向的是以该原型对象为原型的对象的构造函数。

6 值类型和引用类型

6.1 值类型和引用类型的概念

原始类型,也叫值类型,还可以被称为不可变类型

对象类型,也叫引用类型,还可被称为可变类型

6.2 值类型和引用类型的存储方式

  1. 变量和原始类型的数据存储在内容的栈结构中,

    对象类型的数据存储到内容的堆结构中。

  2. 变量的值如果是原始类型,变量和值都在栈结构中;

    如果变量的值是对象类型,对象存在堆结构中,栈结构中存的是变量和对象的地址。

  3. 如果变量a赋值给变量b,变量a的值如果是原始类型,直接把值给b。

    变量a的值如果是对象类型,把地址给b,导致a和b指向的是一个对象。

  4. 原始类型的赋值方式成为值传递;

    对象类型的赋值方式成为引用传递。

6.3 不可变和可变

不可变类型: 原始类型的数据,无法修改数据中的一部分,一个原始类型的数据必须作为一个整体。

可变类型: 对象类型的数据,可以修改数据中的一部分(修改属性、修改元素)。

 // 定义变量 变量的值是数字
var a = 100;
var b = a;
a = 200;
console.log(b);
console.log('');

// 定义变量 变量的值是对象
var obj1 = {age: 100};
var obj2 = obj1;
obj2.age = 200;
console.log(obj1.age); // 200
console.log('');

// 修改了变量的引用关系
var obj3 = obj1;
obj3 = {age: 600}; //重新给obj3赋值,obj3改变地址
console.log(obj1.age); // 200;
  1. 下面代码中的对象f有方法a和方法b吗?
var F = function () {}
Object.prototype.a = function () {}
Function.prototype.b = function () {}

var f = new F()
// 对象f中,有a,没有b
// 对象F中,有 a,b
  1. 写出下列代码输出结果
Object.prototype.a=function(){
  console.log('a');
};

Function.prototype.b=function(){
  console.log('b');
}

var F = function(){};
var f = [];

f.a();  	 //a
F.a();     //a
F.b();		 //b
f.b();		 //cuo
  1. 写出下面代码的输出结果
function User() {
}
User.prototype = {
    name: 'aaaa'
};
var u = new User();
console.log(u.name);  //aaaa

User.prototype.name = 'bbb';
console.log(u.name);   //bbb

User.prototype = {
    name: 'ccc'
};
console.log(u.name);  bbb

第九章 内置对象

在线文档: developer.mozilla.org/zh-CN/docs/…

内置构造函数,主要掌握内置构造函数实例的属性和方法。

内置对象就是内置构造函数的实例。

1. Boolean

创建一个Boolean类型的数据,与数组一样也具有三种方式。

//直接量方式
var b1 = true;

//构造函数方式(具有数据类型转换的功能)
var b2 = new Boolean(true);

//函数
var b3 = Boolean(false);

2 Number

方法名含义
Number 实例的属性和方法
toFixed()返回指定位数的小数,参数设置为保留的小数位,没有参数默认取整;四舍五入规则。
toString()把数字转为其他进行形式,参数指定进制。
Number 构造函数本身的属性和方法
Number.MAX_VALUE得到 JS 中所能表示的最大的值
Number.MIN_VALUE得到 JS 中所能表示的最小的值

3 String

String 实例的属性和方法
length属性,只读,获取字符串长度(字符串中字符的个数)
charAt(index)取出指定索引的字符,通产用 [] 语法代替。
charCodeAt(index)取出指定索引字符的 uncode 编码。
indexOf(value)返回 vlue 在字符串中第一次出现的位置。
lastIndexOf(value)返回 value 在字符串中最后一次出现的位置。
slice(start [,end])截取字符串,第一个参数指定开始截取的位置,第二个参数指定结束位置(结果中不包括结束位置的字符),不指定第二个参数,截取到字符串结尾。
substring(start [,end])同 slice 一模一样。
substr(start, [,len])截取字符串,与 slice 相比,第二个参数指定的是截取的长度,不指定第二个参数截取到字符串结束
split([分隔符])把字符串分隔为数组。
toUpperCase()把字符串全部转为大写。
toLowerCase()把字符串全部转为小写。

注意:

  1. 通常可以使用 indexOf() 和 lastIndexOf() 来判断字符串中是否包含某个值,返回 -1 就是不包含。
  2. 字符串实例的 charCodeAt() 方法和 String 构造函数本身的 fromCharCode() 方法互为逆向操作。
String 构造函数本身的属性和方法
String.fromCharCode(数字编码)返回指定 unicode 编码对应的字符

4 Math

方法名含义
Math.PI属性,得到圆周率
Math.abs()取绝对值
Math.sqrt()开平方
Math.pow()求次幂
Math.round()四舍五入取整
Math.floor()舍一取整
Math.ceil()进一取整
Math.max()取所有参数中最大的
Math.min()取所有参数中最小的
Math.random()取随机数,结果是0~1的小数 (0 是有一定的概率被随机到,但是 1 绝无可能)

取随机数的规则:

  1. 随机取 0 ~ n 之间的整数: Math.floor(Math.random() * (n + 1))
  2. 随机取 s ~ n 之间的整数: Math.floor(Math.random() * (n - s + 1)) + s

5 Date

创建 date 日期时间对象

var today = new Date();
var birthday = new Date('December 17, 1995 03:24:00');
var birthday = new Date('1995-12-17T03:24:00');
var birthday = new Date(1995, 11, 17);
var birthday = new Date(1995, 11, 17, 3, 24, 0);

Date 实例的属性和方法
getFullYear()日期时间对象中所包含的年
getMonth()日期时间对象中所包含的月,取值范围 0 ~ 11
getDate()日期时间对象中所包含的日(每月几号)
getDay()日期时间对象中所包含的星期几
getHours()日期时间对象中所包含的小时
getMinutes()日期时间对象中所包含的分钟
getSeconds()日期时间对象中所包含的秒
getMilliseconds()日期时间对象中所包含的毫秒
getUTC...获取零时区的年月日时分秒
getTime()获取日期时间对象距离1970年1月1日0时0分0秒的毫秒数(时间戳)
set ...设置日期时间对象的年月日时分秒
setUTC...设置日期时间对象零时区的年月日时分秒
setTime()用时间戳的形式设置日期时间对象

Date 构造函数本身的属性和方法
Date.now()此时此刻的时间戳
Date.UTC()指定日期时间的时间戳,需要设置 6 个参数。

6 Array

数组实例的访问器方法

访问器方法指的是调用该方法,对象本身不会被修改,计算结果会以方法的返回值给出。一般对象中只有访问器方法。

方法名含义
concat(value1[, value2[, ...[, valueN]]])合并数组,返回合并好的数组;参数可以指定一到多个。
slice([begin[, end]])截取数组,返回截取后的数组;第一个参数指定开始截取的位置,第二个参数结束位置(不指定,截取到结尾)
join([separator])把数组合并成字符串,返回字符串;可以指定一个分隔符(默认是逗号

数组实例的修改器方法

修改器方法指的是,调用该方法,对象本身会被修改。只有数组对象才有修改器方法。

方法名含义
push(e lement1,...,elementN)在数组的后面添加一个或多个元素,返回添加了新数组之后数组的长度
unshfit(element1,...,elementN)在数组的前面添加一个或多个元素,返回添加了新数组之后数组的长度
pop()删除数组最后一个元素,返回被删的元素。
shift()删除数组的第一个元素,并返回这个元素。
splice(start[, deleteCount[, item1[, item2[, ...]]]])给数组添加、删除或替换元素。 返回数组,数组中是被删掉的元素(如果没删元素就是空数组)
sort([compareFunction])对数组进行排序。返回排好序的数组。
reverse()翻转数组。返回翻转之后的数组。

ES5 增加的数组实例的方法

这些方法也都是访问器方法!都需要回调函数作为参数。

方法名含义
forEach(callback[,thisArg])用于数组遍历,需要一个回调函数作为参数
filter(callback[,thisArg])从原数组中提取满足条件的元素组成新数组并返回,需要一个回调函数作为参数;
map(callback[,thisArg])返回一个元素个数与原数组一样的新数组,需要一个回调函数作为参数
回调函数返回什么,新数组的元素就是什么。
every(callback[,thisArg])返回布尔值,要求每个元素都满足条件才得到true,需要一个回调函数作为参数。
some(callback[,thisArg])返回布尔值,只要一个元素满足条件就可以true,需要一个回调函数作为参数。
reduce(callback[,initialValue])用于综合计算,需要一个回调函数作为参数,第二个参数指定初始值
回调函数接收三个参数:①上一次回调的结果 ②当前的元素 ③ 索引
indexOf(searchElement[,fromIndex])返回指定元素在数组中第一次出现的位置。
lastIndexOf(searchElement[,fromIndex])返回指定元素在数组中最后一次出现的位置。

第二部分 BOM/DOM

1 BOM概念

1.1 什么是BOM?

BOM 全称 Browser Object Model,译为浏览器对象模型。

BOM 是浏览器为 JavaScript 提供的能够对浏览器进行相关操作的 API。

1.2 BOM的作用

1)弹出新浏览器窗口的能力。

2)移动、关闭和更改浏览器窗口大小的能力。

3)可提供WEB浏览器详细信息的导航对象。

4)可提供浏览器载入页面详细信息的本地对象。

5)可提供用户屏幕分辨率详细信息的屏幕对象;

6)支持Cookies。

2 内置构造函数Function

Function的实例的属性和方法:
length获取函数所需函数的个数
call(obj,1,2)调用函数并指定函数内this的指向,第二个参数以及后面的参数都是调用call的函数的参数
apply(obj,[1,2])调用函数并指定函数内this的指向,第二个参数是数组,数组的每个元素是调用call的函数的参数
bind()返回一个修改了this指向的新函数。

1. call 和 apply 的区别?

传参方式不同。

2. call 和 bind 区别?

call() 会把函数调用了,bind() 返回新函数(不会调用)

call()改过this的指向后,会再执行函数,bind()改过this后,不执行函数,会返回一个绑定新this的函数

相同点:都是用来改变this的指向。

3 BOM

3.1 window

① 弹框

// 警告弹框   方法没有返回值
alert('警告框');

// 确认弹框   方法返回布尔值
var res = confirm('你确定吗?');

// 输入弹框   方法返回输入的内容
prompt('请输入您的银行卡密码:');

② 打开新窗口

// 新窗口打开页面
window.open('页面地址');

// 打开空白的新窗口
window.open('');

// 在窗口打开新页面
window.open('页面地址', window.name);

// 打开新窗口并指定窗口的宽高
window.open('页面地址', '', 'width=600,height=400');

// 关闭窗口  只有 open 打开的窗口,chrome才允许使用 close 关闭。
window.close()

// 调用浏览器的打印机接口
window.print();

③ 页面滚动

// 滚动到指定位置(页面顶部)
window.scrollTo(0, 0);

// 滚动到指定位置(页面顶部)带有效果
window.scrollTo({
    top: 0,
    behavior: 'smooth'
});

// 指定滚动多少距离  向右滚动100px,向下滚200px
window.scrollBy(100, 200);

// 指定滚动多少距离,带有效果
window.scrollBy({
    left:0, 
    top: 200,
    behavior: 'smooth'
});

④ 定时器

多次定时

// 开启多次定时
setInterval(function() {
    console.log('hello 芳芳');
}, 2000);


// 开启定时器
setInterval(run, 3000);
function run() {
    var r = Math.floor(Math.random() * 256);
    var g = Math.floor(Math.random() * 256);
    var b = Math.floor(Math.random() * 256);
    document.body.style.backgroundColor = 'rgb('+r+','+g+','+b+')';
}


// 开启定时器 返回定时器标识
var intervalId = setInterval(function() {
    // 数字累减
    num --;
    // 设置 box 元素中的内容
    boxNode.innerHTML = num;
    // 判断如果 num 值变为 0,停止定时器
    if (num <= 0) {
        clearInterval(intervalId);
    }
}, 1000);

单次定时:

// 开启单次定时
setTimeout(function() {
   
}, 2000);

// 开启单次定时,返回定时器标识符
var timeoutId = setTimeout(runTime, 2000);
function runTime() {
    
}

// 停止单次定时, 指定定时器标识符
clearTimeout(timeoutId)

⑤ window 对象属性和方法总结

属性:
name设置或获取本窗口的名字
length获取子窗口的数量(一个iframe就是一个子窗口)
innerWidth获取视口的宽度
innerHeight获取视口的高度
document
locationlocation 是最有用的 BOM 对象之一,它提供了与当前窗口中加载的文档有关的信息,还提供了一些导航功能。location 对象的用处不只表现在它保存着当前文档的信息,还表现在它将 URL 解析为独立的片段,让开发人员可以通过不同的属性访问这些片段。
historyhistory 对象提供了操作浏览器会话历史(浏览器地址栏中访问的页面,以及当前页面中通过框架加载的页面)的接口。
navigator
screen
方法:
alert()警告框
confirm()确认框
prompt()输入框
open()打开新窗口
close()关闭窗口
print()调用打印接口
scrollTo()滚动到指定位置
scrollBy()滚动多少距离
setInterval()开启多次定时
clear Interval()停止多次定时
setTimeout()开启单次定时
clearTimeout()停止单次定时

3.2 history

history 对象 描述本窗口的历史记录。
history.length属性,获取本窗口历史记录的条数
history.back()跳转到历史记录中的上一页
history.forward()跳转到历史记录中的下一页
history.go(n)跳转到前n页或后n页,n是数字,正数是后n页,负数是前n页

3.3 location

location 对象描述当前窗口的地址(URL)信息。

属性:
location.href获取或设置本窗口完整的URL信息
location.protocol获取或设置URL中的协议信息
location.hostname获取或设置URL中的主机名
location.port获取或设置URL中的端口号
location.host获取或设置URL中的主机名+端口号
location.pathname获取或设置URL中的路径布局
location.search获取或设置URL中的搜索字符串
location.hash获取或设置URL中的锚点部分
方法:
location.reload()刷新本页面
location.assign(url)跳转到指定页面
location.replace(url)跳转到指定页面,会把原页面在历史记录中删除

3.4 navigator

navigator 对象用于描述浏览器的版本信息、内核信息以及操作系统的部分信息。

userAgent		获取客户端浏览器的信息

3.5 screen

screen 对象描述屏幕相关的信息。

width	获取屏幕的宽度
height  获取屏幕的高度
var name = 'window';
function func(){
    console.log(this.name);
}
var obj1 = {name: 'obj1'};
var obj2 = {name: 'obj2'};

obj1.say = func;
obj1.say.call(obj2);   // obj2

2. 有如下数组,按照要求操作

var products = [
    {
        "title":"坦克",
        "des":"125毫米主炮",
        "price": 998
    },
    {
        "title":"飞机",
        "des":"携带10枚导弹",
        "price": 1998
    },
    {
        "title":"航空母舰",
        "des":"携带30架飞机",
        "price": 2998
    }
]
1. 取出价格大于等于 1000 的商品 (数组的 filter 方法)
   products.filter(function(item) {
   		return item.price >= 1000;
   });

2. 每个商品的价格加 200,返回新的数组 (数组的 map 方法)
   produces.map(function(item) {
   		item.price += 200;
   		return item;
   });

3. location.assign()location.replace() 两个方法的区别?

location.assign()  跳转到新地址之后,原页面保留历史记录中
location.replace() 跳转到新地址之后,原页面不会保留在历史记录中

DOM概述

1 DOM介绍

MDN 文档对象模型手册: developer.mozilla.org/zh-CN/docs/…

1)DOM 英文全称“Document Object Model”,译为“文档对象模型”。

2)DOM 是一个与平台和编程语言无关的接口,通过这个接口程序和脚本可以动态的访问和修改文档的内容、结构和样式。

2 Node 节点

2.1 五大节点类型

document 	文档节点 nodeType:9
element		元素节点 nodeType:1  nodeName 获取元素的标签名
attribute 属性节点 nodeType:2
text 			文本节点 nodeType:3
comment 	注释节点 nodeType:8

2.2 节点的属性

nodeName		获取节点的名字
nodeValue		获取节点的值
nodeType		获取节点的类型,用数字表示

DOM操作

1 获取元素

① 通过 ID 名

document.getElementById('ID名');

总结:

  1. 方法返回一个元素对象。
  2. 如果没有满足条件的元素返回 null。

② 通过标签名

// 从文档中获取
document.getElementsByTagName('标签名');

// 从元素的后代中获取
element.getElementsByTagName('标签名');

总结:

  1. 方法返回一个 HTMLCollection 类型的对象,这是一个伪数组对象,里面的成员是元素对象。
  2. 如果没有满足条件的元素,返回空的集合。

③ 通过类名(了解,IE8 + 支持)

// 从文档中获取
document.getElementByClassName('类名');

// 从元素的后代中获取
element.getElementsByClassName('类名');

总结:

  1. 方法返回一个 HTMLCollection 类型的对象,这是一个伪数组对象,里面的成员是元素对象。
  2. 如果没有满足条件的元素,返回空的集合。

④ 通过 name 属性值 (了解)

// 从文档中获取
document.getElementsByName('name属性的值');

总结:

  1. 方法返回一个 NodeList 类型的对象,与 HTMLCollection 相似,也是伪数组,里面的元素是元素对象。
  2. 如果没有满足条件的元素,返回空集合。

⑤ 使用选择器获取元素 (推荐)

// 从文档中获取
document.querySelector('选择器');
document.querySelectorAll('选择器');

// 从元素的后代中获取
element.querySelector('选择器');
element.querySelectorAll('选择器');

总结:

  1. querySelector() 方法返回一个元素对象,如果满足条件的元素有多个只取第一个,如果没有满足条件的元素返回 null。
  2. querySeleorAll() 方法返回 NodeList 集合,如果没有满足条件的元素返回空集合。

⑥ 获取所有的元素

document.all

总结: 获取文档中所有的元素,是个 HTMLCollection 类型的集合

**注意:**使用 document.all 可以判断IE还是非IE,这是一个语法糖。

if (document.all) {
 document.write('我是IE浏览器!');
} else {
 document.write('我不是IE');
}

2 文档结构(元素关系)

2.1 节点树

childNodes			获取所有子节点的集合,是个 NodeList 类型的对象
firstChild			获取第一个子节点
lastChild				获取最后一个子节点

previousSibling	获取紧邻在前面的兄弟节点
nextSibling			获取紧邻在后面的兄弟节点

parentNode			获取父节点

2.2 元素树

children						获取所有子元素的集合,是个HTMLCollection 										类型的数据
firstElementChild			获取第一个子元素
lastElementChild			获取最后一个子元素

previousElementSibling	获取紧邻在前面的兄弟元素
nextElementSibling			获取紧邻在后面的兄弟元素

parentElement				获取父元素

3 元素的属性操作

3.1 读写内置属性

元素.属性名;

3.2 读写自定义属性

元素.getAttribute('属性名')		 读取属性的值,如果不存在概述返回 nulll
元素.setAttribute('属性名', '值')  设置属性的值,如果属性不存在会添加该属性

注意:

  1. getAttribute() 和 setAttribute() 可以操作写在标签上的所有属性,不论内置的还是自定义的。
  2. 如果要操作的属性是内置,使用 .属性名 的方式;如果要操作的属性是自定义的再使用 getAttribute() 和 setAttribute()。

3.3 data-* 形式的自定义属性

HTML5标准建议标签中的自定义属性都是 data-messagedata-group-name 这样的形式,此种形式的自定义属性,DOM 提供了快捷的操作方式:

元素.dataset.属性名;  // 可读可写

imgEle.dataset.message;  // 对应的是 data-messag 属性
imgEle.dataset.gorupName; // 对应的是 data-group-name 属性

1、 属性 childNodes 和 属性 children 有什么区别?

childNodes:获取所有的子节点
children:获取所有的子元素

2、 总结一下 JS 从 HTML 文档中获取到某个元素有哪些方式? (至少写三种)

1. 通过 ID 名
   document.getElementById()
   
2. 通过标签名
   document.getElementsByTagName()
   element.getElementsByTagName()
   
3. 通过类名
   document.getElementsByClassName()
   element.getElementsByClassName()
   
4. 通过 name 值
   document.getElementsByName();
   
5. 使用选择器获取元素
   document.querySelector()
   element.querySelector()
   document.querySelectorAll()
   element.querySelectorAll()
   
6. document.all  获取所有的元素

3、 BOM 对象有哪一些?(5个)

window
history
location
navigator
screen

4、 JS 中如何实现页面跳转(至少写两种)

1. window.open()

2. history.back() / history.forward() / history.go()

3. location.assign() / location.replace()
   修改 location 的属性: href 、 protocol、host、hostname、port、pathname

5、DOM 中有哪几种类型的节点?

1, document		nodeType: 9
2. element      nodeType: 1
3. attribute	nodeType: 2
3. text			nodeTYpe: 3
4. coment		nodeType: 8

4 元素的CSS样式操作

4.1 读写行内样式

元素.style.属性名;

注意:

  1. 该方式只能读取设置在行内的样式,设置也是把样式设置到行内。
  2. 如果CSS属性名中有 - 会自动映射为小驼峰的形式,如果 backgorund-color 映射为 backgroundColor

4.2 读取计算样式

getComputedStyle(元素).属性名;
 /**
  * 封装读取计算样式的函数
 */
function getStyle(ele, attr) {
    // 判断
    if (window.getComputedStyle) {
        // IE9 + 以及其他非 IE
        return getComputedStyle(ele)[attr];
    } else if (ele.currentStyle) {
        // IE8 以及以下
        return ele.currentStyle[attr];
    }
}

**注意:**该方式只能读取。

4.3 通过设置元素的类名操作样式

① className

元素.className;  // 可读可写

注意: dom 对象中的 className 属性与 标签中的 class 属性对应。

② classList

元素.classList;   // 得到描述元素类名的对象,是所有类名组成的集合,是个伪数组

// classList 对象具有以下三个方法:
元素.classList.add('类名');   // 给元素添加一个类名
元素.classList.remove('类名');  // 删除一个类名
元素.classList.toggle('类名');   // 类名切换(如果元素有该类名就删除,没有该类名就添加)

5 读写元素的文本内容(可读可写)

innerHTML		读写元素中所有的内容(包括内部的html标签)
outerHTML		读写元素自己在内的所有代码内容(包括自己的标签代码)
innerText		读写元素内的文内容(不包括html标签)(只保留换行)
textContent	读写元素内的文内容(不包括html标签),保留原格式(缩进和换行)

6 元素尺寸位置

6.1 读取元素的尺寸(只读)

offsetWidth盒子的宽度(内容宽+左右内边距+左右边框宽)
offsetHeight盒子的高度(内容高+上下内边距+上下边框宽)
clientWidth宽度(内容宽+左右内边距)
clientHeight高度(内容高+上下内边距)
scrollWidth如果内容没有溢出与clientWidth一致,如果内容溢会加上溢出的内容的宽度
scrollHeight如果内容没有溢出与clientHeight一致,如果内容溢会加上溢出的内容的高度
getBoundingClientRect()返回一个对象,对象中有 width 和 height 属性,获取盒子的尺寸,与 offsetWidth/offsetHeight 是一致的

如何获取视口的尺寸?

// 第一种方式  会包括滚动条本身的宽度
window.innerWidth;
window.innerHeight;

// 第二种方式 不会包括滚动条本身的宽度,推荐
document.documentElement.clientWidth
document.documentElement.clientHeight

6.2 读取元素的位置 (只读)

client*系列
clientLeft元素左边框的宽度
clientTop元素上边框的宽度
clientX鼠标位置距离当前浏览器窗口可视区域的x坐标
clientY鼠标位置距离当前浏览器窗口可视区域的y坐标
offset*系列
offsetLeft元素在第一个定位的祖先元素上的位置,没有定位的祖先元素,参照整个页面
offsetTop元素在第一个定位的祖先元素上的位置,没有定位的祖先元素,参照整个页面
offsetX/Y鼠标位置相对于事件源的XY坐标
getBoundingClientRect()返回一个对象,对象中有如下属性读取元素的位置
left(x)元素在视口上的位置,水平坐标
top(y)元素在视口上的位置,垂直坐标
right元素右下角的水平坐标
bottom元素右下角的垂直坐标

6.3 内容位置(可读可写)

scrollLeft值变大,内容向左滚动(滚动滑块向右)
scrollTop值变大,内容向上滚动(滚动滑块向下)

总结

1. 元素的样式操作
   1.1 读写行内样式
   1.2 读取计算样式
   1.3 操作元素的类名设置样式  className classList
   
2. 读写元素中的文本内容 (可读可写)
	innerHTML
	outerHTML
	innerText
	textContent
	
3. 读取元素的尺寸(只读)
	offsetWidth / offsetHeight
	clientWidth / clientHeight
	scrollWidth / scrollheight
	getBoundingClientRect()
	
4. 读取元素的位置(只读)
	offsetLeft / offsetTop
	clientLeft / clientTop
	getBoundingClientRect()
	
5. 读写元素中内容的位置(可读可写)
	scrollleft / scrollTop
	

1. offsetWidth 、clientWidth、scrollWidth 之间有什么区别?

offsetWidth		盒子的宽度(内容+内边距+边框)
clientWidth		内容+内边距
scrollWidth		clientWidth + 溢出内容

2. 如何获取元素在视口中的位置?

元素.getBoundingClientRect().left
元素.getBoundingClientRect().top

3. 如何获取元素的计算样式?

getComputedStyle(元素).属性名

4. 根据要求写代码

1. 获取一个类名为 item 的元素
   var item = document.querySelector('.item');


2. 给前面获取的元素添加一个类名 current (注意原来的类名也要保留)
   item.classList.add('current');

5. BOM 和 DOM 有什么区别?

BOM:	浏览器对象模型
DOM:	文档对象模型

7 节点(元素)的创建添加删除替换克隆

7.1 创建元素节点

document.createElement('标签名'); //返回创建好的元素

**注意:**新创建的元素并不在文档结构中!

7.2 添加子节点

① 在最后面添加子节点

父元素.appendChild(新节点);

② 指定位置添加子节点

父元素.insertBefore(新节点,旧节点)

**注意:**新的节点会在旧的前面!

7.3 删除子节点

父元素.removeChild(要删除的节点);

7.4 替换子节点

父元素.replaceChild(新节点,旧节点);

7.5节点克隆

元素.cloneNode(true

注意:方法的参数默认是false,只克隆自己,后代和内容不克隆;设置参数为true,后代内容都克隆。

8 documnt 对象

属性
lastModified获取文档最后一次修改时间,只读
cookie读写cookie信息,可读可写
all获取文档中所有的元素,HTMLCollection,只读
documentElement获取html元素,只读
body获取body元素,只读
head获取head元素,只读
title读写页面标题,可读可写
方法
write()把内容写入文档

9 documentFragment 对象

9.1 创建 documentFragment 对象

document.createDocumentFragment();

9.2 documentFragment 对象的特点

1)documentFragment 也是一类节点, nodeType 是 11,并不是元素对象,它通常作为其他节点的一个临时的容器。

2)documentFragment 不是真实 DOM 树的一部分,它的变化不会触发 DOM 树的重新渲染,且不会导致性能等问题。

9.3 documentFragment 对象的应用

1)如果连续给一个元素添加多个子元素,可以先把子元素添加到 documentFragment 对象中,最后把 documentFragment 对象添加到父元素中,减少浏览器渲染次数。

2)利用 documentFragment 实现对一组元素进行倒序排列。

第四章 HTML DOM

1 表相关的元素

1.1 表单相关元素

① form 元素

submit()		调用该方法,让表单提交
reset()			调用该方法,让表单重置

② 文本输入框和文本域(input 和 textarea)

blur()		失去焦点
focus()		获取焦点
select()	选中里面的文本

③ select 元素

属性:
length		下拉选项的数量(可读可写)
options		获取所有下拉选项的集合
selectedIndex	获取所选的下拉选项的索引
value			获取所选的下拉选项的value值

方法:
add()		添加一个下拉选项,参数是option元素
remove()	删除一个下拉选项,参数是要删除的下拉选项的索引
blur()		失去焦点
focus()		获取焦点

注意:

new Option() 可以创建一个 option 元素, Option() 构造函数的第一个参数指定下拉选项的文本内容,第二个参数指定下拉选项的value值。

1.2 表格相关元素

① table 元素

属性:
rows		获取表格中所有行元素的集合
cells		获取表格中所有单元格元素的集合

方法:
insertRow()	插入一行
deleteRow()	删除一行

② tableRow 元素(tr 元素)

属性:
cells		获取本行内所有的单元格元素的集合
rowIndex	本行的索引

方法:
insertCell()	插入一个单元格
deleteCell()	删除一个单元格

③ tableCell 元素 (td 或 th)

属性:
cellIndex	获取本单元格在行内的索引

1.3 快速创建 img 元素

new Image();  // 相当于 document.createElement('img')

第五章事件

1 事件监听

① 给元素监听事件的三种方式

第一种方式:把事件作为 HTML 标签的属性:

<button onclick="代码"></button>

第二种方式: 把事件作为元素对象的方法:

element.onclick = function() {
    
}

第三种方式: 使用 addEventListener 方法:

element.addEventListener('click', function() {
    
});

总结: addEventListener 方式可以给同一个事件绑定多个回调函数!

② 解除事件的监听

如果是第一种方式和第二种方式监听的事件:

// 重写写一遍事件监听,把前面的覆盖掉; 使用 null 覆盖原来的函数
element.onclick = null;

如果是 addEventListener() 方法监听的事件:

element.removeEventListener('click', 回调函数名);

注意: 使用 addEventListener 监听事件,如果以后需要解除监听,回调函数不能使用匿名的。

2 事件流

事件的触发会经历以下三个阶段:

捕获阶段: 从 window、document 一层一层到目标元素 (具体发生了事件动作的元素)

目标阶段: 获取目标元素,标志着捕获阶段的结束,冒泡阶段的开始。

冒泡阶段: 从目标元素开始,层层向上,一直到 document、window。

**注意:**事件默认在冒泡阶段触发(回调函数执行)。

注意: addEventListener 方法的第三个参数设置 true,可以设置事件在捕获阶段触发。

1 添加子节点的方法是什么?

appendChild()
insertBefore()

2 删除子节点的方法是什么?

removeChild()

3 cloneNode() 的参数 true 和 false 有什么区别?

false: 只克隆本身,不克隆内容和后代节点
true: 克隆本身、内容以及后代节点

4 如何创建一个新元素?

document.createElement()

5. 事件触发的过程分为哪几个阶段,事件在哪个阶段触发?

捕获阶段
目标阶段
冒泡阶段

事件在冒泡阶段触发

3 事件回调函数中 this 的指向

函数中 this 指向的规则: this 指向调用该函数的对象。

② 事件回调函数中的 this 指向监听了该事件的元素对象, 事件的回调函数由监听了事件的元素调用。

4 常用事件总结

4.1 鼠标事件

click单击
dblclick双击
contextmenu右击
mouseover鼠标悬停在元素上, 建议用 mouseenter 代替
mouseout鼠标离开元素,建议用 mouseleave 代替
mouseenter鼠标悬停在元素上
mouseleave鼠标离开元素
mousedown鼠标按键按下
mouseup鼠标按键抬起
mousemove鼠标移动

4.2 键盘事件

keydown键盘按键按下
keyup键盘按键抬起
keypress键盘按键按下,用于可输入字符按键

1. 哪些元素可以监听键盘事件?

① document

② 可以获取焦点的元素(表单控件,尤其是可输入的元素)

2. keydown 和 keypress 的区别?

① keydown 所有的按键按下都可以触发,无法区分大小写按键。

② keypress 只有可输入字符按键按下才可以触发,可以区分大小写按键。

3. 如何获取按下的是哪个按键?

使用 event 对象中的属性:

  • evnet.keyCode 获取按键对应的 ascii 值

  • event.which 同 keyCode

  • event.key 获取按键的字符值。

4.3 文档事件

load页面中所有的一切加载完毕就会触发,可以监听到window上或者body元素
DOMContentLoaded页面中所有的元素加载完毕就会触发,可以监听在window或者document上, 只能使用 addEventListener 监听事件
beforeunload当关闭网页的时候触发

load 事件与 DOMContentLoaded 事件的区别:

① load 事件是页面中所有的一切加载完毕才能触发,包括元素以及外部资源。

② DOMContentLoaded 事件是页面中所有的元素加载完毕就可以触发,不包括外部资源。

4.4 表单事件

submit当表单提交的时候触发,该事件监听到form元素
reset当表单重置的时候触发,该事件监听到form元素
focus当表单控件获取焦点的时候触发
blur当表控件单失去焦点的时候触发
select输入框或文本域中的内容被选中
change对于输入框,内容改变且失去焦点才会触发;适合用于select
1. this 的指向
2. 鼠标事件: click dblclick contextmenu  mouseenter  mouseleave  mousedown mouseup mousemove
3. 键盘事件: keydown  keyup keypress
4. 文档事件: load  DOMContentLoaded  beforeunload
5. 表单事件: submit reset blur focus  select change
6. 案例: 元素拖拽、keyup案例、blur案例、地址联动案例


作业:
1. 键盘控制元素移动
2. 轮播图

4.5 图片事件

load		图片文件下载完毕
error		图片加载失败

4.6 其他事件

resize		监听到 window上,视口大小发生改变
scroll		监听到window或者是具有滚动体的元素,页面或元素中的内容发生滚动就触发。

5 Event 对象

5.1 获取 Event 对象

给事件的回调函数设置第一个形参,就可以获取 event 对象。

不同类型的事件获取的 Event 对象类型也不同。

5.2 鼠标事件对象 MouseEvent 的属性和方法

offsetX / offsetY		获取鼠标在目标元素上的坐标位置
clientX / clientY		获取鼠标在视口上的坐标位置
pageX / pageY			获取鼠标在页面上的坐标位置
screenX / screenY       获取鼠标在屏幕上的坐标位置
button					获取按的是哪个鼠标按键, 0:左键; 1:中间键; 2:右键

5.3 键盘事件对象 KeyBorardEvent 的属性和方法

keyCode		获取按键对应的编码值
which		同 keyCode
key			获取按键对应的字符值

5.4 所有类型的事件对象都有的属性和方法

type				获取事件名
timeStamp			获取触发事件时距离打开页面时的毫秒数
target				获取目标元素
stopPropagation()	阻止事件冒泡
preventDefault()	阻止浏览器默认行为

5.5 阻止事件冒泡

在事件的回调函数中执行 event.stopPropagation(),就可以阻止冒泡。

5.6 浏览器的默认行为

① 浏览器有哪些默认行为

超链接点击跳转
表单的提交和重置
右键弹出系统菜单
等...

② 阻止浏览器默认行为

在事件的回调函数中调用 event.preventDefault() 即可阻止默认行为。

注意: 如果使用第二种方式监听事件,在回调函数中 return false 同样可以阻止默认行为。

6 事件委托

事件监听到祖先元素上,判断目标元素,如果目标元素满足条件,就执行相关操作。

事件委托的优势:

  1. 对于给大量的元素监听相同的事件,使用事件委托比遍历挨个监听效率更好。
  2. 利用事件委托可以让新增的元素也可以响应事件。

7 DOM 对象深入分析

7.1 元素对象的原型链关系

HTMLDivElement类型  ->  HTMLElement类型 -> Element类型 -> EventTarget类型 -> Object 类型 -> Object.prototype

7.2 事件对象的原型链关系

MouseEvent类型 -> UIEvent类型 -> Event类型 -> Object类型 -> Object.prototype

7.3 HTMLCollection 和 NodeList 的区别

① HTMLCollection 对象

  • getElementsByTagName()、getElementsByClassName()、document.all、children 等方式方法可以获取到 HTMLCollection 类型的对象
  • HTMLCollection 类型的对象中的成绩必须都是元素节点。
  • 该对象没有 forEach 方法
  • HTMLCollection 对象是动态的,可以实时更新集合中的元素。

② NodeList

  • getElementsByName()、querySelectAll()、childNodes 等方式方法可以获取 NodeList 类型的对象
  • NodeList 类型的对象中的元素可以是任意类型的节点(元素、文本节点、注释节点、document 等)
  • 该对象有 forEach 方法
  • NodeList 对象是静态的对象。
  1. 获取了一个元素对象赋值给了变量 box,根据要求获取以下信息
① 获取元素在整个页面中(根元素)的位置 (注:该元素的祖先元素没有定位的)
  box.offsetLeft
  box.offsetTop


② 获取元素在视口中的位置
  box.getBoundingClientRect().left
  box.getBoundingClientRect().top


③ 获取元素的宽度和高度(包括内容、内边距和边框)
  box.offsetWidth
  box.offsetHeight
  1. 根据要求获取以下信息
① 获取视口的宽度和高度
  document.documentElement.clientWidth
  document.documentElement.clientHeight


② 获取页面内容向上滚动的距离 (滚动条向下滚动的距离)
  document.documentElement.scrollTop || document.body.scrollTop

  1. 在一个鼠标事件中获取了事件对象,赋值给了变量 event,根据要求获取以下信息
① 获取鼠标在视口上的位置
  event.clientX
  event.clientY

② 获取鼠标在整个页面(根元素)上的位置
  event.pageX
  event.pageY

③ 获取鼠标在屏幕上的位置
  event.screenX
  event.screenY


④ 获取鼠标在目标元素上的位置
  event.offsetX
  event.offsetY

第三部分 JS 高级

1 JavaScript 中的垃圾回收机制(GC)

1.1 垃圾回收相关概念

① 什么是垃圾

没有被使用(引用)的对象就是垃圾

② 什么是垃圾回收

没有被引用的对象被销毁,内存被释放,就是垃圾回收

C、C++ 等编程语言需要手动垃圾回收。

Java、JavaScript、PHP、Python 等变成语言自动垃圾回收。

③ 变量的生命周期(何时会被回收)

全局变量: 整个脚本执行完毕,全局变量就被销毁。

局部变量: 函数调用完毕,局部变量就被销毁。

④ 垃圾没有及时回收的后果

没有被及时回收的垃圾会常驻内存,造成内存泄漏

⑤ 垃圾回收的常见算法

- 引用计数
- 标记清除

1.2 引用计数

① 原理

- ① 每个对象都有一个引用标记,记录引用次数
- ② 如果对对象进行引用,引用标记+1
- ③ 如果取消了对对象的引用,引用标记-1
- ④ 当对象的引用标记次数为 0,就变为垃圾对象,会立即被清除。

② 优缺点:

- 优点: 垃圾对象清除地非常及时
- 缺点: 如果两个对象互相引用,会造成两个对象常驻内存,造成内存泄漏

1.3 标记清除

① 原理

浏览器会不停地进行垃圾回收的循环,每次循环经历两个阶段:
- 标记阶段:
  从根对象开始,一层一层向下找,对所有的能找到的对象进行标记,有标记的对象成为可到达对象,没有标记的对象就是不可到达对象,也就是垃圾对象。
- 清除阶段
  线性变量内存中所有的对象,如果对象没有标记就作为垃圾被清除。
  清除完垃圾之后,去掉所有对象的标记,继续进行下一轮的标记清除。

② 优缺点

- 优点:不会造成内存泄漏
- 缺点:需要进行深度递归遍历,垃圾回收不如引用计数方式更及时。

2 执行上下文和执行栈

2.1 执行上下文

① 全局执行上下文

1. 全局代码正式执行之前,创建全局执行上下文对象,赋值给 window2. 对全局的数据进行预处理
   ① 所有使用 var 声明的全局变量进行提升,添加为 window 的属性。
   
   ② 所有使用 function 关键字声明的函数进行提升,添加为 window 的方法。
   
   ③ 给 this 赋值为 window

3. 开始执行全局代码

② 函数内的执行上下文

1. 当函数被调用的时候,函数体内的代码执行之前,先创建函数的执行上下文对象

2. 对局部的数据进行预处理
   ① 给形参赋值,把形参添加为执行上下文对象的属性。
   
   ② 给 arguments 赋值,添加为执行上下文对象的属性。
   
   ③ 对函数内使用 var 声明的变量进行提升,添加为执行上下文对象的属性。
   
   ④ 对函数内使用 function 关键字声明的函数进行提升,添加为执行上下文对象的方法。
   
   ⑤ 给 this 赋值为调用该函数的对象(this不是执行上下文对象)

3. 开始执行函数体的代码

注意: 函数每调用一次,就创建一个执行上下文对象!

2.2 执行栈

执行栈也叫调用栈,用于存储代码执行过程中产生的执行上下文对象。

使用经典的数据存储结构,特点是先进后出、后进先出。

数据进入栈成为压栈进栈, 数据移除栈(销毁)称为出栈

1. 代码执行之前,创建一个执行栈对执行上下文对象进行管理。

2. 全局执行上下文对象创建之后,进入执行栈(进栈)

3. 当调用函数的时候,会创建本次函数调用的执行上下文对象,进入执行栈(进栈)

4. 函数调用结束,本次函数调用的执行上下文对象被销毁(出栈)

5. 最后,栈中只剩全局执行上下文对象,待所有代码执行完毕,出栈。

2.3 作用域和执行上下文的关系

区别:
1. 作用域是静态的,变量的作用域只与所在函数声明的位置有关,无在哪里调用无关。

2. 执行上下文对象是动态的,没调用一次函数,就创建一个执行上下文对象。

联系:
执行上下文对象也属于所在的所用域
全局执行上下文 -> 全局作用域
函数的执行上下文 -> 函数的局部作用域

3 闭包

3.1 什么是闭包?

1)简单讲,闭包就是指有权访问另一个函数作用域中的数据。
2)MDN 上面这么说:闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

3.2 如何产生闭包

 // 声明函数 A
function A() {
    // 定义局部变量
    var a = 100;
    var b = 200;

    // 定义函数 B
    function B() {
        return a + b;
    }

    // 把函数返回
    return B;
}

// 调用函数 A 并获取返回值
var fn = A();

// 调用函数 fn
console.log(fn()); //300
1. 有函数A,函数A中有自己作用域的数据, 在函数A里面定义函数B
2. 函数B中使用函数A里面的数据
3. 函数B作为函数A的返回值,或者让函数B被其他方式引用(作为事件的回调函数、赋值给全局变量)

3.3 闭包和作用域

1. 函数(函数B)可以访问上层作用域中(函数A)的数据
2. 变量的作用域只与函数声明的位置有关系

3.4 闭包和垃圾回收

1. 闭包导致局部变量除了被自己的执行上下文对象引用,还被函数以外的其他函数所引用
2. 函数调用结束,局部也不会变为垃圾对象
3. 总结: 闭包延长了局部变量的生命周期!

3.5 闭包的缺点

闭包延长了局部变量的声明周期,局部变量常驻内存,有内存泄漏的风险。

4 对象高级

4.1 原型链总结

// 定义构造函数
function Foo() {
    
}
// 实例化 Foo
var f1 = new Foo();
var f2 = new Foo();

// 实例化 Object
var o1 = new Object();
var o2 = new Object();
f1、f2 的构造函数是 Foo
f1、f2 的原型是 Foo.prototype
Foo.prototype 的构造函数是 Object
Foo.prototype.constructor === Foo

o1、o2 的构造函数是 Object
o1、o2 的原型是 Object.prototype

FooObject 的构造函数是 Function
FooObject 的原型是 Function.prototype


Function 的构造函数是 Function

Foo.protype === f1.__proto__
Foo.__proto__ === Function.prototype

Function.__proto__ === Function.prototype
Object.__proto__ !== Object.protype

f1、f2  -->  Foo.prototype  -->  Object.prototype
o1、o2  -->  Object.prototype
Foo  -->  Function.prototype -->  Object.prototype
Object --> Function.prototype --> Object.prototype
Function --> Function.prototype --> Object.prototype


Foo.prototype.constructor === Foo
Object.prototype.constructor === Object
Function.prototype.constructor === Function


Foo.prototype === f1.__proto__
Object.prototype === o1.__proto__
Function.prototype === Function.__proto__

4.2 面向对象继承

① 面向对象语言的继承规则

class User {
    // 定义实例的属性和方法
}

// 定义子类
class VipUser extends User {
    
}

// 定义子类
class NewUser extends User {
    
}

  1. 一个父类可以有多个子类,一个子类只能有一个父类
  2. 子类的实例不但拥有定义在子类上的属性好方法,还继承了定义在父类的属性和方法

② JS 中继承关系的特点

1. JS 中的对象基于原型继承: 对象可以使用原型上的属性和方法,原型链的都可以使用
2. 如果构造函数 A 实例的原型是构造函数 B 的实例,也可以认为构造函数 A 是构造函数 B 的子类
3. instanceof 对象自己的构造函数和对象原型的构造函数,以及原型链上的对象的构造函数,都可以成立
// 自定义构造函数
function User() {

}
User.prototype = [100,200];
// 实例化
var u = new User();

console.log(u instanceof User);  // true
console.log(u instanceof Array); // true
console.log(u instanceof Object); // true

③ 实现JS中构造函数和构造函数之间继承

1. 子类中,先把父类调用一遍,并修改其 this 指向
2. 设置子类的实例的原型是父类的一个实例
3. 设置子类的实例的constructor属性(非必要)
// 定义父类
function Person(name, height) {
    this.name = name;
    this.height = height;
}
Person.prototype.eat = function() {
    
}

// 定义子类
function Man(name, height, aaa) {
    // 调用父类,设置父类中 this 的指向
    Person.call(this, name, height);
    this.aaa = aaa;
}

// 设置 Man 实例的原型是 Person 的一个实例
Man.prototype = new Person();
// 指定 constructor 属性
Man.prototype.constructor = Man;
// 添加方法
Man.prototype.fun = function() {}


// 定义子类
function Girl(name, height, bbb) {
	// 把父类调用一遍
    Person.call(this, name, height);
    this.bbb = bbb;
}    

// 设置原型
Girl.prototype = new Person();
Girl.prototype.constructor = Girl;
// 添加方法
Girl.prototype.fn = function() {}

2.3 安全的类型检测

function getType(data) {
    var str = Object.prototype.toString.call(data)
    return str.slice(8, str.length - 1)
}

5 单线程和事件轮询机制

5.1 进程和线程

1. 什么是进程?
   进程是CPU进行资源调度和分配的基本单位
  
2. 什么是线程?
   线程是CPU进行运算的最小单位, 进程的运算也要基于线程
   
  
3. 进程和线程的关系
   - 进程中必须有一个主线程
   - 进程中可以同时运行多个线程,这样的程序叫多线程运行
   - 同一个进程的不同线程可以共享数据
   - 不同进程之间无法直接共享数据

补充:死锁

1、什么是死锁?

在两个以上的并发进程中,如果每个进程都持有某种资源而又等待其他进程释放他们持有的资源,在未改变这种状态前,谁都无法推进,则发生了死锁。所有进程无限期等待,且循环等待,都处于阻塞态。

2、死锁产生的四个必要条件

  1. 互斥。一个资源一次只能被一个进程占有
  2. 请求与保持。一个进程因为请求资源而阻塞时,不释放自己持有的资源
  3. 非剥夺。无法在进程结束前剥夺它对资源的所有权
  4. 循环等待。若干进程收尾相接形成环形等待关系

3、死锁处理策略

​ 1、预防死锁。破坏后三个条件中的一个即可(互斥是非共享设备的特性,无法更改): ​ (1)破坏请求与保持条件。规定一个进程开始前,必须申请所有需要的资源。 ​ (2)破坏非剥夺条件。当无法得到需要的资源时,释放自己持有的资源,等需要时再重新申请。 ​ (3)破坏循环等待条件。将所有资源按类型线性排队,并赋予不同的编号,所有进程请求资源时,必须按照资源递增顺序,以防出现环路。如果一个进程已经分配到了R资源,那么它再申请时,只能申请排在R后面的资源,而不能申请前面的资源。 ​ 2、死锁避免。避免死锁并不是事先采取某种限制措施破坏死锁的必要条件,而是再资源动态分配过程中,防止系统进入不安全状态,以避免发生死锁: ​ 银行家算法 ​ 系统安全状态 ​ 安全性算法 ​ 3、死锁的检测与解除 ​ 资源分配图 ​ 死锁定理 ​ 死锁解除

5.2 JS 单线程运行

为什么 JS 是单线程运行?
如果多线程运行,可能不同线程同时修改dom,导致浏览器无法渲染冲突; 最终采用了单线程。

5.3 同步代码和异步代码

1. 同步代码
   同步代码也叫同步任务,按照顺序依次执行,上一个任务执行完毕下一个任务才开始执行。
   
2. 异步代码
   异步代码也叫异步任务,满足执行条件且执行线程空闲(同步代码执行完毕),执行异步任务

3. 哪些是异步任务?
   - 定时器的回调函数
   - DOM 事件的回调函数
   - Ajax 的回调函数
   - Promise 的回调函数


4. 注意事项:
   - 开启定时器、监听事件本身是同步任务,它们的回调函数才是异步任务。
   - 异步的代码通常都是回调函数,但是回调函数不一定是异步的。

5.4 事件轮询机制

- 执行栈
  负责执行代码,不论是同步任务还是异步任务都要进行执行栈执行
  
- 管理模块
  负责管理异步任务是否满足执行条件,有定时器管理模块、DOM事件管理模块、Ajax管理模块等
  
- 回调队列
  当管理模块发现异步任务满足条件,将回调函数放入回调队列,等待执行。
  队列的特点:先进先出
  
- 事件轮询
  负责监听执行栈什么时候空闲,一旦执行栈空闲,立即将回调队列中的回调函数放入执行栈执行。