看到 一个论坛帖子 问到:(类似下面的代码)
var arr = [1, 2, 3]; arr.a = 'a'; arr.b = 'b';
为什么可以这样写?arr 自身到底是什么?
原因其实很简单,因为 js 里一切都是对象,array 也是 object,所以它就能做所有 object 能做的事情,比如,给它加个属性 a。
如果 Array 能那么搞,Boolean,Date, Function, Number,RegExp,String 也可以就不足为奇了。
function toStr(obj) {
return Object.prototype.toString.call(obj);
}
function addAttr(arr) {
for(var i = arr.length-1; i >= 0; i--) {
arr[i].attr = toStr(arr[i]);
}
return arr;
}
function checkAttr(arr) {
for(var i = 0; i < arr.length; i++) {
console.log(typeof arr[i], arr[i].attr);
}
}
var arr = [
new Boolean(false),
new Date(),
new Function(),
new RegExp('\\w+'),
new Number(1.0),
new String('str')];
checkAttr(addAttr(arr));
/** output >>
object [object Boolean]
object [object Date]
function [object Function]
object [object RegExp]
object [object Number]
object [object String]
*/
细心的人会发现上面都是使用 new 构造的对象,如果换成是字面方式 (literal) 定义的原生类型 (primitive) 又会是如何呢?
var arr = [false, 1.0, 'str']; checkAttr(addAttr(arr)); /** output >> boolean undefined number undefined string undefined */
在 js 里一切都是对象,但原生类型对象 (boolean, number, string) 与非原生类型对象还是有区别的。前者可能称之为 原生数据 (primitive data) 会更加适合。而在需要的时候(访问属性,调用方法),js 会自动将原生数据封装成对象然后再进行处理 [参考]。而原生数据的存在,是因为性能的原因 [参考]。所以 js 里一切都是对象是对使用者而言。
虽然,我们可以给数组对象添加属性,但并不建议那么做,原因是 Array 并不预期被当作 key/value 形式的对象来使用。情况放到 Boolean, Number, String 等对象上是一样的。
除非,不给定这个预期,或者改变这个预期。
比如,使用 jQuery,在执行 $(selector) 返回的是一个 jQuery 对象,这个对象可以执行 jQuery.fn 上面的所有方法,并且可以像访问一个数组那样访问里面所包含的数据节点。下面是 jQuery 中 /src/core.js 的一些源代码片断:
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function( selector, context, rootjQuery ) {
// ...
return jQuery.merge( this, selector );
},
// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push: push,
sort: [].sort,
splice: [].splice
};
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
通过查询过滤之后,人们会预期得到的是一个节点数组,但 jQuery 并没有简单的返回那个数组(如果真是那样,后面就没戏了),而是将查询的结果合并到一个 jQuery 类型的对象上然后返回。也就是上面代码中 constructor 和 init 的部分。后面添加到 jQuery.fn 的 push, sort, splice 都是 Array prototype 上的方法,目的就是使这个对象可以像个 Array 一样操作,虽然注释里说的是仅供内部使用。
在 Array object 上添加属性数据算不符合预期。但如果反过来,将一个 object 模拟成 Array,这样就没有了原来的“预期”的障碍,通过给出新的预期,来表达程序的设计目的。
我在各种场合,无数次听见人们盛赞 jQuery 是如何好用,里面也是有几分 JavaScript Object 的功劳的,但更重要的是,设计者有扎实的基本功,既尊重传统,又敢于突破。
Object 是如此的简单而灵活。它可以包含属性与方法,当成对象来使用;可以以 maps 或者 key/value pairs 的形式作为数据使用。它是否还有什么未知领域等待人们去发掘呢?