实验:扩展本机对象

时间:2014-09-16 17:31:47

标签: javascript

我正在阅读有关在javascript中扩展本机对象的一些讨论。在当前浏览器中扩展本机对象似乎比以前少得多。当然,我们可以通过以下方法更好地控制对象的扩展:

 Object.defineProperty(<some parameters...>)

然而,存在的一个巨大风险是不同代码/库之间的冲突,导致意外行为。

命名冲突(和全局崩溃)的风险可以通过命名自己的函数来减少。所以我想如果扩展原生对象,为什么我们不这样做呢?当然执行上下文是一个问题,但我们可以使用bind()来解决这个问题,使我们的扩展功能中可以使用该对象的本机函数。所以我创建了以下内容来扩展Element对象:

// Define the extend function
Object.defineProperty(Element.prototype, 'extend', { value: function(namespace, object) {
    var cache = {};
    // Create  the namespace and make sure on invocation the Element is bound
    Object.defineProperty(Element.prototype, namespace, { value: function() {
        var objectCheck = typeof cache[Element.prototype.constructor.name] !== 'undefined';
        if(objectCheck && typeof cache[Element.prototype.constructor.name][namespace] !== 'undefined'){
            console.log('cache used');
            return cache[Element.prototype.constructor.name][namespace];
        } else {
            var extended = Element.prototype[namespace].extended;
            for (var e in extended) {
                extended[e] = extended[e].bind(this);
            }

            if(!objectCheck)
                cache[Element.prototype.constructor.name] = {};
            cache[Element.prototype.constructor.name][namespace] = extended;
        }

        return extended;
    }});

    this[namespace].extended = object;
    return this;
}});

// Extend the Element prototype with an attr function in 
// the namespace 'namespace' or ofcourse whatever function
Element.prototype.extend('namespace',{
    attr: function(name, value) {
        if(arguments.length === 1) {
            return this.getAttribute(name);
        } else {
            this.setAttribute(name, value);
            return this;
        }
    }
});

当在实际元素上查看它时看起来很好,命名空间就在那里,在里面我们发现我们的&#39; attr&#39;功能。我们可以像这样调用它:

document.querySelector('.a-css-class').namespace().attr('class')
// returns 'a-css-class'

可以进一步重构代码以动态扩展各种对象。但是,我很好奇,这对性能意味着什么,这个实验有意义吗?主要问题是,这比直接延伸更好吗?

编辑(基于Bergi关于表现的评论):

可以在外部函数中缓存创建的函数。让我们看看我是否可以提出一个实现。

编辑2:

添加了一个简单的缓存函数,以确保在每次调用时都不会创建所有命名空间方法。

编辑3:

更新的代码。为了使扩展本机对象更安全,生成了以下代码。它可能会阻止命名冲突。谢谢@Bergi:

/**
 * Extend a (NATIVE) object with a namespace. See below for an example
 */
Object.defineProperty(Object.prototype, 'extend', { value: function(namespace, object) {
    function Wrapper(that) {
        this.that = that;
    }
    Wrapper.prototype = object;
    Object.defineProperty(Object.prototype, namespace, { value: function() {
        return new Wrapper(this);
    }});
}});

// This example uses the Element object, but it could be any object
Element.prototype.extend('namespace',{
    attr: function(name, value) {
        if(arguments.length === 1) {
            return this.that.getAttribute(name);
        } else {
            this.that.setAttribute(name, value);
            return this;
        }
    }
});

1 个答案:

答案 0 :(得分:0)

  

然而,我很好奇,这对性能意味着什么

您当前的代码意味着无论何时调用namespace(),您都会创建一个新对象和许多绑定函数,这将大大影响大型命名空间的性能。

我建议让你的namespace方法返回一个带有element: this属性的包装器对象,该属性继承了可以在其上调用的所有方法。您使用this.element.getAttribute代替this.getAttribute

Element.prototype.extend = function(namespace, object) {
    function Wrapper(el) {
        this.element = el;
    }
    Wrapper.prototype = object;

    Element.prototype[namespace] = function() {
       return new Wrapper(this);
    };
};
  

主要问题,这比直接延伸更好吗?

不多。您仍然在extend上定义namespaceElement.prototype属性,对于这两个属性,所有反对扩展DOM的参数仍然有效。您可能会降低与更常见名称(attr)发生冲突的风险,但它并不比仅定义namespace_attr方法更好。