Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array in JavaScript #2

Closed
Tracked by #6
swiftwind0405 opened this issue Jan 13, 2020 · 0 comments
Closed
Tracked by #6

Array in JavaScript #2

swiftwind0405 opened this issue Jan 13, 2020 · 0 comments

Comments

@swiftwind0405
Copy link
Owner

swiftwind0405 commented Jan 13, 2020

非连续内存

本质上JavaScript数组的特殊之处在于JavaScript的数组不一定是连续内存。
而维基百科关于数组的定义:

在计算机科学中,数组数据结构(英语:array data structure),简称数组(英语:Array),是由相同类型的元素(element)的集合所组成的数据结构,分配一块连续的内存来存储。

JavaScript的数组是否分配连续内存取决于数组成员的类型,如果统一是单一类型的数组那么会分配连续内存,如果数组内包括了各种各样的不同类型,那么则是非连续内存。

非连续内存的数组用的是类似哈希映射的方式存在,比如声明了一个数组,他被分配给了1001、2011、1088、1077四个非连续的内存地址,通过指针连接起来形成一个线性结构,那么当我们查询某元素的时候其实是需要遍历这个线性链表结构的,这十分消耗性能。

image

而线性储存的数组只需要遵循这个寻址公式,进行数学上的计算就可以找到对应元素的内存地址。

a[k]_address = base_address + k * type_size

非线性储存的数组其速度比线性储存的数组要慢得多。

数组类型的判断

ES6之前:

var a = [];
// 1.基于instanceof
a instanceof Array;
// 2.基于constructor
a.constructor === Array;
// 3.基于Object.prototype.isPrototypeOf
Array.prototype.isPrototypeOf(a);
// 4.基于getPrototypeOf
Object.getPrototypeOf(a) === Array.prototype;
// 5.基于Object.prototype.toString
Object.prototype.toString.apply(a) === '[object Array]';

以上,除了 Object.prototype.toString 外,其它方法都不能正确判断变量的类型。原因是只要手动指定了某个对象的 __proto__ 属性为 Array.prototype,便会导致了该对象继承了 Array 对象,这种毫不负责任的继承方式,使得基于继承的判断方案瞬间土崩瓦解。

因此 ES6 中有了更好判断数组类型的方法Array.isArray()

Array.isArray([]); // true
Array.isArray({0: 'a', length: 1}); // false

方法

数组原型提供的方法非常之多,主要分为三种,一种是会改变自身值的,一种是不会改变自身值的,另外一种是遍历方法。

改变自身值的方法(9个)

基于ES6,改变自身值的方法一共有9个,分别为pop、push、reverse、shift、sort、splice、unshift,以及两个ES6新增的方法copyWithin 和 fill。
对于能改变自身值的数组方法,日常开发中需要特别注意,尽量避免在循环遍历中去改变原数组的项

不会改变自身的方法(9个)

基于ES7,不会改变自身的方法一共有9个,分别为concat、join、slice、toString、toLocateString、indexOf、lastIndexOf、未标准的toSource以及ES7新增的方法includes。

遍历方法(12个)

基于ES6,不会改变自身的方法一共有12个,分别为forEach、every、some、filter、map、reduce、reduceRight 以及ES6新增的方法entries、find、findIndex、keys、values。

以上方法具体不再赘述,详情可参考:【深度长文】JavaScript数组所有API全解密以及Array - JavaScript | MDN

这些方法之间存在很多共性。比如:

  • 所有插入元素的方法, 比如 push、unshift,一律返回数组新的长度;
  • 所有删除元素的方法,比如 pop、shift、splice 一律返回删除的元素,或者返回删除的多个元素组成的数组;
  • 部分遍历方法,比如 forEach、every、some、filter、map、find、findIndex,它们都包含 function(value,index,array){}thisArg 这样两个形参。

Array.prototype 的所有方法均具有鸭式辨型这种神奇的特性。它们不止可以用来处理数组对象,还可以处理类数组对象。

例如 javascript 中一个纯天然的类数组对象字符串(String),像join方法(不改变当前对象自身)就完全适用,可惜的是 Array.prototype 中很多方法均会去试图修改当前对象的 length 属性,比如说 pop、push、shift, unshift 方法,操作 String 对象时,由于String对象的长度本身不可更改,这将导致抛出TypeError错误。

推荐一个查询方法是不是会造成mutate的网站:Does it mutate?

最佳实践

  • Replacing Array.indexOf with Array.includes
    要获取 index 就用 Array.indexOf,不然就用 Array.includes 获取布尔值
  • Using Array.find instead of Array.filter
    Array.filter 会遍历整个数组,并且返回多个值。Array.find 只返回满足这个回调第一个元素的值
  • Replacing Array.find with Array.some
    Array.some 返回一个需要的布尔值
  • Using Array.reduce instead of chaining Array.filter and Array.map
    后者会遍历两遍数组,推荐使用 Array.reduce 作为数组累加器

参考资料

@swiftwind0405 swiftwind0405 changed the title 代码规范 【Day17】reducer Mar 16, 2020
@swiftwind0405 swiftwind0405 changed the title 【Day17】reducer 【JavaScript】reducer Apr 29, 2020
@swiftwind0405 swiftwind0405 changed the title 【JavaScript】reducer 【JavaScript】Array Apr 29, 2020
@swiftwind0405 swiftwind0405 changed the title 【JavaScript】Array 【JavaScript】Array in JavaScript Sep 7, 2020
@swiftwind0405 swiftwind0405 changed the title 【JavaScript】Array in JavaScript Array in JavaScript Oct 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant