基本引用类型

本文最后更新于:2022年1月31日 凌晨

集合引用类型

Object

显示的创建Object类型:

  • new 和 Object构造函数:

    1
    2
    3
    let obj = new Object();
    obj.name = "jack";
    obj.age = 23;
  • 使用对象字面量的方式创建对象:

    1
    2
    3
    4
    5
    6
    let obj = {
    name: "jack",
    age: 20,
    5: true, // 数值属性会自动转换成字符串
    false: 4,
    }
  • 在使用对象字面量表示法定义对象时,并不会实际调用 Object 构造函数;

  • 读取object属性的方式:

    • 点语法;

    • 中括号;

      1
      2
      3
      4
      console.log(preson.name);
      console.log(preson["name"]); // 当使用中括号的访问需要加上引号
      // 中括号可以访问到一些点语法无法访问的特殊属性, 比如空格
      console.log(preson["hello world"]);

Array

创建数组的方式:

  • 数组的构造函数, 在使用 Array 构造函数时,也可以省略 new 操作符:

    1
    2
    3
    4
    5
    6
    7
    let colors = new Array();
    // 创建一个固定长度的数组, 这里需要注意的当我们使用这样方式创建空的数组,可能存在length但是item是空的不是undefined,所以不能进行map等操作
    let colors = new Array(20);
    // 在构造的传入保存的数组元素
    let colors = new Array("blue", "yellow", "red");
    // 省略new操作符:
    let colors = Array("blue");
  • 数组字面量的创建方式:

    1
    2
    let names = []; // 创建一个空数组
    let colors = ["red", "blue", "green"];
  • 与对象一样,在使用数组字面量表示法创建数组不会调用 Array 构造函数;

from

Array.from()的第一个参数是一个类数组对象,即任何可迭代的结构,或者有一个 length 属性和可索引元素的结构, 返回一个数组,不回改变原数组:

  • 字符串:

    1
    console.log(Array.from("Matt")); // ["M", "a", "t", "t"]
  • 浅复制:

    1
    2
    3
    4
    5
    // Array.from()对现有数组执行浅复制
    const a1 = [1, 2, 3, 4];
    const a2 = Array.from(a1);
    console.log(a1); // [1, 2, 3, 4]
    alert(a1 === a2); // false
  • 对象转换成数组:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 一个类数组对象必须要有length属性,他们的元素属性名必须是数值或者可以转换成数值的字符。
    const arrayLikeObject = {
    0: 1,
    1: 2,
    2: 3,
    3: 4,
    length: 4
    };
    console.log(Array.from(arrayLikeObject)); // [1, 2, 3, 4]

Array.from()还接收第二个可选的映射函数参数。这个函数可以直接增强新数组的值;

1
2
3
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1, x => x*2);
console.log(a2); // [2, 4, 6, 8]
of

Array.of()可以把一组参数转换为数组。

1
2
3
console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
console.log(Array.of(undefined)); // [undefined]
console.log(Array.of({ name: "jack", age: 23 })); // [{ name: "jack", age: 23 }]
数组空位

使用数组字面量初始化数组时,可以使用一串逗号来创建空位(hole)。

1
2
// ES6 新增方法普遍将这些空位当成存在的元素,只不过值为 undefined:
let arr = [,,,,]

⚠️: 由于行为不一致和存在性能隐患,因此实践中要避免使用数组空位。如果确实需要空位,则可以显式地用 undefined 值代替, ES6 之前的方法则会忽略这个空位。

数组索引

要取得或设置数组的值,需要使用中括号并提供相应值的数字索引,索引都是从0开始的:

1
2
3
4
5
let colors = ["red", "blue", "green"];
// 显示第一项
alert(colors[0]);
// 修改第三项
colors[2] = "black";
  • 如果索引小于数组包含的元素数,则返回存储在相应位置的元素;
  • 如果把一个值设置给超过数组最大索引的索引,则数组长度会自动扩展到该索引值加 1;
数组的length

数组 length 属性的独特之处在于,它不是只读的。通过修改 length 属性,可以从数组末尾删除或添加元素:

1
2
3
4
5
6
7
8
9
let colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
// 删除
colors.length = 2;
alert(colors[2]); // undefined

let colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
// 新增
colors.length = 4;
alert(colors[3]); // undefined
1
2
3
4
5
// 增量增加数组元素
// 数组中最后一个元素的索引始终是 length - 1,因此下一个新增槽位的索引就是 length
let colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
colors[colors.length] = "black"; // 添加一种颜色(位置 3)
colors[colors.length] = "brown"; // 再添加一种颜色(位置 4)
检测数组

