in ECMAScript

ECMA-262-3 in detail. Chapter 8. Evaluation strategy.

Read this article in: Russian, French.

In this small note we will consider strategy of passing arguments to functions in ECMAScript.

Generally this section of a computer science is called evaluation strategy, i.e. set of rules for evaluation and calculation of some expressions in programming language. Strategy of passing arguments to functions is one of its cases.

Many developers are used to think that objects in JavaScript are passed in functions by reference, and the primitive values are passed by value. Moreover, we can see this “fact” in various articles, discussions on forums and even books on JavaScript. However, how correct this terminology and description are, we will consider in this article.

In the general theory there are two types of evaluation strategy: strict, meaning that arguments are calculated before their application and non-strict, meaning calculation of the arguments on demand, when they are required (so-called “lazy” evaluation).

However, here we consider basic strategies of passing arguments to functions which are important for understanding from the point of ECMAScript.

And to begin with, it is necessary to notice that in ECMAScript, as well as in many other languages (for example, C, Java, Python, Ruby, other) the strict strategy is used.

Also the order in which arguments are being evaluated is important — in ECMAScript it is left-to-right. In other languages and their implementations the reverse evaluation order (i.e. right-to-left) can be used.

Since not all of the discussed below strategies are used in ECMAScript, for the examples describing behavior of concrete theoretical strategies, we use the abstract pseudo-code similar to Pascal-like syntax.

This type of strategy is well-known by many programmers. The value of an argument here is a copy of the passed object. The changes made inside the function do not affect the passed object outside. Generally, there is an allocation of a new memory block, to which the passed object is copied, and exactly the value from this new memory location is used.

bar = 10
 
procedure foo(barArg):
  barArg = 20;
end
 
foo(bar)
 
// changes inside foo didn't affect
// on bar which is outside
print(bar) // 10
 

However, this strategy causes big performance issues in case when function argument is not a primitive value, but a complex structure, an object. This exactly what happens, for example, in C/C++ when a structure is passed by value to a function — it is fully copied.

Let’s describe the general example which we will use in the description of the following evaluation strategies. Let the abstract procedure accepts two arguments: value of an object and also a boolean flag, whether we need to reassign the parameter to a new value, or only to change properties of the original object.

bar = {
  x: 10,
  y: 20
}
 
procedure foo(barArg, isReassign):
 
  if isReassign:
    barArg = {z: 1, q: 2}
    exit
  end
 
  barArg.x = 100
  barArg.y = 200
 
end
 
foo(bar)
 
// with call by value strategy,
// object outside has not been changed 
print(bar) // {x: 10, y: 20}
 
// the same with full change
// (assigning the new value)
foo(bar, true)
 
//also, there were no changes made
print(bar) // {x: 10, y: 20}, but not {z: 1, q: 2}
 

In contrast, the by reference evaluation strategy receives not a copy, but the implicit reference to an object, i.e. the address directly related with an object from the outside. Any changes to the parameter inside the function (assignment of a new value or changes of properties) affect the original object. I.e. an argument is an alias for the object from the outside.

Pseudo-code:

// definition of foo procedure see above
 
// having the same object
bar = {
  x: 10,
  y: 20
}
 
// results of foo procedure
// with call by reference
// are the following
 
foo(bar)
 
// property values of the object are changed 
print(bar) // {x: 100, y: 200}
 
// assigning of the new value
// is also affected on the object
foo(bar, true)
 
// bar now references the new object
print(bar) // {z: 1, q: 2}
 

This strategy allows more efficiently passing complex structures, when just the address is passed.

If the first two discussed strategies for the majority of developers are well-known, then the following strategy (and to be more exact, its discussing terminology) is not widely used. However, as we will see shortly, exactly it plays the key role in passing arguments strategy in ECMAScript.

Alternative names of this strategy are “call by object” or “call by object-sharing”.

Strategy “by sharing” was proposed and first named by Barbara Liskov for CLU programming language in 1974.

The main difference of this strategy is that function receives the copy of the reference to object. This reference copy is associated with the formal parameter and is its value.

Regardless of usage concept of reference in this case, this strategy should not be confused with the “call by reference” discussed above. The value of the argument is not a direct alias, but the copy of the address.

In this case an assignment of a new value to the argument does not affect the passed outside (as it is in case of by reference). However, since the formal parameter still received an address, it can access the fields of the original object, and mutate them.

// definition of foo procedure see above
 
// again, the same object structure
bar = {
  x: 10,
  y: 20
}
 
// call by sharing
// affects on object
// in the following manner
 
foo(bar)
 
// values of object properties are changed
print(bar) // {x: 100, y: 200}
 
// but with full change of object
// there is no changes
foo(bar, true)
 
