JavaScript

1. JS 的三大核心

        1-1. ECMAScript

          => JS 的语法

        1-2. BOM - Browser Object Model

          => 一套操作浏览器的属性和方法

        1-3. DOM - Document Object Model

          => 一套操作文档流的属性和方法

  

目录

1. JS 的三大核心

  

3. do ... while 循环

4. for 循环

1. 函数

      2. 函数的两个阶段

 ES6 的类语法


        2-1. 行内式

       => a 标签

          -> 书写在 href 属性上, 书写 javascript: 代码 ;

       => 非 a 标签

          -> 添加一个行为属性, onclick

          -> 在行为属性值的位置直接书写代码

        2-2. 内嵌式

        => 书写在一个 script 标签对中

        => 打开页面直接执行, 不需要依赖行为

        => 一个页面可以引入多个 script 标签, 会按照从上到下的顺序依次执行

        => script 标签可以书写在任意位置, 但是建议书写在 head 或者 body 的末尾

            -> 目前推荐书写在 body 的末尾

        2-3. 外链式

        => 代码书写在一个 .js 后缀的文件内, 以 script 标签的 src 属性引入页面

        => 打开页面直接执行, 不需要依赖行为

        => 一个页面可以引入多个 script 标签, 会按照从上到下的顺序依次执行

        => script 标签目前推荐书写在 body 的末尾

        => 注意: 一个 script 标签书写了 src 属性以后, 表示当做外链式使用, 写在标签对内的代码没有意义

    3. JS 定义变量

        => 使用 var 关键字进行定义

        3-1. 单独声明一个变量不赋值

          => var x

        3-2. 单独声明一个变量并赋值

          => var x = 100

        3-3. 一次性声明多个变量不赋值

          => var x1, x2, ...

        3-4. 一次性声明多个变量并赋值

          => var x1 = 100, x2 = 200, ...

        3-5. 一次性声明多个变量, 有的赋值有的不赋值

          => var x1 = 100, x2, x3 = 300, ...

    4. JS 变量的命名规则和规范

        4-1. 规则: 必须遵守, 不然报错

          => 变量只能有 数字 / 字母 / 下划线 / 美元符 组成

          => 变量不能由数字开头

          => 严格区分大小写

          => 不能使用关键字或者保留字

        4-2. 规范: 建议你遵守

          => 变量语义化

          => 驼峰命名法

            -> 当变量由多个单词组成的时候, 第二个单词开始首字母大写

          => 不要用中文

    5. JS 的数据类型

        5-1. 基本数据类型

          => Number 数值

          => String 字符串

          => Boolean 布尔

          => Undefined 空

          => Null 空

        5-2. 复杂数据类型

    6. JS 的数据类型检测

        => 语法:

          -> typeof 要检测的变量

          -> typeof(要检测的变量)

        => 返回值(结果):

          -> 以字符串类型返回你要检测的变量的数据类型

        => 注意: 当两个及以上 typeof 连用的时候, 结果一定是 string

    7. 数据类型转换

        7-1. 转数值

          => Number()

          => parseInt()

          => parseFloat()

          => 非加法的数学运算

        7-2. 转字符串

          => String()

          => toString()

            -> 注意: undefined 和 null 不能使用 toString 方法

          => 使用加号进行字符串拼接

        7-3. 转布尔

          => Boolean()

            -> 有五个内容是 false, 其余全是 true

            -> 数值 0

            -> 空字符串 ''

            -> 数值 NaN

            -> undefined

            -> null

    8. 运算符

        8-1. 算数运算符

          => +

            -> 意义1: 数学运算, 只有符号两边都是 数值或者布尔 的时候会进行数学运算

            -> 意义2: 字符串拼接, 只要符号任意一边是 字符串 的时候, 就会进行字符串拼接

          => -

          => *

          => /

          => %

        8-2. 赋值运算符

          => =

          => +=

          => -=

          => *=

          => /=

          => %=

20210622

  1. 运算符 - 比较运算符

        1-1. > 大于

        1-2. < 小于

        1-3. >= 大于等于

        1-4. 

        1-5. == 等于, 只比较数据的值, 不比较数据类型

        1-6. === 等等于, 比较数据类型和值

        1-7. != 不等于, 比较的是值不等于

        1-8. !== 不等等, 比较的是值和数据类型任意一个不等于就是不等

  2. 运算符 - 逻辑运算符

        2-1. && 与

          => 需要左右两边都是 true 才能返回 true

          => 短路表达式, 左边为 true 的时候, 右边的代码才会执行

        2-2. || 或

          => 需要任意一边是 true, 就能返回 true

          => 短路表达式, 左边为 false 的时候, 右边的代码才会执行

        2-3. !  非(取反)

          => 本身是 true 结果就是 false

          => 本身是 false 结果就是 true

          => 双取反可以转布尔

  3. 运算符 - 自增自减运算符

        3-1. ++ 加加

          + 使用:

            => 前置 ++ : 符号在 数据的前面

            => 后置 ++ : 符号在 数据的后面

          + 共同点:

            => 都会让变量的值改变(+1)

          + 区别: 在参与运算的时候

           => 前置 ++ : 先把变量的值改变(+1), 用改变以后的值参与运算

           => 后置 ++ : 先把变量本身的值参与运算, 然后在改变变量的值(+1)

        3-2. -- 减减 ( 如上和加加一样 )

  4. if 分支语句

        4-1. if (条件) { 代码 }

          => 意义: 条件为 true 就执行 {} 内的代码

        4-2. if (条件) { 代码 } else { 代码 }

          => 意义: 条件为 true 执行 if 后面的 {}, 条件为 false, 执行 else 后面的 {}

          => 两个大括号必然且只能执行一个

        4-3. if (条件) { 代码 } else if (条件2) { 代码 } ...

          => 意义: 哪一个条件满足, 就执行哪一个 if 后面的 {} 内的代码

          => 注意: 前面的条件满足了, 后面的就不执行了

          => 多个大括号最多能执行一个

        4-4. if (条件) { 代码 } else if (条件2) { 代码 } ... else { 代码 }

          => 意义: 哪一个条件满足, 就执行哪一个 if 后面的 {} 内的代码, 都不满足的时候, 执行 else 后面的 {}

          => 注意: 前面的条件满足了, 后面的就不执行了

          => 多个大括号必然且只能执行一个

   5. switch 分支语句

        5-1. 基础语法

          switch (要判断的变量) {

            case 情况1:

              情况1 满足的时候执行的代码

              break

            case 情况2:

              情况2 满足的时候执行的代码

              break

            default:

              所有情况都不满足的时候执行的代码

          }

        5-2. 注意:

          => 要判断的变量 和 各个情况之间 必须是 === 为 true 才叫做 满足

          => case 不能书写范围的判断, 只能是准确的某一个值

          => case 不能书写 逻辑运算符 的情况

          => default 可以不写, 不写的时候, 就是所有 case 都不满足就没有代码执行

          => break 必须写, 如果不写, 会发生穿透情况

            -> 穿透: 下一个 case 不管是不是满足, 都会执行代码, 直到遇到 break 为止

          => break 穿透会从第一个满足条件的 case 开始向下穿透

      6. 三元表达式 / 三目 / 三目运算符

        => 两个符号 和 三个数据 组成的运算符

        => 是一个 if else 语句的简写形式

          -> 只能简写 if else 语句

        => 语法:

          -> 条件 ? 满足 : 不满足

          -> 如果条件满足, 执行满足位置的代码

          -> 如果条件不满足, 就执行不满足位置的代码

        => 注意:

          1. 每一个代码执行区间, 只能书写一句话

          2. 只能简写 if else 语句

          3. 可以用来执行代码, 也可以用来赋值

20210623

1. 循环

        + 循环三要素

          => 开始

          => 结束

          => 步长

2. while 循环

        + 语法: while (条件) { 重复执行的代码 }

3. do ... while 循环

        + 语法: do { 重复执行的代码 } while (条件)

   + 和 while 循环的区别

     => 不管条件是不是满足, 都先执行一次在开始进行条件判断

     => 当初始变量在条件范围以内的时候, 和 while 循环一样

     => 当初始变量在条件范围以外的时候, 只执行一次, while 循环一次都不执行

4. for 循环

    + 语法: for (定义初始变量; 条件判断; 修改初始变量) { 重复执行的代码 }

5. 循环给我们提供了什么 ?

        => 重复执行代码的能力

        => 一组有规律的数字, 通过循环控制变量

6. 如何控制循环的次数 ?

        => 开始可以控制

        => 结束可以控制

        => 步长可以控制

7. 循环控制语句

    7-1. break

     => 当循环内执行到 break 的时候, 会直接结束整个循环

    7-2. continue

      => 当循环内执行到 continue 的时候, 会结束循环的本次, 直接开始下一次

8、 循环的嵌套

        + 一个循环里面再次书写一个循环

        + 注意: 里外层循环不要用一个变量控制

        + 建议: 看到循环嵌套, 从里向外看

20210624

1. 函数

        + 是一个 JS 中的复杂数据类型, 叫做 Function

        + 什么是函数 ?

          => 是一个 "盒子", 承载一段代码

          => 需要的时候, 可以把 "盒子" 内的代码拿出来执行

      2. 函数的两个阶段

        2-1. 函数定义阶段

          => 声明式函数: function 函数名() { 代码 }

          => 赋值式函数: var 函数名 = function () { 代码 }

          => 注意: 函数定义阶段, 书写在函数体内的代码是不执行的

        2-2. 函数调用阶段

          => 函数名()

          => 每书写一次, 就会把函数体内的代码从上到下的执行一遍

      3. 函数的参数

        3-1. 形参

          => 书写在函数定义阶段的小括号内

          => 可以书写多个, 多个之间使用逗号(,) 分隔

          => 就是一个定义在函数内部的变量, 只能在函数内部使用

          => 形参的值由函数调用时的实参决定

        3-2. 实参

          => 书写在函数调用阶段的小括号内

          => 可以书写多个, 多个之间使用逗号(,) 分隔

          => 就是在按照顺序依次给每一个形参进行赋值

      4. 函数内的注意

        4-1. 调用上的注意

          => 声明式函数可以先调用, 也可以后调用

          => 赋值式函数只能后调用, 先调用会报错

        4-2. 参数个数上的注意

          => 一样多: 按照从左到右的顺序依次给每一个形参赋值

          => 形参多: 前面的按照顺序依次赋值, 多出来的形参因为没有实参赋值, 在函数内使用的时候就是 undefined

          => 实参多: 前面的按照顺序依次赋值, 多出来的实参因为没有形参接受, 不能再函数内直接使用

      5. arguments

        => 是一个函数内天生自带的变量

        => 是一个 "盒子", 是一个承载 数据 的 "盒子"

        => 承载的就是所有的实参

        5-1. arguments 的索引

          -> arguments 内的所有数据按照 "序号" 进行排列的

          -> 我们管 "序号" 叫做 索引 或者 下标

          -> 索引(下标): 从 0 开始, 依次 +1

        5-2. 访问 arguments 内的数据

          -> 利用索引进行访问

          -> 语法: arguments[索引]

          -> 如果 arguments 内有该索引, 那么就是该索引位置的数据

          -> 如果 arguments 内没有该索引, 那么就是 undefined

        5-3. arguments 的长度

          -> 语法: arguments.length

          -> 得到的是一个数字, 表示 arguments 的长度  内有多少个实参

就是有多少个数据

  = > 长度和索引的关系

          -> 最后一个数据的索引一定是 长度 - 1

        5-4. 遍历 arguments

          -> 遍历: 从头到尾访问到每一个 arguments 中的数据

          -> 利用 for 循环来进行遍历

            + 因为 arguments 的索引是一组有规律的数字

            + 又因为循环可以给我们提供一组有规律的数字

            + 我们就可以使用循环提供的一组有规律的数字来当做索引访问 arguments 内的每一个数据

      6. 函数的返回值

        => 给函数创造一个执行后的结果

        => 语法: return xxx

        => 这个函数执行完毕后的结果, 就是书写在函数内 return 后面的内容

      7. 打断函数

        => 使用 return 关键字

        => 当代码执行到 return 关键字的时候, 会直接结束函数

        => 写在 return 后面行的内容不再执行了

      8. 作用域

        => 什么是作用域: 一个 变量(变量名 和 函数名) 的生效范围

        => 主要提供了三个机制

        => 作用域的分类

          -> 全局作用域: 每一个页面打开就是一个全局作用域

          -> 私有作用域: **只有函数生成私有作用域**

        => 作用域的上下级关系

          -> 写在哪一个作用域下的函数, 就是哪一个作用域的子级作用域

        8-1. 变量定义机制

          -> 定义在哪一个作用域内的变量

          -> 就是哪一个作用域的私有变量

          -> 只能在该作用域及其后代作用域内使用

        8-2. 变量赋值机制

          -> 当你需要给一个变量赋值的时候

          -> 自己作用域内有, 直接给自己的赋值

          -> 自己的没有, 就给父级作用域的赋值

          -> 如果还没有, 就给再父级作用域的赋值

          -> 以此类推直到全局作用域都没有

          -> 那么把这个变量定义为全局变量在进行赋值

        8-3. 变量访问机制

          -> 当你需要获取一个变量的值的时候

          -> 首先在自己作用域内查找, 如果有, 直接使用, 停止查找

          -> 如果没有, 去到父级作用域查找, 如果有直接使用

          -> 如果还没有, 去到再父级作用域查找

          -> 以此类推, 直到全局作用域都没有, 那么直接报错

20210625

1、 预解析(预解释)

        + 预: 预先, 提前

        + 解析: 解释一些内容, 做出一个说明

        + 在所有代码执行之前, 对代码进行通读并解释

2、 预解析的内容

        1. 解析了 var 关键字

          => 规则: 会把 var 关键字声明的变量进行提前说明, 但是不进行赋值

        2. 解析了 声明式 函数

          => 规则: 会把 函数名 进行提前说明  并且赋值为一个函数

3、 预解析的无节操

        1. 在代码中, 不管 if 条件是否为 true, if 语句代码里面的内容依旧会进行预解析

        2. 函数体内, return 后面的代码虽然不执行, 但是会进行预解析

     预解析中的重名问题

        + 当你使用 var 定义变量 和 声明式函数 重名的时候, 以 函数为主

        + 只限于在预解析阶段, 以函数为准

4、递归函数(了解)

      一种函数的应用方式

       => 一个函数自己调用自己, 并且正确设置了结束条件, 可以逐步回归的时候

       => 我们管这种调用函数的方式叫做 递归调用

      书写递归的原则

        1. 先写折返点

        2. 按照规则书写未到达折返点的情况

5. 对象

      对象两个含义

          => 第一个: 一类事物的某一个个体

          => 第二个: 一种数据类型

      对象数据类型 Object

        + 是 JS 中的一种数据类型, 是一个 复杂 数据类型

        + 什么是 对象 ? (私人)

          => 是一个 "盒子", 承载一些数据

        + 就是一个 键值对 的集合

      如何创建一个对象

        + 语法:

          1. 字面量方式创建对象

            => 语法: var obj = { 键值对, ... }

          2. 内置构造函数创建对象

            => 语法: var obj = new Object()

      创建一个带有数据(键值对)的对象

        + 目前只能使用 字面量方式 创建

        + 语法:

          => var obj = { 键值对, ... }

        + 键值对:

          => 键: 值

          => 多个键值对之间, 使用 逗号(,) 分隔

6、 对象的基本操作

        + 操作: 增删改查

          => 向对象内增加一条数据

          => 删除对象内的某一条数据

          => 修改对象内的某一条数据

          => 访问对象内某一条数据的值

        + 一共有两种语法

          1. 点语法

            => 增: 对象名.键名 = 值

            => 删: delete 对象名.键名

            => 改: 对象名.键名 = 值

            => 查: 对象名.键名

          2. 数组关联语法

            => 增: 对象名['键名'] = 值

            => 删: delete 对象名['键名']

            => 改: 对象名['键名'] = 值

            => 查: 对象名.键名

7、 对象操作的点语法

      增:

        + 向对象内增加一条数据

        + 语法: 对象名.键名 = 值

      删:

        + 删除对象内的某一个成员

        + 语法: delete 对象名.键名

      改:

        + 修改对象内某一个成员的值

        + 语法: 对象名.键名 = 值

        + 因为对象内键名不重复的特点

          => 当你第二次设置某一个键名的时候, 就是修改

          => 原先对象内有, 就是修改

          => 原先对象内没有, 就是增加

      查:

        + 获取对象内某一个成员的值

        + 语法: 对象名.键名

8、 数组关联语法

      增:

        + 向对象内增加一条数据

        + 语法: 对象名['键名'] = 值

      删:

        + 删除对象内的某一个成员

        + 语法: delete 对象名['键名']

      改:

        + 修改对象内某一个成员的值

        + 语法: 对象名['键名'] = 值

        + 因为对象内键名不重名的特点, 原先有就是修改, 原先没有就是增加

      查:

        + 访问对象内某一个成员的值

        + 语法: 对象名['键名']

9、 对象两种操作语法的区别

      对象内的键名都可以如何书写

        1. 符合变量命名规则和规范的可以书写

        2. 可以是纯数字

        3. 其他特殊字符也可以, 但是需要在 键名 的位置单独包裹一个引号

      两种语法的区别

        1. 对于名字的操作

          => 如果是符合变量命名规则和规范的 键名, 两种语法都可以

          => 纯数字只能使用数组关联语法, 不能使用点语法 

          => 带有特殊符号的, 只能使用数组关联语法, 不能使用点语法

        2. 和变量相关的时候

          => 点语法, 不管如何, 都不能和变量产生联系

            -> 始终都是访问的对象内某一个准确的键名

          => 数组关联语法, 当你的 [] 内书写的是一个变量的时候

            -> 会把变量解析出来填充在 [] 内

10、 对象的遍历

        + 对象没有办法使用 for 循环来进行遍历

          => 因为 for 循环能提供的是一组有规律的数字

          => 但是对象内的键名 没有规律

      for in 循环

        + 主要作用, 就是用来遍历对象数据类型的

11、 数据类型存储的区别

        + 栈内存和堆内存

        + 都是用来存储代码中的数据的位置

        + 基本数据类型 和 复杂数据类型 在存储上是有区别的

        + 了解栈内存: 按序排列, 先来的在栈底, 后来的在栈顶

        + 了解堆内存: 无序排列, 根据地址来查找(堆空间地址)

      基本数据类型的存储

        + 直接存储在 栈 内存中

      复杂数据类型的存储 (地址数据类型 / 引用数据类型)

        + 把数据存储在 堆内存 中

        + 把地址赋值给 栈内存 的变量里

12、 数据类型赋值上的区别

基本数据类型

        + 赋值的时候, 就是直接值的复制

        + 赋值以后, 两个变量互相之间没有任何关系

        + 改变一个变量, 另一个不会发生变化

复杂数据类型

        + 赋值的时候, 是把变量内存储的地址进行赋值

        + 赋值以后, 两个变量操作的是一个存储空间

        + 任意一个变量去改变存储空间内的数据, 另一个变量看到的一样是改变以后的

数据类型比较上的区别

        + 基本数据类型, 就是 值 和 值 之间的比较

        + 复杂数据类型, 是 地址 和 地址 之间的比较

Number 的转换规则

        + 把需要转换的内容当做一个整体来看待

        + 如果整体可以转换成 数字, 那么就是一个 数字

        + 如果整体不可以转换成 数字, 那么就是 NaN

20210628 / 0629

一、数组数据类型

      数组是 数据的组合

    + 是一个 JS 中的复杂数据类型, 叫做 Array

          => 也是一个 "盒子", 是一个承载数据的 "盒子"

          => 数组里面的数据排列是按照顺序的

          => 有序的数据集合

    + 数组里面所有数据的排列都是按照 "序号" 进行排列的

          => 我们管 "序号" 叫做 索引 或者 下标

          => 索引(下标): 从0开始, 依次+1

二、数组的创建

1. 字面量方式创建数组

=> 创建空数组: var arr = []

 => 创建带有数据的数组: var arr = [ 数据1, 数据2, 数据3, ... ]

2. 内置构造函数创建数组

=> 创建空数组: var arr = new Array()

-> 等价于 var arr = []

=> 创建指定长度的数组: var arr = new Array(数字)

-> 数字表示的是数组的长度

-> 因为没有数据填充, 就会用 empty 填充

=> 创建带有数据的数组: var arr = new Array(数据1, 数据2, 数据3, ...)

-> 每一个内容都是数组内部的真实数据, 没有表示长度的数据了

-> 等价于 var arr = [ 数据1, 数据2, 数据3, ... ]

三、数组的基本使用

 1. 数组有一个 length 的属性

  + 是一个 读写 的书写

    + 读:

    => 语法: 数组名.length

    => 获取到的就是该数组的长度, 也就是数组内有多少个数据

    + 写:

    => 语法: 数组名.length = 数字

    => 设置该数组的长度

       -> 设置的数字和原始长度一样, 相当于没有设置

       -> 设置的数字比原始长度大, 多出来的位置, 用 empty 补齐

       -> 设置的数字比原始长度小, 从后面开始删除

2. 数组的索引属性

  + 是一个 读写 的属性

    + 读:

     => 语法: 数组[索引]

     => 获取数组该索引位置的数据

      -> 如果数组有该索引位置, 那么就是该索引位置的数据

      -> 如果数组没有该索引位置, 那么就是 undefined

    + 写:

      => 语法: 数组[索引] = 值

       => 给数组的某一个索引位置进行赋值

        -> 如果原先数组中就有该索引位置, 那么就是修改

        -> 如果原先数组中没有该索引位置, 那么就是添加

        + 如果你设置的索引刚好和 length 一样, 那么就是在最后追加

        + 如果你设置的索引大于 length, 那么中间空出来的位置用 empty 补齐, 为了保证最后一位的索引一定是 length - 1

3. 遍历数组

        + 因为数组的索引是一组有规律的数字

        + for 循环也可以提供一组有规律的数字

        + 所以我们使用 for 循环遍历数组

四、冒泡排序 bubble sort

  + 目的: 为了锻炼逻辑思维, 练习数组的遍历和操作

  + 排序: 把一个乱序的数组, 通过代码的方式调整成有序的数组(正序或者倒序)

  + 冒泡的原理:

  => 第一个数字和第二个数字进行比较, 如果第一个比第二个大, 就交换位置

    => 以此类推, 直到数组遍历完毕, 最大的一定在最后面

    => 重复执行第一轮的操作, 直到所有数字排好

    => 问题: 重复多少轮 ?

    -> 当 8 个大的排好以后, 剩下的最小的自然在最前面

      + 口诀:

          => 双层 for 循环, 一层减一次

          => 里层减外层, 变量相交换

五、选择排序 select sort

        + 逻辑:

          => 第一轮

            -> 假设索引 0 位置数字最小

            -> 循环遍历数组, 找到真实最小数字的索引

            -> 让最小数字的索引位置和 [0] 位置数据交换

            -> 目的: 把最小的数字放在了 [0] 的位置, 最前面

          => 第二轮

            -> 假设索引 1 位置数字最小

            -> 循环遍历数组, 找到真实最小数字的索引

            -> 让最小数字的索引位置和 [1] 位置数据交换

            -> 目的: 把第二小的数字放在了 [1] 的位置

六、什么是数组常用方法

        + 所有的数组常用方法的通用语法: 数组名.xxx()

          => 只是方法名不一样

          => () 内部传递的数据不一样

数组常用方法 1 文件内的所有方法, 会改变原始数组

1. push()

        => 语法: 数组.push(数据)

        => 作用: 向数组的末尾追加数据

        => 返回值: 添加数据以后, 数组最新的 **长度**

2. pop()

        => 语法: 数组.pop()

        => 作用: 删除数组的最后一个数据

        => 返回值: 被删除的数据

3. unshift()

        => 语法: 数组.unshift('数据')

        => 作用: 从数组的最前面插入一个数据

        => 返回值: 添加数据以后, 数组最新的 **长度**

      4. shift()

        => 语法: 数组.shift()

        => 作用: 删除数组最前面一个数据

        => 返回值: 被删除的数据

      5. reverse()

        => 语法: 数组.reverse()

        => 作用: 反转数组

        => 返回值: 反转后的数组

      6. sort()

        => 语法:

          -> 数组.sort()

            + 把每一个数据一位一位的看待, 进行排序的

eg:1、1、12、2、23、3 、34、35、

          -> 数组.sort(function (a, b) { return a - b })

            + 把所有数据进行升序排列

          -> 数组.sort(function (a, b) { return b - a })

            + 把所有数据进行降序排列

        => 作用: 对数组进行排序

        => 返回值: 排序后的数组

      7. splice()

        => 语法:

          -> 数组.splice(开始索引, 多少个)

          -> 数组.splice(开始索引, 多少个, 要插入的数据)

            + 从安一个索引开始删除, 从哪一个索引开始插入

        => 作用: 截取数组并选择性是否插入内容

        => 返回值: 必然是一个新的数组

          -> 你截取了多少内容, 新数组里面就有多少内容

          -> 你如果没有截取内容, 那么就是一个空的新数组

数组常用方法 2 文件内的所有方法, 不会改变原始数组, 以返回值的形式给到结果

1. concat()

        + 语法: 数组.concat(数据, 数组, ...)

        + 作用: 进行数组拼接

        + 返回值: 拼接好的数组

        + 注意: 不会改变原始数组

      2. join()

        + 语法: 数组.join('连接符')

          => 连接符不写, 默认使用 逗号(,)

        + 作用: 使用连接符把数组内的每一项连接成为一个字符串

        + 返回值: 连接好的字符串

        + 注意: 不会改变原始数组, 返回值是一个字符串类型

      3. slice()

        + 语法: 数组.slice(开始索引, 结束索引)

          => 开始索引: 默认值是 0

          => 结束索引: 默认值是 length

        + 特点:

          => 包前不包后

          => 可以填写负整数, 当你填写负整数的时候, 等价于 负整数+length

        + 作用: 截取数组, 从数组内复制一些内容出来

        + 返回值: 一个新的数组

          => 如果你截取了数据, 那么截取多少, 新数组内就有多少数据

          => 如果你没有截取数据, 那么就是一个空的数组

        + 注意: 不会改变原始数组

      4. indexOf()

        + 语法: 数组.indexOf(数据, 开始索引)

          => 开始索引选填, 默认值是 0

        + 作用: 从开始索引向后检索该数组中是否有该数据

        + 返回值:

          => 如果检索到了改数据, 那么就是该数据第一次出现的索引位置

          => 如果没有检索到该数据, 那么就是 -1

        + 注意: 不会改变原始数组

      5. lastIndexOf()

        + 语法: 数组.lastIndexOf(数据, 开始索引)

          => 开始索引选填, 默认值是 length

        + 作用: 从开始索引向前检索该数组中是否有该数据

        + 返回值:

          => 如果检索到了改数据, 那么就是该数据第一次出现的索引位置

          => 如果没有检索到该数据, 那么就是 -1

        + 注意:

          => 不会改变原始数组

          => 不会改变索引位置

数组常用方法 3 文件内的方法相对复杂一些, 也都不会改变原始数组, 都以返回值形式给出结果

