Live2D

JavaScript从入门到放弃 第四步

1. new Array() 创建数组

缺点:当参数只有一个的时候创建为你填写的数字的长度,内容为undefined的数组

new Array(1,2,3) 
arr => [1,2,3]

// 可以简写省略new
Array(1,2,3)

Array(2)
new Array(2)
arr => [empty x 2] 实际每项内容为undefined相当于 [undefined, undefined]

2. Array.of() 创建数组

优点:解决上面方法的弊端

Array.of()可以把一组参数转换为数组。替代es5之前的方法 Array.prototype.slice.call(arguments)
接收一组参数,创建数组

1.
Array.of(2)  
arr => [2]

2.
Array.of('a');
arr => ['a']

3.
Array.of(1,2,3)
arr => [1,2,3]

3. Array.from()

// 将第一个参数有length的属性转为数组,比如字符串,伪数组,数组,arguments等,第二个参数为回调函数(callback)
// 返回值为数组
1./*因为str有length属性*/
const str = 'abcd'  
Array.from(str)
arr => ['a', 'b', 'c', 'd']

2./*对象没有length属性不能解析返回的是个空数组*/
const o = {
    name: 's',
    age: 18
}
Array.from(o)
arr => []

3./*同样对象没有length属性不能解析返回的是个空数组*/
const o2 = {
    0: 's',
    1: 18
}
Array.from(o2);
arr => []

4./*对象有length属性,解析length长度就会,返回为length的值的数组,但是属性不是iterator*/
const o3 = {
    name: 's',
    age: 18,
    length: 2
}
Array.from(o3);
arr => [undefied, undefined]; // 返回长度为o3.length,值为undefined的数组

5./*但是这样,对象有length属性并且属性值为索引值,可以被解析*/
const obj = {
    0: 'aaa',
    1: 'bbb',
    2: 'ccc',
    length: 3
}
Array.from(obj);
arr => ["aaa", "bbb", "ccc"]

6.
const obj = {
    name: 'xiaoming',
    1: 18,
    length: 2
}
console.dir(Array.from(obj));
[undefined, 18] // 首先根据length属性的值,来创建长度为length的数组,然后数组的第一项(索引为0),去找以0为属性取填充,如果没有则为undefined,如果有取其值,然后再找,以1为属性的值,作为数组的第二项(索引为1),其实也就是:数组内每一项的值,是根据数组每一项的索引值,去对象中找是否有对应的索引值属性,如果有取其值,没有取undefined

7. 第二个参数,作用类似数组的map方法,用来对每一个元素进行处理,将处理后的值放入返回的数组中
const obj = {
    name: 'xiaoming',
    1: 18,
    length: 2
}
let newObj = Array.from(obj, item => item || 'zcy');
["zcy", 18]
console.dir(Array.from(obj)); // 不会改变原数组

8.第三个参数,类似与map的第二个参数,与this绑定
const obj = {
    name: 'xiaoming',
    1: 18,
    length: 2
}
let newObj = Array.from(obj, function(item) {
    console.log(this);
    return item || this.age;
}, { age: 19 });

// [19, 18]
# 注意: 此处函数如果写箭头函数,那么this就是window而不是 { age: 19 } 这个对象,所以如果使用传入的this就不能使用箭头函数

9.实现浅拷贝(传入一组数组)
let arr = [1,2,3,{},4];
arr2 = Array.from(arr);

10.将迭代器转为数组
转为迭代器的数组方法,values(),keys(),entries();
let arr = [2,2,4,6,1];
Array.from(arr.values()); // [2, 2, 4, 6, 1]
Array.from(arr.keys()); // [0, 1, 2, 3, 4]
Array.from(arr.entries()); // [[0, 2],[1, 2],[2, 4],[3, 6],[4, 1]]

其实我们可以使用for...of直接遍历迭代器后面会讲

4. Array.isArray() 检测是否为数组

/*检测数组是不是数组*/
const arr = [];
Array.isArray(arr);
=> true

const obj = {};
Array.isArray(obj);
=> false

5. 将数组转换为字符串的方法

1. toString() 方法
const arr = [1, 2, 3];
const str = arr.toString();
typeof str	
str => "1,2,3"
"string"

2. String() 方法
const arr2 = [4, 5, 6];
const str2 = String(arr2);
typeof str2	
str => "4,5,6"
"string"

3. join() 方法
const arr3 = [7, 8, 9];
const str3 = arr3.join('-');
str3 => "7-8-9"

6. 将字符串转换为数组的方法

1./**需要传入根据什么字符分割数组**/
let str = "zcy";
let arr = str.split(""); /*根据空格分割为数组*/
arr => ['z', 'c', 'y']

2. 
let str = "zcy";
let arr = str.split(); /*直接将字符串转换为数组(不管字符串有没有符号)*/
arr => ['zcy']

