JavaScript中数组的特殊之处(一)

数组是几乎所有编程语言的基础语法,JavaScript因为语法特性,之前缺少集合类对象,对数组的使用相对更多。
数组作为基础的语法知识,大部分人都已经非常熟悉了。本文将不会介绍它的基础语法和用法,而是从JavaScript中数组的一些特殊之处入手,通过这些少有的特性介绍,更加加深我们对数组知识的理解。

基本介绍

首先,作为开始,我们还是需要简单介绍下JavaScript中的数组:

  • 有序的数据集合,索引值从0开始递增
  • 拥有length长度属性
  • 数组元素值可以是JavaScript中的任何类型
  • 是动态的,可以增减元素
  • 可以循环数组元素,拥有一系列可操作的实例方法
  • 支持元素为数组的多维数组
  • 读取元素以数组名[索引值]的方式表示

以上就是对数组的基础介绍,大部分都很熟悉,接下来,我们就来看看数组的一些特殊之处,

数组类型和判断

在JavaScript中,是没有数组这种数据类型的,所以数组本质上是一种特殊的对象,它的类型值会返回 object,如下所示:

typeof [] // 'object'

由于返回的是 object 类型,就无法通过 typeof 语法判断一个值或对象是否是数组,得使用其他方式,才能正确的判断数组对象。

typeof的详细知识,可见博文一文搞懂js中的typeof用法

判断为数组的方式

判断一个对象是否为数组,有不少种方式,但我们一般使用较多也就两三种,而其中最正确有效的方式就两种。

正确判断方式

这两种正确有效判断数组类型的方式如下:

  • Array.isArray()
    ES6推出的语法,专门用于判断对象类型是否为数组,是则返回true,否则返回false,简单好用。
  • Object.prototype.toString.call()
    在ES6推出之前的JS语法中,一般使用这种方式来判断数组,除了数组,它可以准确判断出其他几乎所有的JS数据类型。
Array.isArray([1, 2, 3]) // true
Object.prototype.toString.call([]) // '[object Array]'
Object.prototype.toString.call({
    
    }) // '[object Object]'

以上代码,就是这两种有效方式的示例,都能准确有效的判断。
当前的前端开发种,ES6语法基本普及的情况下,使用 Array.isArray() 将更方便。

其他方式介绍

除以上两种以外,还有其他几种基于原型链上的判断方式,可用于判断数组,但这些方式都不够准确:

  • [] instanceof Array
    这里使用instanceof运算符表示给定值是否是数组的实例。
  • [].constructor === Array
    给定值的实例构造函数是否是数组。
  • Array.prototype.isPrototypeOf([])
    给定值的原型链上是否存在数组。
  • Object.getPrototypeOf([]) === Array.prototype
    给定值的原型对象是否等于数组的原型对象。

这几种方式本质上都比较类似,只不过由于原型链能够被修改,所以这几种方式并不推荐使用。
如使用 instanceof 判断的方式:

[] instanceof Array // true
[] instanceof Object // true

以上代码,使用 instanceof 运算符时,一个数组实例属于 ArrayObject,都是成立的,因为Object在Array的原型链上。

数组索引值和长度

数组通过下标索引值进行元素值的读取,必须要使用方括号才可以,否则无法读取元素值。

const arr = [1, 2, 3]
arr[1] // 2
arr.1 // Uncaught SyntaxError: Unexpected number

以上代码,使用 arr.1 的写法,就报了语法错误,因为JavaScript中单独数字作为标识符是不合法的。关于错误类型,可见博文JavaScript中的Error错误对象与自定义错误类型

前面提到,数组是一种特殊的对象,而object对象,是可以通过键名来读取元素的,只不过数组的键名只能是数字,所以当做标识符读取时报错。而object对象如果使用数字作键名时,也无法通过标识符来读取:

const obj = {
    
     1: 'hello' }
obj.1 // Uncaught SyntaxError: Unexpected number

如上代码所示,对象使用 obj.1 的方式读取属性,也是报同样的错误。

索引值是字符串

数组使用方括号读取元素值,而Object对象也能通过这样的方法读取属性值,这样就算对象的键名是数字也能正常读取了:

const obj = {
    
     1: 'hello', key: 'world' }
obj[1] // 'hello'
obj['key'] // 'world'

事实上,JavaScript中Object对象的键名均为字符串类型,而数组的类型又是object,所以它的索引值(键名)也可以使用字符串。

const arr = [1, 2, 3]
arr['1'] // 2
arr['2'] // 3

以上代码,当使用 ‘1’、‘2’ 等字符串时,也能正确读取数组的元素。

但需要注意的是,数组的索引值,必须是能自动转成正整数数字的值。如果是其他数值的时候,则需要注意。

索引值为小数、负数

如果数组使用小数或者负数读写操作时,数组是什么一种表现呢,可以看下面的代码:

const arr = [1,2]
arr[-1] = 0
arr[2.0] = 5
arr[3.6] = 8
Array.isArray(arr) // true
arr // [1, 2, 5, -1: 0, 3.6: 8]
arr.length // 3
arr[-1] // 0
arr[3.6] // 0

以上代码所示:

  • 使用小数 2.0,能自动转换成正整数,所以可以作为数组的第三个元素;
  • 使用负数 -1 和无法自动转换成正整数的小数 3.6,这两种情况都作为了数组的键值对的方式成为了数组的属性,但并不被包含在数组元素中,因为数组的length属性为3,并不包含这两个值。
  • 负数 -1 和小数 3.6,都被当作字符串在使用,同理,也可以使用 arr[true] = 50,这里的bool值 true,也被当做了字符串 ‘true’。

所以,负数或小数不能作为数组的索引值,但可以被当做键值对的方式,作为数组的属性被读写。

索引值是字符串等其他类型时

如果我们给数组使用字符串、布尔值等其他类型的值作为下标索引时,这个时候和小数负数的表现类似。
就是把数组用作了对象,这些类型的下标索引则都被当做了数组对象的属性在操作,可以正常读写,但不是数组的元素,不计入数组的length长度中。

const arr = [1]
arr['key'] = 0
arr['value'] = '是的'
arr.length // 1
arr // [1, key: 0, value: '是的']

以上代码,就体现了数组属于对象类型的特点,可以增加键值对的属性。

length属性

length是数组的长度属性,表示数组元素的个数,但请注意,它不是只读,而是可写的,即我们可以给数组的length属性赋值:

[].length = 3
// 数组的长度被赋予为3

但有几点要注意:

  • 如果设置数组的length值小于元素个数,则数组会自动删除所有大于length值的元素
  • 如果设置数组的length值大于元素个数,则数组元素个数会自动增加到length值,并且新增的元素都是空位(返回undefined)
  • 如果将数组的length属性设置为0,则会清空整个数组,变为空数组
  • 数组的length值必须为正整数(或可以自动转换成正整数的值),其他值都是不合法的,设置不合法值时会报错。
    • 能自动转换成正整数的值包含 2.0''truefalse'10' 等等这类
const arr = [1, 2]
arr.length = true
console.log(arr) // [1]

arr.length = -1 // VM307:1 Uncaught RangeError: Invalid array length

以上代码示例,
修改了数组的length值为true,true可以转换成1,数组的长度就变为了1,元素被删除了一个;
当设置数组长度为负数的时候,报错,无效的数组长度。

另外,除了以上几种情况,还有我们需要知道的一点就是,数组有最大长度。

数组的最大长度

JavaScript中的数组能赋予的最大长度是32位的正整数,即 2**32 - 1 = 4294967295,从0计算,长度减1。
当我们给数组设置的长度超过最大长度的值时,也会报无效长度的错误:

[].length = 4294967296 // Uncaught RangeError: Invalid array length

但如果我们使用超过最大长度的数字作为键名给数组赋值的话,则仍然可以使用,如下代码所示:

const arr = []
arr[4294967296] = 1
arr // [4294967296: 1]

前面也有介绍过,这是由于数组本质上是个对象,当使用超出最大长度范围的数字时,这个数字会被当做数组一个属性的键名,并且可以自动转成字符串,这个时候它并不数组的元素,也不计算在数组的length属性里,和前文介绍索引值时一样。

猜你喜欢

转载自blog.csdn.net/jimojianghu/article/details/128031232