为啥我可以将命名属性添加到数组中,就好像它是一个对象一样?

Posted

技术标签:

【中文标题】为啥我可以将命名属性添加到数组中,就好像它是一个对象一样?【英文标题】:Why can I add named properties to an array as if it were an object?为什么我可以将命名属性添加到数组中,就好像它是一个对象一样? 【发布时间】:2010-10-26 19:54:13 【问题描述】:

下面两个不同的代码sn-ps好像和我一样:

var myArray = Array();
myArray['A'] = "Athens";
myArray['B'] = "Berlin";

var myObject = 'A': 'Athens', 'B':'Berlin';

因为它们的行为相同,而且typeof(myArray) == typeof(myObjects)(都产生“对象”)。

这些变体之间有什么区别吗?

【问题讨论】:

【参考方案1】:

实际上,javascript 中的所有内容都是一个对象,因此您可以通过在其上设置任意属性来“滥用”Array 对象。这是should be considered harmful。数组用于数字索引数据 - 对于非数字键,请使用 Object。

这里有一个更具体的例子,说明为什么非数字键不能“适应”数组:

var myArray = Array();
myArray['A'] = "Athens";
myArray['B'] = "Berlin";

alert(myArray.length);

这不会显示“2”,而是“0” - 实际上,没有向数组添加任何元素,只是向数组对象添加了一些新属性。

【讨论】:

myArray.length 返回数组中最后一个元素的数字索引/键,但不是元素的实际数量。 Array 对象的属性和数组值不一样吗? 我只是想说明 Array 对象的预期语义如果您只是将其视为常规对象,则会被滥用。不过,链接的文章做得更好:) 下次有人说 JavaScript 是一种很好的开发语言时,我会给他看这个示例。谢谢。 @Olivier,你所说的“错误”也可以是一个很棒的“功能”。您可以为数组添加标题和描述,而不会影响其内容或长度,也不必将它们包装在具有titledescriptionitems 属性的对象中。这完全取决于您对语言的了解程度以及使用方式。 在数组上使用自定义属性本质上不是错误的。错误的是,一旦您这样做,他们就希望它们充当数组成员。它们是数组属性,而不是成员,因此不受数组方法的影响。这实际上是上述链接文章的作者在 cmets 中所说的。现在,平心而论,我建议不要将其作为一种做法,因为它可能会使使用您的代码的人感到困惑。或者,如果他们刚刚开始,这将使他们走上一条危险的道路,举例来说。但我不会说 JavaScript 不好,因为它允许大多数不希望被允许的东西。【参考方案2】:

在 JS 中数组是对象,只是稍作修改(增加了一些函数)。

功能如下:

concat
every   
filer
forEach
join
indexOf
lastIndexOf
map
pop
push
reverse
shift
slice
some
sort
splice
toSource
toString
unshift
valueOf 

【讨论】:

虽然我不认为列出的所有函数都内置在每个 JS 实现中,但你明白了。另一个区别是不同的原型(这些额外的功能暗示了这一点)。【参考方案3】:

我认为,我对之前的回答过于隐喻和神秘。澄清如下。

Array、Boolean、Date、Function、Number、RegExp、String 的一个实例是一个 Object,但增强了针对每种类型的方法和属性。例如,数组具有预定义的length 属性,而通用对象则没有。

javascript:alert([].length+'\n'+.length)

展示

0 不明确的

从本质上讲,FF Gecko 解释器还区分了数组和通用对象,它们在评估语言结构时存在明显差异。

javascript:
  ra=[  "one",   "two",   "three"]; ra.a=4;
  ob=0:"one", 1:"two", 2:"three"; ob.a=4;
  alert(
    ra            +"\n\n"+
    ob            +"\n\n"+
    ra.toSource() +"\n\n"+
    ra.a          +"\t .toSource() forgot me! \n\n"+
    ra.length     +"\t and my length! \n\n"+
    ob.toSource());
  ps=""; for(i in ra)ps+=i+" "; alert(ps);  /* NB .length is missing! */
  ps=""; for(i in ob)ps+=i+" "; alert(ps);

显示

一二三 [对象对象] [“一二三”] 4 .toSource() 忘记了我! 3和我的长度! (0:"一", 1:"二", 2:"三", a:4)

0 1 2 a0 1 2 a

关于所有对象都是函数的说法:

将任意对象实例用作123()"abc"()[]()()obj() 之类的函数在语法上和语义上都不正确,其中obj 是@987654332 以外的任何类型@,所以任意对象实例不是Function。但是,给定一个对象obj,它的类型为Array, Boolean, Date, ...obj 是如何变成Array, Boolean, Date, ... 的?什么是Array, Boolean, Date, ...

javascript:
    alert([Array, Boolean, Date, Function, 
              Number, Object, RegExp, String] . join('\n\n') );

展示

function Array() 
    [native code]


function Boolean() 
    [native code]


function Date() 
    [native code]


function Function() 
    [native code]


function Number() 
    [native code]


function Object() 
    [native code]


function RegExp() 
    [native code]


function String() 
    [native code]

