13

JavaScript忍者の秘密、2013年、125ページの本では、次のように述べています。

constructor JavaScript の各オブジェクトには、オブジェクトの作成に使用されたコンストラクターを参照するという名前の暗黙的なプロパティがあります。また、プロトタイプはコンストラクターのプロパティであるため、各オブジェクトにはプロトタイプを見つける方法があります。

これは実際、私が JavaScript について聞いた中で最も欠陥のあるものの 1 つかもしれません。それはおそらく JavaScript の専門家からのものでした。本当じゃないですか

  1. [[prototype]]すべての JavaScript オブジェクトは、internalプロパティを使用して「そのプロトタイプを見つける方法を持っています」 ( ECMA-262 仕様の 32 ページのように)。Chrome と Firefox では を使用してアクセスでき、__proto__最近のバージョンの IE では を使用してアクセスできます。Object.getPrototypeOf

  2. 任意のオブジェクトはconstructor、参照先のプロトタイプ オブジェクトでプロパティを取得することにより、プロパティを取得__proto__します。constructor一部の JavaScript ライブラリまたはフレームワークではプロパティがまったく使用されないため、プロパティが正しく設定されないことさえあります 。constructorオブジェクト自体のプロパティではなく、プロトタイプ オブジェクトのプロパティです。

(Chrome の開発者ツールで見られるように):

> function Foo() {}
undefined

> var foo = new Foo()
undefined

> foo.hasOwnProperty("constructor")
false

> foo.__proto__.hasOwnProperty("constructor")
true

> foo.__proto__.constructor === Foo
true

上記の(1)と(2)は本当ですか?constructor引用されたテキストのように、JavaScriptの「暗黙のプロパティ」とは何ですか? [[prototype]]内部プロパティであるような何かを意味しようとしていますか? しかし、もっと重要なことは、上記の (1) と (2) が真実であり、引用されたテキストが言っていることではないかどうかを知りたいということです。

4

3 に答える 3

2

引用されたテキストは非常に正確で、このメカニズムを非常に簡単に説明しています。

「JavaScript の各オブジェクトには、オブジェクトの作成に使用されたコンストラクターを参照するコンストラクターという名前の暗黙のプロパティがあります。」

@Mathleticsが指摘したように、これは絶対に真実です:

foo.constructor === Foo // true

...「プロトタイプはコンストラクターのプロパティであるため、各オブジェクトにはそのプロトタイプを見つける方法があります。」

これも読むと分かりやすい。コンストラクターからプロトタイプを取得することは、インスタンスがそのプロトタイプを見つけるための有効な方法です。

foo.constructor.prototype // Foo {}

また、

foo.constructor.prototype === foo.__proto__ // true

この本で説明されている方法が、最も適切な方法だと思います。「__proto__」プロパティは、理由により、両側に 2 つのアンダースコアが付いた名前になっています。ご指摘のとおり、これは内部プロパティであり、二重下線は内部プロパティの命名規則として広く使用されています。hasOwnPropertyでは「表示」されません。特に内部プロパティであるという理由ではなく、オブジェクト自体に直接設定されていないためです。これはhasOwnProperyが何を意味するかをより明確に説明するかもしれません:

foo.a = 4;
foo.a; // 4
foo.hasOwnProperty("a"); // true
foo.constructor.prototype.b = 5;
foo.b; // 5
foo.hasOwnProperty("b"); // false
于 2013-06-25T09:02:33.257 に答える
1

ジョン・レシグは間違っている

はい、jQuery の作成者も間違いを犯す可能性があります。これは彼が述べたことです:

constructorJavaScript の各オブジェクトには、オブジェクトの作成に使用されたコンストラクターを参照するという名前の暗黙的なプロパティがあります。また、プロトタイプはコンストラクターのプロパティであるため、各オブジェクトにはプロトタイプを見つける方法があります。