3. 使用扩展运算符
let str = "zcy";
let strArr = [...str]; // ["z", "c", "y"] 与split不传参数结果是一样的

4. 使用Array.from()
let str = "zcy";
let strArr =Array.from(str); // ["z", "c", "y"] 与split不传参数结果是一样的

7.【 ... 】展开运算符

扩展运算符适合展开有iterable接口的,例如字符串,arguments,NodeList(DOM节点),数组等 例如[...str],但是对象没有iterable也可以展开来进行对象的混入

1.数组的展开运算符
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [...arr1, ...arr2];
改变arr3不会改变arr2和arr1
console.table(arr3);

2.函数参数的展开运算符

// 数组的参数不一定时
// 【注意】这里用...args,在ES6中叫做不定参数,和arguments的效果类似,把传入的所有参数,作为一个array赋值给args
// 可以这样理解,当...args在变量的位置时,args就是一个数组,吸收所有参数。当...args在值的位置,就是展开数组中的值,打散。
// 定义一个求和的函数
function sum (...args) {
    console.log(args); // [1,2,3]
    return args.reduce((pre, cur) => {
        return pre + cur;
    }, 0)
}
sum(1,2,3)

3. 字符串展开
let str = 'abcd';
[...str] // ["a", "b", "c", "d"]

4. NodeList(DOM节点)展开
let NodeList = document.querySelectorAll('div');
[...NodeList] //  [div#oneGoogleBar, div#oneGoogleBarEndOfBody] 百度首页可查看

5. 使用展开运算符进行对象的混入,不能进行转换为数组
let obj1 = { name: 'sb', age: 2 };
let obj2 = { sex: 'w' };
let obj3 = {
  ...obj1,
  ...obj2  
}

8. 遍历dom节点伪数组

<div> aaa </div>
<div> bbb </div>
const divs = document.querySelectorAll('div'); // 获取到的是dom元素的伪数组不能使用数组的遍历方法 除forEach 有兴趣的话 可以查看原型 看看有哪些方法
1. // 借用数组的map()方法
Array.prototype.map.call(divs, item => {
    console.log(item);
})

2. // 借用数组的map()方法
[].map.call(divs, item => {
    console.log(item);
})

3.
Array.from(divs).map(item => {
    console.log(item);
})

4. 
   // NodeList 自身有forEach但没有map方法 转为数组可以用Array.from
[...divs].map(item => {
    console.log(item)
})

9. 数组的解构赋值

数组的解构就是右侧数组的值,平均分配给左侧变量

1.以往的赋值
const arr = ['zcy',18];
const name = arr[0];
const age = arr[1];

2.解构赋值
const arr = ['zcy',18];
const [name, age] = arr;

3.使用展开运算符当变量(右边数值,比左侧变量多可以用...语法全部接收右侧的值)
const arr = ['zcy', 18, '男'];
const [name, ...args] = arr;
name => 'zcy'
args => [18, '男'];

4.当变量数量大于值数量(右边数值,比左侧变量少,如果没设置默认值少的用undefined填补)
const arr = ['zcy'];
const [name, age] = arr;
name => 'zcy'
age => undefined;

5.当变量没值,可以给变量设置默认值
const arr = ['zcy'];
const [name, age = 18] = arr;
name => 'zcy'
age => 18

6.当变量有值,还设置默认值的话,优先取赋值的值
const arr = ['zcy', 19];
const [name, age = 18] = arr;
name => 'zcy'
age => 19

10. 数组的添加和删除方法

栈方法:

ECMAScript 给数组提供几个方法,让它看起来像是另外一种数据结构。数组对象可以像栈一样, 也就是一种限制插入和删除项的数据结构。栈是一种后进先出(LIFO,Last-In-First-Out)的结构,也就 是最近添加的项先被删除。数据项的插入(称为推入,push)和删除(称为弹出,pop)只在栈的一个 地方发生,即栈顶。ECMAScript 数组提供了 push()和 pop()方法,以实现类似栈的行为。

栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)

1. push()方法

### 向数组末尾添加元素

1.利用数组长度向数组末尾添加元素
const arr = [1, 2, 3];
arr[arr.length] = 4;
arr => [1, 2, 3, 4]

2.利用数组的push()方法向数组末尾添加元素【push()方法的参数可以为多个】
const arr = [1, 2, 3];
arr.push(4); // push()方法返回值为数组的长度 【4】可以用变量去接收
const arrLength = arr.push(4);
arrLength => 4;
arr => [1, 2, 3, 4]

3.利用数组的push()方法与展开运算符向数组末尾添加多个元素
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2); // 此处是将arr2数组的值展开
arr1.push(4, 5, 6); // 相当于这样
arr1.push(...arr2,7,8) // 那同样也可以这样

arr => [1, 2, 3, 4, 5, 6, 7, 8];

2. pop()方法

### 删除数组末尾元素

const arr = [1, 2, 3];
arr.pop(); 
const arrItem = arr.pop(); // pop()方法返回值为删除的元素 【3】