一个经典的 ECMAScript 问题是判断一个对象是不是数组,使用typeof只能返回一个’object’;

1
2
3
4
let arr = [1, 2, 3, 4];
let obj = {name: "xxxx"};
console.log(typeof arr); // 'object'
console.log(typeof obj); // 'object'

为了能更好区别于是不是数组类型,可以使用instanceof:

1
2
3
4
5
6
let arr = [1, 2, 3, 4];
let obj = {name: "xxxx"};
console.log(arr instanceof Array); // true
// 同时 Array 类型使用 Object 也是一样返回的 true
console.log(arr instanceof Object); // true
console.log(obj instanceof Object); // true

为了更好的区分不同的Array的构造函数,那么需要一个新的方法Array.isArray去判断是不是数组:

1
Array.isArray(value); // true or false
iteration迭代器

这里只是简单介绍迭代器,下面的 keys,values, entries三个都是跟迭代器相关的方法:

iteration成为迭代器,又叫遍历器。它的作用是给不同的数据结构提供统一的遍历访问机制,这种机制可以使得数据结构里的成员按照顺序依次被遍历访问,最常见的就是数组、map的遍历了;

其实iteration有三个作用:

  1. 为各种数据结构,提供一个统一的、简便的访问接口;
  2. 使得数据结构的成员能够按某种次序排列;
  3. 主要供for...of消费

iteration一般是放在Symbol.iteration其实主要是说明含有iteration就可以遍历,该方法主要是返回一个迭代器对象;

keys

keys返回数组索引的迭代器;

1
2
3
let arr =  ["foo", "bar", "baz", "qux"];
// 因为迭代器方法返回的是迭代器的对象,所以需要使用Array.from方法转换成数组
console.log(Array.from(arr.keys())); // [0,1,2,3]
values

values()返回数组元素的迭代器

1
2
3
let arr =  ["foo", "bar", "baz", "qux"];
// 因为迭代器方法返回的是迭代器的对象,所以需要使用Array.from方法转换成数组
console.log(Array.from(arr.values())); // ["foo", "bar", "baz", "qux"]
entries

entries()返回索引/值对的迭代器,经过转换,对应索引和值都是在同一个数组中

1
2
3
let arr =  ["foo", "bar", "baz", "qux"];
// 因为迭代器方法返回的是迭代器的对象,所以需要使用Array.from方法转换成数组
console.log(Array.from(a.entries())); // [[0, "foo"], [1, "bar"], [2, "baz"], [3, "qux"]]
1
2
3
4
5
// 在values, keys和entries都可以在for--of中直接使用
const arr = [1,2,3,4];
for(const item of arr.keys()) {}
for(const item of arr.values()) {}
for(const [index, values] of arr.entries()) {}
fill—数组的填充

使用 fill()方法可以向一个已有的数组中插入全部或部分相同的值。开始索引用于指定开始填充的位置,它是可选的。如果不提供结束索引,则一直填充到数组末尾。

改方法会改变数组值,并且会返回改变后的数组;

1
2
3
4
5
6
7
8
9
10
11
12
13
let zeroes = [0, 0, 0, 0, 0];
zeroes.fill(5);
console.log(zeroes); // [5, 5, 5, 5, 5]

// 用 6 填充索引大于等于 3 的元素
zeroes.fill(6, 3);
let b = zeroes.fill(6, 3);
console.log(zeroes); // [0, 0, 0, 6, 6]
console.log(b); // [0, 0, 0, 6, 6]

// 用 7 填充索引大于等于 1 且小于 3 的元素
zeroes.fill(7, 1, 3);
console.log(zeroes); // [0, 7, 7, 0, 0];
数组转换方法

所有对象都有 toLocaleString()、toString()和 valueOf()方法。其中,valueOf()返回的还是数组本身;

1
2
3
4
let colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
alert(colors.toString()); // red,blue,green
alert(colors.valueOf()); // ["red", "blue", "green"]
alert(colors); // ["red", "blue", "green"]
  • 当使用toLocaleString()方法也可能返回跟 toString()和 valueOf()相同的结果,但也不一定;它与另外两个方法唯一的区别是,为了得到最终的字符串,会调用数组每个值的 toLocaleString()方法,而不是toString()方法。
  • 继承的方法 toLocaleString()以及 toString()都返回数组值的逗号分隔的字符串。如果想使用不同的分隔符,请使用下面的方法:
  • 如果数组中某一项是 null 或 undefined,则在 join()、toLocaleString()、toString()和 valueOf()返回的结果中会以空字符串表示。
join
  • join()方法接收一个参数,即字符串分隔符,返回包含所有项的字符串。
  • 如果不给 join()传入任何参数,或者传入 undefined,则仍然使用逗号作为分隔符。
  • 如果某一项是null或者是undefined那么则返回空字符串;
1
2
3
let colors = ["red", "green", "blue"]; 
alert(colors.join(",")); // red,green,blue
alert(colors.join("||")); // red||green||blue
push

push()方法接收任意数量的参数,并将它们添加到数组末尾,⚠️该方法返回数组的最新长度,并且会改变原数组;

1
2
3
let colors = new Array(); // 创建一个数组
let count = colors.push("red", "green"); // 推入两项
alert(count); // 2
pop

pop()方法则用于删除数组的最后一项,同时减少数组的 length 值,⚠️该方法返回被删除的项,并且会改变原数组;

1
2
3
let item = colors.pop(); // 取得最后一项
alert(item); // black
alert(colors.length); // 2
shift

shift()会删除数组的第一项并返回它,然后数组长度减 1,⚠️该方法会返回删除的项并且改变原数组;

1
2
3
4
5
let colors = new Array(); // 创建一个数组
let count = colors.push("red", "green"); // 推入两项

let item = colors.shift(); // 取得第一项
alert(item); // red
unshift

unshift()在数组开头添加任意多个值,⚠️该方法是返回新的数组,修改原数组;

1
2
3
let colors = new Array(); // 创建一个数组
let count = colors.unshift("red", "green"); // 从数组开头推入两项
alert(count); // 2
reverse

reverse()方法就是将数组元素反向排列,该方法会返回排序后的数组并且修改原数组;

1
2
3
let values = [1, 2, 3, 4, 5]; 
values.reverse();
alert(values); // 5,4,3,2,1
sort

默认情况下,sort()会按照升序重新排列数组元素,即最小值在最前面;为此,sort()会在每一项上调用 String()转型函数,然后比较字符串来决定顺序。即使数组的元素都是数值,也会先把数组转换为字符串再比较、排序;⚠️该方法是返回新的数组,修改原数组

1
2
3
let values = [0, 1, 5, 10, 15]; 
values.sort();
alert(values); // 0,1,10,15,5 因为 字符串 10 在 5 前面

sort()方法可以接收一个比较函数,用于判断哪个值应该排在前面;

1
2
3
4
5
6
7
8
let values = [0, 1, 5, 10, 15]; 
values.sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
console.log(values); // 15,10,5,1,0

// 也可以写成:
var numbers = [4, 2, 5, 1, 3];
numbers.sort((a, b) => a - b);
console.log(numbers);
concat

concat()方法可以在现有数组全部元素基础上创建一个新数组。它首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组,意思就是并不会改变原数组;

1
2
3
4
let colors = ["red", "green", "blue"]; 
let colors2 = colors.concat("yellow", ["black", "brown"]);
console.log(colors); // ["red", "green","blue"]
console.log(colors2); // ["red", "green", "blue", "yellow", "black", "brown"]

为了防止colors2中的第二个参数的数组打平,可以将数组中的属性Symbol.isConcatSpreadble变成false;

1
2
3
4
let colors = ["red", "green", "blue"]; 
let newColors = ["black", "brown"];
newColors[Symbol.isConcatSpreadable] = false;
let colors2 = colors.concat("yellow", newColors); // ["red", "green", "blue", "yellow", ["black", "brown"]]
slice

slice()用于创建一个包含原有数组中一个或多个元素的新数组;

slice()方法可以接收一个或两个参数:返回元素的开始索引和结束索引。如果只有一个参数,则 slice()会返回该索引到数组末尾的所有元素。如果有两个参数,则 slice()返回从开始索引到结束索引对应的所有元素,其中不包含结束索引对应的元素。

该方法不会对原数组产生影响;

