C#開発者のための最新JavaScript事情(関数編)特集:C#×JavaScript(1/3 ページ)

コードを書きながら、C#とJavaScriptにおける関数の違いを比較し、C#プログラマーが注意すべき点などを見ていこう。

» 2015年11月06日 05時00分 公開
[かわさきしんじInsider.NET編集部]
特集:C#×JavaScript
業務アプリInsider/Insider.NET

powered by Insider.NET

「特集:C#×JavaScript」のインデックス

連載目次

 前回はC#におけるクラス定義が、JavaScript 5*1/TypeScript/ECMAScript 2015*2のそれぞれでどのように変わるかを見た。今回はC#における関数と、最新のJavaScript系言語とを比較しながら、その差について見てみよう。なお、以下では単に「JavaScript」と表記した場合にはJavaScript系の三つの言語を意味する。JavaScript 5.1を対象とする場合には「JavaScript 5」と書くことにする。

*1 現在、広く使われているJavaScriptのバージョンは5.1である。ここでいう「JavaScript 5」とはEcma internationalが策定するスクリプト言語仕様であるECMAScript 5.1に準拠して、各ベンダーが実装しているスクリプト言語実装/実行環境の総称を指す。

*2 2015年6月にはその最新仕様としてECMAScript 2015が策定された(なお、このバージョンのECMAScriptは「ECMAScript 6」あるいは略称として「ES6」などと表記されることもよくある)。ECMAScript 2015は、JavaScript 5に対してクラスベースのオブジェクト指向プログラミングを可能にする構文拡張、アロー関数、2進/8進リテラル、デフォルト引数、可変長引数などの機能追加が行われている。


関数とは何か

 もちろん、関数とは「何らかの入力を受け取り、何らかの出力を返す」ものだ。そして、プログラム中の複数カ所に登場して同じ処理をするコードを、一つにまとめることで保守性を高めたり、意味のあるひとまとまりのコードに名前を付けることでコードの可読性を高めたりするために関数は使われる。

 ただし、C#にしても、JavaScriptにしても「関数」的な振る舞いをするコードにはさまざまな種類がある。前回も出てきたC#のコンストラクターやクラスメソッド、インスタンスメソッドなどはその代表だ。それらの全てを取り上げて、C#とJavaScriptの比較をするわけにもいかないので、ここではいくつかを取り上げて、C#とJavaScriptそれぞれの特徴を考えてみよう。

 実際のコーディングで気になることはあまりないかもしれないが、一つ覚えておきたいのは「JavaScriptにおける関数とメソッド」だ。JavaScript 5/ECMAScript 2015では「プロパティの値となっている関数のこと」をメソッドと呼ぶ(そして、メソッドの呼び出しに使われたオブジェクトが「this」としてメソッドに渡される)。これはJavaScript 5でもECMAScript 2015でも同じだ。

 TypeScriptではハンドブック言語仕様のいずれにも上記のような記述はなく、クラスのメンバーとなる関数のことを「メンバー関数」と呼んでいる。「TypeScriptがJavaScriptに対してクラスベースのオブジェクト指向プログラミングが可能なように拡張したものである」ことからJavaScriptで通常呼ばれているメソッドとTypeScriptのクラスのメンバーとを(概念的に)区別するためにこのような呼称を使っているのかもしれない。

 が、本稿では「プロパティの値である関数」も「TypeScriptのクラスのメンバー関数」も「メソッド」と総称する。また、メソッドと関数を区別する必要がなければ、これらを総称して「関数」と表記する。

 一方、C#では関数は多くの場合、何らかのクラスに所属するメソッドとして記述する。こちらもメソッドと関数を区別する必要がなければ「関数」と表記する。

JavaScriptにおける関数定義の概要

 JavaScriptではいくつかの方法で関数を定義できる。以下では、C#で似た処理を行うコードと並記していこう。なお、ここではコードがシンプルになるように、C#のコードはProgramクラスの静的メソッドとして記述することにする。

関数宣言

 まず関数宣言を使う方法だ。これはJavaScript 5/TypeScript/ECMAScript 2015のいずれでも可能だ。

function 関数名 (パラメーターリスト) {
  関数本体
}

関数宣言

 これにより、指定した関数名でその関数を呼び出せるようになる。以下に例を示す(TypeScriptでは型注釈を付加できるが、ここではTypeScriptのコードは割愛する)。

function Hello(s) {
  console.log("hello " + s);
}

Hello("JavaScript");


 これに近いのがC#でのオーソドックスな静的メソッド定義だろう(以降のコードではusingによる名前空間の導入は省略している)。

namespace cs
{
  class Program
  {
    static void Hello(string s)
    {
      Console.WriteLine("Hello " + s);
    }

    static void Main(string[] args)
    {
      Hello("C#");

      Console.ReadKey();
    }
  }
}

