JS基础语法一网打尽

158 阅读2分钟
JS的重要思想是不报错,由于他是一门弱类型语言,所以很经常发生数据类型转换来保证代码不报错。

引入

编写位置

  • js内部script标签
  • 写在外部的js文件中
  • 指定属性内

基本语法

  • 多行注释

    • 注释中的内容会被解释器忽略
    • 可以通过注释来对代码进行解释说明
    • 也可以通过注释来注释掉不想执行的代码
  • 单行注释

  • JS严格区分大小写

  • 在JS中多个空格和换行会被忽略,可以利用这个特点来对代码进行格式化

  • JS中每条语句都应该以分号结尾, JS中具有自动添加分号的机制,所以如果不写分号解释器会自动添加

变量名

声明变量 -> let 变量名 /const 变量名[只能赋值一次]/var 变量名【没有块作用域,在块外仍可以访问】

变量赋值 -> a = xx

声明和赋值同时进行 -> let 变量 = 值

使用优先级:const>let>var

命名规范

在JS中,所有可以由我们自主命名的内容,都可以认为是一个标识符,像变量名 函数名 类名...

使用标识符需要遵循如下的命名规范:

  • 标识符只能含有字母、数字、下划线、$,且不能以数字开头

  • 标识符不能是JS中的关键字和保留字,也不建议使用内置的函数或类名作为变量名

  • 命名规范:

    • 通常会使用驼峰命名法

      • 首字母小写,每个单词开头大写
    • 类名会使用大驼峰命名法

      • 首字母大写,每个单词开头大写
    • 常量的字母会全部大写

变量内存

变量中并不存储任何值,而是存储值的内存地址!

数据类型

基本数据类型

1. Number:数值类型

- 在JS中所有的整数和浮点数都是Number类型
-  JS中的数值并不是无限大的,当数值超过一定范围后会显示近似值
-  Infinity 是一个特殊的数值表示无穷
-  所以在JS中进行一些精度比较高的运算时要十分注意
-  NaN 也是一个特殊的数值,表示非法的数值
```javascript
a = 9999999999999991111111111111111111
a = 99999 ** 99999 //infinity
a = Infinity
a = 0.1 + 0.2  //0.30000000000000004
a = 1 - 'a' //NaN
a = NaN //(Not a Namber)
```