arrItem => 3;
arr => [1, 2]

队列方法:

就像栈是以 LIFO 形式限制访问的数据结构一样,队列以先进先出(FIFO,First-In-First-Out)形式 限制访问。队列在列表末尾添加数据,但从列表开头获取数据。因为有了在数据末尾添加数据的 push() 方法,所以要模拟队列就差一个从数组开头取得数据的方法了。这个数组方法叫 shift(),它会删除数 组的第一项并返回它,然后数组长度减 1。使用 shift()和 push(),可以把数组当成队列来使用:

3. unshift()方法

### 向数组头部添加元素

1.向数组头部添加一个元素
const arr = [1, 2, 3];
arr.unshift(0); 
const arrLength = arr.unshift(0); // unshift()方法返回值为数组长度

arrLength => 4;
arr => [0, 1, 2, 3]

2.向数组头部添加多个元素
const arr = [1, 2, 3];
arr.unshift(-1, 0); 
const arrLength = arr.unshift(0); // unshift()方法返回值为数组长度

arrLength => 5;
arr => [-1, 0, 1, 2, 3]

3.利用数组的unshift()方法与展开运算符向数组头部添加多个元素
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr2.unshift(...arr1); // 此处是将arr2数组的值展开
arr1.unshift(1, 2, 3); // 相当于这样
arr1.unshift(-1,...arr2); // 那同样也可以这样

4. shift()方法

### 删除数组头部元素 arr.shift(); 

const arr = [1, 2, 3];
const arrItem = arr.shift(); // shift()方法返回值为删除的元素 【1】

arrItem => 1;
arr => [2, 3]

5. 总结:

数组的添加方法push,unshift方法返回值为数组的长度。

数组的删除方法shift,pop方法返回值为删除的元素

11. fill()数组填充

const arr = Array(6); // [empty × 6]
arr.fill('1'); // 使用1填充上面的数组
arr => [1, 1, 1, 1, 1, 1]

arr.fill(3, 2); // 使用值为3填充上面数组,从index为2的元素开始填充包括2
arr => [1, 1, 3, 3, 3, 3]

arr.fill(3, 2, 3); // 使用值为3填充上面数组,从index为2的元素开始填充(包括2)到截至到index为3不包括3
arr => [1, 1, 3, 1, 1, 1]

12. slice()数组的截取方法

### 不改变原数组
### slice: 切; 割; (v)

const arr = [1, 2, 3, 4, 5, 6];
1.// 当没有参数的时候,代表不截取,返回原组的一个副本(是浅拷贝哦,如果数组每一项都是基本数据类型,可以看作深拷贝,但是数组至少一项为引用类型,那么是浅拷贝哦)
arr.slice()
=> [1, 2, 3, 4, 5, 6]

2.//当传入一个参数代表从哪个位置截取(包括那个位置)
arr.slice(2) // 从下标值为2的位置开始截取
=> [3, 4, 5, 6];

3.//当传入两个参数代表从哪个位置截取(包括那个位置)截取到哪个位置(不包括后者)
arr.slice(2, 4)
=> [3, 4]

13. splice()数组添加截取方法

### 改变原数组
### 向/从数组中添加/删除项目,然后返回被删除的项目。
### splice: 胶接,粘接 (v)
### 返回值为删除的数组,如果未删除 则返回空数组
array.splice(index,howmany,item1,.....,itemX)

index	必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany	必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1, ..., itemX	可选。向数组添加的新项目。

1.删除数组
const arr = [1, 2, 3, 4, 5];
arr2 = arr.splice(0, 3); // 截取数组
arr = [4, 5];
arr2 = [1, 2, 3];

2. 删除并新增
const arr = [1, 2, 3, 4, 5];
arr2 = arr.splice(0, 3, 'zcy'); // 截取数组
arr = ['zcy',4, 5];
arr2 = [1, 2, 3];

3.替换
const arr = [1, 2, 3, 4, 5];
arr2 = arr.splice(2, 1, 'zcy'); // 截取数组
arr = [1, 2, "zcy", 4, 5];
arr2 = [3];

splice应用的例子
// 移动数组中一个元素的位置到另一个位置的函数
function move (arr, from, to) {
    // 如果传入的不是数组return
    if(!Array.isArray(arr)) {
        return;
    }
    
    // 判断传入的下标是否为其他类型如果传入不是number类型那么return
    if(typeof from !== 'number' && typeof to !== 'number') {
        return;
    }
    
    // 判断传入的下标是否为负数,如果为负数return
    if(from < 0 || to < 0) {
        return;
    }
    
    // 判断传入的下标超过数组的长度,如果超过return
    if(from > arr.length || to > arr.length) {
        return;
    }
    
    // 不能改变原数组所以我们要创建一个新数组, 对新数组操作
    const tempArr = [...arr];
    const item = tempArr.splice(from, 1); //  返回来的是截取一位数组
    tempArr.splice(to, 0, ...item);
    return tempArr;
}
const arr = [1,2,3,4,5];
move(arr,1,4); // 数组的第一个索引和第三个索引的数进行置换

