JavaScript:thisの挙動と用途

JavaScriptの初心者を悩ます文法の一つとして、thisが挙げられます。
thisが何を指しているのか、どのような場合に使われるのか、なかなかイメージが難しいと思います。

この記事では、その疑問について答えていきたいと思います。

今回の記事では、サンプルコードの実行はNode.jsで行います。
また、非Strictモード(’use strict’を宣言したりexport句でStrictモードに変更したりしない場合)を前提とします。
(Strictモードの場合は、thisによりグローバルオブジェクトを参照しようとした場合にundefinedになります)


1.thisキーワードの説明

「this」の説明の前に、JavaScriptとオブジェクトの関係について簡単に説明します。
JavaScriptは、必ずオブジェクトの中で実行する必要があります。
実行環境(ブラウザやNode.js)は「グローバルオブジェクト」と呼ばれるオブジェクトを用意しており、自分でオブジェクトを定義しない場合はグローバルオブジェクトから各々の処理が実行されています。
(ブラウザの場合のグローバルオブジェクトは「window」、Node.jsの場合のグローバルオブジェクトは「global」となります)

「this」キーワードは、記述箇所を動かしているオブジェクトを参照します。
自分で定義したオブジェクトから記述箇所を呼び出している場合はそのオブジェクト、そうではない場合はグローバルオブジェクトを返します。

【サンプルコード】

【実行結果】

・1回目の関数呼び出し

・2回目の関数呼び出し

2.callやapplyを用いて関数呼び出しした場合のthisの参照先

関数をメソッドチェーンで「call」や「apply」と繋ぐことで、呼び出される関数の中でthisが参照するオブジェクトを指定することができます。
オブジェクトの指定は「call」や「apply」の第一引数で行います。

また、「call」や「apply」は第二引数以降も指定が可能です。
第二引数以降では、関数呼び出しの際の引数を指定することができ、呼び出される関数の中では「arguments」キーワードでそれを受け取ることができます。
「call」は第二引数、第三引数、第四引数…といった具合に変数を一つずつ定義し、「apply」は第二引数に配列形式でまとめて変数を定義します。

【サンプルコード1】

【実行結果1】

・1回目の関数呼び出し

・2回目の関数呼び出し

【サンプルコード2】

【実行結果2】

・1回目の関数呼び出し

・2回目の関数呼び出し

3.bindを用いたthisの参照先変更

関数をメソッドチェーンで「bind」と繋ぐことで、thisの参照先を強制的に結びつけた新たな関数を定義できます。
結びつけるオブジェクトは第一引数で定義します。

【サンプルコード】

【実行結果】

4.アロー関数で関数定義した場合のthisの参照先

アロー関数で関数を定義した場合は、thisの参照先は固定になります。 関数を定義した時点のオブジェクトを指すようになります。

【サンプルコード】

【実行結果】

・1回目の関数呼び出し

・2回目の関数呼び出し


thisは、どのオブジェクトから呼び出されたかによって挙動を変えたい時に使用することが多いです。
ポリモーフィズムを実現するために使用する、と言った方がわかりやすいかもしれません。
また、今回の例では取り上げていませんが、関数を定義したオブジェクトを渡せばコールバック処理も可能です。

以下のサンプルコードは、呼び出したオブジェクトの名前を参照する例であり、どのオブジェクトから呼び出したかによって挙動を変えることができています。

【サンプルコード】

【実行結果】

・1回目の関数呼び出し

・2回目の関数呼び出し


いかがでしたでしょうか?

JavaScriptのthisは混乱しやすい文法ですが、JavaScriptはグローバルオブジェクトの元で実行されること、通常は呼び出し元のオブジェクトを指すこと、「call」「apply」「bind」キーワードやアロー演算子によりthisが指す先が変わること、を覚えておけば、thisを理解できるのではないかと思います。

JavaScript独自の文法については、これからの紹介していきたいと思います!

JavaScript:Strictモードとは

JavaScriptには「Strictモード」と呼ばれるモードが用意されています。
Strictモードにすることで、バグに気付かずに実行が継続されるのを防ぎやすくなり、高速化することもあります。

詳しくはMozillaのページに書かれていますが、この記事では簡単に紹介したいと思います。


Strictモードに設定するためには、以下の2つの方法があります。
(export句を使用した方法もありますが、この記事では割愛します)

