jsネイティブ構文プロトタイプ、__proto__、コンストラクタの徹底的な理解

jsネイティブ構文プロトタイプ、__proto__、コンストラクタの徹底的な理解

1 はじめに

Vueのソースコードコメントをいくつか書いてみましたが(分析ではありません...)、プロトタイプの理解が足りないと感じました。JSではプロトタイプが非常に重要です。JSの山を登ろうとする限り、それはあなたを笑って、「理解しましたか?」と尋ねます。そうでなければ、重力を10倍にします。理解すれば、間違いなく月に1万元以上を稼ぎ、美しくて裕福な女性を獲得し、人生の頂点に達することができます~~~


この記事は私自身の理解について語っており、オリジナルであるはずです(99%確信していますが、以前に記事を読んで心に覚えていて引用できない場合は、私に連絡して追加できます)。ただし、誰かが私の記事を参照する場合は、この記事へのリンクを提供したいと考えています。実際、私は自分の記事の読者を増やし、月に1万元以上を稼ぎ、美しくて裕福な女性を獲得し、人生の頂点に到達したいだけです~~~

2 前提条件

2.1 データ型

jsには7つのデータ型があります

属性が読み取れるかどうかによって、2つのカテゴリに分けられます。

  • 次のプロパティを読み取ることができます:
    • 属性を持つことができます: オブジェクト
    • 属性を持つことはできません: 文字列、数値、ブール値、シンボル
  • プロパティを読み取れません: null、未定義

null、未定義の型、プロパティの読み取りと設定は不正であり、エラーが直接報告されます。

オブジェクトのみが独自のプロパティを持ち、プロパティを読み取ったり設定したりできます。

文字列、数値、ブール値、シンボルの各型は属性を読み取ることができます。実際には、最初にラッパー オブジェクトに構築され、その後属性が読み取られます。属性の設定についても同様です。すぐに破棄されるラッパー オブジェクトに属性を設定すると、属性は設定できますが、実質的な効果がないことがわかります。

2.2 それが自身のプロパティであるかどうかを判断する (hasOwnProperty)

hasOwnProperty メソッドは継承され、オブジェクト自体にこのプロパティがあるかどうかを判断するために使用されます。プロパティがある場合は、値が何であっても問題ありません。

定数オブジェクト = { a: 1 }
定数 o = Object.create(obj)
オブ = 1
oc = 無効 0
console.log('a', oa, o.hasOwnProperty('a')) // 継承された値は読み取れますが、独自のプロパティは読み取れません console.log('b', ob, o.hasOwnProperty('b')) // 独自のプロパティである値は読み取れます console.log('c', oc, o.hasOwnProperty('c')) // 未定義の独自のプロパティを読み取ります console.log('d', od, o.hasOwnProperty('d')) // 未定義のプロパティを読み取ります。独自のプロパティではなく、このプロパティから継承されたものではありません

3 ちょっとした考察

プログラムはデータ構造とアルゴリズムです。優れたプログラムは、最小限のメモリを使用してデータを保存し、最短時間で操作を完了して結果を取得します。

データを再利用すると、メモリ使用量を削減できます。たとえば、a と b が同じ機能を実行する必要がある場合、同じメソッド (属性) を再利用できます。

次に、この再利用方法がどこに存在し、a と b はそれをどうやって見つけられるかという問題を解決する必要があります。

js での解決策は、a と b の両方が関数 (ここでは関数と呼びます) から構築され、再利用されたメソッドが関数 (prototype プロパティ内) に格納されることです。

(再利用されたメソッドを格納する必要があるのはコンストラクターだけなので、プロトタイプは構築可能な関数にのみ存在します。矢印関数は構築には使用されないため、プロトタイプはありません。関数でもない他のオブジェクトには、このプロパティはありません。)

次に、a と b が Function で使用できる再利用方法を見つける必要があるため、a、b、および Function 間の接続を確立する必要があります。

jsでの実装はコンストラクタ属性を介して行われます。つまり、a.constructor、b.constructorは関数を見つけることができます。

したがって、a.constructor.prototype を通じて、再利用できるメソッドのストレージ アドレスを見つけることができます。js をすばやく見つけるために、1 ステップで検索できるショートカット メソッド a.__proto__ が提供されています。つまり、a.constructor.prototype と a.__proto__ は同じオブジェクトを検索するため、当然、それらは等しくなります。

// どちらも独自のプロパティではありません。これら 2 つのプロパティからプロトタイプ オブジェクトを見つける方法がわかりません。これは魔法に違いありません.....
定数オブジェクト = {}
console.log(obj.hasOwnProperty('__proto__')) // 偽
console.log(obj.hasOwnProperty('constructor')) // false