1. forEach()

        + 语法: 数组.forEach(function (item, index, arr) {})

          => 函数可以接受三个参数

          => 第一个参数: 表示数组内的每一项

          => 第二个参数: 表示数组内每一项的索引

          => 第三个参数: 表示原始数组

        + 作用: 用来遍历数组

        + 返回值: 没有 undefined

      2. map()

        + 语法: 数组.map(function (item, index, arr) {})

        + 作用: 映射数组

        + 返回值:

          => 是一个新数组, 并且和原始数组长度一致

          => 新数组内每一个数据都是根据原始数组中每一个数据映射出来的

          => 映射条件以 return 的形式书写

      3. filter()

        + 语法: 数组.filter(function (item, index, arr) {})

        + 作用: 过滤数据

        + 返回值:

          => 是一个新数组

          => 数组内是原始数组中所有满足条件的项

          => 条件以 return 的形式书写

      4. every()

        + 语法: 数组.every(function (item, index, arr) {})

        + 作用: 判断数组中是不是每一个都满足条件

        + 返回值: 一个布尔值

          => 如果数组中每一个都满足条件, 那么返回值 true

          => 只要数组中任何一个不满足条件, 那么返回 false

        + 判断条件以 return 的形式书写

      5. some()

        + 语法: 数组.some(function (item, index, arr) {})

        + 作用: 判断数组中是不是有某一个满足条件

        + 返回值: 一个布尔值

          => 如果数组中有任何一个满足条件, 那么返回 true

          => 只有数组中所有的都不满足条件, 才会返回 false

      6. find()

        + 语法: 数组.find(function (item, index, arr) {})

        + 作用: 查找数组中某一个数据

        + 返回值:

          => 数组中你查找到的该数据

        + 查找条件以 return 的形式书写

        + 所用在复杂数据类型的查找

      7. reduce()

   语法: 数组.reduce(function (prev, item, index, arr) {}, 初始值)

          => 函数, 函数根据数组中的成员进行重复调用

            -> 第一个参数: 初始值 或 每一次叠加后的结果

            -> 第二个参数: 每一项

            -> 第三个参数: 索引

            -> 第四个参数: 原始数组

          => 初始值: 默认是 0, 表示从什么位置开始叠加

        + 作用: 进行叠加累计

+ 借助有一个数据类型叫做 Set 去重

          => 数据类型不接受重复数据

        + 语法: var s = new Set( [ 数据1, 数据2, 数据3, ... ] )

        2. 把 set 数据类型还原成数组

          => 语法1: Array.from(set数据类型)、

          => 语法2: [ ...set数据类型 ]

七、数组塌陷

+ 当你在数组中去删除内容的时候, 就会发生塌陷

解决数组塌陷

 + 方案1: 不让数组塌陷

 => 倒着循环数组

 + 方案2: 数组塌陷, 循环控制变量跟着一起塌陷

 => 只要执行了删除的操作, 就执行一次 i--

八、ES发展史(了解)

        + ECMAScript 简称 ES

        + 就是 JS 的语法, 提供了一系列的方法

      1997 年以前, 没有 ES

      1997 年出现了 ES1

      1998 年出现了 ES2

      1999 年出现了 ES3

      2000 年提案了 ES4, 没通过, 搁置

      2008 年出现了 ES5, 社区叫做 ES3.1

      2011 年出现了 ES5.1, 社区叫做 ES5, 同年各大浏览器厂商支持了 ES5.1

      2015 年出现了 ES2015

        => 官方统一命名, 以年份命名, ES4 进行了修改得到的

        => 社区叫做 ES6

 ES5 的严格模式(了解)

        + 书写代码规范更严格, 不允许随便书写了

        + 如何开启严格模式

          => 语法: 在作用域的顶部书写一个 'use strict'

 严格模式

        1. 所有的变量使用必须要提前定义

        2. 函数的参数不允许重名

        3. 八进制的数字的书写形式

          => 以前: 0100

          => 严格: 0o100

        4. ...

20210630

字符集(了解)

        + ASCII 读做: as key

        + 其实就是一个 文字 和 二进制的 对照表

          => 每一个文本的存储, 其实都是以 二进制 的形式存储在 硬盘内

          => a 这个文本存成什么样的 二进制

          => 约定一个对照表, 不同的文本内容, 对应一个固定的 二进制 数字

        + 因为最早的计算机是 美国人 发明的

          => 最早的计算机对照变只有 128 个字符

          => 出现了一个 128 个字符组成的对照表, 叫做 ASCII 编码字符集

万国码 / 统一码

        + unicode 编码

        + 也是一个对照表, 前 128 个是 ASCII 编码

        + 从 128 个以后, 开始加入了全世界各个国家的文字

        + 每一个文字都有对应的 二进制 数字编码

        + 有一种 unicode 的变种编码, 是 八位 的 unicode 编码

        + 简写叫做 UTF-8 专门用于 web 应用

国标编码

        + GBK

        + 前 128 个是 ASCII 编码

        + 后面的都是中文

        + 适用于中国人编码

字符串

        + 在 JS 内用 单引号 或者 双引号 包裹的内容就叫做 字符串

        + 字符串也是一个 包装数据类型

1、包装数据类型

        + 存储的时候, 是以基本数据类型的形式进行存储

        + 当你使用它的时候, 会瞬间转换成 复杂数据类型 的样子让你使用

        + 等你使用完毕, 瞬间转换成 基本数据类型 的形式进行存储

2、创建字符串的方式

(1). 字面量创建

=> var str = 'hello world'

    => var str = "hello world"

 (2). 内置构造函数创建

    => var str = new String('hello world')

3、字符串的基本操作

 (1). 字符串有一个 length 属性

 + 是一个 只读 的属性, 只能获取不能设置

          + 读:

            => 语法: 字符串.length

            => 获取到的就是该字符串的长度, 也就是字符串有多少个字符组成

            => 注意: 空格也是一个字符, 中文也是一个字符

  (2). 字符串有一个 索引 属性

          + 是一个 只读 的属性, 只能获取不能设置

          + 读:

            => 语法: 字符串[索引]

            => 获取到的就是该字符串指定索引位置的那一位字符

              -> 如果有该索引位置, 那么就是该索引位置的字符

              -> 如果没有该索引位置, 那么就是 undefined

          + 索引: 从0开始, 依次+1

  (3). 遍历字符串

          + 使用 for 循环进行遍历

字符串常用方法

        + 字符串常用方法为什么会出现 ?

          => 因为字符串并不好操作

        + 字符串常用方法的统一语法

          => 字符串.xxx()

        + 注意: 所有字符串常用方法, 都不会改变原始字符串, 都是以返回值的形式出现结果

      1. charAt()

        => 语法: 字符串.charAt(索引)

作用 :根据索引找到对应字符( 内容 )

        => 返回值: 该索引位置的对应字符

          -> 如果有该索引位置, 那么就是该索引位置的字符

          -> 如果没有该索引位置, 那么就是 空字符串 ''

      2. charCodeAt()

        => 语法: 字符串.charCodeAt(索引)

        => 返回值: 该索引位置的对应字符的 编码(十进制)

      3. toUpperCase()

        => 语法: 字符串.toUpperCase()

        => 作用: 把所有字母转换成 大写

        => 返回值: 转换好的字符串

      4. toLowerCase()

        => 语法: 字符串.toLowerCase()

        => 作用: 把所有字母转换成 小写

        => 返回值: 转换好的字符串

      5. substr()

        => 语法: 字符串.substr(开始索引, 多少个)

        => 作用: 截取字符串

        => 返回值: 截取出来的字符串

      6. substring()

        => 语法: 字符串.substring(开始索引, 结束索引)

          -> 包前不包后

        => 作用: 截取字符串

        => 返回值: 截取出来的字符串

      7. slice()

        => 语法: 字符串.slice(开始索引, 结束索引)

          -> 包前不包后

          -> 可以填写负整数

        => 作用: 截取字符串

        => 返回值: 截取出来的字符串

      8. replace()

        => 语法: 字符串.replace(换下字符, 换上字符)

        => 作用: 使用换上字符替换掉字符串内的换下字符

        => 注意: 只能替换出现的第一个, 只能替换一个

        => 返回值: 替换好的字符串

      9. split()

        => 语法: 字符串.split('切割符号')

          -> 可以不传递, 会把完整字符串当做一个整体

          -> 传递一个空字符串(''), 会一位一位的切割

        => 作用: 按照切割符号, 把字符串切割开, 放在一个数组里面

        => 返回值: 是一个数组, 内容就是按照切割符号切割开的每一个部分

      10. concat()

        => 语法: 字符串.concat(字符串)

        => 作用: 字符串拼接

        => 返回值: 拼接好的字符串

      11. indexOf()

        => 语法: 字符串.indexOf(字符, 开始索引)

        => 作用: 检测该字符在字符串内的索引位置

        => 返回值:

          -> 如果有该字符内容, 那么就是该字符的索引位置

          -> 如果没有该字符内容, 就是 -1

      12. lastIndexOf()

        => 语法: 字符串.lastIndexOf(字符, 开始索引)

        => 作用: 从后向前检测该字符在字符串内的的索引位置

        => 返回值:

          -> 如果有该字符内容, 那么就是该字符的索引位置

          -> 如果没有该字符内容, 就是 -1

      13. trim()

        => 语法: 字符串.trim()

        => 作用: 去除字符串首尾的空白内容

        => 返回值: 去除空白内容以后的字符串

      14. trimStart() / trimLeft()

        => 语法:

          -> 字符串.trimStart()

          -> 字符串.trimLeft()

        => 作用: 去除前面的空白内容

        => 返回值: 去除前面空白内容以后的字符串

      15. trimEnd() / trimRight()

        => 语法:

          -> 字符串.trimEnd()

          -> 字符串.trimRight()

        => 作用: 去除后面的空白内容

        => 返回值: 去除后面空白内容以后的字符串

json 格式转换:

  1、把 js 的数据类型转换成 json 格式的字符串

        + 语法: JSON.stringify(要转换的 js 的数据类型)

        + 返回值: 转换好的 json 格式字符串

  2、把 json 格式转换成 js 的数据类型

        + 语法: JSON.parse(json 格式字符串)

        + 返回值: 转换好的 js 格式的数据类型

        + 注意:

          => 如果你的小括号内填写的不是一个 json 格式的字符串, 会报错

json 格式:

1. 所有 key 的位置, 必须使用 双引号 包裹, 单引号不行

2. 所有 value 的位置, 除了数字和布尔值, 其他的必须用 双引号 包裹, 单引号不行

3. 对象的最后一个成员后面不要写 逗号(,)

操作数字的常用方法

        + 专门提供给我们进行数字的操作

        + 统一语法: Math.xxx()

      1. Math.random()

        => 语法: Math.random()

        => 作用: 获取一个 0 ~ 1 之间的随机小数

        => 返回值: 一个 0 ~ 1 之间的随机小数

        => 注意: 有可能得到 0, 但是绝不可能得到 1

      2. Math.round()

        => 语法: Math.round(数字)

        => 返回值: 四舍五入取整后的结果

      3. Math.ceil()

        => 语法: Math.ceil(数字)

        => 返回值: 向上取整以后的结果

      4. Math.floor()

        => 语法: Math.floor(数字)

        => 返回值: 向下取整以后的结果

      5. Math.abs()

        => 语法: Math.abs(数字)

        => 返回值: 取绝对值以后的结果

      6. Math.pow()

        => 语法: Math.pow(底数, 指数)

        => 返回值: 取幂以后的结果

      7. Math.sqrt()

        => 语法: Math.sqrt(数字)

        => 返回值: 该数字的算术平方根

      8. Math.max()

        => 语法: Math.max(数字1, 数字2, 数字3, ...)

        => 返回值: 若干个数字中的最大值

      9. Math.min()

        => 语法: Math.min(数字1, 数字2, 数字3, ...)

        => 返回值: 若干个数字中的最小值

      10. Math.PI

        => 语法: Math.PI

        => 得到的就是一个近似 π 的值

20210701

一、保留小数位

 + toFixed()

   => 语法: 数字.toFixed(保留几位小数)

   => 返回值: 是一个字符串类型, 就是保留好的小数

   => 注意:

         1. 返回值是一个 **字符串类型**

         2. 当小数位不够的时候, 使用 0 补齐

进制转换(了解)

        + 进制分类

          => 从 2 ~ 36

      1. toString()

        => 语法: 十进制数字.toString(你要转换的进制)

        => 返回值: 转换好进制以后的数字

        => 注意: 返回值是一个 **字符串类型**

      2. parseInt()

        => 语法: parseInt(数字, 几进制)

截取整数

        => 返回值: 你把数字当做几进制使用, 转换成十进制

      + get: 获取

      + set: 设置

      + year: 年

      + month: 月

      + date: 日期

      + day: 天

      + hours: 小时

      + minutes: 分钟

      + seconds: 秒钟

      + milliSeconds: 毫秒

      + time: 时间

二、时间对象

        + 时间是一个 js 内的复杂数据类型

        + 专门存储时间信息的

      获取时间对象

        + 语法: var time = new Date()

        + 得到: 当前终端的时间信息

          => 注意: 和你终端设置的时区有关系

创建指定时间节点的时间对象

          => 传递数字:

            -> 第一个表示年

            -> 第二个表示月

            -> 第三个表示日

            -> 第四个表示小时

            -> 第五个表示分钟

            -> 第六个表示秒钟

            -> 注意:

              + 所有的位置都可以进行自动进位

              + 如果只传递一个参数, 不好使, 至少传递两个参数

              + 月份信息中, 0 表示 1 月, 11 表示 12 月

        2. 传递字符串

          + 'yyyy-mm-dd hh:mm:ss'

          + 'yyyy/mm/dd hh:mm:ss'

          + 注意:

            => 当你传递字符串的时候, 1 表示 2 月, 12 表示 1 月

            => 年月日 和 时分秒 之间一定要有一个 空格

三、时间对象常用方法 - 获取

      1. getFullYear()

        => 语法: 时间对象.getFullYear()

        => 返回值: 该时间对象内的年份信息

      2. getMonth()

        => 语法: 时间对象.getMonth()

        => 返回值: 该时间对象内的月份信息

        => 注意: 0 表示 1 月, 11 表示 12 月

      3. getDate()

        => 语法: 时间对象.getDate()

        => 返回值: 该时间对象内的日期信息

      4. getHours()

        => 语法: 时间对象.getHours()

        => 返回值: 该时间对象内的小时信息

        => 注意: 获取到的是 24 小时制的小时时间

      5. getMinutes()

        => 语法: 时间对象.getMinutes()

        => 返回值: 该时间对象内的分钟信息

      6. getSeconds()

        => 语法: 时间对象.getSeconds()

        => 返回值: 该时间对象内的秒钟信息

      7. getMilliSeconds()

        => 语法: 时间对象.getMilliSeconds()

        => 返回值: 该时间对象内的毫秒信息

      8. getDay()

        => 语法: 时间对象.getDay()

        => 返回值: 该时间对象是一周中的第几天, 周几

        => 注意: 0 表示周日, 1 ~ 6 表示周一到周六

      9. getTime()

        => 语法: 时间对象.getTime()

        => 返回值: 该时间对象的时间戳

        => 时间戳: 时间对象 ~ 格林威治时间 的 毫秒数