2. String: 字符串

  • 在JS中使用单引号或双引号来表示字符串
  • 转义字符 \" --> " \' --> ' \\ --> \ \t --> 制表符 \n --> 换行 - 模板字符串 - 使用反单引号` 来表示模板字符串 - 模板字符串中可以嵌入变量 - 使用typeof检查一个字符串时会返回 "string"
   let a = "Hello"
       a = "你好"
       a = '这是一个"字\\\\符串'
       a = "呵呵\t哈哈哈"
       a = "呵呵\n哈哈哈"
       console.log(typeof c) //string

3. Boolean:布尔类型

  • 空值用来表示空对象
  • 空值只有一个 null
  • 使用typeof检查一个空值时会返回"object"
  • 使用typeof无法检查空值
let bool = true; // 真
     bool = false; // 假
     let num = 1;
     let a = null;
     //    console.log(typeof bool)
     //    console.log(typeof a)
     //    let b
     console.log(typeof b);

4. BigInt:大整数

  • 大整数用来表示一些比较大的整数
  • 大整数使用n结尾,它可以表示的数字范围是无限大
a = 99999999999999999999999999999999999999999999999999n
其他进制的数字:二进制 0b
              八进制 0o
              十六进制 0x
a = ob1010
a = 0o10
a = 0Xff

5. Null:空值

  • 空值用来表示空对象
  • 空值只有一个 null
  • 使用typeof检查一个空值时会返回"object"
  • 使用typeof无法检查空值

6. Undefined:未定义

  • 当声明一个变量而没有赋值时,它的值就是Undefined
  • Undefined类型的值只有一个就是 undefined
  • 使用typeof检查一个Undefined类型的值时,会返回 "undefined"

7. Symbol:符号

  • 用来创建一个唯一的标识
  • 使用typeof检查符号时会返回 "symbol"
  • 作用(不希望被用户访问到)
let c = Symbol(); // 调用Symbol()创建了一个符号 console.log(typeof c);

引用数据类型

Object 对象

对象是JS中的一种复合数据类型,它相当于一个容器,在对象中可以存储各种不同类型数据

类型转换

强制类型转换

强制类型转换——字符串

  1. 调用toString()方法将其他类型转换为字符串
  • 调用xxx的yyy方法 --> xxx.yyy()
  • 由于null和undefined中没有toString() 所以对这两个东西调用toString()时会报错
  1. 调用String()函数将其他类型转换为字符串
  • 调用xxx函数 --> xxx()
  • 原理: 对于拥有toString()方法的值调用String()函数时, 实际上就是在调用toString()方法 对于null,则直接转换为"null" 对于undefined,直接转换为"undefined"

强制类型转换——数值

  1. 使用Number()函数来将其他类型转换为数值 转换的情况:
  • 如果字符串是一个合法的数字,则会自动转换为对应的数字 - 如果字符串不是合法数字,则转换为NaN
  • 如果字符串是空串或纯空格的字符串,则转换为0
  • 布尔值: - true转换为1,false转换为0 - null 转换为 0 - undefined 转换为 NaN
  1. 专门用来将字符串转换为数值的两个方法
  • parseInt() —— 将一个字符串转换为一个整数 - 解析时,会自左向右读取一个字符串,直到读取到字符串中所有的有效的整数 - 也可以使用parseInt()来对一个数字进行取整
  • parseFloat() —— 将一个字符串转换为浮点数 - 解析时,会自左向右读取一个字符串,直到读取到字符串中所有的有效的小数
let a = '123' // 123
        a = 'abc' // NaN
        a = '3.1415926' // 3.1415926
        a = '11px' // NaN
        a = ''  // 0
        a = '    ' // 0

        a = true // 1
        a = false // 0

        a = null // 0
        a = undefined // NaN

        // console.log(typeof a, a)

        a = Number(a)
        // console.log(typeof a, a)
        
        let b = '123px'
        b = 'a123'
        b = '123.45'
        // b = 456.123
        console.log(typeof b, b)

        b = parseInt(b)
        console.log(typeof b, b)

强制类型转换——布尔值

  • 转换的情况: 数字

  • 0 和 NaN 转换为false - 其余是true

  • 字符串: - 空串 转换为 false - 其余是true

  • null和undefined 都转换为 false

  • 对象:对象会转换为true

  • 所有表示空性的没有的错误的值都会转换为

  • false: 0、NaN、空串、null、undefined、false

let a = 1 // true
        a = -1 // true
        a = 0 // false
        a = NaN // false
        a = Infinity // true

        a = 'abc' // true
        a = 'true' // true
        a = 'false' // true
        a = '' // false
        a = " " // true
        a = null
        a = undefined 

        console.log(typeof a, a)

        a = Boolean(a)

        console.log(typeof a, a)

隐式类型转换——自动类型转换

JS是一门弱类型语言,当进行运算时会通过自动的类型转换来完成运算
总结:
1.算数运算符计算非字符串的加法->数值
2.某某+""->字符串
3.一元的+-操作非数值类型->数值
4.!!->布尔值
        a = 10 - '5' // 10 - 5
        a = 10 + true // 10 + 1
        a = 5 + null // 5 + 0
        a = 6 - undefined // 6 - NaN
  • 当任意一个值和字符串做加法运算时,它会先将其他值转换为字符串, 然后再做拼串的操作 可以利用这一特点来完成类型转换 可以通过为任意类型 + 一个空串的形式来将其转换为字符串

    a = 'hello' + 'world'
    a = '1' + 2 // "1" + "2"
    a = true
    
    a = a + ''
    
        console.log(typeof a, a)
    

运算符

算术运算符

  • 运算符可以用来对一个或多个操作数(值)进行运算
  • 算术运算符:
                  + 加法运算符
                    - 减法运算符
                    * 乘法运算符
                    / 除法运算符
                    ** 幂运算
                    % 模运算,两个数相除取余数
  • 注意: - 算术运算时,除了字符串的加法, 其他运算的操作数是非数值时,都会转换为数值然后再运算
let a = 1 + 1
    a = 10 - 5
    a = 2 * 4
    a = 10 / 5
    a = 10 / 3
    a = 10 / 0 // Infinity
    a = 10 ** 4
    a = 9 ** .5 // 开方
    a = 10 % 2

赋值运算符

=
- 将符号右侧的值赋值给左侧的变量
??=
       - 空赋值
       - 只有当变量的值为nullundefined时才会对变量进行赋值
+=
       - a += n 等价于 a = a + n
-=
       - a -= n 等价于 a = a - n
*=
      - a *= n 等价于 a = a * n
/=
      - a /= n 等价于 a = a / n
%=
      - a %= n 等价于 a = a % n
**=
     - a **= n 等价于 a = a **

一元运算符

  • 一元的±
    • 正号 - 不会改变数值的符号
    • 负号 - 可以对数值进行符号位取反

当我们对非数值类型进行正负运算时,会先将其转换为数值然后再运算

自增和自减运算符

  1. ++ 自增运算符
  • ++ 使用后会使得原来的变量立刻增加1
  • 自增分为前自增(++a)和后自增(a++)
  • 无论是++a还是a++都会使原变量立刻增加1
  • 不同的是++a和a++所返回的值不同 a++ 是自增前的值 旧值 ++a 是自增后的值 新值
  1. -- 自减运算符
  • 使用后会使得原来的变量立刻减小1
  • 自减分为前自减(--a)和后自减(a--)
  • 无论是--a还是a--都会使原变量立刻减少1
    • 不同的是--a和a--的值不同 --a 是新值 a-- 是旧值

逻辑运算符

1.! 逻辑非

  • ! 可以用来对一个值进行非运算
  • 它可以对一个布尔值进行取反操作
    • true --> false
    • false --> true
  • 如果对一个非布尔值进行取反,它会先将其转换为布尔值然后再取反 可以利用这个特点将其他类型转换为布尔值
  1. 类型转换
  • 转换为字符串
    • 显式转换 String()
  • 隐式转换
    • +"" 转换为数值 显式转换 Number() 隐式转换 + 转换为布尔值 显式转换 Boolean() 隐式转换 !!
  1. && 逻辑与
  • 可以对两个值进行与运算
  • 当&&左右都为true时,则返回true,否则返回false
  • 与运算是短路的与,如果第一个值为false,则不看第二个值
  • 与运算是找false的,如果找到false则直接返回,没有false才会返回true
  • 对于非布尔值进行与运算,它会转换为布尔值然后运算 但是最终会返回原值
  • 如果第一个值为false,则直接返回第一个值 如果第一个值为true,则返回第二个值
  1. || 逻辑或
  • 可以对两个值进行或运算
  • 当||左右有true时,则返回true,否则返回false
  • 或运算也是短路的或,如果第一个值为true,则不看第二个值
  • 或运算是找true,如果找到true则直接返回,没有true才会返回false
  • 对于非布尔值或运算,它会转换为布尔值然后运算 但是最终会返回原值
  • 如果第一个值为true,则返回第一个 如果第一个值为false,则返回第二个
let result = true && true // true
       result = true && false // false
       result = false && true // false
       result = false && false // false

       // true && alert(123) // 第一个值为true,alert会执行
       false && alert(123) // 第一个值为false,alert不会执行

       //       true && true  -> true
       result = 1 && 2 // 2 
       //       true && false -> false
       result = 1 && 0 // 0
       //       false && false -> false 
       result = 0 && NaN // 0


       result = true || false // true
       result = false || true // true
       result = true || true // true
       result = false || false // false

       // false || alert(123) // 第一个值为false,alert会执行
       true || alert(123) // 第一个值为true,alert不会执行

       result = 1 || 2 // 1
       result = "hello" || NaN // "hello"
       result = NaN || 1 // 1
       result = NaN || null // null

关系运算符

  • 关系运算符用来检查两个值之间的关系是否成立
  • 成立返回true,不成立返回false >
  • 用来检查左值是否大于右值 >=
  • 用来检查左值是否大于或等于右值 <
  • 用来检查左值是否小于右值 <=
  • 用来检查左值是否小于或等于右值

相等运算符

  1. ==
  • 相等运算符,用来比较两个值是否相等
  • 使用相等运算符比较两个不同类型的值时, 它会将其转换为相同的类型(通常转换为数值)然后再比较 类型转换后值相同也会返回true
  • null和undefined进行相等比较时会返回true
  • NaN不和任何值相等,包括它自身
  1. ===
  • 全等运算符,用来比较两个值是否全等
  • 它不会进行自动的类型转换,如果两个值的类型不同直接返回false
  • null和undefined进行全等比较时会返回false
  1. !=
  • 不等,用来检查两个值是否不相等
  • 会自动的进行类型转换
  1. !==
  • 不全等,比较两个值是否不全等
  • 不和自动的类型转换

条件运算符

条件表达式 ? 表达式1 : 表达式2

  • 执行顺序: 条件运算符在执行时,会先对条件表达式进行求值判断,
    • 如果结果为true,则执行表达式1
    • 如果结果为false,则执行表达式2
        let a = 100
        let b = 200
        // a > b ? alert('a大!') : alert("b大!")
        let max = a > b ? a : b

运算符优先级

  • 可以通过优先级的表格来查询运算符的优先级 developer.mozilla.org/en-US/docs/…

  • 在表格中位置越靠上的优先级越高,优先级越高越先执行,优先级一样自左向右执行 优先级我们不需要记忆,甚至表格都不需要看 因为()拥有最高的优先级,使用运算符时,如果遇到拿不准的,可以直接通过()来改变优先级即可

琐碎的函数

1.prompt()方法用于显示可提示用户进行输入的对话框。方法返回用户输入的字符串。
2.alert()弹出警告框
3.isNaN 判断是否是非法数字
4.console.log()控制台日志 console.time()开始计时器 console.timeEnd()结束计时器

流程控制

循环语句

  1. if-else语句
        if(age >= 18 && age < 30){
            alert('你已经成年了!')
        }else if(age >= 30 && age < 60){
            alert('你已经步入中年了!')
        }else if(age >= 60){
            alert('你已经退休了!')
        }
  1. while
练习:假设银行存款的年利率为5%,问1000块存多少年可以变成5000let year = 0
        // 编写循环,计算存款的年数
        while(money < 5000){
            money *= 1.05 // 循环没执行一次,就相当于钱存了一年
            year++
        }       
        console.log(`需要存${year}年,最终的钱数为${money}元!`)
  1. do-while
let i = 10
        do{
            console.log(i)
            i++
        }while(i < 5)
  1. for循环
for (let i = 0; i < 5; i++) {
                for (let j = 0; j < 5 - i; j++) {
                    document.write("*&nbsp;&nbsp;")
                }
  1. Switch
switch (num) {
                case 1:
                    alert("壹")
                    break // break可以用来结束switch语句
                case 2:
                    alert("贰")
                    break
                case 3:
                    alert("叁")
                    break
                default:
                    alert("我是default")
                    break
            }

break 和 continue

            - break                    
                - break会终止离他最近的循环

            - continue
                - continue用来跳过当次循环

对象

含义:对象是JS中的一种复合数据类型, 它相当于一个容器,在对象中可以存储各种不同类型数据

对象的基础操作

创建对象的方式

1. let obj = Object()

2.自变量创建
const obj = {
    name:'xxx'
}

3.ES6 class 
class Person {
  constructor(name, age){
  this.name = name
  this.age = age
  }
}

对象的属性

  • 通常属性名就是一个字符串,所以属性名可以是任何值,没有什么特殊要求 但是如果你的属性名太特殊了,不能直接使用,需要使用[]来设置 虽然如此,但是我们还是强烈建议属性名也按照标识符的规范命名

  • 也可以使用符号(symbol)作为属性名,来添加属性 获取这种属性时,也必须使用symbol 使用symbol添加的属性,通常是那些不希望被外界访问的属性

  • 使用[]去操作属性时,可以使用变量

    • 属性值 - 对象的属性值可以是任意的数据类型,也可以是一个对象
    • 使用typeof检查一个对象时,会返回object
obj["1231312@#@!#!#!"] = "呵呵"// 不建议
        let mySymbol = Symbol()
        let newSymbol = Symbol()
        // 使用symbol作为属性名
        obj[mySymbol] = "通过symbol添加的属性"
        // console.log(obj[mySymbol])
        
        let str = "address"

        obj[str] = "花果山" // 等价于 obj["address"] = "花果山"

in 运算符

  • 用来检查对象中是否含有某个属性 - 语法 属性名 in obj - 如果有返回true,没有返回false

枚举属性

  1. for-in语句
for(let 属性变量名 in 对象){
     语句...
     console.log(属性变量名, obj[属性变量名] //属性 属性值
}
  1. for-in的循环体会执行多次,有几个属性就会执行几次, 每次执行时,都会将一个属性名赋值给我们所定义的变量

方法

含义:当一个对象的属性指向一个函数, 那么我们就称这个函数是该对象的方法 调用函数就称为调用对象的方法

let obj = {
        name : "孙悟空",
        age : 18,
        // 函数也可以成为一个对象的属性
        sayHello :function(){
            alert("hello")
        }
      }
      obj.sayHello()

函数

函数(Function)

- 函数也是一个对象
- 它具有其他对象所有的功能
- 函数中可以存储代码,且可以在需要时调用这些代码
- 使用typeof检查函数对象时会返回function

函数的创建

函数的定义方式:
1.函数声明
function 函数名(){
            语句...
 }

 2.函数表达式
 const 变量 = function(){
       语句...
}

 3.箭头函数
      () => {
       语句...
         }

参数

  • 如果实参和形参数量相同,则对应的实参赋值给对应的形参
  • 如果实参多余形参,则多余的实参不会使用
  • 如果形参多余实参,则多余的形参为undefined
  • 参数的类型
    • jS中不会检查参数的类型,可以传递任何类型的值作为参数
const fn = (a=10, b=20, c=30) => {
      console.log("a =", a);
      console.log("b =", b);
      console.log("c =", c);
}

对象作为参数时

  • 修改对象时,如果有其他变量指向该对象则所有指向该对象的变量都会受到影响
  • 函数每次调用,都会重新创建默认值
function fn2(a = {name:"沙和尚"}){
            console.log("a =", a)
            a.name = "唐僧"
            console.log("a =", a)
        }

        fn2() // 沙和尚 唐僧
        fn2() // 沙和尚 唐僧 or 唐僧 唐僧

函数作为参数时

  • 在JS中,函数也是一个对象(一等函数) 别的对象能做的事情,函数也可以
function fn2(){
  console.log("我是fn2")
}
function fn (a){
  console.log('fn',a);
}

fn(fn2) //fn [Function: fn2]

箭头函数

注意点:

  • 只有一个语句,箭头函数的大括号可以省略不写
  • 只有一个参数,箭头函数的参数括号可省略
  • 没有自己的this和arguments,它的this有外层作用域决定,不能作为构造函数调用
  • 无法通过call、apply、bind指定函数的this
例子:
      无参箭头函数:() => 返回值
      一个参数的:a => 返回值
      多个参数的:(a, b) => 返回值

      只有一个语句的函数:() => 返回值
      只返回一个对象的函数:() => ({...})
      有多行语句的函数:() => {
                    ....    
                    return 返回值
                }

函数返回值

  • 任何值都可以作为返回值使用(包括对象和函数之类)
  • 如果return后不跟任何值,则相当于返回undefined
  • 如果不写return,那么函数的返回值依然是undefined
  • return一执行函数立即结束
return {name:"孙悟空"}
return ()=>alert(123)

箭头函数的返回值

  • 箭头函数的返回值可以直接写在箭头后
  • 如果直接在箭头后设置对象字面量为返回值时,对象字面量必须使用()括起来
const sum = (a,b) => a + b
const fn = () => ({mame : 'XXX'})
const fn2 = (a,b) => {
        alert(123)
        retuen a + b
}

作用域

  • 全局作用域
  • 局部作用域

函数作用域

  • 函数作用域也是一种局部作用域
  • 函数作用域在函数调用时产生,调用结束后销毁
  • 函数每次调用都会产生一个全新的函数作用域
  • 在函数中定义的变量是局部变量,只能在函数内部访问,外部无法访问

作用域链

  • 当我们使用一个变量时, JS解释器会优先在当前作用域中寻找变量, 如果找到了则直接使用 如果没找到,则去上一层作用域中寻找,找到了则使用 如果没找到,则继续去上一层寻找,以此类推 如果一直到全局作用域都没找到,则报错 xxx is not defined

window对象

  • 在浏览器中,浏览器为我们提供了一个window对象,可以直接访问
  • window对象代表的是浏览器窗口,通过该对象可以对浏览器窗口进行各种操作 除此之外window对象还负责存储JS中的内置对象和浏览器的宿主对象
  • window对象的属性可以通过window对象访问,也可以直接访问
  • 函数就可以认为是window对象的方法var 用来声明变量,作用和let相同,但是var不具有块作用域
    • 在全局中使用var声明的变量,都会作为window对象的属性保存
    • 使用function声明的函数,都会作为window的方法保存
    • 使用let声明的变量不会存储在window对象中,而存在一个秘密的小地方(无法访问)
    • var虽然没有块作用域,但有函数作用域

提升

变量的提升

使用var声明的变量,它会在所有代码执行前被声明 所以我们可以在变量声明前就访问变量

函数提升

使用函数声明创建的函数,会在其他代码执行前被创建 所以我们可以在函数声明前调用函数

console.log(a)  // 2
            var a = 1
            
            console.log(a) // 1
            
            function a() {
                alert(2)
            }

            console.log(a) // 1

            var a = 3

            console.log(a) // 3

            var a = function () {
                alert(4)
            }

            console.log(a) // 4

            var a

            console.log(a) // 4

debug

在网页开发者工具源代码中debug,打下断点后添加监视点

立即执行函数

  • 立即是一个匿名的函数,并它只会调用一次
  • 可以利用IIFE来创建一个一次性的函数作用域,避免变量冲突的问题
;(function(){
  console.log('fn');
}()) //等价于})()

this

this会指向一个对象

  • this所指向的对象会根据函数调用方式的不同而不同
  • 1.以函数形式调用时,this指向的是window
  • 2.以方法的形式调用时,this指向的是调用方法的对象
  • 3.箭头函数没有自己的this,它的this由外层作用域决定,箭头函数的this和它的调用方式无关
  • 通过this可以在方法中引用调用方法的对象
function Person(color) {
  console.log(this)
  this.color = color;
  this.getColor = function () {
    console.log(this)
    return this.color;
  };
  this.setColor = function (color) {
    console.log(this)
    this.color = color;
  };
}

Person("red"); //this是谁?
//window
var p = new Person("yello"); //this是谁?
//p
p.getColor(); //this是谁?
//p
var obj = {};
p.setColor.call(obj, "black"); //this是谁?
//obj
var test = p.setColor;
test(); //this是谁?
//window
function fun1() {
  function fun2() {
    console.log(this);
  }

  fun2(); //this是谁?
  //window
}
fun1();

严格模式

"use strict" // 全局的严格模式
function fn(){
    "use strict" // 函数的严格的模式
}

面向对象

  1. 类是对象模板,可以将对象中的属性和方法直接定义在类中定义后,就可以直接通过类来创建对象
  2. 通过同一个类创建的对象,我们称为同类对象 可以使用instanceof来检查一个对象是否是由某个类创建 如果某个对象是由某个类所创建,则我们称该对象是这个类的实例
class Person{}
class Dog{}
const p1 = new Person()  // 调用构造函数创建对象
const d1 = new Dog()
console.log(p1 instanceof Person) // true

属性

  • 类的代码块,默认就是严格模式, 类的代码块是用来设置对象的属性的
class Person{
      name = "孙悟空" // Person的实例属性name p1.name
      age = 18       // 实例属性只能通过实例访问 p1.age

    static test = "test静态属性" // 使用static声明的属性,是静态属性(类属性) Person.test
           static hh = "静态属性"   // 静态属性只能通过类去访问 Person.hh
           }
           const p1 = new Person()
           console.log(p1.name)
           console.log(Person.hh)

方法

class Person{
            name = "孙悟空"
            sayHello(){
                console.log('大家好,我是' + this.name)
            } // 添加方法(实例方法) 实例方法中this就是当前实例
            static test(){
                console.log("我是静态方法", this)
            } // 静态方法(类方法) 通过类来调用 静态方法中this指向的是当前类
        }
    const p1 = new Person()
    Person.test() //Person
    p1.sayHello() //p1

构造函数

class Person
   // 构造函数会在我们调用类创建对象时执行
   constructor(name, age, gender){
     // console.log("构造函数执行了~", name, age, gender)
    // 可以在构造函数中,为实例属性进行赋值
    // 在构造函数中,this表示当前所创建的对象
                this.name = name
                this.age = age
                this.gender = gender
            }
        }

面向对象的特点

封装 —— 安全性
继承 —— 扩展性
多态 —— 灵活性

封装

  • 私有化数据 - 将需要保护的数据设置为私有,只能在类内部使用
  • 提供setter和getter方法来开放对数据的操作
- 实现封装的方式:
1.属性私有化 加#
 2.通过getter和setter方法来操作属性
    get 属性名(){
        return this.#属性
       }

     set 属性名(参数){
         this.#属性 = 参数
        }

多态

  • 在JS中不会检查参数的类型,所以这就意味着任何数据都可以作为参数传递
  • 如果一个东西走路像鸭子,叫起来像鸭子,那么它就是鸭子
  • 多态为我们提供了灵活性

继承

  • 可以通过extends关键来完成继承
  • 当一个类继承另一个类时,就相当于将另一个类中的代码复制到了当前类中(简单理解)
  • 通过继承可以减少重复的代码,并且可以在不修改一个类的前提对其进行扩展
  • 通过继承可以在不修改一个类的情况下对其进行扩展
  • OCP 开闭原则: 程序应该对修改关闭,对扩展开放
class Animal{
           constructor(name){
               this.name = name
           }
           sayHello(){
               console.log("动物在叫~")
           }
       }

       class Dog extends Animal{           
       }
       const dog = new Dog("旺财")

对象的解构

对象中存储属性的区域实际有两个:

  • 对象自身

    • 直接通过对象所添加的属性,位于对象自身中
    • 在类中通过 x = y 的形式添加的属性,位于对象自身中
  • 原型对象(prototype)

    • 对象中还有一些内容,会存储到其他的对象里(原型对象)

    • 在对象中会有一个属性用来存储原型对象,这个属性叫做proto(隐式原型)

    • 原型对象也负责为对象存储属性,当我们访问对象中的属性时,会优先访问对象自身的属性,对象自身不包含该属性时,才会去原型对象中寻找

    • 会添加到原型对象中的情况:

      • 在类中通过xxx(){}方式添加的方法,位于原型中
      • 主动向原型中添加的属性或方法

原型对象

访问一个对象的原型对象:

  • 对象.proto
  • Object.getPrototypeOf(对象)
  • 类.prototype

原型对象中的数据

  1. 对象中的数据(属性、方法等)
  2. constructor (对象的构造函数)【也就是类本身】

原型链

  • 原型对象也有原型,这样就构成了一条原型链,根据对象的复杂程度不同,原型链的长度也不同 p对象的原型链:p对象 --> 原型 --> 原型 --> null obj对象的原型链:obj对象 --> 原型 --> null
  • 读取对象属性时,会优先对象自身属性, 如果对象中有,则使用,没有则去对象的原型中寻找 如果原型中有,则使用,没有则去原型的原型中寻找 直到找到Object对象的原型(Object的原型没有原型(为null)) 如果依然没有找到,则返回undefined
    • 作用域链,是找变量的链,找不到会报错 - 原型链,是找属性的链,找不到会返回undefined
  • 原型链的作用
    • 原型就相当于是一个公共的区域,可以被所有该类实例访问, 可以将该类实例中,所有的公共属性(方法)统一存储到原型中 这样我们只需要创建一个属性,即可被所有实例访问
    • JS中继承就是通过原型来实现的, 当继承时,子类的原型就是一个父类的实例
尝试:
函数的原型链是什么样子的?
Object的原型链是什么样子的?
class Animal{
            }
 class Cat extends Animal{
            }
 class TomCat extends Cat{
            }
// TomCat --> cat --> Animal实例 --> object --> Object原型 --> null

// cat --> Animal实例 --> object --> Object原型 --> null
// p对象 --> object --> Object原型 --> null

const cat = new Cat()

console.log(cat.__proto__.__proto__.__proto__.__proto__)

instanceof 和 hasOwn

  • instanceof 用来检查一个对象是否是一个类的实例

    • instanceof检查的是对象的原型链上是否有该类实例,只要原型链上有该类实例,就会返回true
  • in

    • 使用in运算符检查属性时,无论属性在对象自身还是在原型中,都会返回true
  • 对象.hasOwnProperty(属性名) (不推荐使用)

    • 用来检查一个对象的自身是否含有某个属性
  • Object.hasOwn(对象, 属性名)

    • 用来检查一个对象的自身是否含有某个属性

旧类

早期JS中,直接通过函数来定义类

  • 一个函数如果直接调用 xxx() 那么这个函数就是一个普通函数
  • 一个函数如果通过new调用 new xxx() 那么这个函数就是一个构造函数
  • 了解旧类的写法与原理
var Person = (function () {
    function Person(name, age) {
        // 在构造函数中,this表示新建的对象
        this.name = name
        this.age = age

        // this.sayHello = function(){
        //     console.log(this.name)
        // }
    }

    // 向原型中添加属性(方法)
    Person.prototype.sayHello = function () {
        console.log(this.name)
    }

    // 静态属性
    Person.staticProperty = "xxx"
    // 静态方法
    Person.staticMethod = function () {}

    return Person
})()

const p = new Person("孙悟空", 18)

旧类继承

var Animal = (function(){
                function Animal(){

                }
                return Animal
            })()

            var Cat = (function(){
                function Cat(){

                }

                // 继承Animal
                Cat.prototype = new Animal()

                return Cat
            })()
            var cat = new Cat()
            console.log(cat)

new运算符

new运算符是创建对象时要使用的运算符 使用new时,到底发生了哪些事情: developer.mozilla.org/en-US/docs/…

  • 当使用new去调用一个函数时,这个函数将会作为构造函数调用

  • 使用new调用函数时,将会发生这些事:

    1. 创建一个普通的JS对象(Object对象 {}), 为了方便,称其为新对象
    2. 新对象的隐式原型=构造函数的显示原型
    3. 使用实参来执行构造函数,并且将新对象设置为函数中的this
    4. 如果构造函数返回的是一个非原始值,则该值会作为new运算的返回值返回(千万不要这么做) 如果构造函数的返回值是一个原始值或者没有指定返回值,则新的对象将会作为返回值返回 通常不会为构造函数指定返回值

总结

对象的分类:

  • 内建对象

    • 由ES标准所定义的对象
    • 比如 Object Function String Number ....
  • 宿主对象

    • 由浏览器提供的对象:BOM、DOM(window\document)
  • 自定义对象

    • 由开发人员自己创建的对象

数组

数组基础

数组简介

  • 数组也是一种复合数据类型,在数组可以存储多个不同类型的数据
  • 创建数组:通过Array()来创建数组,也可以通过[]来创建数组
  • 如果读取了一个不存在的元素,不会报错而是返回undefined
  • length是可以修改的,也就是说数组的大小可以任意修改
let arr = [1, "hello", true, null, { name: "孙悟空" }, () => {}]

遍历数组

  1. for循环遍历
class Person {
                constructor(name, age) {
                    this.name = name
                    this.age = age
                }
            }
            const personArr = [
                new Person("孙悟空", 18),
                new Person("沙和尚", 38),
                new Person("红孩儿", 8),
            ]
            for(let i=0; i<personArr.length; i++){
                if(personArr[i].age < 18){
                    console.log(personArr[i])
                }
            }
  1. for of遍历对象
for-of语句可以用来遍历可迭代对象
语法:
    for(变量 of 可迭代的对象){
                    语句...
     }
const arr = ["孙悟空", "猪八戒", "沙和尚", "唐僧"]
 for (const value of arr) {
      console.log(value);
}
类似java的foreach语句

数组的方法

  • Array.isArray(对象)//用来检查一个对象是否是数组
  • at() //可以根据索引获取数组中的指定元素
    • 可以接收负索引作为参数 (最后一个元素为-1)
  • concat()//用来连接两个或多个数组
    • 非破坏性方法,不会影响原数组,而是返回一个新的数组
const arr = ["孙悟空", "猪八戒", "沙和尚", "唐僧"]
console.log(arr.at(-2))
const arr = ["孙悟空", "猪八戒", "沙和尚", "唐僧"]
let result = arr.concat(arr2, ["牛魔王","铁扇公主"])
console.log(result) //[ '孙悟空',   '猪八戒', '沙和尚',   '唐僧','白骨精',   '蜘蛛精','玉兔精',   '牛魔王', '铁扇公主']
  • indexOf()//获取元素在数组中第一次出现的索引
    • 参数:
      1. 要查询的元素
      2. 查询的起始位置
  • lastIndexOf()//获取元素在数组中最后一次出现的位置
    • 返回值:找到了则返回元素的索引,没有找到返回-1
  • join()
    • 将一个数组中的元素连接为一个字符串,并返回
    • ["孙悟空", "猪八戒", "沙和尚", "唐僧", "沙和尚"]-> "孙悟空,猪八戒,沙和尚,唐僧,沙和尚"
    • 参数:指定一个字符串作为连接符
  • slice()//用来截取数组(非破坏性方法)
    • 参数:
      1. 截取的起始位置(包括该位置)
      2. 截取的结束位置(不包括该位置)
        • 第二个参数可以省略不写,如果省略则会一直截取到最后
        • 索引可以是负值
      • 如果将两个参数全都省略,则可以对数组进行浅拷贝(浅复制)
let arr = ["孙悟空", "猪八戒", "沙和尚", "唐僧", "沙和尚"]
let result = arr.indexOf("沙和尚", 3) //4
            result = arr.lastIndexOf("沙和尚", 3) //2

            result = arr.join() //孙悟空,猪八戒,沙和尚,唐僧,沙和尚
            result = arr.join("@-@") //孙悟空@-@猪八戒@-@沙和尚@-@唐僧@-@沙和尚

            arr = ["孙悟空", "猪八戒", "沙和尚", "唐僧"]
            result = arr.slice(0, 2) //["孙悟空", "猪八戒"]
            result = arr.slice(1, -1)//-1 = 唐僧 ["沙和尚", "唐僧"]
            result = arr.slice() //浅复制
  • 以下六个是破坏性方法
  • push()//向数组的末尾添加一个或多个元素,并返回新的长度
  • pop()//删除并返回数组的最后一个元素
  • unshift()//向数组的开头添加一个或多个元素,并返回新的长度
  • shift()//删除并返回数组的第一个元素
  • splice()
    • 可以删除、插入、替换数组中的元素
    • 参数:
      1. 删除的起始位置
      2. 删除的数量
      3. 要插入的元素
    • 返回值:被删除的元素
  • reverse()反转数组
let arr = ["孙悟空", "猪八戒", "沙和尚"]
let result = arr.push("唐僧", "白骨精") //["孙悟空", "猪八戒", "沙和尚","唐僧", "白骨精"]
let result1 = arr.pop() //["孙悟空", "猪八戒"]
    arr.unshift("牛魔王") //["牛魔王","孙悟空", "猪八戒", "沙和尚"]
    arr.shift()  //["猪八戒", "沙和尚"]
    arr.splice(1, 2) //["孙悟空"]
    arr.splice(1, 0,"红孩儿") //["孙悟空","红孩儿", "猪八戒", "沙和尚"]
    arr.reverser()  //[ "沙和尚","猪八戒","孙悟空"]   
  • sort(

    • sort用来对数组进行排序(会对改变原数组)
    • sort默认会将数组升序排列 注意:sort默认会把数组的元素转换成字符串再按照Unicode编码进行排序,所以如果直接通过sort对数字进行排序可能会得到一个不正确的结果
    • 参数:
      • 可以传递一个回调函数作为参数,通过回调函数来指定排序规则 (a, b) => a - b 升序排列 (a, b) => b - a 降序排列
  • forEach()

    • 用来遍历数组
    • 它需要一个回调函数作为参数,这个回调函数会被调用多次 数组中有几个元素,回调函数就会调用几次 每次调用,都会将数组中的数据作为参数传递
    • 回调函数中有三个参数:
      • element 当前的元素
      • index 当前元素的索引
      • array 被遍历的数组
  • filter()

    • 将数组中符合条件的元素保存到一个新数组中返回
    • 需要一个回调函数作为参数,会为每一个元素去调用回调函数,并根据返回值来决定是否将元素添加到新数组中
    • 非破坏性方法,不会影响原数组
  • map()

    • 根据当前数组生成一个新数组
    • 需要一个回调函数作为参数,回调函数的返回值会成为新数组中的元素
    • 非破坏性方法不会影响原数组
  • reduce()

    • 可以用来将一个数组中的所有元素整合为一个值
    • 参数:
      1. 回调函数,通过回调函数来指定合并的规则
      2. 可选参数,初始值
  • find(回调函数)

    • 返回符合条件的一个数组元素
const arr3=[2,6,3,5,2,1,2,2,9,10];
arr3.sort((a,b)=>a-b);  
console.log(arr3)//[1, 2, 2, 2, 2, 3, 5, 6, 9, 10]  
arr3.forEach((element,index)=>console.log(index,element));  
let result=arr3.filter(element=>element>3);  
console.log(result);//[5, 6, 9, 10]  
result=arr3.map(element=>element*3);  
console.log(result);// [3, 6, 6, 6, 6, 9, 15, 18, 27, 30]  
result=arr3.reduce((a,b)=>a+b,20);  
console.log(result);//62=所有值之和加上20

深拷贝和浅拷贝

对象的复制

  • Object.assign(目标对象, 被复制的对象)[浅拷贝]
  • 将被复制对象中的属性复制到目标对象里,并将目标对象返回
  • 也可以使用展开运算符对对象进行复制
const obj3 = { address: "高老庄", ...obj, age: 48 }

let obj1 = { name: '孙悟空', age: 18 };
let obj2 = { name: '猪八戒', address: '高老庄' };
let mergedObj = { ...obj1, ...obj2 }; // 合并两个对象
// Object {name: '猪八戒', age: 18, address: '高老庄'}

深拷贝与浅拷贝

  • 浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。 如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址。
  • 深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象。
  • 区别:浅拷贝基本类型之前互不影响,引用类型其中一个对象改了地址,就会影响另一个对象。深拷贝改变新对象不会影响原对象,他们之前互不影响。
const arr2 = arr.slice() // 浅拷贝
const arr3 = structuredClone(arr) // 专门用来深拷贝的方法
补充:
//能使用JSON实现深复制:
const obj2 = JSON.parse(JSON.stringify(obj))

数组去重

  1. for循环去重
for (let i = 0; i < arr.length; i++) {
     // 获取当前值后边的所有值
     for (let j = i + 1; j < arr.length; j++) {
         // 判断两个数是否相等
         if (arr[i] === arr[j]) {
            // 出现了重复元素,删除后边的元素
             arr.splice(j, 1)
             //相同时,它会自动的删除j位置的元素,然后j+1位置会变成j位置的元素而j位置已经比较过了,当删除一个元素后,需要将该位置的元素在比较一遍
             j--
     }
  }
}
console.log(arr)
  1. indeof 去重
基础版
for(let i=0; i<arr.length; i++){
            const index = arr.indexOf(arr[i], i+1)
            if(index !== -1){
                // 出现重复内容
                arr.splice(index, 1)
                i--
    }
}
console.log(arr)
进阶版
const newArr = []

   for(let ele of arr){
        if(newArr.indexOf(ele) === -1){
                newArr.push(ele)
    }
}

 console.log(newArr)

数组排序

冒泡排序

  • 比较相邻的两个元素,然后根据大小来决定是否交换它们的位置
  • 时间复杂度:n^2
const arr = [9, 1, 3, 2, 8, 0, 5, 7, 6, 4]
for (let j = 0; j < arr.length - 1; j++) {
     for (let i = 0; i < arr.length - 1; i++) {
          // arr[i] 前边的元素 arr[i+1] 后边元素
           if (arr[i] < arr[i + 1]) {
             let temp = arr[i] 
             arr[i] = arr[i + 1] 
              arr[i + 1] = temp 
                    }
                }
            }

            console.log(arr)

选择排序

  • 取出一个元素,然后将其他元素和该元素进行比较,如果其他元素比该元素小则交换两个元素的位置
  • 时间复杂度:n^2
const arr = [9, 1, 3, 2, 8, 0, 5, 7, 6, 4]
for(let i=0; i<arr.length; i++){
    for(let j=i+1; j<arr.length; j++){
        if(arr[i] > arr[j]){
            let temp = arr[i]
            arr[i] = arr[j]
            arr[j] = temp
      }
   }

}
        console.log(arr)

函数高级

封装函数

class Person {
                constructor(name, age) {
                    this.name = name
                    this.age = age
 }
    }
            const personArr = [
                new Person("孙悟空", 18),
                new Person("沙和尚", 38),
                new Person("红孩儿", 8),
                new Person("白骨精", 16),
            ]

            // 封装filter()函数用来对数组进行过滤
            function filter(arr) {
                const newArr = []

                for (let i = 0; i < arr.length; i++) {
                    if (arr[i].age < 18) {
                        newArr.push(arr[i])
                    }
                }

                return newArr
            }

            result = filter(personArr)
            console.log(result)

高阶函数

回调函数

  • 一个函数的参数也可以是函数,如果将函数作为参数传递,那么我们就称这个函数为回调函数(callback)
  • 通常回调函数都是匿名函数
class Person {
                constructor(name, age) {
                    this.name = name
                    this.age = age
                }
            }

 const personArr = [
     new Person("孙悟空", 18),
     new Person("沙和尚", 38),
     new Person("红孩儿", 8),
      new Person("白骨精", 16),
  ]
function filter(arr, cb) {
                const newArr = []

     for (let i = 0; i < arr.length; i++) {
          if (cb(arr[i])) {
             newArr.push(arr[i])
         }
    }
                return newArr
}

      result = filter(personArr, a=>a.name === "孙悟空")
      console.log(result)
      const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
      result = filter(arr, a => a % 2 === 0) //[2,4,6,8,10]

高阶函数

  • 如果一个函数的参数或返回值是函数,则这个函数就称为高阶函数
  • 为什么要将函数作为参数传递?(回调函数有什么作用?)
    • 将函数作为参数,意味着可以对另一个函数动态的传递代码
function someFn() {
     return "hello"
}

function outer(cb){
   return () => {
      console.log("记录日志~~~~~")
       const result = cb()
         return result
    }
}

   let result = outer(someFn)

闭包

  • 闭包就是能访问到外部函数作用域中变量的函数

  • 什么时候使用:当我们需要隐藏一些不希望被别人访问的内容时就可以使用闭包

  • 构成闭包的要件:

    1. 函数的嵌套
    2. 内部函数要引用外部函数中的变量
    3. 内部函数要作为返回值返回
  • 函数的作用域

    • 在函数创建时就已经确定的(词法作用域)和调用的位置无关
    • 函数在全局作用域创建,那么它的外层作用域就是全局作用域
    • 闭包利用的就是词法作用域
  • 闭包的生命周期:

    • 闭包在外部函数调用时产生,外部函数每次调用都会产生一个全新的闭包
    • 在内部函数丢失时销毁(内部函数被垃圾回收了,闭包才会消失)
  • 注意事项:

    • 闭包主要用来隐藏一些不希望被外部访问的内容,这就意味着闭包需要占用一定的内存空间
    • 相较于类来说,闭包比较浪费内存空间(类可以使用原型而闭包不能),
    • 需要执行次数较少时,使用闭包
    • 需要大量创建实例时,使用类
unction fn(){
            console.log(a)
        }

        function fn2(){
            let a = "fn2中的a"

            fn()
        }

        // fn2()

        function fn3(){
            let a = "fn3中的a"

            function fn4(){
                console.log(a)
            }

            return fn4
        }

        let fn4 = fn3()

        fn4()

递归

  • 调用自身的函数称为递归函数
    1. 编写递归函数,一定要包含两个要件:
      • 基线条件 —— 递归的终止条件
      • 递归条件 —— 如何对问题进行拆分
求阶乘
function jieCheng2(num){
                // 基线条件
                if(num === 1){
                    return 1
         }
                // 递归条件
                // num! = (num-1)! * num
                return jieCheng2(num-1) * num
  }
  result = jieCheng2(5)

可变参数

arguments

  • arguments是函数中的一个隐含参数
  • arguments是一个类数组对象(伪数组) 和数组相似,可以通过索引来读取元素,也可以通过for循环变量, 但是它不是一个数组对象,不能调用数组的方法
  • arguments用来存储函数的实参,无论用户是否定义形参,实参都会存储到arguments对象中
  • 可以通过该对象直接访问实参
function fu(){
   console.log(arguments);
}
fu(1,2,4);

可变参数,在定义函数时可以将参数指定为可变参数

  • 可变参数可以接收任意数量实参,并将他们统一存储到一个数组中返回

  • 可变参数的作用和arguments基本是一致,但是也具有一些不同点:

    1. 可变参数的名字可以自己指定
    2. 可变参数就是一个数组,可以直接使用数组的方法
    3. 可变参数可以配合其他参数一起使用
    4. 当可变参数和普通参数一起使用时,需要将可变参数写到最后
function fu1(...a){
    return a.reduce((a,b)=>a+b,0);
}
let result=fu1(1,2,3,4,5);
console.log(result);//15

call,apply和bind

根据函数调用方式的不同,this的值也不同:

  1. 以函数形式调用,this是window
  2. 以方法形式调用,this是调用方法的对象
  3. 构造函数中,this是新建的对象
  4. 箭头函数没有自己的this,由外层作用域决定,外层this是谁,他的this就是谁
  5. 通过call和apply调用的函数,它们的第一个参数就是函数的this
  6. 通过bind返回的函数,this由bind第一个参数决定(且this无法修改)
函数.call()
函数.apply()

function fn2(a, b) {
                console.log("a =", a, "b =", b, this)
            }

            fn2.call(obj, "hello", true)
            fn2.apply(obj, ["hello", true])
call和apply的区别
- 通过call方法调用函数,函数的实参直接在第一个参数后一个一个的列出来
- 通过apply方法调用函数,函数的实参需要通过一个数组传递

bind() 是函数的方法,可以用来创建一个新的函数并返回

  • bind可以为新函数绑定this
  • bind可以为新函数绑定参数,参数排列非数组 箭头函数没有自身的this,它的this由外层作用域决定,也无法通过call、apply和bind修改它的this 箭头函数中没有arguments
function fn(a, b, c) {
        console.log("fn执行了~~~~", this)
        console.log(a, b, c)
  }
 const obj = {name:"孙悟空"}
 const newFn = fn.bind(obj, 10, 20, 30)

内建对象

解构赋值

let a, b, rest;
[a, b] = [10, 20]; // 第一个元素给a,第二个元素给b
console.log(a); // 10
console.log(b); // 20

[a, , b] = [10, 20, 30]; // 第一个元素给a,第三个元素给b,中间空出一个
console.log(a); // 10
console.log(b); // 30

[a, b, ...rest] = [10, 20, 30, 40, 50]; // 第一个元素给a,第二个元素给b,剩下的给rest
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]

const [d, e, f] = [40, 50, 60] // 可以在赋值时直接声明变量
console.log(d); // 40
console.log(e); // 50
console.log(f); // 60

[a=5, b=7] = [1]; // 赋值是可以指定默认值
console.log(a); // 1
console.log(b); // 7

对象的解构

  1. let { name, age, gender } = obj //声明变量同时解构对象
  2. 先声明再解构会产生语法错误,误以为{代码块}=obj ,需要外层加个括号
  3. 变量得同属性名才能解构,不同于属性名会返回undefined
let a, b;
({ a, b } = { a: 10, b: 20 }); //属性a赋值给变量a,属性b赋值给变量b
console.log(a); // 10
console.log(b); // 20

({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}); //属性a赋值给变量a,属性b赋值给变量b,其余的保存到rest中
console.log(a); // 10
console.log(b); // 20
console.log(rest); // {c: 30, d: 40}

({a = 10, b = 5} = {a: 3}); // 可以指定默认值
console.log(a); // 3
console.log(b); // 5

const {name, age} = {name:'孙悟空', age:18}; // 对象解构时如果使用const、let、var开头,可以不加()
console.log(name); // 孙悟空
console.log(age); // 18

//可以使用起别名和指定默认值让解构更灵活  【:后面是变量名】
let {name:a} ={name:孙悟空}
console.log(a)//孙悟空

const {len: width} = {len:100}; // 将对象的len属性值赋值给变量width
console.log(width); // 100

const {inner:{size}} = {a: 10, b: 20, inner:{size: 5}}; // 将对象中inner.size赋值给变量size
console.log(size) // 5

const {inner:{size:mySize}} = {a: 10, b: 20, inner:{size: 5}}; // 将对象中inner.size赋值给变量mySize
console.log(mySize); // 5

对象的序列化

  • JS中的对象使用时都是存在于计算机的内存中的
  • 序列化指将对象转换为一个可以存储的格式【可以存储到外存里】 在JS中对象的序列化通常是将一个对象转换为字符串(JSON字符串)
  • 序列化的用途(对象转换为字符串有什么用):
    • 对象转换为字符串后,可以将字符串在不同编程语言之间进行传递 甚至人可以直接对字符串进行读写操作,使得JS对象可以在不同编程语言之间传递
    • 用途:
      1. 作为数据交换的格式
      2. 用来编写配置文件
    • 如何进行序列化:
      • 在JS中有一个工具类 JSON (JavaScript Object Notation)
JSON.stringify() //可以将一个对象转换为JSON字符串
  • JS对象序列化后会转换为一个字符串,这个字符串我们称其为JSON字符串

    JSON.parse() //可以将一个JSON格式的字符串转换为JS对象
    
  • 也可以手动的编写JSON字符串,在很多程序的配置文件就是使用JSON编写的

  • 编写JSON的注意事项:

    1. JSON字符串有两种类型: JSON对象 {} JSON数组
    2. JSON字符串的属性名必须使用双引号引起来
    3. JSON中可以使用的属性值(元素)
    • 数字(Number)
    • 字符串(String)必须使用双引号
    • 布尔值(Boolean)
    • 空值(Null)
    • 对象(Object {})
    • 数组(Array [])
    1. JSON的格式和JS对象的格式基本上一致的, 注意:JSON字符串如果属性是最后一个,则不要再加,
补充:
//能使用JSON实现深复制:
const obj2 = JSON.parse(JSON.stringify(obj))

Map

  • Map用来存储键值对结构的数据(key-value)

  • Object中存储的数据就可以认为是一种键值对结构

  • Map和Object的主要区别:

    • Object中的属性名只能是字符串或符号,如果传递了一个其他类型的属性名,JS解释器会自动将其转换为字符串
    • Map中任何类型的值都可以称为数据的key

使用

  • 创建:const map=new Map()

  • 属性和方法:

    map.size() //获取map中键值对的数量
    map.set(key, value) //向map中添加键值对
    map.get(key) //根据key获取值
    map.delete(key) //删除指定数据
    map.has(key) //检查map中是否包含指定键
    map.clear() //删除全部的键值对
    map.keys() //获取map的所有的key
    map.values() //获取map的所有的value
    map.forEach((key,value)=>{...}) //遍历map
    Array.from(map)
    [...map] //将map转换为数组
    
    const m=new Map();  
    m.set(NaN,0);  
    m.set(undefined,-1);  
    m.set("name","小米");  
    let keys=m.keys();  
    console.log(keys);  
    let values=m.values();  
    console.log(values);  
    let array=Array.from(m);  
    console.log(array)  
    //遍历  
    for(const entry of m){  
    console.log(entry);  
    }  
    //解构遍历  
    for(const[key,value]of m){  
    console.log(key,value);  
    }  
    m.forEach((key,value)=>{  
    console.log(key,value);  
    })
    

Set

  • Set用来创建一个集合
  • 它的功能和数组类似,不同点在于Set中不能存储重复的数据,可以利用这一点帮助数组去重

使用

  • 创建
    • new Set()
    • new Set(iterable)//iterable可迭代对象
  • 方法
    size()//获取数量
    add()//添加元素
    has()//检查元素
    delete()//删除元素
    [...set]//转换为数组
    
    const arr2 = [1,2,3,2,1,3,4,5,4,6,7,7,8,9,10]
    const set2 = new Set(arr2)
    console.log([...set2])
    

Math

  • Math一个工具类
  • Math中为我们提供了数学运算相关的一些常量和方法

使用

  • 常量: Math.PI 圆周率
  • 方法:
    Math.abs() //求一个数的绝对值
    Math.min() //求多个值中的最小值
    Math.max() //求多个值中的最大值
    Math.pow() //求x的y次幂
    Math.sqrt() //求一个数的平方根
    Math.floor() //向下取整
    Math.ceil() //向上取整
    Math.round() //四舍五入取整
    Math.trunc() //直接去除小数位
    Math.random() //生成一个0-1之间的随机数
    

Date

  • 在JS中所有的和时间相关的数据都由Date对象来表示

使用

  • 创建:new Date()
    • 直接通过new Date()创建时间对象时,它创建的是当前的时间的对象
    • 可以在构造函数中传递一个表示时间的字符串【不推荐】
    • 字符串的格式:月/日/年 时:分:秒 ,年-月-日T时:分:秒
  • 方法
    getFullYear() //获取4位年份
    getMonth() //返当前日期的月份(0-11)
    getDate() //返回当前是几日
    getDay() //返回当前日期是周几(0-6) 0表示周日
    getTime() //返回当前日期对象的时间戳
    /*时间戳:自1970年1月1日0时0分0秒到当前时间所经历的毫秒数,
    计算机底层存储时间时,使用都是时间戳*/
    Date.now() //获取当前的时间戳
    

日期的格式化

  • 2023年1月28号 15:33:02
  • date.toLocaleDateString()//只转日期
    • 2023/1/18
  • date.toLocaleTimeString()//只转时间
    • 15:33:02
  • date.toLocaleString()//可以将一个日期转换为本地时间格式的字符串
  • 参数:
  1. 描述语言和国家信息的字符串

    • zh-CN 中文中国
    • zh-HK 中文香港
    • en-US 英文美国
  2. 需要一个对象作为参数,在对象中可以通过对象的属性来对日期的格式进行配置

    • 方式1:dateStyle 日期的风格,timeStyle 时间的风格:
      • 可选值
        • full
        • long
        • medium
        • short
    • 方式2:{year,month,day,weekday,hour,minute,second}给各属性赋值
      • weekday 星期的显示方式:long,short,narrow
      • hour12 是否采用12小时值:true,false
      • 不写就不显示
      result = d.toLocaleString("zh-CN", {
              year: "numeric",
              month: "long",
              day: "2-digit",
              weekday: "short",
          })
      

包装类

在JS中,除了直接创建原始值外,也可以创建原始值的对象

  • 通过 new String() 可以创建String类型的对象
  • 通过 new Number() 可以创建Number类型的对象
  • 通过 new Boolean() 可以创建Boolean类型的对象
  • 但是千万不要这么做,这么做不便于随时随地类型转换,抹杀了js语法的最大特点

JS中一共有5个包装类:

  • String --> 字符串包装为String对象
  • Number --> 数值包装为Number对象
  • Boolean --> 布尔值包装为Boolean对象
  • BigInt --> 大整数包装为BigInt对象
  • Symbol --> 符号包装为Symbol对象
  • 通过包装类可以将一个原始值包装为一个对象,当我们对一个原始值调用方法或属性时,JS解释器会临时将原始值包装为对应的对象然后调用这个对象的属性或方法 (自动装箱)
  • 由于原始值会被临时转换为对应的对象,这就意味着对象中的方法都可以直接通过原始值来调用
let str = new String("hello")
            let num = new Number(11)
            let bool = new Boolean(true)

String的方法

  • 字符串其本质就是一个字符数组
  • "hello" --> ["h", "e", "l", "l", "o"]
  • 字符串的很多方法都和数组是非常类似的

属性和方法

length
    - 获取字符串的长度
str[索引]
    -获取指定位置的字符
str.at()
    - 根据索引获取字符,可以接受负索引
str.charAt()
    - 根据索引获取字符
str.concat()
    - 用来连接两个或多个字符串
str.includes()
    - 用来检查字符串中是否包含某个内容,有返回true,没有返回false
str.indexOf()
str.lastIndexOf()
    - 查询字符串中是否包含某个内容,根据内容返回索引
str.startsWith()
    - 检查一个字符串是否以指定内容开头
str.endsWith()
    - 检查一个字符串是否以指定内容结尾
str.padStart()
str.padEnd()
    - 通过添加指定的内容,使字符串保持某个长度
str.replace()
    - 使用一个新字符串替换一个指定内容
str.replaceAll()
    - 使用一个新字符串替换所有指定内容
str.slice()
    - 对字符串进行切片
str.substring()
    - 截取字符串
str.split()
    - 用来将一个字符串拆分为一个数组
str.toLowerCase()
    - 将字符串转换为小写
str.toUpperCase()
    - 将字符串转换为大写
str.trim()
    - 去除前后空格
str.trimStart()
    - 去除开始空格
str.trimEnd()
    - 去除结束空格

正则表达式

  • 正则表达式用来定义一个规则
  • 通过这个规则计算机可以检查一个字符串是否符合规则,或者将字符串中符合规则的内容提取出来
  • 正则表达式也是JS中的一个对象,所以要使用正则表达式,需要先创建正则表达式的对象

创建方式

new RegExp() 可以接收两个参数(字符串) 1.正则表达式 2.匹配模式
let reg = new RegExp("a", "i") // 通过构造函数来创建一个正则表达式的对象
使用字面量来创建正则表达式:/正则/匹配模式
            reg = /a/i
            reg = new RegExp("a") // /a/ 表示,检查一个字符串中是否有a        

正则使用

1.在正则表达式中大部分字符都可以直接写
2.| 在正则表达式中表示或
3.[] 表示或(字符集)
    [a-z] 任意的小写字母
    [A-Z] 任意的大写字母
    [a-zA-Z] 任意的字母
    [0-9]任意数字
4.[^] 表示除了
    [^x] 除了x
5. . 表示除了换行外的任意字符
6. 在正则表达式中使用\作为转义字符
7. 其他的字符集
    \w 任意的单词字符 [A-Za-z0-9_]
    \W 除了单词字符 [^A-Za-z0-9_]
    \d 任意数字 [0-9]
    \D 除了数字 [^0-9]
    \s 空格
    \S 除了空格
    \b 单词边界
    \B 除了单词边界
8. 开头和结尾
    ^ 表示字符串的开头
    $ 表示字符串的结尾
re = /[^a-z]/ // 匹配包含小写字母以外内容的字符串
re = /^a$/ // 只匹配字母a,完全匹配,要求字符串必须和正则完全一致
9. 量词 只匹配前一个字符,要匹配多个字符(……){量词}  
{m} 正好m个  
{m,} 至少m个  
{m,n} m到n个  
+ 一个以上,相当于{1,}  
* 任意数量的a  
? 0-1次 {0,1}  
10. 匹配模式  
i忽略大小写  
g全局模式匹配

re = /^(ab){3}$/
re = /^[a-z]{1,3}$/
  • 正则表达式.test(#字符串)检查字符串是否符合,返回ture/false
  • 正则表达式.exec(#字符串) 获取字符串中符合正则表达式的内容,返回数组(可以通过在正则表达式里加括号的方式进行分组)
    • 不开全局模式此方法只会匹配第一个
    • 开了之后调一次匹配一个,下次调用从上次匹配的结尾开始
let str = "abcaecafcacc"

// 提取出str中符合axc格式的内容
let re = /a(([a-z])c)/ig

let result = re.exec(str)

console.log(result)

while(result){
    console.log(result[0], result[1], result[2])
    result = re.exec(str)
}

image.png

字符串与正则表达式

split()
    - 可以根据正则表达式来对一个字符串进行拆分
search()
    - 可以去搜索符合正则表达式的内容第一次在字符串中出现的位置
replace()
    - 根据正则表达式替换字符串中的指定内容
match()
    - 根据正则表达式去匹配字符串中符合要求的内容
    - 返回字符串
matchAll()
    - 根据正则表达式去匹配字符串中符合要求的内容(必须设置g 全局匹配)
    - 它返回的是一个迭代器
练习:
let re = /1[3-9]\d{9}/g
    re = /(1[3-9]\d)\d{4}(\d{4})/g
let str = "dajsdh13715678903jasdlakdkjg13457890657djashdjka13811678908sdadadasd"
let result
    while (result = re.exec(str)) {
console.log(result[0], result[1], result[2])                console.log(result[1]+"****"+result[2])
 }
    re = /^1[3-9]\d{9}$/
    console.log(re.test("13456789042"))

BOM和DOM的区别

  • BOM 主要用于获取或设置浏览器的属性行为,属性上如:获取屏幕分辨率浏览器版本号等等;行为上如:新建窗口页面跳转滚动条滚动等等
  • DOM 描述了处理网页内容的方法和接口,是 HTML 和 XML 的 API,DOM 把整个页面规划成由节点层级构成的文档。主要用于获取或控制 HTML 文档里面的文档节点,如:增删改查元素以及控制元素的样式等等