ColdFusion 9.0 Resources |
Working with arguments and variables in functionsContents [Hide]Good argument naming practiceUse an argument name that represents its use. For example, the following code is unlikely to result in confusion: <cfscript> function SumN(Addend1,Addend2) { return Addend1 + Addend2; } </cfscript> <cfset x = 10> <cfset y = 12> <cfoutput>#SumN(x,y)#</cfoutput> The following, similar code is more likely to result in programming errors: <cfscript> function SumN(x,y) { return x + y; } </cfscript> <cfset x = 10> <cfset y = 12> <cfoutput>#SumN(x,y)#<cfoutput> Passing argumentsColdFusion passes the following data types to the function by value:
As a result, any changes that you make in the function to these arguments do not affect the variable that was used to call the function, even if the calling code is on the same ColdFusion page as the function definition. ColdFusion passes queries, structures, and external objects such as COM objects into the function by reference. As a result, any changes to these arguments in the function also change the value of the variable in the calling code. For an example of the effects of passing arguments, see Passing complex data. Passing complex dataStructures, queries, and complex objects such as COM objects are passed to UDFs by reference, so the function uses the same copy of the data as the caller. Arrays are passed to user-defined functions by value, so the function gets a new copy of the array data, and the array in the calling page is unchanged by the function. As a result, always handle arrays differently from all other complex data types. Passing structures, queries, and objectsFor your function to modify the copy of a structure, query, or object, in the caller, pass the variable as an argument. Because the function gets a reference to the structure in the caller, the caller variable reflects all changes in the function. You do not have to return the structure to the caller. After the function returns, the calling page accesses the changed data by using the structure variable that it passed to the function. If you do not want a function to modify the copy of a structure, query, or object, in the caller, use the Duplicate function to make a copy and pass the copy to the function. Passing arraysIf you want your function to modify the caller’s copy of the array, the simplest solution is to pass the array to the function and return the changed array to the caller in the function return statement. In the caller, use the same variable name in the function argument and return variable. The following example shows how to directly pass and return arrays. In this example, the doubleOneDArray function doubles the value of each element in a one-dimensional array. <cfscript> //Initialize some variables //This creates a simple array. a=ArrayNew(1); a[1]=2; a[2]=22; //Define the function. function doubleOneDArray(OneDArray) { var i = 0; for ( i = 1; i LE arrayLen(OneDArray); i = i + 1) { OneDArray[i] = OneDArray[i] * 2; } return OneDArray; } //Call the function. a = doubleOneDArray(a); </cfscript> <cfdump var="#a#"> This solution is simple, but it is not always optimal:
If you do not use the return statement to return the array to the caller, you can pass the array as an element in a structure and change the array values inside the structure. Then the calling page can access the changed data by using the structure variable it passed to the UDF. The following code shows how to rewrite the previous example using an array in a structure. It returns True as a status indicator to the calling page and uses the structure to pass the array data back to the calling page. <cfscript> //Initialize some variables. //This creates a simple array as an element in a structure. arrayStruct=StructNew(); arrayStruct.Array=ArrayNew(1); arrayStruct.Array[1]=2; arrayStruct.Array[2]=22; //Define the function. function doubleOneDArrayS(OneDArrayStruct) { var i = 0; for ( i = 1; i LE arrayLen(OneDArrayStruct.Array); i = i + 1) { OneDArrayStruct.Array[i] = OneDArrayStruct.Array[i] * 2; } return True; } //Call the function. Status = doubleOneDArrayS(arrayStruct); WriteOutput("Status: " & Status); </cfscript> </br> <cfdump var="#arrayStruct#"> Use the same structure element name for the array (in this case Array) in the calling page and the function. About the Arguments scopeAll function arguments exist in their own scope, the Arguments scope. The Arguments scope exists for the life of a function call. When the function returns, the scope and its variables are destroyed. However, destroying the Argument scope does not destroy variables, such as structures or query objects, that ColdFusion passes to the function by reference. The variables on the calling page that you use as function arguments continue to exist; if the function changes the argument value, the variable in the calling page reflects the changed value. The Arguments scope is special, in that you can treat the scope as either an array or a structure. This dual nature of the Arguments scope is useful because it makes it easy to use arguments in any of the following circumstances:
The contents of the Arguments scopeThe following rules apply to the Arguments scope and its contents:
The following example shows these rules. Assume that you have a function declared, as follows: <cffunction name="TestFunction"> <cfargument name="Arg1"> <cfargument name="Arg2"> </cffunction> You can call this function with a single argument, as in the following line: <cfset TestFunction(1)> The resulting Arguments scope looks like the following:
In this example, the following functions return the value 2 because the scope contains two defined arguments: ArrayLen(Arguments) StructCount(Arguments) However, the following tests return the value false, because the contents of the second element in the Arguments scope is undefined. Isdefined("Arguments.Arg2") testArg2 = Arguments[2]> Isdefined("testArg2") Note: The IsDefined function does not
test the existence of array elements. Instead, place any code that
could access an undefined array element in a try block and use a
catch block to handle exceptions that arise if elements do not exist.
Using the Arguments scope as an arrayThe following rules apply to referencing Arguments scope as an array:
To demonstrate these rules, define a simple function that displays the contents of its Arguments array and call the function with various argument combinations, as the following example shows: <cffunction name="TestFunction" > <cfargument name="Arg1"> <cfargument name="Arg2"> <cfloop index="i" from="1" to="#ArrayLen(Arguments)#"> <cfoutput>Argument #i#: #Arguments[i]#<br></cfoutput> </cfloop> </cffunction> <strong>One Unnamed argument</strong><br> <cfset TestFunction(1)> <strong>Two Unnamed arguments</strong><br> <cfset TestFunction(1, 2)> <strong>Three Unnamed arguments</strong><br> <cfset TestFunction(1, 2, 3)> <strong>Arg1:</strong><br> <cfset TestFunction(Arg1=8)> <strong>Arg2:</strong><br> <cfset TestFunction(Arg2=9)> <strong>Arg1=8, Arg2=9:</strong><br> <cfset TestFunction(Arg1=8, Arg2=9)> <strong>Arg2=6, Arg1=7</strong><br> <cfset TestFunction(Arg2=6, Arg1=7)> <strong>Arg1=8, Arg2=9, Arg3=10:</strong><br> <cfset TestFunction(Arg1=8, Arg2=9, Arg3=10)> <strong>Arg2=6, Arg3=99, Arg1=7</strong><br> <cfset TestFunction(Arg2=6, Arg3=99, Arg1=7)> Note: Although you can use the Arguments scope as
an array, the IsArray(Arguments) function always
returns false and the cfdump tag displays the scope
as a structure.
Using the Arguments scope as a structureThe following rule applies when referencing Arguments scope as a structure:
Using the Arguments scope in CFScriptA function can have optional arguments that you do not have to specify when you call the function. To determine the number of arguments passed to the function, use the following function: ArrayLen(Arguments) When you define a function using CFScript, the function must use the Arguments scope to retrieve the optional arguments. For example, the following SumN function adds two or more numbers together. It requires two arguments and supports any number of additional optional arguments. You can reference the first two, required, arguments as Arg1 and Arg2 or as Arguments[1] and Arguments[2]. Access the third, fourth, and any additional optional arguments as Arguments[3], Arguments[4], and so on function SumN(Arg1,Arg2) { var arg_count = ArrayLen(Arguments); var sum = 0; var i = 0; for( i = 1 ; i LTE arg_count; i = i + 1 ) { sum = sum + Arguments[i]; } return sum; } With this function, any of the following function calls are valid: SumN(Value1, Value2) SumN(Value1, Value2, Value3) SumN(Value1, Value2, Value3, Value4) and so on. The code never uses the Arg1 and Arg2 argument variables directly, because their values are always the first two elements in the Arguments array and it is simpler to step through the array. Specifying Arg1 and Arg2 in the function definition ensures that ColdFusion generates an error if you pass the function one or no arguments. Note: Avoid referring to a required argument in the
body of a function by both the argument name and its place in the
Arguments scope array or structure, as doing so can be confusing
and makes it easier to introduce errors.
Using the Arguments scope in cffunction definitionsWhen you define a function using the cffunction tag, you generally reference the arguments directly by name if all arguments are named in the cfargument tags. If you do use the Arguments scope identifier, follow the rules listed in About the Arguments scope. For more information on using the Arguments scope in functions defined using CFScript, see Using the Arguments scope in CFScript. Function-only variablesIn addition to the Arguments scope, each function can have variables that exist only inside the function, and are not saved between times the function gets called. As soon as the function exits, all the variables in this scope are removed. In CFScript, you create function-only variables with the var statement. Unlike other variables, you never prefix function-only variables with a scope name. Using function-only variablesMake sure to use the var statement in CFScript UDFs to declare all function-specific variables, such as loop indexes and temporary variables that are required only for the duration of the function call. Doing so ensures that these variables are available inside the function only, and makes sure that the variable names do not conflict with the names of variables in other scopes. If the calling page has variables of the same name, the two variables are independent and do not affect each other. For example, if a ColdFusion page has a cfloop tag with an index variable i, and the tag body calls a CFScript UDF that also has a loop with a function-only index variable i, the UDF does not change the value of the calling page loop index, and the calling page does not change the UDF index. So you can safely call the function inside the cfloop tag body. In general, use the var statement to declare all UDF variables, other than the function arguments or shared-scope variables, that you use only inside CFScript functions. Use another scope, however, if the value of the variable must persist between function calls; for example, for a counter that the function increments each time it is called. Referencing caller variablesA function can use and change any variable that is available in the calling page, including variables in the caller’s Variables (local) scope, as if the function was part of the calling page. For example, if you know that the calling page has a local variable called Customer_name (and no function scope variable named Customer_name exists) the function can read and change the variable by referring to it as Customer_name or (using better coding practice) Variables.Customer_name. Similarly, you can create a local variable inside a function and then use it anywhere in the calling page after the function call. You cannot use the variable before you call the function. However, generally avoid using the caller’s variables directly inside a function. Using the caller’s variables creates a dependency on the caller. Ensure that the code outside the function uses the same variable names as the function. Doing so can become difficult if you call the function from many pages. You can avoid these problems by using only the function arguments and the return value to pass data between the caller and the function. Do not reference calling page variables directly in the function. As a result, you can use the function anywhere in an application (or even in multiple applications), without concern for the calling code variables. As with other programming practices, valid exceptions to this recommendation exist. For example, you can do any of the following:
Note: If your function must directly change a simple
variable in the caller (one that is not passed to the function by
reference), you can place the variable inside a structure argument.
Using argumentsFunction arguments can have the same names, but different values, as variables in the caller. Avoid such uses for clarity, however. The following rules apply to argument persistence:
Note: If a function must use a variable from another
scope that has the same name as a function-only variable, prefix
the external variable with its scope identifier, such as Variables
or Form. (However, remember that using variables from other scopes directly
in your code is often poor practice.)
|