彼の発言が誤りである理由は次のとおりです。

  1. すべてのオブジェクトにプロトタイプがあるわけではありません。したがって、これらのオブジェクトには暗黙的なプロパティもありません。
  2. プロトタイプを持つすべてのオブジェクトがコンストラクター関数によって作成されるわけではありません。ただし、これらのオブジェクトはコンストラクター関数によって暗黙的に作成されるため、これにはオブジェクト、配列、および正規表現のリテラルと関数は含まれません。
  3. プロトタイプを持ち、コンストラクター関数によって作成されるすべてのオブジェクトに、 という名前の暗黙のプロパティがあるわけではありませんconstructor
  4. プロトタイプを持ち、コンストラクターによって作成され、名前付きの暗黙的なプロパティを持つすべてのオブジェクトが、constructorそのオブジェクトを作成したコンストラクター関数を指すプロパティを持っているわけではありません。
  5. constructorプロトタイプを持ち、コンストラクターによって作成され、そのオブジェクトを作成したコンストラクター関数を指すという名前の暗黙的なプロパティを持ち、prototypeそのコンストラクター関数で呼び出されるプロパティを持つすべてのオブジェクトがあるわけではありません。
  6. constructorプロトタイプを持ち、コンストラクターによって作成され、そのオブジェクトを作成したコンストラクター関数を指すという名前の暗黙的なプロパティを持ち、prototypeそのコンストラクター関数で呼び出されるプロパティを持ち、そのプロパティがそのオブジェクトのプロトタイプを指すわけではありません。

これらのステートメントを例で証明して、John Resig のステートメントが誤りであることを証明しましょう。これらのステートメントはすべて「すべてのオブジェクトではない」という主張で始まっているため、John Resig のステートメントが誤りであることを証明するには、各ステートメントの 1 つの例を見つけるだけで済みます。

ステートメント 1 の証明

このObject.createメソッドを使用して、新しいオブジェクトを作成し、その内部[[prototype]]プロパティを設定できます。したがって、プロトタイプを持たないオブジェクトを作成するために使用できます。

var o = Object.create(null); // o has no prototype

上記の例のオブジェクトにはプロトタイプがなく、内部[[prototype]]プロパティが に設定されていnullます。したがって、暗黙的なプロパティもありません。

ステートメント 2 の証明

次のようにp、オブジェクトから継承する別のオブジェクトを作成しましょう。o

var p = Object.create(o); // the prototype of p is o

したがって、オブジェクトpにはプロトタイプがありますが、コンストラクターによって作成されたものではありません。

ステートメント 3 の証明

よし、代わりにコンストラクターからオブジェクトを作成しましょうp(実際、これはまさにObject.create関数の実装方法です)。

function F() {}  // F is a constructor
F.prototype = o; // objects constructed by F inherit from o
var p = new F;   // p is an object which is constructed by F

ここで、オブジェクトpはコンストラクターによって作成されFます。ただし、という名前の暗黙的なプロパティはありませんconstructor

ステートメント 4 の証明

変数oにオブジェクト リテラルが割り当てられ、prototypeコンストラクターのプロパティとして使用された場合はどうなるFでしょうか。

var o = {};      // object literals inherit from Object.prototype
function F() {}  // F is a constructor
F.prototype = o; // objects constructed by F inherit from o
var p = new F;   // p is an object which is constructed by F

これで、オブジェクトpには という名前の暗黙のプロパティがありますが、それはではなく をconstructor指しています。したがって、の代わりに を指します。ObjectFp.constructor.prototypeObject.prototypeo

陳述書の証明 5

おそらく問題は継承だと思いますか?よし、継承を完全にやめましょう。ゼロから始める:

var p = new F;      // p is constructed by F, it inherits from F.prototype
delete F.prototype; // devious isn't it? I love being naughty
function F() {}     // declarations are hoisted

これで、オブジェクトpは から継承され、それ自体を指すF.prototypeという名前の暗黙のプロパティがあります。ただし、 からプロパティを削除したため、 viaのプロトタイプにアクセスできません(現在は が返されます)。constructorFprototypeFpp.constructor.prototypeundefined

陳述の証明 6

最後の例を少し変更してみましょう。削除する代わりに、別のF.prototypeものに設定します。例えば:

var o = {};      // object literals inherit from Object.prototype
var p = new F;   // p is constructed by F, it inherits from F.prototype
F.prototype = o; // oops, what will happen now?
function F() {}  // declarations are hoisted

オブジェクトpは から継承し、それ自体を指すF.prototypeという名前の暗黙のプロパティを持ちます。ただし、アクセス時にに設定しているため、元の の代わりに取得します。constructorFF.prototypeop.constructor.prototypeoF.prototype

結論

ご覧のとおり、John Resig の発言は完全に誤りです。これを証明するのに 6 つの例は必要ありませんでした。どれか1つの例で十分です。しかし、私は彼の発言がどれほど間違っているかを示したかった. したがって、私は彼の発言を反証する考えられるすべての例を書きました。

