簡體   English   中英

將自定義函數添加到 Array.prototype

[英]adding custom functions into Array.prototype

我正在開發一個支持 AJAX 的 asp.net 應用程序。 我剛剛向 Array.prototype 添加了一些方法,例如

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

該解決方案對我有用,可以以“漂亮”的方式重用代碼。

但是當我在整個頁面上測試它時,我遇到了問題。我們有一些自定義的 ajax 擴展器,它們開始表現得出乎意料:一些控件在其內容或值周圍顯示“未定義”。

這可能是什么原因? 我是否遺漏了一些關於修改標准對象原型的內容?

注意:我很確定當我修改 Array.prototype 的原型時錯誤就開始了。 它應該只與 IE 兼容。

雖然與其他代碼位沖突的可能性仍然存在風險,但如果您想使用現代版本的 JavaScript 執行此操作,您可以使用Object.defineProperty方法,例如

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

修改內置對象原型通常是一個壞主意,因為它總是有可能與其他供應商的代碼或加載在同一頁面上的庫發生沖突。

在 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進行數組迭代,至少如果你想要最大的可移植性,只是為了防止其他一些討厭的代碼修改了數組原型的情況. 所以你真的需要兩者都做:你應該避免plain for..in ,以防一些n00b修改了Array原型,你應該避免修改Array原型,這樣你就不會弄亂任何使用plain for..in的代碼迭代數組。

最好使用 doSomething 函數創建自己類型的對象構造函數,而不是擴展內置數組。

Object.defineProperty呢?

現在存在Object.defineProperty作為擴展對象原型的一般方法,而無需枚舉新屬性,盡管這仍然不能證明擴展內置類型是合理的,因為即使除了for..in之外,仍然存在與其他腳本。 考慮使用兩個 Javascript 框架的人,它們都嘗試以類似的方式擴展 Array 並選擇相同的方法名稱。 或者,考慮有人分叉您的代碼,然后將原始版本和分叉版本放在同一頁面上。 對 Array 對象的自定義增強功能仍然有效嗎?

這就是 Javascript 的現實,也是為什么你應該避免修改內置類型的原型,即使使用Object.defineProperty 使用自己的構造函數定義自己的類型。

有一個警告! 也許你做到了:小提琴演示

讓我們說一個返回第一個元素的數組和方法 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 ... break 未被調用

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

產生 abcdefg

測試 2 ...在元素 c 之后調用 break

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

生產 abc

有2個問題(如上所述)

  1. 它是可枚舉的(即會在for .. in看到)
  2. 潛在沖突(js、自己、第三方等)

為了解決這兩個問題,我們將:

  1. 使用Object.defineProperty
  2. 為我們的方法提供一個唯一的 id
const arrayMethods = {
    doSomething: "uuid() - a real function"
}

Object.defineProperty(Array.prototype, arrayMethods.doSomething, {
    value() {
        // Your code, log as an example
        this.forEach(v => console.log(v))
    }
})

const arr = [1, 2, 3]
arr[arrayMethods.doSomething]() // 1, 2, 3

語法有點奇怪,但如果你想鏈接方法很好(只是不要忘記return this ):

arr
    .map(x=>x+1)
    [arrayMethods.log]()
    .map(x=>x+1)
    [arrayMethods.log]()

一般來說,弄亂核心 javascript 對象是一個壞主意。 您永遠不知道任何第三方庫可能期望什么,並且更改 javascript 中的核心對象會改變它們的一切。

如果您使用 Prototype 則尤其糟糕,因為原型也會與全局范圍混淆,並且很難判斷您是否會發生沖突。 實際上,即使在 javascript 中,修改任何語言的核心部分通常也是一個壞主意。

(lisp 可能是那里的一個小例外)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM