Methods

Methods are simply named functions inside a class. The following is a method that renders the current view by assigning the el instance variable’s innerHTML to the result of a template.

classes/methods.dart
 
class​ ComicsView {
 
// ...
 
render() {
 
el.innerHtml = template(collection);
 
}
 
}

Inside the class, methods may be invoked by calling the method directly—prepending the method with this. is not required like it is in JavaScript (though it would still work). In the previous example, template is an instance method that is invoked with the view’s collection property.

In Dart, it is generally considered good practice to prepend the return type of a method or prepend void if the method does not return anything.

classes/method_with_type.dart
 
class​ ComicsView {
 
void​ render() {
 
el.innerHtml = template(collection);
 
}
 
}

In addition to “normal” methods, Dart supports specialized setter and getter methods as well as operator methods.

Getters and Setters

Getter methods are those that take no arguments and are declared with the keyword get after the type and before the name of the method. They also lack the parentheses of normal methods.

classes/getter_method.dart
 
class​ ComicsCollection {
 
String ​get​ url => ​'/comics'​;
 
}

Getters get their name from how they are used, which greatly resembles getting an object’s property in other languages.

 
// No parens required!
 
comics_collection.url; ​// => '/comics'

Dart also supports setters, which are functions that assign new values. These are declared with the set keyword and take the new value as a parameter.

classes/setter_method.dart
 
class​ ComicsCollection {
 
String _url;
 
void​ ​set​ url(new_url) {
 
_url = new_url;
 
}
 
String ​get​ url => _url;
 
}

Setters are of interest primarily because they override the assignment operator. To set the new URL in the Comics class, we would not pass it as the argument to the url method. Rather, we assign it.

 
comics_collection.url = shiny_new_url;

Dart recognizes assignment as a special setter operation and looks in the class definition for the set keyword to decide how to proceed.

Setters and getters beat the pants off of languages that force us to choose from any number of poor conventions to indicate intent.

Operators

In fact, there are a number of operator-like methods that can be described by a Dart class. The remaining operators are declared with the same keyword: operator.

We saw operator in the ComicModel class as a way to access attributes of the model.

classes/operators.dart
 
class​ ComicModel {
 
Map attributes;
 
operator​ [](attr) => attributes[attr];
 
}

With that, we can then look up the title of a Comic object with the following:

 
comic[​'title'​] ​// => "V for Vendetta"

The square bracket lookup is by far the most common operator in Dart, but a myriad are supported: ==, <, >, <=, >=, -, +, /, ~/, *, %, |, ^, &, <<, >>, []=, [], and ~. Dart’s support for operators make it ideal for building math or logic-based libraries or even more fanciful libraries that want to use these operators in their own, unique ways.

The Special, Non-Operator, Non-Getter/Setter, call Method

There is also a special call method that lets us describe what should happen when an object is applied. For instance, if we want calling a model (for example, comic()) to be an alias for saving it, we could declare it as follows:

classes/call_method.dart
 
class​ ComicModel {
 
call() {
 
save();
 
}
 
save() {
 
// do save things here...
 
}
 
}

Then, saving could be accomplished with the following:

 
comic();

Between operator methods and the call method, Dart already supports a very expressive syntax. And it still has more surprises in store for us.

Metaprogramming with noSuchMethod

In its early days, Dart provides for limited metaprogramming facilities. One vehicle for dynamically changing behavior at runtime is the special method noSuchMethod. When Dart attempts to locate a method being called, it first looks in the current class for the explicit definition. If the method is not located, the superclass and all ancestor classes are checked. Failing that, Dart then invokes noSuchMethod—if it has been declared—in the current class.

When invoked, noSuchMethod is supplied with an object that mirrors the method call—an “invocation mirror.” This invocation mirror object includes the member (that is, the method) invoked, the list of positional arguments, the list of named arguments, and more.

classes/no_such_method.dart
 
class​ ComicModel {
 
// ...
 
noSuchMethod(args) {
 
if​ (args.memberName != ​new​ Symbol(​'save'​)) {
 
throw​ ​new​ NoSuchMethodError(
 
this​,
 
args.memberName,
 
args.positionalArguments,
 
args.namedArguments
 
);
 
}
 
// Do save operations here...
 
}
 
}

We will look into noSuchMethod in more detail in Chapter 11, Project: Varying Behavior.

Method Cascades

We already touched on method cascades in Chapter 4, Manipulating the DOM, but they are worth mentioning again. Cascades are introduced with the double dot operator instead of the usual single dot operator for ordinary methods. Thanks to cascades, we are not stuck adding elements to a list one at a time.

classes/cascades.dart
 
var​ list1 = [];
 
list1.add(17);
 
list1.add(42);

Instead, we can add them in a single statement.

 
var​ list2 = [];
 
list2
 
..add(17)
 
..add(42);

The cascade syntax is meant to evoke the idea of working in Unix directories, where one dot represents the current directory and two dots points to the parent directory. In cascades, two dots refer to the original object receiving the method, not the result of the previous method. This makes for nice, jQuery-like chained code, but for all methods, not just those that operated on jQuery wrapped sets.

A surprising feature of cascades is the support for cascading setters. Setters are, after all, methods in Dart. This allows us, for example, to rewrite a laundry list of style settings.

 
el.style.opacity = ​'0'​;
 
el.style.position = ​'absolute'​;
 
el.style.top = ​'80px'​;
 
el.style.left = ​'0px'​;
 
el.style.zIndex = ​'1001'​;
 
el.style.transition = ​'opacity 1s ease-in-out'​;
 
el.style.opacity = ​'1'​;

That might not look too bad, especially if you are used to performing CSS updates in JavaScript. Compare that to the same functionality, but written in cascades:

 
el.style
 
..opacity = ​'0'
 
..position = ​'absolute'
 
..top = ​'80px'
 
..left = ​'0px'
 
..zIndex = ​'1001'
 
..transition = ​'opacity 1s ease-in-out'
 
..opacity = ​'1'​;

The intent of the code is so much clearer with cascades. It is immediately evident that all of the setters apply to the element’s style whereas the old-fashioned way of doing it requires a non-trivial scanning before we can convince ourselves that all setters operate on the same property. Multiply that kind of readability times a hundred or a thousand over a codebase’s lifetime, and you will realize some hefty time savings.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.138.195