思考的小知识

57 阅读10分钟

事件传播:

     *      当我们在子元素内触发了一个事件的时候,
     *      他的对应的父元素也会触发相同的事件
  
   
     *  事件传播的两种方式: 冒泡与捕获
     * 
     *      目标:   我们点击的元素就是目标
     * 
     *      冒泡: 从目标开始, 逐层向上传递事件, 这个过程叫做 事件冒泡
     *              1. 目标
     *              2. 目标的父元素
     *              3. 目标的父元素的父元素
     *              4. 目标的父元素的父元素.....
     *              5. body
     *              6. document
     *              7. window
     * 
     *      在 开发中, 默认的事件传播方式为 冒泡
     * 
     *      注意: 事件传播的时候, 只要触发了对应的事件, 那么传播一定会传递上去, 
     *              哪怕中间的一些元素没有绑定对应的事件
     * 
     *      捕获:   从页面的最顶层, 一直向下传递到 目标
     *              1. window
     *              2. document
     *              3. body
     *              4. 目标的父元素
     *              5. 目标
     * 
     *      冒泡和捕获, 只有执行顺序的区别, 其他的没有任何区别
  


        // DOM元素.addEventListener('事件类型', 事件处理函数, 一个布尔值决定当前的传播方式)     第三个参数默认为 false, 代表 传播方式为 冒泡

        box1.addEventListener(
            'click',
            function () {
                console.log('box11111111111111111111111111111')
            },
            true
        )

        box2.addEventListener('click', function () {
            console.log('box22222222222222222222222222')
        }, true)

        document.body.addEventListener('click', function () {
            console.log('body........................')
        }, true)


     *  事件委托
     *
     *      因为 JS 中 事件的传播方式默认是 冒泡
     *      所以我们触发了子元素的事件时, 会将事件传递给父级
     * 
     *      这是冒泡的核心, 我们的事件委托就是利用了 事件冒泡完成的
     * 
     *      其实就是将子级的事件, 委托给共同的父级
     * 
     * 
     *  好处:
     *      1. 简化代码量与内存空间
     *      2. 后续的子元素, 都会有对应的事件
    

* this

     function fn() {
        console.log(this)
    }

    1. 在代码中直接调用函数  this === window
    fn()

    2. 将上述的函数 放在一个对象中调用      this === 当前对象  (当前调用者)
    const obj = {
        name: '我是obj对象的名字',
        age: 10086,
        objFn: fn
    }
    obj.objFn()

    3. 将上述的函数 放在 定时器或者倒计时器内部      this === window
    window.setTimeout(fn, 10)

    4. 将上述函数 放在事件处理中             this === 事件源
    var box = document.querySelector('div')
    box.onclick = fn

    5. 将函数放在自执行函数中  目前市场已经不多见  this === window
    (function fn() {
        console.log(this)
    })()

    (书写上要执行的函数)(这个小括号可以给第一个括号内的函数传参)

    ;(function () {
        console.log('当你看到我的时候, 请你找一下, 有没有函数的调用')
    })()

    ;(function (num) {
        console.log('当你看到我的时候, 请你找一下, 有没有函数的调用', num)
    })(10086)

     *  是每一个函数中都有的一个 变量
     * 
     *  因为 this 的值在书写的时候 无法确定, 只有在函数被调用的时候才能确定
     
     
         function fn(num) {
                console.log(num, this)
            }

            var obj = {
                name: '我是 obj 对象'
            }

            fn(10086)    // window


            // 需求: 调用 fn 函数的时候, 内部的 this === obj

           
                     *  3. bind
                     * 
                     *      语法:   函数名.bind('将函数内部的 this 更改为谁?', '传递给函数的实参1', '传递给函数的实参2', '传递给函数的实参3' .......)
                     * 
                     *      区别:   刚才的两个方法调用后都会立即执行函数
                     *              当前方法不会立即执行, 而是会返回一个内部的this更改过的函数
                    */
                           //var res = fn.bind(obj, 'QF001')
                  
                     *  2. apply
                     * 
                     *      语法: 函数名.apply('将函数内部的this更改为谁?', ['传递给函数的实参1', '传递给函数的实参2', '传递给函数的实参3', .......])
                    */
                              // fn.apply(obj, [12345])
                  /**
                     *  1. call
                     * 
                     *      在 JS 中, 每一个 函数 都自带一些方法, 其中一个就是 call
                     * 
                     *      语法:   函数名.call('将函数内部的this更改为谁?', '传递给函数的实参1', '传递给函数的实参2', '传递给函数的实参3' .......)

                    // fn.call(obj, 10010)

* 箭头函数

     * 
     *      只是对我们之前函数的一个语法优化
     */




    // 箭头函数写法优化:    如果只有一行代码, 并且也需要返回值, 那么我们可以省略 大括号
    // var fn = () => {
    //     return 100
    // }

    // var fn = () => 100
    // console.log(fn())


    // 基本写法
    // var fn = (a) => {
    //     return a
    // }

    // 形参只有一个, 可以省略 小括号
    // var fn = a => {
    //     return a
    // }

    // 要执行的代码只有一行, 所以可以省略 {}
       var fn = a => a
       
    console.log(fn)         // a => a
    console.log(fn())       // undefined
    console.log(fn(10086))  // 10086




    // 箭头函数写法优化:    如果只有一个形参, 那么小括号可以省略

    var fn = (a) => { console.log(a) }
    var fn = a => { console.log(a) }
    fn(100)

    //但是注意, 如果这一个形参有默认值, 那么必须添加 小括号
    var fn = (a = 500) => { console.log(a) }
    fn(100)
        

