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

Read this article in: Russian.

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 some expressions in programming language. Strategy of passing arguments to function is its special case.

The reason for this article was the similar topic on one of forums in which result we have come to the most exact description of passing arguments strategy in ECMAScript. Also we gave corresponding definitions which possible to use on forums and discussions.

Many programmers are assured that objects in JavaScript (as well as in some other habitual languages) are passed in function by reference while values of primitive types are passed by value. Moreover, we can see this “fact” in various articles, discussions at forums and even books on JavaScript. However, how much exact is this terminology and how much true this description (and that is more important, how much true this understanding), we will consider in this article.

Briefly notice that in the general theory there are two kinds of evaluation strategy: strict, meaning that arguments are calculated before their application and non-strict, meaning, as a rule, calculation of arguments on demand when they are required (so-called “lazy” calculations).

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

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) strict strategy arguments passing 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.

Strict passing strategy is also subdivided into several strategies, most important of which we consider in detail.

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

This type of strategy is well-known by many programmers. The value of argument here is the copy of value of the object passed by the caller. The changes made to the parameter inside the function do not influence the passed object outside. Generally, there is an allocation of new memory (thus, concrete implementation in this discussion is not so essential — it can be a stack or dynamic memory) in which value of external object is copied, and exactly value from this new area of memory is used inside the function.

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 the big performance issue in a case when function argument is not primitive value, but complex structure, an object. That exactly occurs, for example, in C/C++ when the structure is passed by value to function — it is completely copied.

Let’s describe the general example which we will use at the description of the following evaluation strategies. Let abstract procedure accepts two arguments: value of an object and also a boolean flag, whether it is necessary to change the object’s value completely (to assign a new value), or just to change only properties of the object.

bar = {
  x: 10,
  y: 20
}
 
procedure foo(barArg, isFullChange):
 
  if isFullChange:
    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 turn by reference evaluation strategy (which is also well-known) receives not a value copy, but the implicit reference to object, i.e. the address directly related with object from the outside. Any change of parameter inside the function (assignment of new value or change of properties) is affected on object outside because the exact address of this object is related with formal parameter, i.e. an argument is kind of 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 effectively to pass complex objects, e.g. big structures with a considerable quantity of properties.

If first two strategies for the majority of programmers were always known, this strategy (and to be more exact, its discussing terminology) is not so widely spread. But, 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” has been offered and named first by Barbara Liskov for CLU programming language in 1974.

The main point 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 the fact that the concept of the reference in this case appears, this strategy should not be treated as call by reference (though, in this case the majority makes a mistake), because the value of the argument is not the direct alias, but the copy of the address.

The main difference consists that assignment of a new value to argument inside the function does not affect object outside (as it would be in case of call by reference). However, because formal parameter, having an address copy, gets access to the same object that is outside (i.e. the object from the outside completely was not copied as would be in case of call by value), changes of properties of local argument object — are reflected in the external object.

// 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 being used. As for other languages, there are alternative terminologies that can be used and which can often be confusing as they are in a contrary with the names in other strategies.

In most cases, for example, in Java, ECMAScript or Visual Basic, this strategy is also named as by value, meaning specific value — a reference copy.

On the one hand, it is true — assigning to argument inside a function only binds this name with new value (address) and does not influence external object.

On the other hand, such terminology crossing can really (if not to examine this question deeply) be treated incorrectly (what led to debate at forums how objects are passed to functions in JavaScript).

The general theory really has a special case of call by value with the specific value — the copy of address. Therefore these technologies do not break terminological rules.

In Ruby, this strategy is named by reference. Again, on one hand it is not passed as a copy of a big structure (i.e., seems, not by value), but on the other hand, we do not deal with the original reference to object and cannot change it; consequently, this crossing in terminology again can be confusing.

The general theory does not describe a special case of call by reference as it does for the specific case of call by value.

However, it is necessary to understand that in all mentioned technologies (Java, ECMAScript, Python, Ruby, other) the local terminology is used; actually — all they use strategy which in the general theory is named as call by sharing.