(したがって、コンストラクタ、プロトタイプ、__proto__ のポインタを手動で変更する場合は、何を行っているかを知っておく必要があります)

(js の設計者がこのように考えているかどうかはわかりませんが、私はそう思います。この方がずっと理解しやすいです)

(このプロセスは継承と呼ばれ、チェーンプロセスです。つまり、先頭が見つかるまで a.constructor.prototype.constructor.prototype.constructor.prototype を検索できます。このプロセスは、a.__proto__.__proto__.__proto__ によって加速できます。これはプロトタイプチェーンと呼ばれます。これは、js で継承を実装する唯一の方法です。)

(上記は思考プロセスをガイドするためのものです。実際には、プロトタイプ オブジェクトは a.constructor.prototype を通じてではなく、__proto__ を通じて直接見つかります)

3.1 コンストラクタを変更する

定数Dog = 関数() {}
定数 dog = 新しい Dog()

dog.コンストラクタ = 0

console.log(dog.hasOwnProperty('constructor')) // true
console.log(dog.constructor) // 0

console.log(dog.__proto__.constructor) // [関数: Dog]

要約すると、このプロパティを変更すると、それを構築するコンストラクターを見つけるのが難しくなります。直接取得することはできず、プロトタイプ オブジェクトから読み取る必要があります。

自身のプロパティとプロトタイプのプロパティの両方が変更された場合、そのコンストラクターのみが見つからなくなり、その他の影響はありません。

3.1.1 インスタンス

Instanceofはプロトタイプチェーンに関係しており、コンストラクタとは関係ありません。

上記の点を確認すると、コンストラクター属性を変更しても、インスタンスがコンストラクターを見つけられなくなること以外には影響はありません。インスタンスを通じてコン​​ストラクターを見つける必要がある場合は、2 つの間の関係を維持する必要があります。

// 構文は // a instanceof b
// この演算子は、a のプロトタイプ チェーンに b.prototype があるかどうかを判断します。b.prototype を決定する必要があるため、b は構築可能な関数である必要があります。そうでない場合はエラーが報告されます。const fn = function () {}
const o = Object.create(fn.prototype)
// この時点で、o のプロトタイプチェーンには fn.prototype が存在します。o.__proto__ === fn.prototype であるためです。
console.log(o instanceof fn) // true
定数空のオブジェクト = {}
fn.prototype = 空のオブジェクト
// この時点では、o.__proto__ は fn.prototype と等しくないため、o のプロトタイプ チェーンには fn.prototype がありません。console.log(o instanceof fn) // false
o.__proto__ = 空のオブジェクト
// o.__proto__ を修正するだけです console.log(o instanceof fn) // true

3.1.2 プロトタイプ

現在、instanceofと同じ機能を実装する新しいAPIがありますが、よりセマンティックで、オブジェクトが別のオブジェクトのプロトタイプチェーン上にあるかどうかを直接判断します。

定数fn = 関数() {}
const o = Object.create(fn.prototype)
console.log(fn.prototype.isPrototypeOf(o)) // 真

3.2 __proto__|prototype を変更する

まずまとめます。インスタンスを構築する際、このインスタンスの __proto__ はこの時点でコンストラクタのプロトタイプを指し、その後インスタンスは実際に __proto__ を継承します。(なぜ今回強調するかというと、コンストラクタのプロトタイプは を指すように変更される可能性があり、その変更は変更後に構築されたインスタンスにのみ影響するからです。変更前に構築されたインスタンスは、変更前のプロトタイプを引き続き使用します)

したがって、__proto__とprototypeを変更することの影響を理解できます。

1. __proto__の方向を変更する

それは自身の継承にのみ影響する

定数Dog = 関数() {}
定数 dog = 新しい Dog()
定数 d = 新しい Dog()
Dog.prototype.name = '犬'
dog.__proto__ = {
  名前: '__proto__',
}
console.log(d.name) // 犬
console.log(dog.name) // __proto__

2. __proto__のプロパティを変更する

このバンド構造に影響を与える例

定数Dog = 関数() {}
定数 dog = 新しい Dog()
定数 d = 新しい Dog()
Dog.prototype.name = '犬'
console.log(d.name) // 犬
console.log(dog.name) // 犬
Dog.プロトタイプ = {
  名前: 'after'、
}
定数 dog1 = 新しい Dog()
定数 d1 = 新しい Dog()
console.log(d1.name) // 後
console.log(dog1.name) // 後
dog1.__proto__.name = '__proto__'
// 現在の構築のインスタンスのみが影響を受けていることがわかります。このセクションには同じ Dog.prototype が含まれており、それらの __proto__ はすべてそれを指しているため、前後のインスタンスは影響を受けません。 console.log(d1.name) // __proto__
console.log(dog1.name) // __proto__
Dog.プロトタイプ = {
  名前: 'new'、
}
定数 dog2 = 新しい Dog()
定数 d2 = 新しい Dog()
console.log(d2.name) // 新しい
console.log(dog2.name) // 新しい

