index.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. 'use strict';
  2. var typeOf = require('kind-of');
  3. var copyDescriptor = require('copy-descriptor');
  4. var define = require('define-property');
  5. /**
  6. * Copy static properties, prototype properties, and descriptors from one object to another.
  7. *
  8. * ```js
  9. * function App() {}
  10. * var proto = App.prototype;
  11. * App.prototype.set = function() {};
  12. * App.prototype.get = function() {};
  13. *
  14. * var obj = {};
  15. * copy(obj, proto);
  16. * ```
  17. * @param {Object} `receiver`
  18. * @param {Object} `provider`
  19. * @param {String|Array} `omit` One or more properties to omit
  20. * @return {Object}
  21. * @api public
  22. */
  23. function copy(receiver, provider, omit) {
  24. if (!isObject(receiver)) {
  25. throw new TypeError('expected receiving object to be an object.');
  26. }
  27. if (!isObject(provider)) {
  28. throw new TypeError('expected providing object to be an object.');
  29. }
  30. var props = nativeKeys(provider);
  31. var keys = Object.keys(provider);
  32. var len = props.length;
  33. omit = arrayify(omit);
  34. while (len--) {
  35. var key = props[len];
  36. if (has(keys, key)) {
  37. define(receiver, key, provider[key]);
  38. } else if (!(key in receiver) && !has(omit, key)) {
  39. copyDescriptor(receiver, provider, key);
  40. }
  41. }
  42. };
  43. /**
  44. * Return true if the given value is an object or function
  45. */
  46. function isObject(val) {
  47. return typeOf(val) === 'object' || typeof val === 'function';
  48. }
  49. /**
  50. * Returns true if an array has any of the given elements, or an
  51. * object has any of the give keys.
  52. *
  53. * ```js
  54. * has(['a', 'b', 'c'], 'c');
  55. * //=> true
  56. *
  57. * has(['a', 'b', 'c'], ['c', 'z']);
  58. * //=> true
  59. *
  60. * has({a: 'b', c: 'd'}, ['c', 'z']);
  61. * //=> true
  62. * ```
  63. * @param {Object} `obj`
  64. * @param {String|Array} `val`
  65. * @return {Boolean}
  66. */
  67. function has(obj, val) {
  68. val = arrayify(val);
  69. var len = val.length;
  70. if (isObject(obj)) {
  71. for (var key in obj) {
  72. if (val.indexOf(key) > -1) {
  73. return true;
  74. }
  75. }
  76. var keys = nativeKeys(obj);
  77. return has(keys, val);
  78. }
  79. if (Array.isArray(obj)) {
  80. var arr = obj;
  81. while (len--) {
  82. if (arr.indexOf(val[len]) > -1) {
  83. return true;
  84. }
  85. }
  86. return false;
  87. }
  88. throw new TypeError('expected an array or object.');
  89. }
  90. /**
  91. * Cast the given value to an array.
  92. *
  93. * ```js
  94. * arrayify('foo');
  95. * //=> ['foo']
  96. *
  97. * arrayify(['foo']);
  98. * //=> ['foo']
  99. * ```
  100. *
  101. * @param {String|Array} `val`
  102. * @return {Array}
  103. */
  104. function arrayify(val) {
  105. return val ? (Array.isArray(val) ? val : [val]) : [];
  106. }
  107. /**
  108. * Returns true if a value has a `contructor`
  109. *
  110. * ```js
  111. * hasConstructor({});
  112. * //=> true
  113. *
  114. * hasConstructor(Object.create(null));
  115. * //=> false
  116. * ```
  117. * @param {Object} `value`
  118. * @return {Boolean}
  119. */
  120. function hasConstructor(val) {
  121. return isObject(val) && typeof val.constructor !== 'undefined';
  122. }
  123. /**
  124. * Get the native `ownPropertyNames` from the constructor of the
  125. * given `object`. An empty array is returned if the object does
  126. * not have a constructor.
  127. *
  128. * ```js
  129. * nativeKeys({a: 'b', b: 'c', c: 'd'})
  130. * //=> ['a', 'b', 'c']
  131. *
  132. * nativeKeys(function(){})
  133. * //=> ['length', 'caller']
  134. * ```
  135. *
  136. * @param {Object} `obj` Object that has a `constructor`.
  137. * @return {Array} Array of keys.
  138. */
  139. function nativeKeys(val) {
  140. if (!hasConstructor(val)) return [];
  141. return Object.getOwnPropertyNames(val);
  142. }
  143. /**
  144. * Expose `copy`
  145. */
  146. module.exports = copy;
  147. /**
  148. * Expose `copy.has` for tests
  149. */
  150. module.exports.has = has;