HOME > Learning Place >  No.2 スコープ

No.2 スコープ

それでは前回の続きをやっていきます!:)
前回、関数の呼び出し方を3種類ほど紹介しましたが、実際に何が良いのかは説明しませんでした。

今回は、「何が便利なのか」についてやっていこうと思います!

ただ、その前にJavaScriptのスコープというものを学んで頂きます。

スコープ?

JavaScriptには、現在のスコープの参照を持つthisという変数があり、今回はこのthisについて学んで行きたいと思います。(この他にも変数のスコープというものが存在しますが、それについては別の機会にご紹介します)

例えば、下記のようなコードをhtmlscriptタグにべた書きした場合

1
console.log(this);

WebインスペクタのConsoleに下記のようにwindowオブジェクトが出力されると思います。

img_slp_02_no2_01

img_slp_02_no2_02

windowオブジェクトは、JavaScriptにおけるグローバルスコープです。よって、この場合のスコープはwindowオブジェクトということが分かると思います。

次は、scriptタグ内に関数定義を行い、その中のスコープを確認してみましょう。

1
2
3
4
function checkScope() {
    console.log(this);
}
checkScope();

上記のようなコードを実装し再度ブラウザを再読込すると、またまたwindowオブジェクトが表示されると思います。これは何故かというと、先ほどのコードはscriptタグに直接関数定義を行いましたが、実はベタ書きでこのように処理を書くと、windowオブジェクトのメソッドのような形で関数定義がされます。

img_slp_02_no2_03

JavaScriptでは、windowオブジェクトは記述時には省略できるため忘れがちになりますが、checkScopeというどこからでも実行できる関数を定義したというよりは、JavaScriptのグローバルオブジェクトであるwindowオブジェクトにメソッドを追加したイメージです。

WebインスペクタのConsoleなどから、window.checkScopeと入力すると、先ほど実装した関数にアクセス出来ることが分かると思います。

以上のことから、checkScope内でのthisは呼び出し元のスコープを指すためwindowオブジェクトになります。

コロコロ変わるスコープ

前項での説明でスコープはwindowオブジェクトを指していましたが、どのような時にこのスコープが変化するのでしょうか?

簡単な例を見ていきましょう

1
2
3
4
5
6
var sampleObj = {
    log: function() {
        console.log(this);
    }
};
sampleObj.log();

この例では、sampleObjオブジェクトlogメソッドを実装してあります。このlogメソッドを実行した際に出力されるthisはどうなるか。

実行すると、下記のような値がConsoleに出力されます。

img_slp_02_no2_04

変わっちゃいましたね・・・:(

このように関数内のthis(スコープ)は、実行元が変わるとthisの内容も変化します。
this(スコープ)の説明をしましたので、1つ前の記事でご紹介した特殊な2種類の関数呼び出しについて再度説明したいと思います。

関数呼び出しその1:Function.call

1
関数名.call(スコープ対象, 引数...);

スコープの説明をしたことで、引数の意味がちょっと分かってきましたかね?:)
先ほどスコープが参照する対象は、場合によって変化すると説明しましたが callapplyもほぼ同等の動き)は、このスコープを設定することが出来るメソッドになります。

記述例
1
2
3
4
5
6
7
8
9
10
11
12
var o = {x:10};
var func = function(v) {
    if (v === undefined) {
        console.log(this.x);
    } else {
        console.log(this.x * v);
    }
}
func(); // 結果:undefined
func.call(o); // 結果:10
// 引数有り
func.call(o, 10); // 結果:100

単純にfuncを実行しても、参照がwindowオブジェクトのためundefinedになると思いますが、callを利用してスコープをオブジェクトoに設定しています。(オブジェクトoにはプロパティxを設定している)

すると、関数内でのスコープがオブジェクトoになり、プロパティxも設定されているため「10」という値が出力されるというわけです。引数を付ける場合はスコープ対象の後に、カンマ区切りで通常の関数呼び出しのよう中たちで付けてあげれば大丈夫です。

img_slp_02_no2_05

関数呼び出しその2:Function.apply

1
関数名.apply(スコープ対象, [引数...]);

引数の渡し方以外については、callと同等です。

1
2
3
4
5
6
7
8
9
10
11
12
var o = {x:10};
var func = function(v) {
    if (v === undefined) {
        console.log(this.x);
    } else {
        console.log(this.x * v);
    }
}
func(); // 結果:undefined
func.apply(o); // 結果:10
// 引数有り
func.apply(o, [10]); // 結果:100

applyは非常にcallと似ています。(実際に動き的には同等なので・・・)
ただ、引数の渡し方が配列のため、関数内で利用できるarguments(関数に渡された引数すべてを配列で受け取る特別な変数)と合わせた使われ方をよくします。

img_slp_02_no2_06

apply × arguments

JavaScriptでは引数すべてを設定しないもしくは、設定されている数より多い引数を渡すことが可能になっており、実行時に渡された引数をそのままパスすることも可能です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var app = {
    name: 'app',
    x: 2,
    y: 200,
    init: function(a, b) {
        arg.init(arguments);
    },
    multi: function() {
        return this.x * this.y;
    },
    str: function() {
        return arguments.join('') + '-' + this.name;
    }
};
var arg = {
    name: 'arg',
    x: 5,
    y: 100,
    init: function(a, b, c, d) {
        console.log(a, b, c, d);
    },
    multi: function() {
        console.log(app.multi.apply(this));
    },
    str: function() {
        console.log(app.str.apply(this));
    }
};
app.init(10, 20, 5, 'abc'); // 結果:10, 20, 5, 'abc'
arg.multi(); // 結果:500
app.str('A', 'B', 'C', 'D'); // 結果:ABCD-app
arg.str('A', 'B', 'C', 'D'); // 結果:ABCD-arg

この機構はSenchaのアーキテクチャでも利用されており、子のメソッドが実行された後に親メソッドにそのまま引数ごとパスする際などにもよく利用されます。

1
this.callParent(arguments);

スコープおよび、スコープを利用した関数呼び出しの説明は以上です!

それでは、又次回をお楽しみに:)

Learning Placeトップに戻る

PAGETOP