object-utils

inheritance, mixins and other stuff, mainly to encapsulate objects

目前為 2016-08-30 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/22752/144699/object-utils.js

  1. var APP = APP || {};
  2. /**
  3. *
  4. * @type {{extend, TruthyFalsy, Truthy, Falsy, emptyFunction}}
  5. */
  6. APP.objectUtils = (function() {
  7. 'use strict';
  8. /**
  9. * Extends an object from another object through the prototype chain
  10. * @param {Object} superObj - The object to be extended
  11. * @param {Object} obj - The object extending the superObj
  12. * @return {boolean} true, if executed successfully
  13. */
  14. function _extend(superObj, obj){
  15. if (typeof superObj === 'undefined') return false;
  16. if (typeof obj === 'undefined') return false;
  17. // save properties of prototype
  18. var descriptors = {};
  19. Object.getOwnPropertyNames(obj.prototype).forEach(function(propName) {
  20. descriptors[propName] = Object.getOwnPropertyDescriptor(obj.prototype, propName);
  21. });
  22. // create new prototype
  23. obj.prototype = Object.create(superObj.prototype, descriptors);
  24. obj.prototype.constructor = obj;
  25. return true;
  26. }
  27. /**
  28. * Clones properties from one object to another.
  29. * A property only gets cloned if it does not yet exist in the target object.
  30. * @param {Object} receiver - the target object receiver the properties
  31. * @param {Object} supplier - the source object supplying the properties
  32. * @param {Array} [props] - names of the relevant properties
  33. * @return {boolean} true, if executed successfully
  34. */
  35. function _mixin(receiver, supplier, props) {
  36. if (typeof supplier === 'object') return false;
  37. if (typeof receiver === 'object') return false;
  38.  
  39. var propNames = Array.isArray(props) ? props : Object.getOwnPropertyNames(supplier);
  40. propNames.forEach(function(propName){
  41. if (!Object.prototype.hasOwnProperty.call(receiver, propName)) {
  42. var desc = Object.getOwnPropertyDescriptor(supplier, propName);
  43. if (typeof desc !== 'undefined') {
  44. Object.defineProperty(receiver, propName, desc);
  45. }
  46. }
  47. });
  48. return true;
  49. }
  50.  
  51. /**
  52. * Has an object extending a super object from their prototype objects.
  53. * @param {Object} obj - The extending object
  54. * @return {{from: extendFrom, mixWith: mixWith}}
  55. */
  56. var extend = function(obj) {
  57. /**
  58. * Clones the properties of the super object if it does not yet exists in the target object.
  59. * @param {Object} superObj - the source object supplying the properties
  60. * @param {Array} [props] - names of the relevant properties
  61. * @return {{thenWith: mixWith}}
  62. */
  63. var mixWith = function(superObj, props) {
  64. if (_mixin(obj.prototype, superObj.prototype, props)){
  65. return {
  66. thenWith : mixWith
  67. };
  68. }
  69. };
  70. /**
  71. * Extends the super object from the prototype chain
  72. * @param superObj
  73. * @return {{mixWith: mixWith}}
  74. */
  75. var extendFrom = function (superObj) {
  76. if (_extend(superObj, obj)) {
  77. return {
  78. mixWith : mixWith
  79. };
  80. }
  81. };
  82.  
  83. return {
  84. from : extendFrom,
  85. mixWith : mixWith
  86. };
  87. };
  88.  
  89. /**
  90. * Defines an object that holds a value and specific values.
  91. * In case the value is a Truthy, Falsy or undefined it is overwritten with specific values.
  92. * @param value The common value.
  93. * @param [ifTruthy] if not undefined it overwrites value when it is a Truthy
  94. * @param [ifFalsy] if not undefined it overwrites value when it is a Falsy
  95. * @param [ifUndefined] if not undefined it overwrites value when it is undefined
  96. * @returns {TruthyFalsy}
  97. * @constructor
  98. */
  99. function TruthyFalsy(value, ifTruthy, ifFalsy, ifUndefined) {
  100. if (!this instanceof TruthyFalsy) {
  101. return new TruthyFalsy(value, ifTruthy, ifFalsy, ifUndefined);
  102. }
  103. this.value = value;
  104. if (ifTruthy !== 'undefined') { this.ifTruthy = ifTruthy; }
  105. if (ifFalsy !== 'undefined') { this.ifFalsy = ifFalsy; }
  106. if (ifUndefined !== 'undefined') { this.ifUndefined = ifUndefined; }
  107. }
  108. TruthyFalsy.prototype = {
  109. constructor : TruthyFalsy,
  110. valueOf: function() {
  111. var result = this.value;
  112. if (typeof this.ifUndefined !== "undefined" && typeof result === "undefined") {
  113. result = this.ifUndefined;
  114. } else if (typeof this.ifTruthy !== "undefined" && result) {
  115. result = this.ifTruthy;
  116. } else if (typeof this.ifFalsy !== "undefined" && result) {
  117. result = this.ifFalsy;
  118. }
  119. return result;
  120. },
  121. toString: function() {
  122. return String(TruthyFalsy.prototype.valueOf.call(this));
  123. }
  124. };
  125.  
  126. function Falsy(value, ifFalsy, ifUndefined) {
  127. if (!(this instanceof Falsy)) {
  128. return new Falsy(value, ifFalsy, ifUndefined);
  129. }
  130. TruthyFalsy.call(this, value, undefined, ifFalsy, ifUndefined);
  131. }
  132. extend(Falsy).from(TruthyFalsy);
  133.  
  134. function Truthy(value, ifTruthy, ifUndefined) {
  135. if (!(this instanceof Truthy)) {
  136. return new Truthy(value, ifTruthy, ifUndefined);
  137. }
  138. TruthyFalsy.call(this, value, ifTruthy, undefined, ifUndefined);
  139. }
  140. extend(Truthy).from(TruthyFalsy);
  141.  
  142. /** placeholder for empty callbacks */
  143. function emptyFunction() {}
  144.  
  145. /**
  146. * Keeps elements assigned by an index
  147. * @param {number} [initialCapacity] - initial size of the array used to store the elements
  148. * @constructor
  149. */
  150. function Keeper(initialCapacity) {
  151. if (!(this instanceof Keeper)) {
  152. return new Keeper(initialCapacity);
  153. }
  154. var _store = new Array(initialCapacity || 16);
  155. var _pointer = 0;
  156. Object.defineProperties(this, {
  157. store: {
  158. get: function() { return _store },
  159. configurable: false, enumerable: false
  160. },
  161. pointer: {
  162. get: function() { return _pointer },
  163. configurable: false, enumerable: false
  164. }
  165. })
  166. }
  167. Keeper.prototype = {
  168. constructor: Keeper,
  169. /**
  170. * Adds a new element
  171. * @param {*|Object} item - The element to keep. Must not be type of <code>undefined</code>.
  172. * @return {number} The index (key) of the added element.<br>
  173. * <code>-1</code> if the element could not be added.
  174. */
  175. push: function(item) {
  176. if (typeof item !== 'undefined') {
  177. var index = this.pointer;
  178. this.store[index] = item;
  179. for (this.pointer = index + 1; this.pointer <= this.store.length; this.pointer++) {
  180. if (typeof this.store[pointer] === 'undefined') { break; }
  181. }
  182. if (this.pointer === this.store.length) {
  183. this.store.length *= 2;
  184. }
  185. return index;
  186. }
  187. return -1;
  188. },
  189. /**
  190. * Removes an element.
  191. * @param index The index (key) of the element to be removed.
  192. * @return {boolean} <code>true</code> if the element could be removed successfully
  193. */
  194. remove: function(index) {
  195. if (index + 1 >= this.store.length) {
  196. this.store[index] = undefined;
  197. this.pointer = index;
  198. return true;
  199. }
  200. return false;
  201. },
  202. /**
  203. * Gets the element with the specified index
  204. * @param {number} index - Index (key) of the element
  205. * @return {*|Object} the element with the specified index or <code>undefined</code>
  206. */
  207. get: function (index) {
  208. if (index + 1 <= this.store.length) {
  209. return this.store[index];
  210. }
  211. return undefined;
  212. }
  213. };
  214.  
  215. return {
  216. extend: extend,
  217. TruthyFalsy: TruthyFalsy,
  218. Truthy: Truthy,
  219. Falsy: Falsy,
  220. Keeper: Keeper,
  221. get emptyFunction() { return emptyFunction }
  222. };
  223. })();