JavaScript

JS引入方式

  • 内部脚本:将JS代码定义在HTML页面中

    • JavaScript代码必须位于script标签内
    • 在HTML文档中,可以再任意地方 ,放置任意数量的script
    • 一般会把脚本置于body元素的底部,可以改善显示速度
  • 外部脚本:将JS代码定义在外部JS文件中,然后引入到HTML页面中

    • 外部JS文件中,只包含JS代码,不包含script标签

    • script标签不能够自闭合,也就是不可以写成

image-20240503145115827

JS基础语法

书写语法

  • 区分大小写:与java一样,变量名、函数名以及其他一切东西都是区分大小写的

  • 每行结尾的分号可有可无

  • 注释:

    • 单行注释://注释内容
    • 多行注释:/注释内容 */
  • 大括号代表代码块:

    1
    2
    3
    4
    //判断
    if(count == 3){
    alert(count);
    }

输出语句

1
2
3
4
window.alert() //写入警告框
document.write()//写入HTML输出
console.log()//在浏览器控制台输出内容
console.dir()//在浏览器控制台输出对象

image-20240503151224544

变量

  • JavaScript 中用 var关键字(variable 的缩写)来声明变量。

    var的特点:

    • 作用域比较大,全局变量。
    • 可以重复定义同一个变量。相当于一个覆盖
  • JavaScript 是一门弱类型语言,变量可以存放不同类型的值

    1
    2
    3
    var a = "张三";//定义a的值是字符串张三
    a = 20//把值20赋值给a
    alert(a)//输出20
  • 变量名需要遵循如下规则:

    • 组成字符可以是任何字母、数字、下划线(_)或美元符号($)
    • 数字不能开头
    • 建议使用驼峰命名(如userName)
  • let 关键字来定义变量。它的用法类似于 var,但是所声明的变量,只在let 关键字所在的代码块内有效,且不允许重复声明。

  • const 关键字,用来声明一个只读的常量。一旦声明,常量的值就不能改变。

数据类型

  • JS中分为:原始类型引用类型

原始类型

image-20240503153618200

运算符

  • 算术运算符:+,-,*,/,%,++,–

  • 赋值运算符:=,+=,-=,,/=,%=

  • 比较运算符:>,<,>=,<=,!=,==,**===**

    ==与===

    • ==会进行类型转换,===不会进行类型转换。

逻辑与(&&)与逻辑或(||)

在编程中,&&|| 是逻辑运算符,它们用于组合布尔表达式。这些运算符在大多数支持C语言风格语法的编程语言中都是可用的,比如 C、C++、Java、JavaScript 等。

逻辑与 (&&)

逻辑与运算符 && 返回 true 当且仅当其两边的操作数都为 true。否则返回 false

示例:

1
2
3
4
5
boolean a = true;
boolean b = false;

// 使用 &&
boolean result = a && b; // 结果为 false, 因为 b 是 false
逻辑或 (||)

逻辑或运算符 || 返回 true 如果其任一操作数为 true。只有当两边的操作数都为 false 时才返回 false

示例:

1
2
3
4
5
boolean x = true;
boolean y = false;

// 使用 ||
boolean result = x || y; // 结果为 true, 因为 x 是 true
短路求值(重要)

需要注意的是,这两个运算符具有短路求值的行为:

  • 对于 &&,如果左边的操作数为 false,则右边的操作数不会被计算(因为无论右边是什么结果都不会改变最终结果)。
  • 对于 ||,如果左边的操作数为 true,则右边的操作数不会被计算(同上)。

非运算符(!)

非运算符 ! 接受一个操作数,并返回一个布尔值:

  • 如果操作数是假值(falsy),则返回 true
  • 如果操作数是真的(truthy),则返回 false

在 JavaScript 中,以下值被认为是假值:

  • false
  • 0
  • "" (空字符串)
  • null
  • undefined
  • NaN

