百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

第58条:区分数组对象和类数组对象

xsobi 2024-12-22 21:36 1 浏览

示例

设想有两个不同类的API。第一个是位向量:有序的位集合

var bits=new BitVector;
bits.enable(4);
bits.enable([1,3,8,17]);

bits.bitAt(4);//1
bits.bitAt(8);//1
bits.bitAt(9);//0

enable方法被重载了,可以传入一个索引或索引的数组。第二个类的API是字符串集合:无序的字符串集合

var set=new StringSet;

set.add('Hamlet');
set.add(['Rosencrantz','Guildenstern']);
set.add({'Ophelia':1,'Polonius':1,'Horatio':1});

set.contains('Polonius');//true
set.contains('Guildenstern');//true
set.contains('Falstaff');//false

其中add方法被重载了,除了可以接收字符串和字符串数组外,还可以接收一个字典对象。

函数重载

为了实现BitVerctor.prototype.enable方法,可以通过测试其他情况来避免如何判断一个对象是否为数组的问题。

BitVector.prototype.enable=function(x){
    if(typeof x === 'number'){
        this.enableBit(x);
    }else{
        for(var i=0,n=x.length;i< n;i++){
 this.enableBit(x[i]);
        }
    }
};

这里只实现了索引及索引数组的判断,很容易实现。那StringSet.prototype.add方法怎么实现呢?这里需要区分数组和对象。在js中数组就是一个对象。这里真正想做的是分离数组对象和非数组对象。这样的区分和js的灵活的类数组对象的概念是有争执的。任何对象都可被视为数组,只要它遵循正确的接口。而且也没有明确的方法来测试一个对象是否满足一个接口。可以尝试把具有length属性的对象视为数组,但这也会有错误出现,比如碰巧一个字典对象有length属性呢?

dimensions.add({
    'length':1,
    'height':1,
    'width':1
});

使用不精确的启发探索法来确定接口是一个容易被误解和滥用的方法。猜测一个对象是否实现了结构类型有时被称为鸭子测试,这是不好的实践。因为对象没有明确的信息标记来表示它们实现的结构类型,并没有可靠的编程方法来检测信息。

重载两种类型意味着必须有一种方法来区分这两种情况。不可能检测一个值是否实现了一种结构性的接口。规则-API绝不应该重载与其他类型有重叠的类型

instanceof

对于StringSet,开始就不要使用结构性的类数组接口。相反,我们应当选择一种类型,这种类型具有明确的定义的“标签”,可以表明用户真想将其作为一个数组。一个显而易见但不完美的选择是使用instanceof操作符来测试一个对象是否继承自Array.prototype。

StringSet.prototype.add=function(x){
    if(typeof x === 'string'){
        this.addString(x);
    }else if(x instaceof Array){
        x.forEach(function(s){
 this.addString(s);
        },this);
    }
};

任何时候一个Array的实例,它的行为会像一个数组。但有时候,在一些可以允许多个全局对象的环境中,可能会有标准的Array构造函数和原型对象的多份副本。在浏览器中有这种情况,每个frame会有标准库的一份单独副本。当跨frame通信时,一个frame中的数组不会继承自另一个frame的Array.prototype。

Array.isArray

出于上面这种情况,ES5引入了Array.isArray函数,其用于测试一个值是否是数组,而不管原型继承。在ES标准中,该函数测试对象的内部[[Class]]属性值是否是Array。当需要测试一个对象是否是真数组,而不仅仅是类数组对象,Array.isArray方法比instanceof操作符更好。

StringSet.prototype.add=function(x){
    if(typeof x === 'undefined'){
        this.addString(x);
    }else if(Array.isArray(x)){
        x.forEach(function(s){
 this.addString(s);
        },this);
    }else{
        for(var key in x){
 this.addString(key);
        }
    }
};

Object.prototype.toString

在不支持ES5的环境中,可以使用标准的Object.prototype.toString方法测试一个对象是否为数组。

var toString=Object.prototype.toString;
functin isArray(x){
    return toString.call(x) === '[object Array]';
}

Object.prototype.toString函数使用对象内部的[[Class]]属性创建结果字符串,所以测试一个对象是否为数组时,它比instanceof操作符更准确。

注意:该版本的add方法存在不同的影响该API的使用者的行为。重载API的数组版本不接收随意的类数组对象。例如,你不能传入arguments对象并期待它视为数组。

function MyClass{
    this.keys=new StringSet;
    //...
}
MyClass.prototype.update=function{
    this.keys.add(arguments);
};

Array.prototype.slice

这里会把arguments作为字典来对待。可以把arguments对象转化为真正的数组。

