質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.59%

宣言と定義の違いについて

受付中

回答 3

投稿

  • 評価
  • クリップ 3
  • VIEW 1,955

score 13

PHP, js, rubyでの宣言と定義の違いを教えてください!!!!
下のサイトを読んだのですが、あまり内容がわからなくて・・・

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13120175843
https://msdn.microsoft.com/ja-jp/library/0e5kx78b.aspx

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • ockeghem

    2017/08/20 11:31

    なぜ宣言と定義の違いを知りたいのかと思う理由を書いたほうが、希望に沿う回答が得られやすいと思います。

    キャンセル

回答 3

+6

 ECMAScript 2017

原則として、この回答は ECMAScript 2017 の仕様を元にしています。
ECMAScript 2017 は JavaScript の根幹となる標準仕様です。

 宣言 (Declaration)

ECMAScript 2017 で「宣言 (Declaration)」と名の付く用語をリストアップします。

上記節に出てくる Syntax (文法) を日本語に翻訳します。

  • 宣言 (Declaration)
  • 巻き上げ可能な宣言 (HoistableDeclaration)
  • Class 宣言 (ClassDeclaration)
  • レキシカル宣言 (LexicalDeclaration)
  • 関数宣言 (FunctionDeclaration)
  • ジェネレータ宣言 (GeneratorDeclaration)
  • 非同期関数宣言 (AsyncFunctionDeclaration)
  • 変数宣言 (VariableDeclaration)

ここで一つの疑問が浮かびます。
宣言とは、「誰が何に向かって宣言している」のでしょうか。
この場合は「コードがパーサに向かって宣言している」といえます。
宣言はコードを構成する部品に過ぎず、中にはそれのみでは valid なコードになれないもの(例: VariableDeclaration)もあります。
パーサはあらゆる宣言を解釈して、コードを実行可能な状態にコンパイルします。

 定義 (Definition)

宣言と同様、ECMAScript 2017 で「定義 (Definition)」と名の付く用語をリストアップします。

定義は宣言よりも広い意味となります。

上記タイトルを日本語に翻訳します。

  • 関数定義 (Function Definitions)
  • アロー関数定義 (Arrow Function Definitions)
  • メソッド定義 (Method Definitions)
  • ジェネレータ関数定義 (Generator Function Definitions)
  • Class 定義 (Class Definition)
  • 非同期関数定義 (Async Function Definitions)
  • 非同期アロー関数定義 (Async Arrow Function Definitions)

定義は宣言よりも総合的な意味合いが強いように読めます。
宣言という言葉は Syntax 上に出てきますが、Definition が出てくるケースはほとんどありません(ゼロではありませんが、少なくとも上にあげたものにはありません)。
詳細は後述しますが、宣言(Declaration)という構成要素をパーサが解釈(パース)し、関数や変数が定義される事を表しているのだと思います。

 宣言する (declare)

ECMAScript syntax intentionally resembles Java syntax. ECMAScript syntax is relaxed to enable it to serve as an easy-to-use scripting language. For example, a variable is not required to have its type declared nor are types associated with properties, and defined functions are not required to have their declarations appear textually before calls to them.

機械翻訳「ECMAScript構文は意図的にJava構文に似ています。 ECMAScript構文は、使いやすいスクリプト言語として機能するように緩和されています。 たとえば、変数の型宣言やプロパティに関連する型の必要はなく、定義された関数の宣言を呼び出す前にテキストで表示する必要はありません。」

"定義された「関数の宣言」を呼び出す" とあります。
これは下記リンク先の Syntax で定義されている FunctionDeclaration (関数宣言) を表しており、「定義された FunctionDeclaration を呼び出す」と読み替えることが出来ます。

ここから読み取れるのは、宣言してから定義するという順番性です。
「関数宣言を定義する」とは、関数を「宣言」する事で「定義」が完了する事を表しています。
ですので、「関数定義を宣言する」のように逆の順番で表現することは出来ません。

 定義する (define)

A var statement declares variables that are scoped to the running execution context's VariableEnvironment.