1.スクリプト全体で定義

スクリプトの最初に「’use strict’;」を記述することで、そのスクリプト全体がStrictモードになります。

・サンプルコード

2.関数全体で定義

関数の最初に「’use strict’;」を記述することで、その関数全体がStrictモードになります。

・サンプルコード


Strictモードに設定することで、例えば以下のような挙動変更が行われます。

1.バグになりやすい誤りがある場合に異常終了させる

例えば、定義済みの変数を参照しようとして変数名の記述を誤った場合、Strictモードの場合は異常終了させることができます。

・サンプルコード(非Strictモード)

・実行結果(非Strictモード)

・サンプルコード(Strictモード)

・実行結果(Strictモード)

2.セキュリティーの強化

JavaScriptでは、必ずオブジェクトを用意する必要があり、自分でオブジェクトを定義しない場合は「グローバルオブジェクト」と呼ばれるブラウザや実行環境が用意したオブジェクトが使用されます。
また、キーワード「this」を使用することで、記述箇所を動かしているオブジェクトの情報を取得することができます。
「this」によりグローバルオブジェクトを参照された場合、セキュリティー上の問題が発生する可能性があるため、Strictモードではグローバルオブジェクトが参照されるようなケースで「this」キーワードは「undefined」を返すようになります。

・サンプルコード(非Strictモード)

・実行結果(非Strictモード)

・サンプルコード(Strictモード)

・実行結果(Strictモード)

3.将来(最新)のJavaScriptの仕様に対応する

例えば、キーワード「private」は、2021/03/31現在でいくつかのブラウザやNode.jsでは使用可能です。
非Strictモードではこのキーワードを変数名として使用できますが、Strictモードではエラーにできます。

・サンプルコード(非Strictモード)

・実行結果(非Strictモード)

・サンプルコード(Strictモード)

・実行結果(Strictモード)


いかがでしたでしょうか?

元々はthisの挙動について書こうと思っていたのですが、thisの挙動はStrictモードによっても変化するので、先にStrictモードについて書きました。
JavaScriptの弱点としてエラーで落ちて欲しい場面で落ちない(その結果誤りに気付くのが遅くなる)というものがあると思いますが、Strictモードはその弱点を埋めるのに良い機能だと思いました。

ブラウザで表示される文字列をJavaScriptのURLを読ませて置換する

ブラウザで表示される文字列について、お気に入りからJavaScriptを読みこませることで置換することができます。
例えば、強調表示したいような場合にこれを活用できます。

例として、mozillaのJavaScriptのページを強調表示してみましょう。

https://developer.mozilla.org/ja/docs/Web/JavaScript

今回の例では、「JavaScript」と「ECMAScript」を強調表示します。

1.お気に入りにJavaScriptを登録する

Chromeの場合は以下のような手順になります。

・ブックマークマネージャーを開く

・右クリックで新しいブックマークを作成する

・ブックマークのURLにJavaScriptを記述する

2.任意のページでブックマークを開く

今回の例では、このようなページが

ブックマークを開くことでこのように強調表示されます。


このように手軽にブラウザの表示の文字列置換が可能なのですが、この方法では大量の文字列置換ができないことに注意する必要があります。
少なくとも、筆者の環境(OS:Windows8.1、ビット数:64、メモリ:8GB、Chromeバージョン: 88.0.4324.150)では、1000通りの文字列置換を行うことはできませんでした。
(恐らく、ブラウザ毎に設けられているURLの文字数制限によるものです)
これが問題になるのであれば、Chromeの拡張機能を開発する等の別の方法を考えた方が良いです。

また、文字列置換によりリンク先が変わってしまうことにも注意が必要です。
(逆に、これを利用して、意図的にリンク先を変えることはできそうです)


いかがでしたでしょうか。

大量の記述に対応できないため、今回紹介した方法は不便に感じることがあるかもしれませんが、最も手軽な方法でもあります。
ちょっとした処理を実行したい場合には便利でしょう。

Chromeの拡張機能については、近日中に別の記事で紹介したいと思います!

JavaScript:matchメソッドで文字列抽出を行う

JavaScriptのmatchメソッドは戻り値に特徴があり、戻り値を利用して文字列抽出もできるようになっています。
今回は、文字列抽出の方法を紹介していきます。


