JavaScript入門です。
僕は、JavaScriptの面白さというのは、単純明快なオブジェクトの代入・呼び出しシステムである、ということではないかと思う。
オブジェクトは単にPythonの辞書のような{ 名前1: 値, 名前2: 値 }といった初期化子で記述できるプロパティ(名前と値)の集団で、変数にオブジェクトを代入すると「オブジェクト名.プロパティ名」でアクセスできる。ブラケットを用いてobj['x']ともアクセスできる(obj.xと同じ)。
変数に型はなく、どんな値あるいはオブジェクトも代入できる名前にすぎない。「オブジェクト名.新しいプロパティ名 = 値」で、いつでも新しいプロパティをオブジェクトに追加できる。
関数も(特別に実行可能な)オブジェクトであり、関数を別の変数に代入して参照することも自由。
コンストラクタは単に関数であり、プロトタイプとして「コンストラクタ関数.prototype.メソッド関数 = function(){}」のように、プロパティとして別のメソッド関数を所有してnewすればオブジェクトを操作するメソッドとなり、newでコンストラクタ関数を呼び出せばオブジェクト指向的なインスタンスの生成もできる。
プロトタイプチェーンを使った継承や委譲も可能。
このような単純明快さ、これがJavaScriptの好まれる理由ではないかと思う。
また、ホストオブジェクトがクライアントサイドのWebブラウザなら、windowオブジェクトからDOMなどにアクセスできるという仕様も単純。同時にNode.jsなどを使えばサーバーサイドのホストオブジェクトも利用できて、そもそもがブラウザの拡張言語として設計されたために拡張性に優れている。拡張言語としての使いやすさから、GNOMEやAdobe製品でもスクリプト用途に採用されている。
後日注記:つまり、JavaScriptにおいてはハッシュと構造体は同じものです。ここに目を付けたのは天才かもしれません。これらはJavaScriptでは、オブジェクトと呼ばれます。また、JavaScriptでは関数もオブジェクトです。関数は、通常の変数とまったく同じように、名前をつけて管理でき、関数の引数に関数オブジェクトとして渡すこともできます。クラスがJavaScriptにないのは、クラスは単なる関数とオブジェクトに過ぎないからです。
詳しくは以下の書籍が参考になります。
JavaScript(プロトタイプチェーン)やオブジェクト指向も参照のこと。
変数は値を入れる箱のこと。
変数を宣言するにはletを使う。文字列は'~'あるいは"~"で囲んで表現する。
let name = 'assy';
letは値を変えられる変数だが、constは値を変えることができない。
変数の中身を参照するには、単に変数名を書けばよい。変数を左において=を用いると、変数の値を変えられる。
let name2 = name;
変数の値を確認したいなら、console.log()という関数を使えば、コンソールに変数の値を出力できる。
console.log(name);
constで宣言した変数の値を変えるとエラーとなる。
const age = 10; age = 20; //エラー
また、配列は[var1, var2, var3]のように囲む。参照する時は変数名の後にインデックス番号をつけてarray[0]などとする。
let baka = ['hoge', 'hage', 'manuke']; console.log(baka[0]);
インデックス番号は0から始まることに注意。
JavaScriptでは、キーと値からなるハッシュあるいは構造体のことをオブジェクトと呼ぶ。オブジェクトは、
let family = { dad: 'andy', mam: 'kate' };
などとする。
条件分岐はif文、反復文はwhile文で可能。また、関数はアロー記法とfunction記法がある。アロー記法を使う場合は、
const loop_log = (text, c) => { let i = 0; while (i < c) { console.log(text); i++; } } loop_log('assy', 30);
などとする。関数はオブジェクトの中に入れることもできる。
JavaScriptでWebブラウザの情報を取得するには、windowオブジェクトを使用する。
たとえばブラウザの大きさを出力するには、
console.log(window.innerWidth); console.log(window.innerHeight);
とする。
documentオブジェクトは、HTMLの情報を取得することができる。
たとえば、getElementsByTagName('button')とすればボタンを取得できる。ここで得られるボタンは配列の形になっているので、4つのボタンのうちの3番目を取得するには、インデックス番号をつけて配列の要素として取得すればいい。
getElementsByTagName()だけではなく、getElementById()を使えばIDから取得することもできる。こちらの方が便利。
最後に、イベント。イベントリスナを使うことで、ドキュメントから取得したボタンに、イベントを関連付けられる。たとえば、クリックした時に関数を実行するのであれば、
let hoge_button = document.getElementById('hoge'); hoge_button.addEventListener('click', function () { window.alert('hogeボタンを押しました'); });
ちなみに、関数はアロー記法でもfunctionを使った記法でもどちらでも結構です。
この説明で分からない方は、以下の動画で分かりやすく説明されているのでそちらをご覧ください。この入門はこの動画を見た上で自分なりに書いてみました。
JavaScript(DOM/Canvas)も参照のこと。
JavaScriptの標準的な算術演算子には+(足し算), -(引き算), *(掛け算), /(割り算), %(割り算の余り)などがある。
比較演算子は==など一通りそろっているが、==とは別に===がある。
===と==の違いについては、==は型を変換して一致させる。===は型を変換しない。
JavaScriptだけでなくPHPなど、Webサービス開発においてIDとパスワードの比較などでは数値にキャストするととんでもないことになることがあるので、基本的に===を使おう。
PHP(セキュリティ)も参照のこと。
以下のようにvarで変数を使用できる。
var name = "Assy";
変数定義については、varの他にletやconstがある。varは関数スコープだが、letとconstはブロックスコープ。また、constは変更されない変数、すなわち定数を定義できる。
let name = "Assy";
後日注記:varによる変数定義は関数スコープであるため、ブロックの中で使った変数をブロックの外からアクセスできるが、これはバグの元になるため、ブロックスコープのletを使おう。
以前から、TypeScriptではvarのほかにletやconstが用意されていました。ですが今ではES6以降標準のJavaScriptの仕様としてletやconstが含まれます。
そのため、「varは時代遅れであり、使うべきではない」とされています。
ですが、以前のJavaScriptの書籍などではvarが使われていることが多いです。以前の仕様で動くコードも残っているため、varを使うことは引き続き可能です。
TypeScriptも参照のこと。
配列は以下のようにします。
let langs = ['Ruby', 'Python', 'JavaScript']; console.log(langs[0]);
配列をひとつひとつ順番に処理するには、後述するイテレータとfor-of文を使います。
配列の要素ひとつひとつに関数を適用し、その実行結果の配列を返したい場合はmap()関数を使います。
JavaScriptでは、配列の操作のためにArrayオブジェクトが用意されており、さまざまな操作ができます。たとえばpush(), delete(), filter(), map(), copy(), sort()などのメソッドがあります。詳しくは以下が参考になります。
配列とリストとハッシュも参照のこと。
if~else文を使った条件分岐は、
if (条件1) { 処理 } else if (条件2) { 処理 }
で可能。条件を満たす場合に処理が行われ、満たされない場合は次の条件に進む。
たとえば以下のようになる。
function zero_or_one(arg) { if (arg == 0) { return 'Zero'; } else if (arg == 1) { return 'One'; } else { return 'Error'; } } console.log(zero_or_one(0)); console.log(zero_or_one(1));
if~else文のほか、switch-case文も利用できる。switch-case文では、case文の場所に定数式だけではなく任意の式を書ける。
後日注記:JavaScriptでは、変数に具体的な値を代入するには=を使って代入すれば良いが、変数の中の具体的な値を知るためには、console.log()などで出力するか、このようにif文やswitch-case文を使って値に基づいて条件分岐をすればいい。
構造化プログラミングも参照のこと。
2023.04.12編集
for文を使った繰り返しは、
for (初期化を行う式; 条件式; 反復する際に実行される式) { 繰り返し処理 }
で可能。条件式が真である間繰り返しが行われる。
もちろんwhile文も使えます。
while (条件式) { 繰り返し処理 }
ほかにもdo-while文などがあります。
永久ループにならないためには条件式を正しく記述するか、break文を使ってループから脱出しましょう。
また、continue文を使うと、その回の繰り返しをスキップして次の繰り返しに移ることができます。
以下は応用例(自分で書いたコード)。
function ul_array(id, array) { document.write('<ul id="' + id + '">'); for (var i = 0; i < array.length; i++) { document.write('<li>' + array[i] + '</li>'); } document.write('</ul>'); } ul_array('langs', ['HTML', 'CSS', 'JavaScript', 'PHP', 'Python', 'Ruby']); ul_array('names', ['Assy', 'Zaidou', 'Frey']);
ここではコードを単純化するためにdocument.write()を使っているが、現在のJavaScript界隈ではdocument.write()は非推奨とされているので注意してほしい。
2023.04.12編集
イテレータとfor-of文を使うことで、要素を順番に処理できます。値を取り出すにはvalues()メソッドを使います。
for (const value of list.values()) { console.log(value); }
インデックス番号と値を同時に取り出したいならentries()メソッドを使えばよいです。Mapの場合keys()を使えばキーだけを取り出せます。
イテレータとジェネレータや関数型プログラミングも参照のこと。
基本的に以下のように定義する。
function sub10(arg) { var v = arg - 10; return v; }
そして、以下のように実行する。
var x = sub10(100);
関数はfunction記法のほかアロー記法があります。たとえば、
function func_add(a, b) { return a + b; }
はアロー記法では
const func_add = (a, b) => { return a + b; }
と書けます。
JavaScriptは関数もオブジェクトであるため、=を使って左辺の変数に代入し、それを実行できます。
let func_loop = (text, c) => { let i = 0; while (i < c) { console.log(text); i++; } } func_loop('OK Google', 100);
関数も参照のこと。
オブジェクトの作成をするためには、オブジェクトリテラルを使う方法と、コンストラクタとnewを使う方法がある。
オブジェクトリテラルを使う方法:
let obj = { x:1, y:2 }; console.log(obj.x + obj.y);
コンストラクタとnewを使う方法:
function HogeClass(x, y) { this.x = x; this.y = y; } let obj2 = new HogeClass(3, 4); console.log(obj2.x + obj2.y);
もしconsole.log()文をshow()のようにオブジェクトのメソッドとして持たせたいなら以下のようにすればいい。
オブジェクトリテラルを使う方法(メソッド付き):
let obj3 = { x:5, y:6, show:function() { console.log(this.x + this.y); } }; obj3.show();
コンストラクタとnewを使う方法(メソッド付き):
function Hoge2Class(x, y) { this.x = x; this.y = y; this.show = function() { console.log(this.x + this.y); } } let obj4 = new Hoge2Class(7, 8); obj4.show();
詳しくはパーフェクトJavaScript (PERFECT SERIES 4)が参考になる。
すべてのクラスの基底クラスはObjectクラスとなる。
文字列のためのクラスとしてStringクラス、配列のためのクラスとしてArrayクラス、関数のためのクラスとしてFunctionクラスがある。
また、数値のためのNumberクラス、数学関数のためのMathクラス、日付クラスとしてDate、正規表現クラスとしてRegExpなどがある。
また、JSONクラスを使うことで、JSONデータをネイティブに扱うことができる。
(パーフェクトJavaScript (PERFECT SERIES 4)を参考に執筆しました。)
JavaScriptでは、C/C++と同様、複数行のブロックコメントとして/* ~ */を、一行コメントとして//を利用できる。
HTMLのコメントである<!-- ~ -->とごっちゃにならないように注意しよう。
2023.02.03
console.log()で、デバッガのコンソールに値を表示できる。
console.log("Assy");
あるいは、
let name = "Assy"; console.log(name);
ブラウザにそのまま内容を表示したい場合はdocument.write()が使えますが、現在では非推奨とされています。
現在では、document.write()の代わりに、insertAdjacentHTML()を使うことが推奨されます。
たとえば、ホスト名は「location.hostname」で取得できる。
ホスト名で処理を分けたいなら、
if (location.hostname==="hoge.hogehoge.com"){ document.write('<p>このサイトはhoge.hogehoge.comです。</p>'); } else if (location.hostname==="hoge2.hogehoge2.com"){ document.write('<p>このサイトはhoge2.hogehoge.com2です。</p>'); }
とすればよい。
ほかにも、ウィンドウ幅によって処理を分けたい場合、たとえばスマホ向けとPC向けで別の内容を表示させたい時などは、window.innerWidthなどを使って定数値と比較するなどの方法がある(ほかにも方法はある)。767pxぐらいで分けるのが一般的。
基本的に、
onclick="OnButtonClick();"
のような属性をHTMLのタグの中に書けば、クリック時にOnButtonClick()という名前の関数を実行できます。
以下のページが参考になります。
JavaScriptには、即時関数と呼ばれる面白い関数の書き方がある。以下のように関数定義を()でくくって後ろに()を記述すれば、関数を定義して即座に実行できる。
(function () { .... }());
引数を持たせることも返り値を返すこともできる。
let v = (function (x, y) { return x + y; }(10, 200)); console.log(v);
詳しくは以下のページが参考になる。
JavaScriptでは、関数定義を関数定義の中でも宣言できる。また、関数を宣言した場所と同一のスコープにある変数には関数の内部からアクセスできる。これをクロージャと呼ぶ。
以下は定期的に関数を実行するsetInterval()を使った例。
let v = 0; let limit = 500; let step = 50; function func() { if (v > limit) { v = 0; } console.log(v); v += step; } setInterval(func, 3000);
たとえば、out_func()の中でin_func()を定義する場合などに役に立つ。
後日注記:上のコードは、応用すると画像を左から右にスライドショーのように切り替えて表示するプログラムが作れます。画像のサイズが50だとして、最初は0地点ですが、それが3秒ごとに50ずつ加算されていきます。画像を結合させて作っておいて、50ずつ位置を移動させるようなことに応用可能です。
JavaScriptでは、オブジェクトを使ってPerlで言うハッシュのようなデータ構造を使うこともできますが、マップ専用のデータ構造としてMapが存在します。同様にSetも存在します。
たとえば、
function f() { var value = 1; function g() { console.log(value); } g(); value = 3; g(); } f();実行結果:
1 3このような時、g()は同じスコープにある変数valueを参照するクロージャであり、valueの値は後々になって変えることもできる。
また、
var f = function() { var value = 0; return function g(number) { value += number; console.log(value); }; }; add = f(); add(3); add(6);実行結果:
3 9このように、関数から関数を返し、この関数をクロージャとして使うこともできる。変数valueの値はクロージャとともに保持される。
並列処理より。
非同期処理を行う上で、「何かの処理がきちんと完了してから次の処理を行いたい」とか、「処理を行った結果を別の処理に渡したい」などとすることがあります。
これを同期処理で書くことは簡単ですが、非同期処理にすることで、長い時間がかかる処理を行っている間、CPUはそれに待たされることなく別の関係のない処理を行えます。
コールバック関数で実現することもできますが、コールバック地獄になることもあります。コールバックを複数使うと、ネストが深くなりすぎ、見通しも悪くなります。
JavaScriptでは、このような時に、Promiseと呼ばれる仕組みを使うことができます。
Promiseでは、関数の引数として、resolve(処理の成功)とreject(処理の失敗)の二つの関数をとります。まず、処理がきちんと完了した時点で、resolve()を実行します。ここで、処理は「解決」されます。これがreturnやyieldに相当します。処理が解決したら、then()に記述された次の関数の処理が実行されます。thenはいくらでも続けることができます(メソッドチェーン)。また、正しく処理が解決せず、不正に終了する場合はreject()を実行します。これをcatch()で捕捉します。catch()は処理中にthrowで吐かれた例外を捕捉することもできます。
また、このようなPromiseによる非同期処理を、もっと同期処理と同じような記述で書くことのできる仕組みとして、async/awaitがあります。asyncを関数定義につけると、その関数は非同期関数となります。awaitはasyncをつけた関数の中にしか記述できませんが、awaitが記述された時点で、awaitをつけた関数のPromiseの結果が返されるまで、非同期関数を一時的に停止して、Promiseの解決を待機します。そして、解決した時点で非同期関数の実行を再開します。
JavaScriptを学ぶなら、ほとんど公式と言ってもいいぐらいのMozillaの情報があります。
実際、JavaScriptをやりたいなら、Mozillaの入門が一番いいです。特に、以下に僕が記述する内容の中でも、ボタンやリンクからJavaScriptを実行する場合などは「HTMLの内部にJavaScriptを書くのは避けた方が良い」といったようなことが書かれており、自分が見てもきちんとMozillaの情報を読んだ方が良いと思います。
しかしながら、一部の内容が英語のままです。ただし、Google Chromeを使っている場合は、Googleの機械翻訳を使うことをおすすめします。Google翻訳は最近のAI技術の向上などにより、驚くほど質の高い日本語を吐いてくれます。
JavaScriptの標準であるECMAScriptは、以前は欧州における標準規格団体だったECMAで標準化が進められています。
ECMAScript 2015のことをES6と言い、ブロックスコープの変数定義やClass文、for-of文によるイテレータ、Promiseによる非同期処理など数多くの変更が加わっています。
以下を参照のこと。