質問: PHP, js, rubyでの宣言と定義の違いを教えてください!!!! 下のサイトを読んだのですが、あまり内容がわからなくて・・・ https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13120175843 https://msdn.microsoft.com/ja-jp/library/0e5kx78b.aspx
回答: 宣言(declaration)と定義(definition)という言葉について何を意味するのかは言語によっては特殊な意味を持つも事があります。ですが、ほとんどの場合はC言語での意味とほぼ同じになるでしょうから、**C言語での宣言と定義が意味するところが**、各言語においてどうなるかを説明していきます。 #### C言語での宣言と定義 C言語は宣言と定義を分けて書くことができる言語です。**分けて書くことができない言語も存在します**し、C言語であっても**宣言と定義が兼ねる場合があります**。この点はご注意ください。 **宣言**は「ある識別子(identifier)はこういう類いの物である」と宣言することです。識別子というのは変数名とか関数名とか型名とかの事です。プログラムの中で`a`という識別子を使うとき、 ``` int a(int); ``` と書いてあったとします。これは関数の宣言であり、「`a`はint一つを引数にしてintを返す関数である」ということを宣言します。この時点ではどのような関数であるかはわかりません。しかし、関数であることはわかっているので、この後のコードでは`a(42)`と言った形で`a`を関数として使用することができます。同じように、これはintの変数だと言った宣言が可能です。 対して、**定義**は「ある識別子(identifier)は具体的にこういう中身である」と定義することです。先ほどの`a`を定義してみましょう。 ``` int a(int x) { return x + 1; } ``` 宣言だけでは`a`は関数であるということしかわかりませんでした。しかし、定義によってどんな動作をする関数であるかと言うことが定義されます。上の例では引数に1足した数を返す関数という定義です。コードの中で`a(42)`等と書かれた場合は、この定義に従って関数の評価が行われ、43を返す事でしょう。 注意して欲しいのは、定義は宣言も兼ねられると言うことです。最初の宣言を書かずに二番目の定義だけを書いたら、定義の部分で宣言がされたと見なされます。宣言と同じように、この後のコードで`a`を使うことが可能です。 逆に宣言と定義を分離できない場合もあります。例えば、型を定義するtypedefは宣言だけを行うことはできません。`T`は型ですと宣言すると同時に、具体的に○○と言う型ですと定義する必要があります。 なお、変数の定義についてですが、変数はローカル変数なのかグローバル変数なのかといった書き方によって宣言が定義を兼ねる場合もあれば、別々の場合もあり、複雑なので具体的には言及しません。ただ、変数の場合はメモリ上に確保されるところをもって定義としているようです。 以上がC言語の場合ですが、これとほぼ同じなのはC++ぐらいです。他の言語では宣言と定義がわかれていない場合があります。 #### PHPでの宣言と定義 PHPについては、細かい仕様を語れるほど詳しくないので、他の人に説明を任せます。ごめんなさい。 #### ECMAScript(JavaScript)での宣言と定義 ECMAScriptの関数もクラスも変数も宣言と定義を兼ねます。先行して宣言だけを行うことはできません。 ``` function a(x) { return x + 1; } ``` このコードを定義の部分を外して書くことができないと言うことです。 人によっては次のコードは宣言と定義がわかれていると言うかも知れません。 ``` var a; a = function (x) { return x + 1; }; ``` このコードの正しい解釈は、`a`という変数が定義され(この時点では`a`にはundefinedが入っています)、その後に`a`という変数に関数が代入されて、`a`は関数として使用できるようになったと言うことに過ぎません。`a`が先行して宣言されたわけではないのです。 なお、仕様上は、章の題名には「Function Definitions(関数定義)」という言葉を使いながら、文法(syntax)では「FunctionDeclaration(関数宣言)」という言葉を使っています。仕様上の文法では関数宣言という名前ですが、実際は具体的な中身が必ず存在するため、最初に述べたC言語での意味での定義がされていると言って差し支えありありません。**これは、ECMAScriptの仕様書上の用語として「宣言」と「定義」がどうなっているとかはまた別の話であり、決して混同しないでください。**もし、混乱してしまったのであれば、私の書き方が悪かったのだと思われますので、文章力に不足があり、申し訳ありません。仕様書の「宣言」と「定義」について知りたい場合は、think49さんの回答が参考になりますから、そちらを読んでください。この回答をいくら読んでも、仕様上の意味での解釈については一切助けにはなりません。 では、ECMAScriptには定義が無い宣言が無いのかというとそうではありません。importとexportがあります。importではこの外部ファイルをインポートしますという宣言だけです。具体的な中身は外部ファイルに書いてあり、importで定義しているわけではありません。exportでは具体的な中身を書くこともできますが、名前だけ指定してexportすることも可能です。つまり、この変数や関数はエクスポートしますと宣言するだけで、その中身は別の所に書いてあると言うことができます。 #### Rubyでの宣言と定義 Rubyも宣言と定義を分けて書くことはできず、全てが宣言と定義を兼ねます。 ``` def a(x) x + 1 end ``` `a`はメソッドとして定義されましたが、メソッドであることを前もって宣言するような書き方はありません。必ず、具体的な中身(何もしないという空の場合もあり得る)が定義される必要があります。 クラスについては次のようにできるから、分けて書けると指摘する人もいるかも知れません。 ``` A = Class.new class A def initialize(x) @x = x end def plus_one @x + 1 end end ``` 最初にクラス`A`については何も中身が無いClassオブジェクトとして作成しました。これは宣言と言えるのでしょうか?いいえ、言えません。具体的にClassのオブジェクトとして生成しているからです。Rubyはオープンクラスと言いわれる後からクラスの中身を実装できる仕組みであるため、後から定義されると言うより、定義が追加で変わっていくと言うだけに過ぎません。最初が定義を意味しないわけではありません。 動的な言語(プログラムの途中で、識別子の意味が動的に変わっていく言語、ECMAScriptとかRubyとか)においては、宣言と定義は一緒の場合が多いように思えます。でも、静的な言語(コンパイル時に、識別子の意味が静的に決定される言語、JavaとかC#とか)であっても、最近の言語は一緒の場合が多いように思えます。宣言と定義の分離は、マシンの性能もコンパイラの品質も貧弱な時代に、静的な言語でコンパイルするための助けになるという意味では有用だったのかも知れませんが、現在においては、単に面倒なだけとされて、廃れた手法になっていっているのかも知れません。 最後に、上では関数の宣言と定義だけで、変数については、少しぼかした表現で、詳しく書いていません。C言語上では、その変数の実体が作られるところ(メモリ領域が確保されるところ)が定義になるのですが、細かく言うとさらに倍の内容になりますので、省いています。PHPのglobalキーワードが変数で宣言と定義がわかれている例になるのですが、PHPについては全てを語るほど詳しくないと言うこともあり、触れていませんでした。 また、もう一つぼかして書いていることで「関数が定義される」と「作成された関数が入っている変数が定義される」の(動作の違いでは無く)**意味的な**違いを詳しく解説していません。申し訳ありませんが、そこまで話をしてしまうと、どんどん話がずれて言ってしまうので、避けてしまいました。どこかで機会があれば、網羅的に宣言と定義について、記事を書きたいとは思いますが、ここでの回答はここまでとさせていただきます。