matchメソッド(String.match)では、引数で与えた文字列が含まれるかどうかを返すメソッドです。
引数で与える文字列には、正規表現を使用することも可能です。

引数で与えた文字列が含まれない場合には、以下のようにnullが返却されます。

【サンプルコード】

・test.js

【実行結果】


含まれる場合は、以下のように色々な情報が返却されます。
配列で返却され、一致した箇所を抜きだした文字列が添字0の要素に格納されます。
また、indexキーには一致した文字列が何文字目に存在するか、inputキーには走査対象とする元々の文字列が格納されます。
groupsキーについては後述します。

【サンプルコード】

・test.js

【実行結果】


matchメソッドの引数では、gオプションを指定することが可能です。
gオプションを指定すると、一致する文字列を見つけても走査を続け、一致する文字列を全て抜き出すという動きになります。
戻り値の形式もgオプション指定時には変更され、以下のように一致した全ての文字列が添字0の要素、添字1の要素…に格納されます。

【サンプルコード】

・test.js

【実行結果】


matchメソッドの引数で使用できる正規表現には、キャプチャグループというものが存在します。
キャプチャグループとは、”()”で囲った箇所をグループ化するというもので、抽出条件には影響しませんが、抽出結果をグループ分けすることができます。
キャプチャグループが存在する場合は、添字1の要素に1つ目のキャプチャグループ、添字2の要素に2つ目のキャプチャグループ…といった具合に、戻り値でキャプチャグループの箇所を抽出することができるようになります。
キャプチャグループを使用することで、柔軟な文字列抽出が可能になります。

【サンプルコード】

・test.js

【実行結果】


キャプチャグループには名前を付けることができます。
“(…)”を”(?…)”とすることで、名前を付けることができます。
“<“と”>”で囲った箇所が、キャプチャグループの名前になります。
名前付きキャプチャグループを使用している場合は、戻り値のgroupsキーで抽出が可能になります。

【サンプルコード】

・test.js

【実行結果】


いかがでしたでしょうか。

“match”という単語だけを見ると「文字列が一致することを確認する」というイメージを持つと思いますが、実際にはこのように文字列抽出も可能なメソッドです。
そのことを知らずに他の人が書いたソースコードを読むと戸惑うことがある(少なくとも私は戸惑った)ので、「matchメソッドは文字列の一致の確認だけでなく文字列の抽出も行う」ということだけでも覚えておくと良いでしょう。

JavaScript:オブジェクトの中身をコピーする方法(deepcopyライブラリ使用)

JavaScriptにもJavaやC#と同じように参照型変数が存在し、オブジェクトや配列、関数が参照型変数です。
そして、JavaやC#と同じように、単純に「=」で代入するだけでは参照先(オブジェクトのメモリ領域を示すポインタ)しかコピーできず、コピー先の変更がコピー元に影響してしまいますし、その逆にコピー元の変更がコピー先に影響してしまいます。
(このようなコピーを「シャローコピー(浅いコピー)」と呼びます)

これを避けたい場合には、参照しているメモリの中身を丸ごとコピー(新たにメモリ領域を確保し書き込み)する必要があります。 (このようなコピーを「ディープコピー(深いコピー)」と呼びます)
JavaScriptでは標準でディープコピーを行うための関数が用意されていないので、自力で実装するか、外部のライブラリを使用するかする必要があります。
今回は、deepcopyというライブラリを使用します。
(なお、ディープコピーをサポートするライブラリは他にもあり、用途に応じて使い分けた方が良いです。JSONでシリアライズ・デシリアライズする方法もありますが、関数やundefinedがコピーされない等の問題があるので注意が必要です。)

以下、サンプルコードです。
Node.jsを使用して検証します。

【事前準備】

・Node.js command prompt を起動

・作業フォルダ(今回は”C:\tmp\”)に移動

・deepcopyライブラリをインストール

【サンプルコード】

サンプルコードは作業フォルダ直下に作成します。

・ShallowCopy.js

・DeepCopy.js

実行結果】


いかがでしたでしょうか?

JavaScriptのディープコピーは実務でも誤りやすいポイントであり、関数が含まれるオブジェクトをJSONのシリアライズ・デシリアライズでディープコピーしてしまいバグになる所を実際に目にしたこともあります。
ディープコピーの方法は今回紹介した方法以外にも色々あるので、現場毎で確認すると良いでしょう。