Skip to content

Commit

Permalink
moved Object.has to Object.hasOwn. Object.has now accepts deep keys and
Browse files Browse the repository at this point in the history
makes no hasOwnPrototype checks.


Former-commit-id: 0d2a6f70914dfbc6234755039d5bdd59f1a35cfe [formerly 55e54f022acc098dff799b810261130bba6e7a06]
Former-commit-id: 24de7dadda61ef64c974a6e3c719826806da9c05
  • Loading branch information
andrewplummer committed Feb 10, 2016
1 parent 79855ad commit 7aae47c
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 18 deletions.
3 changes: 3 additions & 0 deletions CAUTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ v2.0.0+
- Level: Major
- `RegExp#addFlag` and `RegExp#removeFlag` are now `RegExp#addFlags` and `RegExp#removeFlags` and now work on multiple flags at once.

- Level: Major
- `Object.has` is now `Object.hasOwn`. The previous method now allows deep keys and makes no `hasOwnProperty` checks.

- Level: Moderate
- `Number#format` no longer accepts arguments for the thousands separator and decimal point. Instead these can now be set globally using Sugar.thousands() and Sugar.decimal(). These will also be respected by Number#abbr, Number#metric, and Number#bytes as well.

Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ v2.0.0
- Removed `Array#include` (now is identical to `Array#add`).
- Added `Object.invert`.
- Moved `String#titleize` to String module from Inflections.
- Added `Array.create`.
- Modified `Object.has` to allow deep keys. Previous behavior is now moved to `Object.hasOwn`.


v1.4.2
Expand Down
23 changes: 16 additions & 7 deletions lib/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -319,17 +319,21 @@ var getKeys = Object.keys || function(obj) {
return keys;
};

function deepHasProperty(obj, key) {
return handleDeepProperty(obj, key, true);
}

function deepGetProperty(obj, key) {
return handleDeepProperty(obj, key);
return handleDeepProperty(obj, key, false);
}

function deepSetProperty(obj, key, val) {
handleDeepProperty(obj, key, true, false, val);
handleDeepProperty(obj, key, false, true, false, val);
return obj;
}

