JavaScriptは奇妙で、時には無価値であるという不満を、多くの人が耳にするでしょう。人々がこのように不平を言うのは、何が起こっているのか理解していないからです。JavaScriptには異なる処理をするシナリオがあることには同意しますが、だからといってそれが奇妙なのではなく、それなりに美しいのです。
プログラミング言語を好きになるには、まずその言語を深く理解し、コンセプトをひとつひとつマスターすることから始めるべきです。
JavaScriptのエキスパートになるために、36のコンセプトをマスターしましょう。
これは私の投稿の中でも長い部類に入りますが、読んでいただく価値はあると断言します。資料を提供してくれたスティーブンとレオナルドに感謝します。
コールスタックの実行
誰もがStack Overflowサイトを聞いたことがあるでしょう。しかし、実際のスタック・オーバーフローについて知っていますか?スタック・オーバーフローとは、スタックを呼び出す操作に関連するエラーのことです。
スタックコールを理解することで、JavaScriptのような高級言語がどのように動作するかを理解することができます。
プリミティブなデータ型
const foo = "bar";
foo.length; // 3
foo === "bar"; // true
などなど!
文字列barを定数fooに代入すると、fooは基本文字列型になります。これは誰にでも受け入れられることです。しかし、なぜ文字列型のlength属性にアクセスできるのでしょうか?
変じゃない?
この機能はオートボックスと呼ばれます。上の例では、JavaScriptは定数を一時的なラッパーオブジェクトにラップし、そのオブジェクトのlengthプロパティにアクセスしています。このステップが完了すると、オブジェクトは安全に破棄されます。
基本的なデータ型を詳しく見ていくことで、それらがバイナリ表現でどのようにメモリに格納されるかを学びます。また、「奇妙な」状況がどのように発生するのか、その論理的な理由についても学びます。
値型と参照型
最近、「参照渡し」という概念がJavaScriptでどのように機能するのか、少し混乱しています。CやJavaのような言語では「参照渡し」や「値渡し」という概念は知っていますが、JavaScriptではどうなのかよくわかりません。
JavaScriptでは、基本型以外の値を変数に代入すると、実際にその値への参照が代入されることをご存知ですか?参照とは、値が格納されているメモリの場所を指すものです。
var arr1 = [1,2,3];
var arr2 = arr1;
arr2.push(10);
console.log(arr2);
//[1, 2, 3, 10]
console.log(arr1);
//[1, 2, 3, 10]
上の例からわかるように、arr2 に加えられた変更は arr1 にも反映されます。これは、実際の値そのものではなく、値への参照を保持しているだけだからです。
値型と参照型の概念を理解することで、変数にどのように値が代入され、メモリ参照が行われるかをより理解できるようになります。
強制型変換
この概念は基本的に、暗黙的な強制型変換と明示的な強制型変換の違いを説明するものです。これはJavaScriptを使うときにうまくいかない数少ない分野のひとつです。特に暗黙的な型変換の概念については、データ型によって動作が異なるためです。
Number('789') // 明示的変換
+'789' // 暗黙の変換
789 != '456' // 暗黙の変換
9 > '5' // 暗黙の変換
10/null // 暗黙の変換
true | 0 // 暗黙の変換
型変換を明確に理解することで、JavaScriptの最も厄介な概念の1つを理解したことになります。
等価比較と'typeof'演算子
この概念では、基本的に二重等号と三重等号の使い方と、それらがいつ、なぜ使われるのかを説明します。表面的には同じように見え、ほとんどの場合同じ結果をもたらしますが、知らずに使用すると予期せぬエラーを引き起こす可能性があります。
また、typeof演算子を使い、出力の可能性を知ることもできるはずです。しかし、オブジェクトがあると混乱することがあります。
typeof 3 // "number"
typeof "abc" // "string"
typeof {} // "object"
typeof true // "boolean"
typeof undefined // "undefined"
typeof function(){} // "function"
typeof [] // "object"
typeof null // "object"
JavaScript
スコープという概念は、JSの旅の最初に理解しておくべきものだと思います。ウィッサムによると、スコープの簡単な定義は、コンパイラが変数や関数を必要とするときに、その変数や関数を探す場所ということです。
スコープを理解することで、JavaScriptをより効率的に使えるようになります。グローバルスコープ、ブロックスコープ、関数スコープ(レキシカルスコープとも呼ばれる)を理解する必要があります。
ステートメントと式
これらはJavaScriptの2つの主要な構文カテゴリです。この2つの違いと、ステートメントがどのように計算されるかを知っておく必要があります。これにより、コードが式と文にどのように構成されるかを完全に理解することができます。あなたのコードのほとんどが式であることに気づくでしょう。また、両者の誤った使用から生じるエラーも避けることができます。
関数式とモジュールの即時呼び出し
即時呼び出し関数式は、定義直後に実行される関数です。主にグローバル・スコープの汚染を避けるために使用されます。その後、ES6モジュールが導入され、グローバルスコープの汚染を回避する標準的な方法が提供されましたが、IIFEの直接的な置き換えにはならないという意見もあります。
IIFEとモジュールを理解することで、アプリケーションの開発において、グローバルスコープの誤操作によるエラーを少なくすることができます。しかし、モジュールを使えば、さらに多くのことができるようになります。
メッセージキューとイベントループ
MDNあるように、JavaScript はイベントループに基づいた同時実行モデルを持っており、コードの実行、イベントの収集と処理、キューに入れられたサブタスクの実行を行います。このモデルは他の言語のものとは大きく異なります。
前述の同時実行モデルでは、メッセージ・キューは最も古いものから順番にメッセー ジを処理します。イベントが発生し、イベント・リスナーがそれをリッスンするとすぐに、メッセージはキューに追加されます。
これらの概念を理解することで、JSがフードの下でどのように動作し、コードをどのように解釈すればよいかをよりよく理解することができます。
時間間隔
JavaScriptで呼び出しや関数をディスパッチするには、2つのメソッドを使用できます。
- setTimeoutは、特定の時間間隔の後に関数を1回実行させます。
- setIntervalは、関数を繰り返し実行し、特定の時間間隔の後に開始し、その時間間隔の間、連続して実行を繰り返すことができます。
これらは以前のメッセージ・キューやイベント・ハンドラの概念と多少関連しています。従って、インターバル・メソッドを理解することで、それらがどのように動作するかを理解し、ユースケースで効果的に使用することができます。
JavaScript
JavaScript エンジンは多くの言語で書くことができます。たとえば、Chrome を動かす V8 エンジンは c++ で書かれており、Firefox を動かす SpiderMonkey エンジンは C と c++ で書かれています。
効率的なコードを書くためには、どのJavaScriptエンジンを使っているかを知ることがとても重要です。ウェブビューを使用するモバイル開発者は特にこの点に注意する必要があります。
ビット操作
これらの演算は、値を10進数、16進数、8進数ではなくビットとして扱います。ビット演算子はこの 2 進表現に対して演算を行いますが、JavaScript の標準的な値を返します。
通常、これらの操作をコードで使用することはありませんが、いくつかの使用例があります。偶数値や奇数値の検索、色の変換、色の抽出、設定フラグなどに使用できます。
これらのビット操作を徹底的に理解することで、WebGLのようなピクセル操作を多く含む技術をうまく扱うことができます。
DOMとレイアウトツリー
Document Object Model(ドキュメント・オブジェクト・モデル)という言葉を聞いたことがある人は多いと思いますが、詳しく知っている人はごく少数でしょう。ブラウザに表示されるのは DOM ではないことをご存知ですか?むしろレンダリングツリーであり、実際には DOM と CSSOM を組み合わせたものなのです。
DOM がどのように機能し、どのように構造化され、どのようにページがレンダリングされるかを理解することで、JavaScript の助けを借りて Web ページを動的に操作することが可能になります。これは特に、アプリケーションの高いパフォーマンスを保証するために必要です。
クラスとファクトリー
JavaScriptはオブジェクト指向言語ではありません。しかし、コンストラクタはOOPプロパティをシミュレートするために使用されます。タニアによると、「JavaScriptのクラスは実際には追加機能を提供せず、よりすっきりしたエレガントな構文を提供するため、しばしばプロトタイプと継承の上に構文的な砂糖を提供すると説明されます。他のプログラミング言語ではクラスを使っているので、JavaScriptのクラス構文は開発者が言語を切り替えるのを簡単にしてくれます。"
ファクトリー関数とは、クラスでもコンストラクタでもなく、オブジェクトを返す関数のことです。JSの第一人者であるEric Elliotは、「JavaScriptでは、どんな関数でも新しいオブジェクトを返すことができる。JavaScriptでは、どんな関数でも新しいオブジェクトを返すことができます。"コンストラクタやクラスでない場合、それはファクトリー関数と呼ばれます。
特に大規模なアプリケーションの開発を始めるのであれば、この2つの概念を十分に理解しておく必要があります。
this キーワードと、'apply'、'call'、'bind' メソッド。
個人的には、JS開発者にとってthisキーワードを理解することは非常に重要だと思います。このキーワードを正しく理解しておかないと、後でアプリケーションに様々な問題が発生します。
this""キーワードを十分に理解したら、applyメソッド、callメソッド、bindメソッドに注目しましょう。これらのメソッドは、適切なコンテキストで関数を呼び出すために必要です。特にbindメソッドは、thisにアクセスするコールバックを渡すときに必要になります。私は友人のコードのデバッグを手伝っているときに、このことを学びました!
第16回 コンストラクタと「instanceOf」演算子
コンストラクタは通常の関数と同じです。しかし、多くの違いがあります。慣習上、関数名は大文字で始まり、new 演算子でしか実行できません。OOPのバックグラウンドを持つプログラマは、この新しいキーワードに精通しているはずです。
オブジェクトの型を正しく識別するには、instanceOf演算子を使います。簡単に言うと、オブジェクトが別のオブジェクトのインスタンスであるかどうかをチェックします。
これは、オブジェクトが互いにどのように継承するのかを理解するのに役立ちます。継承はプロトタイプを通して行われます。
プロトタイプ
これは、10年の経験を持つ人でも、JavaScriptで最も混乱する概念の1つです。
JavaScriptにおけるプロトタイプは、オブジェクト間で共通の機能を共有するためのメカニズムです。典型的なオブジェクトはすべてのプロパティとメソッドを Object.prototype から継承します。
簡単に言うと、プロトタイプとはJavaScriptオブジェクトがメソッドやプロパティを継承するオブジェクトのことです。
プロトタイプをよりよく理解することで、効率的で高速なアプリケーションを構築することができます。
new', 'Object'を使いましょう。.create'オブジェクト.assign'オブジェクトの作成
JavaScriptでオブジェクトを作成する方法はたくさんあります。しかし、newキーワードを使うよりもObject.createを選ぶ人がいるのには理由があります。Object.createでオブジェクトを作成する場合、既存のオブジェクトを新しく作成するオブジェクトのプロトタイプとして使用することができます。これにより、既存のオブジェクトのプロパティや関数を再利用することができます。
Object.assign メソッドを使用すると、独自の列挙可能プロパティを 1 つ以上のソース・オブジェクトからターゲット・オブジェクトにコピーできます。この場合、ターゲット・オブジェクトのプロトタイプにはソース・オブジェクトのプロパティは含まれません。これが 2 つのメソッドの主な違いです。
これら3つのオブジェクト生成方法を理解することで、アプリケーションのユースケースに応じて適切に使い分けることで、メモリ効率の良いプログラムを作成することができます。
'map', 'filter', そして「reduce」メソッド
これらの3つのメソッドは、配列の操作に関して非常に便利です。これらは配列のプロトタイプにあります。
配列があり、各要素に対して何かを行いたい場合は、mapメソッドを使用します。
配列があり、各要素に対して条件を指定してその条件を満たす値を取得したい場合は、 filter メソッドを使用します。
reduce メソッドは、配列のすべての要素に対して reducer 関数を実行し、最後に値を返します。完全な例は、配列のすべての要素の合計を取得することです。
let numbers = [1,2,3,4,5,6]
const reduced = numbers.reduce((accumulator, currentValue) => accumulator + currentValue)
console.log(reduced)
// 21
上記の3つのメソッドは、元の配列の値を変更しないことに注意してください。
純粋関数、副作用、状態変化
これら3つの概念は、JavaScript開発者にとって重要です。状態の変更は、React を使用する開発者にとって特に重要です。
純粋関数は、スコープ外の変数にアクセスしたり変更したりすることなく、常に与えられた入力と一致する値を返します。このタイプの関数は、読みやすく、デバッグしやすく、テストしやすいものです。
副作用とは、不要なときに変数が作成され、スコープ全体で利用可能になるコードの一部です。関数がスコープ外の変数にアクセスすると、副作用が発生します。
状態変化とは、変数の値が変化することです。変数を変更した場合、変更した値によっては他の関数に影響を与える可能性があります。reactの環境では、状態を変更しないことが推奨されます。インバリアントについてはこちらの記事参考になります。
クロージャ
クロージャを理解するのは難しい。しかし一度理解すれば、JavaScriptの素晴らしさが見えてくるはずです。オンラインには豊富なリソースがあります。時間をかけてクロージャについて学んでください。
クロージャを使うと、関数の内部スコープから外部関数のスコープにアクセスできるようになります。JavaScript のクロージャは、関数を作成するたびに作成されます。
クロージャをより深く理解するために、クロージャを使うべき理由を学びましょう。
高階関数
高階関数とは、他の関数を引数として受け取ったり、関数を返したりする関数のことです。高階関数を使用すると、合成のパワーを最大限に引き出すことができます。1つのタスクだけを処理する小さな関数を作成し、その小さな関数の助けを借りて複雑な関数を構築することができます。これにより、コードの再利用性も向上します。
これによりエラーも減り、コードが読みやすく理解しやすくなります。
再帰
再帰はすべてのプログラミング言語に共通する概念です。簡単に言えば、再帰とは大きな問題を小さな問題に分解する概念です。
実際には、これは通常、自分自身を呼び出す関数を書くことを意味します。再帰は頭が痛くなるような分かりにくい概念ですが、小さな問題から何度も練習することで、理解を深めることができます。
ただし、再帰に注意しないと、スタック・オーバーフロー・エラーに遭遇する可能性があることに注意してください。練習として、このエラーについて調べてみてください。このエラーの背景を完全に理解するには、最初のトピックであるスタックの呼び出しに関する知識を復習する必要があります。
コレクションとジェネレーター機能
ES6ではコレクションとジェネレータ関数が新しく導入されました。新しく導入されたコレクションは、Map、Set、WeakSet、WeakMapです。これらのコレクションは、非常に優れたユースケースを提供します。特にモダンなJavaScriptを使用する場合は、これらの使用方法を理解することが重要です。
一方、ジェネレータ関数は、特に初心者にとっては、理解するのが少し難しい場合があります。ジェネレータを使うと、JavaScriptでは非常に珍しい、他のコードの実行をブロックせずに関数を一時停止したり再開したりできるコード関数を書くことができます。
Promises
ジャクリーヌは約束についてこう説明します。あなたのお母さんが、来週新しい携帯電話を買ってあげると約束したとします。
その携帯電話をもらえるかどうかは、来週にならないとわかりません。お母さんは本当に新しい携帯電話を買ってくれるのか、それとも不満だから買ってくれないのか。
プロミスには3つの状態があります。それらは
- 保留:その電話がかかってくるかどうかはわかりません。
- 満たされた:お母さんはとても喜んで、あなたに新しい携帯電話を買ってあげました。
- 不採用:携帯電話を買ってもらえないのが不満のようです。
今のところ、これは私が持っていたPromiseの説明の中で最もシンプルでわかりやすいものです。正直なところ、私はデモプロジェクトに取り組んでいるときにPromiseの概念を学びました。そのため、Promiseが何なのか全く分からなかったので、何が起こっているのか理解するのが大変でした。現在に話を戻します。ウェブ上の豊富なリソースのおかげで、私はPromiseをよりよく理解できるようになりました。プロジェクトに関する実務的な知識と組み合わせることで、私は明確に理解することができました。
非同期プログラミング
非同期プログラミングとは何かを理解するためには、まず同期プログラミングの知識を深める必要があります。同期プログラミングはスレッドブロックで、JavaScriptはシングルスレッドなので、コードは一行ずつ実行されます。
しかし、非同期コードを使えば、メインスレッドをブロックすることなく、長いネットワークリクエストを実行することができます。これは、完了までに時間がかかる複数のタスクを実行する必要がある場合に特に便利です。しかし、非常に長いタスクであってもスレッドをブロックする必要がある場合もあります。このような場合に、async/await の概念を使用します。
これらの概念をよく学ぶことで、多くのタスクが実行されていても効率的に実行できるプログラムを書くことができるようになります。
ES6矢印関数
アロー関数は、通常の関数を構文的に置き換えるES6アドオンです。違いは、アロー関数が this、arguments、super、new.target キーワードに束縛されないことです。このため、アロー関数は良い選択となる場合もありますが、悪い選択となる場合もあります。
したがって、矢印関数を使う習慣は絶対にやめましょう。ユースケースに応じて実装してください。
データ構造
使用するプログラミング言語にかかわらず、データ構造は開発者が持つべき基本的な知識のひとつです。
「ダメなプログラマーはコードを気にします。良いプログラマーは、データ構造とデータ間の関係を気にします。"リナス・トーバルズ、LinuxとGitの生みの親
さまざまなデータ構造について理解を深めることで、さまざまな環境でうまく動作する効率的なプログラムを構築できるようになります。連鎖テーブル、キュー、スタック、ツリー、グラフ、ハッシュテーブルについて知っておく必要があります。
時間の複雑さ
時間複雑性解析もまた、プログラミング言語に依存しない、コンピュータ・プログラミングのもう1つの基本原則です。より良いアプリケーションを作るためには、より良い解を書くべきです。そのためには、時間の複雑さの概念を理解する必要があります。これはビッグ・オーと呼ばれることもあります。
ビッグO記号は、アルゴリズムが必要とする実行時間または使用するスペースを表します。ビッグO表記は特に最悪のシナリオを表します。
これによって、最悪のケースであっても、最もパフォーマンスの高いアルゴリズムを選択して実装することができます。
アルゴリズム
これは、コンピュータサイエンスのコースで最初に学ぶことの1つです。要するに、アルゴリズムとは目標を達成するための段階的なプロセスのことです。プログラマーはどんな問題でもアルゴリズムの視点から見ることができなければなりません。プログラマーは、問題と解決策をステップバイステップで組み立てることができなければなりません。このアルゴリズムは、後であなたが書くプログラムです。
アルゴリズムの使用例は何千とありますが、そのうちの2つは非常に一般的です。
- 検索
- ソート
この2つのユースケースはプログラマにとって非常に一般的であり、少なくとも利用可能な既知のアルゴリズムについて包括的に理解しておく必要があります。これらのアルゴリズムのどちらかを使うべきだという決まったルールはありませんが、これらのアルゴリズムはよく知られており、性能に関してもよく文書化されています。
独自のアルゴリズムを作成し、世界に紹介することもできます。それが現在知られているアルゴリズムよりも優れていれば、あなたは次のプログラミングのスターになれるかもしれません!
継承、ポリモーフィズム、コードの再利用
JavaScriptの継承はプロトタイプで動作します。これはJavaScriptが非オブジェクト指向言語だからです。しかし、JavaScriptはプロトタイプ継承を提供することで、OOPのいくつかの機能を提供しています。
一方、ポリモーフィズムとは、オブジェクト、変数、関数が複数の形をとることができるという概念です。ポリモーフィズムの典型的なタイプは静的型付けされたシステムでより明らかになるため、JavaScript でポリモーフィズムの効果を見るのは少し難しいです。
上記の両方の概念は、JavaScript におけるコードの再利用に役立ちます。特にJavaScriptでは、この2つの概念をしっかりと理解することで、高品質で有用なコードを書くことができるようになります。
デザイン・パターン
関数型プログラミング
Wikiによると、"関数型プログラミングはプログラミングパラダイムであり、計算を数学的関数の計算として扱い、状態の変化やデータの変化を避けるコンピュータプログラムの構造や要素を構築するスタイルである "とのこと。
関数型プログラミングには、習得すべきいくつかの概念があります。
- Pure functions
- Immutability
- Referential transparency
- 高次関数
関数型プログラミングのこれらのコンセプトを理解することで、あなたは確実に優位に立つことができます。
クリーンコードの原則
これは、どのプログラミング言語を使うにせよ、すべての開発者が習得すべき必須のスキルです。それぞれのプログラミング言語には、個別に「良い」プラクティスがあります。これらの「良い」プラクティスは主観的なもので、職場によって異なりますが、「良い」と認識されているプラクティスはいくつかあります。
これらのコード原則に従うことで、誰にとっても読みやすく保守しやすいコードにすることができます。そうすることで、アプリ開発中、あなたとあなたのチームがスムーズに協力し合えるようになります。
脱構築
デストラクチャリング演算子はES6で導入されました。この演算子には、ぜひとも知っておきたい使用例がたくさんあります。同じユースケースであれば、以前の実装よりもシンプルで効率的です。これは拡張演算子としても知られています。
ES2020
プログラミングの素晴らしいところは、常に学び続けなければプログラミング言語のエキスパートになれないということです。プログラミング言語は、メジャーバージョンアップのたびに機能が追加され、進化していきます。
つまり、ある特定のコンセプトに関するあなたの専門知識は、バージョンアップとともにより優れた代替品がリリースされるため、今後10年のうちに陳腐化または時代遅れになるということです。これはどのプログラミング言語でもよくあるシナリオです。
言語をマスターするには長年の経験と時間が必要ですが、何をマスターすべきかを知っていれば、物事はより簡単になります。
上記の36のコンセプトの学習教材は、下記のリソースをご覧ください。このGithubストレージは素晴らしい出発点です。
スティーブン・カーティスの記事
レオナルド・マルドナドのgithubリポジトリ