除了上述假值之外的所有值都被认为是真值。例如:

  • 非零数值(如 1-3
  • 非空字符串(如 "hello"
  • 数组(如 [][1, 2, 3]
  • 对象(如 {}new Date()
  • 三元运算符:条件表达式?true_value:false value

类型转换:

image-20240622132055925

  • 字符串类型转为数字:

    • paseInt方法:这个方法能够将字符串转为数字,原理是从字符串左边开始读取,读取到不是数字的地方(如12A45他会读取到A这个为止而停下来)停下来,取该位置前面的输出。

      alert(parseInt("12"));//转为12
      alert(parseInt("12A45"));//转为12
      alert(parseInt("A45"));//转为NaN(not a number)
      
    • 可以使用一元加号 (+) 前缀,来把字符串转为数字型。
  • 其他类型转为boolean:

    • Number类型:0和NaN转为false,其他均转为true。
    • String类型:空字符串转为false,其他 均转为true。
    • Null类型和undefined类型:均转为false。
  • 隐式转换

    • image-20240622131333679
  • 检测数据类型

    • typeof + 检测的名字
    • image-20240622130940174

流程控制

  • if…else if ….else….

  • switch

  • for

  • while

  • do…while

    值得注意的是,在JS中这些流程控制语句和我们的java是完全一致的。

  • break和continue

    • image-20240622145759783

JS函数

函数的定义

1
2
3
4
5
6
7
8
9
function functionName(参数1,参数2...){
//要执行的代码
}


//另一种方式调用
let functionName = function(参数1,参数2...){
//要执行的代码
}
  • 注意
    • 形式参数不需要类型。因为JS是弱类型语言。
    • 返回值类型也不需要定义,可以再函数内部直接使用return返回即可。
  • 调用:函数名称(实际参数列表)

匿名函数

  • 函数分为

    • image-20240622154237927
  • 函数表达式

    • <script>
          // 1.函数表达式
      let fn = function (x,y){
          console.log( x + y );
      }
      fn(1,2);
      
      // 函数表达式 和具名函数(有名函数) 有什么不同呢?
      
      // 1.具名函数的调用可以写到任何位置
      // 2.匿名函数的函数表达式形式写法必须先声明在使用,否则会报错未声明
      </script>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21

      * 立即执行函数

      * ```js
      // 1、立即执行函数

      (function(x,y){

      console.log(x+y);

      })(1,2);

      // 2、第二种 写法

      (function(x,y){

      console.log(x+y);

      }(1,3));

      // 上面结构中,第二个小括号是实参,function()中的小括号传递的是形参{}中写入要执行的逻辑
      * 注意:多个立即执行函数要在函数末尾用分号隔开。

回调函数

回调函数是编程中一个非常重要的概念,尤其在异步编程模型中极为常见。在JavaScript中,回调函数的应用尤为广泛,因为JavaScript是单线程的,但可以通过异步操作实现并发效果。

回调函数的基本概念

回调函数就是一个作为参数传递给另一个函数的函数。当外部函数完成某些操作后,会“回调”这个函数作为处理结果的一种方式。这使得程序可以在等待某个耗时操作(如网络请求、文件读写等)的同时继续执行其他代码,而不会阻塞执行流程。

示例

假设你有一个函数doSomethingAsync,它需要一段时间才能完成操作。为了不让主线程等待,你可以传入一个回调函数,当doSomethingAsync完成时调用这个回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
function doSomethingAsync(callback) {
setTimeout(function() {
// 模拟异步操作
let result = 'Data from async operation';
callback(result);
}, 2000); // 假设这个操作需要2秒
}

// 使用回调函数
doSomethingAsync(function(data) {
console.log('Received data:', data);
});

在这个例子中,doSomethingAsync函数接受一个回调函数作为参数。当异步操作完成时,它调用回调函数并传递结果。这样,即使doSomethingAsync需要较长时间来完成,主线程也不会被阻塞,可以立即执行后续代码。

注意事项

回调函数可以导致所谓的“回调地狱”(Callback Hell),特别是当多个异步操作嵌套在一起时。这种情况下,代码结构可能会变得难以阅读和维护,因为每个操作的回调都嵌套在前一个操作的回调内。为了解决这个问题,现代JavaScript倾向于使用Promise链和async/await语法来使异步代码看起来更像同步代码。

递归函数

什么叫递归函数呢?

如果一个函数在内部可以调用其本身,那么这个函数就是递归函数

  • 简单理解:函数内部自己调用自己,这就是递归函数。
  • 递归函数的作用和循环效果类似。
  • 由于递归很容易发生”栈溢出”错误,所以必须要有退出条件 return

生成一个范围随机数

  • Math.random()是随机数函数,返回一个0~1之间的浮点数,并且不包括1,包括0

  • Math.floor()向下取整,比如8.9就取8;10.4就取10

如生成0-10的随机数

1
Math.floor(Math.random() * 11)

生成X~N之间的随机数

1
Math.floor(Math.random() * (X + N))

JS对象

Array(数组对象)

  • 定义:let 变量名 = new Array(元素列表);或者let 变量名= [元素列表];

    1
    2
    3
    4
    <script>
    //定义数组
    let arr1 = new Array(1,2,3,4,5);
    //或者let arr1 = [1,2,3,4,5]

JavaScript 数组方法

  • 把数组转换为数组值(逗号分隔)的字符串。:toString()

  • 将所有数组元素结合为一个字符串,可以指定分隔符:join()

    • var fruits = ["Banana", "Orange","Apple", "Mango"];
      document.getElementById("demo").innerHTML = fruits.join(" * "); 
      <!--结果是:Banana * Orange * Apple * Mango	-->
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10

      - 从数组中删除最后一个元素,并返回被删除的值:`pop()`

      - (在数组结尾处)向数组添加一个新的元素,并返回新数组的长度:`push()`

      - 向数组追加新元素的简易方法:`length`

      - ```js
      var fruits = ["Banana", "Orange", "Apple", "Mango"];
      fruits[fruits.length] = "Kiwi"; // 向 fruits 追加 "Kiwi"
  • 删除首个数组元素,并把所有其他元素“位移”到更低的索引,返回被删除的值:shift()

    • var fruits = ["Banana", "Orange", "Apple", "Mango"];
      fruits.shift();            // 从 fruits 删除第一个元素 "Banana"
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41

      - 使用delete也可以删除数组元素,但是会导致留空洞,空洞会变为`undefine`推荐使用`pop或shift方法删除元素`。

      - (在开头)向数组添加新元素,并把旧元素向后移动,并返回新数组的长度:`unshift()`

      - 用于向数组添加新项,该函数接收两个参数:`splice()`
      - 第一个参数定义在某个索引位置之前添加新元素(拼接)
      - 第二个参数定义删除多少元素
      - 剩下参数定义要添加的新元素
      - 该函数返回值是被删除元素组成的数组
      - 也可以使用该函数进行删除元素(第一个参数是要删除元素的索引,第二个参数是删除元素的个数,比如`fruits.splice(0, 1); `)
      - 比如:`fruits.splice(2, 0, "Lemon", "Kiwi");`

      - 通过合并(连接)现有数组来创建一个新数组:`concat()`
      - `concat()` 方法可以使用任意数量的数组参数
      - `concat()` 方法不会更改现有数组。它总是返回一个新数组。
      - 参数可以是具体的值,也可以是数组

      - 用数组的某个片段切出新数组:`slice()`
      - `slice()` 方法创建新数组。它不会从源数组中删除任何元素。
      - `slice()` 可接受两个参数(索引),比如 (1, 3)。
      - 切的区间是前闭后开的,也就是包括1,不包括3
      - 如果结束参数没写,那么会从索引位置向后切出剩余部分,包括索引位置处的元素

      ### JavaScript 数组排序

      - 以字母顺序对数组进行排序:`sort()`!!!只对字母生效,数字无效

      - 如果想用该函数比较数字,**则应该通过比值函数**来修正
      - 比值函数:`(a,b)=> return a-b;`升序 **/** `(a,b)=> return b-a;`降序

      - 反转数组中的元素:`reverse()`

      - 查找数组中的最高值:`Math.max.apply`,举例子:

      - ```js
      //在本例中,我们使用 apply() 来正确地将数组 arr 的所有元素作为单独的参数传递给 Math.max()。
      //由于 Math.max() 不依赖于 this 的值,所以通常这里会传入 null 或者 undefined。
      function myArrayMax(arr) {
      return Math.max.apply(null, arr);
      }
  • 找数组中的最低值:Math.min.apply

  • 比较字符串:

    • cars.sort(function(a, b){
            var x = a.type.toLowerCase();
            var y = b.type.toLowerCase();
            if (x < y) {return -1;}
            if (x > y) {return 1;}
            return 0;
      });
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17

      ### JavaScript 数组迭代

      - 为每个数组元素调用一次函数(回调函数):`forEach(function(value,index,array){})`

      - 该函数接收三个参数:value,index,array

      - 该函数只对数组进行遍历,不返回新数组。

      - ```js
      var txt = "";
      var numbers = [45, 4, 9, 16, 25];
      numbers.forEach(myFunction);

      function myFunction(value, index, array) {
      txt = txt + value + "<br>";
      }
  • map(function(value,index,array){})方法

    • map() 方法通过对每个数组元素执行函数来创建新数组。
    • map() 方法不会对没有值的数组元素执行函数。
    • map() 方法不会更改原始数组。
    • 该方法返回一个新数组。
    • 同样,它也有3个参数:项目值,项目索引,数组本身
  • 创建一个包含满足某些条件的数组元素的新数组:filter(function(value,index,array){})

    • 它有3个参数:项目值,项目索引,数组本身

    • var numbers = [45, 4, 9, 16, 25];
      var over18 = numbers.filter(myFunction);
      
      function myFunction(value, index, array) {
        return value > 18;
      }
      
      1
      2
      3
      4
      5

      - 可以用来对数组中的所有元素进行聚合计算,比如求和、计算乘积、查找最大值/最小值:`reduce()`

      - ```js
      array.reduce(callback(returnData, currentValue[, currentIndex[, array]])[, initialValue])
    • reduce函数的执行顺序是从左到右,函数reduceRight()是从右到左,语法完全一致。

    • 该函数接受一个回调函数(callback()),它会被执行数组中的每一个元素(除了第一个,除非提供了初始值)。

      • returnData:该参数是该回调函数的返回值,同时它也是下次调用时的第一个参数。

      • currentValue:当前元素的值。

      • currentIndex:可选的,当前元素的索引。

      • array:可选的,被遍历的数组本身。

      • initialValue:初始值,可选。

      • 对于该方法做出如下特别解释:

        image-20240824172102164

      • 可接受初始值,如果给出初始值那么给出的值会被赋值给returnData

      •     let arr = [1, 2, 3, 4, 5, 7];
            let sum = arr.reduce((callback,value)=>{
                return callback + value
            })
            console.log(sum);
        
        1
        2
        3
        4
        5
        6
        7
        8
        9

        - ```js
        var numbers1 = [45, 4, 9, 16, 25];
        //调用回调函数的同时传入了初始值100
        var sum = numbers1.reduce(myFunction, 100);

        function myFunction(total, value) {
        return total + value;
        }
  • 检查所有数组值是否满足某个条件:

    • every(function(value,index,array){})
    • 接受3个参数:项目值(value),项目索引(index),数组本身(array)
    • 如果所有值满足该条件,返回true,如果所有值当中有一个不满足,则返回false
  • 检查某些数组值是否满足某个条件:

    • some(function(value,index,array){})
    • 接受3个参数:项目值(value),项目索引(index),数组本身(array)
    • 如果数组当中的某些值满足条件,则返回true,否则返回false
  • 在数组中搜索元素值并返回其位置:

    • indexOf()
    • 语法:array.indexOf(item, start),item是查找的项目,start用来定义开始起点(start值小于0时,将从结尾开始的给定位置开始,并查到到结尾)。
    • 如果未找到,则返回-1
    • 如果某项目多次出现,则返回第一次出现的位置(索引)
    • !!!特别注意:一定要注意该函数执行的方向,开始位置可能会变,但执行顺序是不会变的
  • 从数组结尾处开始查找:

    • lastindexOf()

    • 语法:

    • item 必需。要检索的项目。
      start 可选。从哪里开始搜索。负值将从结尾开始的给定位置开始,并搜索到开头。
    • !!!特别注意:一定要注意该函数执行的方向,开始位置可能会变,但执行顺序是不会变的

  • 返回数组中满足某个条件的第一个元素的值:

    • find(function(value,index,array){})
    • 接受3个参数:项目值(value),项目索引(index),数组本身(array)
  • 返回数组中满足某个条件的第一个元素的索引值:

    • findIndex(function(value,index,array){})
    • 接受3个参数:项目值(value),项目索引(index),数组本身(array)

String对象

1
2
3
4
5
6
7
<script>
//String对象
//方式一:
var stringTest = new String("zhangsan");
//方式二:
var stringTest1 ="lisi";
</script>

JavaScript 字符串方法

查找字符串中的字符串

  • length 属性返回字符串的长度:

    • var txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
      var sln = txt.length;
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65

      - `indexOf()` 方法返回字符串中指定文本*首次*出现的索引(位置):
      - 从0计算
      - 未找到返回-1
      - 可传递起始位置参数
      - `lastIndexOf()` 方法返回指定文本在字符串中*最后*一次出现的索引:
      - 未找到返回-1
      - 可传递起始位置参数
      - 检索方向:从结尾向开始方向检索

      #### 检索字符串中的字符串

      - `search()` 方法搜索特定值的字符串,并返回匹配的位置:
      - 返回首次出现的位置。
      - 可以使用正则表达式进行匹配

      #### 提取部分字符串

      **!!!!**值得注意的是,下面这三种切字符串的方法,索引值是负值的时候,只是指定了重字符串末尾开始,而没有改变切割方向,切割方向还是从左到右的

      - `slice()` 提取字符串的某个部分并在新字符串中返回被提取的部分。
      - slice有切割,切片的意思
      - 两个参数:开始索引和结束索引,切出来的片段不包括结束索引。
      - 参数值可以是负数,如果出现负数则意味着从字符串结尾开始计数
      - 如果只有一个参数,那么就是从该位置向后切
      - `substring()` 类似于 `slice()`,但不接受负值索引:
      - 两个参数:开始索引和结束索引,切出来的片段不包括结束索引。
      - 如果只有一个参数,那么就是从该位置向后切
      - 参数不能为**负值**

      #### 替换字符串内容

      - `replace()` 方法用另一个值替换在字符串中指定的值:
      - 返回值是一个新的字符串,对原字符串无影响。
      - 默认只替换首个匹配
      - 该函数对大小写敏感(注意字母的大小写)
      - 参数1:要替换谁?参数2:替换为谁

      #### 转换为大写和小写

      - 通过 `toUpperCase()` 把字符串转换为大写
      - 通过 `toLowerCase()` 把字符串转换为小写

      #### concat()方法

      - `concat()` 连接两个或多个字符串:
      - 可替换`+`,我们知道`+`有可以用来拼接字符串
      - 不改变原来字符串,返回一个新的字符串

      #### trim()方法

      - `trim()`方法删除字符串两端的空白符

      #### 提取字符串字符

      - `charAt()` 方法返回字符串中指定下标(位置)的字符串:
      - 返回指定索引位置的字符
      - `charCodeAt()` 方法返回字符串中指定索引的字符 unicode 编码:
      - 返回指定索引位置字符的unicode编码

      #### 属性访问(Property Access)

      ```js
      var str = "HELLO WORLD";
      str[0]; // 返回 H

但不推荐这样使用。

把字符串转换为数组

  • split() 将字符串转换为数组:

    • 可指定分隔符。如果指定了分隔符,那么它会把字符串中的每个字符按照给定的分隔符分割成一个单个的字符串并返回一个数组

      • let str2 = "LI,ZE,WEI"
        console.log(str2.split(","));//返回['LI', 'ZE', 'WEI']
        
        1
        2
        3
        4
        5
        6

        - 指定了分隔符,但分隔符是空字符串(`" "`),那么被返回的数组将是间隔单个字符的数组

        - ```js
        let str2 = "LI,ZE,WEI"
        console.log(str2.split("")); //返回['L', 'I', ',', 'Z', 'E', ',', 'W', 'E', 'I']
    • 如果未指定分隔符,则返回一个由该字符串本身组成的数组,该字符串就是数组的第一个元素

      • let str2 = "LI,ZE,WEI"
        console.log(str2.split()); //返回['LI,ZE,WEI']
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23

        ### JavaScript 字符串搜索

        - String.indexOf()
        - String.lastIndexOf()
        - String.startsWith()
        - String.endsWith()

        本小节着重介绍后两个方法,前两个方法在上一小节已经介绍过了

        #### String.match()

        - match() 方法根据正则表达式在字符串中搜索匹配项,并将匹配项作为 **Array 对象**返回。

        - 参数是一个正则表达式

        - 如果正则表达式没有加`g`修饰符,则匹配第一个符合表达式的字符串

        - 若没有符合条件的字符串,则返回null

        - ```js
        let text = "The rain in SPAIN stays mainly in the plain";
        text.match(/ain/g) // 返回数组 [ain,ain,ain]

String.includes()

  • 如果字符串包含指定值,includes() 方法返回 true。
    • 两个参数:第一个为要检索的值,第二个参数为开始索引
    • 该方法是ES6新引入的
    • 开始索引如果是负值,改变了索引方向,也改变了检索方向
    • 对大小写敏感

String.startsWith()

  • 如果字符串以指定值开头,则 startsWith() 方法返回 true,否则返回 false:、
    • 两个参数:第一个是要检索的值,必选;第二个开始位置,可选
    • 对大小写敏感,区分大小写

String.endsWith()

  • 如果字符串以指定值结尾,则 endsWith() 方法返回 true,否则返回 false
    • 两个参数:第一个是要检索的值,必选;第二个开始位置,可选
    • 区分大小写

为了很好记忆做出的总结

  • 数组:

    添加元素的方法:push()在末尾添加,unshift()在头部添加,splice()能够更精确添加元素

    删除元素的方法:pop()在末尾删除,shift()在头部删除,splice()也能够进行元素删除

    对数组进行切片:slice()方法

    把数组转换为字符串:join()方法

  • 字符串:

    字符串长度:length属性

    把字符串转换为数组:split(),可以指定分隔符

    对字符串进行切割:slice()方法

json对象

  • 概念: JavaScript Object Notation,JavaScript,对象标记法

  • JSON 是通过JavaScript 对象标记法书写的文本。

  • 由于其语法简单,层次结构鲜明,现多用于作为数据载体,在网络中进行数据传输。

数据格式:

1
2
3
4
5
6
{
"name" : "xiaoli",
"age":25,
"sex":"男性",
"key1":"value1"
}

image-20240503172509557

定义json案例

1
2
3
4
5
6
7
8
9
10
11
<script>
//定义json
var jsonStr = '{"name" = "小李","age" = 25, "address" = "北京" }';

//JSON字符串转为JS对象——JSON.parse()方法
var jsonStrObject = JSON.parse(jsonStr);
console.log(jsonStrObject);
//JS对象转为JS字符串——JSON.stringify()方法
var jsObject = JSON.stringify(jsonStrObject);
console.log(jsObject);
</script>

BOM对象

  • 概念:Browser Object Model 浏览器对象模型,允许lavaScript与浏览器对话,lavaScript 将浏览器的各个组成部分封装为对象

Window对象

  • 介绍:浏览器窗口对象。

  • 获取:直接使用window,其中 window.可以省略window.alert()和alert()效果一样

  • 属性

    • history:对 History 对象的只读引用。请参阅 History 对象
    • location:用于窗口或框架的 Location 对象。请参阅Location 对象。
    • navigator:对 Navigator 对象的只读引用。请参阅Navigator 对象
  • 介绍

    • windows是一个全局对象
    • 所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法
    • window对象下的属性和方法调用的时候可以省略window
  • 方法

    • alert():显示带有一段消息和一个确认按钮的警告框。
    • confirm():显示带有一段消息以及确认按钮和取消按钮的对话。
    • **setlnterval():**按照指定的周期(以毫秒计)来调用函数或计算表达式。
    • setTimeout():在指定的毫秒数后调用函数或计算表达式。

**setlnterval()setTimeout()**都可以进行清除操作

1
2
3
4
5
6
7
8
9
10
11
12
//延时函数参数有两个,一个是回调函数,一个是延时的秒数
let timer1 = setTimeout(() => {
console.log("2秒后打印");
}, 2000);
//清除延时函数
clearTimeout(timer1)
//周期执行函数参数有两个,一个是回调函数,一个是周期执行的秒数
let timer2 = setInterval(() => {
console.log("1111");
}, 1000);
//清除周期执行函数
clearInterval(timer2)

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
//获取
window.alert("Hello BOM");
alert("Hello BoM window");
// 方法
// confirm-对话框--确认:true ,取消:false
var flag = confirm("您确认删除该记录吗?");
alert(flag);

// 定时器-setInterval -- 周期性的执行某一个函数
var i=0;
setInterval(function(){
i++;
console.log("定时器执行了"+i+"次");
},2000);

//定时器延迟指定时间执行一次:-setTimeout
setTimeout(function(){
alert("Js");
},3000);
</script>

Location对象

  • 介绍:地址栏对象。

  • 获取:使用window.location获取,其中window.可以省略不写

  • 属性:

    • href:设置或返回完整的URL,如果设置URL那么他会自动跳转所设置的URL。

    • search:返回地址中参数部分。

    • hash:用于获取或设置URL中的 “#(锚点)” 后面的部分。这个属性返回一个字符串,该字符串以 “#” 开头。

    • **reload()**:用于重新加载当前文档。传入参数true表示强制刷新,类似与ctrl+F5

      代码示例:

      1
      2
      3
      4
      5
      6
      <script>
      //获取当前完整的URL
      console.log(location.href);
      //设置URL自动跳转
      location.href="http://www.itcast.cn"
      </script>
  • navigator的数据类型是对象,该对象记录了浏览器自身的相关信息
  • 常用属性和方法
    • userAgent:检测浏览器的版本以及平台

history对象

  • history的数据类型是对象,它管理历史记录,该对象与浏览器地址栏的操作相应,如前进、后退、历史记录等等

  • 常用属性和方法

    history对象方法
    back() 可以后退
    forword() 前进
    go(参数) 前进后退功能,参数是1前进一个页面,如果是-1则后退一个页面

DOM对象

什么是DOM

  • 概念:Document Object Model,文档对象模型。

  • 将标记语言的各个组成部分封装为对应的对象:

    • Document:整个文档对象
    • Element:元素对象
    • Attribute:属性对象
    • Text:文本对象
    • Comment:注释对象
  • JavaScript 通过DOM,就能够对HTML进行如下操作:

    • 改变 HTML元素的内容
    • 改变 HTML元素的样式(CSS)
    • 对 HTML DOM 事件作出反应
    • 添加和删除 HTML元素

image-20240503180943024

DOM节点

让我们通过一些简单的例子来理解 DOM 节点:

文档节点(Document Node)

文档节点代表整个 HTML 或 XML 文档。它是所有其他节点的父节点。

1
2
3
<!DOCTYPE html>
<html>
<!-- "document" 是文档节点 -->

元素节点(Element Node)

元素节点是 HTML 标签,如 <div><p> 等。

1
2
<div id="example">
<!-- "<div>" 和 "</div>" 是元素节点 -->

属性节点(Attribute Node)

属性节点是 HTML 元素上的属性,例如 idclass 等。

1
2
<div id="example" class="container">
<!-- "id" 和 "class" 是属性节点 -->

文本节点(Text Node)

文本节点是元素内的纯文本,不包含任何 HTML 标签。

1
2
<p>Hello, world!</p>
<!-- "Hello, world!" 是文本节点 -->

注释节点(Comment Node)

注释节点是 HTML 文档中的注释。

1
2
<!-- This is a comment -->
<!-- 这个是注释节点 -->

查找节点

在该小节,我们主要利用关系来查找

父节点查找(只能找到最近一级)

  • 父节点查找
    • 用途:获取当前元素的父节点。
    • 语法element.parentNode

子节点查找

  • 子节点查找

    • 用途:获取当前元素的所有子节点(包括文本节点和元素节点)。

      • 语法fatherElement.childNodes
    • 用途:获取当前元素的第一个或最后一个子节点。

      • 语法fatherElement.firstChildfatherElement.lastChild
    • 用途:获取所有的元素节点

      • 语法fatherElement.children
      • 该方法返回一个伪数组(1.索引访问:可以使用索引来访问元素,类似于数组的方式。2.长度属性:通常有一个 length 属性,表示对象中的元素数量。3.非数组:不是真正的 Array 类型对象,因此不支持数组的一些方法(如 .push().pop().slice() 等)

兄弟节点查找

  • 下一个兄弟节点:

    • 用途:获取当前元素的下一个同级元素节点。
    • 语法element.nextElementSibling
  • 上一个兄弟节点:

    • 用途:获取当前元素的上一个同级元素节点。
    • 语法element.previousElementSibling

新增节点

创建节点

使用 document.createElement('标签名') 方法来创建一个新的 HTML 元素。

1
const newElement = document.createElement('div');

设置属性和内容

你可以设置新元素的属性和内容。

1
2
newElement.id = 'newDiv';
newElement.textContent = '这是一个新的 div 元素。';

添加节点到DOM中

将新元素添加到文档树中的某个位置。常见的方法有:

  • appendChild():将新元素作为最后一个子节点添加到指定的父节点中。
  • **insertBefore()**:插入到父元素中某个子元素的前面,参数有两个:第一个是要插入的新元素,第二个参数是参考的元素
  • **replaceChild()**:替换父元素中某个子元素
  • **removeChild()**:移除指定的子节点。

举个例子

示例:添加到文档末尾

1
document.body.appendChild(newElement);

在某个元素前插入

1
2
const referenceElement = document.getElementById('reference');
referenceElement.parentNode.insertBefore(newElement, referenceElement);

替换某一个元素

1
2
const oldElement = document.getElementById('old');
oldElement.parentNode.replaceChild(newElement, oldElement);

克隆节点

使用背景

  • 复制一个原有节点
  • 把复制的节点放入到指定的元素内部

克隆节点

  • 用途:复制出一个一模一样的节点
  • 语法:element.cloneNode()
    • 参数:若为true,则克隆时会包含后代节点以及内容一起克隆,若为false则不包含后代节点。
    • 默认是false

删除节点

  • 在JS中如要删除某个节点,必须通过其父元素进行删除
    • 语法:fatherElemnet.removeChild(要删除的元素)
    • 若不存在父子关系,则无法删除
    • 如果该节点被删除,那么就会从html中移除该节点。

日期对象

在JavaScript中,Date 对象用于处理日期和时间。这个内置对象可以用来获取、设置以及操作日期和时间数据。下面是一些关于 Date 对象的基本使用方法:

创建 Date 对象

  1. 无参数 - 创建一个表示当前日期和时间的对象:

    1
    const now = new Date();
  2. 带参数 - 创建一个指定日期和时间的对象:

    1
    const specificDate = new Date(2024, 7, 17); // 注意月份是从 0 开始的,所以 8 月是 7
  3. 毫秒数 - 创建一个从1970年1月1日午夜开始经过的毫秒数所表示的日期:

    1. 这里的参数是从开始时间~结束时间所经过的毫秒数。
    1
    const milliSecondsSinceEpoch = new Date(1692278827000);

日期对象的常用方法

获取日期和时间的部分

你可以使用 Date 对象的方法来获取日期和时间的不同部分,例如:

  • getFullYear(), getMonth(), getDate()
  • getHours(), getMinutes(), getSeconds(), getMilliseconds()
  • getTime() - 返回从1970年1月1日午夜开始经过的毫秒数。
  • getDay()-返回星期,0~6

示例:

1
2
3
4
const today = new Date();
console.log(today.getFullYear()); // 输出当前年份
console.log(today.getMonth()); // 输出当前月份(0-11)
console.log(today.getDate()); // 输出当前日期(1-31)

设置日期和时间的部分

同样也可以设置日期和时间的不同部分,例如:

  • setFullYear(), setMonth(), setDate()
  • setHours(), setMinutes(), setSeconds(), setMilliseconds()
  • setTime() - 设置从1970年1月1日午夜开始经过的毫秒数。

示例:

1
2
3
4
5
const today = new Date();
today.setFullYear(2025);
today.setDate(1);
today.setMonth(0); // 设置为一月
console.log(today);

格式化日期

JavaScript 中没有内置的日期格式化函数,但你可以使用第三方库如 Moment.js 或者使用模板字符串和计算来实现简单的格式化,例如:

1
2
3
const today = new Date();
const formattedDate = `${today.getFullYear()}-${('0' + (today.getMonth() + 1)).slice(-2)}-${('0' + today.getDate()).slice(-2)}`;
console.log(formattedDate); // 输出 "2024-08-17"

在格式化日期中,遇到时分秒补0时,可以使用三元运算符

其他方法

在JavaScript中,Date 对象提供了多个方法来格式化日期和时间。其中 toLocaleString() 是一个非常有用的内置方法,它返回一个表示日期/时间的本地格式化的字符串。此外,还有其他一些相关的方法可以帮助你以不同的方式格式化日期。

1. toLocaleString()

toLocaleString() 方法返回一个本地化格式的日期/时间字符串。默认情况下,它会使用浏览器的语言环境设置来决定日期的格式。

1
2
const date = new Date();
console.log(date.toLocaleString()); // 输出本地格式的日期和时间
2. toLocaleDateString()

toLocaleDateString() 方法仅返回日期部分的本地化格式字符串。

1
2
const date = new Date();
console.log(date.toLocaleDateString()); // 输出本地格式的日期
3. toLocaleTimeString()

toLocaleTimeString() 方法仅返回时间部分的本地化格式字符串。

1
2
const date = new Date();
console.log(date.toLocaleTimeString()); // 输出本地格式的时间
参数选项

这些方法都支持选项对象作为参数,允许你进一步定制输出格式。例如,你可以指定使用24小时制还是12小时制,是否显示秒等。

1
2
3
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
const date = new Date();
console.log(date.toLocaleDateString('en-US', options)); // 自定义格式

更多方法

除了上面提到的方法外,Date 对象还提供了其他一些有用的方法:

  • toString():返回日期/时间的标准格式字符串。
  • toISOString():返回一个符合 ISO 标准的日期/时间字符串。
  • toJSON():与 toISOString() 类似,用于 JSON 序列化。
  • toUTCString():返回日期/时间的 UTC 字符串格式。
  • toLocaleDateString()toLocaleTimeString() 的更多选项:可以指定时区、时间格式等。

时间戳

  • 使用场景:制作倒计时效果
  • 什么是时间戳:指从1970年01月01日00时00分00秒起至现在的毫秒数
获取时间戳

要获取当前时间的时间戳,可以使用 Date.now() 方法:

1
2
const timestamp = Date.now(); // 获取当前时间的时间戳
console.log(timestamp);

如果你已经有了一个 Date 对象,可以通过调用 getTime() 方法来获取该日期对应的时间戳:

1
2
3
const date = new Date();
const timestampFromDate = date.getTime(); // 获取Date对象对应的时间戳
console.log(timestampFromDate);

同样,如果有了Date对象,可以通过在 new Date()前面加一个转换获得

1
2
3
const date = new Date();
//通过隐式转换,把字符型转换为数字型来获取
console.log(+ new Date());

第2,3中方法可以获取指定时间的时间戳。

时间戳转换为日期

如果你有一个时间戳并想将其转换为 Date 对象,可以使用 new Date(timestamp) 构造函数:

1
2
3
const timestamp = 1692278827000; // 给定的时间戳
const dateFromTimestamp = new Date(timestamp); // 将时间戳转换为Date对象
console.log(dateFromTimestamp);

image-20240817125524914

使用时间戳

时间戳在JavaScript中非常有用,尤其是在需要比较不同时间点或者执行定时任务时。例如,你可以使用时间戳来计算两个时间点之间的时间差:

1
2
3
4
5
const startTime = Date.now();
// 执行某些操作...
const endTime = Date.now();
const timeDifference = endTime - startTime;
console.log(`操作耗时 ${timeDifference} 毫秒`);
实际应用

这里是一个示例,展示如何使用时间戳来计算从1970年1月1日到2024年8月17日这段时间的时间戳:

1
2
3
4
5
// 计算2024年8月17日的时间戳
const targetDate = new Date(2024, 7, 17); // 注意月份是从0开始的
const targetTimestamp = targetDate.getTime();

console.log(targetTimestamp); // 输出时间戳

JS事件监听

事件:HTML事件是发生在HTML元素上的“事情”。比如:

  • 按钮被点击

  • 鼠标移动到元素上

  • 按下键盘按键

  • 事件监听:JavaScript可以在事件被侦测到时 执行代码。

事件绑定

  • 方式一:通过HTML标签中的时间属性进行绑定

    1
    2
    3
    4
    5
    6
    7
    <input type="button" onclick="on()" value="按钮1">
    <script>
    function on()
    {
    alert('我被点击了!);
    }
    </script>
  • 方式二:通过DOM元素属性绑定

    1
    2
    3
    4
    5
    6
    7
    <input type = "button" id ="btn1" value = "按钮2">
    <script>
    document.getElementById("btn1").onclick = function()
    {
    alert("我被点击了!");
    }
    </script>

常见事件

在JavaScript中,事件是用户或浏览器的动作,它们可以触发预定义的函数或代码块。事件驱动的编程是现代Web开发的核心部分,允许网站对用户的交互作出反应。以下是在JavaScript中常见的几种事件类型:

鼠标事件

  • click:当用户点击鼠标左键时触发。
  • dblclick:当用户双击鼠标左键时触发。
  • mousedown:当鼠标按钮被按下时触发。
  • mouseup:当鼠标按钮被释放时触发。
  • mousemove:当鼠标在页面上移动时持续触发。
  • mouseentermouseleave:当鼠标进入或离开一个元素时触发。这两个事件不会存在冒泡和捕获。
  • mouseovermouseout:类似于 mouseentermouseleave,会存在捕获和冒泡阶段
  • contextmenu:当用户右击鼠标时触发。

键盘事件

  • keydown:当键盘上的一个键被按下时触发。
  • keypress:当按下有字符输出的键时触发。
  • keyup:当键盘上的一个键被释放时触发。

文档和窗口(window)事件

  • load:当页面或资源加载完成时触发。
  • unload:当页面正在卸载时触发。
  • beforeunload:页面即将卸载之前触发。
  • resize:当窗口大小改变时触发。
  • scroll:当页面滚动时触发。

表单事件

  • change:当表单元素(如输入框、下拉列表)的内容被改变时触发。
  • focusblur:当元素获得或失去焦点时触发。
  • submit:当表单被提交时触发。

其他事件

  • error:当错误发生时触发,如图像加载失败。
  • resizescroll:分别在窗口尺寸变化或滚动时触发。
  • touchstart, touchend, touchmove:触摸设备上的事件。

事件处理可以通过直接在HTML标签中设置事件属性(如onclick="someFunction()"),或者通过JavaScript的addEventListener来监听事件

事件对象

事件处理器通常会接收到一个事件对象,这个对象包含了关于事件的所有信息,如事件类型、触发事件的元素、鼠标的位置、键盘按键等。事件对象通常作为函数的第一个参数传递,例如:

1
2
3
element.addEventListener('click', function(event) {
console.log(event.type); // 输出 "click",这里的event指的是事件对象
});

事件对象在JavaScript中提供了一系列的属性,用于描述触发事件的详细信息。下面列出了事件对象中一些常用的属性:

  1. type

    • 描述事件的类型,如 ‘click’, ‘keydown’, ‘load’ 等。
  2. target

    • 指向事件最初触发的DOM元素。即使事件被冒泡到其他元素,target始终指向最初触发事件的元素。
  3. currentTarget

    • 指向当前正在处理事件的DOM元素。如果事件被冒泡或捕获,currentTarget将根据事件流而变化。
  4. eventPhase

    • 描述事件当前处于哪个阶段:AT_TARGET (2),CAPTURING_PHASE (1),或 BUBBLING_PHASE (3)。
  5. timeStamp

    • 事件发生的精确时间戳,单位通常是毫秒。
  6. preventDefault()

    • 方法,阻止事件的默认行为。例如,在链接上阻止页面跳转。
  7. stopPropagation()

    • 方法,阻止事件冒泡到父级元素。
  8. stopImmediatePropagation()

    • 类似于stopPropagation(), 但是它还会阻止事件处理函数队列中的后续函数执行。
  9. clientXclientY

    • 对于鼠标事件,表示鼠标指针相对于视口的位置(不包括滚动条偏移)。
  10. pageXpageY

    • 对于鼠标事件,表示鼠标指针相对于整个文档的位置(包括滚动条偏移)。
  11. screenXscreenY

    • 对于鼠标事件,表示鼠标指针相对于屏幕的位置。
  12. button

    • 对于鼠标事件,表示哪个鼠标按钮被按下:0(主按钮,通常是左键),1(中间按钮),2(次按钮,通常是右键)。
  13. keyCodekey

    • 对于键盘事件,keyCode 是一个数字,表示按下的键。在现代浏览器中,更推荐使用key属性,它是一个字符串,提供了更清晰的键标识。
  14. which

    • 类似于keyCode,但跨浏览器兼容性更好。
  15. ctrlKey, altKey, shiftKey, metaKey

    • 布尔值,表示是否同时按下了Ctrl, Alt, Shift, 或 Meta (Command) 键。

环境对象

环境对象:指的是函数内部特殊的变量this,它代表着当函数运行时所处的环境

作用:弄清楚this的指向,this指代的对象也不同

  • 函数调用方式不同,this指代的对象也不同。
  • 谁调用该函数,this就指向谁,这是判断this指向的粗略规则

事件流

定义事件流指定是一个事件完整执行过程中的流动路径

在JavaScript中,事件流描述了事件是如何在文档对象模型(DOM)中传播的。事件流主要分为两个阶段:事件捕获事件冒泡。还存在一个中间阶段,即目标阶段,在这个阶段事件直接作用于目标元素。

事件捕获 (Event Capturing)

这是事件流第一阶段:从父到子

事件捕获是从DOM树的根节点(通常是documentwindow)开始,事件沿着元素层级向下传播,直到达到目标元素。在这一阶段,事件处理程序有机会在事件到达目标元素之前进行拦截和响应。

  • 给addEvenListener函数写入第三个参数,true,意味着开启了捕获机制。
  • 事件要同名。

目标阶段 (Target Phase)

一旦事件到达目标元素,目标元素上的事件处理程序被调用。这是事件直接作用于触发事件的元素的阶段。

事件冒泡 (Event Bubbling)

这是事件流第二阶段:从子到父

在事件冒泡阶段,事件从目标元素开始,沿DOM树向上逐级传播,直到达到DOM树的根节点。在这个过程中,事件处理程序可以被沿途的任何父元素捕获。

  • 给addEvenListener函数写入第三个参数,false(这也是默认值,所以一般不写),意味着开启了冒泡机制。
  • 当触发冒泡机制时,一个元素触发事件后,会一次从触发事件的子元素向上调用所有父级元素的同名事件
  • 事件冒泡默认存在。

事件流的传播方向

  • 事件捕获:从外部到内部(从根节点到目标节点)。
  • 事件冒泡:从内部到外部(从目标节点到根节点)。

使用事件处理程序

你可以使用addEventListener方法来附加事件处理程序,并通过传递第三个布尔参数来选择事件流的哪个阶段来触发该处理程序:

  • 如果第三个参数为true,则事件处理程序会在事件捕获阶段被调用。
  • 如果第三个参数为false(或省略),则事件处理程序会在事件冒泡阶段被调用。

阻止冒泡

目标:能够写出阻止冒泡的代码

  • 问题:因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素

  • 需求:若想把事件就限制在当前元素内,就需要阻止事件冒泡

  • 前提:阻止事件冒泡需要拿到事件对象

  • 语法:

    事件对象.stopPropagation(),propagation有(动植物等的)繁殖,增殖,;(观点、理论等的)传播;(运动、光线、声音等的)传送的意思。

  • 注意:此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效

事件解绑

  • 解绑:onxxxx事件,利用on定义的事件存在冒泡不存在捕获。

    1
    2
    3
    4
    5
    btn.onclick = function(){
    alert('我被点击了')
    //通过赋值空对象来解绑
    btn.onclick = null
    }
  • 解绑:通过addEvenListener添加的事件

    1
    2
    3
    4
    5
    6
    function fn(){
    alert("我是通过addEvenListener添加的事件,点击之后我就被解绑了")
    }
    btn.addEventListener("click",fn)
    //由于第二个参数需要传入函数的名字,所以匿名函数无法移除
    btn.removeEventListener("click",fn)

注意:匿名函数无法解绑!!!!

事件委托

事件委托(Event Delegation)是一种在父元素上处理子元素事件的技术。它的核心思想是,而不是在每个子元素上单独添加事件监听器,你可以在一个共同的祖先元素上添加一个监听器,然后根据事件的目标(event.target)来确定哪个子元素触发了事件。这种方法可以显著减少事件监听器的数量,从而提升性能,特别是在处理大量动态生成的元素时。

如何实现事件委托

事件委托利用了事件冒泡的特性,即事件从最深层的触发元素开始,向上冒泡到DOM树的更高层。当事件冒泡至监听器所在的元素时,你可以检查事件的目标是否符合你的需求。

实现步骤

  1. 选择一个合适的祖先元素:选择一个包含所有相关子元素的共同祖先作为事件监听器的挂载点。
  2. 添加事件监听器:在选定的祖先元素上添加事件监听器,通常是在clickmouseover等常见事件类型上。
  3. 检查事件目标:在事件处理函数中,使用event.target来确定触发事件的实际元素,并根据需要做出响应。如果需要更确切的确定元素,可以通过元素的tagName来进一步判断

示例代码

假设你有一个列表,其中包含许多链接,你想要在这些链接被点击时执行某个操作,但是你不想为每个链接单独添加事件监听器。

1
2
3
4
5
<ul id="list">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
<li><a href="#">Link 3</a></li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
const list = document.getElementById('list');

list.addEventListener('click', function(event) {
const target = event.target;
// 检查目标是否是我们感兴趣的元素
if (target.tagName.toLowerCase() === 'a') {
console.log('Link clicked:', target.textContent);
// 阻止默认行为,例如链接跳转
event.preventDefault();
}
});

在这个例子中,我们没有在每个<a>标签上添加事件监听器,而是将监听器添加到了<ul>元素上。当用户点击任何一个链接时,事件会冒泡到<ul>元素,然后我们检查event.target以确定哪个链接被点击。

阻止事件的默认行为

在JavaScript中,阻止元素的默认行为通常是在事件处理函数中完成的,这样可以防止与该事件相关的默认动作被执行。例如,阻止链接的导航、表单的提交、或阻止某些键盘快捷键的默认行为。

要阻止元素的默认行为,可以使用 event.preventDefault() 方法。这个方法是事件对象的一个属性,当在事件处理函数中被调用时,会取消事件的默认行为。

以下是一个阻止链接点击时默认跳转行为的例子:

1
2
3
4
5
6
const link = document.querySelector('a');

link.addEventListener('click', function(event) {
event.preventDefault();
console.log('Link clicked, but default action prevented.');
});

在这个例子中,当链接被点击时,原本的跳转行为会被 event.preventDefault() 取消,而控制台将输出一条信息。

同样地,如果你想要阻止表单提交的默认行为,可以这样做:

1
2
3
4
5
6
7
const form = document.querySelector('form');

form.addEventListener('submit', function(event) {
event.preventDefault();
console.log('Form submitted, but default action prevented.');
// 在这里执行自定义的提交逻辑
});

在这个表单提交的例子中,event.preventDefault() 阻止了表单的默认提交动作,使页面不会重新加载或导航到表单的action URL。

需要注意的是,preventDefault() 并不影响事件的冒泡或捕获过程。如果你想阻止事件的进一步传播,可以使用 event.stopPropagation()。但通常,preventDefault() 用于取消事件的默认行为,而 stopPropagation() 用于阻止事件向父元素的传播。

在某些情况下,如阻止拖放行为或阻止某些键盘快捷键的默认行为,preventDefault() 也是必需的。例如,阻止用户使用 Ctrl+C 复制文本:

1
2
3
4
5
6
document.addEventListener('keydown', function(event) {
if (event.ctrlKey && event.key === 'c') {
event.preventDefault();
console.log('Copy operation prevented.');
}
});

元素在页面中的位置

页面滚动

在JavaScript中,scroll通常指的是与滚动条和滚动位置相关的操作。浏览器窗口或元素的滚动可以通过以下几种方式来访问或修改:

  1. window.scroll()window.scrollTo()
    这些方法用于将浏览器窗口滚动到特定的位置。它们接受两个参数,第一个是水平位置(X坐标),第二个是垂直位置(Y坐标)。

    1
    window.scrollTo(0, 500); // 滚动到页面顶部以下500像素的位置

    或者使用 scroll() 方法:

    1
    window.scroll({top: 500, left: 0, behavior: 'smooth'}); // 平滑滚动到指定位置
  2. window.scrollBy()
    此方法相对于当前位置移动窗口。同样接受两个参数,分别表示水平和垂直方向上的偏移量。

    1
    window.scrollBy(0, 100); // 向下滚动100像素
  3. window.scrollXwindow.scrollY
    这两个属性返回窗口当前的滚动位置。scrollX是水平滚动距离,scrollY是垂直滚动距离。

    1
    console.log(window.scrollY); // 输出垂直滚动的距离
  4. element.scrollTopelement.scrollLeft
    这些属性用于获取或设置特定元素的滚动位置。scrollTop表示垂直滚动距离,scrollLeft表示水平滚动距离。

    1
    2
    3
    let element = document.getElementById('someElement');
    console.log(element.scrollTop); // 获取元素的垂直滚动距离
    element.scrollTop = 200; // 将元素的垂直滚动位置设置为200像素
  5. element.scrollHeightelement.scrollWidth
    这些属性返回元素的内容高度和宽度,包括溢出的部分。

    1
    2
    let element = document.getElementById('someElement');
    console.log(element.scrollHeight); // 获取元素内容的高度,包括不可见部分
  6. element.scrollIntoView()
    这个方法会将元素滚动到视图内,可选择性地使元素对齐到视口的顶部、中心或底部。

    1
    2
    let element = document.getElementById('someElement');
    element.scrollIntoView({behavior: 'smooth', block: 'center'}); // 平滑滚动并使元素居中显示

cliet家族

“client”家族的一系列属性提供了关于浏览器窗口可视区域的信息,而不包括工具栏、滚动条、菜单栏等非内容区域。

  1. clientWidth: 返回元素的宽度,包括内边距(padding),但不包括边框(border)和滚动条。如果应用于 window 对象,它返回浏览器窗口的可视宽度(不包括滚动条)。
  2. clientHeight: 类似于 clientWidth,但返回高度。这包括元素的内边距,但不包括边框和滚动条。在 window 对象上,它返回浏览器窗口的可视高度。
  3. clientLeft: 返回元素左侧边框的宽度。如果元素没有边框,此值为0。
  4. clientTop: 返回元素顶部边框的宽度。如果元素没有边框,此值为0。
  5. clientX: 这个属性通常与鼠标事件一起使用,表示相对于视口的鼠标水平位置。例如,在 mousedownmousemovemouseup 等事件中,event.clientX 提供了鼠标的水平坐标。
  6. clientY: 与 clientX 类似,但表示垂直坐标。

offset家族

在Web开发中,offset 家族的属性提供了一组与元素的位置和尺寸有关的信息。这些属性主要用于获取元素相对于其offset parent(偏移父元素)的位置和尺寸,而offset parent通常是元素最近的具有定位(positioned)的祖先元素或者是<body>元素本身(如果没有任何定位的祖先)。

offset 家族主要包括以下几个属性:

  1. offsetWidth: 返回元素的宽度,包括元素的内容、内边距(padding)、边框(border),但不包括外边距(margin)。单位是像素。
  2. offsetHeight: 返回元素的高度,包括元素的内容、内边距、边框,但不包括外边距。单位是像素。
  3. offsetLeft: 返回元素左边缘相对于其offset parent的左边缘的偏移量。单位是像素。**(只读属性)**
  4. offsetTop: 返回元素顶边缘相对于其offset parent的顶边缘的偏移量。单位是像素。**(只读属性)**
  5. offsetParent: 返回元素的offset parent。这个元素是最近的具有定位(position)属性的祖先元素,或者是<body>元素(如果没有定位的祖先)。

这些属性非常实用,尤其是在动态调整布局、响应式设计、元素对齐或动画效果中。由于它们返回的是实际的像素值,因此可以用于计算和调整元素的位置和大小。

值得注意的是,offset 家族的属性返回的值总是数值,而不是带有单位的字符串。这意味着,如果需要进行样式设置,你可能需要将这些数值转换为带有单位的字符串。

图解:

image-20240816090902709

获取元素大小位置的另外方法

  • 方法 :getBoundingClientRect()
    • 该方法返回元素的位置大小是相对于视口的位置来看的
    • image-20240816091457398

总结

属性 作用 说明
scrollLeft和scrollTop 被卷去的头部和左侧 配合页面滚动事件来使用,可读写
cilentWidth和clientHeight 获得元素的宽度和高度 不包含元素的border、padding、滚动条等,用于利用js读取元素大小,只读属性
offsetWidth和offsetHeight 获取元素的宽度和高度 包含元素的border、padding、滚动条等,只读属性
offsetLeft和offsetTop 获取元素距离自己已经拥有定位属性的父级元素的左、上方向的距离 获取元素位置的时候可以使用,只读属性

变量

语法:let 变量名

1.变量的赋值

语法:变量 = 要赋的值

1
2
3
4
5
6
7
let age

age = 18
//或者
let age = 18
//弹出输入框prompt("请输入姓名")
let name = prompt("请输入姓名")

2.var、const、let的区别

在较旧的JavaScript,使用关键字 var 来声明变量 ,而不是 let。var现在开发中一般不再使用它,只是我们可能再老版程序中看到它。let 为了解决 var 的一些问题。

为什么不用var 声明

  • 可以先使用 在声明(不合理)
  • var 声明过的变量可以重复声明(不合理)
  • 比如变量提升、全局变量、没有块级作用域等等

推荐使用 let 的情况:

  • 当你需要声明一个变量,且这个变量的值可能会发生变化时,使用 let
  • let 允许你在块级作用域内声明变量,这有助于避免变量之间的冲突和意外覆盖。

推荐使用 const 的情况:

  • 当你声明一个变量并且知道它的值不会改变时,应该使用 const
  • 使用 const 可以明确表示该变量是不可变的,这有助于提高代码的可读性和可维护性。
  • 即使是对象或数组,虽然你可以改变它们的内容,但 const 仍然有助于确保不会重新分配一个新的引用。

总结

  1. **默认使用 const**:尽可能地使用 const 来声明变量。这样可以确保在变量值不需要改变的情况下,它们不会被意外修改。
  2. **只有当需要改变时使用 let**:当你确定某个变量的值会在后续的代码中发生改变时,才使用 let 来声明。
  3. image-20240622165854723
  4. image-20240622170552011

webAPI基本认识

获取DOM元素

根据CSS选择器来获取DOM元素

  • 选择匹配的第一个元素

  • 语法

    • document.querySelector('css选择器')
      
      // 获取匹配的第一个元素(通过标签元素和类名来进行选择)
        const firstObj = document.querySelector('div')
        const firstObj2 = document.querySelector(".test")
        console.log(firstObj);
        console.log(firstObj2);
      
      const nav = document.querySelector("#nav")
      console.log(nav);
      
      const li = document.querySelector("ul li:first-child")
      console.log(li);
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12

      * 参数:包含一个或多个有效的CSS选择器字符串

      * 返回值:CSS选择器匹配的**第一个元素**,一个HTMLElement对象

      * 选择多个元素

      * 语法:

      ```javascript
      const lis = document.querySelectorAll("ul li")
      console.log(lis);
    • 返回值:返回一个数组

      • image-20240622173913182
      • 得到的是一个伪数组有长度有索引号的数组,但是没有 pop()push()等数组方法,想要得到里面的每一个对象,则需要遍历(for)的方式获得。

其他获取DOM元素的方法

image-20240622174536995

操作元素内容

对象.innerHtml属性

  • 将文本内容添加/更新到任意标签位置
  • 能够解析标签,通常配合模板字符使用

对象.innerText属性

  • 将文本内容添加/更新到任意标签位置
  • 显示纯文本,不能够解析标签

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改元素内容.html</title>
</head>
<body>
<div class="test1">
<span>这是修改前的内容</span>
<button onclick="changeDom1()">点我修改dom内容</button>
</div>
<div class="test2" style="margin-top: 10px;">
<span>这是修改前的内容</span>
<button onclick="changeDom2()">点我修改dom内容</button>
</div>
</body>
<script>

function changeDom1(){
let obj = document.querySelector('.test1')
obj.innerHTML = `<span>这是修改后的内容</span>`
}

function changeDom2(){
let obj = document.querySelector('.test2')
obj.innerText = "我是innerText,我只能修改文本,并不能识别HTML标签"
}

</script>
</html>

操作元素的属性

操作元素常用属性

  • 通过JS设置/修改标签的元素属性,比如通过src更换图片
  • 最常见的属性比如:href,title,src等
  • 语法:对象.属性 = 修改的值

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改元素内容.html</title>
</head>
<body>
<div class="test1">
<span>这是修改前的内容</span>
<button onclick="changeDom1()">点我修改dom内容</button>
</div>
<div class="test2" style="margin-top: 10px;">
<span>这是修改前的内容</span>
<button onclick="changeDom2()">点我修改dom内容</button>
</div>
</body>
<script>

function changeDom1(){
let obj = document.querySelector('.test1')
obj.innerHTML = `<span>这是修改后的内容</span>`
}

function changeDom2(){
let obj = document.querySelector('.test2')
obj.innerText = "我是innerText,我只能修改文本,并不能识别HTML标签"
}
</script>
</html>

操作元素样式属性

  • 通过style属性来操作CSS
  • 通过类名(className)来操作CSS
  • 通过classList操作类控制CSS

通过style属性来操作CSS

  • 语法:对象.style.属性 = 值
  • 多单词采取小驼峰命名
  • 注意单位
1
2
3
4
5
// 对象.style.属性 = 值
let box = document.querySelector('div')
box.style.width = '400px'
box.style.backgroundColor = "red"
box.style.border = "2px solid blue"

通过类名(className)来操作CSS

  • 语法:dom对象.className = "类名"
  • 修改较多样式,推荐使用该方法
  • 因为class是关键字,所以这里要使用className来代替class。
  • 通过这种方式是新值替换旧值,会顶替掉之前的类名。如果使用这种方法,要记得保留原来类名
1
2
3
//通过类名(className)来操作CSS
let box1 = document.querySelector("div:nth-child(2)")
box1.className = "testStyle"

通过classList操作类控制CSS

  • 追加一个类:
    • 对象.classList.add("类名")
  • 删除一个类:
    • 对象.classList.remove("类名")
  • 切换一个类:
    • 对象.classList.toggle("类名")
    • 如果原来有切换的类,那么这个类通过切换就会被移除
    • 如果没有,那么就会添加这个类
1
2
3
4
5
6
//通过classList操作类控制CSS
let box2 = document.querySelector("div:nth-child(3)")
box2.classList.add("testStyle2")
//添加这行代码。会让第三个盒子背景变为红色
box2.classList.remove("test")
box2.classList.toggle("test")

操作表单元素属性

操作HTML表单及其元素的属性可以通过DOM(Document Object Model)来实现。以下是一些常见的操作,比如获取或设置表单元素的属性、值、样式等:

获取和设置表单元素的值

1
2
3
4
5
// 获取文本框的值
var value = document.getElementById('myInput').value;

// 设置文本框的值
document.getElementById('myInput').value = 'Hello World';

检查复选框的状态

1
2
// 检查复选框是否被选中
var isChecked = document.getElementById('myCheckbox').checked;

设置复选框的状态

1
2
// 设置复选框为选中状态
document.getElementById('myCheckbox').checked = true;

操作选择框(下拉列表)

1
2
3
4
5
// 获取选择框的选中项的值
var selectedValue = document.getElementById('mySelect').value;

// 设置选择框的选中项
document.getElementById('mySelect').value = 'option2';

添加和删除表单元素

1
2
3
4
5
6
7
8
9
10
11
12
13
// 创建一个新的输入元素
var newInput = document.createElement('input');

// 设置新输入元素的类型和ID
newInput.type = 'text';
newInput.id = 'newInput';

// 将新输入元素添加到表单中
var form = document.getElementById('myForm');
form.appendChild(newInput);

// 从表单中移除一个元素
form.removeChild(document.getElementById('someElementId'));

设置和获取表单元素的CSS样式

1
2
3
4
5
// 设置元素的背景颜色
document.getElementById('myInput').style.backgroundColor = '#f0f0f0';

// 获取元素的背景颜色(注意这将返回计算后的样式,可能与设置的不同)
var bgColor = window.getComputedStyle(document.getElementById('myInput')).backgroundColor;

监听表单事件

1
2
3
4
// 当提交按钮被点击时,执行某些操作
document.getElementById('submitButton').addEventListener('click', function() {
alert('表单已提交');
});

本地存储

localStorage 对象

localStorage 对象提供对特定网站的本地存储的访问。它允许您存储、读取、添加、修改和删除该域的数据项。存储的数据没有到期日期,并且在浏览器关闭时不会被删除。这些数据将在几天、几周和几年内均可用。本地存储只能存为字符串

!温馨提示:本地存储在进行增删改等业务推荐使用同一个键,值推荐用数组存储多个,方便处理。

setItem(key,value)

  • 用途:用于将数据项存储在 storage 中。
    • 参数:它接受一个名称和一个值作为参数:

实例

1
2
 //利用localStorage存储数据 以K-V形式存储
localStorage.setItem('name', '小李');

getItem(key)

  • 用途:用于从localStorage中读取相应键对应的值
    • 参数:接收一个键值,字符串形式。

实例

1
2
let name = localStorage.getItem('name');
console.log(name);//小李

removeItem(key)

  • 用途:删除对应键的值
    • 参数:接收一个key值

实例

1
2
//从localStorage中删除数据
localStorage.removeItem('name');

sessionStorage 对象

sessionStorage 对象与 localStorage 对象相同。

不同之处在于 sessionStorage 对象存储会话的数据。

当浏览器关闭时,数据会被删除。

setItem(key,value)

  • 用途:sessionStorage.setItem() 方法将数据项存储在存储(storage)中。
    • 参数:它接受一个名称和一个值作为参数

实例

1
sessionStorage.setItem("name", "Bill Gates");

getItem(key)

  • 用途:sessionStorage.getItem() 方法从存储(storage)中检索数据项。
    • 参数:它接受一个名称作为参数

实例

1
sessionStorage.getItem("name");

removeItem(key)

  • 用途:删除对应键的值
    • 参数:接收一个key值

存储复杂类型的数据

这里以对象类型举例

如果要存储一个对象类型的数据,步骤如下

  1. 把对象类型转换成JSON字符串类型
    1. 语法:JSON.stringify(对象)
  2. 使用本地存储的存储方法存储数据

如果要取出一个字符串类型的对象数据,步骤如下

  1. 通过方法取出JSON字符串类型的对象数据
  2. 把取出来的JSON字符串类型数据转换成对象类型
    1. 语法:JSON.parse()

Storage 对象属性和方法

属性/方法 描述
key(n) 返回存储中第 n 个键的名称。
length 返回存储在 Storage 对象中的数据项数。
getItem(keyname) 返回指定的键名的值。
setItem(keyname, value) 将键添加到存储中,或者如果键已经存在,则更新该键的值。
removeItem(keyname) 从存储中删除该键。
clear() 清空所有键。

正则表达式

在JavaScript中,正则表达式(Regular Expression)是一种用于匹配字符串中字符组合的强大工具。它们可以用来执行复杂的文本处理任务,如搜索、替换和提取文本。

  • 使用场景:
    • 来进行表单验证,过滤敏感词等等。

创建正则表达式

  1. 字面量表示法:flags是修饰符,详见修饰符小结

    1
    let regex = /pattern/flags;
  2. 构造函数方式

    1
    let regex = new RegExp('pattern', 'flags');

常用的正则表达式模式

  • 匹配特定字符:

    • . 匹配任何单个字符(除了换行符)。
    • \d 匹配数字。
    • \w 匹配字母或数字。
    • \s 匹配空白字符。
  • 重复:

    • * 匹配前面的子表达式零次或多次。
    • + 匹配前面的子表达式一次或多次。
    • ? 匹配前面的子表达式零次或一次。
    • {n} 匹配前面的子表达式恰好 n 次。
    • {n,} 匹配前面的子表达式至少 n 次。
    • {n,m} 匹配前面的子表达式至少 n 次但不超过 m 次。
  • 选择:

    • | 或运算符用于匹配多个表达式中的任意一个。
  • 边界:

    • ^ 表示行的开始。
    • $ 表示行的结束。
    • \b 表示单词边界。

使用正则表达式的常见方法

  1. test() 方法

    1
    2
    let regex = /abc/;
    console.log(regex.test('abcdef')); // 输出 true
  2. exec() 方法:

    1
    2
    3
    4
    let regex = /(\d+)/;
    let result = regex.exec('The year is 2023.');
    console.log(result[0]); // 输出 "2023"
    console.log(result[1]); // 输出 "2023"

元字符

在JavaScript的正则表达式中,元字符(Metacharacters)是一些具有特殊含义的字符,它们被用来定义复杂的搜索模式。下面列出了一些常用的元字符及其功能:

常见元字符

边界符

  1. ^ (caret)
    • 在方括号 [ ] 内表示取反,即匹配不在方括号内的字符。
    • 不在方括号内时,如果位于正则表达式的开头,则表示匹配字符串的开始。
  2. $ (美元符号)
    • 如果位于正则表达式的末尾,则表示匹配字符串的结尾。

量词

  1. * (星号)
    • 表示匹配前面的字符零次或多次。
  2. + (加号)
    • 表示匹配前面的字符一次或多次。
  3. ? (问号)
    • 表示匹配前面的字符零次或一次。
  4. {n} (花括号)
    • 表示重复前面的字符 n 次。
  5. {n,} (花括号)
    • 表示重复前面的字符至少 n 次。
  6. {n,m} (花括号)
    • 表示重复前面的字符至少 n 次但不超过 m 次。

字符类

  1. [] (方括号)
    • 定义字符集,匹配方括号中的任何一个字符。
  2. . (点)
    • 表示匹配任何单个字符(除了换行符)。
  3. [^...] (取反方括号)
    • 定义非字符集,匹配不属于方括号中的任何字符。
  4. () (圆括号)
    • 定义捕获组,用于对匹配进行分组并捕获匹配的部分。
  5. | (管道符号)
    • 表示逻辑“或”,匹配两个或多个选项中的任意一个。
  6. \b (单词边界)
    • 匹配单词边界,即单词字符和非单词字符之间的边界。
  7. \B (非单词边界)
    • 匹配非单词边界,即两个单词字符或两个非单词字符之间的地方。
  8. \d (数字)
    • 匹配任何十进制数字等同于 [0-9]
  9. \D (非数字)
    • 匹配任何非数字字符等同于 [^0-9]
  10. \w (单词字符)
    • 匹配任何单词字符等同于 [a-zA-Z0-9_]
  11. \W (非单词字符)
    • 匹配任何非单词字符等同于 [^a-zA-Z0-9_]
  12. \s (空白字符)
    • 匹配任何空白字符等同于 [ \t\n\r\f\v]
  13. \S (非空白字符)
    • 匹配任何非空白字符等同于 [^ \t\n\r\f\v]
  14. \A (字符串开始)
    • 表示匹配字符串的开始。
  15. \Z (字符串结束)
    • 表示匹配字符串的结束。
  16. \z (字符串结束)
    • 同样表示匹配字符串的结束。
  17. \G (上次匹配结束处)
    • 表示从上次匹配的结束位置开始匹配。
  18. \n, \r, \t, \f, \v (转义字符)
    • 分别代表新行、回车、制表符、换页符和垂直制表符。

修饰符

语法:/正则表达式/修饰符,也就是在正则表达式后面的就是修饰符

  1. g (全局匹配)
    • 这个标志告诉正则表达式引擎在整个字符串中查找所有匹配项,而不仅仅是找到第一个匹配后就停止。如果没有使用这个标志,正则表达式默认是从左向右只匹配第一个符合条件的结果。
  2. i (忽略大小写)
    • 当使用此标志时,正则表达式在进行匹配时不区分大小写。例如,/abc/i 将会匹配 "ABC", "AbC" 等。
  3. m (多行模式)
    • 此标志使 ^$ 能够分别匹配每行的开始和结束,而不仅仅是整个字符串的开始和结束。例如,在多行文本中,/^abc/m 将匹配每一行的开头为 “abc” 的部分。
  4. s (单行模式)
    • 也被称为 “dotall” 模式。此标志使得 . 可以匹配任何字符,包括换行符。没有此标志时,. 不匹配换行符。
  5. u (Unicode 模式)
    • 此标志使得正则表达式支持完整的 Unicode 字符范围。当使用 \w, \d, \s 等预定义类时,将考虑所有 Unicode 字符,而不仅仅是 ASCII 字符。
  6. y (粘贴模式)
    • 该标志使得正则表达式只能从上次匹配的位置开始匹配。这通常与全局标志 g 一起使用,以便在连续的调用中保持匹配的位置。

示例

让我们通过一些例子来看看这些元字符是如何使用的:

匹配任何数字

1
2
let regex = /\d/;
console.log(regex.test('123')); // true

匹配以数字开头的字符串

1
2
let regex = /^\d/;
console.log(regex.test('123abc')); // true

匹配以数字结尾的字符串

1
2
let regex = /\d$/;
console.log(regex.test('abc123')); // true

匹配连续三个或更多的字符

1
2
let regex = /a+/;
console.log(regex.test('aaa')); // true

匹配连续一个到三次的字符

1
2
3
4
5
let regex = /a{1,3}/;
console.log(regex.test('aaa')); // true
console.log(regex.test('aa')); // true
console.log(regex.test('a')); // true
console.log(regex.test('aaaa')); // false

匹配任何数字或字母

1
2
3
let regex = /[a-zA-Z0-9]/;
console.log(regex.test('a')); // true
console.log(regex.test('9')); // true

匹配任何非数字

1
2
3
let regex = /\D/;
console.log(regex.test('a')); // true
console.log(regex.test('9')); // false

JavaScript异步

JS中的Promise(更详细请移步笔记Vue前置知识.md)

  • 概念:Promise 是一种处理异步操作的更优雅的方法。Promise 对象代表了一个最终会在未来完成(或失败)的值的初次计算结果。

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
//1.promise的基本语法
const promise = new Promise(function(resolve,reject){
//2.这里是用来执行异步任务的代码块
// 异步任务执行成功调用resolve() 此时可以通过 .then 方法访问结果。
//异步任务执行失败调用reject() 此时可以通过 .catch 方法处理错误。
})
//3.接收结果
promise.then(result =>{
//成功后获得数据
console.log(result);
}).catch(error =>{
//处理失败
})

promise的三种状态

Promise 有三种状态:

  • pending:初始状态,既未成功也未失败。对应 new Promise()
  • fulfilled:成功完成,此时可以通过 .then 方法访问结果。 ——调用了resolve()函数
  • rejected:操作失败,此时可以通过 .catch 方法处理错误。——调用了reject()函数

promise对象的使用

1
2
3
4
5
6
7
8
9
10
11
let myPromise = new Promise(function(myResolve, myReject) {
// 执行某些异步任务

myResolve(); // 成功时
myReject(); // 出错时
});

// 下面就是对promise对象的使用
myPromise.then(
function(value) { /* 成功时的代码 */ }
).catch(function(error) { /* 出错时的代码 */ })

回调地狱

多个回调函数形成嵌套,形成回调地狱。

缺点:

  • 可读性差
  • 异常捕获困难
  • 耦合性差、

promise链式调用

  • 原理:promise对象.then()方法,利用then方法返回新的promise对象。和上一个对象不相同

  • 概念:依靠then()方法返回一个新的promise对象特性,继续串联下一环任务,直到结束。

image-20240827155942150

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('北京市')
}, 2000)
})
//获取
//查看p.then()返回对象
//函数返回了一个新的promise对象,
const p2 = p.then((result) => {
//then方法返回一个新的promise对象。
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result + '-----北京市')
}, 3000);
})
})
p2.then(result =>{
console.log(result); //北京市-----北京市
})

