在Javascript中扩展对象

4
我正在尝试通过以下方式扩展Object功能:
Object.prototype.get_type = function() {
    if(this.constructor) {
        var r = /\W*function\s+([\w\$]+)\(/;
        var match = r.exec(this.constructor.toString());
        return match ? match[1].toLowerCase() : undefined;
    }
    else {
        return typeof this;
    }
}

这很棒,但是存在一个问题:

var foo = { 'bar' : 'eggs' };
for(var key in foo) {
    alert(key);
}

将会出现 3 次循环。有没有什么方法可以避免这种情况?


如果你还没有阅读过的话,我强烈推荐你阅读 JavaScript 中的原型继承JavaScript 中的经典继承。当你试图理解 JavaScript 中的面向对象编程时,它们非常有用。请注意:原型继承的文章比经典继承的文章更新,所以如果你发现冲突或意见变化,请以原型继承为准。 - Hank Gay
不要这样做!每当你向一个不属于你的函数原型添加东西时,都会有一个人就这样死了!!! - Pablo Cabrera
6个回答

4
我并不完全反对扩展原生类型和ECMA-262 5th ed。它以一种很好的方式解决了其他答案和链接文章中提到的问题。查看这些幻灯片,可以得到很好的概述。
您可以扩展任何对象并定义属性描述符来控制这些属性的行为。可以使属性不可枚举,这意味着在使用for..in循环访问对象属性时,该属性将不被包括在内。
以下是如何在Object.prototype本身上定义getType方法并使其不可枚举的方法:
Object.defineProperty(Object.prototype, "getType", {
    enumerable: false,
    writable: false,
    configurable: false,
    value: function() {
        return typeof this;
    }
});

// only logs "foo"
for(var name in { "foo": "bar" }) {
    console.log(name); 
}

上面的getType函数大多数情况下是无用的,因为它只返回typeof object,在大多数情况下将只是object,但它只是用于演示。
[].getType();
{}.getType();
(6).getType();
true.getType();

这仅适用于ECMA-262第5版吗?换句话说,在浏览器方面,这有多广泛的支持?谢谢! - nategood
1
是的,它是用ES5实现的。您可以在此处查看不同浏览器的最新实现状态:http://kangax.github.com/es5-compat-table/。 - Anurag

3

出于这个原因,您不应该扩展对象原型:

http://erik.eae.net/archives/2005/06/06/22.13.54/

使用静态方法代替。

如果您没有选择,可以使用“hasOwnProperty”方法:

Object.prototype.foo = function(){ alert('x'); }
var x = { y: 'bar' };

for(var prop in x){
  if(x.hasOwnProperty(prop)){
    console.log(prop);
  }
}

你刚好比我快几秒钟发布了它,哈哈。 - CharlesLeaf
1
你链接的文章认为扩展对象原型是不好的,因为你添加的属性将是可枚举的,并在使用“in”运算符时显示出来。然而,通过Object.defineProperty,您可以设置这些属性,使它们不可枚举。这似乎解决了这个问题,但不能保证扩展对象原型会创建其他问题。 - Wasbazi

3
你可以使用 hasOwnProperty() 方法来检查该属性是否属于 foo 对象:
var foo = { 'bar' : 'eggs' };
for (var key in foo) {
   if (foo.hasOwnProperty(key)) {
      alert(key);
   }
}

如果我写了某种框架呢?那将不会简单易用。 - UnstableFractal

2

有没有什么方法可以避免这种情况?

是的,不要扩展原生类型。

使用包装器来替代:

var wrapper = (function(){

    var wrapper = function(obj) {
        return new Wrapper(obj);
    };

    function Wrapper(o) {
        this.obj = obj;
    }

    Wrapper.prototype = wrapper.prototype;

    return wrapper;

}());

// Define your get_type method:
wrapper.prototype.get_type = function(){
    if(this.obj.constructor) {
        var r = /\W*function\s+([\w\$]+)\(/;
        var match = r.exec(this.obj.constructor.toString());
        return match ? match[1].toLowerCase() : undefined;
    }
    else {
        return typeof this.obj;
    }
};

使用方法:

var obj = { 'bar' : 'eggs' };

alert(wrapper(obj).get_type());

for(var i in obj) { ... works properly }

1

当您循环枚举对象的可枚举属性时,可以使用Object.hasOwnProperty()来确定当前属性是否是“继承”的。

for ( var key in foo )
{
  if ( foo.hasOwnProperty( key ) )
  {
    alert(key);
  }
}

但要让你们知道猴子补丁的危险,特别是在Object上,正如其他人所发表的那样。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接