1 // ========================================================================== 2 // Project: SproutCore Costello - Property Observing Library 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 /** @namespace 9 The `SC.ObjectMixinProtocol` protocol defines the properties and methods that you may implement 10 in your mixin objects (i.e. JavaScript Objects passed to SC.Object's `extend` or `create`) in 11 order to access additional functionality when used. They will be used if defined but are not 12 required. 13 14 # What is a Mixin? 15 16 A mixin, in this context, is a simple JavaScript Object that can be used to provide extra 17 functionality to SC.Object subclasses. While you can mix JavaScript Objects into "classes" (i.e. 18 using `SC.mixin(SomeClass)`), this particular protocol only refers to mixins in the context of 19 use with an SC.Object "instance" (i.e. SC.Object.create({ ... })). 20 21 For example, in order to share a method between two different classes of object, we can use a 22 mixin object that both will consume, 23 24 // Common default properties and shared methods which our different classes may consume. 25 MyApp.MyMixin = { 26 a: true, 27 28 aFunc: function () { 29 this.set('a', false); 30 }, 31 32 b: [], // SHARED OBJECT! 33 34 c: {} // SHARED OBJECT! 35 }; 36 37 // Two different object types, which both need the functionality provided by MyApp.MyMixin. 38 MyApp.ObjectType1 = SC.Object.extend(MyApp.MyMixin); 39 MyApp.ObjectType2 = SC.Object.extend(); 40 41 obj1 = MyApp.ObjectType1.create(); 42 obj2 = MyApp.ObjectType2.create(MyApp.MyMixin); 43 44 // Some proofs. 45 // 1. The default properties are copied over to the new objects. 46 obj1.get('a'); // true <-- 47 obj2.get('a'); // true <-- 48 49 // 2. The primitive properties are unique to each object. 50 obj1.set('a', false); 51 obj1.get('a'); // false <-- 52 obj2.get('a'); // true <-- 53 54 // 3. The methods are copied over to the new objects. 55 obj1.aFunc; // function () { ... } <-- 56 obj2.aFunc; // function () { ... } <-- 57 58 // 4. The functions/objects are shared between objects. 59 obj1.aFunc === MyApp.MyMixin.aFunc; // true <-- 60 obj1.aFunc === obj2.aFunc; // true <-- 61 obj1.b === obj2.b; // true <-- !! Beware of modifying this object !! 62 obj1.c === obj2.c; // true <-- !! Beware of modifying this object !! 63 64 In this example, we used a mixin to share functionality between two classes, which is very easily 65 achieved. There is one issue, that has been known to trip up developers, which should be 66 highlighted. If you set default *Objects* (e.g. [] or {}) in a mixin, these same Objects will be 67 shared between all of the mixin's consumers. 68 69 If you want to set a default Object that is unique to each consumer of the mixin, a better 70 practice is to set it in `initMixin()` or to check for its existence the first time it is used 71 and only create it then. 72 73 *Note: Do not mix `SC.ObjectMixinProtocol` into your classes. As a protocol, it exists only for 74 reference sake. You only need define any of the properties or methods listed below in order to use 75 this protocol.* 76 */ 77 SC.ObjectMixinProtocol = { 78 79 /** 80 This *optional* method is called to further initialize the consumer of the mixin when it is 81 created. When a mixin (i.e. JavaScript Object) is used to extend an `SC.Object` subclass, we may 82 want to perform additional set up of the `SC.Object` instance when it is created according to 83 the needs of the mixin. In order to support this, `SC.Object` will call this method, 84 `initMixin`, *if implemented*, on each mixin in the order that they were added. 85 86 For example, if we use two mixins that both initialize the same value, the last mixin added 87 would win, 88 89 myObject = SC.Object.create( 90 // First mixin. 91 { 92 initMixin: function () { 93 this.set('a', true); 94 } 95 }, 96 97 // Second mixin. 98 { 99 initMixin: function () { 100 this.set('a', false); 101 } 102 }); 103 104 myObject.get('a'); // false <-- 105 106 This was just an example to illustrate the order in which `initMixin` is called. It is rare 107 that mixins will collide with each other, but it is something to bear in mind when making heavy 108 use of mixins. 109 110 Note, that unlike the similar `init()` method of `SC.Object`, you do *not* need to call 111 `sc_super` in `initMixin`. 112 */ 113 initMixin: function () {}, 114 115 /** 116 This *optional* method is called to further de-initialize the consumer of the mixin when it is 117 destroyed. When a mixin (i.e. JavaScript Object) is used to extend an `SC.Object` subclass, we 118 may want to perform additional teardown of the `SC.Object` instance when it is destroyed 119 according to the needs of the mixin (e.g. to clean up objects that the mixin code initialized 120 and that may otherwise lead to memory leaks). In order to support this, `SC.Object` will call 121 this method, `destroyMixin`, *if implemented*, on each mixin in the order that they were 122 initially added. 123 124 For example, if we use two mixins that both de-initialize the same value, the last mixin added 125 would win, 126 127 myObject = SC.Object.create( 128 // Mixin. 129 { 130 initMixin: function () { 131 // Created extra object for some purpose. 132 this.set('anObject', SC.Object.create()); 133 }, 134 135 destroyMixin: function () { 136 // Clean up extra object that the mixin is responsible for. 137 var anObject = this.get('anObject'); 138 anObject.destroy(); 139 this.set('anObject', null); 140 } 141 }); 142 143 myObject.get('a'); // false <-- 144 145 Note, that unlike the similar `destroy()` method of `SC.Object`, you do *not* need to call 146 `sc_super` in `destroyMixin`. 147 */ 148 destroyMixin: function () {} 149 150 }; 151