CreateLife ~AlwaysLatest~

レキシカル環境とは

JavaScript

どういうものか

  • キー、バリューセットのオブジェクトの様なもの
  • スクリプトを読み込む前に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