14. 清空数组

let arr = [1,2,3];
let hd = arr;
1.
arr = [] // 会新开辟一个为空数组内存地址,并指向arr,并不是清空原先的数组,arr和hd引入的不是同一地址 赋值null undefined会有同样的效果
hd => [1,2,3]
arr => []

2.
arr.length = 0; // 不会新开辟一个为空数组内存地址,而是清空原先的数组,arr和hd引入的还是同一地址数组都为空了
hd => []
arr => []

3.
arr.splice(0, arr.length);
arr => []

4.
while(hd.pop()); // 解释一下 因为 删除成功会返回删除的元素 但是如果是一个空数组调用pop方法 因为没元素可删除 所以 返回undefined 当while(undefined)的时候不执行了所以就会一直删,听懂了么,听懂的打个懂字(手动滑稽)

15. join()数组转为字符串方法

join 连接,加入
split 分裂,分割

const arr = ['zcy', '18'];
const str = arr.join('/'); // 可以不加分隔符,也可以随意加分隔符
str => zcy/18

// 字符串转为数组  
const str = 'zcy-18'
const arr = str.split('-'); // 以什么形式分割为数组,不传参数默认是以点空格作为分隔符
arr => ['zcy', '18'];

16. concat()数组拼接方法

concat()方法可以在现有数组全部元素基础上创建一个新数组。它首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组。如果传入一个或多个数组,则 concat()会把这些数组的每一项都添加到结果数组。如果参数不是数组,则直接把它们添加到结果数组末尾。

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

1.
const arr3 = arr1.concat(arr2); // 不会改变元素组
arr3 => [1, 2, 3, 4, 5, 6];
arr1 => [1, 2, 3];
arr2 => [4, 5, 6];

//我们更多的时候使用【...】语法
2.
arr3 = [...arr1, ...arr2];

3.浅复制数组
let copyArr = arr1.concat(); // [1, 2, 3];

4.
let copyArr = arr1.concat(4); // [1, 2, 3, 4]

17. copyWithin()

// 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
array.copyWithin(index, start, end);
// 会改变改变元素组
index	必需。从该位置开始替换数据
start	可选。从哪个位置开始复制数据
end	    可选。停止复制位置 (默认为 array.length)。如果为负值,表示倒数。不包括它

const fruits = ["Banana", "Orange", "Apple", "Mango"];
1.
fruits.copyWithin(2, 0); // 从下标为0开始复制,如果没有结束位置复制到数组最后一项,将复制到元素,从下标为2的位置开始复制
fruits => ['Banana', 'Orange', 'Banana', 'Orange']

2.
fruits.copyWithin(2,1,2);
fruits => ['Banana', 'Orange', 'Orange', 'Mango']

解释一下 要不然很多人不是很懂,第一个参数2就是代表我们从原数组fruits[2]的值开始替换,而替换的内容是什么呢 是第二个参数1 fruits[1],和第三个参数2,fruits[2]这之间的值,包括开始的值,不包括结束的值,如果 替换的内容有多个 那么就替换多个 直到替换到多个完成

18. 查找数组元素的方法

1. indexOf()方法

arr.indexOf(searchvalue,from); // 字符串也有此方法
searchvalue	必需需检索的数组值。
from 可选参数。开始检索的位置,取值是 0 到 arr.length - 1。
// 从左往右开始查询,返回查找到的下标值,如果没有查到返回【-1】
// 区分大小写,区分数字与字符串,严格类型的查询。

const arr = [1,2,3,4,1];

arr.indexOf(2);
=> 1 // (返回的是首次出现的下标值) 如果查找不到返回-1

arr.indexOf(1,2); // 从下标为2的位置开始查
=> 4
arr.indexOf(1,-1);
=> 4
arr.indexOf(1,-2);
=> 4
arr.indexOf(1,-3);
=> 4
arr.indexOf(1,-4);
=> 4
// 总结:个人认为当使用indexOf第二个参数为负数不生效,因为下标为负数代表从右侧查询与indexOf从左侧开始查相反故不生效

2. lastIndexOf()方法

arr.lastIndexOf(searchvalue,from); // 字符串也有此方法
searchvalue	必需需检索的数组值。
from 可选参数。开始检索的位置,取值是 0 到 arr.length - 1。
// 从右往左开始查询,返回查找到的下标值,如果没有查到返回【-1】
// 区分大小写,区分数字与字符串,严格类型的查询。

const arr = [1,2,3,4,1];

arr.lastIndexOf(1)
=> 4

arr.lastIndexOf(1,0); // 从0开始查

3. includes()方法

arr.includes(value);  // 字符串也有方法
// 返回boolean值

