Your question is based on two misconceptions:
- In
obj2
c
is a defined property with the value undefined
.
stringify()
does not serialize undefined
according to the spec - your test is not safe
Both objects are unequal.
On Jasmine toEqual()
toEqual uses the internal util.equals()
which will compare the object key by key for all enumerable keys defined in a and b.
After some type checks it's getting into comparing the keys of the object
On defining properties
Take a look at the ECMAscript spec. This internal Put
method is called when you create object literals:
11.1.5 Object Initialiser
[...]
The production PropertyNameAndValueList : PropertyAssignment is evaluated as follows:
- Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name.
- Let propId be the result of evaluating PropertyAssignment.
- Call the [[DefineOwnProperty]] internal method of obj with arguments propId.name, propId.descriptor, and false.
- Return obj.
[...]
The production PropertyAssignment : PropertyName : AssignmentExpression is evaluated as follows:
- Let propName be the result of evaluating PropertyName.
- Let exprValue be the result of evaluating AssignmentExpression.
- Let propValue be GetValue(exprValue).
- Let desc be the Property Descriptor{[[Value]]: propValue, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}
Similary defining properties via member expression:
8.12.5 [[Put]] ( P, V, Throw )
When the [[Put]] internal method of O is called with property P, value V, and Boolean flag Throw, the following steps are taken:
[...]
Else, create a named data property named P on object O as follows
a. Let newDesc be the Property Descriptor {[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}.
b. Call the [[DefineOwnProperty]] internal method of O passing P, newDesc, and Throw as arguments.
The implementation of DefineOwnProperty
, which is described in 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw), will not be repeated. Also MDN says that even the default value is undefined
.
Check:
> var obj2 = {a: 1, b: 2, c: undefined };
> obj2.hasOwnProperty("c");
< true
On stringify()
Take a look at the ECMAscript spec on stringify()
or at the JSON spec:

(Source: json.org)
Here's a piece of the ECMAscript spec:
- Else
a. Let K be an internal List of Strings consisting of the names of all the own properties of value whose [[Enumerable]] attribute is true. The ordering of the Strings should be the same as that used by the Object.keys standard built-in function.
They say, that the enumerated properties of that object should conform to the order (and result) of Object.keys()
. Let's test that ...
> var obj2 = {a: 1, b: 2, c: undefined };
> Object.keys(obj2);
< ["a", "b", "c"]
Uh, they're right!
Then there's a Str()
function which defines the behavior of handling undefined
values. There are a couple of If Type() ...
steps, which do not apply for an undefined value, ending with
- Return undefined.
When called by an object serializer:
For each element P of K.
a. Let strP be the result of calling the abstract operation Str with arguments P and value.
b. If strP is not undefined
[...]