関数の変数
ユーザー定義関数の作成
UDF を作成する前に、UDF を定義する場所や、関数の作成に CFML を使用するか CFScript を使用するかを決定します。
ユーザー定義関数の作成場所の決定
関数は、次の場所で定義できます。
- ColdFusion コンポーネント。ColdFusion コンポーネントで定義した関数は、ColdFusion コンポーネントの使用の説明に従って使用します。
- 関数を呼び出すページ。ページ内の関数呼び出しより下の場所で関数を定義することもできますが、これはよいコーディングスタイルとはいえず、コードが読みにくくなります。
- cfinclude タグを使用してインクルードするページ。この cfinclude タグは、関数を呼び出す前に実行する必要があります。例えば、アプリケーションのすべての関数を単一のページで定義しておき、その関数を使用するページの先頭で cfinclude タグを使用することができます。
- 任意のページの、呼び出しページからアクセス可能な特定のスコープ。UDF スコープについて詳しくは、UDF の効果的な使用方法の関数のスコープの指定を参照してください。
- Application.cfc または Application.cfm ページ。詳しくは、ColdFusion アプリケーションの設計と最適化を参照してください。
関数を定義する場所の選択方法については、UDF の効果的な使用方法の Application.cfm および関数インクルードファイルの使用と関数のスコープの指定の節を参照してください。
CFScript による関数の作成について
CFScript で関数を定義するには、function ステートメントを使用します。CFScript での関数の定義には、次の特徴と制限があります。
- JavaScript や多くのプログラミング言語に近いシンタックスで関数を定義できます。
- CFScript は、式や条件操作などのビジネスロジックを効率的に記述できます。
- CFScript 関数の定義には、CFML タグを含めることはできません。
2 の累乗を返す関数を CFScript で定義する例を次に示します。
function twoPower(exponent) { return 2^exponent; } </cfscript>
CFScript を使用した関数の定義方法について詳しくは、CFScript でのコンポーネントと関数の定義を参照してください。
CFScript での関数の定義
CFScript の関数は、JavaScript の関数に似た方法で定義できます。単一の CFScript ブロックで複数の関数を定義できます。
CFScript の使用について詳しくは、CFML スクリプト言語による ColdFusion ページの拡張を参照してください。
CFScript 関数の定義のシンタックス
CFScript 関数を定義するシンタックスは次のとおりです。
{ CFScript Statements }
次の表に、関数の変数を示します。
|
説明 |
---|---|
functionName |
関数の名前。標準の ColdFusion 関数の名前や「cf」で始まる名前は使用できません。同じ名前を使用して 2 つの異なる関数を定義することはできません。関数名にピリオドを含めることはできません。 |
argName1... |
関数で必要な引数の名前。関数に渡す引数の数は、関数定義冒頭の括弧内にある引数の数以上である必要があります。必須の引数を呼び出しページで省略した場合は、引数の数が一致しないことを示すエラーが発生します。 |
関数定義の本文は、空であっても中括弧で囲む必要があります。
次の 2 つのステートメントは、関数定義内でのみ使用できます。
ステートメント |
説明 |
---|---|
var variableName = expression; |
関数のローカル変数(関数の変数)を作成および初期化します。この変数は、関数の内部でのみ意味を持ちます。同じ関数を次に呼び出しても、前回の値は保存されていません。関数本文の中では、他のスコープに同名の変数があっても、関数の変数が優先されます。関数の変数にはスコープ識別子の接頭辞を付けません。変数名にピリオドを含めることはできません。変数の初期値は、expression の評価結果です。expression には、定数や別の UDF などで構成される、任意の有効な ColdFusion 式を指定できます。すべての var ステートメントは、関数宣言の先頭(その他のすべてのステートメントの前)に配置する必要があります。変数を宣言するときには、初期化をおこないます。引数と同じ名前の変数を定義することはできません。1 つの var ステートメントで初期化できる変数は 1 つのみです。関数内でのみ使用する変数(ループカウンタや一時変数など)は、すべて var ステートメントを使用して初期化してください。 |
return expression; |
expression(変数など)を評価し、関数を呼び出したページにその値を返し、関数を終了します。ColdFusion の任意の変数型を返すことができます。 |
簡単な CFScript の例
次の関数の例では、2 つの引数を加算して、その結果を返します。
function Sum(a,b) { var sum = a + b; return sum; } </cfscript>
この例では、1 行で関数の変数を宣言し、式を使用して戻り値を設定しています。次のようにこの関数を単純化して、関数の変数を使用しないようにすることも可能です。
function MySum(a,b) {Return a + b;}
関数定義の本文は、ステートメントが 1 つであっても、中括弧で囲む必要があります。
関数の引数は、関数のローカルスコープにはコピーされません。スコープが指定されていない変数が呼び出された場合は、最初に引数スコープ、次にローカルスコープが検索されます。
タグによる関数の作成について
CFML で UDF を定義するには、 cffunction タグを使用します。この cffunction タグのシンタックスには、次の特長と制限事項があります。
- このシンタックスは、スクリプティングやプログラミングの経験はないが CFML や HTML の経験はある開発者にとってわかりやすいものになっています。
- 任意の ColdFusion タグを関数定義に含めることができます。これによって、例えば、データベースにアクセスする関数を作成できます。
- CFScript コードを関数定義に埋め込むことができます。
- この cffunction タグの属性を使用すれば、関数を実行できるユーザーを制限したり、関数にアクセス可能な方法を指定したりすることができます。
次のコードでは、 cffunction タグを使用して、累乗の関数を定義します。
<cfargument name="exponent"> <cfreturn 2^exponent> </cffunction>
この cffunction タグを使用した関数の定義について詳しくは、CFScript でのコンポーネントと関数の定義を参照してください。
cffunction タグによる関数の定義
cffunction タグと cfargument タグを使用すると、CFScript を使用せずに CFML で関数を定義できます。
ColdFusion コンポーネントについては、ColdFusion コンポーネントの構築と使用を参照してください。cffunction タグについて詳しくは、『CFML リファレンス』を参照してください。
cffunction タグの関数定義の形式
cffunction タグでは、次の形式を使用して関数を定義します。
access="accessType" output="Boolean"]> <cfargument name="argumentName" [Type="type" required="Boolean" default="defaultValue">] <!--- Function body code goes here. ---> <cfreturn expression> </cffunction>
角括弧([])はオプションの引数を示します。 cfargument タグの数に制限はありません。
cffunction タグでは、この関数を呼び出すときに使用する名前を指定します。次の表に示すように、オプションで関数の他の特性を指定することもできます。
属性 |
説明 |
---|---|
name |
関数名。 |
returnType |
(オプション)関数が返すデータの型。有効な標準の型名は、any、array、binary、Boolean、date、guid、numeric、query、string、struct、uuid、variableName、xml、および void です。他の名前を指定する場合は、その名前を持つ ColdFusion コンポーネントを引数として使用する必要があります。ここで指定した型に自動変換できない型のデータを関数が返そうとすると、エラーが発生します。例えば、関数が数値の計算結果を返す場合、文字列または数値の returnType 属性は有効ですが、配列は無効です。 |
roles |
(オプション)このメソッドを実行できるセキュリティロールのカンマ区切りのリスト。この属性を省略すると、この関数へのユーザーのアクセスが制限されません。この属性を使用すると、現在のユーザーが cfloginuser タグを使用してログインしており、この属性で指定されているいずれかのロールのメンバーである場合にのみ、関数が実行されます。それ以外の場合は、承認されていないアクセスの例外が発生します。ユーザーセキュリティについて詳しくは、アプリケーションの保護を参照してください。 |
output |
(オプション)関数本文内の表示可能な出力の処理方法を指定します。このオプションを指定しない場合は、関数の本体が通常の CFML として処理されます。したがって、関数定義の本体内のテキストと cfoutput タグの結果が、関数を実行するたびに表示されます。true または yes を指定した場合は、関数の本体が cfoutput タグの中にあるものとして処理されます。変数や式をシャープ記号(#)で囲むと、その変数の値や式の結果が表示されます。false または no を指定した場合は、関数が cfsilent タグの中にあるものとして処理されます。関数は出力を表示しません。関数の結果を表示する処理は、その関数を呼び出したコードでおこなうことになります。 |
関数の必須引数には cfargument タグを使用する必要があります。すべての cfargument タグは、cffunction タグの本体で他の CFML コードを記述する前に使用する必要があります。したがって、cffunction 開始タグの直後に cfargument を置きます。cfargument タグには次の属性があります。
属性 |
説明 |
---|---|
name |
引数名 |
type |
(オプション)引数のデータ型。関数に渡されるデータの型。有効な標準の型名は、any、array、binary、Boolean、date、guid、numeric、query、string、struct、uuid、および variableName です。他の名前を指定する場合は、その名前を持つ ColdFusion コンポーネントを引数として使用する必要があります。ここで指定した型に自動変換できない型のデータが関数に渡された場合には、エラーが発生します。例えば、引数の type 属性が数値の場合、配列を使用して関数を呼び出すことはできません。 |
required |
(オプション)引数が必須であるかどうかを指定するブール値。true に設定した場合、その引数を省略して関数を呼び出すとエラーが発生します。デフォルト値は false です。default 属性を指定した場合は、required 属性を指定する必要はありません。関数を呼び出すときは、引数名を指定しないこともあるので、cffunction を定義するときは、必須引数を定義するすべての cfargument タグを、オプション引数を定義する cfargument タグよりも前に置く必要があります。 |
default |
(オプション)引数に値が渡されなかった場合に使用する、オプション引数のデフォルト値。この属性を指定した場合、required 属性は無視されます。 |
この cfargument タグで定義しなくてもかまいません。この機能は、関数の引数の数が不確定な場合に役立ちます。この cfargument タグで定義していないオプション引数は、Arguments スコープ配列でその位置を指定して参照できます。詳しくは 関数での引数および変数の操作を参照してください。
ユーザー定義関数での CFML タグの使用
関数を CFScript で定義せずに cffunction タグで定義する場合の最も重要な利点は、関数内で CFML タグを使用できることです。これによって、ColdFusion タグを必要とする処理(データベース検索など)をカプセル化することができます。また、 cfoutput タグが使用できるので、簡潔なコードで呼び出しページに出力を表示できます。
パフォーマンスを向上させるには、ColdFusion 関数で cfparam タグを使用しないようにします。代わりに、cfset タグを使用します。
次の関数は、従業員の部門 ID を検索して返します。この関数は、従業員 ID を表す 1 つの引数を取り、cfdocexamples の Employee テーブルから対応する部門 ID を検索します。
<cfargument name="empID" required="true" type="numeric"> <cfset var cfdocexamples=""> <cfquery dataSource="cfdocexamples" name="deptID"> SELECT Dept_ID FROM Employee WHERE Emp_ID = #empID# </cfquery> <cfreturn deptID.Dept_ID> </cffunction>
関数定義のルール
CFScript または cffunction タグを使用して定義する関数には、次のルールが適用されます。
- 関数名は一意である必要があります。既存の変数または UDF と異なる名前にする必要があります。ただし、ColdFusion 拡張セキュリティ関数名は使用できます。
- ユーザー定義関数には、CFC のビルトイン関数と同じ名前を付けることができます(CFM のビルトイン関数と同じ名前は使用できません)。
- 次の名前を使用してユーザー定義関数を作成することはできません。
- writedump
- writelog
- location
- throw
- trace
- 関数名の先頭に cf という文字は使用できません(例えば、CF_MyFunction、 cfmyFunction 、 cfxMyFunction は無効な UDF 名です)。
- 関数の再定義や、オーバーロードはできません。ある関数定義がアクティブである場合、同じ名前で別の関数を定義するとエラーが発生します。
- 関数の定義はネストできます。つまり、関数定義の内部で別の関数を定義することができます。後述の例を参照してください。
- 関数は再帰的に定義できます。つまり、関数定義の本文でその関数自身を呼び出すことができます。
- 関数は、必ずしも値を返す必要はありません。
UDF の作成方法には、タグを使用する方法と、CFScript を使用する方法があります。どちらの方法にも、利点と欠点があります。
CFScript を使用する場合
<cfscript> function hypotenuse(a, b) { function square(x) { return x*x; } return sqr(square(a) + square(b)); } result = hypotenuse(1,2); writeoutput(result); </cfscript>
cffunction タグを使用する場合
<!---hypotenuse.cfc---> <cfcomponent> <cffunction name="calchypotenuse"> <cfargument name="a" required="true"/> <cfargument name="b" required="true"/> <cffunction name="square"> <cfargument name="x" required="true"/> <cfreturn x*x/> </cffunction> <cfreturn sqr(square(a) + square(b))/> </cffunction> </cfcomponent>
<!---hypotenuse.cfm---> <cfset myhypotenuse=new hypotenuse()> <cfoutput>#myhypotenuse.calchypotenuse(1,2)#</cfoutput>
即時実行関数式(IIFE)
即時実行関数式(IIFE)を使用すると、関数を作成後すぐに実行することができます。IIFE は、グローバルオブジェクトには影響を及ぼしません。IIFE を使用すると、変数宣言を簡単に分離することができます。
詳しくは、Wikipedia の IIFE 定義を参照してください。
- IIFE は自己実行型の匿名関数ブロックです。
- IIFE には独自のスコープがありますつまり、関数式で宣言された変数は関数の外部では使用できません。
- 他の関数と同じように、IIFE に名前を付けることも、匿名にすることもできますが、IIFE に名前が付いている場合でも、それを参照したり呼び出したりすることはできません。
- IIFE は、1 回使用すると、再利用することはできません。
- 即時実行関数式を使用すると、関数のスコープとその内部の変数をうまく保護することができます。
IIFE の話に進む前に、一般的な関数宣言がどのようなものかをざっと振り返りましょう。
function doSomething(){ // ...do something... }
もう 1 つの方法は、関数式を使用して関数を作成することです。
doSomething = function(){ // ...do something... }
IIFE では、一般的な関数宣言は次のようになります。
(function(){ // ...do something... })()
上記の式を詳しく見てみましょう。
(function(){ // ...do something... })
関数を括弧内にラップすることによって、関数宣言ではなく関数式として式が評価されます。
次に、2 番目の括弧が何のために使用されているかを考えてみましょう。
(function(){ // ...do something... })()
この 2 番目の括弧によって、関数が直ちに呼び出されます。一言で言うと、IIFE では、式は 1 番目の括弧内で評価され、2 番目の括弧内で呼び出されます。
IIFE を使用する理由
即時実行関数式は、関数のスコープとその内部の変数を保護します。
次のタイプの関数でスコープがどのように定義されるかを見てみましょう。
通常の関数
<cfscript> // function Definition function addTogether() { x = 20 y = 20 answer = x + y writeOutput(answer) } // function invocation addTogether() </cfscript>
出力
40
クロージャ
<cfscript> // closure Definition func = function () { x = 20 y = 20 answer = x + y writeOutput(answer) } // closure invocation func() </cfscript>
出力
40
IIFE
<cfscript> (function addTogether() { x = 20 y = 20 answer = x + y writeOutput(answer) })() </cfscript>
出力
40
IIFE には独自のスコープがあります。つまり、関数式で宣言された変数は関数の外部では使用できません。
他の関数と同様に、IIFE は名前付きにすることも匿名にすることもできます。また、パラメーターを持つことができます。
IIFE は、1 回使用すると、再利用することはできません。
例
クロージャ
<cfscript> // Normal function expression function printOutput(){ writeOutput("Hello") } printOutput() //Using IIFE writeoutput((function(name) { return( "Hello #name#" ); })("Darkness, my old friend!<br>")); </cfscript>
出力
Hello
Hello Darkness, my old friend!
名前付きクロージャ
<cfscript> // IIFE (function(name) { writeOutput("Hello " & "#name#") })("Joker<br/>"); // named function (function printName(name) { writeOutput("Hello " & "#name#") })("Batman<br/>"); </cfscript>
出力
Hello Joker
Hello Batman
ラムダ関数
<cfscript> // using lambda ((name)=> { writeOutput("Hello " & "#name#") })("Joker<br/>"); // lambda- two parameters ((fName,lName)=>{ writeOutput("Hello " & fName & " " & lName) })("Bruce","Wayne") </cfscript>
出力
Hello Joker
Hello Batman
ラムダ関数内のステートメント
<cfscript> // simple statement (()=>{ writeOutput("Hello IIFE") })() // assignment statement x=(()=>2)() writeOutput("<br/>" & x) </cfscript>
出力
Hello IIFE
2
ラムダ関数内の演算子
<cfscript> // ternary operator ((a,b)=>{ c = (a < b) ? a : b writeOutput(c) })(20,10) // unary operator unOp=(()=>13)() writeOutput(unOp) </cfscript>
出力
10
13
二項演算子
<cfscript> // binary operator // AND ((a,b)=>{ c=a && b writeOutput("a && b is: " & c) })(6,1) // OR ((a,b)=>{ c=a || b writeOutput("a || b is: " & c) })(6,1) // XOR ((a,b)=>{ c=a XOR b writeOutput("a xor b is: " & c) })(6,1) </cfscript>
出力
a && b is: 1a || b is: 6a xor b is: NO
代入
<cfscript> // simple assignment (()=> {writeOutput("This is a simple assignment<br/>")})() // multi assignment d = 3 a=b=c=d*(() => 5)() writeOutput(a) </cfscript>
出力
This is a simple assignment
15
ステートメントとパラメーター
<cfscript> // single param, single statement writeOutput((name => "Hello, #name#")("Joker")) // multiple params, single statement func=(( name, lastname ) => "Hello, #name# #lastname#")("Tony", "Stark") writeOutput(func) // multiple params, multi statement func1=(( name, lastname ) => { return "Hello, #name# #lastname#" })("Jimmy", "Page") writeOutput(func1) </cfscript>
出力
Hello, Joker
Hello, Tony Stark
Hello, Jimmy Page
UDF
<cfscript> // As a return statement in UDF function plusTwo(num) { return (() => num + 2)() } writeOutput(plusTwo(5) & "<br>") // As a param to built-in / UDF methods myStringVar = UCase((() => " more sleep!")()) writeOutput(myStringVar) </cfscript>
出力
7
MORE SLEEP!
If-else
<cfscript> // if-else statement if((function () {return "orange"})() == "banana"){ writeOutput("Fruit is orange!" & "<br/>") } else{ writeOutput("Fruit is something else") } </cfscript>
出力
Fruit is something else
再帰的 IIFE
<cfscript> // recursive IIFE writeoutput(((param) => param + 15)((() => 13)())) </cfscript>
出力
28
for ループ
<cfscript> // for loop for (i=1;i<=(()=>5)();i++){ writeOutput(i & ". " & "Hello World" & "<br/>") } </cfscript>
出力
1. Hello World
2. Hello World
3. Hello World
4. Hello World
5. Hello World
暗黙の初期化
<cfscript> // Array implicit initialization arr = [ (()=> 1)(), (()=> 2)(), (()=> 3)() ] writeDump(arr) // Struct impicit initialization str={ "a":(()=>1)(), "b":(()=>2)(), "c":(()=>3)() } writeDump(str) </cfscript>
出力