Regarding С/С++, this strategy is ideologically similar to passing by pointer values, but with one important difference — that it is possible to dereference the pointer and to change the object completely. But in general, assigning a value (address) to the pointer binds it with the new memory block (i.e. the memory block to which the pointer referenced before remains untouched); and changes of object properties referencing via the pointer — influences the external object.

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

However, С/С++ also has special “sugar” at referencing properties of objects without obvious pointer’s dereferencing:

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, deleting objects by GC. This data type even has the similar name — shared_ptr.

Now we know which evaluation strategy for passing objects as arguments is used in ECMAScript — call by sharing: changes of properties of the argument are affected outside, assignment of the new value — do not influence external object.

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) 

And assignment only binds an identifier with the new object (with the new address), but does not influence the (already previously) bound object as it would be in case of the reference:

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

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

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

Again, here all is related with that fact that variable values in case of object type are addresses, but not object structures themselves. Assignment of one variable to another — copies its value-reference; thus both variables references the same place in memory. The next assignment of the new value (the new address) unbinds a name from the old address and binds it with the new. 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 position, arguing in abstraction of ECMAScript, it is possible to say precisely and exactly that there is only “value” and, accordingly, only call by value.

However, to avoid misconceptions (why properties of external object can be changed inside a function), here it is already necessary to consider in detail the implementation level where we see call by sharing, or roughly speaking — “by safe pointer which is impossible to dereference and to change object completely, but is possible to change properties of this object”.

Let’s define versions of allowed terminology concerning this question in ECMAScript.

It can be either “call by value”, with specifying that the special case of call by value is meant when value is the address copy. From this position it is possible to say that all without an exception objects in ECMAScript are passed by value, that actually provides level of ECMAScript abstraction.

Or special for this case strategy — “call by sharing” which precisely allows seeing differences from classical call by value and from call by reference. In this case it is possible to divide passing types: primitive values are passed by value and objects — by sharing.

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

I hope this article has helped to understand in more details evaluation strategy in a whole, and also concerning this feature in ECMAScript. As always, I will be glad to answer your questions in 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

Tags: , , , , ,

 
 
 

16 Comments:

  1. Gravatar of Robert Polovsky Robert Polovsky
    11. April 2010 at 18:12

    Thank you Dmitry, very informing article.


  2. Gravatar of denisdeng denisdeng
    16. April 2010 at 11:57

    thank you!


  3. Gravatar of Yonatan Yonatan
    2. May 2010 at 15:30

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


  4. Gravatar of Morten Krogh Morten Krogh
    23. January 2011 at 23:31

    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.


  5. Gravatar of Dmitry A. Soshnikov Dmitry A. Soshnikov
    24. January 2011 at 13:10

    @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.


  6. Gravatar of Morten Krogh Morten Krogh
    25. January 2011 at 16:19

    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.


  7. Gravatar of Dmitry A. Soshnikov Dmitry A. Soshnikov
    25. January 2011 at 16:56

    @Morten Krogh

    Nope, I’m not aware about such a language. Seems, it wouldn’t be very useful ;)

    Dmitry.


  8. Gravatar of eric eric
    23. September 2011 at 12:04

    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).


  9. Gravatar of eric eric
    23. September 2011 at 12:09

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


  10. Gravatar of eric eric
    23. September 2011 at 12:14

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

    Java can call by sharing?I doubt…

    http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html


  11. Gravatar of eric eric
    23. September 2011 at 12:44

    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


  12. Gravatar of Dmitry A. Soshnikov Dmitry A. Soshnikov
    24. September 2011 at 17:51

    @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”.


  13. Gravatar of eric eric
    25. September 2011 at 08:50

    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.


  14. Gravatar of M M
    20. May 2012 at 08:34

    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.


  15. Gravatar of Dmitry Soshnikov Dmitry Soshnikov
    25. May 2012 at 21:03

    @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.


  16. Gravatar of lail3344 lail3344
    26. November 2012 at 18:46

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


Leave a Reply

Code: For code you can use tags [js], [text], [ruby] and other.

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>