Edited at

javascriptをもう一度、基礎の事から本読んで勉強してみた。さらに、理解を深めるために記事を書こうと思った。

More than 3 years have passed since last update.


概要

必要なときに、必要なページを開いて使っていた

「パーフェクトJavaScript」という本。(以下、本書と略します。)

なんだかんだ基礎を疎かにして雰囲気で使っていましたが、それは駄目だなぁ。と思いました。

part1~3までを一通り読み、更に理解を深めるために自分なりな記事にしてみよう!

そんな感じの理由で生まれた記事です。

ちょっと冗長ぎみで、もったりした文章ですが...せっかくなんでqiitaに上げますね。

細かい部分はあえて書きません。本買って読んでね!


Part1 JavaScriptとは。


JavaScript

1995年にネットスケープコミュニケーションズ社がJavaScriptを公開したらしい。

そ、そうだったのか。割りと歴史が浅いんだな。


ECMAScript

時々JavaScriptに触れていると見る単語。

当時、JavaScriptの互換としてマイクロソフト社がJScriptなるものを開発したそうなんです。

が、結構独自拡張が多かったり、JavaScriptの仕様が分裂する危険があった。

ということでEcma Internationalという標準化団体にネットスケープ社がJavaScriptを提出した。

その時に標準言語仕様の名前をECMAScriptと言うそうです。

ECMAScript準拠の機能がJavaScript標準と言うことなのかな。

現在ECMAScriptは5.1th Editionが主流っぽい。

2015年の6月にECMAScriptの6th Editionが公開されたようです。ただ、このバージョン6に対応するブラウザは最新のものだけに限られる。

ので未だしばらくはバージョン5が使われていくと思う。まぁ時間の問題ですね。実際qiitaでも既にECMAScript6の記事とかは投稿されつつあるし....


クライアントサイドとサーバサイドのJavaScript

パーフェクトJavaScriptでメインとなる話がクライアントサイド。

所謂「Webブラウザ上で動くJavaScript」の事を指す。

そうじゃなくて「サーバー上で動くJavaScript」がサーバサイド。そのまんまですね。


ソースコードの圧縮って効果あるの?

所謂min化ですよね。改行消したり、コメントアウト消したりするからふんわり「早くなるんだろうなぁ」程度で把握していました。ええ、間違いなく有効だそうです。


  • 転送量減による待ち時間の短縮

  • インタプリタ言語(コンパイル無しで実行できる)なので、コード解釈のスピードが早くなる

主にこの2点の効果が得られそう。


おまけ - 適当にjsを書いて動かしたい時

OSXのターミナルでJavascriptを動かす

がすぐに実践できそう。

あとはchromeの開発者ツールのコンソールとかでも出来るので、ちょっと試したい時とかすごく便利ですね。

余談ですが、

「普段のJavaScript?それともjQueryなの?」というような謎の質問を受けた際に

「いや、jQueryはJavaScriptのライブラリーだよ」と答えても理解されない際には

「JavaScriptをパワーアップさせる道具がjQueryだよ」と言えば分かってもらえるんじゃないかなと書いてて思った...


Part2 JavaScriptの言語仕様

基礎からきちんとやっていきますよー。


特徴

自分がぱっと思いつくだけだと


  • 動的型

  • インタプリタ言語

というところですが、本書を読むと


  • C言語やJavaに似ている構文

  • プロトタイプベースのオブジェクト指向

  • 関数型プログラミング

という部分も特徴のようです。

確かに、似ているって言う部分は取り組みやすくなっていいかも。

関数型な部分とか、ふんわり使ってたけど、確かにコレってJavaScriptだと重要な部分だよなぁ。しっかりやらないとな。


変数の基礎


varで変数宣言。そして最近constも出来た。

var a = 1

var b = "stringだよ"

var c = 2,d = '文字だよ'

a + c //=>3
b + d //=>stringだよ文字だよ

と言った具合ですね。そしてECMAScript6からは

const name = "太郎"

のような定数で設定できるようにもなりました!

(注意:未対応なブラウザでは変数として処理されたりする模様。)

ちなみに、varは省略可能です。が基本は絶対付けて変数を用意するべき。

可読性も下がるし、突然グローバルで出てきたのかと焦るし....いいことないです。


関数の基礎

//関数の宣言

function f1(){
return 'hello';
}
//関数リテラル
var f2 = function(){
return 'hello';
}

function sum1(a,b){
return a+b
}

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