機械翻訳「var 文(statement)は、実行中の実行コンテキストのVariableEnvironmentにスコープされている変数を宣言します(declares)。」

これは VariableStatement という変数文を解釈⇒実行する過程の一文です。
変数文は Syntax 上の VariableDeclaration (変数宣言)を解釈します。

var a, b; // 変数文全体で VariableStatement を表す。a, b がそれぞれ VariableDeclaration を表し、複数の変数宣言を解釈できる。

Within the scope of any VariableEnvironment a common BindingIdentifier may appear in more than one VariableDeclaration but those declarations collective define only one variable.

機械翻訳「VariableEnvironmentのスコープ内では、共通のBindingIdentifierが複数のVariableDeclarationに現れることがありますが、それらの宣言は1つの変数のみを定義します。」

細かな説明は省きますが、重要なのは「宣言を解釈して定義する」という順番性です。

var a = 1, b = 2;

a = 1 という変数 a の宣言」と「b = 2 という変数 b の宣言」を解釈し、VariableStatement として認められる事で 変数 a, b が定義される、となります。

var a = 1, b = !!!; // SyntaxError: Unexpected token ;

このコードは変数 a を宣言しましたが、変数 b の宣言に失敗している為、結果として変数 ab は定義されていません

 ユーザ定義関数(User-defined function)

ユーザ定義関数(User-defined function) はECMAScript 2017 に存在しない用語ですが、比較的読む見る用語の為、触れておきます。
下記の関数 foo のようなコード制作者が定義した任意の関数を「ユーザ定義関数」と呼びます。

function foo () {
  console.log(foo);
}

コード制作者は上記コードを「関数 foo を定義する」と表現します。
関数宣言(FunctionDeclaration)は関数を定義する上で最上位の Syntax なので、「関数 foo を宣言する」と表現しても概ね通じますが、最終的に関数 foo が実体化された事を強調するには「定義する」の方が好ましいといえます。

また、「ユーザ定義関数(User-defined function)」という言葉はありますが、「ユーザ宣言関数(user-declared function)」という言葉は読んだ覚えがありません。
これも宣言してから定義するという順番性を表しているのかもしれません。

 関数宣言(FunctionDeclaration)と関数式(FunctionExpression)

上記リンク先では、大別して次の2つの Syntax が定義されています。

  • 関数宣言 (FunctionDeclaration)
  • 関数式 (FunctionExpression)

関数定義は関数宣言と関数式を総合した名称です。
従って、「関数定義 === 関数宣言」ではありませんし、関数宣言と関数式は別個の存在として定義されています。

function foo () { console.log('foo'); }

上記コードは関数宣言(FunctionDeclaration)を定義しています。

(function bar () {
  console.log('bar');
}());

上記コードは関数式(FunctionExpression)を定義しています。

var piyo = function piyo () {
  console.log('piyo');
};

上記コードは関数式を定義した後に変数 piyo を定義しています。

「変数文」と「関数式」の定義が独立している点に注意して下さい。
厳密には、変数文の処理過程で変数宣言が行われている事になります(詳細は「定義する」の節を参照して下さい)。

  1. 関数式 piyo を定義する
  2. 変数 piyo を宣言する
  3. 変数 piyo を定義する

 更新履歴

  • 2017/08/21 11:41 「関数宣言(FunctionDeclaration)と関数定義(Function Definitions)」の節を追記
  • 2017/08/25 12:38 見出しを修正。関数宣言(FunctionDeclaration)と関数定義(Function Definitions) -> 関数宣言(FunctionDeclaration)と関数式(FunctionExpression)

Re: COCO-nuts さん

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/20 12:49

    FunctionDeclaration と FunctionExpression の違いに触れるのを忘れていました。
    後で追記します。

    キャンセル

+3

