【问题标题】:What are the performance repercussions of adding functions to the String class in JavaScript and Node.js?在 JavaScript 和 Node.js 中向 String 类添加函数会对性能产生什么影响?
【发布时间】:2011-09-23 17:00:00
【问题描述】:

在 JavaScript 中向 String 类添加函数有什么影响?这是一个坏主意吗?例如,

// String functions
String.prototype.startsWith = function(string) {
    return (this.indexOf(string) === 0);
}
String.prototype.empty = function() {
    //console.log($.trim(this.valueOf()));
    if($.trim(this.valueOf()) == '') {
        return true;
    }
    else {
        return false;
    }
}

【问题讨论】:

  • 这就是很多框架所做的......尤其是 mootools,他们鼓励这样做。这也是为什么某些框架不能很好地协同工作的原因,例如 mootools 与 Prototype。
  • 你认为这是个坏主意吗?
  • 不,我认为这是个好主意。这几乎就是 mootools 的哲学……我同意。这就是 JS 的伟大之处。
  • @Crayon - 我特意问@Kirk 作为一个发人深省的评论。碰巧我同意你的看法。
  • @Crayon - 这几乎就是 Javascript 的哲学,以及一般的基于原型的 OOP。 Mootools 很好地利用了它。

标签: javascript jquery performance node.js


【解决方案1】:

就性能而言,效果为零。

但这仍然不是一个好主意,imo。您现在依赖于全局更改的状态。如果其他模块做同样的事情会发生什么?或者更糟的是,稍微不同的事情,但使用相同的名称?哎哟。

最好只定义接受字符串参数的函数,然后使用这些函数。

【讨论】:

    【解决方案2】:

    首先原型比对象上的函数慢。创建函数和查找一样有成本。在原型上查找比在普通对象上查找更昂贵

    查看这个 JSPerf: http://jsperf.com/prototype-methods-vs-object-methods

    设置如下:

    <script>
    Benchmark.prototype.setup = function() {
        function Bob() {
        }
    
        var b = new Bob();
        Bob.prototype.testproto = function() {};
        b.testobj = function() {};
    };
    </script>
    

    话虽如此,向字符串原型添加内容与向任何其他原型添加内容没有什么不同。

    【讨论】:

    • 所以如果我想为所有字符串添加功能,我必须使用 String.protoype 对吗?
    • @Kirk 如果您想执行以下操作:"HI".toLowerCase() 那么是的,您应该将函数添加到 String.prototype。话虽这么说,没有什么能阻止你写像function toLowerCase(str) 这样的东西,然后把你的字符串传递进去。每次调用它都会更快。我不推荐其中之一。它是一种便利/范围权衡。您还可以创建一个对象:var strHelpers = { toLowerCase: myLowerCaseFn }。这也将比原型更快。这是一种方便与轻微性能的权衡。
    • 有些人还建议不要修改您不拥有的原型。这条规则并不坏。我认为它可以保护您的安全;但是,这应该是您与团队共同决定的政策。
    【解决方案3】:

    它没有性能损失。它不直接影响任何String对象,该函数只绑定到String.prototype-object。

    不这样做的唯一原因是因为有人可能在没有检查hasOwnProperty 的情况下使用了for-in 构造,并且意外地迭代了您的新函数。

    如果这不太可能,你应该继续。

    【讨论】:

    • 顺便说一句,您可以通过使用 Object.defineProperty 并为 enumerable 指定 false 来利用 ECMAScript 5。
    【解决方案4】:

    我建议编写自己的原生对象原型扩展库,这比这句话的第一句话可能建议的要简单和快捷得多。 Prototype.js 做了类似的事情,就像我在我维护的各种系统上使用的专有库中一样。此路线的一个优点是库间兼容性,另一个优点是在不需要扩展方法时减少创建新本机对象的开销。像这样的一些库使用单个对象作为扩展方法,我更喜欢使用与特定本机对象相关的对象。

    (function(){
    
    //native object specific factory objects for expansion
    var Arrays = function(o) { this.value = o; },
    ,   Objects = function(o) { this.value = o; },
    ,   Strings = function(o) { this.value = o; },
    ,   Functions = function(o) { this.value = o; },
    ,   Dates = function(o) { this.value = o; },
    ,   Numbers = function(o) { this.value = o; };
    ,   Booleans = function(o) { this.value = o; };
    
    
    //route object to correct expansion pack
    var Shell = function(o) {
      var type = Object.prototype.toString.call(o);
      switch( type ) {
        case '[object Array]' : return new Arrays(o);
        case '[object Boolean]' : return new Booleans(o);
        case '[object Date]'    : return new Dates(o);
        case '[object Function]': return new Functions(o);
        case '[object Number]' : return new Numbers(o);
        case '[object Object]' : return new Objects(o);
        case '[object String]' : return new Strings(o);
        default : throw { name : "Unsupported Object", message: "Unsupported Object type: "+type};
      }
    }
    
    //easy access for prototyping
    Shell.Array = Arrays.prototype;
    Shell.Boolean = Booleans.prototype;
    Shell.String = Strings.prototype;
    Shell.Function = Functions.prototype;
    Shell.Date = Dates.prototype;
    Shell.Number = Numbers.prototype;
    Shell.Object = Objects.prototype;
    
    //global access
    this._nativeWrapper = Shell;
    
    
    })(window);
    

    您想要添加的一些东西可能是用于链接和一次将原型扩展应用于所有工厂对象的方法。您还需要确保,如果您编写了一个扩展器,用于将属性复制到 Shell 的原型或任何工厂对象上,那么您这样做的方式是考虑到 getter 和 setter。

    【讨论】:

      【解决方案5】:

      就性能而言,向 String 原型添加方法与向任何其他对象添加方法没有什么不同。我还进行了一些测试,并且在那里使用自定义方法似乎不会减慢任何本机 String 方法的执行速度——即,将“someOtherFunc”添加到 String.prototype 不会导致“indexOf”运行速度变慢。

      当然,你的 JavaScript 函数会比那些原生版本慢,所以如果你给老版本的浏览器添加一个“trim”函数,那些有原生 trim 函数的浏览器运行速度会比没有的快很多倍。这是不言而喻的。

      正如其他人所指出的,当您向核心 JavaScript 对象的原型添加太多函数时,您可能会遇到冲突问题;随着时间的推移,其他框架可能想要添加相同的函数,甚至可能会出现同名的本地函数。出于这个原因,我只喜欢使用它来为旧版浏览器添加对现代 Web 标准的支持,例如如果 String.prototype.trim 本身不存在,则将“trim”添加到 String.prototype。如果您这样做,请确保您的函数的行为与现代标准相同,这样其他使用它的库就不会被阻塞。

      如果您想添加实际上不属于 ECMAScript 5 等新标准的全新功能,通常最好将它们添加到实用程序对象或使其成为正常功能。这不会让它们跑得更快。它只是让您的代码在多年后更易于维护。

      【讨论】:

        猜你喜欢
        • 2010-11-27
        • 2011-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多