JavaScript 集合对象

1. 集合对象

1.1 Object

关于Object类型的建立和底层存储原理我在另外一篇文章有说明: JavaScript 对象属性底层原理javascript

咱们知道了大多数状况下Object底层都是Hash结构,咱们再看看V8中从Object派生的继承图html

1.2 Array

数组是一种类列表对象,能够存储重复的对象,只能用整数做为数组元素的索引。
在V8中数组继承于Object,数据结构依然是Hash表。java

//An instance of the built-in array constructor (ECMA-262, 15.4.2).
//Definition at line 3562 of file v8.h
 class V8_EXPORT Array : public Object {
   public:
   uint32_t Length() const;
   /**
    * Creates a JavaScript array with the given length. If the length
    * is negative the returned array will have length 0.
    */
   static Local<Array> New(Isolate* isolate, int length = 0);
 
   V8_INLINE static Array* Cast(Value* obj);
  private:
   Array();
   static void CheckCast(Value* obj);
 };

1.3 Map

Map对象保存键值对,Key能够是任何值(对象或原始值),Value也能够是任何值。node

  • Key的比较是基于 "SameValueZero" 算法:NaN 是与 NaN 相等的(虽然 NaN !== NaN),剩下全部其它的值是根据 === 运算符的结果判断是否相等。

根据ECMA6-262的定义c++

Map object must be implemented using either hash tables or other mechanisms that, on average, provide access times
that are sublinear on the number of elements in the collection.
Map能够是hash表结构或相等访问时间的结构.算法

根据如下定义,在V8中Map继承于Object,数据结构依然是Hash表。数组

//An instance of the built-in Map constructor (ECMA-262, 6th Edition, 23.1.1).
 class V8_EXPORT Map : public Object {
  public:
   size_t Size() const;
   void Clear();
   V8_WARN_UNUSED_RESULT MaybeLocal<Value> Get(Local<Context> context,Local<Value> key);
   V8_WARN_UNUSED_RESULT MaybeLocal<Map> Set(Local<Context> context,Local<Value> key,Local<Value> value);
   V8_WARN_UNUSED_RESULT Maybe<bool> Has(Local<Context> context,Local<Value> key);
   V8_WARN_UNUSED_RESULT Maybe<bool> Delete(Local<Context> context,Local<Value> key);
 
   /**
    * Returns an array of length Size() * 2, where index N is the Nth key and
    * index N + 1 is the Nth value.
    */
   Local<Array> AsArray() const;
 
   /**
    * Creates a new empty Map.
    */
   static Local<Map> New(Isolate* isolate);
 
   V8_INLINE static Map* Cast(Value* obj);
 
  private:
   Map();
   static void CheckCast(Value* obj);
 };

Object VS Map

1. 相同

都容许你按键存取一个值、删除键、检测一个键是否绑定了值数据结构

2. 不一样

  • Object的键只能是字符串或者Symbols. Map的键能够是任意值,包括函数、对象、基本类型。
  • Map中的键值是有序的, 而Object中的键则不是。当遍历时,Map对象是按插入的顺序返回键值。
  • Map能够经过size属性获取键值对个数, Object则不行。
    注: ES5中的Object.keys()能够返回普通对象的全部属性,可是若是是方法对象,定义在原型链上的属性没法经过该方法获取
  • Map内置有Iterator能够直接迭代,而Object的迭代须要先获取它的键的数组,而后再进行迭代。
  • Map在涉及频繁增删键值对的场景下会相对Object性能较好。

1.4 Set

Set对象容许你存储任何类型的惟一值,不管是原始值或者是对象引用。ide

根据ECMA6-262的定义函数

Set objects must be implemented using either hash tables or other mechanisms that, on average, provide access times
that are sublinear on the number of elements in the collection.
Distinct values are discriminated using the SameValueZero comparison algorithm.

  • Set能够是hash表结构或相等访问时间的结构
  • Key的比较是基于 "SameValueZero" 算法

根据如下定义,在V8中Set继承于Object,数据结构依然是Hash表。

