javascript/js中Array、Set、Map数据结构特性及用法

前言

本文温习前端开发中常见的3种数据结构:数组Array、有序集合Set、有序映射Map,并分别介绍其特性、用法示例

数组-Array

适用于存放和读取有序集合、不要求集合元素唯一性时;可通过索引快速访问元素,实现元素增删改查时

使用方法

1.创建、修改
    // 直接创建
    let arr = [6,7,8,9,0];
    // 使用构造函数创建
    // arr = new Array(6,7,8,9,0);
    console.log('len:',arr.length); // len: 5
    // 得到数组长度要用length而不是size
    console.log('size:',arr.size) // size: undefined
    console.log(arr.at(0)); // 6
    console.log(arr[0]); // 6
    arr[0] = 100;  // 修改指定位置的元素
    console.log(arr); // [ 100, 7, 8, 9, 0 ]
    // fill(x,start?,end?)  从start索引开始填充元素x,end可选,end指定的位置不包含
    console.log(arr.fill(9999,2,4)); // [ 100, 7, 9999, 9999, 0 ]
    // 所有元素填充为 -1
    arr.fill(-1,0);
    console.log(arr); // [ -1, -1, -1, -1, -1 ]
2.添加删除元素

push添加,pop尾部删除,shift头部删除,unshift头部插入,concat合并数组

    let arr = [ 6, 7, 8, 9, 0 ]
    // push(x)尾部添加指定元素
    let len = arr.push(10);
    console.log("len:",len); // len: 6
    // pop() 尾部删除一个元素,并返回这个元素,若数组为空,返回undefined
    let lastVal = arr.pop();
    console.log("lastVal:",lastVal,", len:",arr.length); // lastVal: 10 , len: 5
    // shift() 头部删除一个元素,返回被删除元素,若数组为空返回undefined
    let delHeadVal = arr.shift();
    console.log("delHeadVal:",delHeadVal,",len:",arr.length); // delHeadVal: 6 ,len: 4
    // unshift(x1,x2,x3...)在元素开头插入一个或多个元素,返回插入后的数组长度
    len = arr.unshift(88);
    console.log("len:",len); // len: 5
    let arr2 = [2,4,6];
    // concat合并两个或多个数组,返回新的数组,注意arr不会发生改变
    let ansArr = arr.concat(arr2); 
    console.log("ansArr len:",ansArr.length); // ansArr len: 8

slice拷贝数组元素

    let nums = [1, 2, 3, 4, 5, 6, 7];
    // slice(start,exclusiveEnd) 包括start,不包括exclusiveEnd,返回一个新数组对象,该方法不会改变原始数组
    let sliceNums = nums.slice(1, 3);
    console.log("sliceNums:", sliceNums); // sliceNums: [ 2, 3 ]
    sliceNums[0] = 888;
    // 修改sliceNums,不会改变原始数组nums
    console.log("nums:", nums); // nums: [ 1, 2, 3, 4, 5, 6, 7 ]
    // 从索引5开始提取所有的元素 
    console.log(nums.slice(4)); // [5, 6, 7 ]
    // 易错提示:-1表示倒数第1个元素,由于exclusiveEnd不包括,因此得到的最后一个元素是倒数第2个元素
    console.log(nums.slice(5, -1)); // [ 6 ]
    // slice() 提取所有元素
    console.log(nums.slice()); // [ 1, 2, 3, 4, 5, 6, 7 ]
    // slice 超出索引范围,返回一个空数组 []
    console.log(nums.slice(88)); // []

