JavaScript は多様で柔軟なデータ型の言語であり、基本的に 2 つのカテゴリ (プリミティブと オブジェクト)に分類されます。この区別は JavaScript が動作する基盤を形成するため、あらゆるレベルの開発者にとって理解することが重要です。理解を確実にするために、これらの概念を再確認してみましょう。
プリミティブ値: 基本
"hello"
や"farewell"
などのテキスト データは引用符で囲まれ、JavaScript でのテキスト操作の基礎として機能します。
-5
) であっても、小数 ( 3.14
) であっても、数値は言語における数学演算の基礎です。
プリミティブはimmutableです。つまり、プリミティブの値は一度作成されると変更できません。この特性は、特にプリミティブ値を保持する変数をその値自体と間違えた場合に混乱を招くことがよくあります。
プリミティブが不変であることを理解すると、JavaScript の動作、特に比較操作と代入操作の多くの側面を明確にするのに役立ちます。
JavaScript を使用する過程では、これらの型の一部を直接使用しない場合もありますが、それらの役割を認識して理解することでコーディング ツールキットが充実し、より洗練された効率的なコードへの道が開かれます。
プリミティブの領域を超えて、JavaScript の世界はオブジェクトによって支配されています。このカテゴリには幅広いデータ構造が含まれており、その中にはArraysなど、驚くかもしれないものもあります。主に、次のような問題に遭遇します。
{}
、配列の場合は[]
で表され、これらの構造は、関連するデータと機能をグループ化するためのバックボーンです。
x => x * 2
として表現される関数は JavaScript の第一級市民であり、コードを変数に割り当てたり、引数として渡したり、他の関数から返すことができます。
オブジェクトはプリミティブとは根本的に異なります。これらは変更可能であり、コード内で直接操作できます。よくある誤解は、JavaScript 内のすべてのものをオブジェクトとして見ることです。これは、プリミティブ値のオブジェクトに似た動作があるため、部分的には当てはまります。たとえば、式"hi".toUpperCase()
次のような疑問が生じるかもしれません。プリミティブ文字列にメソッドがあるのはなぜでしょうか?
これは、 「ボックス化」として知られるプロセスを通じて発生します。このプロセスでは、JavaScript はメソッドにアクセスするためにプリミティブ値をオブジェクト ラッパーに一時的にラップしますが、操作が完了するとこれらのオブジェクトのみが破棄されます。
これは JavaScript の設計の興味深い側面であり、プリミティブが実際にはオブジェクトでなくてもオブジェクトのようなメソッドの恩恵を受けることができます。 JavaScript の類型をさらに深く掘り下げる場合、この違いを理解することが重要です。
typeof
演算子とnull
の一意のケースを調べるJavaScript でさまざまなデータ型を区別するのは、ちょっとした魔法のように感じることがあります。 typeof
演算子を入力します。これは、指定された値の型を明らかにする JavaScript ツールキットの強力なツールです。実際の動作は次のとおりです。
console.log(typeof(5)); // Outputs "number" console.log(typeof("hi")); // Outputs "string" console.log(typeof(undefined)); // Outputs "undefined" console.log(typeof({})); // Outputs "object" console.log(typeof([])); // Outputs "object" console.log(typeof(x => x * 2)); // Outputs "function"
ただし、JavaScript の領域では、すべてが見た目どおりに見えるわけではありません。たとえば、 typeof
演算子のnull
の処理を考えてみましょう。予想に反して、 typeof null
"object"
を返します。これは多くの開発者を困惑させる結果です。この動作はバグというよりも、JavaScript の初期の設計上の決定に根ざした言語の癖です。
値null
、オブジェクト値が意図的に存在しないことを表しますが、オブジェクトとしてtypeof
されます。この癖はよく知られており、下位互換性への懸念から JavaScript の進化を通じて存続しています。
値が割り当てられていないことを示すundefined
とは異なり、 null
は「値なし」という意図的な割り当てを示すために明示的に使用されることを覚えておくことが重要です。 JavaScript ではnull
とundefined
の使用方法の区別は強制されませんが、コードに一貫したアプローチを採用すると、意図が明確になり、読みやすさと保守性の両方が向上します。
活気に満ちた JavaScript の世界では、コードを書くことは質問を投げかけることに似ており、言語は答えを返します。これらのインタラクションは、いわゆる「式」を通じてキャプチャされます。 JavaScript の式は、値に解決される有効なコード単位です。
簡単な例を見てみましょう。
console.log(5 + 5); // Outputs 10
この例では、 5 + 5
は、JavaScript が値10
で評価する式です。
式は JavaScript コードの構成要素であり、プログラム内での動的な対話と計算を可能にします。上記の例と同じくらい単純なものもあれば、より複雑なものもあります。実際、式は、インタラクティブで動的な Web アプリケーションを作成するための言語との直接のコミュニケーション手段です。
JavaScript のプリミティブ データ型 (文字列、数値、ブール値など) は事前定義されたエンティティとして呼び出されますが、オブジェクトは異なる原理で動作します。 {}
(中括弧) を使用するたびに、既存のブループリントを参照しているだけではありません。私たちはまったく新しいオブジェクトを存在させています。 2 つの単純なオブジェクトの作成を考えてみましょう。
const cat = {}; const dog = {};
ここでは、 cat
とdog
別個のオブジェクトであり、それぞれがメモリ内に独自のスペースを持っています。この原則は、単なるオブジェクト リテラルを超えて、配列、日付、関数など、JavaScript のすべての複雑なデータ構造を網羅します。
これらのエンティティを作成するにはさまざまな方法がありますが、オブジェクトの場合は{}
、配列の場合は[]
、日付の場合はnew Date()
を使用するのが、オブジェクト インスタンスを作成するための最も直接的な方法です。
しかし、これらのオブジェクトが不要になったらどうなるのでしょうか?それらは JavaScript の世界に無期限に残るのでしょうか?答えは、JavaScript のガベージ コレクション メカニズム(使用されなくなったメモリを効率的にクリーンアップするプロセス) にあります。
ガベージ コレクションは自動操作です。つまり、コード内でオブジェクトへの参照がなくなると、オブジェクトが破棄され、割り当てられたメモリが再利用されます。
JavaScript では、値を比較することは、等価性を理解するという同じ目的地に至るさまざまなパスがある迷路をナビゲートしているように感じることがあります。値を比較するには主に 3 つの方法があります。
厳密な等価( ===
): この形式の等価は最も正確で、2 つのオペランドの値と型の両方をチェックします。これは、「これら 2 つの値は型も内容も同じですか?」と尋ねることと同じです。
緩い等価性( ==
): 厳密な等価性よりも緩い等価性では、比較の前に型の強制が許可されます。これは、「これら 2 つの値の型を無視した場合、同じとみなせるでしょうか?」と尋ねているようなものです。
同一値の等価性( Object.is
): このメソッドは厳密な等価性と似ていますが、特に JavaScript の特別なケースを処理する方法において、いくつかの重要な違いがあります。
Object.is
の動作を見てみましょう。
console.log(Object.is(2, 2)); // true console.log(Object.is({}, {})); // false
Object.is({}, {})
false を返すのはなぜですか?各オブジェクト リテラル{}
はメモリ内に一意のオブジェクトを作成するため、構造的な類似性にもかかわらず、 Object.is
それらを別個のエンティティとして扱います。
厳密な等価性は簡単ですが、特に特定の JavaScript 値の場合に、独自の一連の特殊性が潜んでいます。
NaN === NaN
: 驚くべきことに、この比較はfalse
を返します。 JavaScript では、 NaN
(Not-a-Number) はそれ自体と等しくないものとみなされます。これは、未定義または誤った計算の結果を通知することを目的としたまれな特性です。
-0
と0
の比較: -0 と0
は JavaScript の数値体系では異なる値であるにもかかわらず、 -0
-0 === 0
と0 === -0
どちらもtrue
を返します。この等式ではゼロの符号が無視され、その大きさのみに焦点が当てられます。等価性チェックにおけるこれらの違いを理解することは、正確でバグのない JavaScript コードを作成するために最も重要です。 ===
と==
にはそれぞれの役割がありますが、 Object.is
いつ使用するかを知ることは、特にNaN
、 0
、および-0
を含むエッジ ケースの場合に重要です。
この知識により、開発者は等価性チェックについて情報に基づいた意思決定を行うことができ、コードが幅広いシナリオで期待どおりに動作することが保証されます。
JavaScript でオブジェクトのプロパティを操作する場合、 ドット表記と括弧表記という2 つの主要なツールがあります。どちらのメソッドも、オブジェクトのコンテンツにアクセスして変更するための簡単なパスを提供します。簡単な入門書は次のとおりです。
object.key
) を通じてプロパティに直接アクセスします。
object['key']
)。
これらのテクニックは、オブジェクトと対話するための基礎です。ただし、理解する必要がある重要な側面は、JavaScript がオブジェクト参照をどのように処理するかです。プリミティブ データ型とは異なり、JavaScript のオブジェクトは参照型です。つまり、オブジェクトを操作するときは、オブジェクト自体の直接コピーではなく、メモリ内のそのオブジェクトの場所への参照を操作することになります。
この概念を実現するために、作家志望の 2 人、エミリーとトーマスが小説で共同作業するシナリオを考えてみましょう。彼らは、JavaScript オブジェクトを使用してストーリーの登場人物と設定を構築することにしました。
const project = { title: "Adventures in Code", characters: { protagonist: { name: "Alex", traits: ["brave", "curious"] } }, setting: { location: "Virtual World", era: "future" } };
ストーリーを展開する中で、エミリーは主人公にインスピレーションを得た、ユニークなひねりを加えたサイドキック キャラクターを紹介します。
const sidekick = project.characters.protagonist; sidekick.name = "Sam"; sidekick.traits.push("loyal");
同時に、トーマスは小説の設定を拡張することを決定します。
const newSetting = project.setting; newSetting.location = "Cyber City"; newSetting.era = "2040";
一見すると、これらの変更が元のproject
オブジェクトにどのような影響を与えるのか不思議に思うかもしれません。結果は次のとおりです。
sidekick
が新しいオブジェクトではなく、 project.characters.protagonist
への参照であるためです。 sidekick
変更は、元のproject
オブジェクトに直接影響します。
newSetting
project.setting
への参照です。つまり、 newSetting
への変更はproject.setting
に直接影響します。この例は、JavaScript の重要な概念を強調しています。オブジェクトを操作するということは参照を操作することを意味し、オブジェクトを変数に代入すると、そのオブジェクトに参照が代入されることになります。
その参照を通じて行った変更は、そのオブジェクトへのすべての参照に反映されます。この動作により、複雑な相互接続されたデータ構造が可能になりますが、意図しない副作用を避けるために慎重な管理も必要になります。
私たちのストーリーでは、エミリーとトーマスの共同プロセスが、オブジェクト参照がコーディングにおける創造的な取り組みにどのように役立ち、複雑な物語、つまりより現実的な言葉で言えば、アプリケーション内の複雑なデータ構造の共有された動的な開発を可能にする方法を美しく示しています。
JavaScript でオブジェクトを操作する場合、参照コピーの性質上、直接代入すると意図しない変更が発生する可能性があります。オブジェクトのコピーを作成すると、元のオブジェクトに影響を与えることなく安全に操作できます。こうすることで、意図しない変更を軽減できます。
ニーズとシナリオに基づいて、浅いコピーと深いコピーのどちらかを選択できます。
Object.assign : このメソッドは、ソースからターゲット オブジェクト ( {}
) にプロパティをコピーすることによって、新しいオブジェクトを生成します。 Object.assign
浅いコピーを実行することに注意することが重要です。つまり、ネストされたオブジェクトまたは配列は値ではなく参照によってコピーされます。
const original = { a: 1, b: { c: 2 } }; const copy = Object.assign({}, original); copy.bc = 3; // Affects both 'copy' and 'original'
スプレッド演算子( ...
): Object.assign
に似たスプレッド演算子は、元のオブジェクトのプロパティを新しいオブジェクトに拡張し、浅いコピーを作成します。
const copyUsingSpread = { ...original }; copyUsingSpread.bc = 4; // Also affects the 'original' object
JSON.parseおよびJSON.stringify : このアプローチでは、オブジェクトを JSON 文字列にシリアル化し、それを解析して新しいオブジェクトに戻します。効果的にディープ コピーを作成しますが、関数、Date オブジェクト、未定義、その他のシリアル化できない値は処理できません。
const deepCopy = JSON.parse(JSON.stringify(original)); deepCopy.bc = 5; // Does not affect the 'original' object
ライブラリ: より複雑なシナリオの場合、Lodash のようなライブラリは、JSON メソッドよりも効率的にさまざまなデータ型を処理するなど、オブジェクトのディープ クローンを作成できる関数(例: _.cloneDeep()
) を提供します。
共同執筆プロジェクトの例をもう一度見てみましょう。
const project = { title: "Adventures in Code", characters: { protagonist: { name: "Alex", traits: ["brave", "curious"] } }, setting: { location: "Virtual World", era: "future" } };
オリジナルに影響を与えずにプロジェクトを変更するには:
JSON.parse(JSON.stringify(project))
を使用して、新しい文字を安全に追加するか、設定を変更します。
Object.assign
またはスプレッド演算子を使用します。
浅いコピーと深いコピーのどちらを選択するかは、オブジェクトの複雑さと操作の特定の要件によって異なります。浅いコピーはより速く、単純なオブジェクトに適していますが、深いコピーはネスト構造を持つオブジェクトに必要であり、元のオブジェクトが変更されないことが保証されます。
これらの対処手法を理解して適用することで、自信を持って JavaScript の参照ベースのシステムを操作でき、データ操作が正確かつ意図的に行われるようになります。
小説の登場人物が祖先から特性を継承するのと同じように、JavaScript のオブジェクトはプロトタイプからプロパティとメソッドを継承します。このコンセプトは、エミリーとトーマスが小説「Adventures in Code」で模索してきたストーリーテリングの手法を反映しています。
プロトタイプについての理解をさらに深めるために、JavaScript の継承モデルを反映した新しいキャラクター アークを紹介しながら、プロトタイプの話を続けましょう。
彼らの小説の世界には、知恵と語学の達人として知られる「古代の暗号士」として知られる伝説の筆記者が存在します。エミリーとトーマスは、次世代のプログラマーを表すこの神話上の人物を基にして、新しいキャラクター「コーダー レオ」を作成することにしました。
// The Ancient Coder, known for his profound wisdom const ancientCoder = { wisdom: 100 }; // Coder Leo, a young scribe in training const coderLeo = { __proto__: ancientCoder, age: 15 };
この物語では、コーダー レオは「プロトタイプ チェーン」として知られる魔法の継承を通じて古代のコーダーと直接結びついています。このつながりにより、レオは先祖の知恵を活用することができます。
console.log(coderLeo.wisdom); // 100
プロトタイプ チェーンのおかげで、コーダー レオは若いにもかかわらず、古代のコーダーの知恵にアクセスできます。しかし、古代のプログラマーが持っていなかった課題や特性に遭遇したらどうなるでしょうか?
console.log(coderLeo.courage); // undefined
この状況は、JavaScript のプロトタイプ システムの重要な原理を示しています。オブジェクトでプロパティが見つからない場合、JavaScript はプロトタイプ チェーンを検索してそれを見つけます。それでもプロパティが見つからない場合は、その特性が存在しないことを示すundefined
が返されます。
物語をさらに進めるために、エミリーとトーマスは、子孫にユニークな特性を追加して祖先と区別する方法を検討します。
// Introducing a unique trait to Coder Leo coderLeo.courage = 50; console.log(ancientCoder.courage); // undefined console.log(coderLeo.courage); // 50
ここで、コーダー レオは、古代のコーダーとは異なる勇気の特性を開発します。この変更は The Ancient Coder の属性を変更するものではなく、JavaScript の動的な性質のおかげでオブジェクト (またはキャラクター) がそのプロトタイプ (または祖先) とは独立してどのように進化できるかを示しています。
物語の中のこの物語は、エミリーとトーマスの小説を前進させるだけでなく、JavaScript のプロトタイプベースの継承にも光を当てます。小説の登場人物と同様に、オブジェクトは祖先から特性を継承することができます。しかし、彼らはまた、独自の道を切り開く能力も持っており、個々の歩みを反映した独自の特性を開発します。
エミリーとトーマスが小説「Adventures in Code」をさらに深く掘り下げていくと、神秘的な章、つまり古代のプロトスの書に遭遇しました。彼らは、この本が単なるプロットの手段ではなく、JavaScript のプロトタイプと組み込みメソッド、つまり物語の世界とコーディングの理解に魔法の層を追加する概念を理解するための比喩であることを発見しました。
彼らの小説の架空の舞台であるスクリプトビルでは、すべての登場人物や物体にプロトスの書からの能力が吹き込まれています。この魔法の本はすべての知識とスキルの源であり、オブジェクトがプロパティとメソッドを継承する JavaScript のプロトタイプに似ています。
// A seemingly ordinary quill in Scriptsville const quill = {};
エミリーは、彼女のキャラクターであるエリーを通じてこの羽ペンを探索しますが、それが魔法の糸を介してプロトスの書にリンクされていることがわかります。これは、JavaScript オブジェクトの__proto__
プロパティに直接似ており、プロトタイプに接続されます。
console.log(quill.__proto__); // Reveals the Tome's ancient scripts!
この啓示により、Ellie は、 hasOwnProperty
およびtoString
呼び出す機能など、本の知恵にアクセスできるようになり、Object プロトタイプから継承された JavaScript の組み込みメソッドが実証されます。
次に、物語はトーマスのキャラクター、魔法のドーナツで知られる有名なパン屋、マスター ドノバンを紹介します。自分の料理の才能を共有しようと、ドノバンは JavaScript でコンストラクター関数を定義するのと同じように、呪文を作成します。
function EnchantedDoughnut() { this.flavor = "magic"; } EnchantedDoughnut.prototype.eat = function() { console.log("Tastes like enchantment!"); };
ドノバンが作ったドーナツはどれも魔法のエッセンスを持っており、食べた人は誰でも魔法を体験できるようになります。ストーリーのこの部分では、ドノバンのドーナツが食べられる機能を継承するのと同じように、JavaScript のコンストラクター関数で作成されたオブジェクトがコンストラクターのプロトタイプからメソッドを継承する方法を説明します。
Scriptsville が進化するにつれて、その魔法も進化し、古代の呪文からクラス構文の現代芸術へと移行しています。エミリーとトーマスは、新しい構文を使用してドノバンの技術を再考し、彼の魔法をよりアクセスしやすくし、JavaScript の現代の実践に合わせます。
class ModernEnchantedDoughnut { constructor() { this.flavor = "modern magic"; } eat() { console.log("Tastes like modern enchantment!"); } }
この移行は、Donovan のベーキング技術を更新するだけでなく、JavaScript の進化を反映し、基礎となるプロトタイプベースの継承を維持しながら、クラス構文の優雅さと効率性を強調します。
「Adventures in Code」の作成を通じたエミリーとトーマスの旅は、プロトタイプ、組み込みメソッド、JavaScript 自体の進化を理解するための魅力的な寓意となっています。
彼らはキャラクターやストーリーを通じて、魅力的かつ奥深い方法で複雑な概念を明らかにし、すべてのオブジェクトとキャラクターが、すべての JavaScript オブジェクトと同様に、継承と革新というより大きな相互接続されたタペストリーの一部であることを示します。
彼らの物語は、ストーリーテリングとプログラミングの両方における基本的な真実を強調しています。つまり、過去を理解することは、現在をマスターし、未来に向けて革新するために不可欠であるということです。
古代の書物、魔法のドーナツ、現代の魔法を備えたスクリプトビルの魔法の世界は、JavaScript のプロトタイプ システムと継承の力の深さを探索するための鮮やかな背景として機能します。
エミリーとトーマスは小説「Adventures in Code」をさらに深く掘り下げていくうちに、キャラクター、設定、魔法のアイテムで満たされた複雑な世界を管理する方法の必要性を発見しました。
彼らは、スクリプトビル出身の賢明な筆記者であるエリーに相談しました。エリーは、物体に魔法をかけ、土地全体の調和を確保する専門知識で知られていました。
エリーは、彼らの世界内でネストされたレルムの存在を保証できる魔法の公式、 ensureObjectPath
を彼らに共有しました。
function ensureObjectPath({obj, path}) { path.split('.').reduce((acc, part) => { if (!acc[part]) acc[part] = {}; return acc[part]; }, obj); return obj; } // Ensuring the path to a hidden forest in their novel const world = {}; ensureObjectPath({obj: world, path: 'hidden.forest.clearing'}); console.log(world); // Outputs: { hidden: { forest: { clearing: {} } } }
エリーは、この魔法によって小説の世界にあらゆる場所を作成できるようになり、各登場人物が存在しない領域に足を踏み入れることを恐れることなく冒険に乗り出せるようになると説明しました。
さらに、エリーは、すべてのキャラクターが旅に必要な必須の属性を確実に持つように設計された別の呪文checkForRequiredKeys
を彼らに紹介しました。
const REQUIRED_KEYS = ['age', 'address', 'gender']; function checkForRequiredKeys(obj) { REQUIRED_KEYS.forEach(key => { if (!Object.hasOwn(obj, key)) { obj[key] = {}; } }); } // Ensuring every character has the essential attributes const character = { name: "Ellie" }; checkForRequiredKeys(character); console.log(character); // Outputs: { name: "Ellie", age: {}, address: {}, gender: {} }
この呪文により、エミリーとトーマスは登場人物に複雑さを織り込むことができ、たとえ物語がどれほど複雑になっても、細部が見落とされることはありませんでした。
彼らの物語が展開するにつれて、エリーが共有した魔法は「コードの冒険」を豊かにするだけでなく、JavaScript のオブジェクト操作と構造の基礎となる原則を明らかにしました。
ensureObjectPath
呪文によってネストされた現実の作成が可能になったのと同様に、JavaScript 開発者はアプリケーション内でデータを構造化するために同様の力を行使します。
同様に、 checkForRequiredKeys
スペルは、データの整合性を確保するために不可欠な防御的なプログラミング手法を反映しています。
スクリプトビルの魅惑的な領域をさまようとき、エミリー、トーマス、エリー、そしてさまざまな魔法の存在たちが私たちの仲間となり、土地そのものと同じくらい魅惑的な方法で JavaScript の秘密を明らかにしてくれました。
冒険と発見の物語を通じて、最も単純な構文から表面下で脈動する複雑なエンジンに至るまで、JavaScript の中心を深く掘り下げてきました。
これは、ストーリーテリングの魔法がプログラミングのロジックと融合し、JavaScript の世界の重層的な驚異を明らかにする、他に類を見ない旅でした。
===
)、緩やかな平等 ( ==
)、および微妙な比較のためのObject.is
の使用を理解することの重要性を明らかにしました。
オブジェクトのプロパティとプロトタイプ: オブジェクトのプロパティを詳しく調べると、プロパティにアクセスして変更するためのドットとブラケット表記の威力と、オブジェクトの継承におけるプロトタイプの極めて重要な役割がわかりました。
Scriptsville の継承ワイヤーと魔法の巻物の物語は、プロトタイプ チェーンに命を吹き込み、オブジェクトがどのように特性を継承しオーバーライドするかを示しました。
ensureObjectPath
やcheckForRequiredKeys
など) は、ネストされたオブジェクトを操作し、必須プロパティの存在を確認するための実用的なテクニックを示しました。
Scriptsville の物語のレンズを通して、私たちは JavaScript の機能が魔法のようにも論理的にもなり、開発者に創造性と問題解決のための広大な遊び場を提供する様子を見てきました。
スクリプトビルの魅力的な物語は単なる物語ではありません。これらはプログラミングの芸術性の比喩であり、デジタル世界を巧みに構築するために JavaScript の核となる原則を理解することの重要性を強調しています。
この旅の本を閉じるとき、冒険はここで終わるわけではないことを忘れないでください。私たちが検討した各概念は、JavaScript をより深く理解し、習得するための足がかりとなります。
エミリーとトーマスが「コードの冒険」の物語を紡いだように、あなたもプログラミングの無限の世界の中で物語、魔法のオブジェクト、魔法の世界を作り上げることができます。