// includes实现原理
const arr = [1,2,3,4,1];
function includes(arr, el) {
    for(value of arr) {
       if(value === el) {
           return true
       }
    }
     return false;
}

arr.includes(1);
=> true
arr.includes(5);
=> false

const arr = [{ name: 'zcy' }, { name: 'xm' }, { name: 'sm' }];
arr.includes({name: 'zcy'});
=> false
// 引用类型比较的时候,是比较内存地址

4. find()方法

find() 方法返回为true的数组的 第一个元素的值。

当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 undefined
## 注意: find() 对于空数组,函数是不会执行的。
## 注意: find() 并没有改变数组的原始值。

const arr = [1,2,3,4,5,6,2];

1.
arr.find(el => {
    return el === 1; // 当el === 1的时候return true 把true对应的值返回
}) 
=> 1

2.
arr.find(el => {
    return el === 2; // 由于有两个2所以只会返回第一个就近原则
}) 
=> 2

// 继续说上面例子
const arr = [{ name: 'zcy' }, { name: 'xm' }, { name: 'sm' }];
arr.find(item => {
    return item.name === 'zcy'
});
=> {name: 'zcy'}
// 引用类型比较的时候,是比较内存地址

5. findIndex()方法

findIndex()与find()方法类似
findIndex方法()返回的是值所对应的下标值。

// 继续拿上面例子来说
const arr = [{ name: 'zcy' }, { name: 'xm' }, { name: 'sm' }];
arr.findIndex(item => {
    return item.name === 'zcy'
});
=> 0 // 返回的是下标值

const arr = [{ name: 'zcy' }, { name: 'xm' }, { name: 'sm' }, { name: 'zcy' }];
arr.findIndex(item => {
    return item.name === 'zcy'
});
=> 0 // 返回的是第一个满足条件元素的下标值所以是0不是3

19. 数组排序sort()方法

const arr = [2,1,3,6,4,5]
// arr1 = [1,2];
// arr2 = [2,1];
例如a等于1,b等于2,
a-b = 1-2为负数那么从小到大排列,
b-a = 2-1为正数那么从大到小排列

1.// 为负数从小到大 a-b为负数(会改变原数组)
arr1 = arr.sort((a,b) => {
    return a - b; 
})
arr1 => [1, 2, 3, 4, 5, 6]

2.// 为正数从大到小 b-a为正数(会改变原数组)
arr.sort((a,b) => {
    return b - a;
})
arr => [6, 5, 4, 3, 2, 1]


例子:根据商品价格从小到大排序
const arr = [{ name: '短裙', price: 100 }, { name: 'jk', price: 99 }, { name: 'ipad', price: 12000 }]
arr.sort((a,b) => {
    return a.price - b.price;
})

arr => (3) [{name: "jk", price: 99}, {name: "短裙", price: 100}, {name: "ipad", price: 12000}]


## sort方法原理
const arr = [2, 1, 3, 6, 4, 5];

// 这样写的是从小到大排序
function sort (arr) {
    for(const x in arr) {
        for(const y in arr) {
            console.log(arr[x])
            if(arr[x] < arr[y]) {
                const temp = arr[x];
                arr[x] = arr[y];
                arr[y] = temp;
                console.log(arr)
            }
        }
    }
    return arr;
}

// 那么如何进行改造像我们sort方法那样呢
function sort(a, b) {
    return a - b;
}

function sort (arr,callback) {
    for(const x in arr) {
        for(const y in arr) {
            if(callback(arr[x] , arr[y]) < 0) {
                const temp = arr[x];
                arr[x] = arr[y];
                arr[y] = temp;
            }
        }
    }
    return arr;
}

1.
sort(arr, function(a, b) {
    return a-b;
});
2.
sort(arr, (a, b) => a-b);

20.数组的循环方法

1. for循环

let arr = [{ name: 'zcy' }, { name: 'xm' }, { name: 'sm' }];

for(let i = 0; i < arr.length; i++) {
    arr[i].name = `${arr[i].name}最帅`
}
arr => arr => [
    {name: "zcy最帅"},
    {name: "xm最帅"},
    {name: "sm最帅"}
]
# 注意for循环还可以遍历dom数组
const list = document.querySelectorAll('div')
for(let i = 0; i < list.length; i++) {
    list[i].style.background = 'pink'
};

2. for... in循环

let arr = [{ name: 'zcy' }, { name: 'xm' }, { name: 'sm' }];

// i为下标值
for(let i in arr) {
   arr[i].name = `${arr[i].name}最帅`;
}

arr => [
    {name: "zcy最帅"},
    {name: "xm最帅"},
    {name: "sm最帅"}
]

 # 注意不能遍历dom伪数组
 # 如果想遍历dom数组,那么需要使用Array.from()或者【...】转为数组才可以遍历
const list = document.querySelectorAll('div')
for (let value in Array.from(list)) {
    Array.from[value].style.background = 'pink';
}

