【JavaScript超入門】jQueryレプリカの作成を通してオブジェクトとメソッドを理解しよう

# 概要
こんにちは！Webエンジニア2年目の@mejilebenです。最近Ruby力の高まりを感じる一方で、日本語力の低まりを感じています。

本記事では、JavaScriptのオブジェクトとかメソッドについて理解を深めるために、jQueryのレプリカを自作してみましょう。

### 対象読者
- なんとなくjQueryを使ったことはある
- けど、サンプルプログラムをコピペしたあとに自分なりに改変できるレベルではない
- JavaScriptのオブジェクトって言われてもピンとこない

なんとなくjQueryを使ったことはあってもJavaScript自体はよく知らない方は多いのではないでしょうか？
僕もWebエンジニアとして働き始めた当初はそんなレベルだったので、同じレベル感の方に役立てば幸いです。

# 目的
jQueryのレプリカを自力で作ってみることで、何気なく使っているjQueryの動きと、JavaScriptのオブジェクト、メソッドの考え方について理解することです。

今回は、jQueryのtextメソッドを作ってみようと思います。
（※ここでの「メソッド」は「関数」と同じ意味と思っていて差し支えありません）

textメソッドは、要素の中身のテキストを書き換えることができるメソッドです。
例えばこんなHTMLがあるとします。

```html:
<div class='hogehoge'>
    before
</div>
<div id='hogehoge'>
    before
</div>
<div class='hogehoge'>
    before
</div>
```

これはブラウザ上では以下のように表示されます。

```
before
before
before
```

そこで、下記のようなJavaScriptを実行すると、

```js:
$('.hogehoge').text('hogehogeクラスが変わったよ');
$('#hogehoge').text('hogehogeIDが変わったよ');
```

このように、「before」だったはずのそれぞれのテキストがJavaScriptで指定した文字列「hogehogeクラスが変わったよ」もしくは「hogehogeIDが変わったよ」に変わって表示されます。

```
hogehogeクラスが変わったよ
hogehogeIDが変わったよ
hogehogeクラスが変わったよ
```

こんな働きをするメソッドを自分で作ってみましょう。

## 作る手順
textメソッドのレプリカを作るためには、大きく分けて２つの手順を踏む必要があります。

1. ```$(文字列)```でHTML上の要素を指定できるようにする
2. 指定した要素に```.text(変更したい文字列)```とすることで、テキストを変更できるようにする

# ```$(文字列)```のレプリカを作る
まずは

```
$(文字列)
```

でHTMLの要素を指定できるようにしましょう。
そのためには、JavaScriptの**オブジェクト**について理解しておく必要があるので、ちょっと遠回りですがオブジェクトの説明から入ります。

## オブジェクトとは？
一言で言うと、**変数とメソッドをひとまとめにできるもの**です。
例えば以下のように書くとオブジェクトを作ることができます。

```js:
{
    // 変数の役割を果たすプロパティ
    sampleVariable: 1,

    // メソッドの役割を果たすプロパティ
    sampleMethod: function(hoge){
        console.log(hoge);
    }
}
```

一番外側の中括弧「{}」で囲っている部分をまとめて**オブジェクト**と言います。
極端な話、JavaScriptでは下記のようなコードもオブジェクトです。

```js:
{}
```

これは中身に何も持っていない空っぽのオブジェクトです。
変数をとりあえず作っておきたい！という場合に```var obj = {};```といった書き方をたまに見ます。初めて見た時は面食らうと思いますが、単純に空っぽのオブジェクトを作って変数に代入しているだけです。
オブジェクトの中には変数もメソッドも入れることができますが、それらをまとめて「プロパティ」と呼びます。
色々呼び名があってややこしいですが、少しずつ慣れていきましょう。
例えば上記のコードでは、「このオブジェクトのsampleVariableプロパティには1が入っている」といった言い方をしますね。

僕も正直言ってこの辺の日本語の使い方はノリで使っているので絶対の自信はないです。周囲の先輩方に合わせたら良いと思います！

ここでは、オブジェクトの書き方だけ覚えておいたらOKです。

```js:
{
    (プロパティ名): (数値や文字列, メソッドなど), // プロパティを複数書く時は行末にカンマを忘れないようにしましょう
    (プロパティ名): (数値や文字列, メソッドなど)
}
```

## オブジェクトを使う
さて、オブジェクトの作り方がわかったところで、どんなふうに使うのかの実例を見てみましょう。
まず、オブジェクトを変数に入れます。

```js:
var object = {
    sampleVariable: 1,
    sampleMethod: function(hoge){
        console.log(hoge);
    }
};
```

このとき、「objectという変数に、sampleVariableとsampleMethodプロパティを持ったオブジェクトを代入した」というような言い方をします。
ちなみにオブジェクトには変数とメソッドを入れることができて、そのオブジェクトをまた変数に入れることができます。
つまり、JavaScriptでは**変数とメソッドをオブジェクトにまとめることで、無限に変数やメソッドを入れ子にすることができます！。**
大規模なコードになると、オブジェクトのプロパティにオブジェクトがあってまたその中にオブジェクト・・・という事態は日常茶飯事ですが、オブジェクトの書き方は変わらないので、そんなコードを見ても焦らず一つずつ読んでいけば必ず理解できます。

次に、オブジェクトの中に入れた値を取り出したり、メソッドを実行してみましょう。

```js:
var object = {
    sampleVariable: 1,
    sampleMethod: function(hoge){
        console.log(hoge);
    }
};

console.log(object.sampleVariable);
object.sampleMethod('メソッドが実行できた！');
```

結果は

```
1
メソッドが実行できた！
```

となり、オブジェクトの中に入れた数値だったり、メソッドが実行できたことがわかります。
書き方としては、

```js:
(オブジェクト).(プロパティ名)
```

でプロパティを取り出す、もしくはメソッドを実行することができるので、変数ならそのままconsole.logしたり計算したり、メソッドなら()で引数を渡して実行することができます。
```object.sampleMethod('メソッドが実行できた！');```としたときのJavaScriptの動作を補足すると、
ここではobjectオブジェクトのsampleMethodというメソッドに```'メソッドが実行できた！'```という文字列を渡して実行していて、実際にobjectオブジェクトの中でsampleMethodプロパティに代入している、

```js:
function(hoge){
    console.log(hoge);
}
```
のメソッドが実行されます。
そのとき```hoge```に```'メソッドが実行できた！'```という文字列が渡ります。
そして```hoge```が```console.log```されることで、画面上に「メソッドが実行できた！」と表示されます。

ここのオブジェクトからプロパティを取り出したりメソッドを実行できる流れを理解できていると、これからの話がめっちゃ分かりやすくなるので、ぜひ理解して次に進んでください！

## 「＄」メソッドを実装しよう
### 書式を解剖してみる
前置きが長くなりました。
ここまで勉強したところで今回作りたいtextメソッドの書式を見てみましょう。

```js:
$('.hogehoge').text('hogehogeクラスが変わったよ');
```

実はこれ、先ほど説明した**オブジェクトのプロパティを取り出す書き方と一緒**なのです。
改めてオブジェクトのプロパティを取り出す書き方を見てみると、

```js:
(オブジェクト).(プロパティ名)
```

となっていて、

```js:
$('.hogehoge').text('hogehogeクラスが変わったよ');
```

と比較すると、```$('.hogehoge')```はオブジェクトで、```text```はそのオブジェクトのプロパティであることが分かります。

ここ、ちょっとピンとこないと思いますので、もう一度説明します。

```js:
(オブジェクト).(プロパティ名)
```
の書き方から分かるように、JavaScriptにおいてドットを間に挟んだ表現があった場合、**ドットの左側はオブジェクトで、ドットの右側はそのオブジェクトのプロパティ**です。（当然ですが数値の小数点のドットは除きます）

その前提のもとで、先程の

```js:
$('.hogehoge').text('hogehogeクラスが変わったよ');
```
を見ると、```$('.hogehoge')```はオブジェクトで、```text```はそのオブジェクトのプロパティであることが分かります。
textは（）が付いていることから分かるようにメソッドなので、今まで見た例の中で言うと、ちょっと前に例示した

```js:
object.sampleMethod('メソッドが実行できた！');
```
に一番近い形ですね。textはsampleMethodと同じく、引数として文字列を受け取るメソッドです。

### ＄の正体に迫る
次に分からないのが、「```$('.hogehoge')```はオブジェクト」ということだと思います。
JavaScriptのコードではめったに見かけない「＄」、これは一体何者なのでしょうか。

一言で回答を先にいうと、「＄」はメソッドです。
```$('.hogehoge')```というのは、「$」というメソッドを、```.hogehoge```という文字列を引数に渡して実行しています。
先ほど説明した、```text('hogehogeクラスが変わったよ')```が、textというメソッドを```hogehogeクラスが変わったよ```という文字列を引数に渡して実行しているのと同じことです。

