// ========================================================================== // Project: SproutCore
Costello - Property Observing Library // Copyright: ©2006-2011 Strobe Inc. and contributors. // Portions ©2008-2011 Apple Inc. All rights reserved. // License: Licensed under MIT license (see license.js) // ==========================================================================
/** @namespace
The `SC.ObjectMixinProtocol` protocol defines the properties and methods that you may implement in your mixin objects (i.e. JavaScript Objects passed to SC.Object's `extend` or `create`) in order to access additional functionality when used. They will be used if defined but are not required. # What is a Mixin? A mixin, in this context, is a simple JavaScript Object that can be used to provide extra functionality to SC.Object subclasses. While you can mix JavaScript Objects into "classes" (i.e. using `SC.mixin(SomeClass)`), this particular protocol only refers to mixins in the context of use with an SC.Object "instance" (i.e. SC.Object.create({ ... })). For example, in order to share a method between two different classes of object, we can use a mixin object that both will consume, // Common default properties and shared methods which our different classes may consume. MyApp.MyMixin = { a: true, aFunc: function () { this.set('a', false); }, b: [], // SHARED OBJECT! c: {} // SHARED OBJECT! }; // Two different object types, which both need the functionality provided by MyApp.MyMixin. MyApp.ObjectType1 = SC.Object.extend(MyApp.MyMixin); MyApp.ObjectType2 = SC.Object.extend(); obj1 = MyApp.ObjectType1.create(); obj2 = MyApp.ObjectType2.create(MyApp.MyMixin); // Some proofs. // 1. The default properties are copied over to the new objects. obj1.get('a'); // true <-- obj2.get('a'); // true <-- // 2. The primitive properties are unique to each object. obj1.set('a', false); obj1.get('a'); // false <-- obj2.get('a'); // true <-- // 3. The methods are copied over to the new objects. obj1.aFunc; // function () { ... } <-- obj2.aFunc; // function () { ... } <-- // 4. The functions/objects are shared between objects. obj1.aFunc === MyApp.MyMixin.aFunc; // true <-- obj1.aFunc === obj2.aFunc; // true <-- obj1.b === obj2.b; // true <-- !! Beware of modifying this object !! obj1.c === obj2.c; // true <-- !! Beware of modifying this object !! In this example, we used a mixin to share functionality between two classes, which is very easily achieved. There is one issue, that has been known to trip up developers, which should be highlighted. If you set default *Objects* (e.g. [] or {}) in a mixin, these same Objects will be shared between all of the mixin's consumers. If you want to set a default Object that is unique to each consumer of the mixin, a better practice is to set it in `initMixin()` or to check for its existence the first time it is used and only create it then. *Note: Do not mix `SC.ObjectMixinProtocol` into your classes. As a protocol, it exists only for reference sake. You only need define any of the properties or methods listed below in order to use this protocol.*
*/ SC
.ObjectMixinProtocol = {
/** This *optional* method is called to further initialize the consumer of the mixin when it is created. When a mixin (i.e. JavaScript Object) is used to extend an `SC.Object` subclass, we may want to perform additional set up of the `SC.Object` instance when it is created according to the needs of the mixin. In order to support this, `SC.Object` will call this method, `initMixin`, *if implemented*, on each mixin in the order that they were added. For example, if we use two mixins that both initialize the same value, the last mixin added would win, myObject = SC.Object.create( // First mixin. { initMixin: function () { this.set('a', true); } }, // Second mixin. { initMixin: function () { this.set('a', false); } }); myObject.get('a'); // false <-- This was just an example to illustrate the order in which `initMixin` is called. It is rare that mixins will collide with each other, but it is something to bear in mind when making heavy use of mixins. Note, that unlike the similar `init()` method of `SC.Object`, you do *not* need to call `sc_super` in `initMixin`. */ initMixin: function () {}, /** This *optional* method is called to further de-initialize the consumer of the mixin when it is destroyed. When a mixin (i.e. JavaScript Object) is used to extend an `SC.Object` subclass, we may want to perform additional teardown of the `SC.Object` instance when it is destroyed according to the needs of the mixin (e.g. to clean up objects that the mixin code initialized and that may otherwise lead to memory leaks). In order to support this, `SC.Object` will call this method, `destroyMixin`, *if implemented*, on each mixin in the order that they were initially added. For example, if we use two mixins that both de-initialize the same value, the last mixin added would win, myObject = SC.Object.create( // Mixin. { initMixin: function () { // Created extra object for some purpose. this.set('anObject', SC.Object.create()); }, destroyMixin: function () { // Clean up extra object that the mixin is responsible for. var anObject = this.get('anObject'); anObject.destroy(); this.set('anObject', null); } }); myObject.get('a'); // false <-- Note, that unlike the similar `destroy()` method of `SC.Object`, you do *not* need to call `sc_super` in `destroyMixin`. */ destroyMixin: function () {}
};