* 箭头函数与普通函数 this 的区别

             *
             *      普通函数 this 指向与调用者
             *      箭头函数 this 取决于上下文
       
        var obj = {
        fn1: function () { console.log(this) },
        fn2: () => {
            /**
             *  箭头函数内部是没有 this 的, 你在这个位置使用的 this
             *  就相当于找到了上一层级最近的一个 this
             */
            console.log(this)
        }
    }
    obj.fn1()   // this === obj
    obj.fn2()   // this === window
    
    
                       /**

* 解构赋值

                        */

                        const obj = {
                            name: 'zs',
                            age: 18,
                            abc: 'QF001'
                        }



                        const { name, abc } = obj

                        console.log(name)
                        console.log(abc)





                        const arr = [1, 2, 3, 4, 5]
                        //           0  1  2  3  4

                        const [a, b, c, q, w, e] = arr


                        console.log(a)
                        console.log(b)
                        console.log(c)
                        console.log(q)
                        console.log(w)
                        console.log(e) // 没有值
                        
                        
                        
 /**
     *  展开运算符
     * 
     *      ...
     * 
     *  一般用于 合并数组, 或者 函数传参
    */
    
    
   // 函数 --- 形参
    function fn(arg) {
        console.log(arg)
    }
    fn()    // undefined
    fn(100)    // 100
    fn(100, 200, 300)    // 100



    function fn(...arg) {
        console.log(arg)
    }

    fn()    // []
    fn(100) // [100]
    fn(100, 200, 300, 400, 500) // [100, 200, 300, 400, 500]
    
    
      // 对象
    const obj = {
        a: 1,
        b: 2,
        c: 3
    }

    
    const obj1 = {
        ...obj,
        a: 100,
        b: 200,
        q: 300,
        w: 400,
    }
    console.log(obj)
    console.log(obj1)


   // 数组
    const arr = [1, 2, 3]
    console.log(arr)
    console.log(...arr)
    const arr1 = [100, 200, 300, ...arr]
    console.log(arr1)
    
    
    
    
    

* Map

     *
     *      是 ES6 新增的一个数据结构, 类似于 对象, 但不是一个东西
     */

    // 创建了一个 空的 map 集合
    const oMap = new Map()
    // console.log(oMap)

    // 1. 向 map 中 添加数据   set()

    // 下边的三行代码也能执行, 但是不建议, 如果这样写和对象没有什么区别, 建议使用对象
    oMap.set('key', 'value')
    oMap.set('age', 18)
    oMap.set('name', '张三')

    // map 数据中, key 的值类型是没有限制的, 但是对象中, 只能是字符串
    const arr = [1, 2, 3]
    oMap.set(arr, [4, 5, 6])
    console.log(oMap)




    oMap.forEach(function (value, key, origin) {
        console.log(value, key)
    })




    // 4. delete()  删除数据中的某一个
    // oMap.delete('key')


    // 5. clear()   清空数据集合
    // oMap.clear()
    // console.log(oMap)



    // 3. has() 判断数据中是否拥有指定的 key

    // console.log(oMap.has('name'))   // true
    // console.log(oMap.has('qwe'))    // false
 
    
    
    // 2. 获取 map 中的一些数据
    // console.log(oMap.get('age'))
    // console.log(oMap.get('name'))
    // console.log(oMap.get(arr))
    
    
  