3. for...of循环

let arr = [{ name: 'zcy' }, { name: 'xm' }, { name: 'sm' }];

// value为遍历的每一项
for(let value of arr) {
   value.name = `${value.name}最帅`;
}

arr => [
    {name: "zcy最帅"},
    {name: "xm最帅"},
    {name: "sm最帅"}
]

// 其实是改变数组中的对象,由于对象是引用类型,所以可以修改原数组,
// 但原数组的每一项都是,值类型那不不会对原数组有任何改变
我们来看下个例子

let arr = [1,2,3];

for(let value of arr) {
   value = '帅'; // 试图修改数组中的每一项
   console.log(value); // '帅'
}
arr => [1,2,3] 
// 结果是没修改成功,因为不是引用类型,会新开辟一块空间赋值给value变量这样就没有修改原数组,可以理解为【副本】

# 注意不能遍历dom数组
# 如果想遍历dom数组,那么需要使用Array.from()或者【...】转为数组才可以遍历
const list = document.querySelectorAll('div')
for (let value of Array.from(list)) {
    value.style.background = 'pink';
}

4. forEach()循环

let arr = [{ name: 'zcy' }, { name: 'xm' }, { name: 'sm' }];

item 为遍历数组的每一项
index 为遍历每一项的下标值
arr 为遍历的数组

arr.forEach((item, index, arr)) {
    item.name = `${item.name}最帅`;
}

// 注意:同样也是只能改变引用类型
arr => [
    {name: "zcy最帅"},
    {name: "xm最帅"},
    {name: "sm最帅"}
]

# 注意forEach还可以遍历dom数组

5. 迭代iterator方法遍历数组

const arr = [{ name: 'zcy' }, { name: 'xm' }, { name: 'sm' }];
# 可迭代的对象就可以使用next()方法
const values = arr.values(); // 返回的是一个迭代对象,取的是值
const keys = arr.keys(); //返回的是一个迭代对象,取的是索引
let { value, done } = values.next(); // 从迭代对象中取出值
console.log(values.next()); 
console.log(values.next());

# 当使用values(),value是值
# 当使用keys(),value是索引
# done代表的是是否迭代完成,false代表没有迭代完成

const entries = arr.entries(); // 返回的是一个迭代对象 是keys和values的综合方法
console.log(entries.next()); // 执行一边的话会只打印第一次迭代的对象

let { done, value: [index, item] } = entries.next()
=> { done: false, value: [1, { name: 'zcy' }] } // 1是索引 { name: 'zcy' }是值

// for...of遍历迭代器
for (const [key, value] of arr.entries()) {
    console.log(key, value);
}

const arr = [{ name: 'zcy' }, { name: 'xm' }, { name: 'sm' }];
const values = arr.values();
while(({ value, done } = values.next()) && done === false) {
    console.log(value); // 也能遍历数组
}

21. 数组的some与every方法

1. some()

// some 返回结果是个boolean值
// some 接收一个回调函数作为参数
// 回调函数有三个参数,分别为数组循环的每一项,索引值,和数组
// 代表数组中只要有一项满足条件就返回true,否则返回false
const arr = [
    { name: 'zcy', achievement: '100' },
    { name: 'sm', achievement: '89' },
    { name: 'xm', achievement: '59' }
];

`假如我想要查询全班是否有未及格的人`

arr.some(function(item, index, arr) {
    return item.achievement < 60; // 只要有一个满足返回的条件那么就返回true
});

简写为:
arr.some(item => item.achievement < 60);

=> true


2. every()

// every 返回结果是个boolean值
// every 接收一个回调函数作为参数
// 回调函数有三个参数,分别为数组循环的每一项,索引值,和数组
// 代表数组的每一项都满足条件就返回true,否则返回false
const arr = [
    { name: 'zcy', achievement: '100' },
    { name: 'sm', achievement: '89' },
    { name: 'xm', achievement: '59' }
];

`假如我想要查询全班考试是否都未及格`

arr.every(function(item, index, arr) {
    return item.achievement < 60; // 每一项都满足条件,那么就返回true
});

简写为:
arr.every(item => item.achievement < 60);

=> false


22. 数组的filter()方法

// filter 返回结果是一个数组,不会改变原数组,但是返回的引用类型数组是浅拷贝,值类型的数组是深拷贝
// filter 接收一个回调函数作为参数
// 回调函数有三个参数,分别为数组循环的每一项,索引值,和数组
// 返回数组满足条件的每一项组成的新数组
const arr = [
    { name: 'zcy', achievement: 100 },
    { name: 'sm', achievement: 100 },
    { name: 'xm', achievement: 59 }
];

arr.filter(item => {
 return item.achievement === 100;
})

arr => [
    { name: 'zcy', achievement: 100 },
    { name: 'sm', achievement: 100 }
];

23. 数组的map()方法

