将自定义函数添加到 Array.prototype

IT技术 javascript arrays internet-explorer
2021-01-15 12:52:57

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

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

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

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

这可能是什么原因?我是否缺少有关修改标准对象原型的内容?

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

6个回答

尽管与其他代码位发生冲突的可能性仍然存在风险,但如果您想使用现代版本的 JavaScript 执行此操作,则可以使用 Object.defineProperty 方法,关闭可枚举位,例如

// functional sort
Object.defineProperty(Array.prototype, 'sortf', {
    value: function(compare) { return [].concat(this).sort(compare); }
});
不需要enumerable: falsefalse的默认值那样声明enumerable
2021-03-13 12:52:57
很有用。api 中缺少一些基本/常见的任务方法。
2021-03-17 12:52:57
它应该与 Array.prototype.sortf = function(compare) { return [].concat(this).sort(compare); }
2021-03-19 12:52:57

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

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

用一个例子来说明(从这里借来的):

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的数组迭代,至少如果您想要最大的可移植性,只是为了防止其他一些讨厌的代码修改了 Array 原型的情况。所以你真的需要两者都做:你应该避免plain for..in,以防一些n00b修改了Array原型,你应该避免修改Array原型,这样你就不会弄乱任何使用plainfor..in来迭代数组的代码

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

怎么样Object.defineProperty

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

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

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

有一个警告!也许你这样做了:小提琴演示

让我们说一个数组和一个返回第一个元素的方法 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为数组定义自己的方法。

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

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

使用递归

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 ... 未调用中断

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

产生 a 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()}
})

产生 a b c