1
2
3
4
5
let colors = ["red", "green", "blue", "yellow", "purple"]; 
let colors2 = colors.slice(1);
let colors3 = colors.slice(1, 4);
alert(colors2); // green,blue,yellow,purple
alert(colors3); // green,blue,yellow
splice
  • 删除

    • 需要给 splice()传 2 个参数:要删除的第一个元素的位置和要删除的元素数量,splice(0, 2),表示删除数组中下表0开始的两个元素;
  • 插入

    • 需要给 splice()传 3 个参数:开始位置、0和要插入的元素,可以在数组中指定的位置插入元素。
    • 注意第二个参数是0,才能表示插入;
    • splice(1, 0, 'name', 'xxx'),表示在下标1处插入这两个元素,如果该位置存在元素,那么向后移动
  • 替换

    • splice()在删除元素的同时可以在指定位置插入新元素,同样要传入 3 个参数:开始位置、要删除元素的数量和要插入的任意多个元素。

    • 比如,splice(2, 1, “red”, “green”)会在下标1 删除一个元素,然后从该位置开始向数组中插入”red”和”green”。

    • 第二个参数要删除的元素数量可以不跟传入元素数量一直,如果需要删除元素数量 > 传入的插入元素数量,那么可能删除掉后续元素;

    • ```js
      let colors = [“red”, “green”, “blue”, ‘ddd’, “ff”, ‘ee’];
      colors.splice(2, 3, ‘xxx’, ‘name’);
      // 之类因为需要删除3位,但是只是传入了两个替代 的元素,所以删除了后续的一个元素’ff’
      console.log(colors) // [ ‘red’, ‘green’, ‘xxx’, ‘name’ , ‘ee’]

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10



      ##### indexof

      接收两个参数:要查找的元素和一个可选的起始搜索位置,从数组的开头向后搜索,返回需要搜索项的下标

      ```js
      let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
      numbers.indexof(4); // 3
lastIndexof

接收两个参数:要查找的元素和一个可选的起始搜索位置,从数组的结尾向开始搜索,返回需要搜索项的下标;

该方法从后向前搜索是以末尾元素下标位0;

1
2
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
numbers.lastIndexof(4); // 5
includes

接收两个参数:要查找的元素和一个可选的起始搜索位置,从数组的开头向后搜索.返回一个布尔值,表示该数组中是不是存在需要搜索的项;

1
2
3
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
numbers.includes(4); // true
numbers.includes(4, 7); // false
find和findIndex

find()和 findIndex()方法使用了断言函数。这两个方法都从数组的最小索引开始。find()返回第一个匹配的元素,findIndex()返回第一个匹配元素的索引;

该两个方法如果找到匹配项都不会继续向下搜索了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const people = [ 
{
name: "Matt",
age: 27
},
{
name: "Nicholas",
age: 29
}
];
alert(people.find((element, index, array) => element.age < 28));
// {name: "Matt", age: 27}
alert(people.findIndex((element, index, array) => element.age < 28));
// 0
every

对数组每一项都运行传入的函数,如果对每一项函数都返回 true,则这个方法返回 true。

1
2
3
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; 
let everyResult = numbers.every((item, index, array) => item > 2);
alert(everyResult); // false
some

对数组每一项都运行传入的函数,如果有一项函数返回 true,则这个方法返回 true。这些方法都不改变调用它们的数组。

1
2
3
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; 
let everyResult = numbers.some((item, index, array) => item > 2);
alert(everyResult); // true
filter

对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回。即通过函数的项都会返回;

1
2
3
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; 
let filterResult = numbers.filter((item, index, array) => item > 2);
alert(filterResult); // 3,4,5,4,3
map

对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组;

1
2
3
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; 
let mapResult = numbers.map((item, index, array) => item * 2);
alert(mapResult); // 2,4,6,8,10,8,6,4,2
forEach

这个方法只会对每一项运行传入的函数,⚠️这个方法和map方法不一样,forEach没有返回值;

1
2
3
4
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; 
numbers.forEach((item, index, array) => {
// 执行某些操作
});
reduce和reduceRight

reduce和reduceRight存在两个参数

  • 对每一项都会运行的归并函数;
    • 归并函数存在四个参数:上一个归并值、当前项、当前项的索引和数组本身;
    • 如果没有给这两个方法传入可选的第二个参数(作为归并起点值),则第一次迭代将从数组的第二项开始,因此传给归并函数的第一个参数是数组的第一项,第二个参数是数组的第二项
  • 可选的以之为归并起点的初始值;

reduce会迭代数组的所有项,并在此基础上构建一个最终返回值;reduce()方法从数组第一项开始遍历到最后一项;
reduceRight()方法与之类似,只是方向相反;

1
2
3
4
5
6
7
8
9
10
11
12
13
let values = [1, 2, 3, 4, 5]; 
let sum = values.reduce((prev, cur, index, array) => prev + cur);
alert(sum); // 15

// 当如果我们需要计算对象数组中的某个属性相加,我们需要传入第二个参数作为初始值,否则正常会把第一项作为初始值,但是这是对象数组,第一项是对象,所以返回值会报错---NaN
let arr = [
{ name: "xxx", age: 13 },
{ name: "ddd", age: 13 },
{ name: "ddd", age: 13 },
]
// 为什么不是prev.age,因为prev是上一项的归并值,并不是上一项的对象
let sumAge = arr.reduce((prev, cur) => prev + cur.age, 0)
console.log(sumAge)

基本引用类型
http://www.clearluv.com/2020/11/09/集合引用类型/
发布于
2020年11月9日
许可协议