【问题标题】:How to extend Obect.prototype correctly?如何正确扩展 Object.prototype?
【发布时间】:2015-02-03 17:01:33
【问题描述】:

我正在编写一个 JavaScript 库,它提供函数 tablify(anything);,它能够将任何数组或对象表示为 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(); ...)
  • 这些“普通”对象的名称是什么?我应该怎么称呼他们?

【问题讨论】:

  • 从对象创建 HTML 的函数听起来不像您通常想要添加到本机 Objects 原型中的东西?
  • “JavaScript 中的一切都是对象”是不正确的。字符串、数字和布尔值不是对象。在被要求这样做的情况下,它们确实表现得像对象,因为语言隐式地将它们转换为相应的对象类型。
  • @Pointy 当我使用我的代码时,42.tablify();"string".tablify();func.tablify(); 也在工作,尽管我只扩展了 Object.prototype。 typeof 也返回 "object"...
  • if (Object.prototype.toString.call(object) === "[object Object]") jsfiddle.net/15snrunw/1
  • re: 编辑:使用42..tablify()(第一个点被解析为小数点)或(42).tablify()

标签: javascript prototype-programming


【解决方案1】:

为什么typeof 总是返回"object"

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

strict mode 用于您的tablify 方法,它会起作用!

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();, "string".tablify(); ...)

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

这些“普通”对象的名称是什么?我应该怎么称呼他们?

只是“对象”。或“普通对象”(将它们与数组、内置对象、宿主对象区分开来)。

【讨论】:

  • 谢谢,这真的很丰富。我使用严格模式让它工作,但我将删除该功能,因为优势微乎其微。
【解决方案2】:

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

"foo".tablify();

在那次调用中,函数被调用之前,字符串原语被转换成一个字符串实例。换句话说,它的行为就像你写了:

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
      }
    }
    // ...
}

您将无法判断装箱的原语是否是隐式创建的,但这对于您的代码的作用可能并不重要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多