LoginSignup
67
72

More than 3 years have passed since last update.

ES6の構文じゃなくて、あえてそれ以前のJavaScriptの基礎を押さえる

Last updated at Posted at 2019-11-06

ES6でいいのでは?

ES6のclass等は、本来あった文法のシンタックスシュガー。なので、ES6の便利な書き方じゃないJavaScriptを押さえて、JS使えます!!って言えるようになりたい。

この記事では、オブジェクト・関数・スコープチェーン(クロージャー)について記載します。

JavaScriptのオブジェクト

一般的なプログラミング言語で使われるすべてのモノを表す文脈で使用される場合と、Objectオブジェクトのインスタンス(生成元のオブジェクトのプロパティを継承したオブジェクト)として使われる場合がある。
前者はいいとして、後者は以下のようになる。

var obj = {
  name: 'objectName',
  getObj: function() {
    console.log(this.name);
  }
}

console.log(obj.name); // objectName
obj.getObj(); // objectName

この変数に代入している{}Objectオブジェクトの生成。new Object()で生成するのと同じ(シンタックスシュガー)。オブジェクト自身には、プロパティnameやメソッドgetObjを定義することができる。

var obj1 = {};
var obj2 = new Object();
console.log(typeof(obj1)) // object
console.log(typeof(obj2)) // object

関数について

関数もオブジェクトの仲間

JavaScriptの関数は第一級オブジェクトである。(これ言ってみたかった)
第一級オブジェクト=生成、代入、演算などが制限なく行える対象 by wiki
... ちょっと難しいが、12などの数値、abなどの文字列などのリテラルと言われる対象のこと。ここで大事なのは関数もオブジェクトなので、変数に自由に代入したりできるということだけ。

var foo = function() { console.log('foooooo'); }
foo() // foooooo

通常の関数呼び出しと、new付きの関数呼び出し

定義した関数を呼び出すときに、newをつけるかつけないかで処理が大きく変わる。

var testFunc = function() {
  this.name = 'foo';
}
var obj1 = new testFunc();
var obj2 = testFunc();
console.log(obj1); // testFunc {name: 'foo'}
console.log(obj2); // undefined

obj2の場合はnewがないので、ただの関数呼び出しになる。returnがない関数の戻り値はundefinedなのでundefinedエラーになってしまう。
一方、obj1の場合はインスタンスが生成されていることがわかる。これはnewしたときに以下のような暗黙の処理が入ることによる。

var testFunc = function() {
  // var this = {}
  this.name = 'foo';
  // return this;
}

コメントアウトした部分が暗黙の処理にあたる。
this1オブジェクトが内部で定義され、そのオブジェクトにnameプロパティがセットされて、thisオブジェクトを返却するという処理を行ってくれると考えると(個人的には..)わかりやすい。

スコープチェーンについて

JavaScriptのイケてるクロージャーを理解するためにはスコープチェーンを理解する必要がある。=プログラミングの裏側で何が起こってるかを意識する必要がある。
まず、JavaScriptを実行すると、グローバルスコープ上にオブジェクトの最上位存在であるGlobalオブジェクトが自動で生成される。例えば、webブラウザのconsoleで実行した場合はwindow2オブジェクトがグローバルオブジェクトになる。グローバル変数の定義=windowオブジェクトのプロパティ定義である。

var foo = 'foo';
foo === window.foo; //true

次に、関数を呼び出す度に自動で生成されるオブジェクトがCallオブジェクトで、関数内のローカル変数・引数・親Callオブジェクトのアドレスなどを管理3する。
このCallオブジェクトをチェーンのように連結したリストは、Globalオブジェクトを末端にし、Callオブジェクトが生成されるたびに伸びていく。
変数などを探索する場合は、Globalオブジェクトと逆の先端から探索して最初に見つかった値を返却する。このリストがスコープチェーンといわれる。

// Globalオブジェクトが生成される #1
var prop1 = 'global';
var prop2 = 'global';  

// outer関数用のCallオブジェクトが生成される #2
function outer() {
  var prop1 = 'outer';

  // inner関数用のCallオブジェクトが生成される #3
  function inner() {
    var prop3 = 'inner';

    console.log(prop1); // outer #3→#2に存在するため
    console.log(prop2); // global #3→#2→#1に存在するため
    console.log(prop3); // inner #3に存在するため
  }

  inner();
  console.log(prop1); // outer #3→#2に存在するため
}  

outer();

上記のサンプルでスコープチェーンを確認すると、Globalオブジェクト(#1)、outerオブジェクト用のCallオブジェクト(#2)、innerオブジェクト用のCallオブジェクト(#3)の順でリストが生成され、#3→#2→#1の順で変数の値を探索していき、値があればその値を返却する。ない場合はReferenceError

クロージャーについて

スコープチェーンがわかればクロージャーを(たぶん)理解したようなもの。
クロージャーとは、自身(関数)を定義している関数の中にローカル変数の参照があるもの。ローカル変数は通常、関数呼び出し後に破棄されるが、クロージャーはローカル変数を参照し続けることが可能。ローカル変数の参照を関数内に閉じ込める(closure)ということ。
先ほどのサンプルのinner関数もクロージャーといえる。が、わかりづらいのでよくあるカウントアップを記載する。

var counterFunc = function() {
  var count = 0;
  return function() {
    count++;
    console.log(count);
  };
};

var counter = counterFunc();
counter(); // 1
counter(); // 2
counter(); // 3

return以下の即時関数内に、counterFuncのローカル変数countを閉じ込める。counter変数内にcounterFuncを代入すると、グローバル変数countercounterFuncを通してcountを参照し続けるので、Glovalオブジェクトがある限り参照され続けるので値が破棄(ガベージコレクション)されず残り続ける。

prototypeについて

オブジェクトはプロトタイプをコピーしたものを元に作成され、すべてのオブジェクトはプロトタイプを参照している。プロトタイプで作成されたメソッドはオブジェクトが継承することができる。
オブジェクト指向言語のメソッドに近く、ES6のclassもこれを元にしている。prototypeを使うメリットは主に、メソッドを作るよりメモリ使用率が少なくなる4こと。
名前はかっこいいが、わかりにくい概念ではなさそう5なので使い方のみ記載。

  Object.prototype.sum = function(arg1, arg2){
    return arg1 + arg2;
  }
  var foo = {}
  console.log(foo.sum(1,2));

最後に

JavaScriptはふわっとしか触ってこなかったので、自分なりにまとめるのは勉強になりました。得意な方のツッコミお待ちしてます。
次はthisとか、関数巻き上げみたいな特殊(?)なものをまとめる予定です。

2019/11/7
続きっぽいもの書きました。

特に参考にしたサイトなど


  1. thisについては別途まとめようかと。 

  2. 何となく、昔JavaScript界隈でよく聞いたDOMを思い出して、ちょっと調べた。document.getElementById的な。数多くあるライブラリでもうすっかり見なくなったが。。DOM=DocumentObjectModelの略で、windowオブジェクト->documentオブジェクトの下にhtmlタグとかが存在して、それらの文章をツリー上に表現したものらしい。documentもwindowオブジェクトのプロパティの一部。 

  3. メモリ管理はヒープ領域で行っていて、ガベージコレクションする言語とのこと。なので、クロージャーとか使えるのかな? 

  4. インスタンス生成時はプロパティやメソッド分のメモリを確保するが、prototypeだと参照だけを持つのでその分が節約になる。 

  5. やっていくうちに難しいと感じたらまとめ直す予定です。 

67
72
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
67
72