▼補足
_JavaScriptの命名規則で、「関数名の一文字目は、半角の英字、アンダースコア、ドル記号のいづれかを使う。」というものがありまして、メソッド名に「＄」を使うのはルール上全然OKなのです。
jQueryが「＄」という変なメソッド名を使っているのは、多分、他のメソッドと間違って名前が被ったりしないようにするため、加えて１文字で表現できるので記述が楽、といった理由からだと思います。_

さて、「```$('.hogehoge')```はオブジェクト」ということは、「＄」メソッドに```.hogehoge```を渡して実行した結果はオブジェクトが返ってくる必要があります。
（返ってくる、というのはコードで言うと```return （何らかのオブジェクト）```と書く必要があるということです）

また、「```$('.hogehoge')```はオブジェクトで、```text```はそのオブジェクトのプロパティ」なので、「＄」メソッドが返すオブジェクトは**textメソッドをプロパティとして持っている**必要があります。

以上の内容を前提にして「＄」メソッドを実装してみると、例えば以下のようになります。

```js:
// $はHTML要素名を引数として受け取るメソッド名
var $ = function(elementName) {
    // textメソッドをプロパティとして持つオブジェクト
    var returnObject = {
        text: function(string){
            console.log(string);
        }
    };
    // 作ったオブジェクトを返す
    return returnObject;
}

$('.hogehoge').text('fugafuga'); // fugafuga と表示される
```

ものすごいそれっぽく書けるようになりましたね！
「＄」メソッドに```'.hogehoge'```を渡して実行した結果```returnObject```が返ってきて、そこから```.text('fugafuga')```とtextメソッドを実行することで、```fugafuga```と表示させることができています。
これで目標としている、```$('.hogehoge').text('hogehogeクラスが変わったよ');```の書式だけとりあえず真似することができました。
あとは実際にHTML要素を扱えるようにしたり、textメソッドでHTML要素のテキストを書き換えることができるように中身を実装していきます。

（この節は非常に大切な内容になっているので、多分一度だと理解しきれないと思います。ぜひもう一度じっくり読んでみてください！）

# ```.text(変更したい文字列)```のレプリカを作る
「＄」メソッドまで実装できたので、肝心のtextメソッドの内容を詰めていきましょう。
そのためには、以下のステップを踏んでいく必要があります。

1. 実際にHTML要素を扱えるようになる
2. textメソッドでそのHTML要素のテキストを書き換えることができるようになる

長旅もあと少しで終わるのでもう一息頑張りましょう（息切れ気味）。

## 実際にHTML要素を扱えるようになろう
まずは、HTML要素を扱うために、HTML要素を扱えるオブジェクトを手に入れる方法をお教えします。

```js:
var classObject = document.getElementsByClassName('hogehoge');
var idObject = document.getElementById('hogehoge');
```

こうすることによって、変数```classObject```にはhogehogeクラスの要素を扱えるオブジェクトが、
変数```idObject```にはhogehogeがIDの要素を扱えるオブジェクトが代入されます。

documentというのはJavaScriptがデフォルトで用意してくれているオブジェクトで、HTMLを操作するための様々なメソッドをプロパティとして持っています。
classObjectの場合、documentオブジェクトのgetElementsByClassNameメソッドに```'hogehoge'```文字列を渡した結果、hogehogeクラスの要素を扱えるオブジェクトが手に入ります。
**ドットの左側はオブジェクトで、ドットの右側はそのオブジェクトのプロパティ**という原則は変わりません。

## HTML要素のテキストを書き換えることができるようになろう

次に、手に入れたオブジェクトを使って、HTML要素のテキストを書き換える方法を教えます。
まずは回答から。ソースコードで書くとこんな感じです。

```js:
classObject.innerText = 'hogehogeクラスが変わったよ';
idObject.innerText = 'hogehogeIDが変わったよ';
```
意外とあっさり実現できてしまいます。
```document.getElementsByClassName```で手に入れたオブジェクトはHTML要素と結びついていまして、オブジェクトのinnerTextプロパティを書き換えることで、ブラウザ上の表示も実際に変わります。

こうすることによって、hogehogeというクラス名、およびIDの要素の中身のテキストを「書き換え後」という文字列に変えることができます。

**実はjQueryは、全部こういった既存のJavaScriptの関数を、書きやすいメソッド名だったり表現方法に置き換えたラッパーにすぎないのです。**

なので、実を言いますと、

```js:
$('.hogehoge').text('hogehogeクラスが変わったよ');
```

なんて書かなくても、極論jQueryが世の中になくても、先程から説明したコードを繋げてしまって、