//An instance of the built-in Set constructor (ECMA-262, 6th Edition, 23.2.1).
class V8_EXPORT Set : public Object {
  public:
   size_t Size() const;
   void Clear();
   V8_WARN_UNUSED_RESULT MaybeLocal<Set> Add(Local<Context> context,Local<Value> key);
   V8_WARN_UNUSED_RESULT Maybe<bool> Has(Local<Context> context,Local<Value> key);
   V8_WARN_UNUSED_RESULT Maybe<bool> Delete(Local<Context> context,Local<Value> key);
 
   /**
    * Returns an array of the keys in this Set.
    */
   Local<Array> AsArray() const;
 
   /**
    * Creates a new empty Set.
    */
   static Local<Set> New(Isolate* isolate);
 
   V8_INLINE static Set* Cast(Value* obj);
 
  private:
   Set();
   static void CheckCast(Value* obj);
 };

1.5 WeakMap

WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值能够是任意的。
与Map相比区别:

  1. Map内部有两个数组(一个存放键,一个存放值), 给map 设置值时会同时将键和值添加到这两个数组的末尾。从而使得键和值的索引在两个数组中相对应,所以赋值和搜索操做都是 O(n) 的时间复杂度。
  2. 由于数组会一直引用着每一个键和值,这种引用使得垃圾回收算法不能回收处理他们,即便没有其余任何引用存在了。
  3. WeakMap 持有的是每一个键或值对象的“弱引用”,这意味着在没有其余引用存在时垃圾回收能正确进行。正因为这样的弱引用,WeakMap 的 key 是不可枚举的 (没有方法能给出全部的 key)。

1.6 WeakSet

它和 Set 对象的区别有两点:

  1. WeakSet对象中只能存放对象引用, 不能存放值, 而 Set 对象均可以.
  2. WeakSet对象中存储的对象值都是被弱引用的, 若是没有其余的变量或属性引用这个对象值, 则这个对象值会被当成垃圾回收掉. 正由于这样, WeakSet 对象是没法被枚举的, 没有办法拿到它包含的全部元素.

2. 集合对象的遍历

2.1 For In

遍历对象的可枚举(enumerable为true,非Symbol)属性,以及对象从其构造函数原型中继承的属性。
原理:首先枚举当前对象的enumerable为true的全部属性,而后枚举从原型链中构造函数原型中继承的属性(更接近原型链中对象的属性覆盖原型属性)。
ES 5标准,设计目的用于遍历key
注:使用for in循环数组会有以下缺陷

  • 索引是字符串型的数字,于是不能直接进行几何运算
  • 遍历顺序可能不是实际的内部顺序
  • 会遍历数组全部的可枚举属性,包括原型。例如的原型方法method和name属性
//函数和实例的遍历
function F0(){
  this.a = "1";
}
F0.b = "2";
for(var k in F0){
  console.log(k);//b
}
var fi0=new F0();
for(var k in fi0){
  console.log(k);//a
}

//函数继承的遍历
function F1(){
  this.a = "a";  
  this.c = "F1";
}
function F2(){
  this.b = "b";
  this.c = "F2"
}
F2.prototype=new F1();
var fi2 = new F2();
for(var k in fi2){
  console.log(k+":"+fi2[k]);//b:b,c:F2,a:a
}

//数组遍历
var ar1 = [1,2,3];
ar1.a = "a";
for(var k in ar1){
  console.log(k);//0,1,2,a
}

2.2 For of

检查对象是否具备Symbol.iterator属性,若是存在,就调用该属性返回的遍历器方法。以上的集合都已经内置了该属性,全部可使用For of遍历。
ES 6标准,遍历value
与for in相比,有以下区别

  • 处集合对象外,还支持类对象和字符串
  • 不支持遍历普通对象,但可经过与Object.keys()搭配使用实现遍历
  • 遍历后的输出结果为数组元素的值
var ar2 = [1,2,3]
ar2.length = 5;
ar2.push(4);

for (var k in ar2) {
  console.log(ar2[k]); //1,2,3,4
}

for (var v of ar2) {
  console.log(v); //1,2,3,undefined,undefined,4
}

Refers:
http://www.ecma-international.org/publications/standards/Ecma-262.htm
https://v8docs.nodesource.com/node-10.6/annotated.html

相关文章
相关标签/搜索