splice修改、删除、插入元素

    let nums = [1, 2, 3, 4, 5, 6, 7];
    // 删除指定位置的N个元素: nums.splice(index,count) 从index位置开始连续删除count个元素,并返回删除的元素,nums本身被改变
    let removedNums = nums.splice(2, 5);
    console.log("removedNums:", removedNums, ", nums:", nums); // removedNums: [ 3, 4, 5, 6, 7 ] , nums: [ 1, 2 ]
    // 元素替换:从索引1处删除0个元素(返回为空),并对原始数组添加多个元素,原始数组被改变
    console.log("res:",nums.splice(1, 0, 'x', 'y', 'z')); // res: []
    console.log("splice nums:", nums); // splice nums: [ 1, 'x', 'y', 'z', 2 ]
    // 删除后续元素:删除从索引2开始的所有元素,返回被删除元素
    console.log("removed: ", nums.splice(2), "nums:", nums); // removed:  [ 'y', 'z', 2 ] nums: [ 1, 'x' ]
    nums = [1, 2, 3, 4];
    // 删除索引2处的元素
    console.log("del: ", nums.splice(2, 1),", nums:",nums); // del:  [ 3 ] , nums: [ 1, 2, 4 ]
    // 将索引2处的1个元素删除,并替换为78,88
    nums.splice(2, 1, 78, 88);
    console.log("nums:", nums); // nums: [ 1, 2, 78, 88 ]
3.筛选、查找元素

filter筛选,find查找元素,findIndex、indexOf、lastIndexOf查找元素索引,includes判断是否包含

    let nums = [1, 2, 3, 4, 5, 6, 7];
    // filter过滤出符合指定规则的元素
    let evenNums = nums.filter(x => x % 2 == 0); 
    // find查找数组中满足测试函数的第一个元素的值,若没有符合的,返回undefined
    console.log("evenNums:", evenNums); // evenNums: [ 2, 4, 6 ]
    let targetValue = nums.find(x => x > 5);
    console.log("targetvalue:", targetValue); // targetvalue: 6
    // findIndex返回符合测试元素的第一个索引,找不到返回-1
    let targetIndex = nums.findIndex(x => x > 5);
    console.log("targetIndex:", targetIndex); //targetIndex: 5
    // indexOf(x)从索引位置0开始寻找一个指定的元素,找到则返回指向第一个元素的索引,找不到则返回-1
    console.log("index:", nums.indexOf(5)); // index: 4
    // indexOf(x,fromIndex) 从index位置寻找一个元素x
    console.log(nums.indexOf(1, 5));  // -1
    // lastIndexOf(x,fromIndex) 若fromIndex缺省,默认从最后一个位置开始向前查找x元素的索引
    console.log(nums.lastIndexOf(5)) // 4
    // includes(x)判断数组中是否包含一个指定元素,若包含返回true,否则false
    console.log(nums.includes(4)); // true
    // includes(x,fromIndex) 从fromIndex位置开始查找是否包含指定元素
    console.log(nums.includes(2, 1)); // true
4.成分判断

every测试所有都符合、some测试至少一个元素符合

    let nums = [5, 8, 4, 6, 7];
    // every 判断所有元素是否都通过测试函数,若是返回true,否则false
    let isAllEven = nums.every(x => x % 2 == 0);
    console.log("isAllEven:", isAllEven); // false
    // some 判断是否至少有一个元素通过测试函数,若是返回true
    let hasEven = nums.some(x => x % 2 == 0);
    console.log("hasEven:", hasEven); // true
5.反转、排序

reverse反转数组顺序,sort排序

     let ids = new Array(2, 0, 3, 5, 1, 4);
     // reverse反转数组元素顺序,并返回数组本身
     let reverseIds = ids.reverse();
     console.log("reverseIds:", reverseIds); // reverseIds: [ 4, 1, 5, 3, 0, 2 ]
     console.log("ids:", ids); // ids: [ 4, 1, 5, 3, 0, 2 ]
     // sort排序会改变原始数组顺序,并返回数组本身,默认从小到大排序
     console.log(ids.sort()); // [ 0, 1, 2, 3, 4, 5 ]
     // 自定义排序,a-b升序,b-a降序
     ids.sort((a,b)=>b-a); 
     console.log(ids);// [ 5, 4, 3, 2, 1, 0 ]
6.元素遍历

for…in遍历索引,for…of遍历元素, forEach遍历元素及索引

   let nums = [5, 8, 4, 6, 7];
    // for...in遍历数组,遍历的是索引
    for (let index in nums) {
        console.log("index:", index, ", value:", nums[index]);
    }
    // for...of遍历数组,遍历得到的是具体元素
    for (let x of nums) {
        console.log("x=", x);
    }
    // forEach遍历元素
    nums.forEach(x => {
        console.log("x:", x);
    });
    // forEach两个参数时第2个元素是索引
    nums.forEach((x, index) => {
        console.log("x:", x, ", index:", index);
    });
    // 普通遍历方法
    for (let i = 0; i < nums.length; i++) {
        console.log("nusm:", nums[i]);
    }
