Szukaj…
Łączenie metod
Łańcuch metod to strategia programowania, która upraszcza kod i upiększa go. Tworzenie łańcuchów metod odbywa się poprzez upewnienie się, że każda metoda na obiekcie zwraca cały obiekt, zamiast zwracać pojedynczy element tego obiektu. Na przykład:
function Door() {
this.height = '';
this.width = '';
this.status = 'closed';
}
Door.prototype.open = function() {
this.status = 'opened';
return this;
}
Door.prototype.close = function() {
this.status = 'closed';
return this;
}
Door.prototype.setParams = function(width,height) {
this.width = width;
this.height = height;
return this;
}
Door.prototype.doorStatus = function() {
console.log('The',this.width,'x',this.height,'Door is',this.status);
return this;
}
var smallDoor = new Door();
smallDoor.setParams(20,100).open().doorStatus().close().doorStatus();
Zauważ, że każda metoda w Door.prototype
zwraca this
, co odnosi się do całej instancji tego obiektu Door
.
Projektowanie i łańcuchowanie obiektów łańcuchowych
Łańcuchowe i łańcuchowe to metodologia projektowania stosowana do projektowania zachowań obiektowych, dzięki czemu wywołania funkcji obiektowych zwracają odwołania do siebie lub innego obiektu, zapewniając dostęp do dodatkowych wywołań funkcji, umożliwiając łączenie wielu wywołań instrukcji wywołującej bez potrzeby odwoływania się do trzymania zmiennej obiekt / y.
Obiekty, które można powiązać, są łańcuchowe. Jeśli nazywasz obiekt łańcuchem, powinieneś upewnić się, że wszystkie zwracane obiekty / operacje podstawowe są poprawnego typu. Wystarczy tylko jeden raz, aby obiekt łańcuchowy nie zwrócił poprawnego odwołania (łatwo zapomnieć o dodaniu return this
), a osoba korzystająca z interfejsu API straci zaufanie i uniknie tworzenia łańcuchów. Przedmioty łańcuchowe powinny być wszystkim lub niczym (nie łańcuchem, nawet jeśli części są). Obiekt nie powinien być nazywany łańcuchem, jeśli są tylko niektóre jego funkcje.
Obiekt zaprojektowany jako łańcuchowy
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);
}
}
Przykładowy łańcuch
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()
Nie twórz dwuznaczności w typie zwrotu
Nie wszystkie wywołania funkcji zwracają przydatny typ łańcuchowy, ani nie zawsze zwracają odniesienie do siebie. W tym przypadku ważne jest stosowanie nazewnictwa opartego na rozsądku. W powyższym przykładzie wywołanie funkcji .clone()
jest jednoznaczne. Inne przykłady to .toString()
oznacza, że zwracany jest ciąg znaków.
Przykład niejednoznacznej nazwy funkcji w obiekcie łańcuchowym.
// 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
Konwencja składniowa
Podczas tworzenia łańcuchów nie ma formalnej składni użycia. Konwencja polega na łączeniu połączeń na jednej linii, jeśli jest krótka, lub na nowej linii, wcięcie jednej zakładki od obiektu odniesienia za pomocą kropki na nowej linii. Użycie średnika jest opcjonalne, ale pomaga w wyraźnym oznaczeniu końca łańcucha.
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
Zła składnia
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
Lewa strona zadania
Po przypisaniu wyników łańcucha przypisywane jest ostatnie wywołanie zwrotne lub odwołanie do obiektu.
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
W powyższym przykładzie vec2
jest przypisana wartość zwrócona z ostatniego wywołania w łańcuchu. W tym przypadku byłaby to kopia vec
po skalowaniu i dodaniu.
streszczenie
Zaletą zmiany jest wyraźniejszy, łatwiejszy w utrzymaniu kod. Niektóre osoby wolą to i przy wyborze interfejsu API będą wymagały tworzenia łańcuchów. Istnieje również korzyść z wydajności, ponieważ pozwala uniknąć konieczności tworzenia zmiennych w celu przechowywania wyników pośrednich. Ostatnim słowem jest to, że obiekty łańcuchowe mogą być również używane w konwencjonalny sposób, więc nie wymuszaj tworzenia łańcuchów, czyniąc obiekt łańcuchem.