レキシカル環境とは
どういうものか
- キー、バリューセットのオブジェクトの様なもの
- スクリプトを読み込む前に1つレキシカル環境を作り、グローバルオブジェクトを登録する
- ブロックスコープを読み込んだら新たなレキシカル環境を作る
- この中にouterEnv(1つ外のレキシカル環境を指した情報)を持つ
- 関数スコープは定義時ではなく、実行時にレキシカル環境を作る
- 定義時には関数オブジェクトの中身にEnvironmentプロパティというものがあり、その定義時のouterEnvの情報をもつ
- レキシカル環境のouterEnvはEnvironmentプロパティの値になる
図解
クロージャとは
関数オブジェクトで、外部の変数の情報を持った関数の事
ということはJavaScriptにおいては関数オブジェクト=クロージャとなる
JavaScriptの定義ではなくもっと広いコンピュータサイエンス上の定義になる
レキシカル環境の概念を使ってできること
プライベートな変数を作り出すことができる
外から変更できないもの
レキシカル環境を作る度にthisが作られる
thisが指し示す場所の違い
グローバルオブジェクトが登録してあるレキシカル環境のthis
グローバルオブジェクトを指す
console.log(this); // グローバルオブジェクト
関数オブジェクト実行時のレキシカル環境のthis
通常 ⇒ グローバルオブジェクトを指す
strictモード ⇒ undefined
let sayThis = function(){
console.log(this);
}
sayThis(); // use strict ? undefined : global object
メソッド実行時のレキシカル環境のthis
メソッドが登録してあるオブジェクトを指す
let sayThis = function(){
console.log(this);
}
const car = {
color: 'red',
sayThis,
};
car.sayThis(); // carのオブジェクトを指す
アロー関数におけるthis
どんな呼び出され方をしてもアロー関数はthisを持たない!!
レキシカル環境にthisがないときはouterEnvのthisを参照する
let sayThis = () => {
console.log(this);
}
const car = {
color: 'red',
sayThis,
};
car.sayThis(); // thisがないのでcarオブジェクトの持つouterEnvのglobal objectを出力する
thisを持たないメリット
メソッドの中でコールバック関数を定義するとき
let logging = (cb) => {
console.log(cd())
};
const car = {
color: 'red',
changeColor: function(color) {
// 通常の関数で書くと、、*A
logging(function()=>{
return this.color;
})
// アロー関数で書けば、、*B
logging(()=>{
return this.color;
})
}
}
car.changeColor(white);
*A. このthisは「関数オブジェクト実行時のレキシカル環境のthis」なので // strict ? undefiend : global.objectとなる
*B. アロー関数なのでthisを持たず、cb()実行時に作られるouterEnvのcarがthisとなる
thisを指定する
thisを指定して呼び出す
callとapply
違いは引数の渡し方
どちらを使っても良い
let sayThis = function(a,b) {
console.log(this);
}
// thisを{hello: 'hello'}のオブジェクトに指定する↓下記の結果は同じ
sayThis.call({hello: 'hello'}, a, b); // 1個づつ渡す
sayThis.apply({hello: 'hello'}, [a, b]); // 配列で渡す
thisを指定して新しい関数オブジェクトを生成する
bind
返り値は関数オブジェクト
引数を与えると固定された引数を定義できる
let sayThis = function(a,b) {
console.log(this);
}
sayThis = sayThis.bind({hello: 'hello'}); // 新しい関数オブジェクトを生成する
sayThis(1,2); // {hello: 'hello'} 1 2
sayThis = sayThis.bind({hi: 'hi'}, 1); // 第一引数のみ固定
sayThis(); // {hi: 'hi'} 1 undefined
sayThis(2); // {hi: 'hi'} 1 2