// still the same from the previous call
print(bar) // {x: 100, y: 200}
 

This strategy assumes that language in the majority operates with objects, rather than primitive values.

Additional information on the semantics of this evaluation strategy can be found in the Name binding section of the Lexical Environments article. In particular, operations of Rebinding and Mutation allows to see the described process in detail.

Strategy by sharing is used in many languages: Java, ECMAScript, Python, Ruby, Visual Basic, etc.

Moreover, in Python community exactly this terminology — by sharing is used.

However, in other languages, e.g. in Java, ECMAScript, and others, this strategy is also called as by value, meaning specific value — a reference copy.

Regarding С/С++, this strategy is similar to passing by pointer. Just in C it is still possible to dereference the pointer and change the object from the outside.

However, assigning a new value to a pointer just rebinds it to the new memory block, keeping the old memory untouched. Still it is possible to change properties of the original object using this pointer.

Therefore, making an analogy with pointers, we can obviously see that this is passing by value of the address, and what exactly pointer is. In this case, by sharing is some kind of “syntactic sugar” which at assignment behaves like a “non-dereferening” pointer, and in case of property changes — like a reference, not requiring the dereferencing operation. Sometimes it can be named as “safe pointer”.

С/С++ also has special “syntactic sugar” for this:

obj->x instead of (*obj).x

The most closely in C++ this ideology can be seen in one of “smart pointer” implementations, for example, in boost::shared_ptr which overloads assigning operator and copy constructor for these purposes, and also uses reference counting of objects. This data type even has the similar name — shared_ptr.

Now we know which evaluation strategy arguments passing is used in ECMAScript — call by sharing: changes of the properties original object are possible, however assignment just rebinds the parameter name to a new memory, keeping the original untouched.

But as we mentioned above, among the ECMAScript developers the local terminology for this strategy is used — there it is called as “call by value where value is the reference copy”.

JavaScript inventor Brendan Eich also notices that the copy of reference (copy of address) is passed. In discussion on one of forums cites of ECMAScript developers which also name this as call by value are also provided.

More precisely, this behavior can be understood and by consideration of simple assignment where we can see that there are two different objects, but with identical value — an address copy.

ECMAScript code:

var foo = {x: 10, y: 20};
var bar = foo;
 
alert(bar === foo); // true
 
bar.x = 100;
bar.y = 200;
 
alert([foo.x, foo.y]); // [100, 200]
 

I.e. two identifiers (name bindings) are bound to the same object in memory, sharing this object:

foo value: addr(0xFF) => {x: 100, y: 200} (address 0xFF) <= bar value: addr(0xFF) 

An assignment just rebinds an identifier to the new object:

bar = {z: 1, q: 2};
 
console.log([foo.x, foo.y]); // [100, 200] – nothing is changed
console.log([bar.z, bar.q]); // [1, 2] – however bar now references the new object

I.e. now foo and bar have different values, i.e. different addresses:

foo value: addr(0xFF) => {x: 100, y: 200} (address 0xFF)
bar value: addr(0xFA) => {z: 1, q: 2} (address 0xFA)
 

Assignment of one variable to another just copies its address, making both variables pointing to the same memory location. The following assignment of a new value unbinds a name from the old address and rebinds it to the new one. And it is the important difference from the by reference strategy.

Moreover, if to consider only the abstraction level provided by ECMA-262 standard, we see that only concept of a “value” appears in every algorithm. And implementation of passing this “value” (and its various kinds — whether it is primitive value or object) — is left behind the scene. From this perspective, it is possible to say that there is only “value” and, accordingly, only call by value.

Let’s define the versions of correct terminology related to ECMAScript in this case.

It can be either “call by value”, with specifying that the special case of call by value is meant — when the value is the address copy. From this position it is possible to say that everything in ECMAScript are passed by value.

Or, “call by sharing”, which makes this distinction from “by reference”, and “by value”. In this case it is possible to separate passing types: primitive values are passed by value and objects — by sharing.

The statement “objects are passed by reference” formally is not related to ECMAScript and is incorrect.

I hope this article helped to understand in more details evaluation strategy in general, and also related to ECMAScript. As always, I’ll be glad to discuss and answer the questions in the comments.

External articles:

ECMA-262-5 in detail:


Translated by: Dmitry A. Soshnikov.
Published on: 2010-04-10

Originally written by: Dmitry A. Soshnikov [ru, read »]
With additions by: Zeroglif

Originally published on: 2009-08-11

Write a Comment

Comment

