public
Description: JavaScript Application Framework - JS library only
Home | Edit | New

Creating Objects and Classes

About SproutCore Objects

JavaScript is not a class-based language. However, you can implement some of the more useful parts of class-based languages, such as inheritance, using JavaScript’s prototype functionality. It turns out that the prototype-based inheritance provided by JavaScript is actually much better for the web because it means you can construct meaningful objects with far less code — if you can figure out how to use it.

SproutCore blends JavaScript’s prototype-inheritance with a more traditional class-like interface, making it easier to do traditional object-oriented programming in JavaScript without losing the power of the prototype.

This document will introduce you to SproutCore Objects and Classes, how to create them, and then dives into some of the details of how SproutCore objects and classes work. You should read the opening sections of this document if you are new to working with SproutCore. You will be interested in the more detailed sections if you already are familiar with SproutCore and would like to learn more about the inner details.

Creating Classes and Objects

You create new classes and new objects in a similar way. Just like most class-based languages, you create a new class by calling the extend() method on the class, passing in any properties you want added to the new class that should differ from the parent class. The return value of the extend() method is a new class:

MyClass = SC.Object.extend({
  firstName: "John",
  lastName: "Doe"
});

Creating an object instance in SproutCore is very similar. Instead of using the extend() method, you call the create() method. However, unlike most class-based systems, you do not pass parameters to create(). Instead you pass any properties you want added directly to the new object, just like when you create a class. This allows you to easily configure a new object exactly the way you want it without having to write long constructor functions. In fact, you rarely will need to write a constructor in SproutCore (although you can; see below):

myObject = SC.Object.create({
  firstName: "John",
  lastName: "Doe"
});
myObject.get('firstName') => 'John'
myObject.get('lastName') => 'Doe'

As you might guess by the similarity between the create() and extend() methods, creating an object and creating a class are very similar operations in SproutCore. In both cases, SproutCore creates a new object and copies any properties you pass onto the new object. For extend() it then sets up a new class object and returns it. For create(), it instead calls the init() method on the new object and then returns the object itself.

You can see this difference yourself on the command line in your web browser. Just load a SproutCore application in your browser, open the console and type the following:

MyClass = SC.Object.extend({
  firstName: "John"
});

// this is the new object created by extend()
MyClass.prototype.firstName 
// => "John"

// this is a new object instance created by create()
myObject = MyClass.create({
  firstName: "Jane"
});
myObject.firstName
// => "Jane"

By making classes and objects so similar, the code you write in your applications can be made much simpler. Instead of defining classes that you only instantiate once, you can simply create objects and configure them as you like. You will do this often in your application code, such as when you create controllers.

Initializing Objects

As we mentioned earlier, since you can configure an object just as you like when creating it, you can usually avoid having to write long constructor functions. Sometimes, however, you need to do additional setup beyond just configuring your object. For this purpose, SproutCore allows you to write an init() method that will be called automatically when your object is created:

MyApp.MyClass = SC.Object.extend({
  init: function() {
   sc_super(); // always call sc_super!
   console.log("object inited!");
   
   // other config code here
  }
});

myObject = MyApp.MyClass.create();
=> "object inited!"

IMPORTANT: When you define an init() function, be sure to always call sc_super() somwhere within the method. Usually this should be the first thing you call. init() is used by SproutCore to setup observers and bindings. If you do not call sc_super() to let SproutCore run it’s native init() method as well, your object will not be fully initialized.

Destroying Objects

Since JavaScript is a garbage collected language, you usually will not need to manage memory yourself. However, sometimes when you create an object, you may want to explicitly notify the object when you are done using it so that it can help release any memory it might have consumed. This is particularly important when working with views, since they manage DOM elements that can leak easily on certain browsers.

All SproutCore objects implement a destroy() method that you can call when you are finished with the object. The default implementation of this method simply calls the destroyMixin() method on any applied mixins. You can override this method to do your own additional cleanup when necessary:

MyApp.MyClass = SC.Object.extend({
  destroy: function() {
   sc_super(); // always call sc_super!
   console.log("object destroyed!");
   
   // other config code here
  }
});

myObject = MyApp.MyClass.create().destroy();
=> "object destroyed!"

Like init(), you must always be sure to call sc_super() inside your destroy() method to allow SproutCore to perform its normal teardown. Usually this should be the first thing you do in your method.

When working with objects created by others, it is always a good idea to call destroy() on an object when you are finished working with it. Although this is not always critical, it will help you keep your memory usage under control for long-running applications.

Other Types of Object Creators

For objects that you create that do not extend from SC.Object (for example, simple data hashes), you can use several utility methods in SproutCore to create new instances:

  • SC.clone(object) – this will create a new object and copy the properties from the old object onto it. Note that the property values will not be cloned, only the object itself.
  • SC.beget(object) – this will create a new object with the old object set as its prototype. This is the most native-form of object inheritance implemented by JavaScript. Note that since the new object is bound to the old object, changes you make to the old object will still be reflected in the new object. Alternatively, changes you make to the new object will not be reflected on the old object.

About Guids

All objects in SproutCore are assigned a guid. You can use guids as an alternative to pointers to keep an index on objects. To get the guid for an object, use the SproutCore utlity method SC.guidFor(object). You can pass any object, even null or undefined, into this method, and get back a constant guid value.

If you want to store a collection of objects by an index key, you should instead use SC.hashFor(). This method works much like SC.guidFor(), however if the object you pass implements the hash() method, it will call that method instead, allowing the object to return a content-based key rather than a simple pointer to the object.

Implementation Details

This class is technically a special type of function known in JavaScript as a “constructor”. When you create object instances, you can find the class they belong to by checking the constructor property:


anObject = MyClass.create();
anObject.contstructor
=> MyClass

We use prototype inheritance…

Last edited by sproutit, Tue Nov 25 09:25:20 -0800 2008
Home | Edit | New
Versions: