JavaScript 数组和对象就像书和报纸一样

2017/05/26 · JavaScript
· 对象,
数组

原文出处: Kevin
Kononenko
   译文出处:蔡耀冠   

简评:作者将 JavaScript
的数组和对象比喻为书和报纸,数组更注重顺序,而标签则对对象更重要。

如果你读书、看报,那么你会理解 JavaScript 的数组和对象之间的不同之处。

当你刚开始学 JavaScript
时,用哪一种方式组织和存储数据更好往往会让人困惑。

一方面,你可能在学习 “for”
循环的时候熟悉了数组。但是,一旦你尽可能多地将数据塞进数组,当你在检查你的代码的时候,你创造的乱七八糟的东西将会让你难以理解。

当你可以快速地决定每个结构的目标时,在对象和数组之间选择会简单得多。数组和书籍存储信息的方式相差无几,而对象则和报纸存储信息的方式差不多。

让我们来看看!

介绍

数组:数据的顺序是最重要的

这是超短篇小说的章节,以数组的形式。

JavaScript

var book = [‘foreword’, ‘boyWhoLived’, ‘vanishingGlass’,
‘lettersFromNoOne’, ‘afterword’];

1
var book = [‘foreword’, ‘boyWhoLived’, ‘vanishingGlass’, ‘lettersFromNoOne’, ‘afterword’];

好吧,我承认,这是《哈利波特》系列的第一本的前三章。这是数组的可视化的形式:

威尼斯人线上娱乐 1当顺序成为组织信息的最重要的因素时你应该使用数组。没有人(我希望)这样看《哈利波特》的章节标题,“嗯…,那章看起来很有趣,让我跳到那!”章节的顺序告诉你下一章是什么。

当你从数组中检索信息时,你使用每个元素的下标。数组是从零开始索引的,这意味着从
0 开始计数而不是 1。

如果你想要访问下标为 0 的书籍数组,你要用:

JavaScript

books[0]

1
books[0]

然后你会得到:

JavaScript

‘foreword’

1
‘foreword’

如果你想得到这本书第三章的章节标题,你要用:

JavaScript

books[2]

1
books[2]

你会基于书的章节顺序来读下一章,而不是基于书的章节标题。

享元模式(Flyweight),运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类)。

对象:数据标签是最重要的

报纸可能看起来是这样的,以对象的形式。

JavaScript

var newspaper= { sports: ‘ARod Hits Home Run’, business: ‘GE Stock Dips
Again’, movies: ‘Superman Is A Flop’ }

1
2
3
4
5
var newspaper= {
  sports: ‘ARod Hits Home Run’,
  business: ‘GE Stock Dips Again’,
  movies: ‘Superman Is A Flop’
}

下面是以可视化的形式来看同样的数据。

威尼斯人线上娱乐 2当你要基于数据标签来组织数据时,对象是最好的。当你看报纸时,你可能不会从前往后一页页地读。你会基于新闻标题来跳过特定的部分。无论在报纸的哪个地方,你都可以快速的跳过并且有合适的上下文。这和书不一样,书的章节顺序很重要。

对象通过键/值对来组织数据。看起来像这样:

JavaScript

key: value

1
key: value

如果你想要进入报纸的商业部分,你会使用这样的

JavaScript

newspaper[‘business’]

1
newspaper[‘business’]

或者:

JavaScript

newspaper.business

1
newspaper.business

这回返回 ‘GE Stock Dips
Again’。所以,通过数据的标签(键)来访问数据是最简单的,你想要把数据存在对象里。

享元模式可以避免大量非常相似类的开销,在程序设计中,有时需要生产大量细粒度的类实例来表示数据,如果能发现这些实例除了几个参数以外,开销基本相同的
话,就可以大幅度较少需要实例化的类的数量。如果能把那些参数移动到类实例的外面,在方法调用的时候将他们传递进来,就可以通过共享大幅度第减少单个实例
的数目。

结合对象和数组

目前为止,我们只是在数组和对象中保存了
strings,你也可以保存其他类型的数据,比如 numbers 和 booleans,同时:

  1. 对象内的数组
  2. 数组中的对象
  3. 数组中的数组
  4. 对象中的对象

现在开始变复杂了。但是,你几乎只需要两种以扩展方式的组合来存储你的数据。当你一星期后回顾代码也想要理解。

让我们再看下书的例子。如果我们想要保存每章的页数会怎样呢?用对象来填满我们的数组可能是最好的。像这样:

JavaScript

var book =[ [‘foreword’, 14], [‘boywholived’, 18] ]

1
2
3
4
var book =[
  [‘foreword’, 14],
  [‘boywholived’, 18]
]

JavaScript

var book = [ {name:’foreword’, pageCount: 14}, {name:’boyWhoLived’,
pageCount: 18}, {name:’vanishingGlass’, pageCount: 13},
{name:’lettersFromNoOne’, pageCount: 17}, {name:’afterword’, pageCount:
19} ];

1
2
3
4
5
6
7
var book = [
  {name:’foreword’, pageCount: 14},
  {name:’boyWhoLived’, pageCount: 18},
  {name:’vanishingGlass’, pageCount: 13},
  {name:’lettersFromNoOne’, pageCount: 17},
  {name:’afterword’, pageCount: 19}
];

我们维护了每章的顺序,现在我们可以叫出每章的特定的属性。所以,如果我们想要知道第二张的页数,我们可以用:

JavaScript

book[1][‘pageCount’]

1
book[1][‘pageCount’]

这会返回一个 18 的

现在假设你想知道你当地报纸每个栏目的顶级作者的排名,基于他们的资历。你可以在报纸对象中用一个数组来表达,像这样:

JavaScript

var newspaper= { sports: ‘ARod Hits Home Run’, sportsWriters: [‘Miramon
Nuevo’, ‘Rick Reilly’, ‘Woddy Paige’], business: ‘GE Stock Dips Again’,
businessWriters: [‘Adam Smith’, ‘Albert Humphrey’, ‘Charles Handy’],
movies: ‘Superman Is A Flop’, moviesWriters: [‘Rogert Ebert’, ‘Andrew
Sarris’, ‘Wesley Morris’] }

1
2
3
4
5
6
7
8
var newspaper= {
  sports: ‘ARod Hits Home Run’,
  sportsWriters: [‘Miramon Nuevo’, ‘Rick Reilly’, ‘Woddy Paige’],
  business: ‘GE Stock Dips Again’,
  businessWriters: [‘Adam Smith’, ‘Albert Humphrey’, ‘Charles Handy’],
  movies: ‘Superman Is A Flop’,
  moviesWriters: [‘Rogert Ebert’, ‘Andrew Sarris’, ‘Wesley Morris’]
}

一个数组用来存储作者很合适,因为顺序很重要。你知道每个数组中靠前的作者排名更高。下标为
0 的作者是排名最高的。

你可以通过创建对象来优化报纸对象。比如,一个包含标题和作者列表的体育对象。但我会让你来尝试!

1 赞 2 收藏
评论

威尼斯人线上娱乐 3

那么如果在JavaScript中应用享元模式呢?有两种方式,第一种是应用在数据层上,主要是应用在内存里大量相似的对象上;第二种是应用在DOM层上,享元可以用在中央事件管理器上用来避免给父容器里的每个子元素都附加事件句柄。

享元与数据层

Flyweight中有两个重要概念–内部状态intrinsic和外部状态extrinsic之分,内部状态就是在对象里通过内部方法管理,而外部信息可以在通过外部删除或者保存。

说白点,就是先捏一个的原始模型,然后随着不同场合和环境,再产生各具特征的具体模型,很显然,在这里需要产生不同的新对象,所以Flyweight模式中常出现Factory模式,Flyweight的内部状态是用来共享的,Flyweight
factory负责维护一个Flyweight pool(模式池)来存放内部状态的对象。

使用享元模式

让我们来演示一下如果通过一个类库让系统来管理所有的书籍,每个书籍的元数据暂定为如下内容:

复制代码 代码如下:

ID
Title
Author
Genre
Page count
Publisher ID
ISBN

我们还需要定义每本书被借出去的时间和借书人,以及退书日期和是否可用状态:

复制代码 代码如下:

checkoutDate
checkoutMember
dueReturnDate
availability

因为book对象设置成如下代码,注意该代码还未被优化:

复制代码 代码如下:

var Book = function( id, title, author, genre, pageCount,publisherID,
ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){
   this.id = id;
   this.title = title;
   this.author = author;
   this.genre = genre;
   this.pageCount = pageCount;
   this.publisherID = publisherID;
   this.ISBN = ISBN;
威尼斯人线上娱乐,   this.checkoutDate = checkoutDate;
   this.checkoutMember = checkoutMember;
   this.dueReturnDate = dueReturnDate;
   this.availability = availability;
};
Book.prototype = {
   getTitle:function(){
       return this.title;
   },
   getAuthor: function(){
       return this.author;
   },
   getISBN: function(){
       return this.ISBN;
   },
/*其它get方法在这里就不显示了*/

// 更新借出状态
updateCheckoutStatus: function(bookID, newStatus,
checkoutDate,checkoutMember, newReturnDate){
   this.id  = bookID;
   this.availability = newStatus;
   this.checkoutDate = checkoutDate;
   this.checkoutMember = checkoutMember;
   this.dueReturnDate = newReturnDate;
},
//续借
extendCheckoutPeriod: function(bookID, newReturnDate){
    this.id =  bookID;
    this.dueReturnDate = newReturnDate;
},
//是否到期
isPastDue: function(bookID){
   var currentDate = new Date();
   return currentDate.getTime() > Date.parse(this.dueReturnDate);
 }
};

程序刚开始可能没问题,但是随着时间的增加,图书可能大批量增加,并且每种图书都有不同的版本和数量,你将会发现系统变得越来越慢。几千个book对象在内存里可想而知,我们需要用享元模式来优化。

我们可以将数据分成内部和外部两种数据,和book对象相关的数据(title,
author 等)可以归结为内部属性,而(checkoutMember,
dueReturnDate等)可以归结为外部属性。这样,如下代码就可以在同一本书里共享同一个对象了,因为不管谁借的书,只要书是同一本书,基本信息是一样的:

复制代码 代码如下:

/*享元模式优化代码*/
var Book = function(title, author, genre, pageCount, publisherID,
ISBN){
   this.title = title;
   this.author = author;
   this.genre = genre;
   this.pageCount = pageCount;
   this.publisherID = publisherID;
   this.ISBN = ISBN;
};

定义基本工厂

让我们来定义一个基本工厂,用来检查之前是否创建该book的对象,如果有就返回,没有就重新创建并存储以便后面可以继续访问,这确保我们为每一种书只创建一个对象:

复制代码 代码如下:

/* Book工厂 单例 */
var BookFactory = (function(){
   var existingBooks = {};
   return{
       createBook: function(title, author,
genre,pageCount,publisherID,ISBN){
       /*查找之前是否创建*/
           var existingBook = existingBooks[ISBN];
           if(existingBook){
                   return existingBook;
               }else{
               /* 如果没有,就创建一个,然后保存*/
               var book = new Book(title, author,
genre,pageCount,publisherID,ISBN);
               existingBooks[ISBN] =  book;
               return book;
           }
       }
   }
});

管理外部状态 外部状态,相对就简单了,除了我们封装好的book,其它都需要在这里管理:

复制代码 代码如下:

/*BookRecordManager 借书管理类 单例*/
var BookRecordManager = (function(){
   var bookRecordDatabase = {};
   return{
       /*添加借书记录*/
       addBookRecord: function(id, title, author,
genre,pageCount,publisherID,ISBN, checkoutDate, checkoutMember,
dueReturnDate, availability){
           var book = bookFactory.createBook(title, author,
genre,pageCount,publisherID,ISBN);
            bookRecordDatabase[id] ={
               checkoutMember: checkoutMember,
               checkoutDate: checkoutDate,
               dueReturnDate: dueReturnDate,
               availability: availability,
               book: book;

           };
       },
    updateCheckoutStatus: function(bookID, newStatus, checkoutDate,
checkoutMember,     newReturnDate){
        var record = bookRecordDatabase[bookID];
        record.availability = newStatus;
        record.checkoutDate = checkoutDate;
        record.checkoutMember = checkoutMember;
        record.dueReturnDate = newReturnDate;
   },
   extendCheckoutPeriod: function(bookID, newReturnDate){
       bookRecordDatabase[bookID].dueReturnDate = newReturnDate;
   },
   isPastDue: function(bookID){
       var currentDate = new Date();
       return currentDate.getTime() >
Date.parse(bookRecordDatabase[bookID].dueReturnDate);
   }
 };
});

通过这种方式,我们做到了将同一种图书的相同信息保存在一个bookmanager对象里,而且只保存一份;相比之前的代码,就可以发现节约了很多内存。

网站地图xml地图