1. 程式人生 > >JavaScript 陣列:對比 slice 與 splice

JavaScript 陣列:對比 slice 與 splice

對於 JavaScript 語言來講,入門者甚至是專家都會經常搞不清 slice 和 splice 這兩個方法。它們雖然名稱相似,但是功能卻完全不同。在使用中,可以通過選擇一個具有強語義表達性的 API 來減少混淆的發生。

陣列的 slice (ECMAScript 5.1 標準 15.4.4.10 節)非常類似於字串的 slice。根據規範,slice 需要兩個引數,_起點_和_終點_。它會返回一個包含了從起點開始,到終點之前之間所有元素的新陣列。理解 slice 的功能並不是太難:

'abc'.slice(1,2)           // "b"
[14, 3, 77].slice(1, 2)    //  [3] 

需要特別注意的是它並不會修改原陣列。下面的程式碼段描述了這個行為,_x_ 的值沒有變,_y_ 則是被擷取的部分。

var x = [14, 3, 77];
var y = x.slice(1, 2);
console.log(x);          // [14, 3, 77]
console.log(y);          // [3] 

雖然 splice15.4.4.12 節)也需要(至少)兩個引數,但它的意義則完全不同。

[14, 3, 77].slice(1, 2)     //  [3]
[14, 3, 77].splice(1, 2)    //  [3, 77] 

除此之外,splice 還會改變原陣列。不要太驚訝,這正是 splice 的本意。

var x = [14, 3, 77]
var y = x.splice(1, 2)
console.log(x)           // [14]
console.log(y)           // [3, 77] 

當你編寫自己的模組時,選擇一個最不容易發生混淆的 API 非常重要。理論上,你的使用者不應該總是通過閱讀文件來判斷他們需要哪一個。那麼我們應該遵循哪種命名規範呢?

我最熟悉的規範(與我之前在 QT 上的經驗有關)是正確地選擇動詞:現在時表示可能的修改行為,過去時則不會修改原物件,而是返回一個新的版本。如果可以的話,這兩種版本都要提供。參考下面的例子:

var p = new Point(100, 75);
p.translate(25, 25);
console.log(p);       // { x: 125, y: 100 }

var q = new Point(200, 100);
var s = q.translated(10, 50);
console.log(q);       // { x: 200, y: 100 }
console.log(s);       // { x: 210, y: 150 } 

注意(在二維笛卡爾座標系中)移動位置的 translate() 和僅建立一個移動過的座標的 translated() 之間的區別。呼叫 translate 會修改點 _p_ 的值。然而,因為 translated() 不修改原物件,物件 _q_ 沒有被修改,而只返回了一個新的拷貝 _s_。

如果這個規範能夠非常一致地部署到你的應用中,那麼上面提到的那種混淆則會被最大化地減低。有一天,你會讓你的使用者高興地喊出我終於明白了