JS原生引用类型解析2-Array类型

(注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!)
(注2:更多内容请查看我的目录。)

1. 简介

Array类型应该是除了Object类型外在JS中最常用的类型了,JS的数组与其他多数语言中的数组有着相当大的区别。虽然JS数组和在其他语言中一样,是一个有序列表,但不同的是其每一项可以保存不同类型的的数据,而且其数组大小是可以动态调整的。

2. 创建数组

创建数组的基本方式有两种,即使用Array构造函数,或者使用数组字面量语法。

2.1 Array构造函数

使用Array构造函数时,传入的参数有三种情况:

  1. 没有参数
var arr = new Array()
console.log(arr);  // []

会返回一个空数组,其长度为0。

  1. 传入一个Number类型的整数值
var arr = new Array(3)
console.log(arr);  // [empty × 3]
console.log(arr.length);  // 3
console.log(arr[1]); // undefined

会返回一个长度是传入值且每一项都是undefined的数组。注意如果传入0会是一个空数组,而传入NaN, Infinity,3.1等无效长度则会报错。

  1. 传入一个非Number类型,或者传入多个参数
var arr = new Array('3');
console.log(arr);  // ["3"]
arr = new Array(undefined);
console.log(arr);  // [undefined]
arr = new Array(1, '2', {a: 1}, null);
console.log(arr);  // [1, "2", {a: 1}, null]

返回参数按顺序组成的数组。

ps:使用Array()构造符时可以省略new操作符,结果相同。

2.2 数组字面量语法

var arr1 = [];
var arr2 = [1];
var arr3 = ['1', {a: 1}, undefined];

与对象一样,使用数组字面量语法并不会调用Array构造函数。

3. Array构造函数的属性与方法

我们用Object.getOwnPropertyNames()方法获取Array构造函数的所有属性与方法。

Object.getOwnPropertyNames(Array);
// (6) ["length", "name", "prototype", "isArray", "from", "of"]

发现一共有6个属性和方法。

3.1 Array构造函数的属性

Array.length
长度为1

Array.name
名称为"Array"

Array.prototype
指向Arrayt构造函数的原型,可以为所有 Array 类型的对象添加属性。

3.2 Array构造函数的方法

Array.from()
从一个类似数组或可迭代对象中创建一个新的数组实例。(ES6新增方法,会在ES6专题中进行详细讲解)
语法:

Array.from(arrayLike, mapFn, thisArg)

参数:

  • arrayLike
    想要转换成数组的伪数组对象或可迭代对象。
  • mapFn (可选参数)
    如果指定了该参数,新数组中的每个元素会执行该回调函数。
  • thisArg (可选参数)
    可选参数,执行回调函数 mapFn 时 this 对象。

返回值:
一个新的数组实例。

Array.isArray()
用于检测传递的值是否是一个数组。

Array.isArray([1, 2, 3]);  // true
Array.isArray({foo: 123}); // false
Array.isArray("foobar");   // false
Array.isArray(undefined);  // false

注意:当检测Array实例时, Array.isArray 优于 instanceof,因为Array.isArray能检测iframes。

Polyfill:
假如不存在 Array.isArray(),则在其他代码之前运行下面的代码将创建该方法。

if (!Array.isArray) {
    Array.isArray = function(arg) {
        return Object.prototype.toString.call(arg) === '[object Array]';
    };
}

Array.of()
创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。
Array.of()和Array构造函数之间的区别在于处理单个整数参数:

Array.of(7);       // [7] 
Array.of(1, 2, 3); // [1, 2, 3]

Array(7);          // [ , , , , , , ]
Array(1, 2, 3);    // [1, 2, 3]

4. Array原型对象的属性与方法

我们用Object.getOwnPropertyNames()方法获取Array原型对象的所有属性与方法。

Object.getOwnPropertyNames(Array.prototype);
// (30) ["length", "constructor", "concat", "pop", "push", "shift", "unshift", "slice", "splice", "includes", "indexOf", "keys", "entries", "forEach", "filter", "map", "every", "some", "reduce", "reduceRight", "toString", "toLocaleString", "join", "reverse", "sort", "lastIndexOf", "copyWithin", "find", "findIndex", "fill"]

发现一共有30个属性和方法。

4.1 Array原型对象的属性

Array.prototype.constructor
指向构造函数Array

Array.prototype.length
Array.prototype 也是个数组,长度为0

4.2 Array原型对象的方法