四、时间对象常用方法 - 设置

      1. setFullYear()

        => 语法: 时间对象.setFullYear(年份信息)

        => 作用: 单独设置时间对象内的年份信息

      2. setMonth()

        => 语法: 时间对象.setMonth(月份信息)

        => 作用: 单独设置时间对象内的月份信息

        => 注意: 0 表示 1 月, 11 表示 12 月

      3. setDate()

        => 语法: 时间对象.setDate(日期信息)

        => 作用: 单独设置时间对象内的日期信息

      4. setHours()

        => 语法: 时间对象.setHours(小时信息)

        => 作用: 单独设置时间对象内的小时信息

        => 注意: 24 小时制的小时信息

      5. setMinutes()

        => 语法: 时间对象.setMinutes(分钟信息)

        => 作用: 单独设置时间对象内的分钟信息

      6. setSeconds()

        => 语法: 时间对象.setSeconds(秒钟信息)

        => 作用: 单独设置时间对象内的秒钟信息

      7. setMilliSeconds()

        => 语法: 时间对象.setMilliSeconds(毫秒信息)

        => 作用: 单独设置时间对象内的毫秒信息

      8. setTime()

        => 语法: 时间对象.setTime(时间戳)

        => 作用: 根据时间戳直接定位时间

五、定时器

  + js 提供给我们的一个代码机制

  + 可以按照定时器规则去执行代码

  + 定时器分类:

    1. 延时定时器(炸弹定时器)

      => 在一段固定时间后执行一段 js 代码

      => 只执行一次, 就没有了

    2. 间隔定时器

      => 每间隔固定时间后执行一段 js 代码

      => 永不停止, 除非手动关闭

(一)、开启定时器

 1. 延时定时器

   => 语法: setTimeout(函数, 数字)

   => 函数: 在固定时间后执行的代码

   => 数字: 表示延后多少时间执行, 单位是 ms

 2. 间隔定时器

   => 语法: setInterval(函数, 数字)

   => 函数: 在间隔固定时间以后执行的代码

   => 数字: 表示每次间隔多少时间, 单位是 ms

    定时器的返回值

      + 不分定时器种类, 都是一个数字

     + 表示的是你是页面上的第几个定时器

(二)、关闭定时器

  1. clearTimeout()

   => 语法: clearTimeout(数字)

   => 数字: 你要关闭页面上的第几个定时器

  2. clearInterval()

   => 语法: clearInterval(数字)

   => 数字: 你要关闭页面上的第几个定时器

   + 不分定时器种类

   + 只要给的数字是对的就可以了

六、js 的异步代码执行机制

  + 因为 js 是一个单线程的代码, 同时只能做一个事情

  + 我们需要一些延后的事情, 不好安排

  + js 提供了一个异步代码执行机制

   => 定时器

 同步: 从上到下依次执行, 上一行没有执行完毕, 下一行不会开始

 异步: 当 js 遇到异步代码的时候, 会先拿出来, 放在异步队列内等待, 暂时不执行

  => 继续向后执行所有同步代码

  => 等到所有同步代码执行完毕

  => 在按照顺序从异步队列内执行异步代码

  结论:

   => 定时器外面的代码先执行

   => 定时器里面的代码后执行

20210702

认识 BOM

        + 全名: Browser Object Model, 浏览器对象模型

          => 私人: 一整套操作浏览器的属性和方法

          => 注意: BOM 不是 js 给的, 是 浏览器 给的

            -> BOM 的规则是浏览器制造厂商给的

          => 就是利用 js 的语法去操作浏览器的相关内容

            -> 操作浏览器滚动条

            -> 操作浏览器历史记录

            -> 操作浏览器的窗口尺寸

            -> 操作浏览器的地址栏(浏览器地址)

            -> ...

        + BOM 的顶级对象是 window(窗口)

          => 所有和 BOM 相关的内容都是, window.xxx

          => 在书写的时候, 都可以省略 window. 不写

        + 一个小问题 :

          => DOM, 是操作页面

          => DOM 也是 BOM 下的一部分

1、浏览器的窗口尺寸

        + 浏览器的可视窗口宽度和高度

        + 语法:

          1. window.innerWidth

            => 获取到的是宽度

          2. window.innerHeight

            => 获取到的是高度

        + 注意: 获取到的尺寸是包含滚动条在内的尺寸

2、浏览器的弹出层

        + 浏览器设置了三种弹出层

          1. 提示框: window.alert()

            => 表现: 有一段提示文本, 和一个确定按钮

            => 语法: window.alert('文本内容')

            => 返回值: 没有

          2. 选择框: window.confirm()

            => 表现: 在 提示框 的基础上多了一个 取消 按钮

            => 语法: window.confirm('文本内容')

            => 返回值: 是一个布尔值

              -> 当用户点击取消的时候, 就是 false

              -> 当用户点击确定的时候, 就是 true

          3. 输入框: window.prompt()

            => 表现: 在 选择框 的基础上多了一个 输入框

            => 语法: window.prompt('文本内容')

            => 返回值:

              -> 如果用户点击的是取消, 那么就是 null

              -> 如果用户点击的是确定, 那么就是文本框内的内容

            => 注意: 不管文本框内书写的是什么, 都是 字符串 类型

        + 共同点:

          => 都会阻断程序的继续执行

          => 直到用户操作为止

3、浏览器的地址栏

    在 widnow 下有一个成员叫做 location 里面存储的都是和地址栏相关的内容

      1. href

        + 是一个 读写 的属性

        + 读:

          => 语法: window.location.href

          => 得到: 就是当前页面的地址栏完整信息

          => 注意: 拿到的是 url 编码格式的地址

        + 写:

          => 语法: window.location.href = '地址'

          => 作用: 把当前地址栏的地址修改

      2. reload()

        + 是一个方法, 使用的时候需要加 ()

        + 语法: window.location.reload()

        + 作用: 把当前页面重新加载一遍, 刷新

        + 注意: 不要写在打开页面就能执行的地方

4、浏览器的历史记录

    window 下有一个成员叫做 history, 里面存储的都是和历史记录相关的信息

      1. 历史回退

        => 语法: window.history.back()

        => 作用: 回到上一个历史页面

        => 前提: 你必须得有上一个页面, 当前页面是从某一个页面跳转过来的

        => 等价于浏览器左上角的 ← 按钮

      2. 历史前进

        => 语法: widnow.history.forward()

        => 作用: 去到下一个页面

        => 前提: 你必须得有下一个页面, 当前也是是从某一个页面回退回来的

        => 等价于浏览器左上角的 → 按钮

      3. 历史跳转

        => 语法: window.history.go(数字)

          -> 正整数  历史前进

          -> 0      从新打开当前页面

          -> 负整数  历史回退

        => 作用: 进行历史跳转, 根据参数的不同进行不同的跳转

5、浏览器的标签页

        + 操作浏览器标签页的方法

      1. open()

        => 语法: window.open('地址')

        => 作用: 开启一个新的标签页打开指定地址

      2. close()

        => 语法: window.close()

        => 作用: 关闭当前标签页

6、浏览器的常见事件

        + 由浏览器行为触发的事件

      1. onload

        => 语法: window.onload = function () {}

        => 时机: 页面所有资源(html 结构, 视音频, 图片 等)加载完毕后触发

        => 作用:

          -> 当你把 js 代码书写在 head 内的时候, 并且还需要操作页面元素

          -> 需要书写一个 window.onload 事件

        => 注意: 这种书写方式, 只能绑定一个函数

      2. onresize

        => 语法: window.onresize = function () {}

        => 时机: 页面可视窗口尺寸改变的时候触发

          -> 不管横向还是纵向, 只要尺寸改变了, 就会触发

        => 作用:

          -> 响应式布局

          -> 在移动端判断横屏

      3. onscroll

        => 语法: window.onscroll = function () {}

        => 时机: 页面滚动条改变位置的时候偶触发

          -> 不管横向还是纵向, 只要滚动条位置改变了, 就会触发

7、浏览器卷去的尺寸

   + 分成 卷去的 高度 和 宽度

   (1)卷去的高度

     + 语法:

          => document.documentElement.scrollTop

            -> 当页面有 DOCTYPE 标签的时候使用

          => document.body.scrollTop

            -> 当页面没有 DOCTYPE 标签的时候使用

     + 兼容:

          => 自己定义一个变量兼容

          => var scrollTop = document.documentElement.scrollTop || document.body.scrollTop

(2)卷去的宽度

      + 语法:

          => document.documentElement.scrollLeft

            -> 当页面有 DOCTYPE 标签的时候使用

          => document.body.scrollLeft

            -> 当页面没有 DOCTYPE 标签的时候使用

      + 兼容:

          => 自己定义一个变量兼容

          => var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft

8、浏览器滚动到

        + 对浏览器的滚动条进行定位

       => 其实就是设置浏览器卷去的高度和宽度

      1. scrollTo()

        + 根据传递不同的参数决定不同的表现形式

        + 参数方案1:

          => 传递数字

          => 语法: window.scrollTo(x, y)

            -> x: 表示设置卷去的宽度

            -> y: 表示设置卷去的高度

          => 注意:

            -> 如果你传递数字设置, 必须传递两个参数

            -> 只能进行瞬间定位, 不能平滑滚动

        + 参数方案2:

          => 传递一个对象数据类型

          => 语法: window.scrollTo({

              top: yyy,

              left: xxx,

              behavior: 'smooth'

            })

          => 注意:

           -> 如果你传递对象数据类型, 那么在对象内, 可以只写一个值

           -> 默认是瞬间定位, 可以通过对象内的第三个成员来让他实现平滑滚动

 认识 DOM

        + 全名: Document Object Model 文档对象模型

          => 私人: 一整套操作文档流的属性和方法

          => 其实就是在操作页面元素

            -> 操作页面元素改变文本内容

            -> 操作页面元素改变样式

            -> 操作删除一个页面元素

            -> 操作添加一个页面元素

            -> ...

        + DOM 以树状结构出现的

          => 顶层是 document

          => 最大的标签是 html

          => 下面分成 head 和 body

          => body 下面就是一些元素标签

          => 操作 DOM 就是对这个 "树上" 的某个枝杈进行修剪或者修饰

        + 操作 DOM 的过程

          1. 找到你要操作的 "树杈" : 获取元素

          2. 对这个枝杈进行各种操作 : 操作 API

1.获取元素

    + 用一个变量来表示页面中的某一个标签

    => 当我用这个变量来操作的时候, 就是在操作这个标签

    1. id 直接使用

          => 给一个元素标签设置一个 id 属性

          => id 名是一个天生的变量, 可以在 js 内直接使用

          => 表示的就是这个标签元素

          => 强烈不推荐使用

    2. 有自己的获取元素的方式

          => 获取非常规标签(html, body, head)

          => 获取常规标签(所有标签)

2、获取非常规标签

        1. html:

          => 语法: document.documentElement

          => 得到的就是该页面的 html 标签

        2. head:

          => 语法: document.head

          => 得到的就是该页面的 head 标签

        3. body:

          => 语法: document.body

          => 得到的就是该页面的 body 标签

3、获取常规标签

    1. 根据 id 获取元素

     => 语法: document.getElementById('id名称')

     => 返回值:

     -> 如果页面中有 id 对应的元素, 那么就是这个元素

     -> 如果页面中没有 id 对应的元素, 那么就是 null

    2. 根据 类名 获取元素

    => 语法: document.getElementsByClassName('类名')

    => 返回值: 必然是一个 **伪数组**

    -> 如果页面上有 类名 对应的元素, 那么有多少获取多少, 放在伪数组内返回

    -> 如果页面上没有 类名 对应的元素, 那么就是一个空的伪数组

    => 注意: 你拿到的不是这个元素, 而是一个集合, 集合内的 [n] 才是元素

   3. 根据 标签名 获取元素

    => 语法: document.getElementsByTagName('标签名')

    => 返回值: 必然是一个 **伪数组**

    -> 如果页面上有 标签名 对应的元素, 那么有多少获取多少, 放在伪数组内返回

    -> 如果页面上没有 标签名 对应的元素, 那么就是一个空的伪数组

    => 注意: 你拿到的不是这个元素, 而是一个集合, 集合内的 [n] 才是元素

     4. 根据 name 属性来获取元素

     => 语法: document.getElementsByName('name属性')

     => 返回值: 必然是一个 **伪数组**

     -> 如果页面上有 name 属性对应的元素, 那么有多少获取多少, 放在伪数组内返回

     -> 如果页面上没有 name 属性对应的元素, 那么就是一个空的伪数组

     => 注意: 你拿到的不是这个元素, 而是一个集合, 集合内的 [n] 才是元素

