introduce
In this chapter, we will explain the strategy of passing parameters to function functions in ECMAScript.
In computer science, this strategy is generally called "evaluation strategy" (Uncle's note: Some people say it is translated into evaluation strategy, while others translate it into assignment strategy. Looking at the following content, I think it is more appropriate to call it an assignment strategy. Anyway, the title should be written as an evaluation strategy that is easy for everyone to understand). For example, in programming languages, setting rules for evaluation or calculation expressions. A strategy for passing parameters to a function is a special case.
http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluation-strategy/
The reason for writing this article is that someone on the forum asked to accurately explain some strategies for passing parameters. We have given the corresponding definition here, hoping it will be helpful to everyone.
Many programmers are convinced that in JavaScript (even in some other languages), objects are passed by reference, while the original value type is passed by values. In addition, many articles talk about this "fact", but many people really understand this term, and how many are correct? We will explain it one by one in this article.
General Theory
It should be noted that in the assignment theory, there are generally two assignment strategies: strict - means that the parameters are calculated before entering the program; non-strict - means that the calculation of the parameters is calculated based on the calculation requirements (that is, it is equivalent to delay calculation).
Then, here we consider the basic function parameter transfer strategy, which is very important from the starting point of ECMAScript. The first thing to note is that strict parameter passing strategies are used in ECMAScript (even in other languages such as C, JAVA, Python and Ruby).
In addition, the calculation order of the passed parameters is also very important - in ECMAScript, it is left to right, and the introductory order (does from right to right) implemented in other languages can also be used.
Strict transmission strategies are also divided into several seed strategies, and we will discuss the most important strategies in detail in this chapter.
Not all the strategies discussed below are used in ECMAScript, so when discussing the specific behavior of these strategies, we used pseudo-code to show it.
Pass by value
Passing by value, many developers are well aware that the value of the parameter is a copy of the object value passed by the caller. Changing the value of the parameter inside the function will not affect the outside object (the value of the parameter outside). Generally speaking, new memory is re-allocated (we do not pay attention to how allocated memory is implemented - it is also the stack or dynamic memory allocation). The value of the new memory block is a copy of the external object, and its value is used inside the function.
The code copy is as follows:
bar = 10
procedure foo(barArg):
barArg = 20;
end
foo(bar)
// Change the value inside foo will not affect the value of the internal bar
print(bar) // 10
However, if the parameters of the function are not the original value but a complex structural object, it will bring great performance problems. C++ has this problem. When passing the structure as a value into the function, it is a complete copy.
Let's give a general example, use the following assignment strategy to test it. Think about a function that accepts 2 parameters. The first parameter is the value of the object, and the second is a Boolean mark to mark whether the object is completely modified (reassigning the object to the object), or only some properties of the object are modified.
The code copy is as follows:
// Note: The following are all pseudo-code, not JS implementation
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)
// Pass by value, external objects will not be changed
print(bar) // {x: 10, y: 20}
// Completely change the object (assign a new value)
foo(bar, true)
//No change either
print(bar) // {x: 10, y: 20}, instead of {z: 1, q: 2}
Pass by reference
Another well-known pass-by-reference is not a value copy, but an implicit reference to the object, such as the direct reference address of the object outside. Any changes to parameters inside the function affect the value of the object outside the function, because both reference the same object, that is, the parameter is equivalent to an alias of the external object.
pseudocode:
The code copy is as follows:
procedure foo(barArg, isFullChange):
if isFullChange:
barArg = {z: 1, q: 2}
exit
end
barArg.x = 100
barArg.y = 200
end
// Use the same object as above
bar = {
x: 10,
y: 20
}
// The result of the call by reference is as follows:
foo(bar)
// The property value of the object has been changed
print(bar) // {x: 100, y: 200}
// Reassigning new values also affects the object
foo(bar, true)
// This object is already a new object
print(bar) // {z: 1, q: 2}
This strategy can more efficiently pass complex objects, such as large structure objects with large batches of attributes.
Call by sharing
Everyone knows the above two strategies, but you may not know the strategy you want to talk about here (actually, it is an academic strategy). However, we will soon see that this is exactly the strategy that plays a key role in the parameter delivery strategy in ECMAScript.
This strategy also has some synonyms: "pass by object" or "pass by object share".
This strategy was proposed in 1974 by Barbara Liskov for the CLU programming language.
The key point of this strategy is that the function receives a copy (copy) of the object, and the reference copy is associated with the formal parameters and its values.
We cannot call the references that appear here "pass by reference", because the parameters received by the function are not direct object alias, but a copy of the reference address.
The most important difference is that the function reassigns new values to the parameter inside will not affect the external object (as the case passed by reference in the above example), but because the parameter is an address copy, the same object accessed outside and inside (for example, the external object is not a complete copy as you want to pass by the value). Changing the attribute value of the parameter object will affect the external object.
The code copy is as follows:
procedure foo(barArg, isFullChange):
if isFullChange:
barArg = {z: 1, q: 2}
exit
end
barArg.x = 100
barArg.y = 200
end
// Still use this object structure
bar = {
x: 10,
y: 20
}
// Passing by contribution will affect the object
foo(bar)
// The object's properties have been modified
print(bar) // {x: 100, y: 200}
// Reassignment does not work
foo(bar, true)
// Still the above value
print(bar) // {x: 100, y: 200}
The assumption of this processing is that the objects used in most languages are not the original values.
Pass by share is a special case of passing by value
The strategy of delivering by sharing is used in many languages: Java, ECMAScript, Python, Ruby, Visual Basic, etc. In addition, the Python community has used this term, and it can be used in other languages, as other names tend to make people feel confused. In most cases, such as in Java, ECMAScript, or Visual Basic, this strategy is also called pass-by-value - meaning: special value - reference copy (copy).
On the one hand, it is like this - the parameters passed to the function are just a name of the bound value (reference address) and will not affect the external object.
On the other hand, these terms are really considered to eat wrong without delving into it, as many forums are talking about how to pass objects to JavaScript functions).
There is indeed a saying in general theory that passes by value: but at this time this value is what we call the address copy (copy), so it does not break the rules.
In Ruby, this strategy is called pass by reference. Let me say again: it is not passed in terms of a copy of a large structure (for example, not by value), and on the other hand, we do not process references to the original object and cannot modify it; therefore, this concept of cross-term terms may be more confusing.
There is no special case passed by reference in theory like a special case passed by value.
But it is still necessary to understand these strategies in the technologies mentioned above (Java, ECMAScript, Python, Ruby, other). In fact, the strategy they use is to pass by sharing.
Press Share and Pointer
For С/С++, this strategy is the same as passing by pointer value, but there is an important difference - this strategy can dereference pointers and completely change objects. However, in general, allocating a value (address) pointer to a new memory block (that is, the memory block referenced previously remains unchanged); changing the object properties through a pointer will affect the external object of Adong.
So, and pointer categories, we can clearly see that this is passed by address value. In this case, passing by by sharing is just "synthetic sugar", like pointer assignment behavior (but not dereference), or modifying properties like references (no dereference operation required), sometimes it can be named "safe pointer".
However, С/С+ + also has special syntactic sugar when referring to object properties without dereferences of obvious pointers:
The code copy is as follows:
obj->x instead of (*obj).x
This ideology that is most closely related to C++ can be seen from the implementation of "smart pointers". For example, in boost:: shared_ptr, the assignment operator and copy constructor are overloaded, and the object's reference counter is also used to delete the object through GC. This data type even has a similar name - Share_ptr.
ECMAScript implementation
Now we know the policy of passing objects as parameters in ECMAScript - Pass by Share: Modifying the properties of the parameter will affect the outside, while reassigning the value will not affect the external object. However, as we mentioned above, the ECMAScript developers generally call it: pass by value, except that the value is a copy of the reference address.
JavaScript inventor Brendan Ashe also wrote: What is passed is a copy of the reference (a copy of the address). So what everyone mentioned in the forum said is also correct under this explanation.
More precisely, this behavior can be understood as a simple assignment. We can see that the internals are completely different objects, but the same value is referenced - that is, the address copy.
ECMAScript code:
The code copy is as follows:
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]
That is, two identifiers (name binding) are bound to the same object in memory, and share this object:
foo value: addr(0xFF) => {x: 100, y: 200} (address 0xFF) <= bar value: addr(0xFF)
For reassignment, the binding is a new object identifier (new address) without affecting the object that has been previously bound:
The code copy is as follows:
bar = {z: 1, q: 2};
alert([foo.x, foo.y]); // [100, 200] No change
alert([bar.z, bar.q]); // [1, 2] But now the reference is a new object
That is, foo and bar now have different values and different addresses:
The code copy is as follows:
foo value: addr(0xFF) => {x: 100, y: 200} (address 0xFF)
bar value: addr(0xFA) => {z: 1, q: 2} (address 0xFA)
Let me emphasize again that the value of the object mentioned here is the address, not the object structure itself, and assigning the variable to another variable - a reference to the assigned value. Therefore, the two variables refer to the same memory address. The next assignment is the new address, which is to resolve the binding to the address of the old object and then bind to the address of the new object. This is the most important difference between passing by reference.
In addition, if we only consider the abstraction level provided by the ECMA-262 standard, we only see the concept of "value" in the algorithm, which implements the passed "value" (can be the original value or the object), but according to our above definition, it can also be completely called "passed by value" because the reference address is also a value.
However, in order to avoid misunderstanding (why the properties of external objects can be changed inside the function), the implementation level details still need to be considered here - what we see is passed by sharing, or in other words - passed by a safe pointer, which cannot be dereferenced and changed the object, but the property value of the object can be modified.
Term Version
Let's define the term version of this policy in ECMAScript.
It can be called "pass by value" - the value mentioned here is a special case, that is, the value is an address copy. From this level we can say: objects in ECMAScript except exceptions are passed by value, which is actually the level of ECMAScript abstraction.
Or in this case, it is specifically called "passed by sharing". Through this, you can see the difference between traditional passing by value and passing by reference. This situation can be divided into two situations: 1: the original value is passed by value; 2: the object is passed by sharing.
The phrase "to function by referencing types" has nothing to do with ECMAScript, and it is wrong.
in conclusion
I hope this post helps to learn more details macro-level and implementation in ECMAScript. As always, if there are any questions, please feel free to discuss it.