DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. JavaScript
  4. What is {} + {} in JavaScript?

What is {} + {} in JavaScript?

Axel Rauschmayer user avatar by
Axel Rauschmayer
·
Jan. 30, 12 · Interview
Like (0)
Save
Tweet
Share
4.26K Views

Join the DZone community and get the full member experience.

Join For Free

Recently, the lightning talk “Wat” by Gary Bernhardt pointed out an interesting JavaScript quirk: You get unexpected results when you add objects and/or arrays. This post explains those results.

The general rule for addition in JavaScript is simple: You can only add numbers and strings, all other values will be converted to either one of those types. In order to understand how that conversion works, we first need to understand a few other things. Whenever a paragraph (such as §9.1) is mentioned, it refers to the ECMA-262 language standard (ECMAScript 5.1).

Let us start with a quick refresher. There are two kinds of values in JavaScript: primitives and objects [1]. The primitive values are: undefined, null, booleans, numbers, and strings. All other values are objects, including arrays and functions.

Converting values

The plus operator performs three kinds of conversion: It converts values to primitives, numbers and strings.

Converting values to primitives via ToPrimitive()

The internal operation ToPrimitive() has the following signature:
    ToPrimitive(input, PreferredType?)
The optional parameter PreferredType is either Number or String. It only expresses a preference, the result can always be any primitive value. If PreferredType is Number, the following steps are performed to convert a value input (§9.1):
  1. If input is primitive, return it as is.
  2. Otherwise, input is an object. Call obj.valueOf(). If the result is primitive, return it.
  3. Otherwise, call obj.toString(). If the result is a primitive, return it.
  4. Otherwise, throw a TypeError.
If PreferredType is String, steps 2 and 3 are swapped. If PreferredType is missing then it is set to String for instances of Date and to Number for all other values.

Converting values to numbers via ToNumber()

The following table explains how ToNumber() converts primitives to number (§9.3).

ArgumentResult
undefinedNaN
null+0
boolean valuetrue is converted to 1, false is converted to +0
number valueno conversion necessary
string valueparse the number in the string. For example, "324" is converted to 324

An object obj is converted to a number by calling ToPrimitive(obj, Number) and then applying ToNumber() to the (primitive) result.

Converting values to strings via ToString()

The following table explains how ToString() converts primitives to string (§9.8).

ArgumentResult
undefined"undefined"
null"null"
boolean valueeither "true" or "false"
number valuethe number as a string, e.g. "1.765"
string valueno conversion necessary

An object obj is converted to a number by calling ToPrimitive(obj, String) and then applying ToString() to the (primitive) result.

Trying it out

The following object allows you to observe the conversion process.
    var obj = {
        valueOf: function () {
            console.log("valueOf");
            return {}; // not a primitive
        },
        toString: function () {
            console.log("toString");
            return {}; // not a primitive
        }
    }
Number called as a function (as opposed to as a constructor) internally invokes ToNumber():
    > Number(obj)
    valueOf
    toString
    TypeError: Cannot convert object to primitive value

Addition

Given the following addition.
    value1 + value2
To evaluate this expression, the following steps are taken (§11.6.1):
  1. Convert both operands to primitives (mathematical notation, not JavaScript):
        prim1 := ToPrimitive(value1)
        prim2 := ToPrimitive(value2)
    
    PreferredType is omitted and thus Number for non-dates, String for dates.
  2. If either prim1 or prim2 is a string then convert both to strings and return the concatenation of the results.
  3. Otherwise, convert both prim1 and prim2 to numbers and return the sum of the results.

Expected results

When you are adding two arrays, everything works as expected:
    > [] + []
    ''
Converting [] to a primitive first tries valueOf() which returns the array itself (this):
    > var arr = [];
    > arr.valueOf() === arr
    true
As that result is not a primitive, toString() is called next and returns the empty string (which is a primitive). Therefore, the result of [] + [] is the concatenation of two empty strings.

Adding an array and an object also conforms to our expectations:

    > [] + {}
    '[object Object]'
Explanation: converting an empty object to string yields the following result.
    > String({})
    '[object Object]'
The previous result is thus created by concatenating "" and "[object Object]".

More examples where objects are converted to primitives:

    > 5 + new Number(7)
    12
    > 6 + { valueOf: function () { return 2 } }
    8
    > "abc" + { toString: function () { return "def" } }
    'abcdef'

Unexpected results

Things get weird if the first operand of + is an empty object literal (results as seen on the Firefox console):
    > {} + {}
    NaN
What is going on here? The problem is that JavaScript interprets the first {} as an empty code block and ignores it. The NaN is therefore computed by evaluating +{} (plus followed by the second {}). The plus you see here is not the binary addition operator, but a unary prefix operator that converts its operand to a number, in the same manner as Number(). For example:
    > +"3.65"
    3.65
The following expressions are all equivalent:
    +{}
    Number({})
    Number({}.toString())  // {}.valueOf() isn’t primitive
    Number("[object Object]")
    NaN
Why is the first {} interpreted as a code block? Because the complete input is parsed as a statement and curly braces at the beginning of a statement are interpreted as starting a code block. Hence, you can fix things by forcing the input to be parsed as an expression:
    > ({} + {})
    '[object Object][object Object]'
Arguments of functions or methods are also always parsed as expressions:
    > console.log({} + {})
    [object Object][object Object]
After the previous explanations, you should not be surprised about the following result, any more:
    > {} + []
    0
Again, this is interpreted as a code block followed by +[]. The following expressions are equivalent:
    +[]
    Number([])
    Number([].toString())  // {}.valueOf() isn’t primitive
    Number("")
    0
Interestingly, the Node.js REPL parses its input differently from either Firefox or Chrome (which even uses the same V8 JavaScript engine as Node.js). The following input is parsed as expressions and the results are less surprising:
    > {} + {}
    '[object Object][object Object]'
    > {} + []
    '[object Object]'
This has the advantage of being more like the results you get when using the input as arguments of console.log(). But it is also less like using the input as statements in programs.

What does it all mean?

In most circumstances, it is not that difficult to understand how + works in JavaScript: You can only add numbers or strings. Objects are converted to either string (if the other operand is a string) or number (otherwise). If you want to concatenate arrays, you need to use a method:
    > [1, 2].concat([3, 4])
    [ 1, 2, 3, 4 ]
There is no built-in way in JavaScript to “concatenate” (merge) objects. You need to use a library such as Underscore:
    > var o1 = {eeny:1, meeny:2};
    > var o2 = {miny:3, moe: 4};
    > _.extend(o1, o2)
    { eeny: 1,
      meeny: 2,
      miny: 3,
      moe: 4 }
Note: In contrast to Array.prototype.concat(), extend() modifies its first argument:
    > o1
    { eeny: 1,
      meeny: 2,
      miny: 3,
      moe: 4 }
    > o2
    { miny: 3, moe: 4 }
If you are up for more fun with operators, you can read the post on “Fake operator overloading in JavaScript”.

References

  1. JavaScript values: not everything is an object

 

From http://www.2ality.com/2012/01/object-plus-object.html

JavaScript engine Strings Data Types Object (computer science) Convert (command)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • A Gentle Introduction to Kubernetes
  • NoSQL vs SQL: What, Where, and How
  • How Chat GPT-3 Changed the Life of Young DevOps Engineers
  • Kubernetes-Native Development With Quarkus and Eclipse JKube

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: