1.JavaScript 高级第一天 作用域&解构&箭头函数

一、作用域

1.作用域

目标:了解作用域对程序执行的影响及作用域链的查找机制,使用闭包函数创建隔离作用域避免全局变量污染。

  • 作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问,
  • 作用域分为:
    局部作用域
    全局作用域
1.1 局部作用域

局部作用域分为函数作用域块作用域

1.函数作用域

在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。

总结:
1.函数内部声明的变量,在函数外部无法被访问
2.函数的参数也是函数内部的局部变量
3.不同函数内部声明的变量无法互相访问
4.函数执行完毕后,函数内部的变量实际被清空了

2. 块作用域

在 JavaScript 中使用 { } 包裹的代码称为代码块,代码块内部声明的变量外部将【有可能】无法被访问。

总结:
1.let 声明的变量会产生块作用域,var 不会产生块作用域
2.const 声明的常量也会产生块作用域
3.不同代码块之间的变量无法互相访问
4.推荐使用 let 或 const

<body>
    <script>
        // 两个块作用域互不影响,所以变量可以相同
        for (let i = 1; i <= 3; i ++){
            // 块作用域
            console.log(i)
        }
        for (let i = 1; i <= 3; i ++){
            // 块作用域
            console.log(i)
        }
    </script>
</body>
总结
  1. 局部作用域分为哪两种?
    函数作用域 函数内部
    块级作用域 {}
  2. 局部作用域声明的变量外部能使用吗?
    不能

1.2 全局作用域

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

1.3 作用域链

在这里插入图片描述
答案:2

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

1.4 JS垃圾回收机制

1.什么是垃圾回收机制?
  • 垃圾回收机制(Garbage Collection) 简称 GC
  • JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
  • 正因为垃圾回收器的存在,许多人认为JS不用太关心内存管理的问题
    但如果不了解JS的内存管理机制,我们同样非常容易成内存泄漏(内存无法被回收)的情况
  • 不再用到的内存,没有及时释放,就叫做内存泄漏
2.内存的生命周期

JS环境中分配的内存, 一般有如下生命周期:
1.内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
2.内存使用:即读写内存,也就是使用变量、函数等
3.内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存

  • 说明:
    全局变量一般不会回收(关闭页面回收);
    一般情况下局部变量的值, 不用了, 会被自动回收掉
3.拓展-JS垃圾回收机制-算法说明

堆栈空间分配区别:
(1)栈(操作系统): 由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面。
(2)堆(操作系统): 一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里面。
下面介绍两种常见的浏览器垃圾回收算法: 引用计数法标记清除法

1.引用计数

(1)IE采用的引用计数算法, 定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象
算法:
1.跟踪记录被引用的次数
2.如果被引用了一次,那么就记录次数1,多次引用会累加 ++
3.如果减少一个引用就减1 –
4.如果引用次数是0 ,则释放内存

(2)引用计数 计算

次数:0

次数:0
(3)存在的问题
但它却存在一个致命的问题:嵌套引用(循环引用)
如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。
因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露

2.标记清除法

现代的浏览器已经不再使用引用计数算法了。
现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。
核心:
1.标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
2.就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。 凡是能从根部到达的对象,都是还需要使用的。
3.那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收

(在函数里面找不到)

1.5 闭包

目标: 能说出什么是闭包,闭包的作用以及注意事项
概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
简单理解:闭包 = 内层函数 + 外层函数的变量
先看个简单的代码:

闭包作用:封闭数据,提供操作,外部也可以访问函数内部的变量
闭包的基本格式:

闭包应用:实现数据的私有
比如,我们要做个统计函数调用次数,函数调用一次,就++

总结

1.怎么理解闭包?
闭包 = 内层函数 + 外层函数的变量
2.闭包的作用?
封闭数据,实现数据私有,外部也可以访问函数内部的变量
闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来
3.闭包可能引起的问题?
内存泄漏

1.6 变量提升

目标:了解什么是变量提升
变量提升是 JavaScript 中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)
注意:
1.变量在未声明即被访问时会报语法错误
2.变量在var声明之前即被访问,变量的值为 undefined
3.let/const 声明的变量不存在变量提升
4.变量提升出现在相同作用域当中
5.实际开发中推荐先声明再访问变量

<script>
        // 1.把所有var声明的变量提升到 当前作用域的最前面
        // 2.只提升声明,不提升赋值
        // var num
        console.log(num + '件')
        var num = 10  //undefined件
    </script>