7.累计、分组运算

reduce对数字元素进行算术累计运算,对字符串连接运算

    let nums = [1, 2, 3];
    let initVal = 0;
    // reduce对数组中每个元素执行一个函数,将结果汇总为单个值返回,initVal指定初始值,preVal+curVal表示将数组元素累加
    let sums = nums.reduce((preVal, curVal) => preVal + curVal, initVal);
    console.log("sums:", sums); // 6
    const words = ['Hello', 'World', 'JavaScript'];
    // reduce将元素连接为一个字符串
    const sentence = words.reduce((accumulator, currentValue) => accumulator + ' ' + currentValue);
    console.log("sentence:"+sentence); // sentence:Hello World JavaScript
    // join将所有元素用字符-连接为一个字符串
    let str = nums.join('-');
    console.log(str); // 1-2-3

reduce分组

  const users = [
    {name:"User-A",age:21},
    {name:'User-B',age:20},
    {name:'User-C',age:20},
    {name:'User-E',age:20}];
    let initVal = {};
    let ageGroup = users.reduce((preVal,curVal)=>{
        let age = curVal.age;
        if(!preVal[age]){
            preVal[age] = [];
        }
        preVal[age].push(curVal);
        return preVal;
    },initVal);
    console.log(ageGroup);
    // {
    //     '20': [
    //       { name: 'User-B', age: 20 },
    //       { name: 'User-C', age: 20 },
    //       { name: 'User-E', age: 20 }
    //     ],
    //     '21': [ { name: 'User-A', age: 21 } ]
    //   }

8.变换

map、flatMap将每个元素进行变换

    let nums = [1, 2, 3];
    console.log(nums.map(x=>'id-'+x)); // [ 'id-1', 'id-2', 'id-3' ]
    // map:对数组中的每个元素执行回调函数,回调函数的返回值作为元素
    let ans = nums.map(x=>[x*2]);
    console.log(ans); // [ [ 2 ], [ 4 ], [ 6 ] ]
    // flatMap: 对每个元素执行回调函数,并将结果展平一层后返回一个新数组,相当于先执行 map,然后再执行 flat
    let ans2 = nums.flatMap(x=>[x*2]);
    console.log(ans2); // [ 2, 4, 6 ]
    let nums2 = nums.map(x=>[x,x*x]);
    console.log(nums2); // [ [ 1, 1 ], [ 2, 4 ], [ 3, 9 ] ]
    let ans3 = nums.flatMap(x=>[x,x*x]); 
    console.log(ans3); // [ 1, 1, 2, 4, 3, 9 ]

有序集合-Set

用于存储具有唯一性的元素,常用于元素查找、去重场景

使用方法

1.元素去重

普通类型插入元素唯一,可用于元素去重,对象插入是否唯一根据引用判断

   let arr = [1,2,2,3,1];
    // 数组转为集合去重
    let setNums = new Set(arr); 
    // 集合转为数组
    console.log(Array.from(setNums)); // [ 1, 2, 3 ]
    let set = new Set();
    set.add(3);
    set.add(2);
    // 2已经存在,所以不会被重复添加
    set.add(2);
    // 获取集合大小要使用set.size,而不是set.length(输出为undefined)
    console.log("size:", set.size); // size: 2
2.元素存在

可以在内部表示为哈希表,查找时间复杂度为O(1)

    // set中是否含有某个元素
    let isContain = set.has(4);
    console.log("isContain:", isContain); // false
3.有序遍历

元素遍历时的顺序保持与插入顺序一致,可使用forEach或 for…of遍历

    let set = new Set([5,8,7,6]);
    // set集合的遍历应该使用 for...of而不是for...in,以下遍历什么也不会输出:
    // for(let id in set){
    //     console.log(id);
    // }
    // 遍历方法1:使用 for...of遍历
    for (let id of set) {
        console.log(id);
    }
    // 遍历方法2:使用forEach
    set.forEach(id => {
        console.log("id:", id);
    });
    // 获取迭代器对象,set.values()迭代器遍历,仍然要使用 for...of而不是for...in
    for (let value of set.values()) {
        console.log("value:", value);
    }
    //输出的 key和value其实是一样的
    for (let [key, value] of set.entries()) {
        console.log("key:", key, "value:", value);
    }
