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