Skip to content

Commit e370968

Browse files
authored
Merge pull request #17 from jonschlinkert/key-properties
Property keys
2 parents 12cbc0b + 393e2cb commit e370968

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ node_js:
1717
matrix:
1818
fast_finish: true
1919
allow_failures:
20+
- node_js: 'node'
2021
- node_js: '0.8'

index.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module.exports = function mergeDeep(orig, objects) {
3232

3333
function merge(target, obj) {
3434
for (var key in obj) {
35-
if (key === '__proto__' || !hasOwn(obj, key)) {
35+
if (!isValidKey(key) || !hasOwn(obj, key)) {
3636
continue;
3737
}
3838

@@ -57,3 +57,7 @@ function hasOwn(obj, key) {
5757
function isObject(val) {
5858
return typeOf(val) === 'object' || typeOf(val) === 'function';
5959
}
60+
61+
function isValidKey(key) {
62+
return key !== '__proto__' && key !== 'constructor' && key !== 'prototype';
63+
}

test.js

+64
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,68 @@ describe('mergeDeep', function() {
134134
var actual = merge(fixture);
135135
assert.deepEqual(actual, fixture);
136136
});
137+
138+
it('should not clone invalid keys', function() {
139+
var obj1 = { a: { b: 1 } };
140+
var obj2 = JSON.parse('{ "a": { "c": 2 }, "constructor": { "keys": 42 } }');
141+
142+
var actual = merge({}, obj1, obj2);
143+
assert.deepEqual(actual, { a: { b: 1, c: 2 } });
144+
assert.notDeepEqual(actual.a, obj1.a);
145+
assert.notDeepEqual(actual.a, obj2.a);
146+
assert.notEqual(actual.keys, 42);
147+
assert.notEqual(actual.constructor.keys, 42);
148+
});
149+
150+
it('should allow being used for custom constructors', function() {
151+
// The following setup code is a simple way to demonstrate multiple inheritance by merging the prototype of one class onto another
152+
function Shape() {
153+
this.type = '';
154+
}
155+
156+
function Position(x, y) {
157+
this.x = x || 0;
158+
this.y = y || 0;
159+
}
160+
161+
Position.prototype.stringify = function() {
162+
return '(' + this.x + ', ' + this.y + ')';
163+
};
164+
165+
function Moveable(x, y) {
166+
Position.call(this, x, y);
167+
}
168+
169+
// By making Moveable inherit from Position, allows us to test what happens when `constructor` is passed to `isValidKey`.
170+
Moveable.prototype = Object.create(Position.prototype);
171+
Moveable.prototype.constructor = Moveable;
172+
Moveable.prototype = merge(Moveable.prototype, Position.prototype);
173+
174+
Moveable.prototype.move = function(x, y) {
175+
this.x += x;
176+
this.y += y;
177+
};
178+
179+
Moveable.prototype.position = function() {
180+
return this.stringify();
181+
};
182+
183+
function Rectangle() {
184+
Shape.call(this);
185+
Moveable.call(this);
186+
this.type = 'rectangle';
187+
}
188+
189+
// Single inheritance using Object.create
190+
Rectangle.prototype = Object.create(Shape.prototype);
191+
Rectangle.prototype.constructor = Rectangle;
192+
193+
// This is the test to ensure that `merge-deep` can be used with prototypal inheritance
194+
Rectangle.prototype = merge(Rectangle.prototype, Moveable.prototype);
195+
196+
var rectangle = new Rectangle();
197+
assert.equal(rectangle.position(), '(0, 0)');
198+
rectangle.move(10, 20);
199+
assert.equal(rectangle.position(), '(10, 20)');
200+
});
137201
});

0 commit comments

Comments
 (0)