function handleDeepProperty(obj, key, fill, fillLast, val) {
var ns, bs, ps, cbi, set, isLast, isPush, isIndex, nextIsIndex;
function handleDeepProperty(obj, key, has, fill, fillLast, val) {
var ns, bs, ps, cbi, set, isLast, isPush, isIndex, nextIsIndex, exists;
ns = obj || undefined;
if (key == null) return;

Expand Down Expand Up @@ -398,7 +402,12 @@ function handleDeepProperty(obj, key, fill, fillLast, val) {
ns[key] = nextIsIndex || (fillLast && isLast) ? [] : {};
}

if (set && isLast) {
if (has) {
exists = key in ns;
if (isLast || !exists) {
return exists;
}
} else if (set && isLast) {
assertWritable(ns);
ns[key] = val;
}
Expand Down Expand Up @@ -430,12 +439,12 @@ function handleArrayIndexRange(obj, key, val) {
end = end === -1 ? obj.length : end + 1;

if (leading) {
obj = handleDeepProperty(obj, leading, set ? true : false, true);
obj = handleDeepProperty(obj, leading, false, set ? true : false, true);
}

if (set) {
for (var i = start; i < end; i++) {
handleDeepProperty(obj, i + trailing, true, false, val);
handleDeepProperty(obj, i + trailing, false, true, false, val);
}
} else {
obj = obj.slice(start, end);
Expand Down
7 changes: 6 additions & 1 deletion lib/extras/upgrade.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
sName: 'extended',
message: 'Extended objects are now implemented as chainables, which can be created through the global object: Sugar.Object(obj)'
},
{
type: 'Object',
sName: 'has',
message: 'Object.has was renamed to Object.hasOwn. Object.has now allows deep keys and performs no "hasOwnProperty" check. Suppress this warning if you are intentionally allowing properties in the prototype chain.'
},
{
type: 'Object',
sName: 'watch,unwatch',
Expand Down Expand Up @@ -66,7 +71,7 @@
type: 'Object',
sName: 'fromQueryString',
check: fromQueryStringMayBeSmart,
message: 'Object.fromQueryString now performs "smart" conversion of numerals, booleans, and multiple keys by default. To turn this off, pass { smart: false } in the options object which is now the second argument to the function. Additionally, deep bracket syntax (`[]` in keys) is now off by defualt but can be enabled with { deep: true } in the same options object.',
message: 'Object.fromQueryString now performs "smart" conversion of numerals, booleans, and multiple keys by default. To turn this off, pass { smart: false } in the options object which is now the second argument to the function. Additionally, deep bracket syntax ([] in keys) is now off by defualt but can be enabled with { deep: true } in the same options object.',
docs: 'Object/fromQueryString'
},
{
Expand Down
32 changes: 24 additions & 8 deletions lib/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -954,19 +954,35 @@ defineInstanceAndStatic(sugarObject, {
},

/***
* @method Object.has(<obj>, <key>)
* @method Object.has(<obj>, <prop>)
* @returns Boolean
* @short Checks if <obj> has <key> using hasOwnProperty from Object.prototype.
* @extra This method is considered safer than %Object#hasOwnProperty% when using objects as hashes. See http://www.devthought.com/2012/01/18/an-object-is-not-a-hash/ for more.
* @short Checks if <obj> has property <prop>, which may be a deep property using dot or bracket notation.
* @extra Note that this method does not care of <prop> is a direct property of <obj> or in the prototype. To check that use `hasOwn` instead.
* @example
*
* Object.has({ foo: 'bar' }, 'foo') -> true
* Object.has({ foo: 'bar' }, 'baz') -> false
* Object.has({ hasOwnProperty: true }, 'foo') -> false
* Object.has({a:'a'}, 'a') -> true
* Object.has({a:{b:'b'}}, 'a.b') -> true
* Object.has({a:{b:'b'}}, 'b.b') -> false
*
***/
'has': function(obj, key) {
return hasOwn(obj, key);
'has': function(obj, prop) {
return deepHasProperty(obj, prop);
},

/***
* @method Object.hasOwn(<obj>, <prop>)
* @returns Boolean
* @short Checks if <obj> has its own property <prop> using hasOwnProperty from Object.prototype.
* @extra This method is considered safer than %Object#hasOwnProperty% when using objects as hashes. See http://www.devthought.com/2012/01/18/an-object-is-not-a-hash/ for more. Note that this method will *not* work with deep keys.
* @example
*
* Object.hasOwn({foo:'bar'}, 'foo') -> true
* Object.hasOwn({foo:'bar'}, 'baz') -> false
* Object.hasOwn({hasOwnProperty:true}, 'foo') -> false
*
***/
'hasOwn': function(obj, prop) {
return hasOwn(obj, prop);
},

/***
Expand Down
15 changes: 13 additions & 2 deletions test/tests/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -2007,8 +2007,19 @@ namespace('Object', function () {
});

method('has', function() {
test({ foo: 'bar' }, ['foo'], true, 'finds a property');
test({ foo: 'bar' }, ['baz'], false, 'does not find a nonexistant property');
test({foo:'bar'}, ['foo'], true, 'finds a property');
test({foo:'bar'}, ['baz'], false, 'does not find a nonexistant property');
test({foo:{a:'b'}}, ['foo.a'], true, 'works on deep properties');
test({foo:{a:'b'}}, ['foo[a]'], true, 'works on deep properties with bracket syntax');
test({foo:{a:'b'}}, [['foo','a']], true, 'works on deep properties with array');
test({ hasOwnProperty: true, foo: 'bar' }, ['foo'], true, 'local hasOwnProperty is ignored');
});

method('hasOwn', function() {
test({foo:'bar'}, ['foo'], true, 'finds a property');
test({foo:'bar'}, ['baz'], false, 'does not find a nonexistant property');
test({foo:{a:'b'}}, ['foo.a'], false, 'does not work on deep properties');
test({foo:{a:'b'}}, ['foo[a]'], false, 'does not work on deep properties with bracket syntax');
test({ hasOwnProperty: true, foo: 'bar' }, ['foo'], true, 'local hasOwnProperty is ignored');
});

Expand Down

0 comments on commit 7aae47c

Please sign in to comment.