可鏈式物件設計和連結
Chaining 和 Chainable 是一種用於設計物件行為的設計方法,以便對物件函式的呼叫返回對 self 或其他物件的引用,提供對其他函式呼叫的訪問,允許呼叫語句將多個呼叫連結在一起而無需引用變數持有物件。
可以連結的物件被認為是可連結的。如果呼叫物件可連結,則應確保所有返回的物件/基元的型別正確。你的可連結物件只需要一次不返回正確的引用(很容易忘記新增 return this
),使用你的 API 的人將失去信任並避免連結。可鏈式物件應該是全部或全部(即使是部件也不是可連結物件)。如果只有一些函式,則不應將物件稱為可連結物件。
物件設計為可連結
function Vec(x = 0, y = 0){
this.x = x;
this.y = y;
// the new keyword implicitly implies the return type
// as this and thus is chainable by default.
}
Vec.prototype = {
add : function(vec){
this.x += vec.x;
this.y += vec.y;
return this; // return reference to self to allow chaining of function calls
},
scale : function(val){
this.x *= val;
this.y *= val;
return this; // return reference to self to allow chaining of function calls
},
log :function(val){
console.log(this.x + ' : ' + this.y);
return this;
},
clone : function(){
return new Vec(this.x,this.y);
}
}
連結的例子
var vec = new Vec();
vec.add({x:10,y:10})
.add({x:10,y:10})
.log() // console output "20 : 20"
.add({x:10,y:10})
.scale(1/30)
.log() // console output "1 : 1"
.clone() // returns a new instance of the object
.scale(2) // from which you can continue chaining
.log()
不要在返回型別中建立歧義
並非所有函式呼叫都返回有用的可連結型別,它們也不會始終返回對 self 的引用。這是常識使用命名很重要的地方。在上面的例子中,函式呼叫 .clone()
是明確的。其他示例是 .toString()
意味著返回一個字串。
可鏈式物件中不明確的函式名稱的示例。
// line object represents a line
line.rotate(1)
.vec(); // ambiguous you don't need to be looking up docs while writing.
line.rotate(1)
.asVec() // unambiguous implies the return type is the line as a vec (vector)
.add({x:10,y:10)
// toVec is just as good as long as the programmer can use the naming
// to infer the return type
語法約定
連結時沒有正式的用法語法。慣例是將單個行上的呼叫連結起來,或者在新行上鍊接從引用的物件中縮排一個製表符,並在新行上新增點。分號的使用是可選的,但通過清楚地表示鏈的末端確實有幫助。
vec.scale(2).add({x:2,y:2}).log(); // for short chains
vec.scale(2) // or alternate syntax
.add({x:2,y:2})
.log(); // semicolon makes it clear the chain ends here
// and sometimes though not necessary
vec.scale(2)
.add({x:2,y:2})
.clone() // clone adds a new reference to the chain
.log(); // indenting to signify the new reference
// for chains in chains
vec.scale(2)
.add({x:2,y:2})
.add(vec1.add({x:2,y:2}) // a chain as an argument
.add({x:2,y:2}) // is indented
.scale(2))
.log();
// or sometimes
vec.scale(2)
.add({x:2,y:2})
.add(vec1.add({x:2,y:2}) // a chain as an argument
.add({x:2,y:2}) // is indented
.scale(2)
).log(); // the argument list is closed on the new line
語法錯誤
vec // new line before the first function call
.scale() // can make it unclear what the intention is
.log();
vec. // the dot on the end of the line
scale(2). // is very difficult to see in a mass of code
scale(1/2); // and will likely frustrate as can easily be missed
// when trying to locate bugs
任務的左手邊
分配鏈的結果時,將分配最後一個返回的呼叫或物件引用。
var vec2 = vec.scale(2)
.add(x:1,y:10)
.clone(); // the last returned result is assigned
// vec2 is a clone of vec after the scale and add
在上面的例子中,vec2
被賦予從鏈中最後一次呼叫返回的值。在這種情況下,這將是縮放和新增後的 vec
的副本。
摘要
更改的優點是更清晰,更易於維護的程式碼。有些人更喜歡它,並且在選擇 API 時會產生可連結的要求。還有一個效能優勢,因為它允許你避免必須建立變數來儲存中間結果。最後一句話是可連結物件也可以以傳統方式使用,因此你不會通過使物件可連結來強制連結。