C#での静的メソッド(クラスメソッド)定義

 クラスのインスタンスを必要とせずに、関数名とパラメーターを指定するだけで呼び出し可能だ。JavaScript 5/ECMAScript 2015では型注釈が使えないので、関数の戻り値やパラメーターに対する型指定がないが、それ以外はほぼ同様だ。これについてはあまり述べることはない。

関数式

 次に関数式を使う方法を見てみよう。以下は関数式の構文である。

function (パラメーターリスト) {
  関数本体
}

関数式
関数式では関数名を付けることも可能だが、ここでは省略している。

 関数式による関数定義では名前のない関数(匿名関数/無名関数)が作られるので、これを実際に利用するには、変数やプロパティに代入するか、即時実行する必要がある。

var Hello = function(s) {    // 関数式で定義した匿名関数を変数Helloに代入
  console.log("hello " + s);
};

Hello("JavaScript");         // 変数を介して関数を呼び出し

(function (s) {              // 無名関数を即時実行
  console.log("hello " + s);
})("function expression");


 構文的に似ているものとしては、C#にも匿名メソッドと呼ばれる機構がある。これとデリゲートを使うことで、変数にメソッドを代入できる。ただし、現在、C#の世界では匿名メソッドはあまり使われず、次に説明するラムダ式を使うのが一般的だ。対して、JavaScriptの世界では、プロパティへの関数の代入などで、関数式/匿名関数を使うのはよくある。というわけで、構文的に似ているからといって比較するのもどうかと思うのだが、一応、コード例を示しておこう。

namespace cs
{
  class Program
  {
    static void Main(string[] args)
    {
      Action<string> Hello = delegate (string s)
      {
        Console.WriteLine("hello " + s);
      };

      Hello("C#");

      Console.ReadKey();
    }
  }
}

C#での匿名メソッドとデリゲートの利用

 C#は静的型付けを持つので、匿名メソッドのパラメーター/戻り値の型を指定するデリゲートAction<T>を使って、その型の変数に匿名メソッドを代入し、その変数を介してメソッドを呼び出している(Action<T>は引数を一つ持ち、戻り値を返さない関数を表すデリゲート)。

アロー関数

 TypeScriptとECMAScript 2015では、アロー関数と呼ばれる関数も定義できる。上でも述べているように、これはC#におけるラムダ式に相当する。かっこの中には関数が受け取るパラメーターのリストを、「=>」の後ろに関数の本体を記述する。

(パラメーターリスト) => { 関数本体 }

アロー関数の構文
パラメーターが一つの場合はかっこを、関数本体が単文の場合は波かっこを省略できる。パラメーターがないときにはかっこは省略できない(「() => …」のようにかっこだけを記述する)。

 以下の例はECMAScript 2015のものだ。

var Hello = (s) => { console.log("Hello " + s); };

Hello("arrow function ");


 パラメーターが一つだけならかっこは省略でき、関数本体が単文なら波かっこを省略できる(関数本体がreturn文だけなら「return」キーワードの記述も省略できる)。よって、これは以下のようにも記述できる。なお、パラメーターがない場合にはかっこは省略できず「() => ...」のように記述する必要がある。

var Hello = s => console.log("Hello " + s);

Hello("arrow function");


 TypeScriptでは型注釈を付加できるので、例えば、以下のような定義になる。ECMAScript 2015と同様、関数本体が1行だけであれば波かっこは省略可能だ。ただし、型注釈を付加した場合には、パラメーターが一つだけであってもかっこは省略できない。

var Hello = (s: string) => alert("Hello " + s);

Hello("TypeScript");


 C#でラムダ式を使ったコードは次のようになる。

namespace cs
{
  class Program
  {
    static void Main(string[] args)
    {
      Action<string> Hello = s => Console.WriteLine("hello " + s);

      Hello("C#");

      Console.ReadKey();
    }
  }
}

ラムダ式を使用したメソッド定義

 ラムダ式を使うと型推論機構が働いてくれるため、前述の匿名メソッドのときとは異なり、パラメーターの型を明記する必要がなくなっている。また、TypeScript/ECMAScript同様、関数本体が単文なのでかっこも必要ない。C#とTypeScript/ECMAScriptでの記述がそっくりで、(慣れると)とてもシンプルに記述できるのが分かる。

 最後にTypeScriptでは関数のオーバーロードが可能だが、JavaScript/ECMAScript 2015ではオーバーロードできない。本稿では割愛するが、興味のある方は「TypeScriptで学ぶJavaScript入門:第11回 関数に関するいくつかのトピック」などを参照してほしい。

 さて、ここまで基本中の基本ともいえる関数定義の方法を見てきた。次にもう少し高度なトピックとしてJavaScriptにおけるクラスメンバーとしてのメソッドを見てみよう。

       1|2|3 次のページへ

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。