JavaScript数据结构【进阶】

注:最后有面试挑战,看看自己掌握了吗


🌸I could be bounded in a nutshell and count myself a king of infinite space.
在这里插入图片描述


使用 splice() 添加元素

还记得在上个挑战中我们提到 splice() 方法最多可以接收 3 个参数吗? 第三个参数可以是一个或多个元素,这些元素会被添加到数组中。 这样,我们能够便捷地将数组中的一个或多个连续元素换成其他的元素。

const numbers = [10, 11, 12, 12, 15];
const startIndex = 3;
const amountToDelete = 1;

numbers.splice(startIndex, amountToDelete, 13, 14);
console.log(numbers);

第二个 12 已被删除,我们在同一索引处添加 13 和 14。 numbers 数组现在将会是 [ 10, 11, 12, 13, 14, 15 ]。

在上面的代码中,数组一开始包含了若干数字。 接着,我们调用 splice() 方法,索引为 (3) 的地方开始删除元素,删除的元素数量是 (1)。然后,(13, 14) 是在删除位置插入的元素。 可以在 amountToDelete 后面传入任意数量的元素(以逗号分隔),每个都会被插入到数组中。

我们已经定义了一个 htmlColorNames 函数,它以一个 HTML 颜色的数组作为输入参数。 请修改这个函数,使用 splice() 来移除数组中的前两个元素,并在对应的位置上添加 ‘DarkSalmon’ 和 ‘BlanchedAlmond’。

function htmlColorNames(arr) {
  // 只修改这一行下面的代码
  arr.splice(0,2,'DarkSalmon' ,'BlanchedAlmond')
  // 只修改这一行上面的代码
  return arr;
}

console.log(htmlColorNames(['DarkGoldenRod', 'WhiteSmoke', 'LavenderBlush', 'PaleTurquoise', 'FireBrick']));

使用 slice() 复制数组元素

接下来我们要介绍 slice() 方法。 slice() 不会修改数组,而是会复制,或者说*提取(extract)*给定数量的元素到一个新数组。 slice() 只接收 2 个输入参数:第一个是开始提取元素的位置(索引),第二个是提取元素的结束位置(索引)。 提取的元素中不包括第二个参数所对应的元素。 如下示例:

let weatherConditions = ['rain', 'snow', 'sleet', 'hail', 'clear'];

let todaysWeather = weatherConditions.slice(1, 3);

todaysWeather 值为 ['snow', 'sleet'],weatherConditions 值仍然为 [‘rain’, ‘snow’, ‘sleet’, ‘hail’, ‘clear’]。

在上面的代码中,我们从一个数组中提取了一些元素,并用这些元素创建了一个新数组。

我们已经定义了一个 forecast 函数,它接受一个数组作为参数。 请修改这个函数,利用 slice() 从输入的数组中提取信息,最终返回一个包含元素 warm 和 sunny 的新数组。

使用展开运算符复制数组

slice() 可以让我们从一个数组中选择一些元素来复制到新数组中,而 ES6 中又引入了一个简洁且可读性强的语法:展开运算符(spread operator),它能让我们方便地复制数组中的所有元素。 展开语法写出来是这样:...

我们可以用展开运算符来复制数组:

let thisArray = [true, true, undefined, false, null];
let thatArray = [...thisArray];

thatArray 等于 [true, true, undefined, false, null]。 thisArray 保持不变, thatArray 包含与 thisArray 相同的元素。

我们已经定义了一个 copyMachine 函数,它接受 arr(一个数组)和 num(一个数字)作为输入参数。 该函数需要返回一个由 num 个 arr 组成的新的二维数组。 同时,我们写好了大致的流程,只是细节实现还没有写完。 请修改这个函数,使用展开语法,使该函数能正常工作(提示:我们已经学到过的一个方法很适合用在这里)!