QS选择器 需要带 . #

    5. 根据 选择器 获取一个元素

     => 语法: document.querySelector('选择器')

     -> 选择器: 就是 css 中捕获元素的内容

     -> .box #box div li:nth-child(1) ...

     => 返回值:

     -> 如果页面上有该 选择器 对应的元素, 那么就是满足条件的 **第一个**

     -> 如果页面上没有该 选择器 对应的元素, 那么就是 null

    6. 根据 选择器 获取一组元素

    => 语法: document.querySelectorAll('选择器')

    -> 选择器: 就是 css 中捕获元素的内容

    -> .box #box div li:nth-child(1) ...

    => 返回值: 必然是一个 **伪数组**

  -> 如果页面上有该 选择器 对应的元素, 那么有多少获取多少, 放在伪数组内返回

    -> 如果页面上没有该 选择器 对应的元素, 那么就是空的伪数组

    => 注意: 你拿到的不是这个元素, 而是一个集合, 集合内的 [n] 才是元素

 4、操作元素内容

   1. innerText

    + 是一个 读写 的属性

    + 读:

     => 语法: 元素.innerText

     => 得到: 该元素内的所有文本内容

    + 写:

     => 语法: 元素.innerText = '值'

     => 作用: 完全覆盖式的书写该元素内的文本内容

     => 注意: 如果你设置一个 html 格式的字符串, 那么不会解析出来的

   2. innerHTML

     + 是一个 读写 的属性

     + 读:

     => 语法: 元素.innerHTML

     => 得到: 该元素下的所有内容, 以字符串的形式给你

     + 写:

     => 语法: 元素.innerHTML = '值'

   => 作用: 完全覆盖式的书写该元素内的超文本内容

   => 注意: 如果你设置的是一个 html 格式的字符串, 那么会把标签内容解析出来

    3. value

     + 是一个 读写 的属性

     + 读:

     => 语法: 表单元素.value

     => 得到: 该标签元素的 value 值

     + 写:

     => 语法: 表单元素.value = '值'

     => 作用: 完全覆盖式的书写该表单元素的 value 值

知识点: 模板字符串

        => JS 定义字符串的一种方式

        => 只不过是使用 反引号(``) 来定义

        => 区别:

          1. 反引号 定义的字符串 可以换行书写

          2. 可以直接在字符串内解析变量

            + 当你需要解析变量的时候, 直接书写 ${变量}

20210705

认识元素的属性

      + 一个元素的属性由 属性名="属性值" 组成的

一、认识属性的分类

      + 在 html 内, 元素的属性大致分成三类

      1. 原生属性, 在 html 规范内有的属性

        => 比如 id, class, style, type, ...

        => 对于标签有特殊意义

      2. 自定义属性, 在 html 规范内没有的属性

        => 我们自己随便书写的一个属性名, 对于标签没有特殊意义

        => 只是为了标注一些内容在标签身上

      3. H5 自定义属性

        => H5 标准下提出的一个自定义属性的书写方式

        => 书写规则: data- 开头

        => 例子: data-a="100"

          -> 属性名: a

          -> 属性值: 100

二、操作元素属性

      1. 操作原生属性

        + 直接使用 元素.属性名 的形式进行操作

        + 读:

          => 语法: 元素.属性名

          => 得到: 该元素, 该属性名对应的属性值

        + 写:

          => 语法: 元素.属性名 = '属性值'

          => 作用: 修改该元素的该属性的值

=> 注意: 布尔类型的属性可以直接使用 true 或者 false 来赋值

      2. 操作自定义属性

        2-1. 获取自定义属性的值

          => 语法: 元素.getAttribute('属性名')

          => 返回值: 该元素内该属性名对应的属性值

        2-2. 设置自定义属性的值

          => 语法: 元素.setAttribute('属性名', '属性值')

          => 作用: 给元素设置一个自定义属性

        2-3. 删除自定义属性

          => 语法: 元素.removeAttribute('属性名')

          => 作用: 删除该元素的某一个属性

  3. 操作 H5 自定义属性

          + 元素身上有一个叫做 dataset 的属性

            => 是一个类似于 对象 的数据类型

            => 里面存储的都是元素标签身上 data- 开头的自定义属性

            => 对于 H5 自定义属性的操作, 就是对这个 dataset 的操作

          + 获取:

            => 元素.dataset.属性名

          + 设置:

            => 元素.dataset.属性名 = '值'

          + 删除:

            => delete 元素.dataset.属性名

      + 以上所有操作元素属性的方法

        => 注意 : 一般不操作元素的 类名 和 样式

三、操作元素样式

  + 在 JS 内操作元素样式分成三种

    1. 设置元素的行内样式

    2. 获取元素行内样式(只能获取到行内样式)

    3. 获取元素非行内样式(可以获取行内样式也可以获取非行内样式)

  1. 设置元素的行内样式

   + 语法: 元素.style.样式名 = 样式值

   + 给元素设置行内样式

   + 注意: 样式名带有中划线的名字, 需要写成驼峰或者数组关联语法

   2. 获取元素的行内样式

    + 语法: 元素.style.样式名

    + 得到: 该元素指定样式对应的值

    + 注意: 只能拿到行内样式的值, 非行内样式是拿不到值的

    + 注意: 样式名带有中划线的名字, 需要写成驼峰或者数组关联语法

   3. 获取元素非行内样式

     + 标准浏览器

     => 语法: window.getComputedStyle(要获取样式的元素).样式名

     => 返回值: 该元素指定样式对应的值

     => 注意: 样式名带有中划线的名字, 需要写成驼峰或者数组关联语法

     => 注意: 行内和非行内都能拿到样式值, 但是只能在标准浏览器下使用

  + IE 低版本

     => 语法: 元素.currentStyle.样式名

     => 得到: 该元素指定样式对应的值

     => 注意: 样式名带有中划线的名字, 需要写成驼峰或者数组关联语法

     => 注意: 行内和非行内都能拿到样式值, 但是只能在 IE 低版本浏览器下使用

 四、操作元素类名

      1. className

        + 实际上就是操作元素的原生属性

        + 只不过在 JS 内 class 是一个关键字

        + 所以我们操作的原生属性改名叫做 className

        + 读:

          => 元素.className

          => 得到的就是元素的完整 类名

        + 写:

          => 元素.className = '值'

          => 作用: 设置元素类名, 完全覆盖式的设置

        + 追加:

          => 元素.className += '空格 值'

          => 注意: 值的位置要书写一个空格

      2. classList

        + 每一个元素有一个属性叫做 classList

        + 是一个类似数组的集合, 里面记录了元素所有的类名

        + 添加一个类名

          => 语法: 元素.classList.add('要添加的类名')

          => 作用: 给该元素添加一个类名, 但是重复的不会添加进去了

        + 删除一个类名

          => 语法: 元素.classList.remove('要删除的类名')

          => 作用: 该该元素删除一个类名

        + 切换一个类名

          => 语法: 元素.classList.toggle('要切换的类名')

          => 作用: 该元素切换一个类名

            -> 切换: 有就删除, 没有就添加

20210706

一、获取元素尺寸

+ 元素尺寸(私人): 占地面积

=> 一个元素在页面(文档流)内占多少位置

1. offsetWidth 和 offsetHeight

=> 语法:

-> 元素.offsetWidth

-> 元素.offsetHeight

=> 得到: 该元素的 内容 + padding + border 区域的尺寸

=> 注意:不管盒子模型是什么状态, 都是 内容 + padding + border 区域的尺寸

-> 当元素被 display: none; 的时候, 是拿不到尺寸的

2. clientWidth 和 clientHeight

=> 语法:

-> 元素.clientWidth

-> 元素.clientHeight

=> 得到: 该元素的 内容 + padding 区域的尺寸

=> 注意:-> 不管盒子模型是什么状态, 都是 内容 + padding 区域的尺寸

-> 当元素被 display: none; 的时候, 是拿不到尺寸的

二、一个元素结构有两个父级

+ 结构父级: 书写在这个标签外部的标签

+ 定位父级: 当你需要给 p 标签设置绝对定位的时候, 根据谁来定位, 谁就是他的定位父级

=> 如果到窗口以后, 那么定位父级就是 body

三、获取元素偏移量

+ 相对于参考元素的 左边 和 上边 的距离

1. 元素的偏移量参考元素是谁 ?

=> 就是这个元素的 定位父级

=> 就是假设需要给这个元素设置绝对定位的时候, 根据谁定位

=> 你获取偏移量的时候, 就参考谁

2. offsetLeft 和 offsetTop

=> 语法:

-> 元素.offsetLeft

-> 元素.offsetTop

=> 得到: 该元素相对于参考元素 左边 和 上边 的距离

四、获取窗口尺寸

1、 BOM 级别的获取

=> 语法:

-> window.innerWidth

-> window.innerHeight

=> 得到:

-> 窗口的宽度和高度

 -> 是包含滚动条的尺寸

2、 DOM 级别的获取

=> 语法:

 -> document.documentElement.clientWidth

 -> document.documentElement.clientHeight

=> 得到:-> 获取可视窗口的宽度和高度-> 不包含滚动条的尺寸

+ class 节点 和 id 节点只是用来描述这个 div 标签的

+ 不作为独立节点出现

五、DOM 节点的操作

+ 什么是节点 ?

=> 页面的每一个组成部分

+ 注意 :

=> 标签是一个节点

=> 节点不都是标签

+ 在一个页面内

=> 所有内容都是一个一个的节点

=> document

=> 元素节点(标签)

=> 文本节点(文本内容)

=> 注释节点(注释内容)

=> 属性节点: 一个标签身上书写的属性(包含自定义属性和原生属性), 不作为独立节点出现

六、获取节点:

+ 包含两种

1. 获取元素节点

 1-1. 非常规标签

 => html

 => body

 => head

1-2. 常规标签

=> getElementById()

 => getElementsByClassName()

 => getElementsByTagName()

 => getElementsByName()

 => querySelector()

 => querySelectorAll()

2. 获取节点(包含但不限于元素节点)

 2-1. childNodes

 => 语法: 父节点.childNodes

=> 得到: 是一个伪数组, 内部包含该父节点下的所有子一级  节点

2-2. children

=> 语法: 父节点.children

 => 得到: 是一个伪数组, 内部包含该父节点下的所有子一级  元素节点

 2-3. firstChild

 => 语法: 父节点.firstChild

 => 得到: 该父节点下的第一个子  节点

 2-4. firstElementChild

 => 语法: 父节点.firstElementChild

 => 得到: 该父节点下的第一个子  元素节点

 2-5. lastChild

=> 语法: 父节点.lastChild

 => 得到: 该父节点下的最后一个子  节点

 2-6. lastElementChild

 => 语法: 父节点.lastElementChild

 => 得到: 该父节点下的最后一个子  元素节点

 2-7. previousSibling

 => 语法: 节点.previousSibling

 => 得到: 该节点的上一个兄弟  节点

 2-8. previousElementSibling

 => 语法: 节点.previousElementSibling

  => 得到: 该节点的上一个兄弟  元素节点

 2-9. nextSibling

  => 语法: 节点.nextSibling

  => 得到: 该节点的下一个兄弟  节点

 2-10. nextElementSibling

  => 语法: 节点.nextElementsSibling

  => 得到: 该节点的下一个兄弟  元素节点

 2-11. parentNode

  => 语法: 节点.parentNode

  => 得到: 该节点的父  节点

 2-12. parentElement

  => 语法: 节点.parentElement

  => 得到: 该节点的父  元素节点

 2-13. attributes

  => 语法: 节点.attributes

  => 得到: 是一个类似于数组的数据结构, 里面包含该元素的所有属性节点

七、创建节点

        + 无中生有, 凭空捏造

        + 利用 js 代码创建一个 节点 出来

      1. 创建元素节点

        => 语法: document.createElement('你要创建的标签名')

        => 可以创建 W3C 规范内的标签, 也可以创建自定义标签

        => 返回值: 一个元素节点

      2. 创建文本节点

        => 语法: document.createTextNode('文本内容')

        => 返回值: 就是一个文本节点, 不是字符串

八、插入节点

        + 把一个节点插入到另一个节点内容

        + 不一定非得是插入到页面上

      1. appendChild()

        => 语法: 父节点.appendChild(子节点)

        => 作用: 把该子节点插入到父节点内部, 并且排列在最后的位置

      2. insertBefore()

        => 语法: 父节点.insertBeofre(子节点, 谁的前面)

        => 作用: 把该子节点插入到父节点内部, 并且排列在哪一个节点的前面

九、删除节点

        + 把一个节点删除掉

        + 不一定非得删除页面上的, 只要是节点, 在某一个节点内, 就可以删除

      1. removeChild()

        => 语法: 父节点.removeChild(子节点)

        => 作用: 把该子节点从父节点内移除

      2. remove()

        => 语法: 节点.remove()

        => 作用: 自杀, 直接把自己移除

十、替换节点

      1. replaceChild()

        => 语法: 父节点.replaceChild(换上节点, 换下节点)

        => 作用: 在该父节点内, 使用换上节点替换掉换下节点

十一、克隆节点

        + 把某一个节点复制一份一模一样的出来

      1. cloneNode()

        => 语法: 节点.cloneNode(参数)

          -> 参数: 选填

          -> 默认是 false, 表示不克隆后代节点

          -> 选填是 true, 表示克隆后代节点

        => 返回值: 该节点复制了一份一模一样的

十二、节点属性

        + 用来描述一类 节点 特点的一个属性

      1. nodeType

        + 表示节点类名, 以一个数字的形式表示

        + 元素节点.nodeType : 1

        + 属性节点.nodeType : 2

        + 文本节点.nodeType : 3

        + 注释节点.nodeType : 8

      2. nodeName

        + 表示节点名称

        + 元素节点.nodeName : 大写标签名

        + 属性节点.nodeName : 属性名

        + 文本节点.nodeName : #text

        + 注释节点.nodeName : #comment

      3. nodeValue

        + 表示该节点的内容部分

        + 元素节点.nodeValue : null

        + 属性节点.nodeValue : 属性值

        + 文本节点.nodeValue : 文本内容(包含换行和空格)

        + 注释节点.nodeValue : 注释内容(包含换行和空格)

20210707

了解事件

        + 我们使用代码的方式 和 一个内容 约定好一个行为

          => 当你打开浏览器, 触发了该行为的时候

          => 会有对应的代码执行

一、事件三要素:

概念: 在事件处理函数中, this 就是 事件源

 1. 事件源: 和谁约定事件(和由谁触发不一样)

2. 事件类型: 约定了一个什么事件

3. 事件处理函数: 当行为发生的时候, 需要执行的函数

 + 这三个内容目的是为了绑定事件, 注册事件, 就是提前做好约定

