JavaScript知识总结

1. 什么是JavaScript

JavaScript是类似于java计算机编程语言的脚本语言,缩写为js。它的作用是提供了一个和html进行动态交互的功能。

1.1 Java 与 JavaScript 的区别

  • Java是静态编译,JavaScript作为脚本动态编译
  • java属于强类型语言,JavaScript是弱类型语言

1.2 JavaScript的使用方式

  1. 在浏览器控制台上直接运用js语言片段
  2. 在html文件的script标签中
  3. 使用.js文件书写js代码,然后在html中通过script标签引入代码
<!-- script 标签写在body后面 -->
<script>
    // console.log 打印日志到控制台
    console.log('hello world')
</script>

<!-- 使用 script 标签引入外部js文件,src 是文件路径 -->
<script src="./main.js"></script>

1.3 JavaScript代码执行顺序

  1. 若有多个script,那么从上而下依序执行
  2. 在同一个script标签中,js的执行顺序
    2.1 以一句话为单位,基本顺序:从上到下,从左到右,每句话用分号隔开“;”
    2.2 若代码中每一行只有一句js,那么句尾的分号可以省略
  3. 赋值运算的顺序:先执行赋值符右侧代码,再赋值给左侧
  4. 函数调用顺序: 从左到右执行,若存在函数,先执行函数。若在执行的函数中存在其他函数,例如下面的fn1的调用,那么会先执行内部的函数,如 fn3 fn2,最后执行 fn1
c = fn2() + 4 + fn1(fn3(), fn2(), 11)

2. JavaScript的基础知识

2.1 变量

变量是存储数据的容器,数据可以发生变化
变量声明:

var a; // 声明后不赋初始值 默认都为 undefined
var b = 5; // 声明变量并赋予初始值
var c, d = 10, e; // 同时声明多个变量并赋予初始值

// let 变量不能重复定义 var 变量可以重复定义
let x, y, z = 88

变量在存储新值之前将完全弃用原来的值

2.1.2 变量的命名规范

1,必须以字母开头,或者使用 $ 或者 _ 开头 ,不能用数字,变量名建议不要使用中文
2,小驼峰命名法: 多个单词构成的名字,第一个单词全小写,后面的首字母大写
3,为了提高代码的可读性,命名规范,见名知意
4,不能使用关键字(有特殊功能的名字:class、var、this)

 	let _a // 下划线开头的变量一般是局部变量出现在函数中作为形式参数
    let $body // $ 开头一般是jquery对象
    let bird // 用字母开头
    let blueBird // 多个单词,首个单词的首字母小写,其余单词的首字母大写
    let simpleDateFormat
    // let const // 由于const是关键字,所以不能用作变量名

2.2 常量

恒定不变的值为常量,声明时必须赋初始值 且声明后无法修改

const g = 10

2.3 var、let 和 const 的区别?

1.var 声明的变量具备变量提升,let,const所声明的变量也有类似效果但是不属于变量提升,应该是‘暂时性死区’
2.var 可以重复声明同名变量,let和const不允许在同一作用域下重复声明同名变量
3.var 和 let 在声明变量时可以不用对变量进行初始化,const声明必须初始化
4.var 不具备块级作用域 ,let 和 const 具备块级作用域 { let }

2.4数据类型

值类型:
boolean布尔型、 number数字型、 string字符串、 bigInt长整数
引用类型
object 对象类型、symbol 符号类型
undefined 未定义:
undefined 是一个单独的类型,用于给未定义的变量赋值
null 空引用:
null 值的是空引用,是 js 的一个原始数据类型,用来指代引用类型数据的空值

2.4.1值类型与引用类型数据的区别

值类型:变量中直接存贮值本身
引用类型:变量中存储的是引用地址,而值是存在引用地址所指向的内存中的某个对应位置

2.4.2 序列化和反序列化

序列化:将对象转换成字符串
反序列化:将字符串转换成对象

<script> 
    let obj = {
        name: '张三的母亲',
        sex: 'other',
        age: 40
    }

    // 序列化
    let str = JSON.stringify(obj)
    console.log(str);

    // 通过json格式声明一个字符串
    str = '{"name": "英雄的母亲", "child": "张三", "score": 88, "isOk": false}'

    // 反序列化
    obj = JSON.parse(str)
    console.log(obj);
</script>

3. 节点操作

3.1插入节点

步骤: 1. 创建节点。 2. 插入节点到文档中。

appendChild 追加一个子元素,参数是要追加的元素

	// 创建节点
    // 参数是标签名
    // 返回一个dom对象
    let img = document.createElement('img')
    // 设置图片源
    img.src = '../img/head.png'
    // 插入节点
    // document.body 就是body标签的dom对象
	document.body.appendChild(img)

insertBefore 插入节点到另一个节点前
第一个参数:要插入的元素;第二个参数:插入位置的子节点

    let ok = document.querySelector('.ok')
    // 插入节点到另一个节点前
    document.body.insertBefore(img, ok)

创建元素并插入元素的用途

  1. 异步加载 js css 等文件
  2. 动态追加一些以html作为模板的元素

3.2查询节点

