0

When reading through the ECMAScript specification, I noticed, it actually never mentions concepts like "pass by value" or "pass by reference".

When looking at the assignment operator, this is how it is specified:

An expression like

let a = 4

would lead to path 1.d. Which basically just sets the value of 4 to a. But same would be true for the assignment of an object.

Does this mean, it would be a conforming ECMAScript implementation to copy objects by value (which obviously isn't the case in any known implementation)?

2 Answers 2

3

This relates to the concept of identity. ECMAScript has various kinds of values. Objects have identity, but undefined, null, booleans, numbers, bigints, and strings do not. (Symbols are complicated).

The value 42 is the same value as 42, the values cannot be distinguished. You cannot modify these values, that just creates a different value.

The only ECMAScript value that you can modify is an Object value. You can make observable modifications by creating, setting, or deleting properties. Variables are similar to properties – they are bindings in an "environment record" that represents a scope, though scopes other than the global-this and with-clauses are not connected to actual ECMAScript Objects.

You can have multiple bindings that refer to the same primitive (non-Object) value, but there's nothing you can do to tell the difference. It is likely that an ECMAScript implementation will just copy values for bools or numbers, and it is free to decide whether or not to copy or share strings.

You can also have multiple bindings that refer to the same Object value. Because Objects have identity, you can tell whether two bindings refer to the same Object, or to two distinct Objects. When you modify an Object through one binding, you can observe that change through all other bindings as well.

Taken together, this means:

  • ECMAScript can be interpreted to have primitive types (anything that is not an Object, in particular strings and numbers).
  • ECMAScript is very much a by-value language, which is clear in the spec through its careful distinction of values and references (which always refer to bindings/properties).
  • But Objects are pointer-like values. Creating a new binding to an Object value does not clone its contents.
  • This approach is sometimes called a "by-sharing" rather than a "by-value" (copying) or "by-reference" evaluation strategy.

A conforming ECMAScript implementation can represent Objects as a pointer to some data structure, and it can copy those pointers around. It cannot generally copy the contents of those objects around, though. Of course, an implementation might be able to show that an object is only used locally, and that such optimizations would be safe.

These kinds of semantics are thoroughly mainstream, and are used in a very similar way e.g. by Java or Python (and by their common ancestor, Smalltalk). Function calls just involve normal bindings of argument values to parameter names, with no special by-name or by-reference semantics. This behaviour is in contrast to languages like C/C++ that also operate by-value, but have explicit pointers and would copy struct contents by default. C++ optionally also supports by-reference calls.

6
  • Those 4 points in the summary make perfect sense to me, coming from Java / C++. But when you say that multiple bindings can refer to the same value, it sounds like references to a unique value-"object" that can be shared. Which works perfectly as the value-"object" is immutable, but seems a bit over-complicating things when you could just say: Primitive variables are bindings to a memory location that stores a value. With objects, this value is a reference.
    – tweekz
    Apr 8, 2023 at 20:04
  • @tweekz: I would rather say that your description is overcomplicating things. There is no need to distinguish primitives and objects in this regard. You can just say "Variables are bindings that contain a reference to a value". While it is true that almost all ECMAScript implementations contain optimizations where they eliminate the intermediate reference and store (some) primitive values directly, there is no way of knowing that. The only way to discover that would be by mutating the value, but primitives are immutable, so you will never be able to tell whether you are looking at two copies … Apr 9, 2023 at 8:36
  • … or two references to the same copy. Apr 9, 2023 at 8:36
  • 1
    And, by the way, none of this is pass-by-reference. ECMAScript is purely pass-by-value, always with no exceptions. More precisely, it is a special case of pass-by-value where the value being passed is an unforgeable reference to a (potentially shared, potentially mutable) value. This is sometimes called call-by-object-sharing, call-by-sharing, or call-by-object, but it is still pass-by-value. Apr 9, 2023 at 8:40
  • Yeah, agree about the "pass by value". But coming from C/C++ I find it less intuitive, to say that every variable has a reference to a value. As a model, isn't it much cleaner to just store values directly at a memory location. And in case of objects, this stored value happens to be a reference. No need for a reference to a primitive.
    – tweekz
    Apr 12, 2023 at 6:13
0

ECMAScript does not specify how objects, values and assignment should be implemented under the hood. But semantic requirements means it would be impractical to copy mutable objects on assignment:

https://tc39.es/ecma262/#sec-identity

Some values with identity are mutable and therefore can have their characteristics (except their identity) changed in-place, causing all holders of the value to observe the new characteristics.

So if an object is modified, all variables the object have been assigned to would reflect the modification, like this:

const a = { foo: 17 }
const b = a;
b.foo = 42;
console.log(a.foo); // should print 42

So while a compliant implementation could in theory copy the object on assignment, it would have to keep the two copies synchronized since they have the same identity. This is of course impractical, so in reality, just a pointer to the same object is copied.

But if the object was frozen or the compiler could prove that it was never modified, it would be perfectly legal to copy the object. For example, Date objects are lightweight and rarely mutated, so it might be less overhead to just copy the objects value rather than allocate it on the heap and copy a pointer. The implementation just needs a fallback strategy in case the date is mutated.

In general, the ECMAScript specification leaves a lot of freedom to the implementation, compared to lower-level languages like C++. Given this freedom, it is not meaningful to describe the language in general as "pass-by-value" or similar, since this describes implementation details.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.