二、事件绑定:

1. DOM 0级 事件

 => 语法: 事件源.on事件类型 = 事件处理函数

 => 注意: 给一个事件源的同一个事件类型, 只能绑定一个事件处理函数

2. DOM 2级 事件(事件侦听器 / 事件监听器)

 2-1. 标准浏览器

  => 语法: 事件源.addEventListener('事件类型', 事件处理函数)

=> 特点:

 -> 可以给同一个事件源的同一个事件类型绑定多个事件处理函数

 -> 多个事件处理函数, 顺序绑定, 顺序执行

2-2. IE 低版本

 => 语法: 事件源.attachEvent('on事件类型', 事件处理函数)

  => 特点:

 -> 可以给同一个事件源的同一个事件类型绑定多个事件处理函数

 -> 多个事件处理函数, 顺序绑定, 倒序执行

三、事件解绑

1. DOM 0级 事件解绑

 => 语法: 事件源.on事件类型 = null

 => 因为赋值符号, 当你给这个事件类型赋值为 null 的时候

-> 会把本身的事件处理函数覆盖

-> 当你再次出发行为的时候, 没有事件处理函数执行

 -> 相当于解绑了事件

2. DOM 2级 事件解绑

 2-1. 标准浏览器解绑

  => 语法: 事件源.removeEventListener('事件类型', 事件处理函数)

 2-2. IE 低版本解绑

 => 语法: 事件源.detachEvent('on事件类型', 事件处理函数)

 + 注意:

 => 当你使用 DOM 2级 事件解绑的时候

 => 因为函数是一个复杂数据类型, 所以你在绑定的时候

 => 需要把函数单独书写出来, 以函数名的形式进行绑定和解绑

四、事件类型

 + JS 给我们提供了哪些事件可以直接使用

 + 注意: JS 的原生事件没有大写字母

 + 在 JS 内事件类型大致分为几类

 1. 鼠标事件

 2. 键盘事件

 3. 浏览器事件

4. 表单事件

 5. 触摸事件

 6. 其他事件

五、鼠标事件

+ 依赖鼠标行为触发的事件

+ 移入移出事件的区别

 => over / out 一套, 不光移入事件源触发, 移入子元素也触发

 1. click: 鼠标左键单击

 2. dblclick: 鼠标左键双击

 3. contextmenu: 鼠标右键单击

 4. mousedown: 鼠标按下(任何一个按键按下)

 5. mouseup: 鼠标抬起

 6. mousemove: 鼠标移动(光标只要位置变换就会触发)

 7. mouseover: 鼠标移入(光标从外面进入事件源范围内)

 8. mouseout: 鼠标移出(光标从事件源内部离开)

 9. mousenter: 鼠标移入

 10. mouseleave: 鼠标移出

六、键盘事件

 + 依赖键盘行为触发的事件

 + 注意:

 1. 所有元素都可以绑定键盘事件, 但是不是谁都能触发

 => 一般可以有 window / document / 表单元素 触发

 2. 触发键盘事件, 一般不去考虑中文输入法

  1. keydown: 键盘按下事件

  => 键盘上任何一个按键按下都会触发

 2. keyup: 键盘抬起事件

        => 键盘上任何一个按键抬起都会触发

  3. keypress: 键盘键入事件

   => 必须要按下可以真实键入内容的按键或者回车键

  => 键入的内容必须和按下的内容一致

七、表单事件

 + 专门提供给表单元素的事件

1. focus: 聚焦事件

 2. blur: 失焦事件

 3. change: 改变事件

 => 要求 聚焦 和 失焦 的时候, 内容不一致, 才会触发

 4. input: 输入事件

 => 只要表单元素输入内容或者删除内容, 就会实时触发

 5. reset: 重置事件

 => 事件需要绑定给 form 标签

 => 由 form 标签内的 reset 按钮触发

 6. submit: 提交事件

 => 事件需要绑定给 form 标签

  => 由 form 标签内的 submit 按钮触发

八、触摸事件

 + 移动端事件, 只能在移动端设备使用

 1. touchstart

 => 触摸开始: 表示手接触到屏幕的瞬间

 2. touchmove

 => 触摸移动: 表示手在屏幕上移动的时候

3. touchend

 => 触摸结束: 表示手离开屏幕的瞬间

九、其他事件

1. selectstart

 => 选择开始: 当你去框选页面文本内容的时候触发

 2. transitionend

 => 过渡结束事件: 当你元素具有过渡属性的时候, 结束以后触发

 => 过渡多少个属性, 触发多少次

 3. ..

十、事件对象

 + 是一个对象数据类型, 记录了本次事件的所有信息

 + 是一个记录了本次事件触发的所有信息的对象数据类型

 获取事件对象

+ 标准浏览器

=> 直接在事件处理函数位置书写一个形参, 会在事件触发的时候

=> 由浏览器自动传递实参

=> 传递的实参就是 事件对象

 + IE 低版本

 => 直接使用 window.event 来获取事件对象

 事件对象兼容: 需要拿信息的时候 就去写它 , 不需要信息的时候就不

=> 在事件处理函数的时候, 正常接受形参

处理事件对象兼容

  => 书写: e = e || window.event

十一、事件对象信息 - 鼠标事件

 + 坐标点

 1. clientX 和 clientY

 => 光标相对于浏览器可视窗口左上角的坐标位置

 2. pageX 和 pageY

 => 光标相对于文档流左上角的坐标位置

 3. offsetX 和 offsetY

 => 光标相对于触发事件的元素左上角的坐标点

十二、事件对象信息 - 键盘事件

 1. 按下的是哪一个按键

2. 是否是组合按键

 1. 按下的哪一个按键

 => 在事件对象内有一个信息叫做 keyCode

 => 注意:

-> 有一个兼容性问题

-> 在标准浏览器下使用 keyCode

 -> 在 FireFox < 20 的浏览器下使用 which

 2. 组合按键

 => 在事件对象内有四个信息

 -> shiftKey

 -> ctrlKey

 -> altKey

 -> metaKey

 + 在 win 系统里面表示 win 键

 + 在 OS 系统里面表示 command 键

 => 以上四个属性的值都是 布尔值

 -> true 表示按下了

-> false 表示没有按下

 => 如果你想判断组合按键, 只要在判断键盘编码的时候

 -> 额外判断一下以上的四个属性就可以了

20210708

一、事件的传播

 + 当一个事件在浏览器中触发的时候

 + 不光在自己元素身上触发, 是会传播出去的

+ 注意: 传播的是事件行为

 + 概念: 当你在一个元素身上触发行为的时候, 会按照结构父级的顺序, 向上直到 window 传递该行为

1、真实的传播

首先 window 接收到行为, 按照结构级别逐层向下传递到准确触发事件的元素

在从准确触发事件的元素向上传递, 回到 window

在这个过程中,每一个内容都接受了两次行为, 但是为什么事件处理函数只被调用了一次

因为浏览器的事件执行机制

2、浏览器的事件执行机制

+ 所有的浏览器默认在 向上传递的 过程中触发事件

3、事件的三个阶段:

 1. 捕获阶段: 从 window 向 目标 传递的过程

 2. 目标阶段: 事件发生在 目标 身上

 3. 冒泡阶段: 从 目标 向 window 传递的过程

二、事件的目标冒泡和捕获

1、事件冒泡: 在事件传播的过程中, 从 目标 到 window 的过程

2、事件捕获: 在事件传播的过程中, 从 window 到 目标 的过程

 => 在 IE 低版本内 只能按照 冒泡的顺序来执行

 => 在标准浏览器下, 事件默认在冒泡阶段执行

 => 可以选择在捕获阶段执行

3、事件目标: 准确触发事件的那个元素

4、如何获取事件目标

+ 在事件对象内有一个固定的信息表示本次事件的事件目标

 + 标准浏览器: 事件对象.target

 + IE 低版本: 事件对象.srcElement

 + 兼容: 我们自己定义一个变量来兼容

5、处理事件目标兼容

  => var target = e.target || e.srcElement

三、如何在捕获阶段执行事件

 + DOM 0级 事件绑定没有办法修改

 + DOM 2级 事件绑定才可以修改事件的执行阶段

 => 通过 addEventListener() 的第三个参数来决定

 => 默认是 false, 表示冒泡阶段

 => 选填是 true, 表示捕获阶段

四、阻止事件传播

+ 只要你点击 p 的时候, 事件不再继续传播

+ 因为事件是默认在 冒泡阶段 触发的

+ 只要不继续向上传播, div 的事件就不会触发了

1、标准浏览器

+ 事件对象.stopPropagation()

2、IE 低版本

+ 事件对象.cancelBubble = true

3、兼容:

 + 不能用 或 运算符

 + 因为有一个会报错

 + try {} catch() {} 语法

 => 首先执行 try 后面的 {} 内部的代码

 => 如果代码没有报错, 那么 catch 就不执行了

=> 如果代码报错了, 那么执行 catch 内部的代码

五、默认行为

 + 不需要绑定事件, 当你触发行为的时候, 就会出现效果的事情

+ 我们叫做默认行为

 + 例子:

=> a 标签的点击行为

 => form 表单的提交行为

 1、标准浏览器

 + 事件对象.preventDefault()

 2、IE 低版本

 + 事件对象.returnValue = false

 3、兼容

 + try ... catch 兼容

 4、通用的阻止默认行为

+ 直接书写 return false

 + 会有问题:

 => 这个代码必须写在最后一行

=> 必须保证前面的代码百分之一百没有错误

掌握

 1. 事件是会传播的: 按照结构父级的顺序, 传递的 **事件行为**

2. 不管自己有没有事件, 行为一定会向上继续传播

 3. 如何获取事件目标

 => 标准浏览器, 事件对象.target

 => IE 低版本, 事件对象.srcElement

  4. 阻止事件传播

  => try { 事件对象.stopPropagation() } catch (err) { e.cancelBubble = true }

  5. 阻止默认行为

   => try { 事件对象.preventDefault() } catch (err) { e.returnValue = false }

  => 通用的(慎用): return false

 了解

        1. 传播过程, 从 捕获阶段 到 目标阶段 到 冒泡阶段

        2. 如何在 标准浏览器 下, 让事件在 捕获阶段 触发

六、事件委托

需求:

 + 页面上有 若干个 li, 点击每一个 li 在控制台输出该标签内的文本内容

 + 有一个 button 按钮, 点击的时候, 可以创建一个 li 插入到页面中

1、循环绑定事件 :

  => 一种给多个元素绑定事件的方式

  => 缺点:

   -> 对于动态添加的元素(后期渲染的元素), 不够友好

   -> 大量操作 DOM

2、事件委托来完成 :

   => 因为需求是点击 li 的时候, 做一些事情

   => 因为事件的传播, 如果我给 结构父级 身上绑定一个 点击事件

   => 也会因为点击 li 而触发

   => 例子: 给 ul 绑定一个点击事件

      -> 点击 ul 会触发

      -> 点击 li 会触发

   => 在事件对象中有一个信息叫做 事件目标

     -> 准确触发事件的那个元素

      -> 如果我是因为点击 ul 触发的事件, 事件目标 就是 ul

      -> 如果我是因为点击 li 触发的事件, 事件目标 就是 li

      => 可以通过判断事件目标来确定我点击的是 li

   => 原则:

      -> 尽可能找到距离最近的公共的父级

      -> 尽可能找到在页面上不动的元素来委托

20210709

认识正则表达式 - RegExp

  + JS 中的数据类型, 是一个复杂数据类型

 + 作用: 专门用来验证字符串是否符合规则

如何创建一个正则表达式

1. 字面量方式创建

=> 语法: var reg = /abcd/

2. 内置构造函数创建

 => 语法: var reg = new RegExp('abcd')

一、两种创建正则表达式的区别

  1. 语法不一样

 2. 书写标识符的时候

 => 字面量方式直接书写在正则的后面

 => 内置构造函数, 以第二个参数的方式传递

 3. 拼接字符串

=> 字面量方式不接受拼接字符串

 => 内置构造函数方式, 可以拼接字符串

4. 基本元字符书写

 => 字面量方式的时候, 直接书写 \s\d\w

 => 内置构造函数书写的时候, 需要书写 \\s\\d\\w

二、正则表达式的常用方法

 1. test() 匹配

 => 语法: 正则表达式.test('要检测的字符串')

=> 返回值: 一个布尔值

   必须是连着完整的内容

  -> 如果该字符串符合正则的规则, 那么就是 true

 -> 如果该字符串不符合正则的规则, 那么就是 false

 2. exec() 捕获

 => 语法: 正则.exec(字符串)

 => 作用: 从 字符串中 把满足正则条件的部分获取出来

 => 返回值:

 -> 原始字符串中没有符合正则要求的字符串片段

 + null

  -> 原始字符串中有符合正则要求的片段

 + 正则没有 () 也没有 全局标识符g

  + 返回值是一个数组

 + 索引 0 是从字符串中捕获出来的满足正则条件的第一个内容

 + 注意: 不管捕获多少次, 每次都是从原始字符串的索引 0 开始检索

 3、+ 正则有全局标识符 g

 + 返回值是一个数组

 + 索引 0 是从字符串中捕获出来的满足正则条件的第一个内容

 + 注意: 第二次捕获是从第一次的结束位置开始向后查询, 直到最后捕获不到为止, 再下一次的时候, 又从字符串的 索引0 开始检索

 4、+ 有 ()

  + 返回的是一个数组

  + 索引 0 是从字符串中捕获出来的满足正则条件的第一个内容

  + 从索引 1 开始, 依次是每一个小括号的单独内容

 + 注意: 按照小括号的开始标志, 来数是第几个小括号

扩展:

 + () 有两个意义

          => 一个整体

          => 单独捕获

   + 如果你想只使用一个意义, 整体的所用, 不想在捕获的时候单独捕获出来

          => 你可以写成 (?:)

          => 匹配但不捕获

三、为什么内置构造函数需要书写双斜线