dom对象中可以查询其父节点和子节点。

  1. 访问子节点使用 children 属性,children 属性包含一个子节点的集合,所以访问具体子节点可以使用索引值。
  2. 访问父节点使用 parentElement 属性。

3.1删除节点

  1. remove可以删除元素,例如:li.remove()
  2. removeChild 删除子节点,参数是想要删除的子节点dom对象。例如:ul.removeChild(li)
  3. 用途: 动态加载一次性js脚本资源,加载完后就可以删除元素了.。例如:下载按钮
 let btn = document.querySelector('button')  
 btn.addEventListener('click', () => {
        // 动态创建a标签并点击它 来实现下载
        let a = document.createElement('a')
        // download 可以指定下载后的文件名
        a.download = '1.png'
        // 下载文件的地址
        a.href = '../img/head.png'
        // 无需插入页面即可点击
        a.click()
        // 若a标签以插入页面则需要调用 remove 来删除
        a.remove()
    })

3.1替换节点

语法:
parent.replaceChild(newNode, child)
parent: 要替换节点的父节点
newNode: 新的要插入文档的节点
child: 被替换的节点

<body>
    <table border>
        <tr>
            <td>1-1</td>
            <td>1-2</td>
            <td>1-3</td>
        </tr>
        <tr class="tr">
            <td>2-1</td>
            <td>2-2</td>
            <td>2-3</td>
        </tr>
        <tr>
            <td>4-1</td>
            <td>4-2</td>
            <td>4-3</td>
        </tr>
    </table>
</body>
<script>
    let tr = document.querySelector('.tr')
    let _td = document.querySelector('.tr>td:nth-of-type(2)')
    
    // 构造一个新的元素用于替换
    let td = document.createElement('td')
    td.textContent = 'hello world'

    tr.replaceChild(td, _td)
</script>

4. 数组

存储一组数据的一个容器称为数组。

 	// 声明数组
    // 使用脚本形式声明数组
    let arr = []
    // 声明并初始化数组
    // 数组中的每一个数据称为 数组成员
    arr = [false, 2, 'hello world', { name: '张三' }, null, undefined]
    console.log(arr);

    // 对象形式的声明方法
    arr = new Array()
    console.log(arr);
    // 声明并初始化
    arr = new Array(false, 2, 'hello world', { name: '张三' }, null, undefined)
    console.log(arr);

访问数组成员: 使用索引值访问数组中的成员。
索引:数组给每个成员的编号称为索引,索引值从零开始依次递增1。
访问数组成员时,若索引超出数组范围,则获取到的结果为 undefined。

    console.log(arr['0']);
    console.log(arr['1']);
    // 数组索引可以不适用引号
    console.log(arr[2]);

给数组成员赋值: 使用数组变量名加上索引值进行赋值,可以给指定索引位置的成员进行赋值。

 arr[4] = { name: '李四' }
 arr[5] = 333

数组长度: 指的就是数组成员的个数

4.1 数组的操作

 	// 数组的操作
    let arr = [1, 2, true, { name: 'Amy' }, 2]

    // push 追加数据到数组末尾
    // 参数:被添加的新数组成员
    arr.push('hello world')
    console.log(arr);
    
    // pop 从尾部取出一个成员
    // 返回值是取出的成员
    let r = arr.pop()
    console.log(r);
    console.log(arr);

    // unshift 在头部添加数据
    // 参数:被添加的新数组成员
    arr.unshift('my best day')
    console.log(arr);

    // shift 从头部取出一个成员
    // 返回值是取出的成员
    r = arr.shift()
    console.log(r);
    console.log(arr);
    
    // push 和 unshift 可以批量添加成员
    arr.push('a', 'b', 'c')
    arr.unshift('x', 'y', 'z')
    console.log(arr);

    // splice 删除指定位置的成员,并用新成员替换,或插入新成员到指定成员的前面
    // 第一个参数:删除成员的起始位置
    // 第二个参数:删除成员的个数
    // 第三个参数:用于替换被删除成员的新数据,该参数可以省略
    // 删除一个成员:
    // r = arr.splice(6, 3)
    // splice的返回值 就是被删除的成员数组
    // console.log(r);

    // 在指定成员前追加新数据
    // 若第二个参数为0,则可以实现在指定位置的前面添加成员的功能
    arr.splice(6, 0, { name: 'Bob' })
    console.log(arr);

    // concat 连接数组
    // 参数:多个被追加进数组的成员,若成员是数组,该数组中每个成员将被加入原数组
    // concat 返回一个新数组
    // r = arr.concat(7, 8, 9)
    // r = arr.concat([7, 8, 9], [10, 11])
    // console.log(r);
    // console.log(arr);

    // concat 的应用场景多用于克隆数组
    let arr2 = [].concat(arr)
    console.log(arr2);
    console.log(arr === arr2);

    // join 使数组成员用一个字符连接起来
    // join 函数接收一个参数,该参数就是连接数组成员时使用的字符
    arr2 = ['abc', 'xyz', '123']
    r = arr2.join('-*-')
    console.log(r);

    // includes 判断是否包含某成员
    r = arr.includes('z')
    console.log(r);

    // indexOf 查询指定数组成员的索引
    r = arr.indexOf('b')
    console.log(r);
    // 可以使用indexOf判断是否包含某个数组成员 若不包含 返回 -1
    r = arr.indexOf('g')
    console.log(r); // => -1
    if (arr.indexOf('g') === -1) {
        console.log('该数组成员不存在');
    }

    // lastIndexOf 查询最后一个指定数组成员的索引(应为数组成员可能重复)
    console.log(arr.indexOf(2));
    console.log(arr.lastIndexOf(2));

    // slice 数组切片 获取子数组
    // 切片遵循“前截后不截”原理: 起始位置包含在结果内,结束位置不包含
    r = arr.slice(5, 8)
    console.log(r);
    // 参数只有一个,代表从该位置开始 一直截取到最后
    r = arr.slice(5)
    console.log(r);

