くまおの森

フロントエンドで学んだこと知ったことを自分なりに噛み砕いてメモしてく技術ブログ

【JavaScript】とてもややこしいスコープチェーン

スコープチェーンについてメモします。

スコープチェーンの検索は、このように最初に発見した値を返します。

xの中身が上書きされていくということは大体想像が付きます。

処理を括るクロージャーの考えを踏まえると、
関数の外でxを呼んだ場合グローバルでxに代入したfalseが出力されます。

ここまではそんなにややこしいことはないのですが、
次の例からややこしくなっていきます。

ややこしや~

上から順に、まずグローバルで変数aに1を代入していますね。
次に変数funcにaをコンソール出力させる関数を代入してます。

次に即時関数内で、変数aに今度は100を代入させます。
※ (); ←これは関数を即時に実行させます。

次にfを実行していますが、fは引数で、
最後の行で引数に二行目で定義したfunc関数を渡しています。

ということで、即時関数内でfunc関数を実行しているわけですが、 先ほどのようにスコープチェーンの検索の仕方と、クロージャーの考えを踏まえると
同じ関数内で変数aに100を上書きしているため、コンソールで出力されるのは100じゃないかなと思います。(私はそう思いました)

ですが、ログで出力されるのはなんとグローバルで代入した1を出力するのです。

これはスコープは関数実行時ではなく、定義時に決められる為なのです。

二行目の
var func = function() { console.log(a); };

ここで定義した時点で、aはスコープチェーンの検索で一行目のvar a = 1;を見ているのです。

なので、この関数を呼び出した関数内のaはスルーされたのでした。

スコープは関数実行時ではなく、定義時に決められる

これが分かってないと出くわした際に躓いてしまいそうですね。

【JavaScript】thisを変数に代入する理由

JavaScriptの基本編。

thisが思いもよらない動作をすることがあります。

というのをメモしていきます。

 

thisはオブジェクトを指します。

オブジェクト内にconsole出力をさせる関数を作った場合に
thisが何を指すのかを見ると、
thisは親のオブジェクトを指します。

では以下のように、関数(fnc1)の中に関数(fnc2,fnc3)を書いて
その中でthisが何を指すのかを見ると...

オブ...じゃなくてグローバルオブジェクト(window)を指しました。
これは思いもよらない動作です。
オブジェクトの中にいるのに突然グローバルを指してしまうのは
JavaScript特有の癖みたいで、
この仕様を理解しておかないといけないです。

このthisの動作を知ることで、ようやくthisを代入する意味が理解できます。

例えば以下のように、最初の関数内でthisをthatに代入すると
ネストされた以降の関数でもthatにthisが保持されているのでthatをthisとして利用できます。

thatに代入する以外にも、selfに代入するのも良く見ますね。

【テンプレートエンジン】Pugでマークアップ環境を作る

 

JavaScriptについてばかりでしたので、たまには違うメモを。

といってもなぜか、サーバーサイド寄りの内容になりますmm

 

前にCodePenを漁っていたら、これ↓が目に入ったのがきっかけで

f:id:naoaru-28:20170601150450p:plain

気になってしょうがなくてそのままPugをずっと調べてました。

 

Pugは元の名がJadeで、ちょっと前にPugに名前が変わったそうです。

私も馴染みのあるHamlに影響を受けたJavaScriptテンプレートエンジンなんだそうです。

↓試しに作ったこれを元にメモしていきます。よければ見てみてください。

github.com

完成図

f:id:naoaru-28:20170606125212p:plain

Pugテンプレート作成

まずテンプレートを用意します。

開発環境のルートディレクトリに

  • config.pug
  • layout.pug

そしてpugフォルダを作り、その中に

  • index.pug

を作成し、3つの主要ファイルを用意します。

そして今回の場合、

メタ要素を入れる    _inc_head.pug

コンテンツには

     ヘッダー             _inc_header.pug

     ナビ                    _inc_nav.pug

     フッター             _inc_footer.pug

という設定です。

テンプレートは全部includeで読み込むので、includeフォルダ内に纏めます。

トップページの要素はtopフォルダ内に纏めます。

f:id:naoaru-28:20170607103920p:plain

このような感じにしてみました。

 

_inc_head.pug

includeテンプレートから作成していきます。

まずヘッダーから、Pugはこのような書き方をします。

_inc_header.pug