1、+ 字符串

  => 被引号包裹的所有内容叫做字符串

  => 当你在字符串内书写 斜线(\) 的时候, 是表示转义符号

  => 会把紧挨着他的一个内容转换

  -> 如果是有意义的内容转换成没有意义的内容

  -> 如果是没有意义的内容转换成有意义的内容

 -> 例子: n 是一个没有意义的字母

 + 当 n 和 转义符 在一起的时候, 就表示 换行

2、+ new RegExp()

  => 第一个参数需要一个字符串

  => 你写的字符串就是正则内部的内容

  => 如果你想得到 /\s\d\w/

 => 那么你要给他的字符串里面写上 \s\d\w

 => 但是, 因为在字符串中 \ 是转义符

 => 当你书写字符串 '\s' 的时候, \ 就会把 s 转换成有意义的特殊内容

->可以字符串中确实没有 \s 组成的特殊意义字符, 所以就变成了一个 单独的 字母 

+ 解决问题:

 => 使用 \ 把有意义的特殊字符 \ 转换成没有意义的文本 \

   => 当你书写 '\\s\\d\\w' 的时候, 实际字符串就是 '\s\d\w'

四、正则表达式的符号 - 基本元字符

 正则表达式的组成部分

1. 元字符

  => 所有的文本内容

 => 特殊符号, 用符号表示一类内容

2. 标识符

 => 书写在正则的外面, 用来修饰正则表达式的

3、基本元字符

1. \d 表示 一位 数字 (因为没有开头结尾所以字符串中至少包含一位 数字(0-9))

 2. \D   表示 一位 非 数字 (字符串中至少包含一位 非数字 内容)

 3. \s   表示 一位 空白内容(空格/缩进/制表符/...)

 4. \S   表示 一位 非 空白内容

 5. \w   表示 一位 数字(0-9)字母(a-zA-Z)下划线(_) 中的任意一个

 6. \W   表示 一位 非 数字字母下划线 中的任意一个

 7. .    表示 一位 非 换行以外的任意内容  (只要不是换行,任意都行)

 8. \    表示 转义符

  -> 把有意义的符号转换成没有意义的普通文本

 -> 把没有意义的文本转换成有意义的符号

五、元字符 - 边界符号

 1. ^   表示 字符串 开始

 2. $   表示 字符串 结尾

  + 注意: 当 ^ 和 $ 一起使用的时候, 表示的是从开头到结尾

六、元字符 - 限定符

 + 注意: 一个限定符只能修饰符号前面的一个内容的出现次数

 1. *     表示出现 0 ~ 多次

  2. +     表示出现 1 ~ 多次

  3. ?     表示出现 0 ~ 1 次

  4. {n}   表示出现 指定 多少次  (n 表示正整数 或 零)(n 写几,就是几次)

  5. {n,}  表示出现 至少 多少次

       => {0,} 等价于 *

       => {1,} 等价于 +

  6. {n,m} 表示出现 n ~ m 次  ( 包含n , 也包含 m)

       => {0,1} 等价于 ?

七、元字符 - 特殊符号

1. ()

 => 意义1: 一个整体

 => 意义2: 单独捕获(欠着)

2. |

 => 意义: 或者的意思

 => 注意: 或的边界, 要么是 (), 要么就到正则的边界

3. []

 => 意义: 包含

  => 注意: 一个 [] 内可以写多个内容, 但是一个 [] 只占一个字符位置, 表示 [] 内的任意一个都行

 => [0-9] 等价于 \d

  => [0-9a-zA-Z_] 等价于 \w

    // 表示字符串从开头到结尾只能有一位字符

    // 这一位字符可以是 a 可以是 b 可以是 c 可以是 d

    // [abcd] 等价于 (a|b|c|d)

    // var reg = /^[abcd]$/

4. [^]

=> 意义: 非

 => 注意: 一个 [^] 内可以写多个内容, 但是一个 [^] 只占一个字符位置, 表示 [^] 内的任意一个都不行

 => [^0-9] 等价于 \D

 => [^0-9a-zA-Z_] 等价于 \W 

  4 . [^]

  // 表示字符串从开头到结尾只能有一位字符

  // 这一位字符不能是 a 不能是 b 不能是 c 不能是 d, 其他的都可以

5. - 中划线

 => 意义: 到 至

 => 在 [] 里面用

 => 注意: 需要和 [] 或者 [^] 连用

 => 注意: 需要 ASCII 编码连着的才可以使用

八、标识符

 + 书写在正则表达式的外面, 专门用来修饰整个正则表达式的符号

 1. i (ignore)

 => 表示忽略大小写不计

 // 标识符 i 修饰正则表达式, 表示该正则在验证字符串的时候忽略大小写不计

 2. g (global) 全局

正则有全局标识符 g

   + 返回值是一个数组

  + 索引 0 是从字符串中捕获出来的满足正则条件的第一个内容

  + 注意: 第二次捕获是从第一次的结束位置开始向后查询, 直到最后捕获不到为止, 再下一次的时候, 又从字符串的 索引0 开始检索

九、正则的两个特性

 1. 懒惰性

        => 每一次捕获都是从原始字符串的索引 0 位置开始检索

        => 解决: 使用全局标识符 g

 2. 贪婪性

        => 贪婪匹配:   能拿多少拿多少     尽可能多的匹配内容

        => 非贪婪匹配: 能拿多少拿多少     尽可能少的匹配内容

  

十、字符串方法

 + 正则常用方法: 正则.xxx()

 + 字符串常用方法: 字符串.xxx()

 1. replace()

        => 语法:

          -> 字符串.replace('换下字符', '换上字符')

          -> 字符串.replace(正则表达式, '换上字符')

        => 返回值: 替换好的字符串

          -> 当你的第一个参数传递字符串的时候, 只能替换一个

          -> 当你的第一个参数传递正则表达式的时候, 只能替换一个

          -> 但是如果你的正则表达式有全局标识符 g, 那么有多少替换多少

 2. search()

        => 语法:

          -> 字符串.search(字符串片段)

          -> 字符串.search(正则表达式)

        => 作用: 在原始字符串内查找指定内容

        => 返回值:

          -> 原始字符串内有指定内容, 那么就是这个内容的索引

          -> 原始字符串内没有指定内容, 那么就是 -1

3. match()

        => 语法:

          -> 字符串.match(字符串片段)

          -> 字符串.match(正则表达式)

        => 返回值: 从原始字符串中捕获满足条件的片段

        => 注意: 如果你的参数传递的是字符串或者没有全局标识符g的正则, 那么和 exec 返回值一模一样

          -> 当你的正则有全局标识符 g 的时候, 那么返回值是一个数组

          -> 能从原始字符串内捕获多少, 数组里就有多少内容

20210712

 一、自执行函数

  + 一个会自己调用自己的函数

  + 当这个函数定义好以后, 直接被调用

 1、自执行函数的意义:

  + 利用函数的私有作用域, 保护变量私有化

  + 不去污染全局

2、 语法:

   => (function () { 代码 })()

   => ~function () { 代码 }()

   => !function () {}()

二、this 指向 !! 重要 !! 重要 !! 重要

   + this 是一个关键字

   + 是一个使用在作用域内的关键字

     1、 要么全局使用

       -> this 就是 window

     2、 要么使用在函数内

       -> this 表示的是该函数的 context(执行上下文)

        -> 私人: 熟读并背诵全文

三、(重要) 函数内的 this (私人)

 + 概念: 函数内的 this, 和 函数如何定义没有关系, 和 函数在哪定义没有关系

    => 只看函数是如何被调用的(箭头函数除外)

     + 几种调用方式, 决定不同的 this 指向

1. 普通调用

   => 函数名()

   => 该函数内的 this 指向 window

2. 对象调用

   => 对象名.函数名()

   -> 对象名['函数名']()

   -> 数组[索引]()

   => 该函数内的 this 指向 点 前面的内容

   -> 也就是那个对象或者数组

3. 定时器处理函数

    => setTimeout(函数, 时间)

     setInterval(函数, 时间)

    => 该函数内的 this 指向 window

 4. 事件处理函数

     => 事件源.on事件类型 = 事件处理函数

      事件源.addEventListener('事件类型', 事件处理函数)

      => 该函数内的 this 指向 事件源(谁身上的事件)

5. 自执行函数

      => (function () {})()

      ~function () {}()

      !function () {}()

       => 该函数内的 this 指向 window

6. 箭头函数

      -> () => {}

      -> this 指向 外部作用域 的 this

四、强行改变 this 指向

        + 不管你本身指向哪, 我让你指向哪, 你就得指向哪

      1. call

        + 语法: 跟随在函数名后面调用

          =>方式一 : 函数名.call()

          =>方式二 : 对象名.函数名.call()

        + 意义: 修改 函数内 的 this 指向

        + 参数:

          => 第二个参数开始: 依次给函数传递实参

        + 特点: 立即调用函数

      2. apply

        + 语法: 跟随在函数名后面调用

          =>方式一 :  函数名.apply()

          =>方式二 :  对象名.函数名.apply()

        + 意义: 修改 函数内 的 this 指向

        + 参数:

          => 第一个参数: 要改变的 this 指向

          => 第二个参数: 是一个数组或者伪数组都行, 里面的每一项依次给函数传递实参

        + 特点: 立即调用函数

        + 特殊作用: 改变给函数传递参数的方式

      3. bind

        + 语法: 跟随在函数名后面调用

          =>方式一 :  函数名.bind()

          =>方式二 :  对象名.函数名.bind()

        + 意义: 修改 函数内 的 this 指向

          => 第一个参数: 要改变的 this 指向

          => 第二个参数开始: 依次给函数传递实参

        + 特点:

          => 不会立即调用函数, 而是返回一个新的函数

          => 一个被改变了 this 指向的新函数

        + 特殊作用: 改变一些不会立即执行的函数内的 this 指向

ES6 的语法 :

一、ES6 定义变量

+ 新增了两个定义变量的关键字

=> let     变量    是会变的量

=> const   常量    是不会变的量 (是一个特殊的变量 ,是变量里的一部分)

1. let/const 和 var 的区别

1-1. 预解析 (只解析 var 和 声明式函数)

=> var 会进行预解析, 可以先使用后定义

=> let/const 不会进行预解析, 必须先定义后使用

 1-2. 变量重名

  => var 定义的变量可以重名, 只是第二个没有意义

  => let/const 不允许在同一个作用域下, 定义重名变量

 1-3. 块级作用域

  => var 没有块级作用域

 => let/const 有块级作用域

  => 块级作用域: 任何一个可以书写代码段的 {} 都会限制变量的使用范围

2. let 和 const 的区别

  2-1. 声明时赋值

 => let 可以在定义变量的时候, 不赋值

 => const 定义变量的时候, 必须赋值

 2-2. 值的修改

  => let 定义的变量可以任意修改值内容

  => const 定义的常量, 一经赋值不允许修改

二、ES6 的模板字符串

 + 使用 反引号(``) 来进行字符串的定义

 + 和 单引号('') 或者 双引号("") 定义的字符串没有区别, 使用上是一样的

 + 只是当你使用 反引号(``) 定义的时候, 会有特殊的能力

  1. 可以换行书写

  2. 可以直接在字符串内解析变量

 => 当你需要解析变量的时候, 书写 ${ 变量 }

 + 注意:

  => ${} 外面的空格是真实在字符串内的空格

  => ${} 里面的空格是代码的空格, 和字符串没有关系

三、ES6 的箭头函数

 + ES6 语法中定义函数的一种方式

 + 只能用来定义函数表达式(匿名函数)

=> 当你把函数当做一个值赋值给另一个内容的时候, 叫做函数表达式

 + 注意: 声明式函数不行

 + 语法: () => {}

 -> () 是写形参的位置

 -> => 是箭头函数的标志

 -> {} 是书写代码段的位置

四、箭头函数的特点:

  1. 可以省略小括号不写

  => 当你的形参只有一个的时候, 可以不写小括号

  => 如果你的形参没有或者两个及以上, 必须写小括号

 2. 可以省略大括号不写

  => 当你的代码只有一句话的时候, 可以省略大括号不写, 并且会自动返回这一句话的结果

 => 否则, 必须写大括号

3. 箭头函数内没有 arguments

 => 箭头函数内天生不带有 arguments

 => 没有所有实参的集合

 4. 箭头函数内没有 this

=> 官方: 外部作用域的 this

=> 私人: 书写在箭头函数的外面那个函数 this 是谁, 箭头函数内的 this 就是谁

五、ES6 的函数参数默认值

   => 给函数的形参设置一个默认值, 当你没有实参进行赋值的时候, 使用默认值

   => 语法: function fn(形参 = 默认值) {}

   + 任何函数都可以使用

   + 注意: 如果你给箭头函数设置参数默认值, 那么不管多少个形参, 都得写小括号

六、ES6 的解构赋值

 + 快速从对象或者数组中获取一些数据

 + 分成两类

  1. 解构数组

 2. 解构对象

1. 解构数组(快速从数组中获取数据)

   => 注意: 使用 [] 解构数组

    => 语法: var [ 变量1, 变量2, 变量3, ... ] = 数组

   -> 按照索引依次把数组中的每一个数据赋值给对应的变量

    => 扩展: 解构多维数组

    -> 数组怎么写, 解构怎么写

    -> 把数据换成变量

2. 解构对象(快速从对象中获取数据)

  => 注意: 使用 {} 解构对象

  => 语法: var { 键名1, 键名2, 键名3, ... } = 对象

   => 按照键名, 依次定义变量从对象中获取指定成员

   => 解构的时候起一个别名

 -> 语法: var { 键名: 别名, 键名2: 别名 } = 对象

 -> 注意: 当你起了别名以后, 原先的键名不能在当做变量名使用了, 需要使用这个别名

七、ES6 的扩展运算符

 + 展开合并运算符

 + 有两个意义

 => 展开

 => 合并

  => 主要是操作 数组 和 对象 的运算符号

 1. 展开

        => 可以 展开对象, 或者 展开数组

        => 如果是展开对象, 就是去掉对象的 {}

        => 如果是展开数组, 就是去掉数组的 []

 2. 合并

        => 当这个符号书写在函数的形参位置的时候, 叫做合并运算符

        => 从当前形参位置开始获取实参, 直到末尾

        => 注意: 合并运算符一定要写在最后一位