4.2 数组的迭代操作

forEach 循环遍历每一个数组成员
every 和 forEach 一样遍历每个数组成员,但是中途可以跳出循环

let students = [
        { id: 0, name: '张三', sex: 'male', age: 16 },
        { id: 2, name: '隔壁老王', sex: 'other', age: 30 },
        { id: 1, name: '李四', sex: 'female', age: 20 },
    ]
    
    // forEach 循环遍历每一个数组成员
        students.forEach((el, index, arr) => {
        console.log(el);
        console.log(index)
        console.log(arr);
    })

    // every 和 forEach 一样遍历每个数组成员,但是中途可以跳出循环
    students.every((el, index, arr) => {
        console.log(el);
        console.log(index)
        console.log(arr);

        if (el.sex === 'female') {
            // every 的回调函数中需要返回一个bool值 false 类似于循环语句中的 break 用于跳出循环
            return false
        }

        // 类似于 continue 继续循环
        return true
    })

map 映射数组到新数组中
map 的回调函数将返回一个值,代表当前被遍历的数组成员在新数组中的投影
map 函数将返回一个新的投影数组

    // 例如将students中所有的年龄放入一个新数组
    let r = students.map((el, index, arr) => {
        console.log(el);
        console.log(index)
        console.log(arr);

        // return 的内容将被放到新的数组中
        return el.age
    })
    console.log(r);

filter 过滤器
filter 返回过滤完后的新数组

    let r = students.filter((el, index, arr) => {
        console.log(el);
        console.log(index)
        console.log(arr);

        // 过滤掉年龄大于25岁的成员
        // if (el.age > 25) {
        //     // return false 代表过滤掉该数据
        //     return false
        // }
        // return true // return true 代表保留该数据

        return el.age <= 25
    })
    console.log(r);

find 查找符合回调函数条件的数组成员并返回它
返回值为查询结果

      let r = students.find((el, index, arr) => {
        // // 查找女同学
        // if (el.sex === 'female') {
        //     return true // return true 代表当前成员就是要查找的元素
        // }
        // return false

        return el.name === '隔壁老王'
    })
    console.log(r);

    findIndex 查找对应成员所在索引
    使用方法和 find 相同,返回结果为查找到的成员索引
    let r = students.findIndex((el, index, arr) => {
        return el.name === '隔壁老王'
    })
    console.log(r);

some 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。
给出一个判断条件,some 函数将对每个成员都做出相同的判断, 任意成员符合条件返回 true 时 some函数则返回true 否则为 false。

    // 请判断 arr2 中是否有成员存在于 arr1 中
    let arr1 = ['x', 'y', 'z']
    let arr2 = ['a', 'b', 'z']

    let r = arr2.some((el, index, arr) => {
        // 判断当前数组成员是否再 arr1 中存在
        if (arr1.includes(el)) {
            // 返回 true 代表 找到一个满足条件的数组成员
            return true
        }
        return false
    })
    console.log(r);

sort 排序,若调用 sort 函数不传参数时,会使用浏览器默认的排序规则

let numList = [90, 12, 110, 9, 40, 214]
    // let numList = [90, 12, 11, 92, 40, 21]
    numList.sort()
    console.log(numList);

    // 自定义排序
    
    // 排序规则:按年龄从小到大排序
    // sort 参数是一个排序规则的函数
    // el1 和 el2 代表的是排序时两两比较的两个数组成员
    // 排序原理是,将每个成员都和剩下所有成员进行比较,每一对 el1 和 el2 决定他们的先后顺序
    // 也叫“冒泡排序”
    // sort 函数执行完后将返回新数组
    students.sort((el1, el2) => {
        if (el1.age > el2.age) {
            // el1.age 若大于 el2.age 则 若从小到达排列 el1 应该放到 el2 的右侧
            // 右侧联想 右箭头 >;即大于符号,则此处应该返回一个大于零的值
            return 1
        } else if (el1.age < el2.age) {
            // el1.age 若小于 el2.age 则 若从小到大排列 el1 应该放到 el2 的左侧
            // 左侧联想到 左箭头 < ;即小于符号,所以返回一个小于 0 的值
            return -1
        } else {
            // 若 el1 和 el2 不需要交换顺序 则返回 0
            return 0
        }
    })
    console.log(students);

