实验:扩展本机对象

Experiment: extending a native object

本文关键字:对象 本机 扩展 实验      更新时间:2023-09-26

我正在阅读一些关于在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;
        }
    }
});

在实际元素上检查它时,一切看起来都不错,命名空间就在那里,在其中我们找到了我们的"attr"函数。我们可以像这样调用它:

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

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

编辑(基于Bergi对性能的评论(:

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

编辑2:

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

编辑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;
        }
    }
});

但是我很好奇,这对性能意味着什么

您当前的代码意味着,无论何时调用 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);
    };
};

主要问题是,这比直接扩展更好吗?

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