// map 返回结果是一个数组,不会改变原数组,但是返回的引用类型数组是浅拷贝,值类型的数组是深拷贝
// map 接收一个回调函数作为参数
// 回调函数有三个参数,分别为数组循环的每一项,索引值,和数组
// 返回数组return每一项组成的新数组

1.数组的每一项都为,值类型的数组
const arr = [1, 2, 3, 4, 5];

const newArr = arr.map((item, index, arr) => {
    return false // 返回什么新数组就是什么
})

newArr => [false, false, false, false, false];
arr => [1, 2, 3, 4, 5]; // 不会改变原数组


const newArr = arr.map((item, index, arr) => {
    return `LimitLess${item}` // 返回什么新数组就是什么
})

newArr => ["LimitLess1", "LimitLess2", "LimitLess3", "LimitLess4", "LimitLess5"]
arr => [1, 2, 3, 4, 5]; // 不会改变原数组

24. 数组的reduce()方法

// reduce 返回结果是一个数组,不会改变原数组,但是返回的引用类型数组是浅拷贝,值类型的数组是深拷贝
// reduce 接收一个回调函数作为参数
// 回调函数有四个参数,分别为数组循环的初始值,下一项的值,索引值,和数组
// 返回数组满足条件的每一项组成的新数组
const arr = [1,2,3,4,4,4,5];

1.
// 如果没有给pre初始值,那么默认pre的值为数组的第一项,之后pre为函数执行返回的值
// next 为数组的第二项
arr.reduce((pre, next, index, arr) => {
    console.log(pre); // 1
    console.log(next); // 2
})

2.
// 如果给pre初始值,那么下一次pre值为函数执行后返回的值
// next 为数组的第一项
arr.reduce((pre, next, index, arr) => {
      console.log(pre); // []
      console.log(next); // 1
},[]) // 给默认值可以为任意值 0 null undefined [] {}

例子1:判断一个元素在数组中出现的次数

function total (arr, el) {
    return arr.reduce((total, next) => {
        total += next === el ? 1 : 0
        return total;
    }, 0)
}

total(arr,4);
=> 3

例子2:获取元素中的最大值
function max (arr) {
    return arr.reduce((pre,next) => {
       return pre > next ? pre : next;
    })
}
max(arr);
=> 5

当然只是练习reduce如果要查询数组中最大值更简单的方法是
Math.max(...arr)

例子3:求和
function sum (arr) {
    return arr.reduce((pre,next) => {
       return pre += next;
    })
}
sum(arr);
=> 23

例子4:数组去重
const newArr = arr.reduce((pre,cur) => {
   pre.includes(cur) ? '' :  pre.push(cur);
    return pre;
},[])

=> [1, 2, 3, 4, 5]

例子5:数组对象去重
const arr = [
    {name: "zcy最帅"},
    {name: "xm最帅"},
    {name: "sm最帅"},
    {name: "zcy最帅"},
    {name: "xm最帅"},
    {name: "sm最帅"}
]

// 1.第一种对象数组去重方法(根据某个字段去重)
const obj = {};
const newArr = arr.reduce((pre,next) => {
    obj[next.name] ? '' : obj[next.name] = pre.push(next);
    return pre;
}, [])

newArr => [
    {name: "zcy最帅"},
    {name: "xm最帅"},
    {name: "sm最帅"}
]
aSelectedtAreaId = arr.map(item => item.name)
 const aMultipleSelectionArea = []; // 筛选出选中的部门数据
          arr.reduce((pre, cur) => {
              console.log(pre);
              console.log(cur);
            pre.includes(cur.name) ? '' : aMultipleSelectionArea.push(cur);
            return pre;
          }, aSelectedtAreaId);

// 2.第二种对象数组去重方法

const newArr = arr.reduce((pre, next) => {
    // 没找到返回undefined,找到返回对象
    let isFind = !!pre.find(item => item.name === next.name); 
    if(!isFind) {
        pre.push(next);
    }
    return pre;
}, [])

newArr => [
    {name: "zcy最帅"},
    {name: "xm最帅"},
    {name: "sm最帅"}
]



总结:使用遍历数组的时候,会把数组中的值赋值给item,如果是值类型,不会改变原数组,如果事对象引用类型,那么会改变原数组

25. reverse()数组的翻转方法

// 会改变原数组
const arr = [1, 2, 3, 4, 5];
arr.reverse();
arr =>[5, 4, 3, 2, 1]

26. 数组的flat()方法

1. flat()方法作用就是数组降维
2. 利用flat()方法的特性来去除数组的空项
3. 不会改变原数组


const arr1 = [1, 2, [3, 4]];
arr1.flat(); 
=> [1, 2, 3, 4]
 
const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
=> [1, 2, 3, 4, [5, 6]]
 
const arr3 = [1, 2, [3, 4, [5, 6]]];
arr.flat(2);
=> [1, 2, 3, 4, 5, 6]
 