```js:
document.getElementsByClassName('hogehoge').innerText = 'hogehogeクラスが変わったよ';
```

と書けば一行で同じ動作が実現できてしまいます笑
とはいえjQueryにはtext以外にももちろん多彩な機能があって、ここまで流行しているわけなので、ぜひ色々なjQueryをこれからも学んでいってください！

## 完成品のソースコード

完成した全体のコードは以下のとおりです。
いきなり長くなりましたが、コメントをあちこちに書いておきましたので、読んでいけばだいたい分かると思います！（ちょっと息切れたのでガバッとまとめちゃいました。笑）
特に工夫したのは、HTMLのクラスは複数の要素がある時があるので、getElementsByClassNameで取得した要素は配列に代入しておき、textメソッドを実行した時は配列の中の要素全部のinnerTextを書き換えることです。

```js:
// 変数$に関数を代入する。引数の.hogehogeといった文字列はelementNameに入る
var $ = function(elementName){
    // [参考] varで変数を宣言するときはメソッドの最初にまとめてしておくべし
    // http://qiita.com/Chrowa3/items/a11adcd811e253a0b333
    var myJQuery,
        // elementNameの要素をこの配列に格納する
        elements = [],
        targetElementList,
        targetClass,
        targetId;

    // 【$(.hogehoge)を実行した時点でやること→実際にHTML要素を扱えるようになる】
    // elementNameが指す要素を扱えるオブジェクトをelementsという配列に入れていく
    // document.getElementsByClassNameもしくは
    // document.getElementByIdを用いる。
    // あとあとtextメソッドで使うので、配列elementｓに要素を扱えるオブジェクトを代入しておく
    if(elementName.startsWith('.')){
        // 文字列からドットを取り除く
        targetClass = elementName.replace('.', '');
        
        // https://developer.mozilla.org/ja/docs/Web/API/Document/getElementsByClassName
        // class名のリストが返ってくる
        // classなので複数要素があるかもしれないため、配列がtargetElementListに入る
        targetElementList = document.getElementsByClassName(targetClass);
        
        for(var i = 0; i < targetElementList.length; i++){
            // https://developer.mozilla.org/ja/docs/Web/API/HTMLCollection
            // この辺の詳細は参考までに。
            // 配列の末尾にHTML要素を追加していく
            elements.push(targetElementList.item(i));
        }
    }else if(elementName.startsWith('#')){
        targetId = elementName.replace('#', '');
        
        // IDは1つだけなので特に繰り返し文はいらない
        elements.push(document.getElementById(targetId));
    }
    
    myJQuery = {
        // 【textメソッドがやること→HTML要素のテキストを書き換えることができるようになる】
        // innerTextプロパティに書き換えたい文字列を代入する
        // textメソッドをmyJQueryオブジェクトに追加する
        text: function(text){
            // もしelementsが空だったら異常な状態なのでこの時点でreturnしておく
            if(!elements.length){
                return false;
            }
        
            // 配列elementsの一つ一つに対して、innerTextを書き換える
            // https://developer.mozilla.org/ja/docs/Web/API/Node/innerText
            // ［参考］
            // forEach文は、配列の要素一つ一つに対して、引数で渡した関数を実行する
            // 引数で渡す関数をinnerTextを書き換える関数にしておけば、全elementのinnerTextを書き換える
            elements.forEach(function(element){
            	element.innerText = text;
            });
            return true;
        }
    };
    
    // 最後にtextメソッドをプロパティとして持っているmyJQueryオブジェクトを返す
    return myJQuery;
}
```

ここまで書いた後に、

```js:
$('.hogehoge').text('hogehogeクラスが変わったよ');
$('#hogehoge').text('hogehogeIDが変わったよ');
```

というコードを書いてみると、冒頭で宣言したとおり、テキストが代わることを確認できます！

こちらのソースコードは、下記リンクから動作を確認できるので、ぜひ動かしてみたり、実装をカスタマイズしたり、textメソッド以外のメソッドを実装したりしてみてください。
https://jsfiddle.net/mejileben/5ma7u9rw/

# 総括
最後まで読んでいただきありがとうございました。
長々と書いてしまいましたが、なんとなくブラックボックスで使っていたjQueryを、ちょっとでもオブジェクトやメソッドのことを理解した上で使いこなせることを願っています！

とはいえ、実際にこの記事を読んでもここがわからない、この表現が理解できないといったことがあると思いますので、疑問を抱いた方はお気軽にコメントしていただけると幸いです！