-
名前付きパイプは、パイプサーバーと複数のパイプクライアントとの間でのプロセス間通信ができます。 名前付きパイプは、メッセージ単位で通信するモードと、バイトストリームとして読み書きするモードのいずれかが選べます。
どちらのモードでも、名前付きパイプを生成し、接続した後は、サーバー側・クライアント側とも通常のファイル入出力を使うことで通信が行われます。
名前付きパイプの例として以下の2通りの方法を示します。■1回の通信毎に名前付きパイプを生成する方法
サーバー側では、名前付きパイプ処理を行うスレッドを立ち上げて、そのスレッドの中で通信処理を行います。
フォームに通信開始ボタンと、受信メッセージ等の表示用にリッチテキストボックスを設置します。
スレッド処理では NamedPipeServerStream によりサーバー用名前付きパイプクラスを生成します。
その後 WaitForConnection メソッドで接続を待ち StreamReader によりパイプからの受信文字列を取得します。 取得文字列をリッチテキストボックスにデリゲートで表示し、すぐに StreamReader とサーバー用名前付きパイプクラスを閉じます。
サーバー側プログラム
Imports System.Threading Imports System.IO Imports System.IO.Pipes Public Class frmNameServer '別スレッドからメッセージを処理するためデリゲートを利用 Delegate Sub SetRichTextBox1Delegate(ByVal Value As String) Private RichTextBox1Delegate As New SetRichTextBox1Delegate(AddressOf _ AppendTextRichTextBox1) 'リッチテキストボックスにメッセージを表示する Private Sub AppendTextRichTextBox1(ByVal message As String) '文字列を最後尾に追加 RichTextBox1.AppendText(message) 'カレット位置を末尾に移動 RichTextBox1.SelectionStart = RichTextBox1.TextLength 'カレット位置までスクロール RichTextBox1.ScrollToCaret() End Sub '別スレッドからボタンの許可設定処理するためデリゲートを利用 Delegate Sub SetButton1EnabledDelegate(ByVal bln As Boolean) Private SetButton1Delegate As New SetButton1EnabledDelegate(AddressOf _ SetButton1Enabled) '[Buttin1]のEnabled設定 Private Sub SetButton1Enabled(ByVal blnEnabled As Boolean) Me.Button1.Enabled = blnEnabled End Sub '[Start NamePipe]ボタンクリックイベント Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Button1.Click '名前付きパイプによる通信スレッド開始 Dim server As New Thread(AddressOf ServerThread) server.Start() AppendTextRichTextBox1("名前付きパイプ通信待機中..." & vbNewLine) '[Start NamePipe]ボタン不可設定 Me.Button1.Enabled = False End Sub '名前付きパイプによる通信スレッド Private Sub ServerThread() 'サーバー用のパイプを双方向でクライアント数は2個で生成 Dim pipeServer As New NamedPipeServerStream("PIPE_TEST", PipeDirection.InOut, 2) Try 'パイプスレッド取得 Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ生成完了(スレッドID:" & threadId & ")" & vbNewLine}) 'パイプの接続を待つ pipeServer.WaitForConnection() Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ接続完了" & vbNewLine}) 'ストリーム読込を生成 Dim pipeStreamReader As New StreamReader(pipeServer) Try 'パイプからの文字列受信(入力) Dim strRead As String = pipeStreamReader.ReadLine() '受信文字列の表示 Me.Invoke(RichTextBox1Delegate, _ New Object() {"受信文字列:" & strRead & vbNewLine}) Catch ex As IOException MsgBox(ex.Message) Finally pipeStreamReader.Close() End Try Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ通信の終了" & vbNewLine}) '[Button1]の許可設定 Me.Invoke(SetButton1Delegate, New Object() {True}) Catch ex As Exception MsgBox(ex.Message) Finally pipeServer.Close() End Try End Sub End Class
クライアント側プログラムは送信ボタンとメッセージ入力用にテキストボックスを設置します。
送信ボタンを押下した時に NamedPipeClientStream によりクライアント用名前付きパイプを生成します。
そのクラスの Connect メソッドで接続後ストリームライタで1回のみ文字列送信を行います。
送信後、ストリームライタとクライアント用名前付きパイプを閉じます。
クライアント側プログラム
Imports System.IO Imports System.IO.Pipes Imports System.Security.Principal Public Class frmNameClient '[Start Named Pipe]ボタンクリック時イベント Private Sub ButtonSend_Click(sender As Object, e As EventArgs) _ Handles ButtonSend.Click Dim pipeStream As NamedPipeClientStream = Nothing Dim pipeSw As StreamWriter = Nothing Try '[Send Message]ボタンを不可設定 Me.ButtonSend.Enabled = False '名前付きパイプ・クライアント生成 pipeStream = New NamedPipeClientStream(".", "PIPE_TEST", _ PipeDirection.InOut, _ PipeOptions.None, _ TokenImpersonationLevel.Impersonation) '名前付きパイプ接続 pipeStream.Connect(1000) 'ストリームライタ pipeSw = New StreamWriter(pipeStream) pipeSw.AutoFlush = True '文字列送信 pipeSw.WriteLine(Me.TextBox1.Text.Trim) Catch ex As Exception MsgBox(ex.Message) Finally 'ストリームライタを閉じる If Not pipeSw Is Nothing Then pipeSw.Close() End If '名前付きパイプを閉じる If Not pipeStream Is Nothing Then pipeStream.Close() End If '[Send Message]ボタンを許可設定 Me.ButtonSend.Enabled = True End Try End Sub End Class
プログラムの実行は以下の様になります。
■名前付きパイプを生成後、複数回の通信を行う方法
1回の通信毎に名前付きパイプを生成するのでは効率が悪いですし、連続での通信ができませんので サーバー側のスレッド部分を複数回の通信が出来る様に変更します。
変更した部分は、スレッド内でストリーム読込を生成をした後で、ループを形成しパイプからの文字列受信を繰り返す様にしています。 受信した文字列がNULLの場合にストリームライタとクライアント用名前付きパイプを閉じます。
サーバー側プログラム
Imports System.Threading Imports System.IO Imports System.IO.Pipes Public Class frmNameServer '別スレッドからメッセージを処理するためデリゲートを利用 Delegate Sub SetRichTextBox1Delegate(ByVal Value As String) Private RichTextBox1Delegate As New SetRichTextBox1Delegate(AddressOf _ AppendTextRichTextBox1) 'リッチテキストボックスにメッセージを表示する Private Sub AppendTextRichTextBox1(ByVal message As String) RichTextBox1.AppendText(message) RichTextBox1.SelectionStart = RichTextBox1.TextLength End Sub '別スレッドからボタンの許可設定処理するためデリゲートを利用 Delegate Sub SetButton1EnabledDelegate(ByVal bln As Boolean) Private SetButton1Delegate As New SetButton1EnabledDelegate(AddressOf _ SetButton1Enabled) '[Buttin1]のEnabled設定 Private Sub SetButton1Enabled(ByVal blnEnabled As Boolean) Me.Button1.Enabled = blnEnabled End Sub '[Start NamePipe]ボタンクリックイベント Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click '名前付きパイプによる通信スレッド開始 Dim server As New Thread(AddressOf ServerThread) server.Start() AppendTextRichTextBox1("名前付きパイプ通信待機中・・・" & vbNewLine) '[Start NamePipe]ボタン不可設定 Me.Button1.Enabled = False End Sub '名前付きパイプによる通信スレッド Private Sub ServerThread() 'サーバー用のパイプを双方向でクライアント数は2個で生成 Dim pipeServer As New NamedPipeServerStream("PIPE_TEST", PipeDirection.InOut, 2) Try 'パイプスレッド取得 Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ生成完了(スレッドID :" & threadId & ")" & vbNewLine}) 'パイプの接続を待つ pipeServer.WaitForConnection() Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ接続完了" & vbNewLine}) 'ストリーム読込を生成 Dim pipeStreamReader As New StreamReader(pipeServer) Try While True 'パイプからの文字列受信(入力) Dim strRead As String = pipeStreamReader.ReadLine() 'ストリームの末尾に到達した場合はNullなので、通信を終了する 'If strRead Is Nothing Then If strRead = "" Then Exit While End If '受信文字列の表示 Me.Invoke(RichTextBox1Delegate, _ New Object() {"受信文字列:" & strRead & vbNewLine}) End While Catch ex As IOException MsgBox(ex.Message) Finally pipeStreamReader.Close() End Try Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ通信の終了" & vbNewLine}) '[Button1]の許可設定 Me.Invoke(SetButton1Delegate, New Object() {True}) Catch ex As Exception MsgBox(ex.Message) Finally pipeServer.Close() End Try End Sub End Class
クライアント側プログラムではパイプ開始ボタンとメッセージ送信ボタンを設置します。
パイプ開始ボタン押下で名前付きパイプ・クライアント生成と接続、及びストリームライタ生成を行います。
この生成されるクラスは、メッセージ送信ボタン押下時にも使用されるため、関数の外で静的変数として宣言します。
メッセージ送信ボタン押下時にはストリームライタでメッセージを送信します。 メッセージが空白の場合はストリームライタ、名前付きパイプ・クライアントを閉じます。
クライアント側プログラム
Imports System.IO.Pipes Imports System.IO Imports System.Security.Principal Public Class frmNameClient '名前付きパイプ・クライアント Private pipeStream As NamedPipeClientStream = Nothing 'パイプ用ストリームライタ Private pipeSw As StreamWriter = Nothing 'フォームロード時イベント Private Sub frmNameClient_Load(sender As Object, e As EventArgs) Handles Me.Load '[Start Named Pipe]ボタンを許可設定 Me.Button1.Enabled = True '[Send Message]ボタンを不可設定 Me.Button2.Enabled = False End Sub '[Start Named Pipe]ボタンクリック時イベント Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Try '名前付きパイプ・クライアント生成 pipeStream = New NamedPipeClientStream(".", _ "PIPE_TEST", _ PipeDirection.InOut, _ PipeOptions.None, _ TokenImpersonationLevel.Impersonation) '名前付きパイプ接続 pipeStream.Connect(1000) 'ストリームライタ生成 pipeSw = New StreamWriter(pipeStream) pipeSw.AutoFlush = True '[Start Named Pipe]ボタンを不可設定 Me.Button1.Enabled = False '[Send Message]ボタンを許可設定 Me.Button2.Enabled = True Catch ex As Exception MsgBox(ex.Message) End Try End Sub '[Send Message]ボタンクリック時イベント Private Sub Button2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button2.Click Try '送信文字列取得 Dim strTX As String = Me.TextBox1.Text.Trim pipeSw.WriteLine(strTX) If strTX = "" Then 'ストリームライタを閉じる pipeSw.Close() '名前付きパイプを閉じる pipeStream.Close() '[Start Named Pipe]ボタンを許可設定 Me.Button1.Enabled = True '[Send Message]ボタンを不可設定 Me.Button2.Enabled = False End If Catch ex As Exception MsgBox(ex.Message) End Try End Sub 'フォームクローズ時イベント Private Sub frmNameClient_FormClosed(sender As Object, _ e As FormClosedEventArgs) Handles Me.FormClosed 'ストリームライタを閉じる If Not pipeSw Is Nothing Then pipeSw.Close() End If '名前付きパイプを閉じる If Not pipeStream Is Nothing Then pipeStream.Close() End If End Sub End Class
クライアント側でフォームを閉じることでパイプの接続が閉じるため、サーバー側に通信の終了が通知できます。
プログラムの実行は以下の様になります。関連する記事
⇒Remoting の IPC を使ったプロセス間通信について
⇒Remoting の IPC を使ったプロセス間通信についてその2(HTTPチャネル)
⇒名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信)
⇒名前付きパイプを使ったプロセス間通信についてその3(クライアントとの双方向通信)
PR -
関数はJavaScriptで繰り返し行う処理などをまとめたものを言います。 他の言語を使っている方には特に説明は必要無いかとは思いますが、 JavaScript特有の記述もあります。 関数の宣言方法の違いによって以下の2種類がありますので、それぞれについて見ていきます。
■一般的な関数の宣言(functionを使った宣言)
function キーワードを使った関数宣言の構文は以下の様になっています。 function キーワードの後に「関数名」を書いて関数の引数が在れば「引数リスト」(複数あれば「,」カンマで区切ります)を記述します。 関数内部には行いたい処理を記述し、最後に関数として戻したい値があれば return 文で戻してやります。
function 関数名( [ 引数リスト ] ) { // 関数内の処理 ... [return [戻り値];] }
基本的な使い方は以下のソースを見て下さい。
func1 という名前で関数を宣言しています。1から引数に与えられた数値までの合計を計算する簡単な関数です。 本来ならば、引数の値チェック(数値以外の文字列等が渡されたらどうするか)や、合計値の桁あふれなどにもチェックが必要かもしれません。
取敢えずこの関数を実行するために、関数の戻り値を変数に代入しています。 尚、戻り値を直接別の関数に渡すことも可能です。(以下では console.log への引数の値として渡されます)//関数の宣言(引数までの合計を計算) function func1( intPara ) { var sum = 0; for (var i = 1; i <= intPara; i++) { sum = sum + i; } return sum; } //変数の宣言 var val; //関数の実行 val = func1(10); // 変数への代入 console.log(val); // 55 と表示される console.log(func1(100)); // 5050 と表示される
「引数」が文字列の場合を想定した関数の例を以下に示します。//関数の宣言(引数に文字列指定) function func2( strPara1, strPara2 ) { var strRet = "" + strPara1 + "..." + strPara2; return strRet; } //関数の実行 console.log(func2("10", "aaaa")); // 10...aaaa と表示される
関数の戻り値としてオブジェクトを指定
関数の戻り値としてオブジェクトを指定できます。以下にその例を示します。
関数としては2個の引数を持ち、それぞれをオブジェクトのプロパティの値として設定しています。 関数の実行後、戻り値をそのままコンソールに表示すれば結果はオブジェクトが戻されることが分かります。
ここで注意が必要なのですが、関数の引数としては数値でも文字列でも指定できますので オブジェクトのプロパティ値もそれぞれ設定したままが戻されます。//関数の宣言(戻り値がオブジェクトの場合) function func3( strPara1, strPara2 ) { //オブジェクトの宣言 var obj = { Para1: strPara1, Para2: strPara2, } //オブジェクトを戻す return obj; } //関数の実行(変数の宣言も同時に行う) var val = func3(100, "aaaa"); console.log(val); // Object { Para1: 100, Para2: "aaaa" } と表示される console.log(val.Para1); // 100 と数値として表示される console.log(val.Para2); // aaaa と文字列として表示される val = func3("100", "aaaa"); console.log(val); // Object { Para1: "100", Para2: "aaaa" } と表示される console.log(val.Para1); // 100 と文字列として表示される console.log(val.Para2); // aaaa と文字列として表示される
上の例でもありますが、引数の型等をどの様に定義するかは関数を作る前によく考えておく必要があります。
関数の引数としてオブジェクトを指定
引数にArrayやユーザ定義のオブジェクトを指定できますが、 関数内でそのオブジェクトのプロパティを変更すると、関数の呼出元にも反映されるため注意が必要です。 以下にその例を示します。
//関数の宣言(引数がオブジェクトの場合) function func4( objPara ) { //プロパティを変更 objPara.prop1 = "000"; } //オブジェクトの宣言 var obj = { prop1: "1234" , prop2: "abc" }; console.log(obj); // Object { prop1: "1234", prop2: "abc" } と表示される //関数の呼出 func4( obj ); console.log(obj); // Object { prop1: "000", prop2: "abc" } と表示される //----- //関数の宣言(引数がArrayの場合) function func5( arr ) { //配列の内容を変更 arr[1] = "111"; } //Arrayの宣言 var arr = new Array("aaa", "bbbb", "123"); console.log(arr); // Array(3) [ "aaa", "bbbb", "123" ] と表示される //関数の呼出 func5( arr ); console.log(arr); // Array(3) [ "aaa", "111", "123" ] と表示される
引数にオブジェクト、及びArrayの場合を例にしましたが、関数内でのプロパティの変更が 関数外にも影響していることが分かります。
■関数式(関数リテラル)による宣言
今までは function キーワードを使った構文としての関数宣言でしたが、 関数式として宣言することができます。
//関数式による関数の宣言 var test = function (strPara) { //引数の前後に括弧([])を付加して戻す return "[" + strPara + "]"; }; //ここで文の最後であるセミコロン「;」を記述(無くてもOKですが) //関数の実行 var str = test("12345"); console.log(str); // [12345] と表示される //ちなみに「test」を表示する console.log(test); // function test()... と表示される
上の例からもわかりますが var test と変数 test を宣言しているようですが、 test の実体は関数として存在しますので、これを関数の様に呼出すことができます。
ただし関数式は名前をつけることもでき、関数内で自身を参照することができます。 以下の例では、1から引数値までの総和を求める処理を行っています。 sum と言う名前を関数に付けて自分自身の中で再帰的に呼び出しを行い、 引数値から順次1小さい値を加算することを再帰的に行っています。//[1~N]までの総和を求める関数を関数式で宣言 var summation = function sum(intPara) { if (intPara <= 1) { //1になったらそこで再帰呼出し終了 return 1; } else { //再帰呼出しで順次1小さい値を加算してゆく return intPara + sum(intPara - 1); } }; //関数の実行 console.log(summation(1)); // 1 と表示される console.log(summation(2)); // 3 と表示される console.log(summation(3)); // 6 と表示される console.log(summation(10)); // 55 と表示される console.log(summation(100));// 5050 と表示される
関数式で宣言した関数オブジェクトを引数にした関数を宣言し、 それぞれの関数式での関数を呼び分ける方法を以下に記します。//関数オブジェクトを引数にした関数宣言 function math(fn, val) { //関数オブジェクトを実行 return fn(val); } //関数式による引数を2乗する関数宣言 var sqr = function(val) { return val * val; }; //関数式による引数を +1 する関数宣言 var inc = function(val) { return val + 1; }; //関数式による引数を -1 する関数宣言 var dec = function(val) { return val - 1; }; //関数式による引数を2倍する関数宣言 var mul2 = function(val) { return val * 2; }; //関数オブジェクトを変えて関数をコールする console.log(math(sqr, 10)); // 100 と表示される console.log(math(inc, 10)); // 11 と表示される console.log(math(dec, 10)); // 9 と表示される console.log(math(mul2, 10)); // 20 と表示される
-
配列の繰り返し処理では通常 for 文を使います。
for 文には一般的に指標を使って繰り返し、指標で配列を参照する方法や、 配列の指標と値を順次取得できる方法があります。
尚、for 文には以下の使い方がある様ですので、それぞれについて見ていきます。- for(指標を使った一般的な方法)
- for...in(オブジェクトのプロパティ名での繰返し)
- for...of(オブジェクトのプロパティの値の繰返し)
- Array.forEach(配列の各要素についての繰返し)
■for(指標を使った一般的な方法)
いまさらですが for 文の構文は以下の様になっています。 「初期値設定」「条件式」「指標処理」の3個の部分を「;」セミコロンで区切ります。 それぞれを [] で囲っているのは、必要が無ければ記述を省略できます。 (通常はあまり使いませんが)
for ( [ 初期値設定 ]; [ 条件式 ]; [ 指標処理 ] ) { // 繰り返しの処理 ... }
基本的な使い方は以下のソースを見て下さい。
最初に3個の要素を持つ配列を宣言し、次に for で配列の長さ分の処理を行います。
配列の長さを取得するために length プロパティを参照しますが、 このプロパティは、配列の宣言を行った時に javascript が内部で設定されます。
繰返し処理の中身は配列の値をコンソールに表示することを行います。 (10 20 30 と順次表示されます。)//配列の宣言 var arr = [10, 20, 30]; //配列の内容を順次表示 for(var i = 0; i < arr.length; i++) { //繰返しの処理... console.log(arr[i]); }
「初期値設定」「条件式」「指標処理」の3個の部分はそれぞれ必要が無ければ記述しなくても問題ありません。 究極、3個とも無くしてしまってもOKです。 但し、ループを抜ける処理は記述しないと無限ループになってしまいます。
このループを抜ける処理が break 文でループを抜けたい条件の中で記述します。 (条件が無くてもOKですが...)
上の処理と同じことを行う例を示します。//配列の宣言 var arr = [10, 20, 30]; //指標の宣言 var i = 0; //for 文の中身は記述しない for( ; ; ) { if (i >= arr.length) { //length の値は「3」 //指標が長さ以上になった場合ループを抜ける break; } //ループ内処理 console.log(arr[i]); //指標を増やす i++; } //break 後はここに来る
break 文が出たところで continue 文を出さないと片手落ちなので以下の例を示します。 指標が「1」になった時に、指標を+1してループの先頭に戻しています。 結果としてコンソールには 10 30 が表示されます。//配列の宣言 var arr = [10, 20, 30]; //配列の内容を順次表示 var i = 0; //for 文の中身は記述しない for( ; ; ) { if (i >= arr.length) { //指標が長さ以上になった場合ループを抜ける break; } if (i == 1) { //指標を増やす i++; //指標が[1]ならば先頭に戻る:「if (i >= arr.length) {」の処理に戻る continue; } //ループ内処理 console.log(arr[i]); //指標を増やす i++; } //break 後はここに来る
■for...in(オブジェクトのプロパティ名での繰返し)
for 文なので配列に使えるのかなと思って、以下の例を実行したのですが、 コンソールに表示されるのは「0 1 2」と指標らしきものが表示されます。 なぜ指標なのかを正式なドキュメントに当たると以下の様に記されています。
「for..in 文は、指定したオブジェクトの列挙可能プロパティに対して、順不同で反復処理をします。」
つまり返されるのはプロパティ名を返す様です。 (単純な配列の場合は指標がプロパティ扱いの様です。//配列の宣言 var arr = [10, 20, 30]; //配列の内容を順次表示 for(var v in arr) { console.log(v); }
そこで以下の様にオブジェクトに対して for 文を行うと、コンソールには「prop1 prop2 prop3」が順次表示されます。 これはオブジェクトのプロパティ名そのものです。//オブジェクトの宣言 var obj = { prop1: 10, prop2: 20, prop3: 30 }; //オブジェクトのプロパティ名を順次表示 for(var v in obj) { console.log(v); }
さらに以下の様にプロパティ名を扱うと、プロパティに対応する値にアクセスできます。
//オブジェクトの宣言 var obj = { prop1: 10, prop2: 20, prop3: 30 }; //オブジェクトの内容を順次処理 var sum = 0; var str = ""; for(var v in obj) { sum += obj[v]; // 数値として加算 str += obj[v]; // 文字列として追加 } console.log(sum); // 60 と表示される console.log(str); // 102030 と表示される
先ほどの配列の場合は以下の様にすれば、指標に対応する値にはアクセスできますが、 指標の順番通り取得できるとは限らないので、指標によるアクセスの方が良いようです。 (以下の例の様に配列の個数が少ない場合は順番にアクセスされる様ですが..)//配列の宣言 var arr = [10, 20, 30]; var sum = 0; //配列の内容を順次表示 for(var v in arr) { sum += arr[v]; // 数値として加算 } console.log(sum); // 60 と表示される
■for...of(オブジェクトのプロパティの値の繰返し)
for...in ではオブジェクトのプロパティ名での繰り返し処理でしたが、 for...of はオブジェクトのプロパティの値を順次アクセスできるものです。
但し対象となるものは iterable(繰り返し可能)なオブジェクト らしいです。 配列(Arrayで宣言されたもの)や、String、Map などです。//---------- //配列の宣言 var arr = [ 10, 20, 30 ]; //配列の値を順次表示 for(var v of arr) { console.log(v); } //コンソールに以下の3行で表示される // 10 // 20 // 30 //---------- //Stringの宣言 var str = "ABC"; //Stringの文字を順次表示 for(var v of str) { console.log(v); } //コンソールに以下の3行で表示される // A // B // C //---------- //Mapの宣言 var M = new Map([["a", 1], ["b", 2], ["c", 3]]); //Mapの要素を順次表示 for (var ent of M) { console.log(ent); } //コンソールに以下の3行で表示される // Array [ "a", 1 ] // Array [ "b", 2 ] // Array [ "c", 3 ] //Mapのキーと値を順次表示 for (var [key, value] of M) { console.log(key + "=" + value); } //コンソールに以下の3行で表示される // a=1 // b=2 // c=3
■Array.forEach(配列の各要素についての繰返し)
Arrayオブジェクトのメソッドである forEach を使っても繰り返し処理が行えます。 構文は以下の通りです。
arr.forEach(function callback( value [, index [, array ] ] ) { //your iterator }[, thisArg]); パラメーター: callback: 各要素に対して実行するコールバック関数で、3つの引数をとります。 value: 現在処理されている配列の要素。 index: 現在処理されている配列の要素のインデックス。 array: forEachが適用されている配列。 thisArg: callback 内で this として使用する値 (i.e the reference Object)。
forEach の例を以下に示します。
//配列の宣言 var arr = ['a', 'b', 'c']; //配列を順次処理 arr.forEach(function(val, idx) { console.log("arr[" + idx + "] = " + val); }); //コンソールに以下の3行で表示される // arr[0] = a // arr[1] = b // arr[2] = c
-
「連想配列の操作について(this キーワード)」の記事の中で 連想配列の様に使えるオブジェクトの説明で、そのオブジェクトの中にメソッドを作成し、 オブジェクト内の変数をアクセスする為に this キーワードを使いました。
⇒JavaScript 連想配列の操作について(this キーワード)
今回はこの this キーワードですが、使う場所によって異なるオブジェクトを指し示すため注意が必要です。
以下の使い方がある様ですので、それぞれについて見ていきます。■グローバル実行コンテクスト
以下のソースを見て下さい。 最初に this キーワードそのものをコンソールにログアウトしています。 結果として this はウインドウオブジェクトであることが分かります。 さらに次のグローバル変数宣言で、ウインドウオブジェクトに value オブジェクトを値「1」で追加します。 これで、ウインドウオブジェクトのプロパティ的に value を参照することが出来ます。
value を「2」として上書きできることも分かります。
ここで注意が必要なのは、存在しない変数をいきなり設定してやると、変数が生成されます。 以下のソースでも value2 が生成されることが分かります。■関数コンテクスト
グローバル関数の宣言を行って、その中で this キーワードで value を参照しています。 表示される結果は、グローバル変数の内容となります。
■オブジェクトのメソッドとして
オブジェクトを宣言し、その中でメソッドも宣言します。 メソッドの中で this を使ってオブジェクト内のプロパティにアクセスしています。 (このメソッドの定義はインラインでの定義と言うそうです。)
//オブジェクト宣言 var objTest = { value: 100, //オブジェクト内のメソッド宣言 logout: function() { console.log("object value = " + this.value); } }; //メソッド実行 objTest.logout(); // object value = 100 と表示される
このインラインのメソッドを、オブジェクトの外で関数として宣言し、 オブジェクトのプロパティに後から追加することもできます。//オブジェクト宣言 var objTest = { value: 200 }; //外側で関数宣言 function _logout() { console.log("object value = " + this.value); } //プロパティに関数を割り当てる objTest.logout = _logout; //メソッド実行 objTest.logout(); // object value = 200 と表示される
この例では、オブジェクトのプロパティを logout と宣言するのと、 そのプロパティを _logout 関数に置き換える処理を一緒に行っています。
一緒に行うのがいやであれば、以下の様にしても同じです。//オブジェクト宣言 var objTest = { value: 200, logout: null //取敢えず先にプロパティ宣言 // logout: 0 }; //外側で関数宣言 function _logout() { console.log("object value = " + this.value); } //プロパティに関数を割り当てる objTest.logout = _logout; //メソッド実行 objTest.logout(); // object value = 200 と表示される
■コンストラクタの呼び出しとして
関数がコンストラクタとして(new キーワードとともに)使用されるとき、 その this は生成された新しいオブジェクトにバインドされます。
以下の例では最初の関数宣言では this を使って内部的にオブジェクトを生成し、そのオブジェクトのプロパティとして value を作り引数の値を設定しています。
この関数をコンストラクタ呼出しを行うと、戻り値としてオブジェクトを返すので、 それを別の変数に代入することで変数を介してそのオブジェクトへアクセスできます。 変数 obj をコンソールに表示させると、オブジェクトが生成されていることが分かります。//関数宣言 function objCreate(pVal) { this.value = pVal; // value プロパティを pVal で設定 } //コンストラクタ呼出しでオブジェクト生成 var obj = new objCreate(100); console.log(obj); // Object { value: 100 } と表示される
もし new キーワードで使用せずに単に関数呼び出しとした場合はどうなるでしょうか。 以下の例を見て下さい。 単なる関数呼び出しでの this はグローバルオブジェクトを示すため、 この場合ですと Window オブジェクトに value プロパティを生成します。//関数宣言 function objCreate(pVal) { this.value = pVal; } //単なる関数呼び出し objCreate(100); console.log(this); // Window { objCreate: function objCreate() , value: 100 ... と表示される
■別オブジェクトのバインド呼び出し(call または apply 呼び出し)
関数本体に this キーワードを使用する場合、call メソッドか apply メソッドを使用した呼び出しで、 その値に特定のオブジェクトをバインドできます。
以下の例はオブジェクト「obj」を宣言し、関数の this の指定が「obj」となっていることを示します。//プロパティ「value」を持つオブジェクト宣言 var obj = { value: "object" }; //グローバルオブジェクトにプロパティ「value」宣言 var value = 'global'; //関数宣言 function objTest() { return this.value; //関数の呼出により[this]の指すものが変わる } var strRet; //通常の関数呼び出し strRet = objTest(); console.log(strRet); // global と表示される //[call]関数呼び出し strRet = objTest.call(obj); // this の指定を「obj」として呼び出し console.log(strRet); // object と表示される //[apply]関数呼び出し strRet = objTest.apply(obj);// this の指定を「obj」として呼び出し console.log(strRet); // object と表示される
call 及び apply の明確な違いは以下の例を見て下さい。 call 呼出しの場合は、 this に対応するオブジェクトの後に引数をそのままの値で設定し、 apply 呼出しの場合は、 this に対応するオブジェクトの後に引数を配列の形で設定します。//関数宣言(引数が3個) function sum(p3, p4, p5) { //[this]によるプロパティ値と引数の値の合計計算 return this.p1 + this.p2 + p3 + p4 + p5; } //プロパティ[p1][p2]を持つオブジェクト宣言 var obj = { p1: 1, p2: 2 }; var ret; //[call]による関数呼び出し ret = sum.call(obj, 3, 4, 5); console.log(ret); // 15 と表示される //[apply]による関数呼び出し ret = sum.apply(obj, [100, 200, 300]); console.log(ret); // 603 と表示される
-
連想配列とは各種のKEYに対応する値を持つ配列の様なものであると「連想配列の作成と初期化について」で説明しましたが、 JavaScriptには実際には連想配列は存在せず、オブジェクトのプロパティとして扱うことで連想配列的に扱えます。 以下の様な例があります。
連想配列的宣言
//連想配列の宣言(実際はオブジェクト) var arrVar = { item: "test1", value: 100, func: function() { // 関数の宣言 console.log("TEST"); } }; //配列内の値表示 console.log(arrVar["item"]); // test1 と表示される console.log(arrVar["value"]); // 100 と表示される //関数の実行 arrVar.func(); // TEST と表示される
上の例では arrVar に関数 func を宣言していますが、このままだと何も意味のある処理ではないので arrVar が持つプロパティを表示する様にしてみます。
そこで必要になるのが this キーワードです。 this 「これ」って何といった感じですが、 this が存在するオブジェクトの親オブジェクトを指すと理解すれば良いと思います。
論より実際の処理を見ると分かりやすいです。上の例に少し修正を加えます。this を使った連想配列の項目アクセス
//連想配列の宣言(実際はオブジェクト) var arrVar = { item: "test1", value: 100, func: function() { // 関数の宣言 var str = ""; str += "item = " + this.item; str += ",value = " + this.value; console.log(str); } }; //関数の実行 arrVar.func(); // item=test1,value=100 と表示される
関数 func が呼ばれた時には this が指し示すオブジェクトは、 関数オブジェクト func が属する親オブジェクトになりますので arrVar を指しそれのプロパティへのアクセスが可能になります。
いわゆるオブジェクト参照の変数ですので this を別の変数に代入しても利用できます。this を使った連想配列の項目アクセスその2
//連想配列の宣言(実際はオブジェクト) var arrVar = { item: "test1", value: 100, add: function(pVal) { // value に加算する関数宣言 this.value += pVal; }, func: function() { // 関数の宣言 var obj = this; // this を別の変数に代入 var str = ""; str += "item = " + obj.item; str += ",value = " + obj.value; console.log(str); } }; //関数の実行 arrVar.add(100); //関数の実行 arrVar.func(); // item=test1,value=200 と表示される
関数 func では this を別の変数に代入して処理を行っています。 add という関数を追加してありますが、これは引数の値をプロパティ value に加算処理を行います。
今回使用した this は奥が深いキーワードでいろんな場所で使えるのですが、 詳しい説明は又の機会にしたいと思います。