Array原型对象的方法分为四类:

  1. 修改器方法
  2. 访问方法
  3. 迭代方法
  4. 泛型方法(已弃用)
4.2.1 修改器方法

修改器方法会改变调用它们的对象自身的值。

Array.copyWithin()
浅复制数组的一部分到同一数组中的另一个位置,并返回它,而不修改其大小,用该方法移动数组数据性能非常高
语法:

arr.copyWithin(target[, start[, end]]);

参数:

  • target
    0 为基底的索引,复制序列到该位置。
    如果是负数,target 将从末尾开始计算(索引位置等同于length+start)。
    如果 target 大于等于 arr.length,将会不发生拷贝。
    如果 target 在 start 之后,复制的序列将被修改以符合 arr.length。
  • start
    0 为基底的索引,开始复制元素的起始位置。
    如果是负数,start 将从末尾开始计算(索引位置等同于length+start)。
    如果 start 被忽略,copyWithin 将会从0开始复制。
  • end
    0 为基底的索引,开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数, end 将从末尾开始计算索引位置等同于length+start)。
    如果 end 被忽略,copyWithin 将会复制到 arr.length。

返回:
被改变的原数组

var arr = [1, 2, 3, 4, 5];
arr.copyWithin(-2);
// [1, 2, 3, 1, 2]

arr.copyWithin(0, 3);
// [4, 5, 3, 4, 5]

arr.copyWithin(0, 3, 4);
// [4, 2, 3, 4, 5]

arr.copyWithin(-2, -3, -1);
// [1, 2, 3, 3, 4]

Array.fill()
用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。
用法:

arr.fill(target[, start[, end]]);

参数:

  • value
    用来填充数组元素的值。
  • start 可选
    起始索引,默认值为0。
  • end 可选
    终止索引,默认值为 this.length。

返回:
被改变的原数组

[1, 2, 3].fill(4)            // [4, 4, 4]
[1, 2, 3].fill(4, 1)         // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2)      // [1, 4, 3]

Array.prototype.pop()
从数组中删除最后一个元素,并返回该元素的值。

Array.prototype.push()
将一个或多个元素添加到数组的末尾,并返回新数组的长度。

Array.prototype.reverse()
颠倒数组中元素的排列顺序,即原先的第一个变为最后一个,原先的最后一个变为第一个。

Array.prototype.shift()
删除数组的第一个元素,并返回这个元素。

Array.prototype.unshift()
在数组的开头增加一个或多个元素,并返回数组的新长度。

Array.prototype.sort()
对数组内元素进行排序,并返回排序后的当前数组。

Array.prototype.splice()
在任意的位置给数组添加或删除任意个元素。

4.2.2 访问方法

不会改变调用它们的对象的值,只会返回一个新的数组或者返回一个其它的期望值。

Array.prototype.concat()
返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组。

Array.prototype.includes()
判断当前数组是否包含某指定的值,如果是返回 true,否则返回 false。
语法:

arr.includes(searchElement[, fromIndex])

参数:

  • searchElement
    需要查找的元素值。
  • fromIndex 可选
    从该索引处开始查找 searchElement。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜索。默认为 0。
[1, 2, 3].includes(2);     // true
[1, 2, 3].includes(4);     // false
[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true
[1, 2, NaN].includes(NaN); // true

Array.prototype.join()
将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串,默认连接符为 ","。

Array.prototype.slice()
方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。原始数组不会被修改。

Array.prototype.toSource()
返回一个由所有数组元素组合而成的本地化后的字符串。遮蔽了原型链上的同名方法。该方法非标准,不要在生产上使用,仅可用于调试。

Array.prototype.toString()
返回一个由所有数组元素组合而成的字符串。遮蔽了原型链上的同名方法。

Array.prototype.toLocalString()
返回一个由所有数组元素组合而成的本地化后的字符串。遮蔽了原型链上的同名方法。

Array.prototype.indexO()
返回数组中第一个与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1。
注意:indexOf 使用strict equality (===操作符基于同样的方法)进行判断 searchElement与数组中包含的元素之间的关系。

Array.prototype.lastIndexOf()
返回数组中最后一个(从右边数第一个)与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1。

4.2.3 迭代方法

在下面的众多遍历方法中,有很多方法都需要指定一个回调函数作为参数。在每一个数组元素都分别执行完回调函数之前,数组的length属性会被缓存在某个地方,所以,如果你在回调函数中为当前数组添加了新的元素,那么那些新添加的元素是不会被遍历到的。此外,如果在回调函数中对当前数组进行了其它修改,比如改变某个元素的值或者删掉某个元素,那么随后的遍历操作可能会受到未预期的影响。总之,不要尝试在遍历过程中对原数组进行任何修改,虽然规范对这样的操作进行了详细的定义,但为了可读性和可维护性,请不要这样做。

Array.prototype.forEach()
为数组中的每个元素执行一次回调函数。
注意:该方法没有返回值。

Array.prototype.entries()
返回一个数组迭代器对象,该迭代器会包含所有数组元素的键值对。

var arr = ["a", "b", "c"];
var iterator = arr.entries();
// undefined

console.log(iterator);
// Array Iterator {}

console.log(iterator.next().value); 
// [0, "a"]
console.log(iterator.next().value); 
// [1, "b"]
console.log(iterator.next().value); 
// [2, "c"]

Array.prototype.every()
如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false。

Array.prototype.some()
如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。

Array.prototype.filter()
将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回。

Array.prototype.find()
找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined。
语法:

arr.find(callback[, thisArg])

参数:

  • callback
    在数组每一项上执行的函数,接收 3 个参数:
  • element
    当前遍历到的元素。
  • index
    当前遍历到的索引。
  • array
    数组本身。
  • thisArg 可选
    可选,指定 callback 的 this 参数。
    返回值:
    当某个元素通过 callback 的测试时,返回数组中的一个值,否则返回 undefined
function isBigEnough(element) {
  return element >= 15;
}

[12, 5, 8, 130, 44].find(isBigEnough); // 130

Array.prototype.findIndex()
找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1。

Array.prototype.keys()
返回一个数组迭代器对象,该迭代器会包含所有数组元素的键。

Array.prototype.map()
返回一个由回调函数的返回值组成的新数组。

Array.prototype.reduce()
从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。

Array.prototype.reduceRight()
从右到左为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。

4.2.4 泛型方法

泛型方法是非标准,并且已弃用,有可能不久就会移除。 需注意的是此方法同时有跨浏览器问题. 但是 Github上有可用的shim

有时我们会希望在字符串或其他类数组对象上使用数组所提供的方法(如函数的 arguments)。此时你可以把一个字符串作为一个字符数组来看待(也就是说,把非数组以某种方式看成是一个数组)。比如,可以用下面的方法来检查变量 str 中的字符是否都是字母:

function isLetter(character) {
  return character >= 'a' && character <= 'z';
}

if (Array.prototype.every.call(str, isLetter)) {
  console.log("The string '" + str + "' contains only letters!");
}

这种方法能够行得通,但不够简洁,JavaScript 1.6 中引入了一个泛型化的简写形式:

if (Array.every(str, isLetter)) {
  console.log("The string '" + str + "' contains only letters!");
}

这些并不属于 ECMAScript 标准,也不能在非 Gecko 浏览器中使用。你可以用标准方法 Array.from()来替代上面的写法, from 方法可以将一个对象转换为真正的数组(虽然老的浏览器可能不支持):

if (Array.from(str).every(isLetter)) { 
  console.log("The string '" + str + "' contains only letters!"); 
}

5. Array实例对象的属性与方法

我们用Object.getOwnPropertyNames()方法获取Array实例对象的所有属性与方法。

var arr = new Array(1, '2', undefined);
Object.getOwnPropertyNames(arr);
// ["0", "1", "2", "length"]

发现数组每一项的index均为其属性,另外还一个属性length表示其长度。(再次同样有JS原生引用类型解析1-Object类型末尾关于_proto_的疑惑)。

参考

MDN_Array
W3schoocl-JavaScript Array 对象
BOOK-《JavaScript高级程序设计(第3版)》第5章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  •   引用类型的值(对象)是引用类型的一个实例。   在 ECMAscript 中,引用类型是一种数据结构,用于将数...
    霜天晓阅读 1,028评论 0 1
  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,093评论 0 13
  • 给大家讲个故事。 高中时候,我在班里的定位应该是中等吧。我们班有个女学霸,学习自然不用说。在最后一个月,我们几乎都...
    Caitlin阅读 170评论 0 0
  • 我有个windows服务程序,今天重点在测试系统逻辑。部署后,在看系统日志时,不经意看到一行:scheduler ...
    buguge阅读 617评论 0 2