関数を変数に突っ込んでるやつを関数リテラル式と言います。

関数リテラル式の返す値は「関数オブジェクトの参照」らしいです。上記4つをtypeofで調べると全部「function」が返ってきます。

なので、1番目と2番目、3番目と4番目はほぼ同じ事をしています。文法的な違いはなく、関数名があるかないかくらいです。

また、関数はあくまで式。と言うことでreturnが暗黙的に存在するようです。関数にreturnが設定されてないとundefinedが返ってきます。

function test(){

sonsole.log("やっほー")
}

typeof test() //=>コンソール上に「やっほー」と表示されるが、typeofの結果としてundefinedが得られる

このコードがクロージャという物の理解にちょっと関連してます。内部的な僅かな違いが関数とクロージャの差になってるみたいですが、また後の話。

また、関数リテラル式は式なので、式の中に欠けるという点があるそうです。例えば

var col = function n(){

return "1";
col = function(){
return "2";
col = n;
}
}

という面白いコードも書けます。

これでcol()を繰り返し実行すると1,2,1,2と順番に返ってきます。関数リテラルは、あくまで式。というのが興味深い....


オブジェクトの基礎

オブジェクトって、あれですよ。クラスの実体と状態を持つ、いわゆるインスタンス化したりする奴。

でも、JavaScriptはもっとシンプルに、名前と値のペアの集合体です。

名前と値のペアはプロパティと言う言い方もできるので、プロパティの集合体。という言い方も出来ますね。

var a = {x:1 , y:2}

var b = {x:1 , y:2,} //おしりにカンマついていても一応大丈夫らしい。が、IEだと不具合があるので回避推奨。IEぇ...

...なんだか連想配列みたいだなぁ。と思っていたら認識としては間違っていないようです。

それぞれのプロパティへのアクセスはドット演算子、ブラケット(これ=>[])演算子を使うと可能です。

a.x//=>1

a.y//=>2

a['x']//=>1
a['y']//=>1

typeof a //=> object

さて、これが出来ると、Javaとかでお馴染みのクラスっぽい事ができます。

var nearClass = {

x:1,
y:2,
sum:function(){
return nearClass.x + this.y //オブジェクト内変数にアクセスする方法はthisや、自分自身の変数名
}
}
nearClass.sum() //=> 3

やった!これでグローバルな変数の汚染を回避して変数を使えるぞ!

クライアントサイドのJavaScriptだと、大抵の場合ファイルを分割して作ることが良くありますよね。

知らぬ間に何処かでグローバルな変数を宣言していて、それがたまたまどこかで変数名が被っちゃったりすると...地獄ですよね。

** 「もう何処で何が書き換わっているのかわけがわからないよ!\(^o^)/」不可避 **

オブジェクトを上手く作らないと、スコープ内の変数を活用することで周りに迷惑かけないコードが書けませんね...

また、途中で書き加えることも可能。まぁややこしくなるので普通はしないと思います。使う機会あるかな?

var y = 6

nearClass.sum2 = function(){
return nearClass.x + y
}
nearClass.sum3 = function(){
return this.y + y
}
nearClass.sum2() //=> 7
nearClass.sum3() //=> 8

グローバルな変数を参照することも出来ますよ。