4.3 数组去重

    let arr = [1, 5, 1, 2, 2, 4, 6, 5, 6, 4]
    // 有以下两种方法供参考
    
    // 1. 缓存重复数据
    // 缓存
    let temp = {}
    // 结果数组
    let result = []
    for (let i = 0; i < arr.length; i++) {
        const item = arr[i];
        // 判断 item 充当 key 是否存在于 temp 中
        // if (temp[item]) {
        if (!Reflect.has(temp, item)) {
            // item 不存在于 temp 的 key 中
            result.push(item)
            // 添加缓存
            temp[item] = true
        }
    }
    console.log(result);

    // 使用 filter 去重
    temp = {}
    result = arr.filter(el => {
        // 若缓存中没有el
        if (!temp[el]) {
            // 加入缓存
            temp[el] = true
            return true
        }
        return false
    })
    console.log(result);

    // 2. 使用 set 集
    // 构造set对象,set对象会自动去重
    let set = new Set(arr)
    console.log(set);
    // 将set转换为数组
    result = Array.from(set)
    console.log(result);
   
    console.log(Array.from(new Set(arr)));  // 合成一句话

5. 函数

函数用于包装可以重复使用的代码,实现代码的复用

5.1 定义函数

语法:
function 函数名(参数列表) { // code block 代码块 }

有参数的函数:
参数列表中声明参数,如有多个参数,用逗号隔开
参数列表中的参数,我们称为:形式参数

返回结果
函数的返回结果称为“返回值”
return 还会终止函数内后续代码的运行,return 终止函数运行并返回一个输出,函数若没有返回值(也就是没有return)那么函数默认返回 undefined

调用有参函数:
通过函数名调用,且调用的同时,传入真实的参数。调用函数时,圆括号中的参数叫做:实际参数

可以通过变量(或常量)存储函数和调用函数。

    let sub = function (x, y) {
        let result = x - y
        return result
    }
    r = sub(1, 2)
    console.log(r);


    sayHow()
    function sayHow() {
        console.log('how');
    }
    let sayHow = function () {
        console.log('how');
    }

定义参数时,可以给参数设置默认值。

	function sayMessage(msg = 'hello message') {
        console.log(msg);
    }
    
    sayMessage('this is my param')
    // 当不给函数提供参数时,参数msg将采用默认值
    sayMessage()

自调用函数: 定义后立即调用的匿名函数
定义自调用函数的步骤:
1.先打两个圆括号
2.在第一个圆括号中声明函数
3.第二个圆括号代表调用函数,圆括号中填入实际参数

console.log(((x, y) => x + y)(2, 3))

5.2 预编译

在浏览器执行js脚本前需要进行编译,有些代码在编译前将会率先被翻译执行并存入内存,这个过程称为预编译。

哪些东西会被预编译:
1. 调用函数时,在函数执行前会进行函数的预编译
2. 预编译时,函数内的 形参和函数内声明的变量和函数会被预编译

预编译的顺序:
1.寻找函数内的形参和变量,为其赋值undefined
2.将实参传值给形参
3.寻找函数声明

5.3 变量的作用域

作用域指的是作用范围,范围内可以访问变量,超出范围则无法访问

全局作用域(Global)

具备全局作用域的只有全局变量或常量

全局变量
指的是作用域为全局(Global)的变量,代码中任何位置都能使用。

全局变量有两种:
1.自动全局变量:给未定义的变量直接赋值,该变量会变成 自动全局变量,若在浏览器中,该变量会被存到window对象里
2. (普通的)全局变量: 直接在函数外声明的变量也是全局作用域的变量

块级作用域(Block):

就是在代码块中有效的作用域,let 关键字声明的变量具备块级作用域

函数作用域(Function):

函数中的变量,无论使用var还是let定义的,都是函数作用域,在函数外无法访问

5.4 lambda表达式

什么是 lambda 表达式?是一种用来定义函数的方法,lambda 表达式也称为箭头函数。

 	// 定义一个无参函数
    // 圆括号部分是参数列表
    // 花括号部分是代码块
    const sayHello = () => {
        console.log('hello');
    }
    sayHello()

    // 定义只有一个参数的函数
    let sayMessage = (msg) => {
        console.log(msg);
    }

    // 参数只有一个时 圆括号可以省略
    sayMessage = msg => {
        console.log(msg)
    }

    sayMessage('hello message')

    // lambda 的返回值
    let add = (x, y) => {
        return x + y
    }

    // 在箭头后不写花括号 代表直接返回箭头后的内容
    add = (x, y) => x + y
    console.log(add(1, 2));

    // 返回对象的情况
    // 在箭头后使用圆括号包裹想要返回的对象
    let getUser = () => ({ name: '张三', sex: 'male' })
    console.log(getUser());

6. 字符串操作

字符串可以被视为字符数组

 	let str = 'hello world !!!'

    // 查看字符串长度
    console.log(str.length);

    // 通过索引访问字符串中的字符
    console.log(str[4]);

    // charAt 函数可以获取指定索引处的字符
    // 等价于 str[4]
    console.log(str.charAt(4));

    // split: 分割字符串
    // 参数:用于分割字符串的字符
    // 返回值:字符串数组
    let r = str.split(' ')
    console.log(r);

    // split + join 替换字符
    // 例如:替换字符 *|& 为 _
    str = 'hello*|&world*|&!!!'
    r = str.split('*|&')
    r = r.join('_')
    console.log(r);

    // trim: 去掉字符串首尾空格
    str = '          hello world !!!          '
    console.log(str);
    r = str.trim()
    console.log(r);

    // substring: 截取子字符串
    // 第一个参数:截取字符串的起始索引位置
    // 第二个参数:截取字符串的结束索引位置
    // 口诀:前截后不截
    // 返回值:截取出来的子字符串
    str = 'hello world !!!'
    r = str.substring(4, 9)
    console.log(r);

    // 第二个参数可以省略,如果只写一个参数,substring将从该参数位置一直截取到字符串末尾
    r = str.substring(6)
    console.log(r)

    // indexOf: 查询字符串中指定字符在字符串中的索引位置
    // 参数:要查询的字符串
    // 返回值:被查询字符串的索引
    console.log(str.indexOf('o'));


    // lastIndexOf
    console.log(str.lastIndexOf('o'))

    // 举例: 截取字符串从 o ~ r
    console.log(str.substring(str.indexOf('o'), str.lastIndexOf('r') + 1));

    // startsWith: 用于判断字符串是否以指定字符串开头
    // 参数:指定开头的字符串
    // 返回值:bool值,true代表是以指定字符串开头的,false代表不是
    console.log(str.startsWith('hello'));
    // endsWith: 用于判断字符串是否以指定字符串结尾
    console.log(str.endsWith('!!!!'));


    // toUpperCase toLowerCase 将字符串中的英文转成全为大写或小写
    str = str.toUpperCase()
    console.log(str);
    str = str.toLowerCase()
    console.log(str);

    // 例如: 统计一个字符串中出现了多少个a字符,忽略大小写
    str = 'alhdAkdjfalKHgladhfdjAhg'
    str = str.toLowerCase()
    let count = 0
    for (let i = 0; i < str.length; i++) {
        const char = str[i];
        if (char === 'a') count++
    }
    console.log(count);

    // 补充:
    // 数字操作:
    // toFixed 保留小数点后多少位的函数
    // 参数:指定小数点后保留几位
    // 返回值:是一个保留了指定小数点位数的字符串
    let num = 3.1415926
    r = num.toFixed(3)
    console.log(r);

6.1 正则表达式

表达式语法:

  \  斜杠:转义;
  ^ :匹配字符串的开头;
  $ :匹配字符串的结尾;

匹配字符个数的符号:这些匹配字符个数的符号,代表的意思是:匹配前一个字符多少次。

	*:匹配任意次
	? : 匹配0次或1次
	+ : 匹配至少1次
	{n} : 匹配指定次数
	{n,} : 匹配至少n次
	{n,m} : 匹配至少n次,至多m次	

匹配字符个数的符号:

	[xyz]: 匹配字符集合,匹配一个字符,该字符在方括号内
	x|y : 或
	[^xyz]: 匹配负值集合,匹配一个字符,该字符不在方括号内
	[a-z] [0-9] : 取范围值,匹配一个字符,该字符在指定范围内
	[^5-7]: 取范围负值,匹配一个字符,该字符不在指定范围内
 // [xyz]: 匹配字符集合,匹配一个字符,该字符在方括号内
    regex = /^[xyz]$/
    console.log(regex.test('z')); // => true
    console.log(regex.test('yy')); // => false

    // x|y : 或
    regex = /^(good|bad)$/
    console.log(regex.test('good')); // => true
    console.log(regex.test('bad')); // => true
    console.log(regex.test('ok')); // => false


    // [^xyz]: 匹配负值集合,匹配一个字符,该字符不在方括号内
    regex = /^[^xyz]$/
    console.log(regex.test('z')); // => false
    console.log(regex.test('a')); // => true
    console.log(regex.test('abc')); // => false

    // [a-z] [0-9] : 取范围值,匹配一个字符,该字符在指定范围内
    regex = /^[A-Z][0-9]$/
    console.log(regex.test('E7')); // => true
    console.log(regex.test('G8')); // => true
    console.log(regex.test('c5')); // => false

    // [^5-7]: 取范围负值,匹配一个字符,该字符不在指定范围内
    regex = /^[^5-7]$/
    console.log(regex.test('6')); // => false
    console.log(regex.test('x')); // => true
    console.log(regex.test('a')); // => true

分组 (pattern)
(pattern): 将pattern里面的所有字符当作一个字符处理

    regex = /^abc(123)+xyz$/
    console.log(regex.test('abc123xyz')); // => true
    console.log(regex.test('abc123123xyz')); // => true
    console.log(regex.test('abc112233xyz')); // => false

站在字符串的角度看,圆括号不仅有分组的作用,同时,它将被取值。

    regex = /abc123xyz/
    let r = '000123abc123xyz444555'.match(regex)
    console.log(r);

(?:pattern): 匹配分组内容,但不获取圆括号中的值

 	regex = /abc(?:123)+xyz/
    r = '000123abc123123xyz444555'.match(regex)
    console.log(r);

6.2 正则表达式的特殊字符

\d : 10进制数
\D : 非10进制数

\r: 回车 \n: 换行
\s: 所有不可见字符,制表符 回车换行 空格等   \S: 所有可见字符

.: 基本等于任意字符 但不包括 \r\n
// \t: 制表符
 	let regex = /^$/
    // 匹配任意字符任意次数的写法如下:
    regex = /[\S\s]*/

7. 闭包和计时器

什么是闭包
闭包也叫函数闭包,通过函数产生一个封闭的内存空间,包裹一些需要被保存的数据, 且函数需要返回一个持续引用的对象,这就叫闭包
应用场景
闭包用于存储一些不让函数外访问的数据,或者为了避免作用域中变量名的冲突,可以使用闭包

计时器
当经过指定时间后触发一段代码的函数就是一个计时器

  • setTimeout
    // 声明一个计时器:setTimeout
    // 第一个参数:计时器计时结束后触发的函数
    // 第二个参数:计时时长,单位:毫秒
    // 返回值: 计时器id
    // 计时器id 用于停止计时
    let timerId = setTimeout(() => {
        console.log('hello setTimeout')
    }, 3000)

    document.querySelector('.btn1').addEventListener('click', () => {
        // clearTimeout 清空计时器
        // 参数是 计时器id
        // 清空后计时器将取消掉
        clearTimeout(timerId)
    })
  • setInterval 循环计时函数
    每次经过指定时间,触发一次指定的函数,参数和返回值 与 setTimeout 相同
   let count = 0
     let timerId2 = setInterval(() => {
         count++
         console.log(count);
     }, 1000)

  • 清空循环计时器
 clearInterval
    document.querySelector('.btn2').addEventListener('click', () => {
        // 清空循环计时器
        clearInterval(timerId2)
    })

注意事项:
1. 尽量不使用 setInterval
理由:setInterval 可能由于人为原因忘了关闭,或者内部出现异常,导致代码死循环
2. 若要做大量循环调用甚至是无限循环调用时(例如轮询死循环调用),请使用 setInterval 而不是使用 setTimeout 递归进行循环
理由:setTimeout 会占用大量内存堆栈

8. 时间对象和数学函数

8.1 时间对象

创建时间对象的方法,语法:new Date(params);
Date 时间对象默认修改了对象的 toString 方法 所以转字符串时,会显示成时间字符串;
所有对象都有 toString 方法,转换字符串时,js回调用该方法。

	 // 1. 当前系统时间
    let date = new Date()
    console.log(date);
  
    // 2. 创建指定时间字符串的时间
    date = new Date('1997-07-07')
    console.log(date);
    // 不推荐使用,因为这是个非标准时间字符串,不同浏览器解析方式可能不同

    // 3. 通过格林威治毫秒时创建时间
    date = new Date(1000 * 60 * 60 * 24 * 365)
    console.log(date);

    // 4. 通过年月日时分秒创建时间
    // 参数分别代表 年月日时分秒
    // 注意:月份是从0开始计算的
    date = new Date(2000, 5, 6, 18, 44, 22)
    console.log(date);

    // 参数至少写前两个参数,后续参数可以省略
    date = new Date(2000, 5, 6, 18, 44)
    console.log(date);
    date = new Date(2000, 5, 6, 18)
    console.log(date);
    date = new Date(2000, 5)
    console.log(date);

    // 如何读取时间?如何设置时间?
    // 读取时间
    date = new Date()
    console.log(date.getFullYear()); // 年
    console.log(date.getMonth()); // 月  月份从0开始计算
    console.log(date.getDate()); // 日 一个月中的第几天
    console.log(date.getDay()); // 一周中的第几天 一周中的第一天是周日 值为 0
    console.log(date.getHours()); // 时
    console.log(date.getMinutes()); // 分
    console.log(date.getSeconds()); // 秒
    console.log(date.getMilliseconds()) // 毫秒

    // 设置时间
    date.setFullYear(2023)
    date.setMonth(0)
    date.setDate(5)
    date.setHours(20)
    date.setMinutes(66)
    date.setSeconds(66)
    date.setMilliseconds(1000)
    console.log(date);

    // 其他常用时间函数
    // Date.now() // 获取当前系统时间的格林威治毫秒时
    console.log(Date.now());
    // date.getTime() // 获取date对象代表的格林威治毫秒时
    console.log(date.getTime());

    // 通常来说日期对象需要转换成字符串显示,否则用户看不懂
    function format(date) {
        return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}.${date.getMilliseconds()}`
    }

    console.log(format(new Date()));

8.2 数学函数

三角函数
注意三角函数 sin()、cos()、tan()、asin()、acos()、atan() 和 atan2() 返回的值是弧度而非角度。若要转换,弧度除以 (Math.PI / 180) 即可转换为角度,同理,角度乘以这个数则能转换为弧度。

反三角函数就在三角函数前加上a就可以了

abs: 获取数字的绝对值

console.log(Math.abs(-100));
console.log(Math.abs(100));

ceil: 一个小数向上取整

console.log(Math.ceil(5.1));
console.log(Math.ceil(5.2));
console.log(Math.ceil(5.5));
console.log(Math.ceil(5.9));

floor: 一个小数向下取整

console.log(Math.floor(5.1));
console.log(Math.floor(5.2));
console.log(Math.floor(5.5));
console.log(Math.floor(5.9));

round:四舍五入

console.log(Math.round(5.1));
console.log(Math.round(5.2));
console.log(Math.round(5.5));
console.log(Math.round(5.9));

max: 取参数中较大数

console.log(Math.max(1, 2, 3, 4, 5, 6, 7));

min: 取参数中较小数

console.log(Math.min(1, 2, 3, 4, 5, 6, 7));

pow:返回x的y次方

// 语法:Math.pow(x, y)
console.log(Math.pow(2, 3));

sqrt: 返回一个数的平方根

console.log(Math.sqrt(25));

random: 取随机数,范围在[0~1)之间,能取到0但取不到1

 console.log(Math.random());
    // 假设随机一个 50 ~ 100 的数
    console.log(50 + Math.random() * 50);
    // 随机一个 n ~ m 的数
    // 则公式为: n + Math.random() * (m - n)

sign: 取符号

console.log(Math.sign(-100))
console.log(Math.sign(-32))
console.log(Math.sign(80))
console.log(Math.sign(45))

9 事件和异常处理

9.1 事件

在js中,事件就是:当某种情况发生的时候,能够触发一段代码,这个发生的情况就是事件。事件在js中以对象形式存在。

addEventListener绑定事件:
第一个参数:要绑定的事件名称
第二个参数:当事件发生时,触发的函数
第二个参数的函数,不是有开发人员调用的,当事件发生时由浏览器调用的

	// 资源事件
    const img = document.querySelector('img')
    // load 加载完成
    img.addEventListener('load', () => {
        console.log('加载完成');
    })

    // error 加载失败
    img.addEventListener('error', () => {
        console.log('加载失败');
    })

    // 焦点事件
    const input = document.querySelector('input')
    const box = document.querySelector('.box')

    // focus 获取焦点
    input.addEventListener('focus', () => {
        console.log('获取焦点');
    })

    // blur 失去焦点
    input.addEventListener('blur', () => {
        console.log('失去焦点');
    })

    box.addEventListener('focus', () => {
        console.log('box获取焦点');
    })

    box.addEventListener('blur', () => {
        console.log('box失去焦点');
    })

    // 鼠标事件
    const box2 = document.querySelector('.box2')

    // 点击事件
    box2.addEventListener('click', () => {
        console.log('单击左键');
    })

    // 右键菜单
    box2.addEventListener('contextmenu', () => {
        console.log('右键菜单');
    })

    // 双击
    box2.addEventListener('dblclick', () => {
        console.log('双击');
    })

    // 鼠标点下
    box2.addEventListener('mousedown', ev => {
        console.log('点下');
        console.log(ev);
        // ev.button 用于区分点击的是哪个键
        // 0: 左键
        // 1: 中键
        // 2: 右键
        console.log(ev.button);
    })

    // 鼠标抬起
    box2.addEventListener('mouseup', ev => {
        console.log('抬起');
        console.log(ev);
        console.log(ev.button);
    })

    // 进入和离开事件
     box2.addEventListener('mouseenter', () => {
         console.log('进入');
     })
     box2.addEventListener('mouseleave', () => {
         console.log('离开');
     })

    // 悬停和出去
     box2.addEventListener('mouseover', () => {
         console.log('悬停');
     })
     box2.addEventListener('mouseout', () => {
         console.log('出去');
     })

    // 移动
    box2.addEventListener('mousemove', ev => {
        console.log('移动');
        console.log(ev);
        // offsetX offsetY 是鼠标相对于元素左上角的坐标
        console.log(ev.offsetX);
        console.log(ev.offsetY);
    })

    // 滚轮
    box2.addEventListener('wheel', ev => {
        console.log('鼠标滚轮');
        console.log(ev);
        // deltaY 纵向滚动的变化量
        // 正数向下 负数向上
        console.log(ev.deltaY);
    })

    // 拖动事件
    // 元素上需要添加 draggable="true"
    const box3 = document.querySelector('.box3')

    // 拖动
    // box3.addEventListener('drag', ev => {
    //     console.log('drag');
    //     console.log(ev);
    // })

    // 开始拖动
    box3.addEventListener('dragstart', ev => {
        console.log('drag start');
        console.log(ev);
    })

    // 结束拖动
    box3.addEventListener('dragend', ev => {
        console.log('drag end');
        console.log(ev);
    })


    // 媒体事件:和多媒体播放相关事件,详细请查文档
    // 表单元素事件
    // 输入事件
     input.addEventListener('input', ev => {
         console.log('输入');
         console.log(ev);
        // 本次输入的内容
         console.log(ev.data);
         // 当前输入框的值
         console.log(ev.currentTarget.value);
         console.log(ev.target.value);
     })

    // 变化事件
    // 一般除了输入框外都使用 change 事件
    input.addEventListener('change', ev => {
        console.log('变化');
        console.log(ev);
        // 当前输入框的值
        console.log(ev.currentTarget.value);
        console.log(ev.target.value);
    })

    // 按键事件
    // 按下
    // 可以按住不放持续触发事件
    input.addEventListener('keydown', ev => {
        console.log('按下');
        console.log(ev);
        // 获取用户点击的哪个按键
        console.log(ev.key);
        console.log(ev.keyCode);
    })

    box.addEventListener('keydown', () => {
        console.log('box按下');
    })

    // 抬起
    input.addEventListener('keyup', ev => {
        console.log('抬起');
        console.log(ev);
        // 获取用户点击的哪个按键
        console.log(ev.key);
        console.log(ev.keyCode);
    })

    // 按压
    input.addEventListener('keypress', ev => {
        console.log('按压');
        console.log(ev);
    })

    // 补充
    // 窗口重置大小
    window.addEventListener('resize', () => {
        console.log('resize');
        console.log(window.innerWidth);
        console.log(window.innerHeight);
    })

    // 滚动条滚动事件
    // 要监听滚动条的滚动事件,需要先找到产生滚动条的元素
    // window.addEventListener('scroll', ev => {
    //     console.log('scroll');
    //     console.log(ev);
    // })
  document.querySelector('.frame').addEventListener('scroll', function (ev) {
        console.log('scroll');
        console.log(ev);
        // this 代表当前触发事件的对象
        // this.scrollTop 代表纵向滚动的高度
        console.log(this.scrollTop);
    })

9.2 异常处理

异常:当程序运行时,运行不下去了,碰到了问题,该问题就是一个异常。
异常的特点:当程序抛出异常后,代码将停止运行

异常处理:当程序出现异常时,开发人员进行的一个手动处理,异常处理的方法叫做捕获异常,只有运行时异常(runtime error)需要进行捕获

运行时异常:程序稳定运行时,可能出现的异常(例如用户输入)

捕获异常:通过 try catch 捕获异常,手动排除异常,并让程序能够继续运行。

语法如下:

    try {
         // 代码块: 该代码块中可以写入你想要尝试运行的代码
    } catch(e) { // e:该参数是一个 Error 对象
    当 try 代码块中的容抛出异常时,将触发 catch 代码块的内容
         // 代码块: 对异常进行处理的逻辑写在这里
    }
    
    注意:try catch 代码块 必须同时存在

    try {
        // 在try中执行可能会有异常的代码
        fn1()
    } catch (e) {
        // Error 对象包含两个常用属性
        // message: 异常消息
        console.log(e.message);

        // stack: 调用栈的信息
        // 调用栈可以用来查看异常程序整个调用的流程,便于找到异常位置
        console.log(e.stack);
    }

    被处理后的异常不会导致程序终止
    程序会继续执行
    console.log('hello world');

异常对象(Error):当程序出现了系统异常时,程序回自动抛出一个 Error 对象,可以手动创建异常对象

  // 这是个除法函数
    function div(x, y) {
        if (y === 0) {
            // 除数不能为零,所以此处我们手动抛出异常
            // 1. 创建异常对象
            // let error = new Error('除数不能为0')
            // // 2. 抛出异常
            // // 抛出异常后程序将终止运行
            // throw error
            throw new Error('除数不能为0')
        }
        return x / y
    }

    try {
        div(2, 0)
    } catch (e) {
        console.error(e);
    }

    console.log('hello world');

finally: 是try catch 之后的一个代码块,作用是,无论try中是否出现异常,finally 中的代码都会执行。

 function div(x, y) {
        if (y === 0) {
            // 除数不能为零,所以此处我们手动抛出异常
            // 1. 创建异常对象
            let error = new Error('除数不能为0')
            // 2. 抛出异常
            // 抛出异常后程序将终止运行
            throw error
        }
        return x / y
    }

    try {
        let r = div(3, 1)
        console.log(r);
    } catch (e) {
        console.error(e);
    } finally {
        // try catch 后 最终一定会执行的程序
        console.log('无论try执行成功还是抛出异常,都会执行此处的代码');
    }

9.3 自定义异常

自定义异常: 开发人员自己创建的异常类就是自定义异常。
作用:给异常分类,告诉开发人员哪些异常是需要处理的,哪些是系统异常不需要处理。

    // 除法
    function div(x, y) {
        // 参数验证
        // if (isNaN(x) || isNaN(y)) throw new Error('参数不是数字')
        if (isNaN(x) || isNaN(y)) throw new NaNError()
        if (y === 0) {
            throw new ArgZeroError()
        }
        return x / y
    }
    
    // 自定义异常类
    // 参数不是数字异常
    class NaNError extends Error {
        // 使用 new 关键字创建类实例时,调用 constructor 构造函数
        constructor() {
            // 父类构造函数
            super('参数不是数字')
        }
    }

    class ArgZeroError extends Error {
        constructor() {
            super('除数不能为0')
        }
    }

    try {
        div('ab7c', 0)
    } catch (e) {
        // 处理异常
        // 判断异常的类型 分别进行处理
        // n instanceof m 含义为: n 是否是 m 类型的实例
        if (e instanceof NaNError) {
            console.error('参数异常');
        } else if (e instanceof ArgZeroError) {
            console.error('参数为0');
        } else {
            console.error(e);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值