3. プロトタイプの方向を変更する

このバンド構造に影響を与える例

4. プロトタイプのプロパティを変更する

これは、__proto__プロパティを変更するのと同じように、このバンド構造のインスタンスに影響を与えます。

4 プロトタイプオブジェクトの変更と取得方法

4.1 変更

プロトタイプと__proto__の変更についてはすでに説明しました。

4.1.1 オブジェクトの作成

定数オブジェクト = {
  名前: 'objName',
}

定数 o = Object.create(obj)
// o.__proto__ = obj と同等ですが、`Object.create` を使用することをお勧めします。

console.log(o.name) // オブジェクト名
console.log(o.__proto__ === obj) // 真

4.1.2 オブジェクト.setPrototypeOf

定数オブジェクト = {
  名前: 'objName',
}

定数o = {}

オブジェクト.setPrototypeOf(o, obj)
// o.__proto__ = obj と同等ですが、`Object.setPrototypeOf` を使用することをお勧めします。
const proto = Object.getPrototypeOf(o)
console.log(proto === obj && proto === o.__proto__) // 真
定数obj1 = {}
o.__proto__ = obj1
const proto1 = Object.getPrototypeOf(o)
console.log(proto1 === obj1 && proto1 === o.__proto__) // 真

まとめると、Object.create はいつ使用し、Object.setPrototypeOf はいつ使用すればよいのでしょうか。まず、どちらも標準 API であり、推奨されています。オブジェクトの作成時にプロトタイプを指定する必要がある場合は、Object.create を使用します。プロトタイプ オブジェクトを動的に変更する必要がある場合は、Object.setPrototypeOf を使用します。

4.2 取得

前述のように、constructor.prototypeと__proto__を通じて取得します。

4.2.1 オブジェクト.getPrototypeOf

定数オブジェクト = {
  名前: 'objName',
}

定数o = {}

オブジェクト.setPrototypeOf(o, obj)

const proto = Object.getPrototypeOf(o)
console.log(proto === obj && proto === o.__proto__) // 真

5 js 組み込みネイティブ コンストラクター

これらのネイティブ コンストラクターのプロトタイプ プロパティは書き込み可能、​​列挙可能、および構成可能ではありません。

console.log(Object.getOwnPropertyDescriptor(オブジェクト、'プロトタイプ'))
// {
// 値: [オブジェクト: null プロトタイプ] {},
// 書き込み可能: false、
// 列挙可能: false、
// 設定可能: false
// }

5.1 js 継承の最上位レベルとは何ですか?

null、それはこの男でなければなりません、そうでなければ私たちはそれを無限に行うしかありません

すると、他のすべてのオブジェクトは Object から構築されるため、すべてのオブジェクトは Object.prototype から継承できるようになります。

定数オブジェクト = {}
const o = 新しいオブジェクト()

5.2 js 継承された第 2 級オブジェクト (関数)

上記の小さな考え方では、js オブジェクトは関数から構築されていると述べられていますが、オブジェクトも関数から構築されており、それ自体もそれ自体から構築されています。

console.log(Object.constructor === Function) // true
// これはばかげています。最初の関数はどこから来るのでしょうか?
console.log(Function.constructor === Function) // true

もう少し理解を深めましょう。おそらく、js 内で何らかの処理が行われているのでしょう。最初の Function が何もないところから作成されます。次に、この Function が Object を構築し、この Object が最初のプロトタイプ オブジェクト Object.prototype を構築し、その後、いくつかの参照関係が変更されます。

実際、最も複雑なのはオブジェクトと機能の関係です

console.log(Object.__proto__ === Function.prototype) // true
console.log(Function.constructor === Function) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.prototype.__proto__ === Object.prototype) // true

5.3 js 継承された第 3 クラス オブジェクト (その他の組み込みコンストラクター)

定数arr = [
  弦、
  配列、
  ブール、
  番号、
  日付、
  正規表現、
  エラー、
  約束、
  地図、
  セット、
  シンボル、
  プロキシ、
]
// すべては関数から構築されます

6 ユーザー定義の特別な市民コンストラクタ

ここが重要なポイントです。上記の理解に基づいて、js 継承についての私の理解について別の記事を書きます。今のところはここに穴を残しておきます。