说明:
JS初学者经常花很多时间才能习惯变量提升,还经常出现一些意想不到的bug,正因为如此,ES6 引入了块级作用域,
用let 或者 const声明变量,让代码写法更加规范和人性化。

二、函数进阶

2.1 函数提升

目标:能说出函数提升的过程
函数提升与变量提升比较类似,是指函数在声明之前即可被调用。

总结:
1.函数提升能够使函数的声明调用更灵活
2.函数表达式不存在提升的现象
3.函数提升出现在相同作用域当中

<body>
    <script>
        // 1.会把所有函数声明提升到当前作用域的最前面
        // 2.只提升函数声明,不提升函数调用
        fn()
        function fn() {
            console.log('函数提升')
        }

        // 函数表达式 必须先声明和赋值, 后调用 否则报错
        fun()
        var fun = function() {
            console.log('函数表达式')
        }
    </script>
</body>

2.2 函数参数

函数参数的使用细节,能够提升函数应用的灵活度。
学习路径:(形参、实参)
1.动态参数
2.剩余参数
产品需求: 写一个求和函数
不管用户传入几个实参,都要把和求出来

形参我改咋写?

1.动态参数

arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参

总结:
1.arguments 是一个伪数组,只存在于函数中
2.arguments 的作用是动态获取函数的实参
3.可以通过for循环依次得到传递过来的实参
ps:概念:伪数组是一个 Object,而真实的数组是一个 Array,具体的来说:伪数组是一个对象、具有length属性、按照索引方式存储数据、不具有数组的push,pop等方法…

<body>
    <script>
        function getSum(){
            // arguments 动态参数 只存在于 函数里面
            // 是伪数组
            // console.log(arguments)
            let sum = 0
            for(let i = 0; i < arguments.length; i ++) {
                sum += arguments[i]
            }
            console.log(sum)
        }
        getSum(2, 3, 4)
        getSum(2, 3, 4, 2, 3, 4)
    </script>
</body>
2.剩余参数

目标: 能够使用剩余参数
剩余参数允许我们将一个不定数量的参数表示为一个数组

(使用的时候不用…)
那和arguments 有什么不同吗?
1… 是语法符号,置于最末函数形参之前,用于获取多余的实参
2.借助 … 获取的剩余实参,是个真数组

开发中,还是提倡多使用 剩余参数

3. 总结

1.剩余参数主要的使用场景是?
用于获取多余的实参
2.剩余参数和动态参数区别是什么?开发中提倡使用哪一个?
动态参数是伪数组
剩余参数是真数组
开发中使用剩余参数想必也是极好的

4.拓展:展开运算符

目标:能够使用展开运算符并说出常用的使用场景
展开运算符(…),将一个数组进行展开

说明:
1.不会修改原数组
典型运用场景: 求数组最大值(最小值)、合并数组等

<body>
    <script>
        const arr = [1, 2, 3]
        // 展开运算符 可以展开数组
        console.log(...arr)  //1 2 3
        // 1.求数组最大值
        console.log(Math.max(...arr))  //3
        console.log(Math.min(...arr))  //1
        // 2.合并数组
        const arr2 = [3, 4, 5]
        const ARR = [...arr,...arr2]
        console.log(ARR)
    </script>
</body>

展开运算符 or 剩余参数
剩余参数:函数参数使用,得到真数组
展开运算符:数组中使用,数组展开

2.3 箭头函数(重要)

目标: 能够熟悉箭头函数不同写法
目的:引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁
使用场景:箭头函数更适用于那些本来需要匿名函数的地方

1.语法

语法1:基本写法

语法2:只有一个参数可以省略小括号

语法3:如果函数体只有一行代码,可以写到一行上(省略大括号),并且无需写 return 直接返回值


语法4:加括号的函数体返回对象字面量表达式

2. 箭头函数参数

1.普通函数有arguments 动态参数
2.箭头函数没有 arguments 动态参数,但是有剩余参数…args

3. 箭头函数 this

在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值, 非常令人讨厌。
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。



在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window,因此
DOM事件回调函数为了简便,还是不太推荐使用箭头函数

4.总结

1.箭头函数里面有this吗?
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this
2.DOM事件回调函数推荐使用箭头函数吗?
不太推荐,特别是需要用到this的时候
事件回调函数使用箭头函数时,this 为全局的 window