4.删除操作

delete删除某个元素,clear清空集合

    // delete 删除集合中数字5,成功返回true,否则false
    let isDel = set.delete(5);
    console.log("isDel:", isDel);
	set.clear();

有序映射-Map

Map 适合于频繁增删改键值对的场景,可确保的键唯一性,内部实现用的是哈希表,具有较好性能

使用方法

1.键值添加、更改

新增或修改set,取值get,判断存在has,删除delete

    let map = new Map();
    // set进行键值添加
    map.set("name", "Alice");
    map.set("age", 24);
    // 键已存在则会被更新
    map.set("name", "Blob");
    // 注意:Map不要使用[]操作符,这种设置属性的方式不会改变 Map 的数据结构,它使用的是通用对象的特性,因此使用has是查不到的
    // ap['gender'] = 'female';
    // console.log('! has:', map.has('gender')); // false
    console.log(map.has('name')); // true
    console.log(Array.from(map.entries())); // [ [ 'name', 'lisi' ], [ 1, 'apple' ] ]
    // 取与键关联的值,若不存在则返回undefined
    let value = map.get("name");
    console.log("value:", value); // value: Blob
    // 获取map大小使用size,而不是length
    console.log(map.size); // 2
    map.clear();
2.有序遍历

Map 的键是有序的,遍历输出保持与添加时的顺序一致

Map遍历for…of或forEach

    // 使用可迭代对象初始化Map
    let userMap = new Map([['id', 23], ['name', 'zhangsan'], ['age', 18]]);
    console.log(userMap);
    // for...of 遍历Map
    for (const [key, value] of userMap) {
        console.log("key:", key, ",value:", value);
    }
    // 或者
    for (const [key, value] of userMap.entries()) {
        console.log("key#:", key, ",value#:", value);
    }
    // 或者
    for (let entry of userMap) {
        console.log("entry:", entry);
        // entry[0]为键,entry[1]为值
        // console.log(entry[0],entry[1])
    }
    // forEach遍历map
    userMap.forEach((value, key) => {
        console.log("value=", value, ",key=", key);
    });
    for (const key of userMap.keys()) {
        console.log("key:", key);
    }
    for (const value of userMap.values()) {
        console.log('value:', value);
    }

关于Map映射与普通Object对象的区别

1.键的有序性:

Map 中的键以简单、直接的方式排序,Map 对象按照插入的顺序迭代条目、键和值

Object 的键排序比较复杂的,最好不要依赖属性的顺序

2.键的类型:

Map 的键可以为任何值(包括函数、对象或任何原始值)

Object 的键必须为 String 或 Symbol

3.大小计算:

Map 中的项目数量确定很容易从其 size 属性中获得

Object 中的项目数量确定比较麻烦,效率也较低,一种常见的方法是通过获取 Object.keys() 返回的数组的长度

4.其他方面:

Map 性能在涉及频繁添加和删除键值对的场景中表现更好,而Object未针对频繁添加和删除键值对进行优化

关于普通Object用法

// 普通对象
// 添加或修改属性obj[property],或obj.property,
// 遍历for...in(遍历key), for...of(遍历entries)

function testObject2() {
    // 创建一个空对象
    let obj = {};
    obj[2] = 'zhangsan';
    obj[1] = 'lisi';
    console.log(obj);
    obj.msg = "hello";
    console.log(obj);
    // 判断对象是否有某个属性
    console.log(obj.hasOwnProperty("msg")); // true
    console.log('msg' in obj); // true
    for (let key in obj) {
        console.log("key:", key, "value:", obj[key]);
    }
    // 遍历键值对
    Object.entries(obj).forEach((key, value) => {
        console.log("key:", key, ", value:", value);
    });
    for (const [key, value] of Object.entries(obj)) {
        console.log("key=", key, ", value=", value);
    }
    // 类似:Object.values(obj);
    Object.keys(obj).forEach(key => {
        console.log("key=", key);
    });
    // delete删除某个属性,删除后再访问该属性为undefined
    delete obj.msg;
    console.log(obj); // { '1': 'lisi', '2': 'zhangsan' }
    console.log(obj.msg); // undefined
    // 键的个数
    console.log(Object.keys(obj).length); // 2
}