function copyMachine(arr, num) {
  let newArr = [];
  while (num >= 1) {
    // 只修改这一行下面的代码
    newArr.push([...arr])
    // 只修改这一行上面的代码
    num--;
  }
  return newArr;
}

console.log(copyMachine([true, false, true], 2));

使用展开运算符合并数组

展开语法(spread)的另一个重要用途是合并数组,或者将某个数组的所有元素插入到另一个数组的任意位置。 我们也可以使用 ES5 的语法连接两个数组,但只能让它们首尾相接。 而展开语法可以让这样的操作变得极其简单:

let thisArray = ['sage', 'rosemary', 'parsley', 'thyme'];

let thatArray = ['basil', 'cilantro', ...thisArray, 'coriander'];

thatArray 会有值 [‘basil’, ‘cilantro’, ‘sage’, ‘rosemary’, ‘parsley’, ‘thyme’, ‘coriander’]

使用展开语法,我们就可以很方便的实现一个用传统方法会写得很复杂且冗长的操作。

我们已经定义了一个返回 sentence 变量的 spreadOut 函数。 请修改这个函数,利用 spread 使该函数返回数组 [‘learning’, ‘to’, ‘code’, ‘is’, ‘fun’]。

使用 indexOf() 检查元素是否存在

由于数组随时都可以修改或发生 mutated,我们很难保证某个数据始终处于数组中的特定位置,甚至不能保证该元素是否还存在于该数组中。 好消息是,JavaScript 为我们提供了内置方法 indexOf()。 这个方法让我们可以方便地检查某个元素是否存在于数组中。 indexOf() 方法接受一个元素作为输入参数,并返回该元素在数组中的位置(索引);若该元素不存在于数组中则返回 -1。

例如:

let fruits = ['apples', 'pears', 'oranges', 'peaches', 'pears'];

fruits.indexOf('dates');
fruits.indexOf('oranges');
fruits.indexOf('pears');

indexOf(‘dates’) 返回 -1,indexOf(‘oranges’) 返回 2,indexOf(‘pears’) 返回 1 (每个元素存在的第一个索引)。

indexOf() 在快速检查一个数组中是否存在某个元素时非常有用。 我们已经定义了一个 quickCheck 函数,它接受一个数组和一个元素作为输入参数。 请通过 indexOf() 方法修改这个函数,使得当传入的参数在数组中存在时返回 true,否则返回 false。

function quickCheck(arr, elem) {
  // 只修改这一行下面的代码
 return arr.indexOf(elem)>=0?true
 :false
  // 只修改这一行上面的代码
}

console.log(quickCheck(['squash', 'onions', 'shallots'], 'mushrooms'));

使用 for 循环遍历数组中的全部元素

使用数组时,我们经常需要遍历数组的所有元素来找出我们需要的一个或多个元素,抑或是对数组执行一些特定的操作。 JavaScript 为我们提供了几个内置的方法,它们以不同的方式遍历数组,以便我们可以用于不同的场景(如 every()、forEach()、map() 等等)。 然而,最简单的 for 循环不仅能实现上述这些方法的功能,而且相比之下也会更加灵活。

请看以下的例子:

function greaterThanTen(arr) {
  let newArr = [];
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] > 10) {
      newArr.push(arr[i]);
    }
  }
  return newArr;
}


greaterThanTen([2, 12, 8, 14, 80, 0, 1]);

在这个函数中,我们用一个 for 循环来遍历数组,逐一对其中的元素进行判断。 通过上面的代码,我们可以找出数组中大于 10 的所有元素,并返回一个包含这些元素的新数组 [12, 14, 80]。

我们已经定义了 filteredArray 函数,它接受一个嵌套的数组 arr 和一个 elem 作为参数,并要返回一个新数组。 arr 数组中嵌套的数组里可能包含 elem 元素,也可能不包含。 请修改该函数,用一个 for 循环来做筛选,使函数返回一个由 arr 中不包含 elem 的数组所组成的新数组。

function filteredArray(arr, elem) {
  let newArr = [];
  // 只修改这一行下面的代码
for(let i=0;i<arr.length;i++){
  if(arr[i].indexOf(elem)==-1){
    newArr.push(arr[i])
  }
}
  // 只修改这一行上面的代码
  return newArr;
}

console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));
function filteredArray(arr, elem) {
  let newArr = [];
  // 只修改这一行下面的代码
for(let i=0;i<arr.length;i++){
  let j=0
  for(;j<arr[i].length;j++){

  if(elem==arr[i][j]){
    break;
  }
  
}
if(j==arr[i].length){
    newArr.push(arr[i]);
}
}
  // 只修改这一行上面的代码
  return newArr;
}

console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));

创建复杂的多维数组

很好! 你现在已经学到很多关于数组的知识了, 但这些只是个开始。我们将在接下来的中挑战中学到更多与数组相关的知识。 但在继续查看 对象 之前,让我们再看一下,看看数组如何变得比我们在之前的挑战中看到的更复杂一些。

数组的一个强大的特性是,它可以包含其他数组,甚至完全由其他数组组成。 在上一个挑战中,我们已经接触到了包含数组的数组,但它还算是比较简单的。 数组中的数组还可以再包含其他数组,即可以嵌套任意多层数组。 通过这种方式,数组可以很快成为非常复杂的数据结构,称为多维(multi-dimensional)数组,或嵌套(nested)数组。 请看如下的示例:

let nestedArray = [
  ['deep'],
  [
    ['deeper'], ['deeper'] 
  ],
  [
    [
      ['deepest'], ['deepest']
    ],
    [
      [
        ['deepest-est?']
      ]
    ]
  ]
];

deep 数组已嵌套 2 层。 deeper 数组嵌套了 3 层。 deepest 数组嵌套了 3 层, deepest-est? 嵌套了 5 层。

虽然这个例子看起来错综复杂,不过,尤其是在处理大量数据的时候,这种数据结构还是会用到的。 尽管结构复杂,不过我们仍可以通过方括号表示法来访问嵌套得最深的数组:

console.log(nestedArray[2][1][0][0][0]);

控制台打印的是字符串 deepest-est?。 既然我们知道数据的位置,当然,我们也可以修改它:

nestedArray[2][1][0][0][0] = 'deeper still';

console.log(nestedArray[2][1][0][0][0]);

现在控制台打印的是 deeper still。

我们已经定义了一个叫做 myNestedArray 的数组变量。 请修改 myNestedArray,使用字符串(string)、数字(number)或布尔值(boolean)的任意组合作为数组的元素,并让 myNestedArray 刚好有 5 层(注意,最外层的数组是第 1 层)。 同时,请在第 3 层的数组中包含字符串 deep;在第 4 层的数组中包含字符串 deeper,在第 5 层的数组中包含字符串 deepest。

将键值对添加到对象中

对象(object)本质上是键值对(key-value pair)的集合。 或者说,一系列被映射到唯一标识符的数据就是对象;习惯上,唯一标识符叫做属性(property)或者键(key);数据叫做值(value)。 让我们来看一个简单的例子:

const tekkenCharacter = {
  player: 'Hwoarang',
  fightingStyle: 'Tae Kwon Doe',
  human: true
};

上面的代码定义了一个叫做 tekkenCharacter 的“铁拳”游戏人物对象。 它有三个属性,每个属性都对应一个特定的值。 如果我们想为它再添加一个叫做 origin 的属性,可以这样写:

tekkenCharacter.origin = 'South Korea';

上面的代码中,我们使用了点号表示法。 如果我们现在输出 tekkenCharacter 对象,便可以看到它具有 origin 属性。 接下来,因为这个人物在游戏中有着与众不同的橘色头发, 我们可以通过方括号表示法来为它添加这个属性,像这样:

tekkenCharacter['hair color'] = 'dyed orange';

如果要设置的属性中存在空格,或者要设置的属性是一个变量,那我们必须使用方括号表示法(bracket notation)来为对象添加属性。 在上面的代码中,我们把属性(hair color)放到引号里,以此来表示整个字符串都是需要设置的属性。 如果我们不加上引号,那么中括号里的内容会被当作一个变量来解析,这个变量对应的值就会作为要设置的属性, 请看这段代码:

const eyes = 'eye color';


tekkenCharacter[eyes] = 'brown';

执行以上所有示例代码后,对象会变成这样:

{
  player: 'Hwoarang',
  fightingStyle: 'Tae Kwon Doe',
  human: true,
  origin: 'South Korea',
  'hair color': 'dyed orange',
  'eye color': 'brown'
};

我们已经为你创建了包含三个项目的 foods 对象。 请使用上述任意语法,来为 foods 对象添加如下三个键值对:bananas 属性,值为 13;grapes 属性,值为 35;strawberries 属性,值为 27。

修改嵌套在对象中的对象

现在我们来看一个稍复杂的对象。 在对象中,我们也可以嵌套任意层数的对象,对象的属性值可以是 JavaScript 支持的任意类型,包括数组和其他对象。 请看以下例子:

let nestedObject = {
  id: 28802695164,
  date: 'December 31, 2016',
  data: {
    totalUsers: 99,
    online: 80,
    onlineStatus: {
      active: 67,
      away: 13,
      busy: 8
    }
  }
};

nestedObject 有 3 个属性:id(属性值为数字)、date(属性值为字符串)、data(属性值为嵌套的对象)。 虽然对象中的数据可能很复杂,我们仍能使用上一个挑战中讲到的写法来访问我们需要的信息。 如果我们想把嵌套在 onlineStatus 中 busy 的属性值改为 10,可以用点号表示法来这样实现:

nestedObject.data.onlineStatus.busy = 10;

我们已经定义了一个 userActivity 对象,它包含了另一个对象。 请将 online 的属性值改为 45。

使用方括号访问属性名称

在关于对象的第一个挑战中,我们提到可以在一对方括号中用一个变量作为属性名来访问属性的值。 假设一个超市收银台程序中有一个 foods 对象, 并且有一个函数会设置 selectedFood;如果我们需要查询 foods 对象中,某种食物是否存在, 可以这样实现:

let selectedFood = getCurrentFood(scannedItem);
let inventory = foods[selectedFood];

上述代码会先读取 selectedFood 变量的值,并返回 foods 对象中以该值命名的属性所对应的属性值。 若没有以该值命名的属性,则会返回 undefined。 有时候对象的属性名在运行之前是不确定的,或者我们需要动态地访问对象的属性值。在这些场景下,方括号表示法就变得十分有用

我们已经定义了 checkInventory 函数,它接受一个被扫描到的商品名作为输入参数。 请让这个函数返回 foods 对象中,以 scannedItem 的值所命名的属性对应的属性值。 在本挑战中,只有合理有效的属性名会作为参数传入 checkInventory,因此你不需要处理参数无效的情况。

使用 delete 关键字删除对象属性

现在我们已经学习了什么是对象以及对象的基本特性和用途。 总之,对象是以键值对的形式,灵活、直观地存储结构化数据的一种方式,而且,通过对象的属性查找属性值是速度很快的操作。 在本章余下的挑战中,我们来了解一下对象的几种常用操作,这样你能更好地在代码中使用这个十分有用的数据结构:对象。

在之前的挑战中,我们已经试过添加和修改对象中的键值对。 现在我们来看看如何从一个对象中移除一个键值对。

我们再来回顾一下上一个挑战中的 foods 对象。 如果我们想移除 apples 属性,可以像这样使用 delete 关键字:

delete foods.apples;

请使用 delete 关键字来移除 foods 中的 oranges、plums 和 strawberries 属性。

