06 - ECMAScript 6 - 01. 作用域、函数进阶、解构赋值、数组方法、综合案例

ECMAScript 6

作用域

局部作用域

1. 函数作用域
  • 函数内部声明的变量只能在函数内部被访问,外部无法直接访问
    ① 函数的参数也是函数内部的局部变量
    ② 不同函数内部声明的变量无法互相访问
    ③ 函数执行完毕后,函数内部的变量实际被清空了
2. 块作用域
  • JS 中使用 {} 包裹的代码称为代码块,代码块内部声明的变量外部将 有可能 无法被访问
    let、const 声明的变量 产生块作用域,var 声明的变量 不会 产生块作用域
    ② 不同代码块之间的变量无法互相访问
    ③ 推荐使用 letconst

全局作用域

  • <script> 标签.js 文件 的【最外层】就是全局作用域,在此声明的变量在函数内部也可以被访问。全局作用域中声明的变量,任何其它作用域都可以被访问
    ① 为 window 对象动态添加的属性默认也是全局的【不推荐】
    ② 函数中未使用任何关键字声明的变量为全局变量【不推荐】
    ③ 尽可能少的声明全局变量,防止全局变量被污染

作用域链

1. 本质
  • 底层的 变量查找机制
    ① 函数被执行时,优先查找当前 函数作用域中查找变量
    ② 若当前作用域查找不到,则会依次 逐级查找父级作用域 直到全局作用域
2. 总结
  • 嵌套关系的作用域串联起来形成了作用域链
  • 相同作用域链中按着从小到大的规则查找变量
  • 子作用域能访问父作用域,父作用域无法访问子作用域

JS 垃圾回收机制

1. 垃圾回收机制(Garbage Collectio,GC)
  • JS 中 内存 的分配和回收都是 自动完成 的,内存在不使用的时候会被 垃圾回收器 自动回收
2. 内存的生命周期
  1. 内存分配:声明变量、函数、对象时,系统会自动分配内存
  2. 内存使用:即读写内存,也就是使用变量、函数等
  3. 内存回收:使用完毕,由 垃圾回收器 自动回收不再使用的内存
3. 说明
  • 全局变量一般不会回收【关闭页面回收】
  • 一般情况下,局部变量的值不用了,会被自动回收掉
4. 内存泄漏

程序中分配的内存由于某种原因,程序 未释放无法释放

5. 算法说明
  • 堆栈空间分配区别
    1. 栈(OS):由 OS自动分配释放 函数的参数值、局部变量等【基本数据类型 放到栈中】
    2. 堆(OS):一般由程序员分配释放,若程序员不释放,由 GC 回收【复杂数据类型 放到堆中】
  • 两种常见的浏览器垃圾回收算法
    引用计数法、标记清除法
  • 引用计数法【IE 采用】【定义“内存不再使用”,是看某对象是否有指向它的引用,若无则回收对象】
    1. 算法
      ① 跟踪记录被引用的次数
      ② 若被引用了一次,则记录次数为1,多次引用则 ++
      ③ 若减少一个引用,则 –
      ④ 若引用次数为0,则释放内存
    2. 缺点:嵌套引用(循环引用)
      若两对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄漏
      function fn() {
      	let o1 = {}
      	let o2 = {}
      	o1.a = o2
      	o2.a = o1
      	return '引用计数无法回收'
      }
      fn()
      
  • 标记清除法
    1. 算法【现今常用】【将“不再使用的对象”,定义为“无法达到的对象”】
      ① 从 根部(即 JS 中的全局对象)出发定时扫描内存中的对象
      ② 凡是能从 根部到达 的对象,都是 还需使用
      ③ 那些 无法 由根部出发触及到的 对象被标记 为不再使用,稍后进行 回收

闭包

  • 使用闭包函数创建隔离作用域避免全局变量污染

  • 概念:闭包 = 内存函数 + 外层函数的变量
    一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域

  • 作用:封闭数据、提供操作,外部也可以访问函数内部的变量

  • 缺陷:可能造成 内存泄漏

  • 应用:实现数据的私有

  • 格式:

    function outer() {
        let a = 100
        function fn() {
            console.log(a)
        }
        return fn
    }
    /* 等价于
    function outer() {
        let a = 100
        return function() {
            console.log(a)
        }
    }
    */
    
    // outer() === fn === function fn() {}
    const fun = outer()
    fun()
    
  • 案例 - 统计函数调用次数【可能造成 内存泄漏

    // // 问题:i是全局变量,易被修改
    // let i = 0
    // function fn() {
    //     i++
    //     console.log(`函数被调用了${i}次`)
    // }
    
    function count() {
        let i = 0
        function fn() {
            i++
            console.log(`函数被调用了${i}`)
        }
        return fn
    }
    
    const fun = count()  // 全局,不会被回收,除非页面被关掉
    

    在这里插入图片描述 在这里插入图片描述

  • 示例
    在这里插入图片描述
    在这里插入图片描述

