【问题标题】:adding custom functions into Array.prototype将自定义函数添加到 Array.prototype
【发布时间】:2010-10-31 04:57:14
【问题描述】:

我正在开发一个支持 AJAX 的 asp.net 应用程序。 我刚刚向 Array.prototype 添加了一些方法,例如

Array.prototype.doSomething = function(){
   ...
}

这个解决方案对我有用,可以以“漂亮”的方式重用代码。

但是当我在整个页面上测试它时,我遇到了问题.. 我们有一些自定义的 ajax 扩展器,但它们开始表现得出人意料:一些控件在其内容或值周围显示“未定义”。

这可能是什么原因?我是否遗漏了一些关于修改标准对象原型的内容?

注意:我很确定当我修改 Array.prototype 的原型时错误就开始了。它应该只与 IE 兼容。

【问题讨论】:

    标签: javascript arrays internet-explorer


    【解决方案1】:

    修改内置对象原型通常是个坏主意,因为它总是有可能与其他供应商的代码或加载在同一页面上的库发生冲突。

    对于 Array 对象原型,这是一个特别糟糕的主意,因为它可能会干扰任何迭代任何数组成员的代码,例如 for .. in

    用一个例子来说明(借自here):

    Array.prototype.foo = 1;
    
    // somewhere deep in other javascript code...
    var a = [1,2,3,4,5];
    for (x in a){
        // Now foo is a part of EVERY array and 
        // will show up here as a value of 'x'
    }
    

    不幸的是,这样做的可疑代码的存在使得有必要避免使用普通的for..in 进行数组迭代,至少如果你想要最大的可移植性,只是为了防止其他一些讨厌的代码修改了数组原型。所以你真的需要两者都做:你应该避免使用普通的for..in,以防某些 n00b 修改了 Array 原型,并且你应该避免修改 Array 原型,这样你就不会弄乱任何使用普通 for..in 进行迭代的代码在数组上。

    您最好创建自己类型的对象构造函数并完成 doSomething 函数,而不是扩展内置数组。

    Object.defineProperty 呢?

    现在存在 Object.defineProperty 作为扩展对象原型的一般方法,而无需枚举新属性,尽管这仍然不能证明扩展 内置 类型是合理的,因为即使在 @987654329 之外@ 仍有可能与其他脚本发生冲突。考虑使用两个 Javascript 框架的人,它们都尝试以类似的方式扩展 Array 并选择相同的方法名称。或者,考虑有人分叉您的代码,然后将原始版本和分叉版本放在同一页面上。对 Array 对象的自定义增强功能是否仍然有效?

    这就是 Javascript 的现实,也是为什么你应该避免修改内置类型的原型,即使是 Object.defineProperty。使用自己的构造函数定义自己的类型。

    【讨论】:

    • 我相信“for (x in y)”构造是用于迭代对象的成员。对于数组的基于索引的迭代,我认为它不合适。但是,您关于干扰页面上其他代码的观点是有效的 - 特别是如果 3rd 方库以这种方式使用 for-in。
    • 是的,反之亦然——您应该避免使用 for..in 以防某些 n00b 修改了 Array 原型,并且您应该避免修改 Array 原型以防某些 n00b 已使用 for..in在一个数组上。 ;)
    • 现在的正确答案是使用Object.defineProperty(Array.prototype, 'method', ...),这将使新方法不可枚举
    • 向内置原型添加方法时所面临的风险是未来版本的 JavaScript 将实现同名的方法,然后您的代码将破坏任何依赖于内置原型的脚本-in 行为,否则您的脚本会因为您的 API 可能与内置 API 不兼容而中断。
    • 坦率地说,hasOwnProperty 是一个不幸的解决方法,可以解决有人做了一些邪恶的事情,比如修改 Array.prototype,并且在与第三方代码共存时是一个好主意。然而,这种变通方法的存在并不能证明这种做法是正当的(或使它不那么邪恶),而且除了对for..in 的影响之外,修改内置对象原型还有其他副作用。
    【解决方案2】:

    使用递归

    function forEachWithBreak(someArray, fn){
       let breakFlag = false
       function breakFn(){
           breakFlag = true
       }
       function loop(indexIntoSomeArray){
    
           if(!breakFlag && indexIntoSomeArray<someArray.length){
               fn(someArray[indexIntoSomeArray],breakFn)
               loop(indexIntoSomeArray+1)   
           }
       }
       loop(0)
    }
    

    测试 1 ... break 未被调用

    forEachWithBreak(["a","b","c","d","e","f","g"], function(element, breakFn){
        console.log(element)
    })
    

    生产 一种 b C d e F g

    测试 2 ... break 在元素 c 之后调用

    forEachWithBreak(["a","b","c","d","e","f","g"], function(element, breakFn){
        console.log(element)
        if(element =="c"){breakFn()}
    })
    

    生产 一种 b c

    【讨论】:

      【解决方案3】:

      虽然重写原型上的函数可能会与代码的其他位发生冲突仍然存在风险,但如果您想使用现代版本的 JavaScript 执行此操作,您可以使用 Object.defineProperty 方法,关闭可枚举位,例如

      // functional sort
      Object.defineProperty(Array.prototype, 'sortf', {
          value: function(compare) { return [].concat(this).sort(compare); }
      });
      

      【讨论】:

      • enumerable: false 不需要声明,falseenumerable 的默认值。
      • 非常有用。 api中缺少一些基本/常用的任务方法。
      • 应该和Array.prototype.sortf = function(compare) { return [].concat(this).sort(compare); }一样
      【解决方案4】:

      请注意!也许你做到了:fiddle demo

      让我们说一个数组和一个返回第一个元素的方法 foo:

      var myArray = ["apple","ball","cat"];
      
      foo(myArray) // <- 'apple'
      
      function foo(array){
          return array[0]
      }
      

      上面没问题,因为在解释期间功能被提升到顶部。

      但是,这不起作用:(因为原型没有定义)

      myArray.foo() // <- 'undefined function foo'
      
      Array.prototype.foo = function(){
          return this[0]
      }
      

      为此,只需在顶部定义原型:

      Array.prototype.foo = function(){
          return this[0]
      }
      
      myArray.foo() // <- 'apple'
      

      是的!你可以覆盖原型!!!这是允许的。您甚至可以为数组定义自己的 add 方法。

      【讨论】:

        【解决方案5】:

        可以说,您增强了泛型类型。您可能已经覆盖了其他一些库的功能,这就是它停止工作的原因。

        假设您使用的某个库使用函数 Array.remove() 扩展了 Array。加载 lib 后,您还将 remove() 添加到 Array 的原型中,但具有您自己的功能。当 lib 调用您的函数时,它可能会按预期以不同的方式工作并中断它的执行......这就是这里发生的事情。

        【讨论】:

          【解决方案6】:

          一般来说,弄乱核心 javascript 对象是个坏主意。您永远不知道任何第三方库可能期望什么,并且更改 javascript 中的核心对象会改变它们。

          如果你使用 Prototype,它尤其糟糕,因为原型也会与全局范围混淆,而且很难判断你是否会发生冲突。实际上修改任何语言的核心部分通常是一个坏主意,即使在 javascript 中也是如此。

          (lisp 可能是个小例外)

          【讨论】:

          • 这没有回答问题。
          猜你喜欢
          • 2021-07-23
          • 1970-01-01
          • 2023-03-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-01-12
          • 2021-04-09
          相关资源
          最近更新 更多