华清远见重庆中心—JS技术总结

一、JS是什么?

js语法名称为:ECMA Script

Ecma国际(前身为欧洲计算机制造商协会,European Computer Manufacturers Association)

ECMA Script 缩写为 ES

见说法ES5、ES6,后面的数字,代表的是语法版本号

二、JS基本操作

1.数据类型

        数据类型是什么?js中对不同种类数据的一个类型分类,mdn 为数据类型一共定义了 8 种:

undefined,Boolean、Number、String、BigInt、Symbol、null,最后一种类型是:Object

数据按照参数的传递方式,可以分为 `值传递类型` 和 `引用传递类型,可以使用 `typeof` 来判断数据类型

2.获取文档元素并修改其属性

代码如下(示例):

 // 1. 查询一个指定选择器的元素
    let box = document.querySelector('.box')

    console.log(box);
    // 2. 查询所有指定选择器的元素
    let boxs = document.querySelectorAll('.box')

    console.log(boxs);

    // 3. 通过id查询元素
    box = document.getElementById('myBox')

    console.log(box);

    // 4. 通过类名查询元素
    boxs = document.getElementsByClassName('box')

    console.log(boxs);

    // 5. 通过标签名查询元素
    boxs = document.getElementsByTagName('div')

    console.log(boxs);

    // 修改属性值
    console.log(box.id);
    // 改id
    box.id = 'my-box'
    // 改样式
    box.style.color = '#00f'
    box.style.backgroundColor = '#000'
    // 改class名称
    box.className = 'box myClass'

3.通过简单的点击事件修改标签体

<script>
    let box = document.querySelector('.box')

    // 声明事件处理程序
    function onClick() {
        // alert('点击了按钮')

        // 1. textContent 修改和读取标签体里的文本内容
        // console.log(box.textContent);
        // box.textContent = '<div style="background-color: #00f;">ok</div>'

        // 2. innerText 修改和读取标签体里的文本内容
        // console.log(box.innerText)
        // box.innerText = '<div style="background-color: #00f;">ok</div>'

        // 3. innerHTML 修改和读取标签体里的html内容
          console.log(box.innerHTML)
          box.innerHTML = '<div style="background-color: #00f;">ok</div>'
    }
</script>

4.简单的元素事件

<script>
    // 1. 通过元素的 on... 属性,例如 onclick
    function clickHandler() {
        console.log('按钮被点击了');
    }

    // 2. dom对象的 on... 属性,例如 dom.onclick
    let btn = document.querySelector('button')
    btn.onclick = function () {
        console.log('按钮被点击了1');
    }

    // 3. dom.addEventListener
    btn.addEventListener('click', () => {
        console.log('按钮被点击了2');
    })
</script>

5.读取元素样式的三种方法

 

方法一: dom 对象的 style 属性,style 属性只能读取元素的 style 无法读取 <style> 标签中的内容

let box = document.querySelector('.box')
    console.log(box.style.width);
    console.log(box.style.height);
    console.log(box.style.backgroundColor);

方法二: document.styleSheets,只能读取 html 文档中 <style> 标签中的属性 无法读取元素身上的 style 属性

console.log(document.styleSheets);
    // document.styleSheets[0] 代表第一个 style 标签
    let styleTag = document.styleSheets[0]
    // styleTag.cssRules[0] 代表第一个样式表
    let boxStyles = styleTag.cssRules[0]

    console.log(boxStyles.style.backgroundColor);

    // 可以赋值属性

    boxStyles.style.backgroundColor = '#0f0'

方法三: getComputedStyle, 可以读取元素身上所有的属性 但是无法赋值

let style = getComputedStyle(box)
    console.log(style.backgroundColor);
    console.log(style.width);
    console.log(style.height);

6.数据类型的转换

不同类型之间的数据是可以互相转换类型的,这个过程叫数据转换

 数据转换的方法分为两种:显示转换,隐式转换 显示转换:在代码中有明确的代码用于转换类型

   , 隐式转换:在代码中没有明确的代码用于转换类型

// boolean 转换成其他类型
    let b = true
    // 转换数字
    b = Number(b)
    // 除了 Number 以外 还能使用 parseInt parseFloat 进行转换
    console.log(b); // => 1
    b = false
    b = Number(b)
    console.log(b) // => 0

    // true 转换数字为 1 false 转换数字为 0

    // 转换字符串
    b = true
    console.log(b);
    b = String(b)
    console.log(b); // => true 的字符串
    b = false
    b = b + '' // 连接字符串时,js会自动转换数据格式为字符串,这是一个隐式转换
    console.log(b); // => false 的字符串


    // 数字类型转其他类型
    let num = -123
    // 转boolean
    num = Boolean(num)
    console.log(num); // => true
    num = 0
    num = Boolean(num)
    console.log(num); // => false

    // 数字为0 结果为 false 否则结果为 true

    // 转字符串
    num = 1314
    num = String(1314)
    console.log(num); // => 1314



    // 字符串转其他类型
    let str = 'hello world'
    // 转boolean值
    str = Boolean(str)
    console.log(str); // => true
    str = ''
    str = Boolean(str)
    console.log(str); // => true

    // 空字符串为 false 其余为 true

    // 转数字
    str = 'hello world'
    str = Number(str)
    console.log(str); // => NaN
    str = '6e+2'
    str = Number(str)
    console.log(str); // => 600
    // 加法运算符直接写在字符串前,可以产生转换成数字的效果
    str = '6e+2'
    str = +str
    console.log(str);

    // 若字符串代表的是合法数字则转换为对应数字 否则为 NaN



    // 对象转其他类型
    let obj = { a: 1, b: 2 }
    // 转 boolean 值
    obj = Boolean(obj)
    console.log(obj); // => true

    obj = null // 此处赋值 undefined 效果一样
    obj = Boolean(obj)
    console.log(obj); // => false

    // 变量是 null 或 undefined 时 转换结果为 false;对象只要存在 结果就为 true

    // 转数字
    obj = { a: 1, b: 2 }
    obj = Number(obj)
    console.log(obj); // => NaN

    // 转字符串
    obj = { a: 1, b: 2 }
    obj = String(obj)
    console.log(obj); // [object Object]
    // 对象强转字符串结果始终为 [object Object]

    // 若希望将对象转换成字符串后能够查看其数据,则可以使用 “序列化”

7.节点操作

7.1插入节点:创建节点、 插入节点到文档中

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


    // 插入节点

    // document.body 就是body标签的dom对象

    // appendChild 追加一个子元素
    // 参数是要追加的元素
    document.body.appendChild(img)

    let ok = document.querySelector('.ok')

    // insertBefore 插入节点到另一个节点前
    // 第一个参数:要插入的元素
    // 第二个参数:插入位置的子节点
    document.body.insertBefore(img, ok)



    // 创建元素并插入元素的用途
    // 1. 异步加载 js css 等文件

    // 例如异步加载js
    let loadJsBtn = document.querySelector('.load-js')
    loadJsBtn.addEventListener('click', () => {
        // 异步加载js

        // 创建标签
        let script = document.createElement('script')
        script.src = '../js/main.js'

        // 插入script标签到body中,这样js脚本将自动加载
        document.body.appendChild(script)
    })

    // 2. 动态追加一些以html作为模板的元素

7.2替换节点

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

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)

7.3查询节点

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

// 获取tr
    let tr = document.querySelector('.tr')

    // 访问子节点使用 children 属性
    console.log(tr.children);

    // children 属性包含一个子节点的集合
    // 所以访问具体子节点可以使用索引值
    let node = tr.children[1]
    node.remove()


    // 访问父节点使用 parentElement 属性
    // 例如 访问tr树节点 tbody 如下:
    console.log(tr.parentElement);
    console.log(tr.parentElement.parentElement);

7.4异步加载JS

let btn = document.querySelector('button')
    btn.addEventListener('click', () => {
        // 创建script
        let script = document.createElement('script')
        script.src = '../js/main.js'

        // load 加载完成事件
        script.addEventListener('load', () => {
            console.log('加载完成');
            // 删除脚本
            script.remove()
        })

        // 插入页面
        document.body.appendChild(script)
    })

8.浏览器操作

8.1 location

location 代表浏览器地址栏

let input = document.querySelector('input')
    let button = document.querySelector('button')
    button.addEventListener('click', () => {
        // 给 location.href 赋值页面就会调转
        window.location.href = input.value
    })

    // 跳转网页不计入历史
    // window.location.replace('https://www.bilibili.com')

    // 刷新页面
    // window.location.reload()

    // 地址栏参数
    // location.search

8.2 localStorage

数据持久化到浏览器中,关闭窗口或浏览器,都不会消失

// 设置数据
    // 第一个参数:key
    // 第二个参数:value 且必须是字符串
    // window.localStorage.setItem('name', '张三')
    // 通过索引方式赋值 等价于 setItem
    // localStorage['sex'] = 'male'

    // 读取数据
    let r = localStorage.getItem('name')
    r = localStorage['sex']
    console.log(r);

    // sessionStorage 用法和localStorage相同,但是窗口关闭数据就消失了

    // window.sessionStorage.setItem('age', '17')
    console.log(sessionStorage['age']);

9.三目运算符

// 三元运算符: 用于判断一个表达式结果,为真时,返回结果1,为假时返回结果2
    // 语法:bool expression? case1: case2
    // 作用:主要用在给变量赋值

    let obj = {
        name: '张三',
        sex: 'other',
        age: 16
    }

    console.log(obj.sex === 'male' ? '男' : '女');
    // 嵌套三元运算符 在问号或冒号处可以换行
    console.log(obj.sex === 'male' ? '男' :
        obj.sex === 'female' ? '女' : '其他');
    // 当嵌套三元运算符在同一行内书写,请给嵌套的三元运算符的表达式用圆括号包起来
    console.log(obj.sex === 'male' ? '男': (obj.sex === 'female' ? '女' : '其他'));

10.数组

10.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);

10.2 数组的迭代操作

        以下所有的遍历函数的参数都是相同的回调函数, 回调函数接收以下参数、el 被遍历的当前

数组成员, index 被遍历的当前成员的索引,arr 被遍历的数组对象

 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 查找
    // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/find

    // some 查询是存在满足条件的成员
    // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/some


    // 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);

10.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)));

11. 字符串操作

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);

12. 闭包 

 闭包也叫函数闭包,通过函数产生一个封闭的内存空间,包裹一些需要被保存的数据、且函数需要返回一个持续引用的对象,这就叫闭包

const demo = (function A() {
        let B = '张三'
        return () => {
            // 函数内引用变量B 则函数A是闭包的
            console.log(B)
        }
        // 当返回一个值时 函数A将返回 A中没被使用的变量将被垃圾回收掉,则A不是闭包的
        // return 'hello world'
        // 当返回的是直接声明的对象或数组的时候,声明时将B作为对象属性的值或数组成员传入对象或数组
        // 当A返回后,变量B则没有再被引用了,则被垃圾回收掉,A不是闭包的
        // return { name: B }
    })()

    // 使用自调用函数,返回一个函数
    const sayMessage = (() => {
        // 声明一个用于记录调用次数的变量
        // 闭包的函数空间内声明的变量,在内存中将持续存在
        // 垃圾回收不会回收该变量
        // 该变量在外部无法访问
        let count = 0

        // 在自调用函数中返回一个函数
        // 返回的函数就是一个闭包后的函数
        return (msg) => {
            console.log(msg)
            count++
            console.log(`sayMessage 调用次数为 ${count}`);
        }
    })()

    sayMessage('hello world')

    const obj = (function () {
        let name = '张三'

        // 在函数空间内 可以返回任意的内容
        return {
            // 获取名称
            getName() {
                return name
            },
            // 改名
            setName(newName) {
                name = newName
            }
        }
    })()

    console.log(obj);
    console.log(obj.getName());
    obj.setName('隔壁老王')
    console.log(obj.getName());


    console.log(sum([1, 2, 3]));
    console.log(avg([4, 5, 6]));

13. 函数

13.1bind call apply函数的应用

 // 函数内可以使用 this 关键字,不同的地方 this 关键字的指代不一样

    function fn(a, b) {
        console.log(this);
        return a + b
    }

    // fn()

    // 函数内的 this 所指代的内容可以通过 bind call apply 来修改

    // bind 绑定函数的 this 指代
    // 语法 function.bind(thisArg, x, y, ...)
    // thisArg: 要修改的this的指代
    // x, y, ... : 函数的参数,选填
    // 返回值: 一个被修改了this指代后的新函数
    let fn2 = fn.bind('hello bind', 1, 2)

    console.log(fn2(4, 5));


    // call 调用函数并指定 this 指代
    // 语法: function.call(thisArg, x, y, ...)
    // thisArg: 要修改的this的指代
    // x, y, ... : 函数的参数,选填
    // 返回值: 函数本身的返回值
    console.log(fn.call('hello call', 5, 6))


    // apply 调用函数并指定 this 指代
    // 语法: function.apply(thisArg, [x, y, ...])
    // thisArg: 要修改的this的指代
    // x, y, ... : 函数的参数,选填
    // 返回值: 函数本身的返回值
    console.log(fn.apply('hello apply', [8, 9]))

13.2节流和防抖

        防抖: 函数将在一个固定时间后被调用,若计时未完成又执行该函数,则取消上次计时,重新开始计时,用于限制频繁的网络请求,例如:搜索功能,用户停止输入的一段时间后才会执行搜索任务。

// 步骤:
    // 1. 取消计时
    // 2. 重新计时

    const fd = (() => {
        // 计时器id
        let timerId
        return () => {
            // 取消计时
            clearTimeout(timerId)
            // 重新计时
            timerId = setTimeout(() => {
                // 计时完成后要执行的代码 写在此处
                console.log('hello world');
            }, 3000)
        }
    })()

    console.log(fd);


    // 封装防抖函数
    // 这样任何函数都能被添加防抖功能

    function add(x, y) {
        console.log('ok');
        console.log(x, y);
        return x + y
    }

    // 定义一个函数来封装防抖
    // fn: 要添加防抖的函数
    // delay: 防抖延迟多久
    // that: 防抖函数内的 this 指代
    function fdPlus(fn, delay, that) {
        let timerId
        return function () {
            // 清除计时器
            clearTimeout(timerId)
            // 重新计时
            timerId = setTimeout(() => {
                // 调用参数fn函数
                // 将当前函数的参数 arguments 作为 fn 的参数传入进去
                fn.apply(that, arguments)
            }, delay)
        }
    }

    add = fdPlus(add, 3000, 'helloworld')

    add(1, 2)

 

        节流:固定时间内只能调用一次的函数,可以使用时间戳或计时器的方式实现。作用同样是限制用户频繁的网络请求,例如:发送验证码

// 计时器
    // 1. 判断是否可以运行节流代码(cd好没有)
    // 2. 执行节流的内容
    // 3. 计时
    function jlTimer() {
        // 计时器id
        let timerId
        let cd = 3000
        return () => {
            // 若已经开始计时 则timerId 存在
            if (timerId) return

            // 执行被节流的函数内容
            console.log('hello 节流');

            // 开始计时
            timerId = setTimeout(() => {
                // 清空计时器id 允许再次调用
                timerId = undefined
            }, cd)
        }
    }

    fn = jlTimer()


    // 封装节流函数
    function sub(x, y) {
        console.log(this);
        console.log('sub');
        console.log(x, y);
        return x - y
    }

    // 定义一个函数来封装节流
    // fn: 要添加防抖的函数
    // delay: 节流的cd
    // that: 节流函数内的 this 指代
    function jlPlus(fn, delay, that) {
        // 计时器id
        let timerId
        return function () {
            if (timerId) return

            // 执行节流的代码
            fn.apply(that, arguments)

            // 计时
            timerId = setTimeout(() => {
                timerId = undefined
            }, delay)
        }
    }

    fn = jlPlus(sub, 5000, 'hello this')

14.正则表达式语法

let regex = /^\\dcom$/

    // \ 斜杠:转义
    console.log(/\\abc/.test('\\abc'));

    // ^ :匹配字符串的开头
    regex = /^abc/
    console.log(regex.test('abcd')); // => true
    console.log(regex.test('abcefg')); // => true
    console.log(regex.test('aabcd')); // => false

    // $ :匹配字符串的结尾
    regex = /xyz$/
    console.log(regex.test('123xyz')); // => true
    console.log(regex.test('666abcxyz')); // => true
    console.log(regex.test('666abcxyz.')); // => false


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

    // * :匹配任意次
    regex = /^o*k$/
    console.log(regex.test('k')); // => true
    console.log(regex.test('ok')); // => true
    console.log(regex.test('ooook')); // => true
    console.log(regex.test('ooookk')); // => false


    // ? : 匹配0次或1次
    regex = /^o?k$/
    console.log(regex.test('k')); // => true
    console.log(regex.test('ok')); // => true
    console.log(regex.test('ooook')); // => false

    // + : 匹配至少1次
    regex = /^o+k$/
    console.log(regex.test('k')); // => false
    console.log(regex.test('ok')); // => true
    console.log(regex.test('ooook')); // => true

    // {n} : 匹配指定次数
    regex = /^o{2}k$/
    console.log(regex.test('k')); // => false
    console.log(regex.test('ok')); // => false
    console.log(regex.test('ook')); // => true
    console.log(regex.test('oook')); // => false

    // {n,} : 匹配至少n次
    regex = /^o{2,}k$/
    console.log(regex.test('k')); // => false
    console.log(regex.test('ok')); // => false
    console.log(regex.test('ook')); // => true
    console.log(regex.test('oook')); // => true

    // {n,m} : 匹配至少n次,至多m次
    regex = /^o{1,3}k$/
    console.log(regex.test('k')); // => false
    console.log(regex.test('ok')); // => true
    console.log(regex.test('ook')); // => true
    console.log(regex.test('oook')); // => true
    console.log(regex.test('ooook')); // => false


    // ------------------- 匹配字符个数的符号 - end

    // [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 = /abc(123)+xyz/
    regex = /abc123xyz/
    let r = '000123abc123xyz444555'.match(regex)
    console.log(r);


    // (?:pattern): 匹配分组内容,但不获取圆括号中的值
    regex = /abc(?:123)+xyz/
    r = '000123abc123123xyz444555'.match(regex)
    console.log(r);

    // assert 断言 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions/Assertions
    // 什么是断言? 断言就是一段下笃定的言论,例如: 今天一定会下雨

    // 先行断言
    // 语法: x(?=y)
    // x 跟随 y 时 匹配 x
    // /小明(?=小红)/ 该正则理解为: 小明后面一定跟随了一个小红
    console.log('小明小红'.match(/小明(?=小红)/));


    // 先行否定断言
    // 语法: x(?!y)
    // x 后没有 y 跟随时 匹配 x
    // /小明(?!小红)/ 小明后面一定没有跟随小红
    console.log('小明小芳'.match(/小明(?!小红)/));


    // 后行断言
    // 语法: (?<=y)x
    // x 的前有 y 则 匹配 x
    // /(?<=小红)小明/ 小明的前面一定有小红
    console.log('小红小明'.match(/(?<=小红)小明/));


    // 后行否定断言
    // 语法: (?<!y)x
    // x 前没有 y 则 匹配 x
    // /(?<!小红)小明/ 小明的前面一定没有小红
    console.log('小芳小明'.match(/(?<!小红)小明/));

15.this关键字

this 关键字代表什么?

根据不同的场景,this关键字代表东西不同,有以下几种情况

<script>
    // 参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this

    // this 关键字代表什么?

    // 根据不同的场景,this关键字代表东西不同,有以下几种情况

    // js 执行上下文中的 this
    console.log(this); // => window
    console.log(this === window);

    // 函数内的this
    function fn() {
        // 非严格模式下 函数内 this => window
        console.log(this);
    }

    fn()

    // json对象方法的this
    let obj = {
        name: '张三',
        fn() {
            console.log(this); // => 对象自己
        }
    }

    obj.fn()


    // 实例对象方法的this
    class A {
        name
        constructor(name) {
            this.name = name
        }
        fn() {
            // 类方法中的 this => 调用该方法的实例对象
            console.log(this);
        }
    }

    let a = new A('a')
    let b = new A('b')
    a.fn()
    b.fn()


    // 对象方法中的this始终是自己


    // 声明变量存储 A 类的 fn 方法,再次调用并观察 this
    // 给变量赋值对象方法,默认情况下 this 为 undefined
    const fnVar = a.fn
    fnVar() // => undefined

    // 给变量赋值函数值,默认情况下 this 为 window
    const fn2 = function () {
        console.log(this);
    }

    fn2()

    // 有些情况下,回调函数中的this可能被其他函数赋值,所以this不是undefined
    // 此处 事件回调函数被 addEventListener 赋值了其中的 this
    // 所以这里的this指的是绑定事件的那个dom对象
    document.querySelector('button').addEventListener('click', function () {
        console.log(this);
    })


    // 自定义函数也可以修改回调函数内的this指代
    function custom(callback) {
        // call apply bind
        callback.call('hello world')
    }

    custom(function () { console.log(this); })

    // 总结:
    // 1. js 执行上下文 => window
    // 2. 函数内 this => window (函数=>window)
    // 3. 方法内的this指向调用方法的对象 (方法=>实例)
    // 4. this 可能被其他函数赋值 例如 addEventListener 这种时候 this 既不是 window 也不是 undefined,它取决于函数 addEventListener 自己
</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值