7. 結論

この記事は、コンストラクター、プロトタイプ、__proto__ について語るインターネット上のほとんどの記事とは異なります。私の出発点は、プロパティを読み取ることができる特定の値から始めることです。js では、null と undefined を除き、他のすべての値が出発点になることができます。この出発点から、その __proto__ プロパティはそのプロトタイプ オブジェクトを記録します。これは、構築されたときのそのコンストラクターのプロトタイプ プロパティの値です。

定数a = 1
console.log(a.__proto__.constructor) // [関数: 数値]

値のプロパティの値を読み取るとき、そのプロパティ自体がある場合は、そのプロパティの値が直接返されます。そうでない場合は、その __proto__ オブジェクトに移動してそれを見つけ、最上位の null が見つかるまで再帰的に続けます。見つかった場合はその値が返され、見つからない場合は undefined が返されます。

この記事には、私が突然悟りを開いた 3 つの理解点があります。それらはすべて、長い実験期間を経て突然到達した結論です。

  1. 分析は、まず値を出発点として開始します
  2. インスタンスを構築するとき、インスタンス__proto__はその時点のコンストラクタのプロトタイプを指します。
  3. プロトタイプオブジェクトを探すときは、__proto__が基準として使用されます。

8. 最後

js ネイティブ構文プロトタイプ、__proto__、コンストラクターに関するこの記事はこれで終わりです。より関連性の高い js ネイティブ構文プロトタイプ、__proto__、コンストラクターのコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • Javascript プロトタイプ
  • JavaScript プロトタイプの詳細
  • JavaScript の isPrototypeOf 関数
  • JavaScriptはプロトタイププロパティを使用して継承操作を実装する例
  • JS 配列の次元削減の実装 Array.prototype.concat.apply([], arr)
  • 先頭と末尾のスペースを削除する js String.prototype.trim 文字の拡張
  • JavaScript __proto__ とプロトタイプ

<<:  VMware Workstation のインストールと、外部ネットワークに接続するための WIN10 オペレーティング システムのインストールのステップ バイ ステップ ガイド (非常に詳細なチュートリアル)

>>:  CSSファイルをインポートする3つの方法の詳細な説明

推薦する

nginx共有メモリの仕組みの詳細な説明

Nginx の共有メモリは、高いパフォーマンスを実現できる主な理由の 1 つであり、主にファイル キ...

ネイティブjsは9マスグリッドのドラッグアンドドロップを実現します

ネイティブJSを使用して9つの正方形のグリッドを記述し、9つのグリッドの位置をドラッグして変更する効...

Ace をベースにした Markdown エディターを共有する

エディターは 2 つのカテゴリに分かれていると思います。1 つは、即時レンダリングを実現するために左...

MySQL データベースの Binlog 使用法の概要 (必読)

MySQL データベースにとって binlog バイナリ ログがどれほど重要であるかについては詳し...

MySQL InnoDB row_id 境界オーバーフロー検証方法の手順

背景クラスメートと row_id の境界問題について話し合ったので、ここで詳しく説明します。 Inn...

CSS を使用して 3 つのステップでショッピング モールのカード クーポンを作成する

今日は618日、主要なショッピングモールはすべてプロモーション活動を行っています。今日は、次のように...

...

MySQL データベース アーキテクチャの詳細

目次1. MySQL アーキテクチャ2. ネットワーク接続層3. データベースサービス層4. 接続プ...

Docker可視化ツールPortainerの導入と中国語翻訳

#docker 検索#docker プルポーター1. イメージを取得した後、中国語パッケージをダウン...

CSS で放射状グラデーションを使用してカード効果を実現する

数日前、同僚がポイントモールプロジェクトを受け取りました。このプロジェクトには、カードやクーポンをギ...

MySQL ストアド関数の詳細な紹介

目次1. ストアド関数を作成する2. ストアド関数の呼び出し3. 保存された関数を削除する4. スト...

Vue は Axios リクエスト フロントエンドのクロスドメイン問題をどのように解決するのか

目次序文1. クロスドメインの問題はなぜ発生するのでしょうか? 2. 解決策クロスオリジンリソース共...

Linux sedコマンドの使用

1. 機能紹介sed (Stream EDitor) は、コンテンツを 1 行ずつ処理するストリーム...

ウェブページでmp3またはフラッシュプレーヤーコードを再生する

コードをコピーコードは次のとおりです。 <オブジェクト id="player1&qu...

2015-2016年に主流となるインタラクティブ体験のトレンド

5月の最も重要なインタラクティブデザイン記事!今年、Baiduのデザイナーは体験の観点から出発し、大...