三、解构赋值

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

3.1 数组解构

数组解构是将数组的单元值快速批量赋值给一系列变量简洁语法
基本语法:
1.赋值运算符 = 左侧的 [] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
2.变量的顺序对应数组单元值的位置依次进行赋值操作


基本语法:典型应用交互2个变量

1.js 前面必须加分号情况

1.立即执行函数

2.数组解构(数组开头的情况前面必须要加)

    <script>
        // 必须用分号的两种情况
        // 1.立即执行函数要加
        // (function(){})();
        // 2.使用数组的时候
        // const arr = [1, 2, 3]
        const str = 'pink';
        [1, 2, 3].map(function(item){
            console.log(item)
        })

        let a = 1
        let b = 2
        ;[b, a] = [a, b]
        console.log(a, b)
    </script>
2.独立完成数组解构赋值

需求①: 有个数组: const pc = [‘海尔’, ‘联想’, ‘小米’, ‘方正’]
解构为变量: hr lx mi fz

        const pc = ['海尔', '联想', '小米', '方正']
        const [hr, lx, xm, fz] = pc
        console.log(hr)
        console.log(lx)
        console.log(xm)
        console.log(fz)

需求②:请将最大值和最小值函数返回值解构 max 和min 两个变量

        function getValue() {
            return [100, 60]
        }
        const [max, min] = getValue()
        console.log(max)
        console.log(min)
3.变量多 单元值少的情况:


变量的数量大于单元值数量时,多余的变量将被赋值为 undefined

4.利用剩余参数解决变量少 单元值多的情况:


剩余参数返回的还是一个数组

5. 防止有undefined传递单元值的情况,可以设置默认值:


允许初始化变量的默认值,且只有单元值为 undefined 时默认值才会生效

6.按需导入,忽略某些返回值:

7.支持多维数组的结构:

3.2 对象解构

对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法

1.基本语法:

1.赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
2.对象属性的值将被赋值给与属性名相同的变量
3.注意解构的变量名不要和外面的变量名冲突否则报错
4.对象中找不到与变量名一致的属性时变量值为 undefined

2.给新的变量名赋值:

可以从一个对象中提取变量并同时修改新的变量名

冒号表示“什么值:赋值给谁”

3.数组对象解构

4. 多级对象解构:




3.3遍历数组 forEach 方法(重点)

forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数
主要使用场景:** 遍历数组的每个元素**
语法:

例如:


注意:
1.forEach 主要是遍历数组
2.参数当前数组元素是必须要写的, 索引号可选。

map返回空数组,forEach只遍历不返回

渲染商品列表案例

请根据数据渲染以下效果

核心思路:有多少条数据,就渲染多少模块,然后 生成对应的 html结构标签, 赋值给 list标签即可
①:利用forEach 遍历数据里面的 数据
②:拿到数据,利用字符串拼接生成结构添加到页面中
③:注意:传递参数的时候,可以使用对象解构

<body>
  <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: '称心如意手摇咖啡磨豆机咖啡豆研磨机',
        price: '289.00',
        picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
      },
      {
        id: '4001594',
        name: '日式黑陶功夫茶组双侧把茶具礼盒装',
        price: '288.00',
        picture: 'https://yanxuan-item.nosdn.127.net/3346b7b92f9563c7a7e24c7ead883f18.jpg',
      },
      {
        id: '4001009',
        name: '竹制干泡茶盘正方形沥水茶台品茶盘',
        price: '109.00',
        picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',
      },
      {
        id: '4001874',
        name: '古法温酒汝瓷酒具套装白酒杯莲花温酒器',
        price: '488.00',
        picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',
      },
      {
        id: '4001649',
        name: '大师监制龙泉青瓷茶叶罐',
        price: '139.00',
        picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',
      },
      {
        id: '3997185',
        name: '与众不同的口感汝瓷白酒杯套组1壶4杯',
        price: '108.00',
        picture: 'https://yanxuan-item.nosdn.127.net/8e21c794dfd3a4e8573273ddae50bce2.jpg',
      },
      {
        id: '3997403',
        name: '手工吹制更厚实白酒杯壶套装6壶6杯',
        price: '99.00',
        picture: 'https://yanxuan-item.nosdn.127.net/af2371a65f60bce152a61fc22745ff3f.jpg',
      },
      {
        id: '3998274',
        name: '德国百年工艺高端水晶玻璃红酒杯2支装',
        price: '139.00',
        picture: 'https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg',
      },
    ]

    // 1.声明一个字符串变量
    let str = ''
    // 2.遍历数据
    goodsList.forEach(item => {
      // console.log(item) //可以得到每一个数组元素 对象
      const {name, price, picture} = item
      str += `
      <div class="item">
      <img src="${picture}" alt="">
      <p class="name">${name}</p>
      <p class="price">${price}</p>
      </div>
      `
    })
    // 3.生成的 字符串 添加给 list
    document.querySelector('.list').innerHTML = str
  </script>