18 Comments

  1. Thank you Dmitry, very informing article.

  2. gr8 ending to a gr8 serie of articles, thanks! (will reread 6 articles again… not including the OOP articles :P)

  3. Hi Dmitry

    Very good Javascript series. Excellent. I have read several of
    your articles, and they are really good.

    One question about this article. Are there languages that use
    call by reference? You don’t give any examples. The closest I
    could think of is in C by using pointers to pointers.

    int f(struct example **x) 
    {
      *x = malloc(sizeof **x);
      ......
    }

    or you could wrap a struct inside a struct.

    But are there languages with more direct examples, where f(x) would really be by reference?

    Morten.

  4. @Morten Krogh

    Are there languages that use call by reference?

    First, we should understand that mentioned strategies are just abstract descriptions.

    However, in some languages, the concept and the exact meaning of a reference can vary. The nearest example of by-reference strategy can be shown with C++ and its reference sugar:

    // C++
    
    void call_by_reference(int& x)
    {
      x = 20; // external *mutation*
    }
    
    void call_by_pointer(int* x)
    {
      *x = 20; // also external *mutation*
      x = new int; // and this is already *rebinding*
      *x = 30; // mutation of the other memory block
    }
    
    void call_by_value(int x)
    {
      x = 20; // just local mutation
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
      int y = 0;
    
      call_by_reference(y);
      call_by_pointer(&y);
      call_by_value(y);
    
      return 0;
    }

    Thus, as said, this by-reference of C++ is just a syntactic sugar (in general, and if to see the assembly generated code) for the by-pointer (which is described above by-sharing) strategy — i.e. for not to dereference a pointer every time.

    The main point is in the operation of assignment. Because often enough there are questions on JS such as “Why if an object is passed by reference I can’t replace it via assignment?”. The answer is — it’s not by-reference (from C++ viewpoint), but “by-pointer” (or “by-value, where value is an address”), where assignment (without dereferencing) just changes the pointer’s address.

    Also take a look on this explanation of a name binding concept, which also accurately explains the difference. Thus, the assignment means a rebinding. And changing of a properties means mutation.

    Dmitry.

  5. Hi Dmitry

    Yes, but do you know a language where all function arguments are by reference. So for example,

    function f(x) {
      x = 5
    };
    
    a = 10;
    f(a);

    and now a = 5.

    It would be a weird language to work with.

    Morten.

  6. I’m not completed assent this article. since in the java world, pass value to method. and if do like you said, pass value will not affect outside object. But its definite will change the properties of outside object, because the parameter variable hold the reference of the original object, if don’t change the reference, change any properties will affect outside original object(this strategy more like your article “call by reference”, but it is called pass by value).

  7. And i know in the C# there has two keyword–“ref”|”out”, this can do like you said “call by share”

  8. Hi Dmitry

    Yes, but do you know a language where all function arguments are by reference. So for example,

    function f(x) {
      x = 5
    };
     
    a = 10;
    f(a);
    and now a = 5.
    

    It would be a weird language to work with.

    Morten.

    C# can do this

     function f(ref x) {
        x = 5;
     }
     a = 10;
     f(a); 

    a will be the 5

  9. @eric

    in the java world, pass value to method … (this strategy more like your article “call by reference”, but it is called pass by value)

    Yes, in Java the terminology “by-value” is used. But the semantics is what is described in this article as “by-sharing”. As noticed above in the last paragraph terminology versions, both terminologies can be used: either “by-sharing” or “by-value when the value is the reference”.

  10. Thanks for your reply. I’m not read very careful, get mixed up “call by reference” and “call by sharing”. In your article, “call by reference” means pass the real object to method, and “call by sharing” ,means pass origin object address copy to method. really need more carefully.

  11. I just use the term “call-by-value” for this — whether the value being passed is the address of an object, or whether it’s an integer, it doesn’t matter. The run-time just copies the bit pattern into the local variable of the called routine. No decision making is needed by the run-time. Note that I don’t actually *know* how the run-time executor is written. But there’s just no need to do anything else.

    As far as the question whether any language allows the calling value to be changed, yeah, several (mostly archaic) languages do, such as Pascal. That concept *is* call-by-reference, and here’s the syntax in Pascal:

    procedure f(var arg1: integer);
        begin
            arg1 := 5;
        end;
    ...
    i = 10;
    f(i);
    // i now holds 5!
    

    The “var” key work means call-by-reference. Memory for arg1 is not allocated because it’s using the calling routine’s memory location.

  12. @M

    Yes, absolutely correct, if you treat as the “integer” address is passed, then you may name it “by-value” which is correct. Notice though, that runtime nevertheless should distinguish form “simple values” and “address values.

    Beside Pascal, C++ (as a sugar for dereferenced pointer) or PHP has references as well.

  13. Thanks Dmitry.
    I read all of these articles, they are very helpful and clear for whom learning javascript like me.