object-utils

inheritance, mixins and other stuff, mainly to encapsulate objects

目前為 2016-09-01 提交的版本,檢視 最新版本

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

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