</body>

四、综合案例

筛选数组 filter 方法(重点)

  • filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
  • 主要使用场景: 筛选数组符合条件的元素,并返回筛选之后元素的新数组
  • 语法:
  • filter() 筛选数组
  • 返回值:返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组
  • 参数:currentValue 必须写, index 可选
  • 因为返回新数组,所以不会影响原数组

商品列表价格筛选

需求:
①:渲染数据列表
②:根据选择不同条件显示不同商品

业务分析:
①: 页面初始渲染
②: 点击不同需求显示不同的数据
分析:
①:渲染页面 利用forEach 遍历数据里面的 数据,并渲染数据列表
②:根据 filter 选择不同条件显示不同商品
步骤:
①:渲染页面模块
(1) 初始化需要渲染页面,同时,点击不同的需求,还会重新渲染页面,所以渲染做成一个函数
(2) 做法基本跟前面案例雷同,就是封装到了一个函数里面
步骤:
②:点击不同需求,显示不同页面内容
(1) 点击采取事件委托方式 .filter
(2) 利用过滤函数 filter 筛选出符合条件的数据,因为生成的是一个数组,传递给渲染函数即可
(3) 筛选条件是根据点击的 data-index 来判断
(4) 可以使用对象解构,把 事件对象 解构
(5) 因为 全部区间不需要筛选,直接 把goodList渲染即可

<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>
    // 2. 初始化数据
    const goodsList = [
      {
        id: '4001172',
        name: '称心如意手摇咖啡磨豆机咖啡豆研磨机',
        price: '289.00',
        picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
      },
      {
        id: '4001594',
        name: '日式黑陶功夫茶组双侧把茶具礼盒装',
        price: '288.00',
        picture: 'https://yanxuan-item.nosdn.127.net/3346b7b92f9563c7a7e24c7ead883f18.jpg',
      },
      {
        id: '4001009',
        name: '竹制干泡茶盘正方形沥水茶台品茶盘',
        price: '109.00',
        picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',
      },
      {
        id: '4001874',
        name: '古法温酒汝瓷酒具套装白酒杯莲花温酒器',
        price: '488.00',
        picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',
      },
      {
        id: '4001649',
        name: '大师监制龙泉青瓷茶叶罐',
        price: '139.00',
        picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',
      },
      {
        id: '3997185',
        name: '与众不同的口感汝瓷白酒杯套组1壶4杯',
        price: '108.00',
        picture: 'https://yanxuan-item.nosdn.127.net/8e21c794dfd3a4e8573273ddae50bce2.jpg',
      },
      {
        id: '3997403',
        name: '手工吹制更厚实白酒杯壶套装6壶6杯',
        price: '99.00',
        picture: 'https://yanxuan-item.nosdn.127.net/af2371a65f60bce152a61fc22745ff3f.jpg',
      },
      {
        id: '3998274',
        name: '德国百年工艺高端水晶玻璃红酒杯2支装',
        price: '139.00',
        picture: 'https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg',
      },
    ]
    // 渲染函数 封装
    function render(arr){
      // 声明空字符串
      let str = ''
      // 遍历数组
      arr.forEach(item => {
        // 解构
        const {name, picture, price} = item
        str += `
        <div class="item">
          <img src="${picture}" alt="">
          <p class="name">${name}</p>
          <p class="price">${price}</p>
        </div>
        `
      })
      // 追加给list
      document.querySelector('.list').innerHTML = str
    }
    render(goodsList)  //页面一打开就需要渲染
    // 2.过滤筛选
    document.querySelector('.filter').addEventListener('click', e => {
      const {tagName, dataset} = e.target
      // 判断
      if (tagName === 'A') {
        // console.log(11)
        // arr 返回新数组
        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
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值