宣言(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については全てを語るほど詳しくないと言うこともあり、触れていませんでした。

また、もう一つぼかして書いていることで「関数が定義される」と「作成された関数が入っている変数が定義される」の(動作の違いでは無く)意味的な違いを詳しく解説していません。申し訳ありませんが、そこまで話をしてしまうと、どんどん話がずれて言ってしまうので、避けてしまいました。どこかで機会があれば、網羅的に宣言と定義について、記事を書きたいとは思いますが、ここでの回答はここまでとさせていただきます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/23 07:04

    think49さん

    最初に「C言語での宣言と定義が意味するところが」って書いていたんですけど、どうやらわかりにくかったみたいで、申し訳ありません。強調しておきました。仕様の話ではありません。

    キャンセル

  • 2017/08/25 11:57 編集

    遅くなりましたが、改めて何度か読み直しました。
    出来るだけ解釈できるように努めましたが、まだECMAScript(JavaScript)の説明に違和感が残っているように感じられました。
    - 仕様の説明ではないにも関わらず、ECMAScript 仕様の Syntax を参照しています(文脈的に「宣言」と「定義」が同義であると説明する為の根拠として用いられているようですが、仕様上では「宣言」と「定義」は別物として扱われています)
    - 関数宣言(FunctionDeclaration) には触れられていますが、関数式(FunctionExpression)及び「変数宣言(VariableDeclaration)」に触れられていません
    特に変数宣言に触れていないのが致命的で、変数宣言があるだけで解釈に大きな違いが出てくると思います。

    私は次のように解釈しました(ECMAScript 2017準拠)。
    https://jsfiddle.net/5c9vgey9/
    このコードには関数宣言(FunctionDeclaration)がありません。
    ですので、関数宣言(FunctionDeclaration)だけを元に説明するのは無理があります。

    > 動的な言語(プログラムの途中で、識別子の意味が動的に変わっていく言語、ECMAScriptとかRubyとか)においては、宣言と定義は一緒の場合が多いように思えます。
    私としては、C言語とECMAScriptの「宣言」と「定義」が同じようには感じられませんでした。
    他人に説明する際には「C言語における型宣言」のように前置きしないと誤解を生むのではないかと思いました。

    キャンセル

  • 2017/08/25 19:59 編集

    下手に仕様書の話を持ち出してしまったのが悪かったようで申し訳ありません。仕様書での意味ではないということ、それと混同しないようにすることを強調しておきました。

    変数の定義について書いていないのはわざとです。私としては`var a;`と書いただけで、C言語で意味するところの「宣言」かつ「定義」になると解釈しています(ECMAScript仕様上での「定義」を意味していると言っているのでは決してありません)。最後に言い訳を追加していますが、全て書くと長くなって、書くのがつらいので、ここでは勘弁して欲しいところです。ごめんなさい。

    私もC言語とECMAScriptで用語が全く同じだと思っていませんし、そう主張したいわけではありません。ただ、質問が複数の言語に渡ってどうなっているのか、参考にしているURLがCおよびC++であったことから、C言語での意味で、それぞれの言語がどうなっているのかを解説したのが今回の回答です。質問者が、各言語の使用上の意味での回答が知りたかったのか、C言語においての「宣言」や「定義」が各言語ではどうなっているのかを知りたかったのか、どちらを聞きたいのか一瞬悩みましたが、言語が複数であることとURLから、後者であると判断しました。

    もしかしたら、think49さんの回答のように、ECMAScript仕様書上での「宣言」「定義」を質問者が求めていたのかも知れませんが、そうであれば、私の回答は質問者の役には立たなかっただけかと思っています。

    キャンセル

+1

まず1つ言えば、言語によって対応する言葉が違ってきます

C言語では(MSDNを見ての通り定義でない宣言があるので)「宣言」と「定義」を峻別する必要が生じますが、ここで上げた3つの言語は、「中身のない関数の宣言だけ」とか「他のファイルで定義されたものを参照するだけの宣言」が存在しないので、変数や型について「定義」や「宣言」という言葉を区別する必要がありません

ただし、JavaScriptでの'use strict';やPHPのdeclareなどは、プログラム内に現れる変数や型と関連を持たずに動作を変えるものですので、「宣言」と呼ばれます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る