この閉じタグなしで入れ子の書き方はHamlの経験があれば馴染みがあります。
%などが無くさらにスッキリです。
divは省略ができ、if文もこんな感じで書けてしまいます。
(keyやpages.top.urlなどの変数は後に出るconfig.pug内で設定してます。)

_inc_nav.pug

eachやforも使えるので、かなりシンプルになります。
リンク内にも変数を使えました。

_inc_footer.pug

最後にscriptを読み込んでますが、
jsフォルダまでのpathをconfig.pugに纏めておけば、
テンプレート内でpathが長くなることがありません。

layout.pug

layout.pugでトップページの構成を組んでいきます。
1行目の書き方でHTML5の宣言になります。
2行目,14行目にblockとありますが、varsには変数、contentsにはコンテンツの内容をindex.pug内で纏めたものを呼び出しています。
8行目のbodyクラスにkeyという変数を使っています。keyはvars内で代入しています。

index.pug

index.pug内はまずextendsでlayout.pugを継承することから始め、
block varsに変数を纏めています。(config.pug内の変数もvarsに纏めています。)
block contentsにコンテンツ内容を纏めています。
caseを使って場合によりコンテナ内が変わるようにしてみました。
ちなみに、コメントアウト//-です。
入れ子にすれば2行以上コメントアウトできます。

config.pug

config.pugにはページ情報を書きます。
省略パスもここで設定してます。
変数を定義するときはなぜか-(ハイフン)が必要になります。
ちなみに変数を使用する場合は#{key}という書き方です。

長くなりましたが、以上でテンプレの準備は大丈夫かなと思います。

そして最後にpugをhtmlに変換します。
ここではgulpを使用しています。
npmパッケージはgulp-pugを使用すれば簡単にコンパイルができます。

【JavaScript】prototypeが何なのかをはっきりさせる

JavaScriptとはプロトタイプベースの言語ということですが、

「プロトタイプ」とは何だろう?

 ということで、メモしていこうと思います。

 

プロトタイプオブジェクトです。

プロトタイプは全てのオブジェクトに関連付けられています。
全てのオブジェクトはプロトタイプのプロパティを継承しています。

なので全てのオブジェクトは、プロトタイプのプロパティを使用することができます。
(※1 Object.prototypeのみプロトタイプを持ちません。)

 

ということで、まずプロトタイプを見てみます。

f:id:naoaru-28:20170531154017p:plain

ArrayオブジェクトのプロトタイプをchromeのConsoleで出力してみました。

Arrayオブジェクトが持つプロパティがダダダーっとあって、その一番下に__proto__:Objectというものがあります。
こいつがプロトタイプです。

では、プロトタイプはどんなプロパティを持っているのかを見ると、こんな感じです。

f:id:naoaru-28:20170531154753p:plain

因みに、※1 Object.prototypeのみプロトタイプを持ちません。ということを書きましたが、
Object.prototypeをConsoleで出力すると以下になります。

f:id:naoaru-28:20170531160352p:plain

一個上の画像のArrayの継承している__proto__と持ってるプロパティが同じ。
ということは、Object.prototypeこそがプロトタイプなんですね。

なので、全てのオブジェクトはObject.prototypeを継承しているとも言えます。

 

このプロトタイプのプロパティはコードを書く際、同じ処理を再利用したり、オリジナルに拡張したりすることに利用します。

このように、Hello name♡とコンソールで出力するhelloメソッドをHumanオブジェクトのプロトタイプのプロパティに追加します。

naninu自身にhelloがあるわけではなく、Humanのprototypeにhelloを入れたので、(今回だとHumanをインスタンス化した変数naninu)naninu.__proto__.helloにあります。
ですが、naninu.hello();で呼び出すことができます。

このようにプロトタイプのプロパティを連鎖的に探し出してくれます。
(naninu.__proto__にhelloがなければ、naninu.__proto__.__proto__を探して、なければnaninu.__proto__.__proto__.__proto__を探して・・・というようにプロトタイプのプロトタイプは...と自動的に探していきます。)

これをプロトタイプチェーンと言います。

 

・・・長くなってしまいましたが、最後にもう一つ。

プロトタイプを使うことで何が良いのかということです。以下ダメな例になります。

このように、直接コンストラクタ関数Humanにメソッドを追加することができますが、
このようにしてしまうと、Humanをnewでインスタンス化した分メソッドの処理も入ってることになります。

先ほど"同じ処理を再利用したり..."と書きましたが、これがそうです。