变量提升

  • 变量提升仅存在于 var 声明的变量
  • 示例
    // 1. 把所有 var 声明的变量提升到当前作用域的最前面
    // 2. 只提升声明,不提升赋值
    console.log(num + ' 件')  // undefined 件
    var num = 10
    
    // 等价于以下代码
    // var num
    // console.log(num + ' 件')
    // num = 10
    
  • 注意
    ① 变量在未声明即被访问时会报语法错误;变量在 var 声明之前即被访问,变量的值为 undefined
    let / const 声明的变量不存在变量提升
    ③ 变量提升出现在相同作用域当中
    ④ 实际开发中推荐先声明再访问变量【推荐使用 let / const】

函数进阶

函数提升

  • 函数在声明之前即可被调用
  • 示例
    // 1. 把所有 函数 声明提升到当前作用域的最前面
    // 2. 只提升声明,不提升赋值
    fun()
    var fun = function () {
        console.log('函数表达式不存在提升现象')
    }
    
    // 3. 函数表达式 必须先声明和赋值,后调用,否则报错
    // 等价于以下代码
    // var fun
    // fun()
    // fun = function () {
    //     console.log('函数表达式不存在提升现象')
    // }
    

函数参数

1. 动态参数
  • arguments 是函数内部内置的伪数组变量,包含了调用函数时传入的所有实参
  • 总结
    arguments 是一个 伪数组,只存在于函数中【出了函数作用域无法打印】
    arguments 的作用是动态获取函数的实参
    ③ 可通过 for 循环依次得到传递过来的实参
  • 语法
    function getSum() {
        // console.log(arguments)
        let sum = 0
        for (let i = 0; i < arguments.length; i++) {
            sum += arguments[i]
        }
        console.log(sum)
    }
    getSum(2, 3, 4)
    
2. 剩余参数【推荐使用】
  • 剩余参数允许将一个不定数量的参数表示为一个数组
  • ... 是语法符号,置于最末函数形参之前,用于获取 多余 的实参
    借助 ... 获取的剩余实参,是个 真数组【有 pop()、push() 等方法】
  • 语法
    function getSum(a, b, ...arr) {  // 至少两个形参
        // console.log(arr)  // 使用的时候不用加 ...
        let sum = a + b
        for (let i = 0; i < arr.length; i++) {
            sum += arr[i]
        }
        console.log(sum)
    }
    getSum(2, 3, 4)
    
3. 展开运算符
  • 展开运算符 ... 将一个数组进行展开
  • 说明
    ① 不会修改原数组
    ② 典型运用场景:求数组最大值 / 最小值、合并数组等
  • 语法
    const arr1 = [1, 2, 3]
    // console.log(...arr1)  // 1 2 3
    // console.log(Math.max(1, 2, 3))  // 3
    
    // 1. 求数组最大值
    // 实际:...arr1 === 1, 2, 3
    console.log(Math.max(...arr1))  // 3
    
    // 2. 合并数组
    const arr2 = [3, 4, 5]
    const arr = [...arr1, ...arr2]
    console.log(arr)  // [1, 2, 3, 3, 4, 5]
    
4. 剩余参数 vs 展开运算符
  • 剩余参数:函数参数中使用,得到真数组【[1, 2, 3]】
  • 展开运算符:数组中使用,数组展开【1 2 3】

箭头函数

  • 目的:比函数表达式更简短的函数写法,并且不绑定 this
  • 使用场景:更适用于那些本来需要匿名函数的地方
1. 基本语法
  • 语法
    ① 箭头函数只有一个参数时,可省略小括号 ()【无参时,不可省略小括号】
    ② 箭头函数函数体只有一行代码时,可省略花括号 {},写到一行上;且自动做为返回值被返回,无需写 return
    加括号 的函数体返回对象字面量表达式
    注:箭头函数属于表达式函数,故 不存在函数提升
  • 语法
    function fn() {
        console.log(123)
    }
    
    const fun1 = function () {  // 函数表达式
        console.log(456)
    }
    
    const fun2 = () => {  // 匿名 箭头函数
        console.log(789)
    }
    fun2()  // 789
    
    const fun3 = (x) => {
        console.log(x)
    }
    fun3(1)  // 1
    
    const fun4 = x => {  // 只有一个形参时,可以省略小括号
        console.log(x)
    }
    fun4(2)  // 2
    
    const fun5 = x => console.log(x)  // 只有一行代码时,可以省略大括号
    fun5(3)  // 3
    
    const fun6 = x => x + x  // 只有一行 return 代码时,可以省略 return
    console.log(fun6(4))  // 8
    
    const fun7 = (uname) => ({ uname: uname })  // 箭头函数可以直接返回一个对象【本来应是花括号包花括号,但因冲突,故变成小括号包花括号】
    console.log(fun7('刘德华'))  // Object
    