クラスは無いんだ! ....new式?あ、有るんだけど、でも仕様的にクラスは無いんです(´;ω;`)

突如現れたnew。何だnewあるじゃねぇーか!騙されたぁ!(騙してない)

...まぁエンジニアの共通言語で分かりやすい言葉だったんだろうけど、紛らわしさが半端ない。

やはり、既知のクラスのような言語仕様はJavaScriptには無い。

var naniKore = new Object();

そう、クラスは無い、のだが...。じゃあ、これなんなの?

ってことなんですが

typeof naniKore //=>object

オブジェクトです。つまり、プロパティの集合体。名前と値の集合体だぁ!

...え?それnewするって何?

読み進めていくと、これはコンストラクタ呼び出しをしているようです。

//コンストラクタ名はアッパーキャメルケースで書くのが慣習らしい。

//クラス定義もどき...???
function MyClass(x,y){
this.x = x
this.y = y
this.show = function(){
console.log(this.x, this.y)
}
}
var mc = new MyClass(3,4)

typeof MyClass //=> function
typeof mc  //=> object

変数mcにmyClassという関数をオブジェクトとして実体化した...という感じだろうか。

typeof MyClass //=> function

typeof MyClass() //=> undefined
typeof new MyClass() //=> object

コンストラクタの呼び出しには暗黙的に、関数の最後にreturn thisが有るような動作をするようです。

つまり、MyClass内部の物を一塊に宣言した変数に突っ込むという挙動になる。んですかね?

つまり

mc = {

x:3,
y:4,
show:function(){
console.log(thisx,this.y)
}
}

という感じになるんだな。まぁ、慣れれば便利か?

と思うけど本書によればこれには2つ問題があるそうだ


  • 全てが同じ関数定義の実体コピーになるので効率が悪い

  • privateとかpublicなアクセス制御が出来ない。

しかし、前者はプロトタイプ継承、後者はクロージャで解決できるそうです。

じゃあ、これは後ほど....関数については一旦切り。


配列の基礎

//配列リテラル

var arr = [1, 3, 5, 7, 11]

オブジェクトの名前が無いバージョンで、中括弧から角括弧になっている。のが配列。

それぞれ添字、Indexを使うことでアクセスできます。

arr[0] //=>1

arr[1] //=>3
arr[2] //=>5
arr[3] //=>7
arr[4] //=>11

...本書の順で紹介しているんだけど、これ圧倒的にオブジェクトより簡単。

こっちから説明したほうがいいのでは!?


文字列型 => string

数値型 => number

ブーリアン型 => boolean

null型 => null

undefined型 => undefined

オブジェクト型 => object

この6種類の型に分類されるようですね。


動的型

これは方じゃなくて、変数の型を動的に変える仕様のことです。

var a = 1

a = 'hello'

これでerrorが怒らないです。

値によって変数が型を固定しないため、なんでもポンポン入れることが出来ます。

まぁ、基本しないでね。わけわからなくなるので。

...swiftだとerrorになりますもんね。(突然の他言語!)


文字列型

文字列型はあくまで文字列型。なんだけど...暗黙的にStringオブジェクトへの変換が起きるそうです。

Stringオブジェクトへの型変換。つまり、String内部で定義されている関数とかがドット演算子などで呼べる。

var h = 'hello'

h.length //=> 5 文字列値のプロパティアクセスに見える
'hello'.length //=> 5 文字列リテラル値のプロパティアクセスに見える

うぅん...まぁ、ぼんやりわかる。つまりlengthという関数を使うために

new String(h)

というワンアクションが挟まっている。ってことなんだろう。

このStringオブジェクトと言うのは自分で宣言することも出来る。

var h = 'hello'

typeof h //=> string

var ch = String(h)
typeof ch //=> string

var oh = new String(h)
typeof oh //=> object

...まぁ暗黙手に変換してくれるんだから、Stringオブジェクトとかあんまり意識しなくていいみたいですね。

寧ろ文字列オブジェクトの利用で生成は避けるべき。そんなことしなくてもStringオブジェクトの関数は使えるし。

混乱を招くというデメリットの大きさと、ほぼ皆無なメリット。new String()する必要はないですね。


文字列は不変!

文字列値や、文字列オブジェクトは不変。だそうです。

var h = 'hello'

var H = h.toUpperCase() //=> HELLO

hの中身を取り出し、変換して、Hに入れている。

HELLOという文字列オブジェクトを生成しているわけなんですね。

一見普通そうですが...

配列だと、要素をとったり消したり、破壊的なオブジェクトだったりすると、順番に何が起きているか追っていかないと意味不明になる。

というのが起こる。不変って素晴らしいんですね!


数値型

JavaScriptの数値は64ビットの浮動小数点数。だそうです。

整数型とか、浮動小数点数型という別け隔てがないから、数値の型変換にまつわるバグとは無縁!

ただ、浮動小数点数の計算のバグからは開放されてないです。



0.1 + 0.2

//=> 0.30000000000000004

な、なんじゃこりゃああああああああ!?

JavaScriptにかかわらずこういった浮動小数点のバグは存在するようです。気を付けようね(^ω^; )


特別な値

むげぇえええええええええんだぁああああああああいなぁああああああああああry

>>> Number.POSITIVE_INFINITY

Infinity

>>> Number.NEGATIVE_INFINITY
-Infinity

>>> Number.NaN
NaN

この3つは一応数値という実体を持っていますが....(typeofでnumber)


  • Number.POSITIVE_INFINITY
    正の方向に無限大。

  • Number.NEGATIVE_INFINITY
    負の方向に無限大。

こいつらは割っても足しても無限!演算出来る性質を失っているといえます。

...負の数値とか掛け算されると逆転しちゃうけど。


  • Number.NaN

何こいつ(^ω^; )

特別中の特別。コイツはアホの子です。

どんなに演算してもNaNから変わりません。もう数値としての実体すら失っているんじゃないかと思いますが、安心して下さい。数値です。(Number)

NaNは演算においてどんな数字とも同値にならない上に、NaN同士の同値判定すらこけます!

>>> NaN == 1

false
>>> NaN === 1
false
>>> NaN == NaN
false
>>> NaN === NaN
false

ちょ、おま!

>>> NaN < 1

false
>>> NaN > 1
false
>>> NaN > NaN
false
>>> NaN >= NaN
false

お前何がしたいんだ!?(゚Д゚ ;)

一応isNaNという関数が用意されていて、バグった時は見つけ出すことも出来ます。

isNaN(NaN) //=> true


文字列型とか数値型の比較

①JavaScriptには === == !== != 同値比較演算子が有る。3つのやつなんなの?

var b = 1 //これは数値型

var B = '1' //文字列型

b === B //=>false わかる
b == B //=>true !?

これでなんとなく答えが分かったかもしれませんが

「比較時に型変換するかしないか」

というのが違いになります。

②文字列の比較

Unicodeのコードポイントベースだそうです。(詳しくないので意味は自分で調べて!

var c ='a'

var C ='b'
c < C //=> true


ブーリアン型

特に言うこと無し。

しいて言うなら同値判定で

var t = 'true'

var T = true

t == T //=> true

t === T //=> false

ということが起きるから気をつけるのだ!


null型

JavaScriptのnullはtypeofの結果から言うと

なぜかobjectという...(゚Д゚)

nullじゃないのかよ。でもnullに対応する関数は存在せず。

なにか適当にドット演算の運用は高確率でTypeErrorになるようです。

時には便利だったりするんだけど、バグの温床であることはどこでも変わらずのようです。


undefined型

nullっぽいけど、undefinedはundefined。

typeof undefined は undefinedです。

グローバルな変数としてundefinedというのがあり、コイツがundefined値を返すと認識しておけばOKかなぁ。

undefinedさんはいつでも君の油断を狙っているぞ...!


  • その変数、未代入ですか!?じゃあちょっとおじゃましますね...

  • へ? 僕そんなプロパティ持ってないよ! undefinedさん代打おなしゃす!

  • 関数内に対応する関数に、値がないじゃん! 仕方ない、undefinedにまかせておけ!

  • その関数、return文ないんですか?僕は入っておきますね!

自己主張激しいやつですね。


オブジェクト型

上記で説明した型以外は全てオブジェクト型です。

なので、objectとかfunctionはオブジェクト型だ!


型変換

弱い型付けであるJavaScriptは、気をつけていないと変数の型がコロコロ変わります。

例えば

'100' - 1 //=> 99 (number)

気を利かせすぎですJS先輩...そこはerrorでもええんやで...

JavaScriptは型変換に起因するバグが発生しやすい言語と本書にも書いてあります!気をつけよう!


君、連想配列くんに似ているね

ぎく...でも僕オブジェクトやし...


プロパティの属性

一応オブジェクトが持つプロパティにそれぞれ、書き換えOKとか、for inで列挙可能とか有るらしい。

ECMASCript5から有るようなんですが、あんまり標準的じゃないみたい。あんまり見たことないです。

でも堅牢に作るには結構優秀な仕様ですので、今後拡がるかも、しれない。


プロトタイプ継承・プロトタイプチェーン

超複雑。

コンストラクタのメモリを大きく食う問題を解決する手法だけども、結構混乱しやすい。本何度読み返しても難しい。

function MyClass(x,y){

this.x = x
this.y = y
}
MyClass.prototype.show = function(){
console.log(this.x, this.y)
}

obj = new MyClass(3,4)
obj.show() //=> 3 4

というような感じです(^ω^)

...(^ω^)?

MyClass.prototype.show = function(){

(´・ω・`;) ???

更に

//既にobj = new MyClass(3,4)済みとして

MyClass.prototype.showx = function(){
console.log(this.x)
}

obj.showx() //=> 3

後付もいける。

こういうのでもいけるにはいけるんだけど

obj.showy = function(){

console.log(this.y)
}
obj.showy() //=> 4

わざわざprototypeと挟むのは一体何なの?

という話。

まず、すべての関数(オブジェクト)はprototypeというプロパティを持ちます。便宜上そのプロパティの参照先を、prototypeオブジェクトとする。

それで、全てのオブジェクトはオブジェクト生成に使ったコンストラクタのprototypeオブジェクトへの(隠し)リンクを持つ

これを、プロトタイプチェーンと呼ばれる仕様のようです。

という仕様があります。

obj2 = new MyClass(7,8)

obj2.showx() //=> 7
obj2.showy() //=> TypeError!! そんなプロパティは無いよ!

そんなこんなで、個別にnewしたやつに追加した関数は見に行けません。

が、prototypeを通してprototypeオブジェクトに宣言した関数は後からnewして作ったオブジェクトからでも呼びにいけます。

もちろん、別のクラス定義(もどき)からは先程prototypeオブジェクトに宣言したものは予備に行けません。

function MyClass2(x, y, z){

this.x = x
this.y = y
this.z = z
}
var mc2 = new MyClass2(8,9,10)

mc2.showx() //=> TypeError!!

ちなみに、prototypeチェーンは優先度が低いです。

例えば

obj = new MyClass(3,4)

MyClass.prototype.y = 10
obj.y //=> 4

こんな感じでオブジェクトがもともと持つプロパティが先に見られて

delete obj.y //objがもつyプロパティを削除

obj.y //=>10

消すとプロトタイプチェーンを追うようになります。

つまり、プロパティの読み込みをするときに、まず自分自身のプロパティを探しに行って、なければprototypeオブジェクトを探す。という感じですね。

どうでしょう?意味わかります?

僕本をしっかり読むまでprototypeという仕様すら知らなかったっす...

深入りすると説明する量が多くなるので、他にも詳しい記事を探してみよう!

いやぁ...オブジェクトは奥が深いぜ....


関数とクロージャ

これで最後にしますね。


スコープ

変数名の有効範囲のあれですね!

これについてはいろんな言語でいろんな記事がありますよねぇ。すごく参考になります。

JacaScriptでは「グローバルスコープ」「関数スコープ」の二種類がありますね。

関数スコープって、Javaだとローカルロコープ(クラスのメソッド内の変数)とはちょっと違うような挙動をします。

var x = 1

function f() {
console.log(x)
}
f() //=> 1
function g(){
console.log(x)
var x = 2
console.log(x)
}

g()
//=> undefined
//=> 2

...どうです?意味わかります?

関数fだと、グローバルスコープ見に行ってるんですが、関数gだと最初のlogがundefinedになっているんです。

ひぇ....

なんだか非常に分かりにくいですが、これはバグです!

JavaScriptは他の言語によくある「変数は使う直前で宣言すべき。」というルールは推奨されないのです。

ローカル変数は関数の先頭でまとめて宣言することが推奨されています。


ブロックスコープは無いようです。

var x = 1

{var x = 3; console.log(x)} //=>3

console.log(x) //=> 3

...え?1じゃないんだ?

普段ならブロックとして区切られている内部の変数は共通で使われない。と、思いがちですがJavaScriptではブロックスコープというのが無い。

関数として区切ってあげないといけないんですね!

** 補足 **

ECMAScript5にブロックスコープは無いですがJavaScriptの拡張として

letやconstを用いたブロックスコープの実装が有ります。

ECMAScript6からはletやconst等が標準機能になったりしますので、どうしても使いたい時は抑えておくといいかもしれないですね!

function f(){

let x = 1
print(x)
{ let x = 2; print(x); }
print(x)
}
f() //=> 1 2 1

※ES6や独自拡張を取り入れてないとこけます


関数の入れ子でも関数スコープが守られる。

function f(){

function f1(){
var y = 3
console.log(y)
}
function f2(){
console.log(y)
}
f1()
f2()
}
f()
//=> 3
//=> Exception: ReferenceError: Can't find variable: y

関数で区切っちゃえばひとまず安心。

ちなみに、グローバルスコープにvar y=1とかあればf2()で1が出力されますよ!


即時関数を使って関数スコープで守る

即時関数とは?

(function () {

print('hello')
}()); //=> hello

(function (x,y) {
print(x,y)
}(1,2)); //=> 1 2

var sum = (function (x,y) {
return x + y
}(1,2));
sum() //=> 3

関数を書いて、即座に実行させる。。まさに即時な関数だ!

え?でもこれって関数として書く意味なくない?

と、これの書く位置がスコープの外側なら思うことでしょう。

そう。これの最大のメリットは ** 関数スコープとして区切ることで汚染を回避することが出来ます! **