在任何情况下,对象类型都明确表示为function 定义,因此声明所有对象都是函数! (开玩笑的是,我故意模糊和模糊了对象实例与其类型的区别!不过,这表明“你不能没有另一个”,对象和函数!大写强调类型为反对实例。)

函数范式和对象范式似乎都是 JS 解释器低级内置原语的编程和实现的基础,例如 MathJSONtrue

 javascript:alert([Math, JSON, true.toSource()].join("\n\n"));

展示

[object Math]

[object JSON]

(new Boolean(true))

在开发 Javascript 时,以对象为中心的编程风格(OOP's - Object Oriented Programming style - “'s” 是我自己的双关语!)很流行,解释器同样被命名为 Java,以提供它更大的可信度。函数式编程技术被降级为更抽象和深奥的考试,研究自动机、递归函数、形式语言等理论,因此不受欢迎。然而,这些形式考虑的优势在 Javascript 中明显体现出来,特别是在 FF 的 Gecko 引擎中实现(即.toSource())。


Function 的 Object 定义特别令人满意,因为它被定义为递归关系!使用它自己的定义来定义!

function Function() [native code] 由于函数是一个对象,因此对于function Object() [native code] 也是如此。

大多数其他定义停顿为静态终端值。但是,eval() 是一个特别强大的原语,因此 String 也可以嵌入任意功能。

再次注意,上面使用的白话掩盖了对象类型和实例的区别。

【讨论】:

【参考方案4】:

JavaScript 中的一切都是原始类型之外的对象。

代码

var myArray = Array();

创建 Array 对象的实例,同时

var myObject = 'A': 'Athens', 'B':'Berlin';

创建一个 Object 对象的实例。

试试下面的代码

alert(myArray.constructor)
alert(myObject.constructor)

所以你会看到区别在于对象构造函数的类型。

Array 对象的实例将包含 Array 原型的所有属性和方法。

【讨论】:

【参考方案5】:

您可以将命名属性添加到 javascript 中的几乎任何内容,但这并不意味着您应该这样做。 javascript 中的Array 应用作列表,如果您想要关联数组,请改用Object

请注意,如果您真的想使用带有命名属性的 Array 而不是 Object,则这些属性将无法在 for...of 循环中访问,并且当 JSON 对其进行编码以传递它时,您还可能会得到意想不到的结果大约。请参阅下面的示例,其中所有 非数字 索引都被忽略:

let arr = [];
let obj = ;

arr['name'] = 'John';
obj['name'] = 'John';

console.log(arr);    // will output [name: "John"]
console.log(obj);    // will output name: "John"

JSON.stringify(arr); // will return [] <- not what you expected
JSON.stringify(obj); // will return "name":"John"

【讨论】:

【参考方案6】:

数组与 JavaScript 中其他对象的区别。虽然数组有一个神奇的更新长度属性,但对于数组以外的对象,没有办法实现这样的属性。

var arrName = [];
arrName[5] = "test";
arrName.length; // <- 6

数组用于存储带有序数索引的东西 - 像传统的数组、堆栈或队列一样使用它。对象是一个散列 - 将它用于具有不同键的数据。

【讨论】:

"对于数组以外的对象,没有办法实现这样的属性。" 可以使用 getter、setter 和代理。这是可能,但不是默认行为。 @VLAZ,对不起我的知识湖,如果出现问题,请编辑并更正答案。【参考方案7】:

在 JavaScript 中,数组是特殊类型的对象

typeof new Array(); // returns "object" 
typeof new Object(); // returns "object

使用编号索引的数组和使用命名索引的对象

这样我们就可以将命名属性添加到数组中

const arr = []
arr["A"] = "Hello" //["A":"Hello"]

console.log(arr.length) // 0 

arr.length 返回 0 ,因为具有命名索引的数组更喜欢调用对象

console.log(Object.keys(clothing)); // ["A"]
console.log(Object.keys(clothing).length); //1

【讨论】:

【参考方案8】:

-notation 只是语法糖,让代码更漂亮;-)

JavaScript 有许多类似的构造,比如函数的构造,其中 function() 只是

var Func = new Function("<params>", "<code>");

【讨论】:

函数构造函数 NOT 是函数字面量的同义词。文字是词法范围的,而构造函数是全局的。 是文字对象表示法,[] 是文字数组,我不确定您的回答有什么意义。 另外,声明的函数在执行任何代码之前可用,使用 Function 构造函数的赋值在创建它们的代码执行之前不可用。

以上是关于为啥我可以将命名属性添加到数组中,就好像它是一个对象一样?的主要内容,如果未能解决你的问题,请参考以下文章

为啥内联未命名的命名空间?

如果它是引用类型,为啥我可以清空一个数组? [复制]

将如何绘制到 ds 的子显示,就好像它是一个帧缓冲区一样?

我想对一个文件夹修改名称,但不能修改,为啥?

为啥将项目添加到空白数组中会重复显示项目?

为啥在java中打开流后一定要关闭流??好像关不关没有不同,程序的结果都是一样的。。。