152 | Chapter 7: Variables 152 | Chapter 7: Variables
152 | Chapter 7: Variables 152 | Chapter 7: Variables
Inside the f() function, the b variable, which is now a Node.js array, can have its values, such as b[0], modified and those modifications will be reflected in the a array variable passed as an argument. If the b variable is a copy of the a variable, how can a change to the b variable affect the a variable?
Node.js copies values using shallow copying. Shallow copying means that for composite variables such as arrays and objects, which are collections of multiple values, the lan‐ guage only copies the single value that points to the composite variable. It is still a copy, though, because the variable itself can be changed to point to a different composite object or assigned a simple value. But with a shallow copy, both the function argument and the function parameter point to the same object.
Deep copying is the opposite of shallow copying. Deep copying means that for composite variables such as arrays and objects, which are collections of multiple values, the lan‐ guage traverses the entire structure and copies all its parts. PHP copies values using deep copying.
When the Node.js code for simulating passing by reference is converted to PHP, it does not work:
$a = 'global' ; function f ( $b ) { $b [ 0 ] = 'local' ; } $a = array ( $a ); f ( $a ); $a = $a [ 0 ]; print $a ; // prints "global"
The $a variable has the value 'global' instead of 'local' after the f() function call. It does not work because, when the f() function is called, the $a variable is copied to the $b variable using deep copying, instead of shallow copying that is used in Node.js. The $b variable does not point to the same array that the $a variable does; it points to
a new copy of the array. Simulating passing by reference relies on shallow copying. In a lot of code, it makes no difference that PHP does deep copying and Node.js does
shallow copying. It is still important to understand the difference, though, so that you can address those relatively rare situations where some PHP code changes an object and relies on deep copying to keep copies of the object unchanged. It is recommended that these situations be eliminated using refactoring although they can also be addressed by explicitly copying the contents of Node.js variables that would otherwise be shared.
Scope | 153
To convert PHP functions that use passing by reference to Node.js, execute the following find-and-replace action. At each occurrence, this action heavily relies on the developer to manually find and change related Node.js code that is not addressed by the Replace field:
Operation: "Find/Replace" in Eclipse PDT Find: &\$? Replace: Options: Regular expressions, Wrap search Action: Find, then Replace/Find
At each occurrence, follow these three steps:
1. Inside the function, add a [0] suffix to each use of the function parameter passed by reference. For example, inside the f() function, replace every use of the a and c variables passed by reference with a a[0] or c[0]. Do not replace the b variable because it is passed by value:
function f ( & a , b , & c ) { if ( a [ 0 ] > 10 ) { // a becomes a[0] a [ 0 ] += 10 ; // a becomes a[0] if ( b < 3 ) { c [ 0 ] = 100 ; // c becomes c[0]
} elseif ( c [ 0 ] > 30 ) { // c becomes c[0] c [ 0 ] = a [ 0 ] * b ; ? // a becomes a[0] and c becomes c[0] } } }
2. Find every function call in every page to the function where a function parameter is passed by reference. Before the function call, pack any function arguments that are passed by reference into an array. After the function call, unpack any function arguments that are passed by reference from the array. For example, the first and third function parameters are passed by value, so put the x and z variables into one- value arrays, call the f() function, then remove the x and z variables from the arrays:
var x = 0 ; var y = 10 ; var z = 100 ; x = [ x ]; z = [ z ];
f ( x , y , z ); x = x [ 0 ]; z = z [ 0 ];
3. Remove the ampersand (&), and if it still exists, the dollar sign ($) from the function parameters. For example, the f() function still uses the ampersand (&) for the a and c variables:
function f ( & a , b , & c ) {
154 | Chapter 7: Variables
Remove the ampersand (&) from the a and c variables so all variables are passed by value:
function f ( a , b , c ) {
Although PHP function parameters are not often passed by reference, it is useful to know how to convert them to Node.js.
PHP functions are more sophisticated than Node.js functions. Besides building in sup‐ port for passing by reference for parameters into the language, PHP also provides spe‐ cific support for default argument values.
A default argument value is the value that the parameter takes if the function call does not provide one. For example, the $c function parameter will take the value 'too little' if a function call is made with two arguments, instead of three:
function f ( $a , $b , $c = 'too little' ) {
... } f ( 10 , 100 , 'too much' ); // 3 arguments so default argument value not used f ( 5 , 50 ); // 2 arguments so $c takes the 'too little' value
Unlike PHP, Node.js does not provide specific support for default argument values. However, Node.js does allow that function calls have a different number of arguments than the number of function parameters. If fewer arguments are provided than function parameters, the extra parameters take on the undefined type with the undefined value. If more arguments are provided than function parameters, the extra arguments are ignored.
The missing arguments can be detected by testing for the undefined type and then resetting the value of the argument to the desired default value. The following Node.js code implements the f() function from the previous PHP code, including the c default argument value:
function f ( a , b , c ) { c = (( typeof c ) != 'undefined' ) ? c : 'too little' );
f ( 10 , 100 , 'too much' ); // 3 arguments so c is set to 'too much' f ( 5 , 50 ); // only 2 arguments so c is set to 'too little'
The use of the typeof operator to implement default operators is very accurate, but there is a more common Node.js idiom that is not as accurate. The following Node.js code is more common:
function f ( a , b , c ) { c = c || 'too little' ; ... }
Scope | 155
The “or” operator (||) returns the left value, if the left value evaluates to true; otherwise, it returns the right value. Most values evaluate to true; only a few values evaluate to false. Besides the boolean value (false), other values that evaluate to false are the undefined value of the undefined type, zero, the empty string, null, and NaN (“Not a Number”).
The difference between implementing the default argument value using the typeof keyword and using the “or” operator (||) idiom is that the “or” operator (||) would replace the empty string into the 'too little' default argument value. This Node.js example illustrates the difference:
var d = '' ; f ( 5 , 50 , d );
If the typeof keyword is used, the c function parameter would be kept as the empty string. If the “or” operator (||) approach is used, the c function parameter would be reset to the 'too little' value. In many cases, both approaches would do the same thing. As long as a zero, the empty string, a null, or a NaN is never passed as a function argument to a function parameter that had a different default argument value, both approaches would work just as well.
To convert default argument values from PHP to Node.js, use the following find-and- replace action to visit all functions:
Operation: "Find/Replace" in Eclipse PDT Find: function Replace: Options: Wrap search Action: Find, then Replace/Find
At each occurrence, identify any default argument values and add a statement similar to the following, but replace 'param' with the actual name of the function parameter and replace the empty string with the actual default value:
param = (( typeof param ) != 'undefined' ) ? param : '' );
Remove the default argument value from the function parameters in the parameters declaration.
If a PHP default argument value is left in the code, Node.js will exit with a stack trace.
A stack trace is also a good way to detect unconverted default argument values. This chapter described how to convert simple variables, like strings and arrays, with
their most common manipulations and in their most common contexts from PHP into Node.js. PHP also supports complex variables that are created from PHP classes. The next chapter is devoted to explaining how to convert PHP classes into the equivalent Node.js code.
156 | Chapter 7: Variables
CHAPTER 8
Classes
All languages, including PHP and Node.js, have a set of fundamental variable types, such as booleans, numbers, and strings. For PHP and Node.js, these fundamental types were described in detail in the previous chapter. Variables can be created of these fun‐ damental types. Many languages, including PHP and Node.js, also allow variables and functions to be combined together into a single more complex variable.
PHP uses what is called a traditional class-based object system. In the PHP class-based object system, a PHP class is a type that is defined by the user. Like a fundamental type, such as a boolean or a number, a type is not an actual variable; it is a kind of variable.
A PHP object is an actual variable that was created from a PHP class. Node.js does not use a traditional class-based object system; it uses a prototype-based
system. In a prototype-based system, objects share a common object, called a prototype, which can provide variables and functions to be used by all objects that use that proto‐ type. A Node.js object is an actual variable. A Node.js prototype object, or just called a “prototype,” is a Node.js object shared by multiple Node.js variables.