参考资料

1.JavaScript 标准内置对象

2.JavaScript数据类型

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/760578.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LoadBalance 负载均衡

什么是负载均衡 负载均衡(Load Balance&#xff0c;简称 LB),是⾼并发,⾼可⽤系统必不可少的关键组件. 当服务流量增⼤时,通常会采⽤增加机器的⽅式进⾏扩容,负载均衡就是⽤来在多个机器或者其他资源中,按照⼀定的规则合理分配负载. 负载均衡的⼀些实现 服务多机部署时,开发⼈…

微积分-导数3(微分法则)

常见函数的导数 常量函数的导数 d d x ( c ) 0 \frac{d}{dx}(c) 0 dxd​(c)0 常量函数的图像是一条水平线 y c y c yc&#xff0c;它的斜率为0&#xff0c;所以我们必须有 f ′ ( x ) 0 f(x) 0 f′(x)0。从导数的定义来看&#xff0c;证明也很简单&#xff1a; f ′ …

44 - 50题高级字符串函数 / 正则表达式 / 子句 - 高频 SQL 50 题基础版

目录 1. 相关知识点2.例子2.44 - 修复表中的名字2.45 - 患某种疾病的患者2.46 - 删除重复的电子邮箱2.47 - 第二高的薪水2.48 - 按日期分组销售产品2.49 - 列出指定时间段内所有的下单产品2.50 - 查找拥有有效邮箱的用户 1. 相关知识点 相关函数 函数含义concat()字符串拼接upp…

MT6989(天玑9300)芯片性能参数_MTK联发科5G处理器

MT6989是联发科Dimensity旗舰系列的成员&#xff0c;旨在为旗舰5G智能手机供应商提供最先进的技术和性能。MT6989也是联发科目前最具创新和强大的5G智能手机芯片&#xff0c;具有领先的功耗效率&#xff0c;无与伦比的计算架构&#xff0c;有史以来最快和最稳定的5G调制解调器&…

MySQL之主从同步、分库分表

1、主从同步的原理 MySQL主从复制的核心是二进制日志 二进制日志&#xff08;binlog&#xff09;记录了所有DDL语句和DML语句&#xff0c;但不包括数据查询&#xff08;select、show&#xff09;语句。 1.1、复制分三步 master主库在事务提交时&#xff0c;会把数据变更记录…

九浅一深Jemalloc5.3.0 -- ②浅*size class

目前市面上有不少分析Jemalloc老版本的博文&#xff0c;但5.3.0却少之又少。而且5.3.0的架构与之前的版本也有较大不同&#xff0c;本着“与时俱进”、“由浅入深”的宗旨&#xff0c;我将逐步分析Jemalloc5.3.0的实现。 另外&#xff0c;单讲实现代码是极其枯燥的&#xff0c;…

mmap()函数和munmap()函数的例子

代码&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <string.h> #include <stdio.h> #include <unistd.h>#define FILELENGTH 80 int main(void) {int fd-1;char …

Objective-C使用块枚举的细节

对元素类型的要求 在 Objective-C 中&#xff0c;NSArray 只能存储对象类型&#xff0c;而不能直接存储基本类型&#xff08;例如 int&#xff09;。但是&#xff0c;可以将基本类型封装在 NSNumber 等对象中&#xff0c;然后将这些对象存储在 NSArray 中。这样&#xff0c;en…

爬虫中如何创建Beautiful Soup 类的对象

在使用 lxml 库解析网页数据时&#xff0c;每次都需要编写和测试 XPath 的路径表达式&#xff0c;显得非常 烦琐。为了解决这个问题&#xff0c; Python 还提供了 Beautiful Soup 库提取 HTML 文档或 XML 文档的 节点。 Beautiful Soup 使用起来很便捷&#xff0c;…

Web后端开发概述环境搭建项目创建servlet生命周期

Web开发概述 web开发指的就是网页向后再让发送请求,与后端程序进行交互 web后端(javaEE)程序需要运行在服务器中 这样前端才可以对其进行进行访问 什么是服务器? 解释1: 服务器就是一款软件,可以向其发送请求,服务器会做出一个响应.可以在服务器中部署文件&#xff0c;让…

使用世界变换的逆转置矩阵对法线进行变换

法向量变换细节记录 最近在做法向量变换的时候&#xff0c;踩了两个坑&#xff0c;记录一下相关的知识点 法向量做变换&#xff0c;最后一位是补0 我们知道&#xff0c;顶点在做变换的时候最后一位是 1.0&#xff0c;法线最后一位是补0.0 vec3 normCurrent (getMatrixWorld() …

【NodeJs】入门

目录 一、前导 二、 url模块 三、path模块 四、buffer模块 五、fs模块 六、stream流模块 七、os模块 八、crypto模块 九、util模块 十、http模块 nodejs官网 Node.js — 在任何地方运行 JavaScript nmp是Node.js包管理器&#xff0c;用来安装各种库、框架和工具&…

基于STM32的八位数码管显示和闹钟计时【Proteus仿真】

某鱼&#xff1a;两栖电子 一、系统功能 采用矩阵键盘&#xff0c;按下对应的数字再按下确认按键&#xff0c;数码管会显示自己输入的数字&#xff0c;如果按错可以使用删除按钮进行删除。点击计时按钮可以显示当前的时间。 二、使用器件 DS1302实时时钟芯片&#xff0c;8位数…

Mac虚拟机软件有什么用?

随着苹果M系列芯片电脑的推出&#xff0c;虚拟机的使用变得越来越流行。不同于苹果以往的Intel处理器电脑&#xff0c;其M系列芯片电脑无法安装双系统。如果要使用非macOS系统&#xff0c;可以通过创建虚拟机系统的方式实现。那么&#xff0c;虚拟机软件有什么作用和用途&#…

DP(动态规划)【3】 最长公共子序列 最长回文子串

目录 1.最长公共子序列 状态转移方程需要二维数组&#xff0c;1-dim已经不太够了 又是这个问题&#xff1a;如何读入字符串 2.最长回文子串 1.最长公共子序列 状态转移方程需要二维数组&#xff0c;1-dim已经不太够了 这里dp[i][j]是说S的前i位与T的前j位公共序列&#xff…

韩顺平0基础学java——第34天

p675-689 UDP网络编程 1.类 DatagramSocket和 DatagramPacket[数据包/数据报]实现了基于UDP协议网络程序。 2.UDP数据报通过数据报套接字DatagramSocket发送和接收&#xff0c;系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。 3.DatagramPacket对象…

FastAPI教程III

本文参考FastAPI教程https://fastapi.tiangolo.com/zh/tutorial 这部分暂无需求的没有记录&#xff0c;仅放置标题。 依赖项 安全性 中间件 你可以向FastAPI应用添加中间件。 ”中间件“是一个函数&#xff0c;它在每个请求被特定的路径操作处理之前&#xff0c;以及在每个…

植物大战僵尸融合版最新版2024蓝飘飘fly

亲爱的花园守护者们&#xff0c;是否已经厌倦了传统塔防游戏的老套模式&#xff1f;是否渴望在熟悉的《植物大战僵尸》中寻找全新的刺激体验&#xff1f;那么&#xff0c;让我们一起走进《植物大战僵尸融合版》的异想世界&#xff0c;开启一场别开生面的园艺之战吧&#xff01;…

区间动态规划——最长回文子序列长度(C++)

把夜熬成粥&#xff0c;然后喝了它。 ——2024年7月1日 书接上回&#xff1a;区间动态规划——最长回文子串&#xff08;C&#xff09;-CSDN博客&#xff0c;大家有想到解决办法吗&#xff1f; 题目描述 给定一个字符串s&#xff08;s仅由数字和英文大小写字母组成&#xff0…

以太网交换机原理

没有配置&#xff0c;比较枯燥&#xff0c;二可以认识线缆&#xff0c; 三比较重要&#xff0c;慢慢理解&#xff0c;事半功倍。 各位老少爷们&#xff0c;在下给大家说段以太网交换机原理&#xff0c;说得不好大家多多包涵&#xff0c;说得好呢&#xff0c;大家叫个好&#x…