2. 箭头函数参数
  • 普通函数有 arguments 动态参数
  • 箭头函数无 arguments 动态参数,但有剩余参数 ...args
    const getSum = (...arrs) => {
    	let sum = 0
    	for (let i = 0; i < arrs.length; i++) {
    		sum += arrs[i]
    	}
    	return sum
    }
    const res = getSum(2, 3)
    console.log(res)  // 5
    
3. 箭头函数 this
  • 箭头函数出现之前,每一个新函数根据它是 被如何调用的 来定义这个函数的 this 值,很麻烦
    箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层沿用 this
  • 开发中,使用箭头函数前需要考虑函数中 this 的值,事件回调函数使用箭头函数时,this 为全局的 window。故 DOM 事件回调函数为了简便,不太推荐使用箭头函数
    <body>
        <button id="btn">button</button>
        <script>
            const btn = document.querySelector("#btn")
            btn.addEventListener('click', () => {
                console.log(this)  // Window
            })
    
            btn.addEventListener('click', function () {
                console.log(this);  // btn
            })
        </script>
    </body>
    
  • 示例
    <body>
        <button id="btn">button</button>
        <script>
            console.log(this)  // Window
    
            function fn1() {
                console.log(this)  // Window
            }
            fn1()  // this 指向 函数的调用者,实际写法是 window.fn()
    
            const obj1 = {
                name: 'andy',
                sayHi: function () {
                    console.log(this);  // obj1
                }
            }
            obj1.sayHi()
    
    
            const fn2 = () => {
                console.log(this);  // Window
            }
            fn2()  // 箭头函数无 this,故向作用域链上一层找,即 script 里的 this,即 Window
    
            let btn = document.querySelector("#btn")
            btn.addEventListener('click', () => {
                console.log(this)  // Window
            })
    
            const obj2 = {
                name: 'bob',
                sayHi: () => {
                    console.log(this)  // Window
                }
            }
            obj2.sayHi()  // 箭头函数无 this,故向作用域链上一层找,即 obj2 里的 this,即 Window.obj2()
    
            const obj3 = {
                name: 'cindy',
                sayHi: () => {
                    let i = 10
                    const count = () => {
                        console.log(this)  // Window
                    }
                    count()
                }
            }
            obj3.sayHi()
    
            const obj4 = {
                name: 'david',
                sayHi: function () {
                    let i = 10
                    const count = () => {
                        console.log(this)  // obj4
                    }
                    count()
                }
            }
            obj4.sayHi()
        </script>
    </body>
    

解构赋值

  • 解构赋值是一种快速为变量赋值的简洁语法,本质上仍是为变量赋值

数组解构

1. 数组解构
  • 将数组的单元值快速、批量赋值给一系列变量的简洁语法
  • 基本语法
    ① 赋值运算符 = 左侧的 [] 用于批量声明变量,右侧 数组的单元值 将被赋值给左侧的变量
    ② 变量的顺序对应数组单元值的位置依次进行赋值操作
  • 语法【变量数量 === 单元值数量】
    const arr = [100, 60, 80]
    const [max, min, avg] = arr  // 数组解构
    // 等价于
    // const max = arr[0]
    // const min = arr[1]
    // const avg = arr[2]
    
    console.log(max, min, avg);  // 100 60 80
    
    语法【变量数量 > 单元值数量】
    const [a, b, c, d] = [1, 2, 3]
    console.log(a, b, c, d);  // 1 2 3 undefined
    
    语法【变量数量 < 单元值数量】
    const [e, f] = [4, 5, 6]
    console.log(e, f);  // 4 5
    
    const [e, f, ...g] = [4, 5, 6, 7]
    console.log(e, f);  // 4 5
    console.log(g)  // [6, 7],真数组
    
    语法【防止 undefined 传递,可设置默认值(只有单元值为 undefined 时默认值才会生效)】
    const [e = 0, f = 0] = [4]
    console.log(e, f);  // 4 0
    
    语法【按需导入,忽略某些返回值】
    const [a, b, , d] = [1, 2, 3, 4]
    console.log(a, b, d)  // 1 2 4
    
    语法【支持多维数组的结构】
    const arr = [1, 2, [3, 4]]
    console.log(arr[0], arr[1], arr[2][0], arr[2][1])  // 1 2 3 4
    
    const [a, b, [c, d]] = [1, 2, [3, 4]]
    console.log(a, b, c, d)  // 1 2 3 4
    
  • 案例 - 交换两个变量
    let a = 1
    let b = 2;  // 这里必须有分号
    [b, a] = [a, b]
    // 或 [a, b] = [b, a]
    console.log(a, b)  // 2 1
    
