Classes and SC.Object
The guide covers some of the core concepts of object-oriented programming with the SproutCore framework. By referring to this guide, you will be able to:
- Understand how Classes work, including SC.Object.
- Write your own subclasses based on SC.Object.
- Create mixins to share functionality between Classes.
- Add Class properties and functions
1 - SC.Object
SproutCore uses a traditional class-based object-oriented
programming style
based on the SC.Object Class. In this style, SC.Object
becomes
the base for all "Classes" in SproutCore and provides powerful extensions to
regular JavaScript objects. These extensions include computed properties,
automatic bindings, observers and more, which will be described in detail below.
1.1 - Creating an SC.Object Instance
Creation of an SC.Object
instance is straightforward.
var obj = SC.Object.create()
In most cases you will want to create your SC.Object
with some pre-
set properties. You accomplish this by providing the properties in a hash. The
following example creates a new SC.Object to represent a person.
// Create an SC.Object to represent a person.
var person = SC.Object.create({
firstName: 'Peter',
lastName: 'Wagenet'
});
// Access our object's properties
person.get('firstName'); // 'Peter'
person.get('lastName'); // 'Wagenet'
The get() and set() functions allow for Key-Value observing which is described in more detail below. For now, trust that they are important and you should make a habit of always using them to access properties on SC.Object and its subclasses.
1.2 - Creating an SC.Object Subclass
In many cases you will want to create subclasses of SC.Object
rather than direct instances. In the above example, we created a new
SC.Object
to represent a person, but it often makes more sense to
define a unique Person
Class, and to create instances of it. Doing
so allows us to define default properties on the Class and to include related
helper functions. To do this, we simply call extend() on
SC.Object
as the next example shows.
MyApp.Person = SC.Object.extend({
firstName: null,
lastName: null,
permissions: 'none',
fullName: function() {
return [this.get('firstName'), this.get('lastName')].compact().join(' ');
}
});
After defining the Person
Class, we can now create new
Person
objects and each will contain the properties
firstName
, lastName
and permissions
as
well as the simple fullName()
function. The following example
shows this.
var person = MyApp.Person.create({
firstName: 'Peter',
lastName: 'Wagenet'
});
person.fullName(); // 'Peter Wagenet'
person.get('permissions'); // 'none'
1.2.1 - Calling superclass methods with sc_super
In some cases when you subclass a Class you will want to augment a method of
the parent Class without completely overriding it. In this case, SproutCore
provides the sc_super method which calls the original function. For those
familiar with Ruby's super
method, sc_super
is very
similar. Here is an example of a subclass of Person
that extends
and uses the superclass's fullName()
method.
// Define a new subclass of MyApp.Person
MyApp.FormalPerson = MyApp.Person.extend({
title: null,
fullName: function() {
// Joins `title` with the result of `fullName()` on the superclass.
return [this.get('title'), sc_super()].compact().join(' ');
}
});
// Create a MyApp.FormalPerson object
var person = MyApp.FormalPerson.create({
title: 'Mr.',
firstName: 'Peter',
lastName: 'Wagenet'
});
person.fullName(); // Mr. Peter Wagenet
person.get('permissions'); // 'none'
sc_super is one of the rare exceptions in SproutCore in that it is not
actually a JavaScript function. It's just a pre-processor directive that gets
replaced with arguments.callee.base.apply(this, arguments)
by the
Build Tools.
1.2.2 - The init method
Whenever an instance of an SC.Object
is created, its init()
method is called first. This function can be overridden when you need to
perform additional setup each time one of your objects is created. The next
example uses init()
to ensure that a calculation is performed
each time a new MyApp.Calculation object is created.
MyApp.Calculation = SC.Object.extend({
input: null,
result: null,
_calculate: function() {
// Do expensive calculation
this.set('result', this.get('input') * 2);
},
init: function() {
sc_super(); // Warning: Always invoke SC.Object's init() method!
this._calculate();
}
});
var calc = MyApp.Calculation.create({ input: 5 });
calc.get('result'); // 10
When overriding the init()
method it is important that
you call sc_super
at the start of your custom init()
method. This will ensure that all internal object initialization takes place
as expected.
1.2.3 - Using Mixins
Mixins are an easy way to extend multiple classes, that don't share an
inheritance tree, with similar functionality. Mixins are simply a hash with a
series of properties that will be added to the objects you create. The next
example defines the MyApp.Introducable
mixin, which can then be
shared between many different Classes.
MyApp.Introducable = {
sayHello: function() {
return 'Hello, my name is ' + this.name;
}
};
To add one or more mixins to a Class, add them as parameters to extend. For example,
MyApp.Person = SC.Object.extend(MyApp.Introducable, { name: ''; });
var person = MyApp.Person.create({ name: 'Bob' });
person.sayHello(); // Hello, my name is Bob
As you may have guessed, SC.Object.extend
just takes a series of
hashes and mixes them all in to a newly created class.
1.2.4 - Class methods
You can also define Class methods on your custom Classes. If you need to add a
single Class method to a Class, you can do it in the traditional JavaScript
manner. For example, the following adds a quickCreate()
helper
function to MyApp.Person
.
MyApp.Person.quickCreate = function(firstName, lastName){
return MyApp.Person.create({
firstName: firstName,
lastName: lastName
});
};
var person = MyApp.Person.quickCreate('Peter', 'Wagenet');
person.fullName(); // Peter Wagenet
However, sometimes you may want to add a series of Class methods and properties. In this case you can use the mixin() method.
MyApp.Person.mixin({
quickCreate: function(firstName, lastName){
return MyApp.Person.create({
firstName: firstName,
lastName: lastName
});
}),
});
person = MyApp.Person.quickCreate('Mary', 'Shelley');
person.fullName(); // Mary Shelley
Note that the mixin()
method is different from the Mixins
described above. The mixin()
method will add the properties and
methods within the block as Class properties and methods. Mixins themselves
add instance variables and methods.
2 - Changelog
- January 12, 2011: initial partial version by Peter Wagenet
- January 19, 2011: further updates by Peter Wagenet
- January 20, 2011: corrections to "The init Method" and "The Run Loop" by Peter Wagenet
- July 19, 2013: added "Changelog" by Topher Fangio
- August 27, 2013: converted to Markdown format for DocPad guides by deeDude