结合Axios来使用链式调用(重点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//由于每发起一次请求,就会有一个新的promise对象所以可以下向下面这样写
axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => {
const pname = result.data.list[0]
//在then函数中,继续向外层return一个新的promise对象
return axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname: pname } })
//.then的位置要写在then函数外,因为利用的是该函数的返回值。
}).then(result => {
const pname = result.config.params.pname
console.log(pname);//北京
const cname = result.data.list[0]
return axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: pname, cname: cname } })
//.then的位置要写在then函数外,因为利用的是该函数的返回值。
}).then(result => {
console.log(result); //返回北京市下的城市名称
})

JavaScript Async(异步)

“async and await make promises easier to write”

async 使函数返回 Promise

await 使函数等待 Promise

Async 语法

函数前的关键字 async 使函数返回 promise:

实例1

1
2
3
async function myFunction() {
return "Hello";
}

等同于:

1
2
3
async function myFunction() {
return Promise.resolve("Hello");
}

以下是使用 Promise 的方法:

1
2
3
myFunction().then(
function(value) { /* 成功时的代码 */ },
).catch(function(error) { /* 出错时的代码 */ })

实例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<body>

<h1>JavaScript async / await</h1>

<p id="demo"></p>

<script>
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
//async 使函数返回promise
async function myFunction() {return "Hello";}
//在这里调用promise返回时的动作
myFunction().then(
function(value) {myDisplayer(value);}, //成功的回调
function(error) {myDisplayer(error);} //失败的回调
);</script>

</body>
</html>

Await 语法

函数前的关键字 await 使函数获得Promise对象”成功状态”的结果值。

  • await只能获取请求成功时,得到的结果。
  • await只能使用在被Async修饰的函数内部。
1
let value = await promise;

await 关键字只能在 async 函数中使用。

基础语法

实例

1
2
3
4
5
6
7
8
9
10
//await只能获取请求成功时,得到的结果。
//await只能使用在被Async修饰的函数内部。
async function getData(){
//await等待请求成功时,将数据赋值给变量。
const provinceName = await axios({
url:'http://hmajax.itheima.net/api/province'
})
console.log(provinceName);
}
getData()

async和await捕获错误

  • 使用try{} catch(error){}来捕获错误。
  • try包裹可能出现错误的代码块
  • 如果在某一环节捕获到了错误,那么剩余的代码不会执行
  • catch来处理错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
async function getData() {
//捕获错误 ——利用try ....catch
//try包裹可能出现错误的代码
try {
//await等待请求成功时,将数据赋值给变量。
const provinceName = await axios({
url: 'http://hmajax.itheima.net/api/provinc'
})
console.log(provinceName);
//利用catch来接收错误信息
//如果在某一环节捕获到了错误,那么剩余的代码不会执行
} catch (error) {
console.log('1111', error);
}
}
getData()