2. js 前必须加分号的情况
  1. 立即执行函数
    (function t() {})();  // 法一
    ;(function t() {})()  // 法二
    
  2. 数组开头的,特别是前面有语句的
    ;[b, a] = [a, b]  // 数组开头的,特别是前面有语句的,一定注意加分号
    
    const str = 'pink';  // 此处必须要加分号
    [1, 2, 3].map(function (item) {
        console.log(item)  // 1 2 3
    })
    

对象解构

  • 将对象属性和方法快速、批量赋值给一系列变量的简洁语法
  • 基本语法
    ① 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧 对象的属性值 将被赋值给左侧的变量
    ② 对象属性的值将被赋值给与属性名 相同 的变量
    ③ 注意解构的变量名不要和外面的变量名冲突,否则报错
    ④ 对象中找不到与变量名一致的属性时,变量值为 undefined
  • 语法【属性名和变量名必须一致】
    const { uname, age } = { uname: 'andy', age: 18 }  // 属性名和变量名必须一致
    // 等价于 const uname = obj.uname
    
    console.log(uname, age);  // andy 18
    
    语法【给新的变量名赋值【旧变量名: 新变量名】】【可从一个对象中提取变量并同时修改新的变量名】
    const uname = 'andy'
    const { uname: username, age } = { uname: 'bob', age: 18 }  // 对象解构的变量名可以重新改名
    
    console.log(username, age)  // bob 18
    
    语法【数组对象解构】
    const pig = [
        {
            uname: '佩奇',
            age: 18
        }
    ]
    const [{ uname, age }] = pig
    
    console.log(uname, age)  // 佩奇 18
    
    语法【多级对象解构】
    const pig = {
        name: '佩奇',
        family: {
            mother: '猪妈妈',
            father: '猪爸爸',
            brother: '乔治'
        },
        age: 6
    }
    const { name, family: { mother, father, brother } } = pig
    
    console.log(name, mother, father, brother);  // 佩奇 猪妈妈 猪爸爸 乔治
    
    const pig = [
        {
            name: '佩奇',
            family: {
                mother: '猪妈妈',
                father: '猪爸爸',
                brother: '乔治'
            },
            age: 6
        }
    ]
    const [{ name, family: { mother, father, brother } }] = pig
    
    console.log(name, mother, father, brother);  // 佩奇 猪妈妈 猪爸爸 乔治
    

数组方法

遍历数组方法

1. forEach()
  • 用于调用 数组 的每个元素,并将元素传递给回调函数
    ① 主要遍历数组【适合于遍历 数组对象】,不遍历对象
    ② 参数当前数组元素必写,索引号可选
    ③ 只遍历,不返回数组【map 会返回数组】
  • 语法
    被遍历的数组.forEach(function(当前数组元素, [当前元素索引号]){
    })
    
  • 示例
    const arr = ['red', 'orange', 'green']
    const result = arr.forEach(function (item, index) {
        console.log(item)  // red orange green
        console.log(index)  // 0 1 2
    })
    console.log(result)  // undefined
    
2. map()
  • 按顺序,映射 遍历处理数组里每个值,并返回 新数组
  • 语法
    被遍历的数组.map(function(val, index){
    	return 新数组
    })
    
  • 示例
    let arr1 = ['小明', '小红', '小刘', '小李', '小赵']
    let newArr1 = arr1.map(val => {
        return '张' + val
    })
    console.log(newArr1)  // ['张小明', '张小红', '张小刘', '张小李', '张小赵']
    
    let arr2 = [
        {
            name: "小明",
            age: 19
        }, {
            name: "小红",
            age: 10
        }, {
            name: "小青",
            age: 39
        }, {
            name: "小爱",
            age: 15
        },
    ]
    let newArr2 = arr2.map(obj => {
        return {
            name: obj.name,
            age: obj.age,
            iq: Math.floor(Math.random() * (100 - 50 + 1) + 50)  // 值为 50~100 的随机整数
        }
    })
    console.log(arr2)
    console.log(newArr2)
    
    let newArr3 = arr2.map(obj => {
        return {
            name: obj.name
        }
    })
    console.log(newArr3)
    

筛选数组方法

filter()
  • 创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
    ① 会返回筛选后元素的新数组。若无符合条件的元素,则返回空数组
    ② 参数当前数组元素必写,索引号可选
    ③ 因为返回新数组,故不会影响原数组
  • 语法
    被遍历的数组.filter(function(当前数组元素, [当前元素索引号]){
    	return 筛选条件
    })
    
  • 示例
    const score = [10, 50, 3, 40, 33]
    const res = score.filter(function (item) {
        return item > 30
    })
    console.log(res)  // [50, 40, 33]
    

综合案例

  • 示例
    const msg = {
        "code": 200,
        "msg": "获取新闻列表成功",
        "data": [
            {
                "id": 1,
                "title": "5G商用自己,三大运用商收入下降",
                "count": 58
            },
            {
                "id": 2,
                "title": "国际媒体头条速览",
                "count": 56
            },
            {
                "id": 3,
                "title": "乌克兰和俄罗斯持续冲突",
                "count": 1669
            }
        ]
    }
    // 需求1:只选出 data 数据
    // const { data } = msg
    // console.log(data)  // Array(3)
    
    // 需求2:把 data 选出当作参数,传递给函数
    // function render({ data }) {
    //     console.log(data);  // Array(3)
    // }
    // render(msg)
    
    // 需求3:将 render() 中的数据名改为 myData
    function render({ data: myData }) {
        console.log(myData);  // Array(3)
    }
    render(msg)
    
  • 案例 - 筛选后渲染页面
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <style>
            .filter a:active,
            .filter a:focus {
                background: #05943c;
                color: #fff;
            }
        </style>
    </head>
    
    <body>
        <div class="filter">
            <a data-index="1" href="javascript:;">0-100元</a>
            <a data-index="2" href="javascript:;">100-300元</a>
            <a data-index="3" href="javascript:;">300元以上</a>
            <a href="javascript:;">全部区间</a>
        </div>
        <div class="list">
            <!-- <div class="item">
                <img src="" alt="">
                <p class="name"></p>
                <p class="price"></p>
            </div> -->
        </div>
        <script>
            const goodsList = [
                {
                    id: '4001172',
                    name: '称心如意手摇咖啡磨豆机咖啡豆研磨机1',
                    price: '100.00',
                    picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
                },
                {
                    id: '4001173',
                    name: '称心如意手摇咖啡磨豆机咖啡豆研磨机2',
                    price: '150.00',
                    picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
                },
                {
                    id: '4001173',
                    name: '称心如意手摇咖啡磨豆机咖啡豆研磨机3',
                    price: '291.00',
                    picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
                },
                {
                    id: '4001173',
                    name: '称心如意手摇咖啡磨豆机咖啡豆研磨机4',
                    price: '292.00',
                    picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
                },
                {
                    id: '4001173',
                    name: '称心如意手摇咖啡磨豆机咖啡豆研磨机5',
                    price: '293.00',
                    picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
                },
                {
                    id: '4001173',
                    name: '称心如意手摇咖啡磨豆机咖啡豆研磨机6',
                    price: '299.00',
                    picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
                },
                {
                    id: '4001173',
                    name: '称心如意手摇咖啡磨豆机咖啡豆研磨机7',
                    price: '300.00',
                    picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
                },
                {
                    id: '4001173',
                    name: '称心如意手摇咖啡磨豆机咖啡豆研磨机8',
                    price: '400.00',
                    picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
                },
            ]
            function render(arr) {
                let str = ''
                arr.forEach(item => {
                    const { name, picture, price } = item
                    str += `
                        <div div class="item" >
                            <img src=${picture} alt="">
                            <p class="name">${name}</p>
                            <p class="price">${price}</p>
                        </div>
                        `
                })
                document.querySelector('.list').innerHTML = str
            }
            render(goodsList)  // 页面一打开就进行渲染
    
            document.querySelector('.filter').addEventListener('click', e => {
                const { tagName, dataset } = e.target
                if (tagName === 'A') {
                    let arr = goodsList
                    if (dataset.index === '1') {
                        arr = goodsList.filter(item => item.price > 0 && item.price <= 100)
                    } else if (dataset.index === '2') {
                        arr = goodsList.filter(item => item.price >= 100 && item.price <= 300)
                    } else if (dataset.index === '3') {
                        arr = goodsList.filter(item => item.price >= 300)
                    }
                    render(arr)
                }
            })
        </script>
    </body>
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值