转行学前端的第 32 天 : 了解 ECMAScript Array 对象基础

1,167 阅读11分钟

我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。

今日学习目标

昨天基于搜索来仔细学习 Object 对象相关的知识。今天主要是基于搜索来基础学习 Array 对象属性和Array构造函数方法,又是适合学习的一天,加油,小又又!!!!


what

JavaScriptArray对象是用于构造数组的全局对象,数组是类似于列表的高阶对象。

数组是一种类列表对象,它的原型中提供了遍历和修改元素的相关操作。

JavaScript 数组的长度和元素类型都是非固定的。因为数组的长度可随时改变,并且其数据在内存中也可以不连续,所以 JavaScript 数组不一定是密集型的,这取决于它的使用方式。

一般来说,数组的这些特性会给使用带来方便,但如果这些特性不适用于你的特定使用场景的话,可以考虑使用类型数组 TypedArray

只能用整数作为数组元素的索引,而不能用字符串。后者称为关联数组。使用非整数并通过方括号或点号来访问或设置数组元素时,所操作的并不是数组列表中的元素,而是数组对象的属性集合上的变量。

数组对象的属性和数组元素列表是分开存储的,并且数组的遍历和修改操作也不能作用于这些命名属性。


how

字面量声明

[element0, element1, ..., elementN]

参数

  • elementN Array 构造器会根据给定的元素创建一个 JavaScript 数组。

案例

const arr = [1,2,3];
console.log(arr);

构造函数声明

语法

new Array(element0, element1[, ...[, elementN]])
new Array(arrayLength)

参数

  • elementN

Array 构造器会根据给定的元素创建一个 JavaScript 数组,但是当仅有一个参数且为数字时除外(详见下面的 arrayLength 参数)。注意,后面这种情况仅适用于用 Array 构造器创建数组,而不适用于用方括号创建的数组字面量。

  • arrayLength

一个范围在 0 到 232-1 之间的整数,此时将返回一个 length 的值等于 arrayLength 的数组对象(言外之意就是该数组此时并没有包含任何实际的元素,不能理所当然地认为它包含 arrayLength 个值为 undefined 的元素)。如果传入的参数不是有效值,则会抛出 RangeError 异常。


案例

const arr1 = Array(1,2,3);
const arr2 = Array(3);
console.log(arr1);
console.log(arr2);

对象属性

访问数组元素

JavaScript数组的索引是从0开始的,第一个元素的索引为0,最后一个元素的索引等于该数组的长度减1。如果指定的索引是一个无效值,JavaScript 数组并不会报错,而是会返回 undefined

const arr = ['this is the first element', 'this is the second element', 'this is the last element'];
console.log(arr[0]);              // 打印 'this is the first element'
console.log(arr[1]);              // 打印 'this is the second element'
console.log(arr[arr.length - 1]); // 打印 'this is the last element'

Array.length

Array 构造函数的 length 属性,其值为1(注意该属性为静态属性,不是数组实例的 length 属性)。


get Array[@@species]

返回 Array 构造函数。


Array.prototype

通过数组的原型对象可以为所有数组对象添加属性。


注意事项

length 和数字下标之间的关系

JavaScript数组的length 属性和其数字下标之间有着紧密的联系。数组内置的几个方法(例如joinsliceindexOf 等)都会考虑length 的值。另外还有一些方法(例如 pushsplice 等)还会改变length 的值。

const fruits = [];
fruits.push('banana', 'apple', 'peach');

console.log(fruits.length); // 3

使用一个合法的下标为数组元素赋值,并且该下标超出了当前数组的大小的时候,解释器会同时修改length 的值

fruits[5] = 'mango';
console.log(fruits[5]); // 'mango'
console.log(Object.keys(fruits));  // ['0', '1', '2', '5']
console.log(fruits.length); // 6

也可以显式地给length 赋一个更大的值:

fruits.length = 10;
console.log(Object.keys(fruits)); // ['0', '1', '2', '5']
console.log(fruits.length); // 10

而为 length 赋一个更小的值则会删掉一部分元素:

const fruits = [];
fruits.push('banana', 'apple', 'peach');
fruits.length = 2;
console.log(Object.keys(fruits)); // ['0', '1']
console.log(fruits.length); // 2

数组中数字开头的属性

虽然数组元素可以看做是数组对象的属性,就像toString 一样,但是下面的写法是错误的,运行时会抛出 SyntaxError 异常,而原因则是使用了非法的属性名:

const arr = ['this is the first element', 'this is the second element', 'this is the last element'];
console.log(arr.0); // a syntax error

并不是JavaScript 数组有什么特殊之处,而是因为在 JavaScript 中,以数字开头的属性不能用点号引用,必须用方括号。

比如,如果一个对象有一个名为 3d 的属性,那么只能用方括号来引用它。下面是具体的例子:

var years = [1950, 1960, 1970, 1980, 1990, 2000, 2010];
console.log(years.0);   // 语法错误
console.log(years[0]);  // √
renderer.3d.setTexture(model, 'character.png');     // 语法错误
renderer['3d'].setTexture(model, 'character.png');  // √

注意在 3d 那个例子中,引号是必须的。

你也可以将数组的索引用引号引起来,比如years[2] 可以写成years['2']years[2]中的 2会被 JavaScript 解释器通过调用 toString 隐式转换成字符串。

正因为这样,'2''02'years 中所引用的可能是不同位置上的元素。

而下面这个例子也可能会打印true

const years = [1950, 1960, 1970, 1980, 1990, 2000, 2010];
console.log(years['2'] != years['02']);

对象中保留字属性

如果对象的属性名称是保留字(最好不要这么做!),那么就只能通过字符串的形式用方括号来访问(从 firefox 40.0a2 开始也支持用点号访问了):

const promise = {
  'var'  : 'text',
  'array': [1, 2, 3, 4]
};

console.log(promise['var']);


Array 构造函数方法

Array.form()

基础语法

Array.from(arrayLike[, mapFn[, thisArg]])

参数说明

arrayLike

想要转换成数组的伪数组对象或可迭代对象。

mapFn (可选参数)

如果指定了该参数,新数组中的每个元素会执行该回调函数。

thisArg (可选参数)

可选参数,执行回调函数 mapFnthis 对象。


返回值说明

一个新的数组实例


详细说明

Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例。

Array.from() 可以通过以下方式来创建数组对象:

  • 伪数组对象(拥有一个 length 属性和若干索引属性的任意对象)
  • 可迭代对象(可以获取对象中的元素,如 MapSet 等)

Array.from() 方法有一个可选参数 mapFn,让你可以在最后生成的数组上再执行一次 map 方法后再返回。也就是说 Array.from(obj, mapFn, thisArg) 就相当于 Array.from(obj).map(mapFn, thisArg), 除非创建的不是可用的中间数组。 这对一些数组的子类,如 typed arrays 来说很重要, 因为中间数组的值在调用 map() 时需要是适当的类型。

from()length 属性为 1 ,即Array.from.length = 1

ES2015 中, Class 语法允许我们为内置类型(比如 Array)和自定义类新建子类(比如叫 SubArray)。这些子类也会继承父类的静态方法,比如 SubArray.from(),调用该方法后会返回子类 SubArray 的一个实例,而不是 Array 的实例。


案例

Array from a String

Array.from('foo'); 
// ["f", "o", "o"]

Array from a Set

let s = new Set(['foo', window]); 
Array.from(s); 
// ["foo", window]

Array from a Map

let m = new Map([[1, 2], [2, 4], [4, 8]]);
Array.from(m); 
// [[1, 2], [2, 4], [4, 8]]

Array from an Array-like object (arguments)

function f() {
  return Array.from(arguments);
}

f(1, 2, 3);

// [1, 2, 3]

在Array.from中使用箭头函数

// Using an arrow function as the map function to
// manipulate the elements
Array.from([1, 2, 3], x => x + x);  
// x => x + x代表这是一个函数,只是省略了其他的定义,这是一种Lambda表达式的写法
// 箭头的意思表示从当前数组中取出一个值,然后自加,并将返回的结果添加到新数组中    
// [2, 4, 6]
// Generate a sequence of numbers
// Since the array is initialized with `undefined` on each position,
// the value of `v` below will be `undefined`
Array.from({length: 5}, (v, i) => i);
// [0, 1, 2, 3, 4]

数组去重合并

function combine(){ 
    let arr = [].concat.apply([], arguments);  //没有去重复的新数组 
    return Array.from(new Set(arr));
} 

var m = [1, 2, 2], n = [2,3,3]; 
console.log(combine(m,n));                     // [1, 2, 3]

Polyfill