JavaScript はプロトタイプのオブジェクト指向プログラミング言語であり、オブジェクトが他のオブジェクトから継承されることを意味します。コンストラクターは、オブジェクトを作成するために厳密に必要というわけではありません。ただし、残念ながらそれが JavaScript でプロトタイプの継承が機能する方法であるため、それらは過度に重要視されています。

プロトタイプ継承のコンストラクター パターンは、しばしば混乱を招き、誤解を招きます。さらに、コンストラクターを使用しないプロトタイプ継承の真の方法 (プロトタイプ継承のプロトタイプ パターン) を隠します。詳細については、次の回答をお読みください: https://stackoverflow.com/a/17008403/783743

于 2013-06-25T12:14:36.507 に答える
0

どうやってそれを学んだか、何が観察できるか

すべてのオブジェクトには__proto__プロパティがあり、すべての関数には追加のprototypeプロパティがあります。それprototype自体がオブジェクトでありconstructor、元の関数を指すというプロパティがあります。

function C(){}
console.log(C.hasOwnProperty("prototype")); //true
console.log(C.prototype.hasOwnProperty("constructor")); //true
console.log(C.prototype.constructor === C); //true

オブジェクトが作成されると、そのプロパティはそのコンストラクタ関数__proto__のプロパティを指すように設定されます。prototype

var c = new C();
console.log(c.__proto__ === C.prototype) //true

何が何であるかについてのすべての情報は、そのプロパティcから知っていますか。これは、プロパティを手動で__proto__変更するだけで確認できます。__proto__

function D(){}
c.__proto__ = D.prototype;
console.log(c.constructor === D) //true
console.log(c instanceof D) //true

したがって、これらすべてを考慮すると、すべてが派生するコンストラクター プロパティがあるとは信じがたいでしょう。__constr__しかし、プロトタイプが要求されたときに、アクセスできないがアクセスされる内部コンストラクター プロパティ (つまり ) にはまだ存在する可能性があります。少なくとも可能性はあると思います。

私を少し混乱させることの1つは、これです:

console.log(c.hasOwnProperty("__proto__")) //false

私の推測では__proto__、この方法では把握できないだけですhasOwnPropertyが、他の誰かがこれに光を当てることができるかもしれません。

フィドル

ECMAは何を言いますか

ECMA 仕様からの抜粋もいくつかあります。

すべてのオブジェクトには、[[Prototype]] という内部プロパティがあります。このプロパティの値は null またはオブジェクトであり、継承の実装に使用されます。ネイティブ オブジェクトが [[Prototype]] としてホスト オブジェクトを持つことができるかどうかは、実装に依存します。すべての [[Prototype]] チェーンは有限の長さでなければなりません (つまり、任意のオブジェクトから開始して、[[Prototype]] 内部プロパティに再帰的にアクセスすると、最終的に null 値になる必要があります)。[[Prototype]] オブジェクトの名前付きデータ プロパティは、get アクセスのために継承されます (子オブジェクトのプロパティとして表示されます) が、put アクセスのためには継承されません。名前付きアクセサー プロパティは、get アクセスと put アクセスの両方で継承されます。

と:

8.12.2 [[GetProperty]] (P)

O の [[GetProperty]] 内部メソッドがプロパティ名 P で呼び出されると、次の手順が実行されます。

  1. プロパティ名 P で O の [[GetOwnProperty]] 内部メソッドを呼び出した結果を prop とする。

  2. prop が未定義でない場合は、prop を返します。

  3. O の [[Prototype]] 内部プロパティの値を proto とします。

  4. proto が null の場合、undefined を返します。

  5. 引数 P で proto の [[GetProperty]] 内部メソッドを呼び出した結果を返します。

私の結論

ECMA は、内部プロトタイプ プロパティがあることを明確に述べていますが、JavaScript が内部コンストラクタ プロパティに最初にアクセスして、既に存在するものにアクセスする場合、実際には意味がありません。また、テストできるものはすべて、コンストラクターがプロトタイプのプロパティであり、その逆ではないという考えを示しています。

したがって、これが機能する方法であると言っても過言ではありません。

Jon Resig からの引用も読み直しました。the prototype is a property of the constructor彼が意味しているのは、直接アクセスでき、すべての関数が持つプロトタイプ プロパティだと思います。彼はここでもあまり具体的ではありません。彼は単純な説明が欲しかっただけで、すぐに人々を混乱させたくなかったのだと思います。

于 2013-06-21T14:22:43.823 に答える