//使用 Infinity 作为深度,展开任意深度的嵌套数组
arr3.flat(Infinity); 
=> [1, 2, 3, 4, 5, 6]
 
//去除空项
var arr4 = [1, 2, , 4, 5];
arr4.flat();
=> [1, 2, 4, 5]; // 但是不能去除undefined,null

27. flatMap()

flatMap()方法对原数组的每个成员执行一个函数,相当于执行Array.prototype.map(),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。

// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[[2, 4], [3, 6], [4, 8]].flatMap((item) => {
    return item;
})

// [2, 4, 3, 6, 4, 8]
注意:flatMap()只能展开一层数组。

28. 排序算法

1.选择排序

// 步骤:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。重复,直到所有元素均排序完毕。

// 代码实现
function selectionSort(arr) {
  for(let i = 0; i < arr.length;i++) {
      let minIndex = i; // 默认将数组的第0项当作最小值
      for(let j = i + 1; j < arr.length; j++) {
         if(arr[j] < arr[minIndex]) {
             minIndex = j;
         }
      }
      let temp = arr[i]; // 存第一项
      arr[i] = arr[minIndex]; // 第一项改为最小项
      arr[minIndex] = temp; // 第一项给默认值
  }
   return arr;
}

let arr = [6,9,4,1,0,7];
selectionSort(arr);


// 进阶封装
// 封装一个从小到大的排序方法
function sort1 (a,b) {
    return a - b;
}

// 封装一个从大到小的排序方法
function sort2 (a,b) {
    return b - a;
}

let arr = [6,9,4,1,0,7];

selectionSort(arr, sort1);

function selectionSort(arr,sort) {
  for(let i = 0; i < arr.length;i++) {
      let minIndex = i; // 默认将数组的第0项当作最小值
      for(let j = i + 1; j < arr.length; j++) {
         if(sort(arr[j], arr[minIndex]) < 0){
             minIndex = j;
         }
      }
      let temp = arr[i]; // 存第一项
      arr[i] = arr[minIndex]; // 第一项改为最小项
      arr[minIndex] = temp; // 第一项给默认值
  }
   return arr;
}
  1. 冒泡排序(Bubble Sort)
比较两个相邻的元素,如果第一个比第二个大,就交换它们两个。对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;针对所有的元素重复以上的步骤,除了最后一个。重复步骤1~3,直到排序完成。【这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。】

1. 代码实现
function bubbleSort(arr){
    for(let i = 0; i < arr.length; i++) {
        // 第一次只需要 交换 arr.length -1 -0 次
        // 第二次只需要 交换 arr.length -1 -1 次
        // 第三次只需要 交换 arr.length -1 -2 次
        // 所以 arr.length -1 - i
        // 第二层for循环每次循环选出一个最大(小)值,所以需要两次for循环外层控制次数,内层控制排序
        for(let j = 0; j < arr.length -1 - i; j++) {
            if(arr[j] > arr[j + 1]) {
                let temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    return arr;
}
let arr = [6,9,4,1,0,7];
bubbleSort(arr);

2. 封装改进
function sort1(a, b) {
    return a - b
} // 实现从小到大的函数

function sort2(a, b) {
    return b - a
} // 实现从大到小的函数

function bubbleSort(arr, func){
    for(let i = 0; i < arr.length; i++) {
        for(let j = 0; j < arr.length -1 - i; j++) {
            if(func(arr[j],  arr[j + 1]) > 0) {
                let temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    return arr;
}
let arr = [6,9,4,1,0,7];
bubbleSort(arr, sort1);

3.插入排序

实现思路:
1.从数组的第二个数据开始往前比较,即一开始用第二个数和他前面的一个比较,如果 符合条件(比前面的大或者小,自定义),则让他们交换位置。
2.然后再用第三个数和第二个比较,符合则交换,但是此处还得继续往前比较,比如有 5个数8,15,20,45, 17,17比45小,需要交换,但是17也比20小,也要交换,当不需 要和15交换以后,说明也不需要和15前面的数据比较了,肯定不需要交换,因为前 面的数据都是有序的。
3.重复步骤二,一直到数据全都排完。

// 代码实现
let arr = [8,15,20,45,17];
function insertionSort() {
    for(let i = 0; i< arr.length; i++) {
        for(let j = 0; j< arr.length; j++){
            if(arr[i] > arr[j]) {
                let temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}

insertionSort(arr);
// 那么如何进行改造像我们sort方法那样呢
function sort(a, b) {
    return a - b;
}

function sort (arr,callback) {
    for(const x in arr) {
        for(const y in arr) {
            if(callback(arr[x] , arr[y]) < 0) {
                const temp = arr[x];
                arr[x] = arr[y];
                arr[y] = temp;
            }
        }
    }
    return arr;
}

posted @ 2021-02-19 17:38  掷轰烈喵生  阅读(75)  评论(0)    收藏  举报