ECMA-262 第六版标准添加了 Array.from 。有些实现中可能尚未包括在其中。你可以通过在脚本前添加如下内容作为替代方法,以使用未原生支持的 Array.from 方法。该算法按照 ECMA-262 第六版中的规范实现,并假定 ObjectTypeError 有其本身的值, callback.call 对应 Function.prototype.call 。此外,鉴于无法使用 Polyfill 实现真正的的迭代器,该实现不支持规范中定义的泛型可迭代元素。

// Production steps of ECMA-262, Edition 6, 22.1.2.1
// Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from
if (!Array.from) {
  Array.from = (function () {
    var toStr = Object.prototype.toString;
    var isCallable = function (fn) {
      return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
    };
    var toInteger = function (value) {
      var number = Number(value);
      if (isNaN(number)) { return 0; }
      if (number === 0 || !isFinite(number)) { return number; }
      return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
    };
    var maxSafeInteger = Math.pow(2, 53) - 1;
    var toLength = function (value) {
      var len = toInteger(value);
      return Math.min(Math.max(len, 0), maxSafeInteger);
    };

    // The length property of the from method is 1.
    return function from(arrayLike/*, mapFn, thisArg */) {
      // 1. Let C be the this value.
      var C = this;

      // 2. Let items be ToObject(arrayLike).
      var items = Object(arrayLike);

      // 3. ReturnIfAbrupt(items).
      if (arrayLike == null) {
        throw new TypeError("Array.from requires an array-like object - not null or undefined");
      }

      // 4. If mapfn is undefined, then let mapping be false.
      var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
      var T;
      if (typeof mapFn !== 'undefined') {
        // 5. else      
        // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
        if (!isCallable(mapFn)) {
          throw new TypeError('Array.from: when provided, the second argument must be a function');
        }

        // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
        if (arguments.length > 2) {
          T = arguments[2];
        }
      }

      // 10. Let lenValue be Get(items, "length").
      // 11. Let len be ToLength(lenValue).
      var len = toLength(items.length);

      // 13. If IsConstructor(C) is true, then
      // 13. a. Let A be the result of calling the [[Construct]] internal method 
      // of C with an argument list containing the single item len.
      // 14. a. Else, Let A be ArrayCreate(len).
      var A = isCallable(C) ? Object(new C(len)) : new Array(len);

      // 16. Let k be 0.
      var k = 0;
      // 17. Repeat, while k < len… (also steps a - h)
      var kValue;
      while (k < len) {
        kValue = items[k];
        if (mapFn) {
          A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
        } else {
          A[k] = kValue;
        }
        k += 1;
      }
      // 18. Let putStatus be Put(A, "length", len, true).
      A.length = len;
      // 20. Return A.
      return A;
    };
  }());
}

有些看不太懂~~~~


Array.isArray()

基础语法

Array.isArray(obj)

参数说明

obj

需要检测的值。


返回值说明

如果值是 Array,则为true; 否则为false。


详细说明

Array.isArray() 用于确定传递的值是否是一个Array

有关更多详细信息,请参阅文章严格判定JavaScript对象是否为数组


案例

基础案例

// 下面的函数调用都返回 true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
Array.isArray(new Array('a', 'b', 'c', 'd'))
// 鲜为人知的事实:其实 Array.prototype 也是一个数组。
Array.isArray(Array.prototype); 

// 下面的函数调用都返回 false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray(new Uint8Array(32))
Array.isArray({ __proto__: Array.prototype });


instanceof 和 isArray

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

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]

// Correctly checking for Array
Array.isArray(arr);  // true
// Considered harmful, because doesn't work though iframes
arr instanceof Array; // false

Polyfill

如果原生不支持的话,在其他代码之前执行以下代码会创建 Array.isArray()

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

这个实现也有些看不太懂~~~


Array.of()

基础语法

Array.of(element0[, element1[, ...[, elementN]]])

参数说明

elementN

任意个参数,将按顺序成为返回数组中的元素。


返回值说明

新的 Array 实例。


详细描述

Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。

Array.of()Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个长度为7的空数组(注意:这是指一个有7个空位的数组,而不是由7个undefined组成的数组)。

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

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

此函数是ECMAScript 2015标准的一部分。


案例

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

polyfill

如果原生不支持的话,在其他代码之前执行以下代码会创建 Array.of()

if (!Array.of) {
  Array.of = function() {
    return Array.prototype.slice.call(arguments);
  };
}

这个实现也有些看不太懂~~~


今日学习总结


今日心情

今日主要是基于搜索来基础学习 Array 对象,希望明天准备学习一下Array实例对象的方法 ~~~~

本文使用 mdnice 排版