* Set

     * 
     *      ES6 新增的一个数据结构, 类似于 数组
     * 
     *      SetMap 都有一个特点, 内部不允许有重复的数据
     *      所以就有人利用 Set 完成数组去重
    */



    // 利用 set 完成数组去重
    // const arr = new Set([100, 100, 2, 2, 3, 3, 4, 4, 5, 5])
    // console.log(arr)    // Set(5) {100, 2, 3, 4, 5}
    // console.log(...arr)    // 100 2 3 4 5
    // console.log([...arr])    // [100, 2, 3, 4, 5]

    const arr = [...new Set([100, 100, 2, 2, 3, 3, 4, 4, 5, 5])]
    console.log(arr)    // [100, 2, 3, 4, 5]




    // const oSet = new Set([100, 100, 2, 2, 3, 3, 4, 4, 5, 5])
    // console.log(oSet[0]) // set 不允许我们通过 下标访问内部的数据



    // oSet.forEach(function (item, index, origin) {
    //     /**
    //      *  item 和 origin 和 数组中 forEach 的含义是相同的
    //      * 
    //      *  但是 index 不同, 因为 set 中 不允许使用 下标获取到内部的数据
    //      * 
    //      *  所以 index 的值不是下标, 而是数组的每一个值
    //      * 
    //      *  所以 在当前方法中, item 和 index 的值 永远相同
    //     */
    //     console.log(item, index, origin)
    // })


    // 3. delete 删除某一个
    // oSet.delete(1)
    // oSet.delete(2)
    // oSet.delete(3)
    // oSet.delete(4)
    // oSet.delete(5)

    // 4. clear()   清空数据集合
    // oSet.clear()



    // 2. 判断数据集合中是否拥有这个数据
    // console.log(oSet.has(5))
    // console.log(oSet.has(500))


    // 1. 向集合中 添加数据
    // oSet.add(10086)
    // oSet.add('QF001')

    // oSet.get()  // oSet.get is not a function        set 数据集合没有提供 get 方法


    // console.log(oSet)
    
    
    
    
    
    
    
    
       /**

* 对象的简写语法

    */

    const a = 100


    // 对象中如果一个属性的属性值是一个函数, 那么我们可以有一个简化的写法
    const obj = {
        fn1: function () {
            console.log(123)
        },
        fn2 () {
            console.log(456)
        }
    }

    obj.fn1()
    obj.fn2()





    // 现在对象中有一个属性和属性值是相同的拼写, 并且 属性值是一个变量, 那么此时可以有一个简化的写法
    
    
    // 1. 提前获取到要发送给后端的一些数据, 比如说账号和密码
    // const username = 'QF001'
    // const password = '123456'

    // 2. 将收集好的信息组装成一个对象, 发送给后端
    // const userInfo = {
    //     username: username,
    //     password: password
    // }
    // const userInfo = {
    //     username,
    //     password
    // }

    
    
    // const obj = {
    //     name: '张三',
    //     // a: a
    //     a,
    //     b: 'QF001'
    // }
    // console.log(obj)
    
    
    

* 作用域

     *
     *      就是一个变量生效的范围
     *
     *
     *      * 全局作用域
     *              在 script 标签中直接书写的变量或者函数, 就是在全局作用域书写的
     *              那么在这个文件中的任意位置, 都可以使用
     * 
     *      * 局部/函数 作用域
     *              在函数内部会有一个独立的作用域, 这个作用域我们称之为 局部/函数 作用域
     *              在当前作用域声明的变量或者函数, 一旦超过了当前作用域的范围, 那么就不能使用了
     * 
     *          * 块级作用域
     *              必须使用 let 或者 const 声明变量的时候, 才会出现块级作用域
     *              {}      之内的区域就是块级作用域
     *              在当前作用域声明的变量或者函数, 一旦超过了当前作用域的范围, 那么就不能使用了
     * 
     *          * 文件作用域
     *              必须使用模块化开发才能出现所谓的文件作用域
     *              每一个文件都是一个独立的作用域, 不能访问文件之外的其他文件内的变量或者函数
     *              除非利用模块化的导入导出
     * 
     * 
     * 
     *  作用域链
     *      是一个纯虚构的概念性知识点
     *
     *      访问一个变量的时候会在当前作用域先查找访问, 如果有直接使用, 并停止查找
     *      如果没有会继续去上一层作用域查找, 直到找到为止或者找到全局
     *      这个层层链接的结构, 我们起了个名字叫做作用域链
     *
     *      1. 查找规则
     *          会在当前作用域先查找访问, 如果有直接使用, 并停止查找
     *          如果没有会继续去上一层作用域查找, 直到找到为止或者找到全局
     *          如果全局也没有, 那么会报错
     *
     *      2. 赋值规则
     *          会在当前作用域先查找访问, 如果有直接使用, 并停止查找
     *          如果没有会继续去上一层作用域查找, 直到找到为止或者找到全局
     *          如果全局也没有, 那么会在全局定义一个变量, 并赋值
     * 
     * 
     *  将来还会有一个 原型链
    */

* 预解析

     * 
     *      JS 在运行前 会整体读取一遍我们的代码
     * 
     *      这个一遍读取, 会将我们的变量声明和函数声明, 提升到页面最开始的为止(提升到当前作用域最开始的位置)
     *              切记: 变量声明只会发生在 利用 var 声明的时候, 如果使用的是 let 或者 const 那么不会有所谓的变量提升
     * 
     *      变量提升
     *          只会提升变量的定义/声明, 但是变量的赋值不会提升, 也就是说我们能够在 变量定义前使用变量, 但是没有值, 实际的值是一个 undefined
     * 
     *      函数提升
     *          会将函数提升到当前作用域的最顶端, 所以我们可以在函数定义前去调用
     * 
     *      在 JS 中 函数是一等公民
    */


    // 演示 1
    // console.log(a)   // a is not defined
    
    // 演示 2
    // console.log(a)  // undefined
    // var a = 100

    // 演示 3
    // console.log(a)  // Cannot access 'a' before initialization
    // let a = 100