检查对象是否具有某个属性

我们已经学习了如何添加、修改和移除对象中的属性。 但如果我们想知道一个对象中是否包含某个属性呢? JavaScript 为我们提供了两种不同的方式来实现这个功能: 一个是通过 hasOwnProperty() 方法,另一个是使用 in 关键字。 假如我们有一个 users 对象,为检查它是否含有 Alan 属性,可以这样写:

users.hasOwnProperty('Alan');
'Alan' in users;

这两者结果都应该为 true。

请完善这个函数,如果传递给它的对象包含四个名字 Alan、Jeff、Sarah 和 Ryan,函数返回 true,否则返回 false。

使用 for…in 语句遍历对象

有时候你需要遍历一个对象中的所有键。 你可以使用 for...in 循环来做这件事。 for…in 循环是这样的:

const refrigerator = {
  'milk': 1,
  'eggs': 12,
};

for (const food in refrigerator) {
  console.log(food, refrigerator[food]);
}

以上代码记录 milk 1 和 eggs 12,每个键值对单独为一行。

我们在循环头中定义了变量 food ,这个变量被设置为每次迭代上对象的每个键值,将每个食物的名称打印到控制台。

注意:对象中的键是无序的,这与数组不同。 因此,一个对象中某个属性的位置,或者说它出现的相对顺序,在引用或访问该属性时是不确定的。

我们定义了一个函数 countOnline,它接收一个参数 allUsers。 在这个函数中使用 for…in 语句来遍历 allUsers 对象,并返回 online 属性为 true 的用户数量。 一个可以传递给 countOnline 的对象的例子显示如下。 每个用户都有 online 属性,其属性值为 true 或 false。

{
  Alan: {
    online: false
  },
  Jeff: {
    online: true
  },
  Sarah: {
    online: false
  }
}

使用 Object.keys() 生成由对象的所有属性组成的数组

我们可以给 Object.keys() 方法传入一个对象作为参数,来生成包含对象所有键的数组。 这个方法将对象作为参数并返回代表对象中每个属性的字符串数组。 需要注意的是,数组中元素的顺序是不确定的。

请完成 getArrayOfUsers 函数的实现,使其返回一个由输入对象中的所有属性所组成的数组。

let users = {
  Alan: {
    age: 27,
    online: false
  },
  Jeff: {
    age: 32,
    online: true
  },
  Sarah: {
    age: 48,
    online: false
  },
  Ryan: {
    age: 19,
    online: true
  }
};

function getArrayOfUsers(obj) {
  // 只修改这一行下面的代码
return Object.keys(obj)
  // 只修改这一行上面的代码
}

console.log(getArrayOfUsers(users));

修改存储在对象中的数组

我们已经学习了 JavaScript 对象的这些基本操作: 添加、修改、移除键值对、检查某个属性是否存在、遍历对象的所有属性。 在继续学习 JavaScript 的过程中,我们会了解对象的更多用法。 另外,在之后的数据结构课程中,我们还会学习 ES6 的 Map 和 Set。 这两种数据结构与我们现在学到的对象十分类似,但它们在对象的基础上提供了一些额外的功能。 目前,我们已经学习了数组和对象的基础知识,让我们试着来用所学的知识解决一些更复杂的问题。

请看一下代码编辑器中我们为你写好的对象。 user 对象包含 3 个属性; data 对象包含 5 个属性,其中包含一个叫做 friends 的数组。 这就是对象作为数据结构所展现出的灵活性。 我们已经写好了 addFriend 函数的一部分, 请你完成这个函数,使其接受一个 user 对象,将 friend 参数中的名字添加到 user.data.friends 数组中并返回该数组。

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

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

相关文章

Linux mmap系统调用视角看缺页中断