JavaScriptで即時関数を使う理由

こちらの記事様がかなり分かりやすくまとめていました!


apply、call関数 それと bind関数

なにこれ....?


現実のプログラミングでは、コールバックのためにapplyやcallを使う場面が少なからず存在します


と本書に書いてありますね。難しそうだけど、見てみよう。

えーとなになに「使うと、呼び出した関数内のthis参照を指定したオブジェクト参照に出来ます」?

つまり、2つのオブジェクトA,Bがあったとして、Aが持っている関数が、Bの持っているプロパティを参照して実行できる。的な感じか?

function f(){ print(this.x) }

var obj = {x:4}

f.apply(obj) //=> 4

おー。関数fはxというプロパティを持ってないのに、objのプロパティを参照した状態で関数を実行できた。面白い。

obj2 = { x:10,

g:function(){ print(this.x) }
}
obj2.g() //=> 10
obj2.g.apply(obj) //=>4

オブジェクトが自分自身で該当するプロパティを持っていてもapplyで割り込みも出来るようです。

obj3 = {

g:function(y,z){ print(this.x, y, z) }
}
obj3.g.apply(obj, [3, 2]) //=> 4 3 4
obj3.g.call(obj, 3, 2) //=> 4 3 4

applyの引数は(object , array[関数と同様の引数] )

callの引数は(object , 関数と同様の引数 )

という違いが有るようです。引数がなければapplyもcallも変わらないようですね。

他にも、bind関数というのがあります。束縛された関数を作る関数。

var x = 5; 

var obj = {
x: 10,
f: function() { return this.x; }
};
obj.f() //=> 10

var global_f = obj.f

global_f() //=> 5

関数を一度オブジェクトの外へ出すと、関数を実行する際にオブジェクトのプロパティを参照しなくなります。

つまり、thisはオブジェクトの外(この場合だとトップレベルのスコープ)。グローバルスコープを参照するようになります。

var bind_f = global_f.bind(obj); //参照を束縛っ

bind_f() //=> 10

使い方はcallと似ていますね。

ただ、callとは違い、関数を実行した結果ではなく、参照を束縛した関数を返しています。それが違いですかね。

いま紹介した奴は全てprototypeが持つ関数です。

え? prototype.call()みたいな使い方じゃなかった?

prototypeの部分でこそっと紹介しましたが、全ての関数(オブジェクト)はprototypeに暗黙のリンクを持っているため、使用することが出来るんですね。

ややこしいですね。

mozilla Function.prototype.apply()

詳細は此方から!


クロージャ

クロージャは状態を持つ関数です。

状態を持つとはなんだ?

という疑惑はひとまず置いておいてクロージャとなる前提条件は


  • 関数の中に関数を入れ子として持っている。

  • 同じコードから異なる状態を持つ関数を作れる。

異なる状態?

ほほぉ。これはコンストラクタとちょっと似ていますね。

というところにピンとくるとちょっと理解が早いかもですね。

早速クロージャを作ってみます。

function f(x){

var y = x

function g(){
console.log(y)
}

return g
}

はい。完成!

まず「関数入れ子」になってますね。

そして....

var g1 = f(5)

var g2 = f(8)

g1() //=> 5
g2() //=> 8

関数fを使用し、異なる呼び出し結果を持つ関数を作る事。すなわち同じコードから異なる状態を持つ関数を作れました。

これすなわち、クロージャ。

勿論関数で区切られているので

y = 10

g1() //=> 5
g2() //=> 8

汚染も発生しません。

関数g自体は関数yを保持していませんが、fの関数を関数オブジェクトが関数fへの参照を持っています。

また、2回呼ぶとそれぞれ別の(状態が違う)関数オブジェクトが生成される。

状態という言葉がふわふわして、掴みどころがなかったかもしれないですが、これで何となく分かるでしょうか。

異なる状態の関数≒異なる結果返す、もしくは異なる処理をする関数

という感じですかね。僕はそのような感覚で一応しっくり来ていますが、そんなことないですかね?


おわりに

これだけ書いて、本書を飛ばし飛ばしやっても、まだ基礎。

いやぁーJavaScriptって奥が深すぎますね....

これでサーバサイドも有るっていうんだから、JSマスターになるには長い道のりです(´・ω・`; )

一応DOMまでは人通り勉強したいなぁと思っていたので、DOMの記事も後ほど書きます。

でも今回の所は一旦ここまで。

ここまで長らく見て頂く有難う御座いましたm(_ _)m