JavaScript の関数について色々見聞きした学んだことをメモっています。

 

 

C言語や Java などからすると、JavaScript の関数っていうのは、いろいろな側面があって戸惑ってしまう部分も多いのですが、ちょっと頭の中で断片的に蓄積されている情報を、ここでまとめておきたい欲求にかられてしまいました。

ここでは、JavaScript における関数について、まとめてみたことを何回かに分けて記録しておきたいと思います。

 

関数の基本

function キーワードを使用して関数を定義します。

function name(pram1, param2, param3) {
  // 実行されるコード
}
  • 関数名は英数字と下線 (_)、ドルマーク ($)を含めることができます。
  • 関数名のあとに続く括弧 (()) 内には、引数としてコンマで区切ってパラメーターを含むことができます。
  • 関数内では引数はローカル変数として扱えます。
  • 実行するコードは波括弧 () の中に置きます。

 

() 演算子は関数を実行します

function toFahrenheit(celsius) {
  return (celsius + (32 / (5/9) ) ) / (5/9);
}

console.log(toFahrenheit);
console.log(toFahrenheit());

toFahrenheitFunctionオブジェクト を参照しており、toFahrenheit() はこの関数の結果を参照しています。

関数を () なしでアクセスすると、関数の結果の代わりに Function オブジェクトを返します。

 

ローカル変数

関数の中で定義された変数は、関数の外からは見えません。

function hoge() {
    var fuga = "fuga";
    return fuga;
}

console.log(fuga); // fuga is not defined でエラー

 

関数内で定義された変数は、関数の外の変数を侵食しません。

var x = 10;
var a = function() {
    var x = 0;
    return x;
};

console.log(a()); // > 0 (関数内で定義された x)
console.log(x); // > 10  (関数の外で定義された x)

関数の内側と外側で同じ変数名を使うのはもちろん避けるべきですが、関数の内側のスコープをカプセル化できることは確認できます。

もちろん外側のスコープの変数は参照できます。

var x = 10;
var a = function() {
    x++;
    console.log(x); 
};

a(); // > 11
console.log(x); // > 11

 

関数定義

関数宣言文

次のような関数の定義を、関数宣言文といいます。英語では、function definitionfunction declaration または、function statement と呼ばれます。

function 関数名( ... ) { ... }

Java や C言語 などと同じような一番シンプルな書き方。

function sum(a, b) {
    var result = a + b;
    return result;
}

var answer = sum(1, 2);
console.log(answer); // > 3

 

関数宣言の場合は、巻き上げ(hoisting) できるので、関数宣言をした行よりも前からでも呼ぶことができます。。

hoge( ... ); // 関数の実行 <-- 使用できる

// ここで関数宣言
function hoge(...) {
    ...
}

hoge( ... ) // 関数の実行 <-- 使用できる

 

関数式

関数式 (function expressions) は

function( ... ) { ... };

あるいは、

function 関数名 ( ... ) { ... };

になります。

式の最後は、原則セミコロンが必要ですが、なくてもエラーにはなりません。

ここで気をつけておきたいのは、

関数式 = 無名関数 (anonymous function) ではない。

ということです。

その証拠に、関数リテラル式に名前を付けてもエラーにはなりません。

// 名前のない関数リテラル式
var sum = function(a, b) {
    // 処理
};
sum(1,2); // > 3
// 名前のある関数リテラル式
var sum = function sum2(a, b) {
    // 処理
};
sum(1,2); // > 3
sum2(1,2); // sum2 は未定義でエラー

 

ようするに、関数式は「名前をつけなくても良い」、ということになります。

こういった「名前のない関数」を、無名関数とか、匿名関数 と呼びます。

関数式で着けた関数名は、その関数オブジェクトの外からは参照できません。

ただし、自身の関数オブジェクトの中から参照することができます。

また、スタックトレースで関数名を特定するのに利用できます。

var count = 0;
var counter = function countup() {
    console.log(count);
    if (count < 10) {
        count++;
        countup(); // 関数リテラル式を名前で呼び出している
    }
};

countup(); // countup is not defined でエラーになる

 

また、関数式の場合は、関数宣言文と違って巻き上げできません。

sum(1,2); // sum is not afunction でエラー

var sum = function(a, b) {
    // 処理
}

 

Function() コンストラクター