问题 1. mmap具体是怎么实现比read/write少一次内存copy的 2.mmap共享映射和私有映射在内核实现的时候到底有什么区别 3.mmap的文件映射和匿名映射在内核实现的时候到底有什么区别 4.父子进程的COW具体怎么实现的 概述 实际开发过程中经常使用或者看到mmap函数&#xff0…

【MySQL】表的内连和外连

本期我们来谈谈表的连接 目录 一、内连接 二、外连接 2.1 左外连接 2.2 右外连接 一、内连接 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选&#xff0c;我们上期学习的复合查询都是内连接&#xff0c;也是在开发过程中使用的最多的连接查询 使用内连接时…

使用windows搭建WebDAV服务,并内网穿透公网访问【无公网IP】

文章目录 1. 安装IIS必要WebDav组件2. 客户端测试3. 使用cpolar内网穿透&#xff0c;将WebDav服务暴露在公网3.1 打开Web-UI管理界面3.2 创建隧道3.3 查看在线隧道列表3.4 浏览器访问测试 4. 安装Raidrive客户端4.1 连接WebDav服务器4.2 连接成功4.2 连接成功 1. Linux(centos8…

Qt 文件对话框使用 Deepin风格

当你在Deepin或UOS 上开发 Qt 程序时&#xff0c;如果涉及到文件对话框功能&#xff0c;那么就会遇到调用原生窗口的问题。 如果你使用的是官方的Qt版本&#xff0c;那么在Deepin或者UOS系统上&#xff0c;弹出的文件对话框会是如下这样&#xff1a; 而Deepin或UOS系统提供的默…

考研数据结构:第七章 查找

文章目录 一、查找的基本概念二、顺序查找和折半查找2.1顺序查找2.3折半查找2.3.1算法思想2.3.2代码实现2.3.3查找效率分析2.3.4折半查找判定树的构造2.3.5折半查找效率2.3.6小结 2.4分块查找 三、树形查找3.1二叉排序树3.1.1二叉排序树定义3.1.2查找操作3.1.3插入操作3.1.4二叉…

DVI端口静电防护,如何设计保护电路图?

Digital Visual Interface&#xff0c;简称DVI&#xff0c;中文名&#xff1a;数字视频接口&#xff0c;是一种视频接口标准&#xff0c;用来传输未经压缩的数字化视频&#xff0c;广泛应用于LCD、数字投影机等显示设备上。DVI端口的种类非常多&#xff0c;有DVI-A、DVI-D、DVI…

Rx.NET in Action 第二章学习笔记

2 Hello, Rx 本章节涵盖的内容: 不使用Rx的工作方式向项目中添加Rx创建你的第一个Rx应用程序 Rx 的目标是协调和统筹来自社交网络、传感器、用户界面事件等不同来源的基于事件的异步计算。例如&#xff0c;建筑物周围的监控摄像头和移动传感器会在有人靠近建筑物时触发&#xf…

threejs中gltf模型出现的问题(黑色,颜色不协调,太小)和解决方案

模型一片漆黑 如下图 可能原因&#xff0c;没有灯光&#xff0c;加下以下代码&#xff1a; // 4、加入灯光 const lightness new THREE.HemisphereLight(0xffffff, 0x444444); lightness.position.set(0, 20, 0); scene.add(lightness); const shadowLight new THREE.Direct…

文献综述|NLP领域后门攻击、检测与防御

前言&#xff1a;在信息安全中后门攻击&#xff08;Backdoor Attack&#xff09;是指绕过安全控制而获取对程序或系统访问权的方法。而随着深度学习以及各种神经网络模型的广泛应用&#xff0c;神经网络中存在的后门问题也引起了研究人员的广泛关注。神经网络后门攻击就是使网络…

robotframework+selenium 进行webui页面自动化测试

robotframework其实就是一个自动化的框架&#xff0c;想要进行什么样的自动化测试&#xff0c;就需要在这框架上添加相应的库文件&#xff0c;而用于webui页面自动化测试的就是selenium库. 关于robotframework框架的搭建我这里就不说了&#xff0c;今天就给大家根据一个登录的实…

淘宝API接口的实时数据和缓存数据区别

电商API接口实时数据是指通过API接口获取到的与电商相关的实时数据。这些数据可以包括商品库存、订单状态、销售额、用户活跃度等信息。 通过电商API接口&#xff0c;可以实时获取到电商平台上的各种数据&#xff0c;这些数据可以帮助企业或开发者做出及时的决策和分析。例如&…

web会话跟踪以及JWT响应拦截机制

目录 JWT 会话跟踪 token 响应拦截器 http是无状态的&#xff0c;登录成功后&#xff0c;客户端就与服务器断开连接&#xff0c;之后再向后端发送请求时&#xff0c;后端需要知道前端是哪个用户在进行操作。 JWT Json web token (JWT), 是为了在网络应用环境间传递声明而…

Vue3 大屏数字滚动效果

父组件&#xff1a; <template> <div class"homePage"> <NumRoll v-for"(v, i) in numberList" :key"i" :number"v"></NumRoll> </div> </template> <script setup> import { onMounted, r…

安装elasticsearch

一、docker安装elasticsearch 1、下载镜像 docker pull elasticsearch:6.5.4 2、启动容器 docker run -p 9200:9200 -p 9300:9300 --name elasticsearch \ -e "discovery.typesingle-node" \ -e "cluster.nameelasticsearch" \ -e "ES_JAVA_OPTS-Xm…

答疑:Arduino IDE配置其他开发板下载速度慢

基于案例&#xff1a;Linux环境Arduino IDE中配置ATOM S3 通常&#xff0c;网络问题较多&#xff0c;可以使用一些技巧。 https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json 没有配置&#xff0c;不支持M5Stack&#xff08;ESP32&…

【设计模式】前端控制器模式

前端控制器模式&#xff08;Front Controller Pattern&#xff09;是用来提供一个集中的请求处理机制&#xff0c;所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志&#xff0c;或者跟踪请求&#xff0c;然后把请求传给相应的处理程序。以下是这种…

机器学习笔记之优化算法(十二)梯度下降法:凸函数VS强凸函数

机器学习笔记之优化算法——梯度下降法&#xff1a;凸函数VS强凸函数 引言凸函数&#xff1a;凸函数的定义与判定条件凸函数的一阶条件凸函数的梯度单调性凸函数的二阶条件 强凸函数强凸函数的定义强凸函数的判定条件强凸函数的一阶条件强凸函数的梯度单调性强突函数的二阶条件…

小白带你学习linux的MongoDB(三十四)

目录 一、概述 1、相关概念 2、特性 二、应用场景 三、安装 四、目录结构 五、默认数据库 1、admin&#xff1a; 2、local: 3、config: 六、数据库操作 1、库操作 2、文档操作 七、MongoDB数据库备份 1、备份命令 2、回数据库删除…

Android侧滑栏(一)可缩放可一起移动的侧滑栏

在实际的各类App开发中&#xff0c;经常会需要做一个左侧的侧滑栏&#xff0c;类似于QQ这种。 今天这篇文章总结下自己在开发中遇到的这类可以跟随移动且可以缩放的侧滑栏。 一、实现原理 使用 HorizontalScrollView 实现一个水平方向的可滑动的View&#xff0c;左布局为侧滑…

纷享销客稳居2022 H2 SFA SaaS 本土CRM厂商市场份额 TOP 1

近期&#xff0c;国际知名研究机构IDC公布了2022年下半年《中国客户关系管理(CRM)SaaS市场跟踪研究报告》&#xff0c;报告全面解析了中国CRM SaaS以及细分市场SFA SaaS的市场现状&#xff0c;并对全球各大厂商在中国SFA市场的份额占比进行了排名。连接型CRM开创者纷享销客在SF…