八、ES6 的模块化开发

 开发的历史演变

        => 最早: 一个 js 文件

          -> 每一个 html 文件对应一个 js 文件

        => 后来: 把一个项目内部的重复功能提取出来

          -> 写成一个单独的 js 文件

        => 后再:

          -> 决定按照功能拆分出一个一个的文件

          -> a.js : 专门做顶部导航栏各种功能

          -> b.js : 专门做二级菜单

          -> c.js : 专门做搜索引擎

          -> d.js : 左侧边栏

          -> e.js : 轮播图

          -> 最后在每一个 页面 准备有一个整合的 js 文件

            + 就是专门用来组合这个页面使用了多少个 js 文件模块

          -> 此时, 我们管每一个 js 文件叫做一个 模块

            + 页面的完整功能, 就是由一个一个的模块来完成的

          -> 叫做 模块化 开发

九、模块化开发的规则

 1. 如果你想使用 ES6 的模块化开发语法

          => 页面必须在服务器上打开

          => 本地打开不行

          => vscode 下载插件, live server

          => 打开页面的时候, 鼠标右键 open with live server

            -> 快捷键, alt + l, 再按一次 alt + o

 2. 当你使用了 模块化语法以后

          => 每一个 js 文件, 都是一个独立的 文件作用域

          => 该文件内的所有变量和方法, 都只能在这个文件内使用

          => 其他文件不能使用

          => 如果像使用, 需要导出

 3. 每一个 js 文件, 也不能使用任何其他 js 文件内部的变量

          => 如果像使用

          => 那么你需要导入该文件

    => 引入:

    -> 必须把页面在服务器上打开

      -> script 标签必须要写一个 type="module" 的属性

   语法: 导出和导入

      + 导出语法:

        export default { 你要导出的内容 }

      + 导入语法:

        import 变量 from 'js 文件'

0713运动函数 自行看翔哥代码

20210714

 认识面向对象开发

        + 一种代码的开发方式, 是我们写代码的思想

     词:

        + 面向过程: 在开发的过程中关注每一个 步骤 顺序 细节 ...

        + 面向对象: 在开发的过程中找到一个能帮我完成功能的对象

 意义:

          => 在开发一个功能的时候

          => 首先书写一个构造函数

          => 这个构造函数可以生成能完成功能的对象

          => 使用构造函数生成一个对象

          => 利用生成的对象完成功能

        + 面向对象 是对 面向过程 的高度封装

        + 核心思想: 高内聚低耦合

创建对象的方式

      1. 字面量方式

        + 语法: var o = {}

        + 不适合: 因为不能批量创建

      2. 内置构造函数创建对象

        + 语法: var o = new Object()

        + 不合适: 因为不能批量创建

      3. 工厂函数创建对象

        3-1. 自己书写一个工厂函数

          => 就是一个函数, 只是因为这个函数可以创建对象

          => 我们起名叫做 工厂函数

        3-2. 用工厂函数创建对象

        + 合适: 可以批量创建, 可以自由设置值

      4. 自定义构造函数创建对象

        4-1. 自己书写一个构造函数

          => 就是一个函数, 只不过在调用的时候需要和 new 关键字连用

          => 我们起名叫做 构造函数

        4-2. 使用构造函数创建对象

        + 合适: 可以批量创建, 可以自由设置值

 构造函数的书写

        + 构造函数也是一个函数

        + 只不过使用的时候需要和 new 关键字连用

      1. 书写构造函数首字母大写(规范)

        => 为了和普通函数做一个区分

      2. 构造函数的调用必须和 new 关键字连用(规范)

        => 因为只有 和 new 关键字连用的时候, 才有自动创建对象的能力

        => 我们书写构造函数的意义, 就是为了批量创建对象

        => 不和 new 关键字连用, 没有自动创建对象的能力了, 那么构造函数书写的意义就没了

      3. 构造函数内不要写 return

        => 当你 return 一个基本数据类型的时候, 写了白写

        => 当你 return 一个复杂数据类型的时候, 构造函数白写

      4. 构造函数内的 this

        => 因为你的函数调用和 new 关键字连用

        => this 会指向自动创建出来的那个对象

        => 又因为这个对象会被自动返回, 赋值给本次调用是前面的那个变量

        => this 指向 new 前面定义的变量

        => 构造函数自动创建对象的过程, 叫做 实例化的过程

        => 我们管构造函数创建出来的对象, 起名叫做 实例对象

        => 概念: 构造函数内的 this 指向当前实例

 构造函数内的不合理

        + 当你把方法直接书写在构造函数体内的时候

        + 会造成额外的资源浪费

 解决构造函数内的不合理

        4-1. prototype

          => 每一个函数天生自带一个属性, 叫做 prototype, 是一个对象数据类型

        4-2. __proto__

          => 每一个对象天生自带一个属性, 叫做 __proto__, 指向所属构造函数的 prototype

        4-3. 对象访问机制

          => 当你访问一个对象的成员的时候

          => 会首先在自己身上查找, 如果有直接使用, 停止查找

          => 如果没有, 会自动去 __proto__ 上查找

          => 未完待续 ...

        + 结论:

          => 书写构造函数的时候

          => 属性直接书写在构造函数体内

          => 方法书写在构造函数的原型(prototype)上

原型 :

      构造函数概念:

        + **每一个函数天生自带一个属性, 叫做 prototype, 是一个对象数据类型**

        + 构造函数也是一个函数, 所以构造函数也有 prototype

        + 这个天生自带的 prototype 内有一个叫做 constrictor 的属性

          => 表名我是谁自带的 prototype

          => 指向自己的构造函数

        + 是一个对象, 我就可以向里面添加一些成员

      实例对象概念:

        + **每一个对象天生自带一个属性, 叫做 __proto__, 指向所属构造函数的 prototype**

        + 实例对象也是一个对象, 所以实例对象也有 __proto__

对象访问机制:

        + 当你需要访问一个对象的成员的时候

        + 首先, 会在对象自己本身身上查找, 如果有, 直接使用, 停止查找

        + 如果没有, 会自动去自己的 __proto__ 上查找

        + 未完待续 ...

      构造函数的不合理

        + 当你把方法书写在构造函数体内的时候

        + 随着创建对象的增加, 会额外造成资源浪费

      解决:

        + 把属性书写在构造函数体内

        + 把方法书写在构造函数的原型上(prototype)

      原型:

        + 一个函数伴生的存储空间

        + 专门由构造函数向内添加方法, 供构造函数的实例使用

和构造函数相关的 this

        1. 构造函数体内的 this

          => 因为和 new 关键字连用

          => this 指向 当前实例

        2. 原型上的方法体内的 this

          => 因为原型上的方法是被当前实例调用的

          => this 指向 当前实例

      复习: this 指向

        + 当 this 指向使用在函数内的时候, 不管函数定义在哪, 不管函数如何定义

        + 只看函数的调用方式(箭头函数除外)

        1. 普通调用

          => 函数名()

          => this 指向 window

        2. 对象调用

          => 对象名.函数名()

          => this 指向 点前面的那个对象

        3. 定时器处理函数

          => setTimeout(函数, 数字)

          => setInterval(函数, 数字)

          => this 指向 window

        4. 事件处理函数

          => 事件源.on事件类型 = 函数

          => 事件源.addEventListener('事件类型', 函数)

          => this 指向 事件源(谁身上的事件)

        5. 自执行函数

          => this 指向 window

        6. 箭头函数

          => 本身没有 this

          => this 指向外部函数的 this

        7. 构造函数

          => new 函数名()

          => this 指向 当前实例(new 前面定义的那个变量)

          => 本质: 构造函数内自动创建出来的那个对象

20210715

原型 和 原型链

        + 原型: 

每个函数天生自带的 prototype 对象数据类型

   作用: 由构造函数向 原型上 添加方法, 提供给该构造函数的所有实例使用

   为了解决构造函数将方法书写在构造函数体内时造成的资源浪费

        + 原型链:

  概念: 用 __proto__ 串联起来的对象链状结构

  作用: 为了对象访问机制服务(当你访问一个对象成员的时候, 为你提供一些服务)

  注意: 只是 __proto__ 串联起来的对象链状结构, 千万不要往 prototype 上靠

      万物皆对象

        + 在 JS 内, 任何一个数据类型其实都是对象

        + 函数也是一个对象, 数组也是一个对象, 正则也是一个对象, ...

          => 是对象, 就可以存储 键值对

        + 以函数为例

          => 当你书写完毕一个函数的时候

          => 此时 函数数据类型 出现了, 同时该函数名也是一个对象数据类型

      概念:

        1. 每一个函数天生自带一个属性叫做 prototype, 是一个对象数据类型

        2. 每一个对象天生自带一个属性叫做 __proto__, 指向所属构造函数的 prototype

        3. 任何一个对象, 如果没有准确的构造函数, 那么看做是 Object 的实例

          => 只要是一个单纯的对象数据类型, 都是内置构造函数 Object 的实例

      问题1: p1 身上的 __proto__ 是谁 ?

        => 因为 p1 是 Person 的时候

        => 根据 概念1 得到, p1.__proto__ 指向所属构造函数的 prototype

      问题2: Person 的 __proto__ 是谁 ?

        => Person 是一个构造函数, 同时也是一个函数, 同时也是一个对象

        => 只要是对象就会有 __proto__ 属性

        => JS 内有一个内置构造函数叫做 Function, 只要是函数, 就看做 Function 的实例

        => 任何一个函数数据类型所属的构造函数都是 Function

        => Person 看做是 Function 的实例

        => Person 所属的构造函数就是 Function

        => Person.__proto__ 指向 Function.prototype

      问题3: Person.prototype 的 __proto__ 是谁 ?

        => Person.prototype 是函数天生自带的一个对象数据类型

        => 只要是对象就会有 __proto__ 属性

        => JS 内有一个内置构造函数叫做 Object, 只要是单纯的对象, 都是 Object 的实例

        => Person.prototype 是一个天生的对象数据类型, 并且是一个单纯的对象数据类型

        => 把 Person.prototype 看做是 Object 的实例

        => Person.prototype 的 __proto__ 就是 Object.prototype

      问题4: Function 的 __proto__ 是谁 ?

        => Function 是一个构造函数, 同时也是一个函数, 同时也是一个对象

        => 只要是对象就会有 __proto__ 属性

        => JS 内有一个内置构造函数叫做 Function, 只要是函数就是 Function 的实例

        => Function 自己本身是一个内置构造函数, 本身也是一个函数

        => Function 自己是自己的实例, 自己是自己的构造函数

        => 在 JS 内管 Function 叫做顶级函数

        => Function.__proto__ 就是 Function.prototype

      问题5: Function.prototype 的 __proto__ 是谁 ?

        => Function.prototype 是函数天生自带的一个对象数据类型

        => 只要是对象就会有 __proto__ 属性

        => Function.prototype 是一个天生的对象数据类型, 并且是一个单纯的对象数据类型

        => 把 Function.prototype 看做是 Object 的实例

        => Function.prototype 的 __proto__ 就是 Object.prototype

      问题6: Object 的 __proto__ 是谁 ?

        => Object 是一个构造函数, 同时也是一个函数, 同时也是一个对象

        => 只要是对象就会有 __proto__ 属性

        => Object 也是一个函数, 只要是函数就是 Function 的实例

        => Object 这个内置函数所属的构造函数依旧是 Function

        => Object.__proto__ 就是 Function.prototype

      问题7: Object.prototype 的 __proto__ 是谁 ?

        => Object.prototype 势函数天生自带的一个对象数据类型

        => 只要是对象就会有 __proto__ 属性

        => 在 JS 内, Object 是顶级对象, Object.prototype 是顶级原型

        => Object.prototype 是唯一一个没有 __proto__ 的对象数据类型

        => Object.prototype 的 __proto__ 是 null

  对象访问机制

        + 当你需要访问一个对象的成员的时候

        + 首先在自己身上查找, 如果有直接使用, 停止查找

        + 如果没有, 会自动去 __proto__ 上查找

        + 如果还没有, 再去 __proto__ 上查找

        + 以此类推, 直到 顶级原型(Object.prototype) 上都没有, 返回 undefined

      结论:

        + Object.prototype 中添加的内容, 所有的数据类型都可以使用

        + Function.prototype 中添加的内容, 所有的函数数据类型都可以使用(专门给函数使用的)

        + 自定义构造函数.prototype 中添加的内容, 给该构造函数的实例使用的

20210716

 ES6 的类语法

        + 解释: ES6 书写构造函数的方式

      语法:

        + class 类名 {}

          => class: 定义类的关键字

          => 类名: 该类的名字

          => {}: 该类里面包含的所有内容

        + 在类里面写的内容

          1. construcor () {}

            => 等价于 ES5 的构造函数体

          2. 原型上的方法

            => 直接在 constructor 的后面书写

            => 方法名 () {}

            => 表示是添加在原型上的方法

          3. 类 的 静态方法和属性

            => static 方法名 () {}

            => static 变量 = 值

      如何区分静态方法和原型方法

        + 在书写上

          => 如果你想写一个方法将来给实例使用, 那么就写成原型方法

          => 如果你想写一个方法将来给类自己使用, 那么就写成静态方法

          => 如果你想写成原型方法, 那么就直接写 ( 方法名 () {} )

          => 如果你想写成静态方法, 那么就直接写 ( static 方法名 () {} )

        + 在使用上

          => 如果你写的就是 ( 方法名 () {} )

          => 那么将来你使用的时候, 要依赖这个类的 实例对象 去调用

            -> const p1 = new Person()

            -> p1.方法名()

          => 获取在方法内以 this 的形式来调用

            -> 方法名 () { this.xxx }

          => 如果你写的是 ( static 方法名 () {} )

          => 那么将来使用的时候要依赖 类名 去调用

            -> 类名.方法名()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值