JavaScriptの初級講座:再入門編
繊細なアニメーションやインタラクティブなページを作成したり、サーバーと連携したりとJavaScriptを理解することは、現在のウェブ開発においては重要なスキルの一つです。ここでは、JavaScript(ES6以降に追加された機能も含めて)の基本を復習していきましょう!という事で再入門記事を書いてみました。
記事が長すぎたので初級講座:前後編に分けました。
1. JavaScriptとは?
JavaScriptとは、ウェブブラウザに組み込まれて動作するスクリプト言語です。
HTMLとCSSとともに、現代のウェブサイトを構築するための3つの基本技術の1つです。
2. JavaScriptの基本構文
JavaScriptの基本的な構文を学びます。
変数と定数
JavaScriptでは、let
、const
、およびvar
キーワードを使用して変数と定数を宣言します。let
はブロックスコープの変数を宣言し、const
はブロックスコープの読み取り専用の定数を宣言します。var
は関数スコープの変数を宣言しますが、現代のJavaScriptではあまり使用されません。
let name = 'John Doe';
const pi = 3.14159;
データ型
JavaScriptには主に7つの基本的なデータ型があります:Number
、String
、Boolean
、Null
、Undefined
、Symbol
、およびBigInt
。加えて、オブジェクトという複合データ型があります。
let count = 10; // Number
let message = 'Hello'; // String
let isActive = true; // Boolean
let nothing = null; // Null
let something; // Undefined
制御フロー
JavaScriptでは、条件分岐にif
、else
、else if
、およびswitch
を、繰り返しにはfor
、while
、およびdo while
を使用します。
if (count > 0) {
console.log('Positive');
} else if (count < 0) {
console.log('Negative');
} else {
console.log('Zero');
}
for (let i = 0; i < 10; i++) {
console.log(i);
}
関数
JavaScriptでは、function
キーワードを使用して関数を定義します。
また、アロー関数も使用できます。
function greet(name) {
return 'Hello, ' + name;
}
const square = (x) => x * x;
3. オブジェクトと配列
JavaScriptでは、複数の値をまとめて扱うためのデータ構造としてオブジェクトと配列が提供されています。オブジェクトは、キーと値のペアを格納することができ、配列は順序付けられた値のリストを格納します。
以下にそれぞれの例を示します:
// オブジェクトの作成
let person = {
name: "John",
age: 25,
greet: function() {
console.log("Hello, " + this.name);
}
};
person.greet(); // "Hello, John"
// 配列の作成
let numbers = [1, 2, 3, 4, 5];
console.log(numbers[0]); // 1
また、これらのデータ型を操作するための組み込みメソッドも持っています。
// オブジェクト
let person = {
name: "Alice",
age: 20
};
console.log(person.name); // Alice
// 配列
let numbers = [1, 2, 3, 4, 5];
console.log(numbers[0]); // 1
4. JavaScriptにおける関数の概念
関数は、JavaScriptにおける中心的な概念であり、このセクションでは関数の定義、スコープ、クロージャについて学びます。
関数の定義
JavaScriptには主に3つの関数定義の方法があります:
通常の関数定義、無名関数(関数式)、そしてアロー関数です。
以下にそれぞれのコード例を示します。
通常の関数定義(Function Declaration):
function greet(name) {
return "Hello, " + name;
}
console.log(greet("Alice")); // Hello, Alice
無名関数(関数式、Function Expression):
let greet = function(name) {
return "Hello, " + name;
}
console.log(greet("Bob")); // Hello, Bob
アロー関数(Arrow Function):
let greet = (name) => {
return "Hello, " + name;
}
console.log(greet("Charlie")); // Hello, Charlie
また、アロー関数では、1行の式のみを実行する関数の場合、波括弧とreturn
キーワードを省略することができます。これを省略形と呼びます。
let greet = name => "Hello, " + name;
console.log(greet("Dave")); // Hello, Dave
以上のように、JavaScriptでは様々な方法で関数を定義することができますが、関数の形式を選ぶ際は、関数の目的や使用する文脈によって最適な形式を選択します。
スコープ
スコープは基本的に変数が存在する範囲やコンテキストを指します。JavaScriptには大きく分けて3つの種類のスコープがあります: グローバルスコープ、関数スコープ、ブロックスコープです。
グローバルスコープ:
プログラムのどこからでも参照できる変数が存在するスコープです。
例えば、次のようなコードでは、myVar
はグローバルスコープに定義されています。
let myVar = "I'm global!";
function test() {
console.log(myVar); // "I'm global!"
}
test();
console.log(myVar); // "I'm global!"
関数スコープ:
関数内部で宣言された変数は、その関数内部でのみ参照できます。これを関数スコープといいます。関数の外部からは参照できません。var
キーワードで定義された変数は関数スコープを持ちます。
function test() {
var myVar = "I'm local!";
console.log(myVar); // "I'm local!"
}
test();
console.log(myVar); // ReferenceError: myVar is not defined
ブロックスコープ:
{}
(波括弧)で囲まれた範囲で変数を宣言すると、その範囲内でのみ参照可能な変数となります。これをブロックスコープといいます。let
やconst
キーワードで宣言された変数はブロックスコープを持ちます。
{
let myVar = "I'm block-scoped!";
console.log(myVar); // "I'm block-scoped!"
}
console.log(myVar); // ReferenceError: myVar is not defined
以上のように、スコープは変数が参照できる範囲を定義します。
スコープの理解は、変数の振る舞いを理解し、バグを防ぐために重要です。
クロージャ
クロージャは、関数が定義されたスコープの変数にアクセスできる特性を指します。データのプライバシーや関数のメモリ化など、さまざまな使い方があります。
以下のコードはクロージャの基本的な例です:
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
let counter = createCounter();
counter(); // 1
counter(); // 2
5. DOM操作
JavaScriptを用いてHTML文書の構造(DOM、Document Object Model)を操作する方法をです。DOM操作は、ウェブページに動的な振る舞いを追加するための基本的なスキルです。
例えば、以下のコードはJavaScriptを使用してHTML要素を操作する基本的な例です:
// HTML要素の取得
let title = document.querySelector('h1');
// HTML要素のテキストを変更
title.textContent = 'Hello, World!';
// 新しいHTML要素の作成と追加
let p = document.createElement('p');
p.textContent = 'This is a paragraph.';
document.body.appendChild(p);
6. イベントハンドリング
イベントハンドリングはJavaScriptの重要な概念で、ブラウザでのユーザー操作(クリック、キーボード入力など)やブラウザ自体の動作(ページの読み込み完了、エラー発生など)を「捉えて」何かアクションを起こすための方法です。
イベントハンドラー(またはイベントリスナー)とは、特定のイベントが発生したときに実行される関数を指します。
以下にイベントハンドリングの基本的な例を示します。
// HTML要素を取得
let button = document.getElementById('myButton');
// クリックイベントに対するハンドラーを設定
button.addEventListener('click', function() {
alert('Button was clicked!');
});
上記の例では、click
というイベント(ボタンのクリック)が発生すると、無名関数が呼び出され、その結果としてアラートが表示されます。この無名関数がイベントハンドラーです。
一般的に、イベントハンドラーはHTML要素(ボタン、入力フィールド、フォームなど)に設定しますが、ブラウザのwindow
オブジェクト(例えば、ページの読み込み完了イベント)に設定することもあります。
window.addEventListener('load', function() {
console.log('Page is fully loaded');
});
上記の例では、load
というイベント(ページの全てのリソースの読み込み完了)が発生すると、無名関数が呼び出され、その結果としてコンソールにメッセージが表示されます。
イベントハンドリングは、JavaScriptを使った対話的なウェブページの制作において中心的な役割を果たします。
記事が長すぎたので初級講座:前後編に分けました。
7. 非同期処理
非同期処理はJavaScriptの重要な概念であり、このセクションでは非同期処理の概念と、その実現方法であるコールバック、Promise、async/awaitについて学びます。
let delayedGreeting = new Promise((resolve, reject) => {
setTimeout(() => resolve("Hello, World!"), 1000);
});
async function greet() {
let greeting = await delayedGreeting;
console.log(greeting);
}
greet(); // "Hello, World!" (1秒後に表示)
コールバック
JavaScriptはシングルスレッドで動作するため、一度に1つのタスクしか処理できません。しかし、ネットワークリクエストやタイマーのような時間がかかる操作を行う場合、その完了を待つと全体の処理が止まってしまいます。そこでJavaScriptでは非同期処理を利用して、待ち時間中に他のタスクを進めることができます。
ここで重要となるのが「コールバック」です。コールバックは、特定のタスクが完了した後で実行するための関数を指します。以下に非同期処理の一例として、JavaScriptの組み込み関数であるsetTimeout
を使用したコードを示します。
console.log("Start");
setTimeout(function() {
console.log("Callback executed");
}, 3000); // 3秒後に実行
console.log("End");
上記のコードを実行すると、”Start”を表示した後、”End”を表示し、最後に3秒後に”Callback executed”を表示します。setTimeout
関数は非同期で動作し、指定した時間が経過した後にコールバック関数を実行します。しかし、その間もメインのプログラムは止まることなく実行を続けます。
非同期処理は、時間のかかる処理をバックグラウンドで行いつつ、メインのプログラムをブロックせずに動作させるための重要な機能です。しかし、コールバックを使った非同期処理は、複数の操作を順序正しく行う場合やエラーハンドリングを行う場合には複雑になりやすいという欠点があります。そのため、現在ではPromiseやasync/awaitといったより現代的な非同期処理のパターンがよく使われます。
Promise(プロミス)
JavaScriptの非同期処理をより扱いやすくするための一つの方法が「Promise」です。Promiseは非同期操作の終了を表すオブジェクトで、その操作が成功したか失敗したかを表します。操作が成功すれば「解決(fulfilled)」状態となり、失敗すれば「拒否(rejected)」状態となります。
以下に非同期処理を行う関数をPromiseを使って書き換えた例を示します。
let myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Operation successful"); // 非同期操作が成功
}, 3000);
});
myPromise.then(
value => { console.log(value); }, // Operation successful
error => { console.log(error); }
);
上記のコードでは、new Promise
を使ってPromiseを作成しています。Promiseのコンストラクタは引数として1つの関数(エクゼキュータ)を受け取ります。このエクゼキュータ関数にはresolve
とreject
という2つの引数があり、非同期操作の成功または失敗をそれぞれ表します。
このPromiseでは、setTimeout
で3秒後にresolve
関数を呼び出しています。つまり、このPromiseは3秒後に「解決」状態になります。then
メソッドは、Promiseが解決または拒否された時に呼び出されるコールバック関数を登録します。第1引数は解決時のコールバック、第2引数は拒否時のコールバックです。
このようにPromiseを使うと、非同期処理の状態をより直感的に扱うことができ、また複数の非同期処理を連続したり並列に行ったりすることが容易になります。ただし、Promiseも多くの非同期処理を扱うとなるとコードが複雑になりがちで、特にエラーハンドリングが難しくなります。そのため、現代のJavaScriptではasync/awaitというさらに直感的な非同期処理のパターンが導入されています。
async/await
JavaScriptのasync
とawait
は非同期処理を行う際の構文で、非常に読みやすいコードを書くことができます。これらはES2017で導入され、Promiseのシンタックスをより明確で直感的なものにする役割を果たします。
まずasync
キーワードは、関数が必ずPromiseを返すことを保証します。これにより、その関数の中で非同期処理を安全に行うことができます。
async function asyncFunction() {
return "Hello, World!";
}
asyncFunction().then(console.log); // Hello, World!
次にawait
キーワードは、Promiseがresolve(解決)またはreject(拒否)されるまで、関数の実行を一時停止します。await
はasync
関数内でのみ使用できます。
async function fetchAndDecode(url) {
let response = await fetch(url);
let data = await response.json();
return data;
}
上記のコードでは、fetch
関数により返されるPromiseがresolveされるまで待ち、その結果をresponse
に代入します。次に、response.json()
により返されるPromiseがresolveされるまで待ち、その結果をdata
に代入します。
このようにasync/await
を使うと、非同期処理をあたかも通常の同期処理のように書くことができます。これにより、コードは非常に読みやすくなります。しかし、async/await
はあくまでPromiseのシンタックスシュガー(構文糖衣)であり、基礎となるPromiseの仕組みを理解していなければ、混乱を招く可能性もあります。そのため、Promiseとその動作を理解した上でasync/await
を使用することをおすすめします。
8. ES6以降の新機能
JavaScriptは進化し続けており、新しいバージョンでは便利な機能が追加されています。特に、ECMAScript 6(ES6)以降では、アロー関数、デストラクチャリング、async/awaitなどの重要な機能が追加されました。
アロー関数:
これは関数を短く書くための新しい構文です。また、アロー関数は自身のthis値を親スコープから継承します。
// 通常の関数
function add(a, b) {
return a + b;
}
// アロー関数
let add = (a, b) => a + b;
console.log(add(1, 2)); // 3
クラス構文:
これはプロトタイプベースの継承をより直感的に書くための新しい構文です。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
}
let alice = new Person("Alice", 20);
alice.greet(); // Hello, my name is Alice.
Promise(プロミス):
これは非同期処理をより直感的に書くための新しい構文です。成功値またはエラー値を持つことができます。
let promise = new Promise((resolve, reject) => {
resolve("Promise resolved");
});
promise.then((value) => {
console.log(value); // Promise resolved
});
デストラクチャリング:
オブジェクトや配列から値を取り出し、個々の変数に代入することができます。
let person = {name: 'Alice', age: 20};
// デストラクチャリングを使わない場合
let name = person.name;
let age = person.age;
// デストラクチャリングを使う場合
let {name, age} = person;
console.log(name, age); // Alice 20
テンプレートリテラル:
これは文字列内に変数を埋め込むための新しい構文です。
let name = 'Alice';
// テンプレートリテラルを使わない場合
let greeting = 'Hello, ' + name + '!';
// テンプレートリテラルを使う場合
let greeting = `Hello, ${name}!`;
console.log(greeting); // Hello, Alice!
スプレッド演算子:
これは配列やオブジェクトを展開するための新しい構文です。
let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5, 6]; // スプレッド演算子によりarr1の要素が展開される
console.log(arr2); // [1, 2, 3, 4, 5, 6]
async/await:
これはPromiseベースの非同期処理をより直感的に書くための新しい構文です。
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Promise resolved"), 1000);
});
// async/awaitを使わない場合
promise.then((value) => {
console.log(value); // Promise resolved
});
// async/awaitを使う場合
async function getPromiseValue() {
let value = await promise;
console.log(value); // Promise resolved
}
getPromiseValue();
9. Node.js入門
Node.jsは、ブラウザの外でJavaScriptを実行するための環境で、Google ChromeのV8 JavaScriptエンジン上に構築されています。これにより、JavaScriptを用いてサーバーサイドの開発が可能になりました。従来、JavaScriptは主にブラウザ内(クライアントサイド)で実行されていましたが、Node.jsの登場により、データベース操作、ファイルシステムの操作、サーバー間通信など、従来のサーバーサイド言語であるPython、Ruby、Java、PHPなどが行ってきたようなタスクをJavaScriptで行えるようになりました。
Node.jsの主な特性は以下の通りです:
- 非同期I/O:
Node.jsでは、データベースへのクエリ、ファイルの読み書き、ネットワークリクエストなどのI/O操作が非同期で行われます。これにより、I/O操作がブロックされることなく、他の処理を同時に行うことが可能です。これはNode.jsが高性能なアプリケーションを構築する上での重要な要素です。 - モジュールシステム:
Node.jsにはCommonJSというモジュールシステムが組み込まれており、コードを再利用可能なパーツ(モジュール)に分割することが可能です。また、npm(Node Package Manager)というパッケージマネージャを通じて、他の開発者が作成したモジュールを利用することも可能です。 - イベントドリブン:
Node.jsはイベントドリブンなアーキテクチャを採用しており、特定のイベント(例えば、ユーザーからのリクエストの受信やデータベースからの応答の受信など)が発生した際に呼び出される関数(コールバック)を設定することができます。
以下に簡単なHTTPサーバーの作成例を示します:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, World!\n');
});
server.listen(3000, '127.0.0.1', () => {
console.log('Server running at http://127.0.0.1:3000/');
});
上記のコードは、最も基本的なHTTPサーバーを作成するNode.jsのコードです。http
モジュールを使用してHTTPサーバーを作成し、そのサーバーが3000番ポートでリクエストを待ち受けるように設定しています。サーバーにリクエストがあった場合、”Hello, World!”というレスポンスを返します。
これらの特性により、Node.jsはリアルタイムアプリケーション(例えば、チャットアプリケーションやゲームサーバーなど)、シングルページアプリケーションのバックエンド、APIサーバー、データストリーミングアプリケーション、コマンドラインツールなど、多岐にわたるアプリケーションを作成するための選択肢となっています。
あとがき
ここまででJavaScriptの再入門編は終了です。この記事を通して、JavaScriptの基本的な構文から関数、オブジェクト、そして非同期処理まで幅広い内容をおさらいしました。
次のステップでは、より深くJavaScriptに触れ、さらに先へ進んでください!
記事が長すぎたので初級講座:前後編に分けました。