MyClass.prototype.update=function{
    this.keys.add(.slice.call(arguments));
};

当调用者传入多个参数时,会把它们转化为真正的数组,然后再使用add操作。当调用者想传递一个类数组对象给一个期望接收真正数组的API时,都需要做这种转换。可以在API的文档中标明各API接收哪些类型的参数。如上面的例子中,enable方法接收数字和类数组对象。add方法接收字符串、真数组和非数组对象。

提示

  • 绝不重载与其他类型有重叠的结构类型

  • 当重载一个结构类型与其他类型时,先测试其他类型

  • 当重载其他对象类型时,接收真数组而不是类数组对象

  • 文档标注你的API是否接收真数组或类数组

  • 使用ES5提供的Array.isArray方法测试真数组

相关推荐

js向对象中添加元素(对象,数组) js对象里面添加元素

一、添加一个元素对象名["属性名"]=值(值:可以是一个值,可以是一个对象,也可以是一个数组)这样添加进去的元素,就是一个值或对象或数组...

JS小技巧,如何去重对象数组?(一)

大家好,关于数组对象去重的业务场景,想必大家都遇到过类似的需求吧,这对这样的需求你是怎么做的呢。下面我就先和大家分享下如果是基于对象的1个属性是怎么去重实现的。方法一:使用.filter()和....

「C/C++」之数组、vector对象和array对象的比较

数组学习过C语言的,对数组应该都不会陌生,于是这里就不再对数组进行展开介绍。模板类vector模板类vector类似于string,也是一种动态数组。能够在运行阶段设置vector对象的长度,可以在末...

如何用sessionStorage保存对象和数组

背景:在工作中,我将[{},{}]对象数组形式,存储到sessionStorage,然后ta变成了我看不懂的形式,然后我想取之用之,发现不可能了~记录这次深刻的教训。$clickCouponIndex...

JavaScript Array 对象 javascript的array对象

Array对象Array对象用于在变量中存储多个值:varcars=["Saab","Volvo","BMW"];第一个数组元素的索引值为0,第二个索引值为1,以此类推。更多有...

JavaScript中的数组Array(对象) js array数组

1:数组Array:-数组也是一个对象-数组也是用来存储数据的-和object不同,数组中可以存储一组有序的数据,-数组中存储的数据我们称其为元素(element)-数组中的每一个元素都有一...

数组和对象方法&amp;数组去重 数组去重的5种方法前端

列举一下JavaScript数组和对象有哪些原生方法?数组:arr.concat(arr1,arr2,arrn);--合并两个或多个数组。此方法不会修改原有数组,而是返回一个新数组...

C++ 类如何定义对象数组?初始化数组?linux C++第43讲

对象数组学过C语言的读者对数组的概念应该很熟悉了。数组的元素可以是int类型的变量,例如int...

ElasticSearch第六篇:复合数据类型-数组,对象

在ElasticSearch中,使用JSON结构来存储数据,一个Key/Value对是JSON的一个字段,而Value可以是基础数据类型,也可以是数组,文档(也叫对象),或文档数组,因此,每个JSON...

第58条:区分数组对象和类数组对象

示例设想有两个不同类的API。第一个是位向量:有序的位集合varbits=newBitVector;bits.enable(4);bits.enable([1,3,8,17]);b...

八皇后问题解法(Common Lisp实现)

如何才能在一张国际象棋的棋盘上摆上八个皇后而不致使她们互相威胁呢?这个著名的问题可以方便地通过一种树搜索方法来解决。首先,我们需要写一个函数来判断棋盘上的两个皇后是否互相威协。在国际象棋中,皇后可以沿...

visual lisp修改颜色的模板函数 怎么更改visual studio的配色

(defunBF-yansemokuai(tuyuanyanse/ss)...

用中望CAD加载LISP程序技巧 中望cad2015怎么加载燕秀

1、首先请加载lisp程序,加载方法如下:在菜单栏选择工具——加载应用程序——添加,选择lisp程序然后加载,然后选择添加到启动组。2、然后是添加自定义栏以及图标,方法如下(以...

图的深度优先搜索和广度优先搜索(Common Lisp实现)

为了便于描述,本文中的图指的是下图所示的无向图。搜索指:搜索从S到F的一条路径。若存在,则以表的形式返回路径;若不存在,则返回nil。...

两个有助于理解Common Lisp宏的例子

在Lisp中,函数和数据具有相同的形式。这是Lisp语言的一个重大特色。一个Lisp函数可以分析另一个Lisp函数;甚至可以和另一个Lisp函数组成一个整体,并加以利用。Lisp的宏,是实现上述特色的...