同じメソッドの処理を何個も作成することで、その分メモリ領域は使用され重くなっていってしまいますが、
プロトタイプを使用することで、処理はHuman.prototype.helloを参照するだけになり、使いまわすことができます。

プロトタイプって、

後での改良を見込んで、その仕事をする大筋として作る最初の模型。

(google検索)という意味なのでそういった目的でプロトタイプを追加していけば良いですね〜。

【JavaScript】暗黙的な型変換の良い例

kumao-no-mori.hatenablog.com

↑に貼っている、前回のメモのおまけの内容になります。

1つ目の実験、a, b, cの引数に3, 3, '3'と渡し、それらを足した結果をコンソールログで出した結果、63と出ました。

これはaとbはnumber型だから、問題なく足し算され6になります。

ただ、最後の'3'はstring型と認識されるので文字としてくっ付きます。

ここまでは想像通りの結果でした。

ではtypeofで型も調べてみます。
するとstringと出ました。

ん...?6はnumber型のはず、number要素はどこかに行ってしまいました。

そう、これが暗黙的な型変換みたいですね。

 

2つ目の実験、a, b, cの引数に'3',3, 3と渡し、それらを足した結果をコンソールログで出した結果、333と出ました。

これはaのstring型にbとcのnumber型を足し算しています。

ん...?bとcはnumber型なのに数字らしく足し算をしていないだと!?

そう、これが暗黙的な型変k(ry

先頭(ここではa)がstring型だと、後に続く(ここではb,c)number型は計算する前にstring型に暗黙的に変換されました。

 

1つ目と2つ目のログは、本当に信用できるのか。
この結果の63と333は今後string型として本当に利用できるのか。

以下7行目から実験しました。

厳密等価演算子で調べてもstring型と言い張るので、これはもう完全なstring型でした。

こんな事象があることを知っていれば、今後あれ?となることが減るかもしれませんね。

【JavaScript】JavaScriptでは全てがオブジェクト?

JavaScriptでは全てがオブジェクトである

と捉えるべきなのです。

そうすると色々な疑問がスッキリします。

その理由について、今日はメモしていこうと思います。

 

JavaScriptにはプリミティブ型があります。

プリミティブ型:

オブジェクトではなく、値のみを持ち、プロパティやメソッドを持たないデータのこと。
文字列、数値、真偽値、null、undefined、symbol(ES6で追加)がそれに当てはまります。

というものです。

オブジェクトではなく、プロパティも持たないとはっきり言われているプリミティブ型ですが、以下のコードをみてみると

コンソールログで2と出力されました。

プリミティブ型なのにどうしてStringオブジェクトがもつlengthプロパティを使えているのかという疑問が出て来ます。

 

これがJavaScriptでは全てがオブジェクトであると捉えるべきという理由です。

実は暗黙的な型変換でオブジェクト型に変換されています。

では、なぜこのような動作を起こしたかというと、はっきりと言える意図があるわけではなさそうで
JavaScriptのクセのようなものみたいです。

注意ですが、暗黙的な型変換でエラーも吐かずプロパティを使えたので、これで良いのですが、明示的に行なっていないため後々問題が起こるかもしれません。

厳格にはやはりプリミティブ型は存在するので、JavaScript勉強中っていう自分レベルの人はJavaScriptでは全てがオブジェクトであるという考え方でとりあえずいいのかなと思いました。

正確にはJavaScriptでは全てがオブジェクトになり得るなのかな。

【JavaScript】値渡し, 参照渡し

JavaScriptの基礎編。

値渡し

プリミティブ型(基本型)だと値渡しになります。

このようにプリミティブ型を渡した変数aを

変数bに代入すると、変数aの内容が変数bにコピーされます。

f:id:naoaru-28:20170523155723p:plain

コピーされるため、メモリ番地は違う場所になります。

参照渡し

オブジェクト型だと参照渡しになります。

aには空の配列を代入しています。

bにはそのaを代入しています。

bの配列に文字列'ほげ'を入れます。

そうすると

bの配列に入れた'ほげ'がaの配列にも入っています。

値を比較すると、aとbは全く同じと出力されます。

f:id:naoaru-28:20170523175418p:plain

bはaのメモリ番地を見ている(参照している)ことになります。

オブジェクト型だと、メモリ番地は同じ場所を差すことになります。

 

何でもかんでも入れられるオブジェクト型をコピーで複製するとなると、

メモリがたくさん使われて、処理が重くなっていくため

このようにオブジェクト型は参照渡しという仕組みになってるらしいです。