Ajax

  • 概念:Asynchronous JavaScript And XML,异步的JavaScript和XML。
  • 作用:
    • AJAX 是开发者的梦想,因为您能够:
      • 不刷新页面更新网页
      • 在页面加载后从服务器请求数据
      • 在页面加载后从服务器接收数据
      • 在后台向服务器发送数据
    • 数据交换:通过Ajax可以给服务器发送请求,并获取服务器响应的数据。
    • 异步交互:可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术,如:搜索联想、用户名是否可用的校验等等。

Axios

Axios 是一个基于 promise 的 HTTP 库,可以用于浏览器和 node.js 中。它被广泛用于与 RESTful APIs 进行交互,特别是在前端开发中,用来发送 AJAX 请求。

基础语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
axios({
//请求方式
method:
//请求地址
url:
//请求参数
data:{/*向服务器发送数据用data选项,一般是post请求*/}
params:{
/*携带查询参数用params*/
参数名:参数值
}
//回调函数
}).then(function(res){
console.loh(res.data)
})

请求配置

常用配置项

  1. url: (必需) 发送请求的 URL 地址。
  2. method: (必需) 请求方法,如 'get', 'post', 'put', 'delete', 'patch', 'head', 'options'
  3. data: (可选) 发送到服务器的数据。对于 POST, PUT, PATCH 等方法来说,这个数据会被序列化并发送到服务器。
  4. params: (可选) 发送到服务器的查询字符串参数。
  5. headers: (可选) 自定义请求头。
  6. timeout: (可选) 请求超时时间,单位为毫秒。如果请求超过了指定的时间,将抛出一个 Timeout 错误。
  7. withCredentials: (可选) 是否携带 cookie,这对于跨域请求非常重要,默认为 false
  8. responseType: (可选) 指定响应的数据类型,如 'arraybuffer', 'blob', 'json', 'text', 'stream'
  9. validateStatus: (可选) 验证服务器响应的状态码是否成功。默认情况下,只有状态码在 2xx 范围内的才会被认为是成功的。
  10. maxRedirects: (可选) 最大重定向次数,默认为 5
  11. cancelToken: (可选) 取消请求的 token,用于取消正在进行的请求。
  12. auth: (可选) 包含用户名和密码的对象,用于 HTTP 基本身份验证。

错误处理

错误处理的基本方式

  1. 使用 .catch() 处理错误: 当请求失败时,.catch() 函数会捕获错误,并允许你执行相应的错误处理逻辑。

    1
    2
    3
    4
    5
    6
    7
    axios.get('https://api.example.com/data')
    .then(response => {
    console.log(response.data);
    })
    .catch(error => {
    console.error("Error:", error);
    });

请求方法

下面是一些基本请求方法:

  1. GET - 用于获取资源。
  2. POST - 用于创建资源。
  3. PUT - 用于更新资源。
  4. DELETE - 用于删除资源。
  5. PATCH - 用于局部更新资源。
  6. HEAD - 用于获取资源的元信息。
  7. OPTIONS - 用于获取服务器支持的通信选项。

HTTP响应状态码

下面是一些常用的状态码

成功状态码

  • 200 OK:请求已成功,这是最常见的成功状态码。
  • 201 Created:请求被成功创建,通常用于 POST 请求。
  • 204 No Content:服务器成功处理了请求,但不需要返回任何内容。

重定向状态码

  • 301 Moved Permanently:请求的资源已被永久移动到新位置。
  • 302 Found(临时重定向):请求的资源现在临时位于不同的 URI。
  • 304 Not Modified:请求的资源未被修改,因此返回 304 并不包含资源的实体部分。

