如何正确扩展Object.prototype?

时间:2015-02-03 17:01:34

标签: javascript prototype-programming

我正在编写一个提供函数tablify(anything);的JavaScript库,它可以将任何数组或对象表示为HTML表。

现在我正在尝试扩展Array和Object原型,以便像这样使用它:

var arr = ["a", "b", "c"];
var obj = {"A": "a", "B": "b", "C": "c"};
arr.tablify();
obj.tablify();

这是我的方法:

Array.prototype.tablify = function() {
    return tablify(this);    
}
Object.defineProperty(Object.prototype, 'tablify', {
    value: function () {
        return tablify(this);
    },
    writable:     true,
    configurable: true,
    enumerable:   false
});

问题是,JavaScript中的所有内容都是一个对象,因此不仅可以转换{a: 1, b: 2}之类的文字,还可以转换其他内容。

这不是什么大问题,因为我的tablify()也可以处理原始类型,但是当我扩展Object.prototype时,typeof anything总是返回"object"而我可以'区分不同的类型:

function tablify(object) {  
    if (object instanceof Array) {  
        return ArrayToTable(object);
    } 
    //This is always true if I extend Object.prototype: 
    if (typeof object === "object") {   //only for "normal" objects like "{a: 1, b: 2, c: [],...}"
        return ObjectToTable(object);
    }
    return PrimitiveToTable(object);    //strings, numbers, functions, ... 
}
  • 为什么typeof始终返回"object"
  • JS-API提供这种功能是一件好事(使用“.tablify();”扩展数组/对象?)
  • 如何区分“普通”对象,数字,功能......?
  • 是否可以只扩展“普通”对象? (禁止(42).tablify();"string".tablify(); ...)
  • 这些“普通”物品的名称是什么?我该怎么称呼他们?

2 个答案:

答案 0 :(得分:2)

  

为什么typeof始终返回"object"

因为在草率模式下,this context总是应该是一个对象。当您调用函数或传递undefined或null时,您将获得全局对象,当您调用方法时,上下文将被转换为对象。

tablify方法使用strict mode,它会起作用!

  

JS-API提供这种功能(使用.tablify();扩展数组/对象)是一件好事吗?

Yes. No.。当您想要共享脚本时,许多人会皱眉使用您的脚本。有关详细讨论,请参阅Why is extending native objects a bad practice?

至少你是properly used non-enumerable properties - 但我建议在Array.prototype使用它们,太多人滥用for in枚举。

  

如何区分"普通"对象,数字,功能......?

typeof似乎很好。

  

是否可以延长"正常"对象? (禁止(42).tablify();,"字符串" .tablify(); ...)

不是真的。所有原始包装器都继承自Object.prototype

  

这些人的名字是什么"正常"对象?我该怎么称呼他们?

Just" objects"。或者"普通物体"如果你愿意(将它们与数组,内置函数,主机对象区分开来)。

答案 1 :(得分:1)

问题是,一旦你在原型上进入新添加的方法,原始的原始值就已经被转换了。例如:

"foo".tablify();

在该调用中,函数被调用之前,字符串基元被转换为String实例。换句话说,它的行为就像你写的那样:

new String("foo").tablify();

我想您可以使用Object.prototype.toString函数来区分:

function  tablify(obj) {
    if (typeof obj === "object") {
      var sig = {}.toString.call(obj);
      if (/ (String|Number|Boolean)\]$/.test(sig)) {
        // treat as primitive
      }
      else {
        // object
      }
    }
    // ...
}

您将无法判断是否隐式创建了盒装基元,但这可能对您的代码无论如何都无关紧要。