JavaScript の関数は、全て Function オブジェクトになります。

Function オブジェクトのコンストラクターを使用して、関数を生成することができます。

const sum = new Function('a', 'b', 'return a + b');
console.log(sum(2, 3)); // -> 5

・・ただ、この方法で関数を生成するのを見たことないですし、使ったこともないですね。

 

即時に実行できる関数

関数式を、直接行頭から書き始めることはできません。

function() { ... }  // NG

ただし、行頭に + をつけるとエラーにはなりません。

+function() {console.log("無名関数");};

ちなみに、console.log でみてみると、

console.log(+function() {console.log("無名関数");}); //  > NaN
console.log(function() {console.log("無名関数");});  //  > [Function]

となります。

最初の結果は、+ 演算子がついているので、数値を返すことが期待 されていますが、関数は数値を返さないので、NaN (Not A Number) となっています。

また、この関数リテラル式の最後に () をつけると、関数が呼び出されます。

+function() {console.log("無名関数");}(); // > 無名関数

いろいろな JavaScript のソースを眺めていると、! で始めるような書き方もあるみたいですね。

! function() { /* ステートメント*/}();

この場合は否定演算子として働くので、関数の戻り値がある場合にはその値に応じて truefalse を、戻り値がない関数の場合は、単に true を返します。

また、+! 演算子の代わりに () で無名関数を囲む事もできます。

(function() {...});

このカッコは、算術演算子のグループ演算子として機能して機能しています。要するに、

(2 + 3) * 4

のカッコと意味は同じになります。

この書き方も最後に () をつけることで、実行することができます。

ここまでくるとよく見る、いわゆる即時関数 (self-invoking function) と呼ばれている構文になるのがわかると思います。

(function() { ... })();

即時関数の様々な書き方。

(function() {
    console.log("即時関数1");
})(); // 最後の () を外に出している。

(function() {
    console.log("即時関数2");
}()); // 最後の () を内側に入れている。

console.log(function() {
    return "即時関数を実行";
}()); // 行頭からではない場合は、関数式を () でくるまなくても良い。

即時関数に引数を渡す場合は、次のようにします。

(function(a, b) {
    console.log(a + b);
}(1,2));

グローバルスコープの変数を即時関数の引数として渡すと、関数内ではローカル変数になるので、グローバルスコープの値を侵食しません。

var x = 10;

(function(x) {
    console.log("即時関数のスコープ:" + (++x)) // > 11
}(x));

console.log("グローバルスコープ:" + x); // > 10

 

関数はオブジェクト

[先程](#Function()コンストラクター) にも書いたとおり、関数はオブジェクトになります。

関数はオブジェクトなのでこんな事ができます。

function sum(a, b) {
    var result = a + b;
    return result;
}

var sum2 = sum; // sum の参照を sum2 に代入
console.log(sum2(2, 3)); // > 5

sum() だと、関数を実行して、その結果を変数に代入するが、sum として代入すると、オブジェクトへの参照が sum2 に代入される。

同様にオブジェクトなので、こんな事もできます。

sum.hoge = "fuga"; // sum オブジェクトにプロパティ fuga を追加
console.log(sum.hoge); // > fuga

このとき、sum2 は、sum の参照なので、

console.log(sum2.hoge); // > fuga

となります。

値以外にも、関数をプロパティ値に関数を指定することもできます。

sum.fuga = function() {
  return "fuga";
}

sum.fuga(); // -> fuga

関数名に () を付けずにオブジェクトの参照を console.log() で表示させると、同じオブジェクトであることを確認できる。

console.log(sum);  // > [Function: sum] { hoge: 'fuga' }
console.log(sum2); // > [Function: sum] { hoge: 'fuga' }

関数定義文の場合と同様、関数式の関数もオブジェクトになります。

var sum = function(a, b) {
    var result = a + b;
    return result;
}

console.log(sum(1,2)); // > 3

var sum2 = sum;
console.log(sum(3,4)); // > 7

関数式を変数に代入することと、関数オブジェクトの参照を変数に代入することは、方法は違えどやっていることは同じで、オブジェクトへの参照を変数に代入する、ということになります。

続く


 
記事を共有する
カテゴリー: javascript

zaturendo

中小企業社内SE。

0件のコメント

コメントを残す

メールアドレスが公開されることはありません。