客户端错误状态码

  • 400 Bad Request:服务器无法理解请求的格式。
  • 401 Unauthorized:请求需要用户身份验证。
  • 403 Forbidden:服务器理解请求客户端的请求,但是拒绝执行此请求。
  • 404 Not Found:请求的资源不存在。
  • 405 Method Not Allowed:请求的方法不允许对请求的资源使用。

服务器错误状态码

  • 500 Internal Server Error:服务器遇到一个未曾预料的状况,导致它无法完成对请求的处理。
  • 502 Bad Gateway:作为网关或代理工作的服务器收到了来自上游服务器的有效响应,但是无效的最终响应。
  • 503 Service Unavailable:服务器目前无法使用,可能是由于服务器过载或维护。

什么是 Ajax

AJAX = Asynchronous JavaScript And XML.

AJAX 并非编程语言。

AJAX 仅仅组合了:

  • 浏览器内建的 XMLHttpRequest 对象(从 web 服务器请求数据)
  • JavaScript 和 HTML DOM(显示或使用数据)

Ajax 是一个令人误导的名称。Ajax 应用程序可能使用 XML 来传输数据,但将数据作为纯文本或 JSON 文本传输也同样常见。

Ajax 允许通过与场景后面的 Web 服务器交换数据来异步更新网页。这意味着可以更新网页的部分,而不需要重新加载整个页面。

Ajax如何工作

AJAX

  1. 网页中发生一个事件(页面加载、按钮点击)
  2. 由 JavaScript 创建 XMLHttpRequest 对象
  3. XMLHttpRequest 对象向 web 服务器发送请求
  4. 服务器处理该请求
  5. 服务器将响应发送回网页
  6. 由 JavaScript 读取响应
  7. 由 JavaScript 执行正确的动作(比如更新页面)

Ajax基础语法

  1. 创建 XMLHttpRequest 对象 - 用于在后台与服务器交换数据。
  2. 定义回调函数 - 当从服务器请求数据返回后执行的函数。
  3. 发送请求到服务器 - 通常使用 GETPOST 方法。
  4. 处理服务器响应 - 在回调函数中处理数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();

// 设置请求完成后的回调函数 --------------写法1
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) { // 请求完成并且成功
console.log(xhr.responseText); // 输出服务器响应的数据
}
};
//当然了,上面的回调函数也可以写成下面这样 --------------写法2
xhr.addEventListener('loadend',()=>{
//对响应回来的结果进行处理
console.log(xhr.responseText);
})
// 初始化请求
xhr.open('GET', 'https://api.example.com/data', true);

// 发送请求
xhr.send();

Ajax传递参数

携带查询参数

查询参数直接拼接在URL后面,利用**?,来把查询参数以‘k = v’**的形式写在后面

1
2
3
4
5
6
7
 const xhr = new XMLHttpRequest()
//参数直接拼接在url后面,利用?,来把查询参数以'k = v'的形式写在后面
xhr.open('get','http://hmajax.itheima.net/api/city?pname=辽宁省')
xhr.onload = function(){
console.log(JSON.parse(xhr.responseText));
}
xhr.send()

携带要提交的数据

  • 设置请求头(Content-Type)为application/json

    1
    2
    //设置请求头为application/json
    xhr.setRequestHeader('Content-Type','application/json')
  • 准备数据,并转为JSON格式

    1
    2
    const userObj = {username:'lizewei111',password:'123456789'}
    const userStr = JSON.stringify(userObj)
  • 发送数据

    1
    2
    //发送请求 - 并把要发送的数据携带上
    xhr.send(userStr)

完整逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const xhr  = new XMLHttpRequest();
xhr.open('post','http://hmajax.itheima.net/api/register')
//设置请求头为application/json
xhr.setRequestHeader('Content-Type','application/json')
//把准备的数据转为JSON字符串
const userObj = {username:'lizewei111',password:'123456789'}
const userStr = JSON.stringify(userObj)
//设置回调函数
xhr.addEventListener('loadend',()=>{
//回调后执行的逻辑
console.log(JSON.parse(xhr.responseText));
})
//发送请求 - 并把要发送的数据携带上
xhr.send(userStr)

xhr对象

获得xhr对象

  1. xhr对象通过构造函数创建

    1
    const xhr = new XMLHttpRequest();

xhr对象方法

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

xhr对象属性

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

同步请求/异步请求

  • 同步代码:逐行执行,需原地等待结果,才继续向下执行。
  • 异步代码:调用会耗用事件,不阻塞代码继续执行,完成后会执行一个回调函数。(耗时任务)

如需执行同步的请求,请把 open() 方法中的第三个参数设置为 false,默认执行异步请求,也就是true

1
xhttp.open("GET", "ajax_info.txt", false);

有时 async = false 用于快速测试。你也会在更老的 JavaScript 代码中看到同步请求。

由于代码将等待服务器完成,所以不需要 onreadystatechange 函数:

实例

1
2
3
xhttp.open("GET", "ajax_info.txt", false);
xhttp.send();
document.getElementById("demo").innerHTML = xhttp.responseText;

我们不推荐同步的 XMLHttpRequest (async = false),因为 JavaScript 将停止执行直到服务器响应就绪。如果服务器繁忙或缓慢,应用程序将挂起或停止。

同步 XMLHttpRequest 正在从 Web 标准中移除,但是这个过程可能需要很多年。

现代开发工具被鼓励对使用同步请求做出警告,并且当这种情况发生时,可能会抛出 InvalidAccessError 异常。

同步和异步:

image-20240504154717543

服务器响应请求

服务器响应属性

属性 描述
responseText 获取字符串形式的响应数据
responseXML 获取 XML 数据形式的响应数据

responseText 属性

responseText 属性以 JavaScript 字符串的形式返回服务器响应,因此您可以这样使用它:

实例

1
document.getElementById("demo").innerHTML = xhttp.responseText;

responseXML 属性

XML HttpRequest 对象有一个內建的 XML 解析器。

ResponseXML 属性以 XML DOM 对象返回服务器响应。

服务器响应方法

方法 描述
getResponseHeader() 从服务器返回特定的头部信息
getAllResponseHeaders() 从服务器返回所有头部信息

getAllResponseHeaders() 方法

getAllResponseHeaders() 方法返回所有来自服务器响应的头部信息。

实例
1
2
3
4
5
6
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("demo").innerHTML = this.getAllResponseHeaders();
}
};

getResponseHeader() 方法

getResponseHeader() 方法返回来自服务器响应的特定头部信息。

实例
1
2
3
4
5
6
7
8
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("demo").innerHTML = this.getResponseHeader("Last-Modified");
}
};
xhttp.open("GET", "ajax_info.txt", true);
xhttp.send();

JavaScript进阶

JS中的堆和栈

栈(Stack)

栈是一种先进后出(Last In First Out, LIFO)的数据结构。在JavaScript中,栈主要用来存储基本类型的值(也称为原始类型,如数字、字符串、布尔值、null、undefined)和对象的引用。

  • 特点:
    • 栈中的数据存取非常快,因为它是按照LIFO原则操作的。
    • 栈空间的大小通常是固定的,超出范围会导致栈溢出错误。
    • 栈中的数据一旦不再需要就会被自动释放。

堆(Heap)

堆是一种无序的、动态的数据结构,在JavaScript中用于存储对象和数组的实际内容。

  • 特点:
    • 堆中的数据可以动态分配和释放,这意味着可以在运行时增加或减少内存。
    • 堆中的数据访问速度比栈慢,因为它涉及到指针查找和间接访问。
    • 堆中的数据不会自动释放,而是通过垃圾回收机制来清理不再使用的数据。

关键区别

  • 存储位置:
    • 栈存储基本类型的值和对象的引用。
    • 堆存储对象本身及其属性。
  • 内存管理:
    • 栈中的数据通常在创建时就已经确定了大小,并且当作用域结束时自动释放。
    • 堆中的数据会在运行时动态分配,并由垃圾回收器管理,当对象不再被引用时会被回收。

作用域

  • 规定了变量能够被访问的范围,离开这个范围,变量无法被访问
    • 作用域分为:局部作用域和全局作用域

局部作用域

在JavaScript中,局部作用域指的是变量在一个特定的代码块(如函数内部)内可见和可访问的范围。当变量在一个函数内部被声明时,它只能在该函数内部被访问,这就是局部作用域的概念。

函数作用域

  • 使用 var 关键字声明的变量在整个函数体内都是可见的。
  • 这种作用域适用于整个函数,而不是某个具体的代码块。

块作用域

块:被{}包裹的就是块

  • 使用 letconst 关键字声明的变量只在声明它们的代码块内可见。
  • 这种作用域适用于像循环或条件语句这样的代码块。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function exampleFunction() {
var varExample = "I am visible in the entire function scope"; // 函数作用域
if (true) {
let letExample = "I am visible only in this block scope"; // 块作用域
const constExample = "I am also visible only in this block scope"; // 块作用域
console.log(varExample); // 输出: "I am visible in the entire function scope"
console.log(letExample); // 输出: "I am visible only in this block scope"
console.log(constExample); // 输出: "I am also visible only in this block scope"
}
console.log(varExample); // 输出: "I am visible in the entire function scope"
console.log(letExample); // ReferenceError: letExample is not defined
console.log(constExample); // ReferenceError: constExample is not defined
}
exampleFunction();

全局作用域

script标签和js文件是全局作用域,全局作用域内声明的变量可以被局部作用域访问

作用域链

作用域链本质上是底层的变量查找机制

  • 在函数执行时,会优先查找当前函数作用域中查找变量
  • 如果当前作用域查找不到则会一次逐级查找父级作用域,直到全局作用域

image-20240820150519335

JS垃圾回收机制

image-20240820153438781

JavaScript 的垃圾回收机制(Garbage Collection, GC)是一种自动管理内存的过程,它负责释放不再使用的内存空间。这使得开发者无需手动管理内存分配和释放,从而降低了内存泄漏的风险。以下是 JavaScript 垃圾回收机制的一些关键方面:

标记与清除(Mark and Sweep)

这是最基本的垃圾收集算法之一。它分为两个阶段:

  • 标记(Mark):垃圾收集器标记所有从根节点开始能够寻找到的对象。
  • 清除(Sweep):清理未被标记的对象,这些对象被认为是垃圾,可以被释放。

引用计数(Reference Counting)

早期的 JavaScript 引擎还使用了引用计数的方法来跟踪对象的引用数量。当一个对象的引用计数变为 0 时,该对象就可以被回收。这种方法简单快速,但是容易导致循环引用的问题,即两个或多个对象相互引用而无法被垃圾收集器识别为垃圾。

分代收集(Generational Collection)

现代 JavaScript 引擎使用了一种称为分代收集的技术。这种技术基于观察到的事实:新创建的对象往往很快就会被丢弃,而存活时间较长的对象则很可能继续存活下去。因此,JavaScript 对象被划分为几代,不同代的对象使用不同的策略进行垃圾回收。

  • 年轻代(Young Generation):新创建的对象通常放在这一代中。年轻代的垃圾回收频率较高,但每次回收的时间较短。
  • 老年代(Old Generation):经过多次年轻代垃圾回收后仍然存活的对象会被移动到老年代。老年代的垃圾回收频率较低,但每次回收可能需要更长的时间。

增量收集(Incremental Collection)

增量收集将垃圾回收过程分成多个小步骤,这样可以在应用程序执行之间进行,从而减少长时间的暂停。

压缩(Compaction)

随着垃圾回收的进行,内存中的空闲空间可能会变得碎片化。压缩是一种技术,它将存活的对象移动到连续的内存区域,从而消除内存碎片。

如何避免内存泄漏

尽管 JavaScript 自动管理内存,但在某些情况下仍可能出现内存泄漏。以下是一些避免内存泄漏的方法:

  • 解除事件监听器:确保在不再需要时移除事件监听器。
  • 断开DOM引用:如果一个DOM元素被移除,确保断开与它的所有引用。
  • 使用弱引用:在某些现代JavaScript引擎中支持弱引用,可以用来避免循环引用导致的内存泄漏。
  • 清理定时器和回调:确保在不再需要时清除定时器和回调函数。
  • 避免闭包中的大对象:闭包可以维持对大对象的引用,确保它们在不再需要时被正确释放。

闭包

闭包(Closure)是 JavaScript 中一个非常重要的概念,简单来说,闭包就是函数和它所能访问的外部作用域变量的组合。

也就是闭包 = 内层函数 + 外层函数的变量,也就是内层函数要用到外层函数的变量

闭包的形成

闭包的形成通常涉及到以下三个要素:

  1. 函数:必须有一个函数。
  2. 外部作用域:函数必须访问其外部作用域中的变量。
  3. 返回值:通常返回一个函数或者使外部作用域能够通过某种方式访问。
1
2
3
4
5
6
7
8
9
10
11
12
function outerFunction() {
var externalVariable = "Hello, world!"; // 外部作用域变量

function innerFunction() {
console.log(externalVariable); // 访问外部作用域变量
}

return innerFunction; // 返回内部函数
}

var closure = outerFunction(); // 创建闭包
closure(); // 输出: "Hello, world!"

闭包的作用

  1. 保持数据私有:闭包可以用来隐藏变量,不让外部代码直接访问。
  2. 持久存储:闭包可以让变量在函数执行完毕后依然存在。
  3. 模拟私有成员:在面向对象编程中,闭包可以用来实现类的私有方法和属性。

闭包可能会造成内存泄漏这和它的持久存储有关系。

变量提升

了解:仅针对于var来声明的变量。

机制:

  • 把所有var声明的变量提升到当前作用域的最前面
  • 只提升,不进行赋值。
1
2
console.log(x); // 输出 undefined
var x = 5;

在这个例子中,虽然x在使用之前被声明了,但由于变量提升的作用,var x;这一行被移到了作用域的顶部,而赋值操作x = 5;则保持原位。因此,在输出x的时候它还没有被赋值,所以输出的是undefined

函数进阶

函数提升

机制:

  • 会把所有函数声明提升到当前作用域的最前面
  • 只提升函数声明,不提升调用

函数提升例子

1
2
3
4
5
console.log(helloWorld()); // 输出 "Hello, World!"

function helloWorld() {
return "Hello, World!";
}

在这个例子中,尽管函数helloWorld是在它被调用之后才被定义的,但是因为函数声明被提升到了作用域的顶部,所以可以正确地调用并返回结果。

警惕var定义的变量后给改该变量赋值。

1
2
3
4
5
console.log(goodbyeWorld()); // TypeError: goodbyeWorld is not a function

var goodbyeWorld = function() {
return "Goodbye, World!";
};

在这个例子中,尽管goodbyeWorld看起来像是一个函数,但实际上它是一个赋值给变量的函数表达式。因此,只有变量名goodbyeWorld被提升到了作用域的顶部,而函数体没有被提升。试图在定义之前调用goodbyeWorld会导致一个类型错误。

函数参数

默认参数

ES6还支持默认参数,允许你在函数定义时为参数指定默认值。当调用函数时如果没有提供某些参数,那么这些参数就会使用默认值。

1
2
3
4
5
6
function greet(name, greeting = "Hello") {
console.log(greeting + ", " + name);
}

greet("John"); // 输出 "Hello, John"
greet("Jane", "Hi"); // 输出 "Hi, Jane"

动态参数

  1. 使用 arguments 对象

场景:参数个数不确定。

该对象返回了一个伪数组,该数据并没有真正数组的方法。

1
2
3
4
5
6
7
8
9
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}

console.log(sum(1, 2, 3, 4, 5)); // 输出 15
  1. 使用剩余参数(…):剩余参数必须位于参数列表的最后。

从ES6开始,JavaScript引入了一种新的语法——剩余参数语法(Rest Parameters Syntax),它可以用于收集传递给函数的多余参数,并将它们作为一个数组来处理。

注意点:

  • 该语法获取的是剩余的参数

    • 比如:getSum(1,2,3),传递三个参数,

    • function getSum(a,b,...arr){
          
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10

      - 在上面这个函数中,a是1,b是2,那么通过剩余参数获得参数将会被放在一个数组中,也就是3会出现在数组中。

      注意:要与展开运算符(...),做一个区分:

      展开运算符,可以用来展开数组

      ```js
      const arr = [1,2,3];
      console.log(...arr) // 输出1,2,3

应用场景:

  • 求数组最大值、合并数组等。

箭头函数

箭头函数有着更简洁的函数写法并且不绑定this,箭头函数语法要比函数表达式简洁

基本语法

1
2
3
4
const fn = ()=>{
console.log('我是箭头函数');
}
fn();
  • 箭头函数可以传递参数

    1
    2
    3
    4
    const fn = (x)=> {
    console.log('我是箭头函数,这是传递的参数',x);
    }
    fn(1);
  • 只有一个形参时,可以省略参数的括号

    1
    2
    3
    4
    const fn = x=> {
    console.log('我是箭头函数,这是传递的参数',x);
    }
    fn(1);
  • 函数体只有一行,可以省略大括号

    1
    2
    const fn = x => console.log('我是箭头函数,这是传递的参数',x);
    fn(1);
  • 函数体只有一行代码,可以省略return

    1
    2
    const fn = x => x
    console.log(fn(1)); // 1
  • 箭头函数可以直接返回一个对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const uname = (uname) => ({uname:uname})
    console.log(uname('刘德华'));
    //上面这种写法相当于下面这样写
    const uname = (uname) => {
    return {
    uname:uname
    }
    }
    console.log(uname('刘德华'));

箭头函数的参数

普通形参,和剩余参数。注意:箭头函数里面没有arguments

1
2
3
4
5
6
7
8
9
10
//使用箭头函数求和,这里用剩余参数去写
let sum = 0;
const getSum = (...arr) => {
for (let i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
const result = getSum(1, 5);
console.log(result);

箭头函数中的this

箭头函数不绑定自己的this,它使作用域中的this值。这意味着箭头函数内部的this值是根据它被定义的位置来决定的,而不是根据它被调用的位置。

解构赋值

数组结构

  • 作用:快速批量赋值变量。

基本语法:

const [变量名] = arr;

1
2
3
4
5
const arr = [100,3,2];
//使用数组解构 - 会把数组中的值,按照声明变量的顺序依次赋值,相当于
// a = 100,b=3,c=2
const [a,b,c] = arr;
console.log(a,b,c);

忽略某些值

有时你可能只想解构数组中的部分值。你可以通过在解构模式中留空来忽略某些值:

1
2
3
4
5
let colors = ['red', 'green', 'blue'];
let [firstColor, , thirdColor] = colors;

console.log(firstColor); // 输出 "red"
console.log(thirdColor); // 输出 "blue"

使用默认值

1
2
3
4
5
6
let colors = ['red', 'green'];
let [firstColor, secondColor, thirdColor = 'default'] = colors;

console.log(firstColor); // 输出 "red"
console.log(secondColor); // 输出 "green"
console.log(thirdColor); // 输出 "default"

解构嵌套数组

1
2
3
4
5
6
7
let nestedColors = [['red', 'green'], ['blue', 'yellow']];
let [[firstColor, secondColor], [thirdColor, fourthColor]] = nestedColors;

console.log(firstColor); // 输出 "red"
console.log(secondColor); // 输出 "green"
console.log(thirdColor); // 输出 "blue"
console.log(fourthColor); // 输出 "yellow"

对象结构

在JavaScript中,对象解构(Object Destructuring)是一种从对象中提取值并将其分配给变量的简洁方式。这种方法可以让你以更直观的方式处理对象中的属性,特别是当你需要从对象中提取特定的属性时非常有用。

对象解构的基本语法如下:

1
let { key1, key2 } = object;

这里,{ key1, key2 }是用于接收对象中属性值的变量列表,而object是要解构的对象。

解构基本对象

1
2
3
4
5
6
7
8
9
10
11
let person = {
firstName: 'John',
lastName: 'Doe',
age: 30
};

let { firstName, lastName, age } = person;

console.log(firstName); // 输出 "John"
console.log(lastName); // 输出 "Doe"
console.log(age); // 输出 30

在这个例子中,我们创建了一个包含姓名和年龄的对象person,然后通过解构将对象中的属性分别赋值给了三个变量。

忽略某些属性

有时你可能只想解构对象中的部分属性。你可以通过在解构模式中留空来忽略某些属性:

1
2
3
4
5
6
7
8
9
10
let person = {
firstName: 'John',
lastName: 'Doe',
age: 30
};

let { firstName, , age } = person;

console.log(firstName); // 输出 "John"
console.log(age); // 输出 30

这里,lastName属性被忽略了,因为它对应的解构位置为空。

使用默认值

如果你希望在解构时为未提供的属性设置默认值,可以这样做:

1
2
3
4
5
6
7
8
9
10
let person = {
firstName: 'John',
lastName: 'Doe'
};

let { firstName, lastName, age = 25 } = person;

console.log(firstName); // 输出 "John"
console.log(lastName); // 输出 "Doe"
console.log(age); // 输出 25

在这个例子中,age属性没有在原始对象中提供,所以我们为它指定了一个默认值25

解构嵌套对象

你还可以解构嵌套的对象:注意:要在前面写上要结构对象的名字

1
2
3
4
5
6
7
8
9
10
11
12
13
let person = {
name: {
first: 'John',
last: 'Doe'
},
age: 30
};

let { name: { first, last }, age } = person;

console.log(first); // 输出 "John"
console.log(last); // 输出 "Doe"
console.log(age); // 输出 30

在这个例子中,我们有两个嵌套的对象,我们通过解构来分别获取每个对象中的属性。

解构并重命名属性

如果你想在解构的同时给变量重新命名,可以这样做:

1
2
3
4
5
6
7
8
9
10
11
let person = {
firstName: 'John',
lastName: 'Doe',
age: 30
};

let { firstName: givenName, lastName: familyName, age } = person;

console.log(givenName); // 输出 "John"
console.log(familyName); // 输出 "Doe"
console.log(age); // 输出 30

深入对象

创建对象

1.通过字面量方式创建:直接使用大括号 {} 来定义一个对象

1
2
3
4
5
6
7
8
let person = {
name: "Alice",
age: 30,
sayHello: function() {
console.log("Hello, my name is " + this.name);
}
};
person.sayHello(); // 输出: Hello, my name is Alice

2.使用new Object创建对象

1
2
const o = new Object({name:佩奇});
console.log(o) // {name:佩奇}

3.使用构造函数来创建

通过定义一个构造函数来创建对象。这种方式可以用来创建多个具有相同属性和方法的对象。

1
2
3
4
5
6
7
8
9
10
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log("Hello, my name is " + this.name);
};
}

let alice = new Person("Alice", 30);
alice.sayHello(); // 输出: Hello, my name is Alice

构造函数

  • 这是一种特殊的函数,主要用来初始化对象。
  • 通过构造函数可以快速创建多个类似的对象。

一般构造函数有两个约定:

  1. 命名以大写字母开头。
  2. 他们只能由’new’操作符来执行。
1
2
3
4
5
6
7
8
//创建构造函数
function Pig(name, age) {
this.name = name
this.age = age
}
//使用构造函数
const pig = new Pig('佩奇', 6)
console.log(pig);

实例成员&静态成员

这里要注意与java进行区别

1.实例成员:实例对象上的属性和方法就是实例属性和实例方法,被称为实例成员

2.静态成员:构造函数上的属性和方法就是静态属性和静态方法,称为静态成员。

存在的问题

存在浪费内存的问题。

内置构造函数

image-20240824164156519

Object—对象

学习三个常用的静态方法(静态方法就是只有构造函数Object可以调用)

  1. Object.keys()静态方法获取对象中所有的属性

    语法:

    1
    2
    3
    const o = {name:'佩奇',age:6}
    //获取对象的所有属性名,并且返回是一个数组
    const arr = Object.keys(o)

    注意:返回的是一个数组。

  2. Object.values()静态方法获取对象中所有的属性值

    语法:

    1
    2
    3
    const o = {name:'佩奇',age:6}
    //获取对象的所有属性名,并且返回是一个数组
    const arr = Object.values(o) //['佩奇',6]

    注意:返回的是一个数组。

  3. Object.assign()静态方法常用于对象拷贝

    语法:

    1
    2
    3
    4
    const o = {name:'佩奇',age:6}
    //获取对象的所有属性名,并且返回是一个数组
    const cloneObj = {}
    Object.assign(CloneObj,o) //把对象o的属性和属性值拷贝给对象cloneObj

    注意:返回的是一个数组。

    使用场景:一般用来给某个对象追加新属性,特别适用。

Array—数组

详细用法请见:JS对象—数组对象小结

基本语法:

1
const str = new Array('这是通过构造函数创建的字符串')

下面是一些字符串的基本方法。

image-20240824170050222

Number—数值

  • 作用:用于创建数组,是一个内置构造函数
1
2
3
4
5
6
7
8
console.log(new Number('123')); // 123
console.log(new Number('45.67')); // 45.67
console.log(new Number(true)); // 1
console.log(new Number(false)); // 0
console.log(new Number('abc')); // NaN
console.log(new Number('100px')); // NaN
console.log(new Number(null)); // 0
console.log(new Number(undefined)); // NaN

一些常用方法

  1. toFixed():设置保留小数位的长度

    1
    2
    3
    const price = 12.345;
    //保留两位小数,四舍五入
    price.toFixed(2) // 12.35

两种编程思想

面向过程编程(Procedural Programming)和面向对象编程(Object-Oriented Programming)。

1. 面向过程编程(Procedural Programming)

面向过程编程是一种以过程为中心的编程范式。它将程序视为一系列的过程或函数调用,每个过程或函数完成特定的任务。这种编程方式强调的是如何一步步地解决问题。

特点包括:

  • 代码组织:通过函数或子程序来组织代码,使得代码更易于理解和复用。
  • 数据处理:数据通常作为参数传递给函数,并且结果也通过函数返回。
  • 控制流:使用流程控制结构(如循环和条件语句)来控制程序执行的顺序。

2. 面向对象编程(Object-Oriented Programming, OOP)

面向对象编程是一种以对象为中心的编程范式。它将程序视为对象的集合,这些对象可以包含数据(属性)和行为(方法)。OOP 强调的是对象之间的交互。

特点包括(三大特性):

  • 封装:将数据和操作数据的方法绑定在一起,隐藏内部细节,只对外提供接口。
  • 继承:允许创建现有类的新版本,新版本可以继承原始类的特性并添加新的功能。
  • 多态:允许一个接口代表多种类型,也就是说,同一个接口可以有多种不同的实现方式。

原型(重要)

原型对象

  • 构造函数通过原型分配的函数是所有对象所共享的。
  • JS规定,每一个构造函数都有一个prototype属性,指向另一个对象,所以我们也成为原型对象
  • 该对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
  • 我们可以吧那些不变的方法,直接定义在prototype对象上,这样索引对象的实例就可以共享该方法
  • 构造函数和原型对象中的this指向实例化对象。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//自己定义数组拓展方法 - 求和 和 最大值
//1.定义的方法,任何数组都可以使用,所以定义在数组的原型对象上

//求最大值
const arr = [1,2,3]
Array.prototype.max = function() {
//this在这里指向实例对象,也就是arr
return Math.max(...this)
}

console.log(arr.max());//3

//求和
Array.prototype.sum = function(){
//this在这里指向实例对象,也就是arr
return this.reduce(function(prev,current){
return prev + current
},0)
}
//调用
console.log(arr.sum()); //6

constructor属性

每个原型对象中都有construtor属性(该属性也被成为construtor构造函数)

  • 作用:该属性指向该原型对象的构造函数,一图理解。

image-20240825144903233

对象原型

每个实例化的对象,都会有一个属性__proto__指向构造函数的prototype的原型对象,之所以我们的实例化对象可以使用构造函数原型对象的方法和属性,就是因为有__proto__对象原型的存在。

一图理解

image-20240825150040171

注意:

  • __proto__是JS非标准属性。
  • __proto__是只读属性。
  • [[prototype]] 和__proto__意义相同。
  • 用来表示当前实例对象指向那个原型对象
  • 最后一点:__proto__对象原型里面也有一个constructor属性,该属性指向创建该实例对象的构造函数。

三者最终关系,如下

image-20240825151653650

原型继承

假设我们有两个构造函数ParentChild,我们想要让Child继承Parent的属性和方法。这可以通过将Childprototype属性设置为Parent的实例来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Parent() {
this.message = 'Hello from Parent';
}

Parent.prototype.showMessage = function() {
console.log(this.message);
};

function Child() {
this.message = 'Hello from Child';
}

// 设置 Child 的 prototype 为 Parent 的实例,这句代码是核心,继承就是通过这样实现。
//但是切记要写成下面这种格式。
Child.prototype = new Parent();

// 修正 constructor 指向 Child
Child.prototype.constructor = Child;

let childInstance = new Child();
childInstance.showMessage(); // 输出 "Hello from Parent"

出现的问题

  • 当你直接将Child.prototype设置为Parent时,所有Child实例共享Parent原型上的属性。这意味着如果你在一个实例中修改了原型上的属性,那么这个变化会影响到其他所有实例。
  • 要注意修正 constructor 指向。不能丢失了constructor属性。

原型链

JS的查找规则:

当我们在JavaScript中访问一个对象的属性时,如果该对象本身没有这个属性,JavaScript引擎会沿着该对象的原型链继续查找。更具体的查找规则如下:

  • 当访问一个对象属性/方法时,第一步查找该对象本身有没有该属性/方法
  • 如果没有,那么就查找它的原型(也就是__proto__指向的prototype原型对象)。
  • 如果还没有那就查找原型对象的原型(也就Object的原型对象)。
  • 依次类推一直找到Object为止,因为在往上找就是null
  1. __proto__对象原型存在的意义就在于为对象成员查找机制提供一个方向。或者说一条路线。
  2. 可以使用instanceof运算符用于检测构造函数中的prototype属性是否出现在某个实例对象的原型链上

原型链是由一系列原型对象组成的链式结构,最终会指向null,因为null没有原型。

原型链的工作原理

  1. 对象创建/原型对象的概念:当使用new关键字创建一个对象时,该对象会自动获得一个原型对象,通常是构造函数的prototype属性所指向的对象。
  2. 属性查找:当访问一个对象的属性或方法时,JavaScript引擎首先会在该对象自身查找该属性。如果没有找到,它会沿着原型链向上查找,直到找到该属性或到达原型链的末端。
  3. 原型链的末端:原型链的末端是一个没有任何属性的空对象,即null

原型链的构造

  1. 内置原型:每个内置的构造函数都有一个prototype属性,如Function.prototypeArray.prototype等。
  2. 自定义构造函数:自定义的构造函数同样拥有prototype属性,用于继承的目的。
  3. 原型链的构建:当创建一个新对象时,该对象的__proto__属性被设置为构造函数的prototype属性所指向的对象。

一图理解

image-20240825181428296

上述红线的方向就是原型链的查找方向,也是原型链的方向

深浅拷贝

在JavaScript中,对象的复制通常分为两种类型:浅拷贝(shallow copy)和深拷贝(deep copy)。这两种拷贝方式的主要区别在于它们处理对象内部可变数据结构的方式不同。

浅拷贝 (Shallow Copy)

浅拷贝会创建一个新的对象,并尽可能地把原对象中的所有可枚举属性值复制到新对象中。如果原对象中有引用类型的属性(如数组、对象等),那么浅拷贝只会复制这些引用类型的地址而不是其内容。这意味着,对于引用类型的属性,浅拷贝后的对象和原对象指向的是同一个内存地址,因此改变其中一个对象会影响到另一个。

实现方法

  • 使用 Object.assign(): 可以用来创建一个新对象,并将一个或多个现有对象的可枚举属性拷贝到新对象中。
  • 使用扩展运算符 ...: 也可以用来实现对象的浅拷贝。

示例:

1
2
3
4
5
6
7
8
9
10
11
let obj1 = {
a: 1,
b: { nested: 2 }
};

let obj2 = Object.assign({}, obj1);
let obj3 = { ...obj1 };

obj1.b.nested = 3;
console.log(obj2.b.nested); // 输出 3
console.log(obj3.b.nested); // 输出 3

深拷贝 (Deep Copy)

深拷贝会创建一个新的对象,并递归地复制原对象的所有属性,包括引用类型的数据也会被完全复制,从而保证了新对象与原对象不共享任何数据。这使得修改新对象不会影响到原对象。

实现方法

  • 使用 JSON.parse()JSON.stringify(): 这种方法可以简单地实现深拷贝,但不适用于循环引用的对象、函数、undefinedSymbol 等。
  • 使用第三方库如 Lodash 的 _().cloneDeep() 方法。
  • 手动实现递归深拷贝。

示例:

1
2
3
4
5
6
7
8
let obj1 = {
a: 1,
b: { nested: 2 }
};

let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.b.nested = 3;
console.log(obj2.b.nested); // 输出 2

需要注意的是,使用 JSON.parse()JSON.stringify() 方法有一些限制,比如不能处理函数、日期、正则表达式等特殊类型的对象。

如果你需要处理更复杂的数据结构或者有特殊需求,可以考虑使用一些成熟的库,例如 Lodash 的 _.cloneDeep() 方法。

手写深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//2.深拷贝
//1.利用递归实现,手写一个拷贝递归函数
const testObj = {}
function deepClone(newObj, oldObj) {
for (let k in oldObj) {
//判断是否存在数组对象
if (oldObj[k] instanceof Array) {
//给新对象,创建一个新数组
newObj[k] = []
//接收方是新对象当中的新建的空数组,送的一方就是旧数组
deepClone(newObj[k], oldObj[k])
//处理对象类型的引用数据
} else if (oldObj[k] instanceof Object) {
//创建一个新对象
newObj[k] = {}
//接收方是新建的空对象,送的一方就是旧对象
deepClone(newObj[k], oldObj[k])
} else {
console.log(typeof (k)); //k是String类型的。
//这种写法不可取newObj.k,会被认为是像对象newObj中追加一个属性名为K的属性。
//要采取下面这种写法,
newObj[k] = oldObj[k]
}
}
}
deepClone(testObj, obj)
console.log(testObj);

异常处理

在JavaScript中,异常处理是一种控制流结构,用于捕获和处理程序执行过程中发生的错误或异常情况。异常处理主要包括以下几个关键部分:try, catch, 和 finally 块。

异常处理语法

  1. try:

    • try 块包含了可能会抛出异常的代码。
    • 如果 try 块中的代码执行正常,则 catch 块将被跳过。
  2. catch:

    • catch 块用于处理 try 块中抛出的异常。
    • catch 块接收一个参数,通常命名为 errore,这个参数包含了异常信息。
  3. finally:

    • finally 块是可选的,它包含了无论是否发生异常都需要执行的代码。
    • finally 块在 trycatch 块之后执行。

使用 try-catch-finally 处理异常

下面是一个简单的示例,演示如何使用 try-catch-finally 结构来处理可能的异常:

1
2
3
4
5
6
7
8
9
10
try {
// 尝试执行可能会抛出异常的代码
let result = 10 / 0; // 这里会抛出异常
} catch (error) {
// 处理异常
console.error("捕获到了异常:", error.message);
} finally {
// 不管是否有异常,都会执行这里的代码
console.log("执行 finally 块");
}

使用 throw 抛出异常

你还可以使用 throw 语句手动抛出异常:

1
2
3
4
5
6
7
    function fn(x,y) {
if(!x || !y){
throw new Error('没有参数传递进来')
}
return x + y
}
console.log(fn());

自定义异常

在JavaScript中,你还可以创建自定义异常类来处理特定类型的错误。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}

function validateEmail(email) {
if (!email.includes("@")) {
throw new ValidationError("无效的电子邮件地址");
}
console.log("电子邮件验证成功!");
}

try {
validateEmail("example.com");
} catch (error) {
if (error instanceof ValidationError) {
console.error("验证错误:", error.message);
} else {
console.error("未知错误:", error);
}
} finally {
console.log("验证完成!");
}

深度学习this

this指向

this的指向在js中非常重要,下面就更加深层的理解this指向

针对普通函数

总结一句话:普通函数的调用方式决定了this的指向,即谁调用函数,this就指向谁

普通函数没有明确调用者时,this指向window,严格模式下没有调用者this指向undefined

针对箭头函数

事实上,箭头函数并不存在this

  1. 箭头函数会默认帮我们绑定外层this的值,所以在箭头函数中this的值和外层的this是一样的。
  2. 箭头函数中后的this引用的就是最近作用域中的this。
  3. 向外层作用域中,一层一层查找this,直到找到this。

需求:看需要不需要使用this,不需要this使用箭头函数更简洁方便。

改变this指向

当然可以。下面是JavaScript中用于改变 this 指向的一些常用方法及其语法格式:

下面的参数中:thisArg,就是规定this指向谁,如果不需要指向,则可以写null

使用 .call()

!该方法会调用函数

.call() 方法调用一个函数,并传入指定的 this 值和参数列表。

语法:

1
functionName.call(thisArg, arg1, arg2, ...);

示例:

1
2
3
4
5
6
7
8
9
function greet(greeting) {
console.log(greeting + ', ' + this.name);
}

const person = {
name: 'Alice'
};

greet.call(person, 'Hello'); // 输出: Hello, Alice

使用 .apply()

!该方法会调用函数

.apply() 方法与 .call() 类似,但是它接受一个参数数组。

语法:

1
functionName.apply(thisArg, [arg1, arg2, ...]);

示例:

1
2
3
4
5
6
7
8
9
function greet(greeting) {
console.log(greeting + ', ' + this.name);
}

const person = {
name: 'Alice'
};

greet.apply(person, ['Hello']); // 输出: Hello, Alice

使用 .bind()

注意:bind()方法不会调用函数。

.bind() 方法创建一个新的函数,这个新的函数的 this 被绑定到提供的值上。

该方法的返回值是一个新函数。

语法:

1
functionName.bind(thisArg, arg1, arg2, ...);

示例:

1
2
3
4
5
6
7
8
9
10
function greet(greeting) {
console.log(greeting + ', ' + this.name);
}

const person = {
name: 'Alice'
};

const boundGreet = greet.bind(person);
boundGreet('Hello'); // 输出: Hello, Alice

理解图

image-20240826115208398

防抖和节流

防抖

单位时间内,频繁触发事件,但只执行最后一次触发。

简单理解:王者荣耀的回城。

使用场景

  • 输入框的搜索建议
  • 窗口尺寸变化时重新布局
  • 鼠标移动时获取位置

实现方式

  1. 使用lodash提供的防抖来处理
  2. 手写防抖函数

手写防抖函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//防抖的概念:单位时间内,频繁触发事件,但只执行最后一次触发。
let i = 1
function addNum() {
document.querySelector('.box').innerHTML = i++;
console.log('111');
}
//2.手写防抖函数:底层是使用定时器实现的。
function debounce(fn, time) {
//定义一个定时器变量
let timer
return function () {
//每次触发判断有没有定时器,如果有清除
if (timer) clearTimeout(timer)
//没有就开启定时器,存入到定时器变量
timer = setTimeout(function () {
//在定时器里面调用函数
fn()
}, time)
}
}
document.querySelector('.box').addEventListener('mousemove', debounce(addNum, 500))

节流

节流的目的则是确保一个函数在指定的时间间隔内最多只被调用一次。即使该函数被频繁触发,它也只会在上一次调用之后等待指定的时间间隔再次执行。

单位时间内,频繁触发事件,只执行一次

类似王者荣耀的技能,是存在CD的,CD时间内只执行一次。

使用场景

  • 鼠标滚轮滚动
  • 滚动事件
  • 定时任务

手写节流函数

image-20240826125802996

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//防抖的概念:单位时间内,频繁触发事件,只执行一次
let i = 1
function addNum() {
document.querySelector('.box').innerHTML = i++;
console.log('111');
}
//2.手写节流函数:底层是使用定时器实现的。
function throttle(fn, time) {
//定义一个定时器变量
let timer = null
return function () {
//每次触发判断有没有定时器,如果有定时,不做任何操作
if (!timer) {
//没有就开启定时器,存入到定时器变量
timer = setTimeout(function () {
//在定时器里面调用函数
fn()
//清空定时器 - 需要注意的是,在定时器内部清除定时器不能使用clearTimeout,而是要重新给定时器赋值为空才行
timer = null
}, time)
}

}
}
document.querySelector('.box').addEventListener('mousemove', throttle(addNum, 1000))

防抖和节流的总结

image-20240826130750397

类的概念

ES6前

在ES6(ECMAScript 2015)引入正式的类语法之前,JavaScript并没有原生支持类的概念。但是,开发者们已经找到了多种方式来模拟面向对象编程中的类行为。以下是几种常见的方法:

1. 使用构造函数

在ES6之前,最常见的方式是使用构造函数来创建“类”。构造函数是一种特殊的函数,用于初始化新创建的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Car(brand, model, year) {
this.brand = brand;
this.model = model;
this.year = year;
}

// 原型方法
Car.prototype.start = function() {
console.log(`The ${this.brand} ${this.model} is starting.`);
};

// 创建实例
var myCar = new Car('Toyota', 'Corolla', 2020);
myCar.start(); // 输出: The Toyota Corolla is starting.

2. 使用原型链实现继承

在ES6之前,继承是通过原型链来实现的。一个对象可以作为另一个对象的原型,从而实现属性和方法的继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Vehicle(model, year) {
this.model = model;
this.year = year;
}

Vehicle.prototype.start = function() {
console.log(`The ${this.model} is starting.`);
};

function Car(brand, model, year) {
Vehicle.call(this, model, year); // 调用父构造函数
this.brand = brand;
}

// 设置Car的原型为Vehicle的一个实例
Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.constructor = Car; // 修复构造函数指针

// 添加子类特有的方法
Car.prototype.drive = function() {
console.log(`Driving the ${this.brand} ${this.model}.`);
};

// 创建实例
var myCar = new Car('Toyota', 'Corolla', 2020);
myCar.start(); // 输出: The Corolla is starting.
myCar.drive(); // 输出: Driving the Toyota Corolla.

3. 使用寄生组合式继承

寄生组合式继承是一种更高效的继承模式,它结合了原型链继承和借用构造函数的优点,避免了原型链继承和构造函数继承的缺点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function inheritPrototype(subType, superType) {
var prototype = Object.create(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 指定对象
}

function Vehicle(model, year) {
this.model = model;
this.year = year;
}

Vehicle.prototype.start = function() {
console.log(`The ${this.model} is starting.`);
};

function Car(brand, model, year) {
Vehicle.call(this, model, year); // 调用父构造函数
this.brand = brand;
}

inheritPrototype(Car, Vehicle);

// 添加子类特有的方法
Car.prototype.drive = function() {
console.log(`Driving the ${this.brand} ${this.model}.`);
};

// 创建实例
var myCar = new Car('Toyota', 'Corolla', 2020);
myCar.start(); // 输出: The Corolla is starting.
myCar.drive(); // 输出: Driving the Toyota Corolla.

4. 使用模块模式

模块模式是一种设计模式,可以用来封装私有变量和方法,同时提供公共接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var Car = (function() {
// 私有变量和方法
var _privateVariable = 'This is private';

function _privateMethod() {
console.log(_privateVariable);
}

// 公共接口
return {
create: function(brand, model, year) {
return {
brand: brand,
model: model,
year: year,
start: function() {
console.log(`The ${this.brand} ${this.model} is starting.`);
},
privateAccess: function() {
_privateMethod();
}
};
}
};
})();

var myCar = Car.create('Toyota', 'Corolla', 2020);
myCar.start(); // 输出: The Toyota Corolla is starting.
myCar.privateAccess(); // 输出: This is private

总结

虽然ES6之前的JavaScript没有原生的类语法,但通过构造函数、原型链、寄生组合式继承和模块模式等技术,开发者可以有效地模拟面向对象编程中的类和继承。这些技术在现代JavaScript中仍然非常有用,特别是在需要向后兼容旧代码或浏览器时。

ES6后

学习JavaScript (JS) 类是一个重要的步骤,尤其是在面向对象编程中。ES6(ECMAScript 2015)引入了类的概念,这使得JavaScript中的面向对象编程更加直观和易于理解。下面是一些关于JS类的基本概念和用法的学习笔记。

1. 定义类

在JavaScript中定义一个类使用class关键字,后跟类名。类名通常首字母大写,以遵循命名约定。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Car {
// 构造函数
constructor(brand, model, year) {
this.brand = brand;
this.model = model;
this.year = year;
}

// 方法
start() {
console.log(`The ${this.brand} ${this.model} is starting.`);
}
}

2. 创建实例

创建类的实例是通过使用new关键字调用类构造器完成的。

1
2
let myCar = new Car('Toyota', 'Corolla', 2020);
myCar.start(); // 输出: The Toyota Corolla is starting.

3. 静态方法

静态方法是不能在类的实例上调用的方法,而是直接在类上访问的方法。它们通常用于工具函数或工厂方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Car {
constructor(brand, model, year) {
this.brand = brand;
this.model = model;
this.year = year;
}

static getCarInfo(car) {
return `${car.brand} ${car.model}, Year: ${car.year}`;
}
}

let carInfo = Car.getCarInfo(myCar);
console.log(carInfo); // 输出: Toyota Corolla, Year: 2020

4. 继承

JavaScript中的类支持继承,子类可以继承父类的属性和方法。使用extends关键字来指定一个类继承自另一个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ElectricCar extends Car {
constructor(brand, model, year, batterySize) {
super(brand, model, year); // 调用父类的构造函数
this.batterySize = batterySize;
}

charge() {
console.log(`The ${this.brand} ${this.model} is charging.`);
}
}

let myTesla = new ElectricCar('Tesla', 'Model S', 2022, 75);
myTesla.start(); // 输出: The Tesla Model S is starting.
myTesla.charge(); // 输出: The Tesla Model S is charging.

5. Getter 和 Setter

可以在类中定义getter和setter方法来控制对属性的访问和修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Car {
constructor(brand, model, year) {
this._brand = brand; // 私有属性
this.model = model;
this.year = year;
}

get brand() {
return this._brand;
}

set brand(newBrand) {
if (newBrand.length > 0) {
this._brand = newBrand;
} else {
console.log('Brand name cannot be empty.');
}
}
}

let myCar = new Car('Ford', 'Fiesta', 2018);
console.log(myCar.brand); // 输出: Ford
myCar.brand = ''; // 输出: Brand name cannot be empty.
myCar.brand = 'Chevrolet';
console.log(myCar.brand); // 输出: Chevrolet

6. 注意事项

  • 类中的所有方法都是不可枚举的,这意味着它们不会出现在for...in循环中。
  • 类表达式可以是有名的也可以是匿名的,有名类表达式的名称仅在该类的作用域内可见。
  • 类体总是自动运行在严格模式下。

JS插件

制作轮播图

建议使用swiper插件

swiper官网:https://swiperjs.com/get-started(英文)

swiper中文网:https://www.swiper.com.cn/

快速收集表单内容

建议使用form-serialize插件

该插件传递两个参数

  1. 要收集那个表单的数据
    1. 表单中的name属性会被作为属性名来展示在收集的数据中
    2. 建议name的值最好和接口文档一致。
  2. 配置对象{hash:true,empty:true}
    1. hash设置获取数据的结构
      • true:JS对象(推荐使用),一般请求体里提交给服务器。
      • false:获取的是查询字符串。
    2. empty设置是否获取表单中的空值。
      • true:获取表单中的空值。
      • false:不获取表单中的空值。