web-dev-qa-db-ja.com

JavaScriptで「Object instanceof Function」と「Function instanceof Object」の両方がtrueを返すのはなぜですか?

JavaScriptで両方を実行する理由Object instanceof FunctionおよびFunction instanceof Object戻るtrue

Safari WebInspectorで試してみました。

37
dinghao

私が理解するのにしばらく時間がかかりましたが、費やす時間は本当に価値があります。まず、instanceofの動作を見てみましょう。

[〜#〜] mdn [〜#〜] からの引用

instanceof演算子は、オブジェクトのプロトタイプチェーンにコンストラクタのprototypeプロパティがあるかどうかをテストします。

_[instanceof]_

instanceof がECMA 5.1仕様でどのように定義されているかを見てみましょう。

プロダクション_RelationalExpression: RelationalExpression instanceof ShiftExpression_は次のように評価されます。

  1. lrefRelationalExpressionの評価結果とする。
  2. lvalGetValue(lref)とします。
  3. rrefShiftExpressionの評価結果とする。
  4. rvalGetValue(rref)とします。
  5. Type(rval)がObjectでない場合は、TypeError例外をスローします。
  6. rvalに_[[HasInstance]]_内部メソッドがない場合は、TypeError例外をスローします。
  7. rvalの_[[HasInstance]]_内部メソッドを引数lvalで呼び出した結果を返します。

最初に左側と右側の式が評価され(GetValue)、次に右側の結果が_[[HasInstance]]_内部メソッドを持つオブジェクトになるはずです。すべてのオブジェクトに_[[HasInstance]]_内部メソッドがあるわけではなく、関数があります。たとえば、次は失敗します

_console.log(Object instanceof {});
# TypeError: Expecting a function in instanceof check, but got #<Object>
_

_[[HasInstance]]_

ここで、ECMA 5.1仕様で _[[HasInstance]]_ がどのように定義されているかを見てみましょう。

Fが関数オブジェクトであると想定します。

Fの_[[HasInstance]]_内部メソッドが値Vで呼び出されると、次の手順が実行されます。

  1. Vがオブジェクトでない場合は、falseを返します。
  2. Oを、Fの_[[Get]]_内部メソッドをプロパティ名_"prototype"_で呼び出した結果とします。
  3. Type(O)がObjectでない場合は、TypeError例外をスローします。
  4. 繰り返す
    1. VVの_[[Prototype]]_内部プロパティの値とします。
    2. Vnullの場合、falseを返します。
    3. OVが同じオブジェクトを参照している場合は、trueを返します。

とても簡単です。 prototypeFプロパティを取得し、OまたはnullprototypeFと同じになるまで、Oの_[[Prototype]]_内部プロパティと比較します。

_[[prototype]]_内部プロパティ

まず _[[prototype]]_内部プロパティ とは何かを見てみましょう

すべてのオブジェクトには、_[[Prototype]]_という内部プロパティがあります。このプロパティの値はnullまたはオブジェクトであり、継承の実装に使用されます。ネイティブオブジェクトがその_[[Prototype]]_としてHostオブジェクトを持つことができるかどうかは、実装によって異なります。すべての_[[Prototype]]_チェーンは有限の長さでなければなりません(つまり、任意のオブジェクトから開始して、_[[Prototype]]_内部プロパティに再帰的にアクセスすると、最終的にnull値につながる必要があります)。

注:_Object.getPrototypeOf_ 関数を使用して、この内部プロパティを取得できます。

prototypeプロパティ

_[[HasInstance]]_は、prototypeオブジェクトに固有の Function と呼ばれる別のプロパティについても説明します。

prototypeプロパティの値は、新しく作成されたオブジェクトのコンストラクタとしてFunctionオブジェクトが呼び出される前に、新しく作成されたオブジェクトの_[[Prototype]]_内部プロパティを初期化するために使用されます。

つまり、関数オブジェクトがコンストラクターとして使用されると、新しいオブジェクトが作成され、新しい__objectはこのprototypeプロパティで初期化された内部_[[Prototype]]_を持ちます。例えば、

_function Test() {}
Test.prototype.print = console.log;
console.log(Object.getPrototypeOf(new Test()) === Test.prototype);
# true
_

実際の問題

それでは、実際の質問に戻りましょう。最初のケースを見てみましょう

_console.log(Object instanceof Function);
# true
_

最初に_Function.prototype_をフェッチし、そのオブジェクトがObjectのプロトタイプ階層にあるかどうかを調べます。それがどうなるか見てみましょう

_console.log(Function.prototype);
# [Function: Empty]
console.log(Object.getPrototypeOf(Object));
# [Function: Empty]
console.log(Object.getPrototypeOf(Object) === Function.prototype);
# true
_

_Function.prototype_はObjectの内部プロパティ_[[Prototype]]_と一致するため、trueを返します。

では、2つ目のケースを見てみましょう

_console.log(Function instanceof Object);
# true
console.log(Object.prototype);
# {}
console.log(Object.getPrototypeOf(Function));
# [Function: Empty]
console.log(Object.getPrototypeOf(Function) === Object.prototype);
# false
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function)));
# {}
Object.getPrototypeOf(Object.getPrototypeOf(Function)) === Object.prototype
# true
_

ここでは、まず_Object.prototype_、つまり_{}_を取得します。現在、同じオブジェクト_{}_がFunctionのプロトタイプチェーンに存在するかどうかを検索しています。 Functionの直接の親は空の関数です。

_console.log(Object.getPrototypeOf(Function));
# [Function: Empty]
_

_Object.prototype_とは異なります

_console.log(Object.getPrototypeOf(Function) === Object.prototype);
# false
_

しかし、_[[HasInstance]]_アルゴリズムはそこで止まりません。それは繰り返されて、もう1レベル上がります

_console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function)));
# {}
_

これは_Object.prototype_と同じです。これがtrueを返す理由です。

38
thefourtheye

から [〜#〜] mdn [〜#〜]

Instanceof演算子は、オブジェクトのプロトタイプチェーンにコンストラクタのプロトタイププロパティがあるかどうかをテストします。

基本的に、ObjectObjectのインスタンスではなく、コンストラクタ自体)がFunction.constructorのインスタンスとしてプロトタイプチェーンのどこかにあるかどうかをチェックしています。

本当に:

> Function.__proto__.__proto__ === Object.prototype
true
> Object.__proto__ === Function.prototype
true

これは、Object instanceof Functionとその逆の理由を説明しています。

14

すべてのオブジェクトには、[[Prototype]]という内部プロパティがあります。このプロパティの値はnullまたはオブジェクトであり、継承の実装に使用されます。オブジェクトのキーを検索しようとしてそれが見つからない場合、JavaScriptはプロトタイプチェーンでキーを検索します。

Functionコンストラクターは、新しいFunctionオブジェクト(Functionコンストラクターのインスタンス)を作成します。 prototypeプロパティはFunctionオブジェクトに固有です。 Functionコンストラクター自体は、Functionオブジェクトです(Functionコンストラクターのインスタンス)。

Functionオブジェクトがコンストラクターとして使用される場合、新しいオブジェクトが作成され、新しいオブジェクトの[[Prototype]]はコンストラクターのプロトタイププロパティで初期化されます。

function Dog () {}
var myCrazyDog = new Dog();
myCrazyDog.__proto__ === Dog.prototype // true

言語仕様では、すべてのオブジェクトはObjectコンストラクターのインスタンスであり、すべての関数はFunctionコンストラクターのインスタンスです。

Object instanceof Function is true Objectは関数なので、Functionのインスタンスです(ObjectはFunctionオブジェクト-Functionコンストラクターのインスタンスです)。オブジェクトはFunction.prototypeを継承します。

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

Object instanceof Objectはtrue ObjectはFunction.prototypeを継承するため。 Function.prototypeはオブジェクトなので、Object.prototypeから継承します。 ObjectのFunctionインスタンスはtrue FunctionはFunction.prototypeを継承するため。 Function.prototypeはオブジェクトなので、Object.prototypeから継承します。プロトタイプチェーンは次のようになります。

Object ---> Function.prototype ---> Object.prototype ---> null
Function ---> Function.prototype ---> Object.prototype ---> null

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

Function instanceof Function is true。関数はそれ自体のインスタンスです(当然、関数なので、関数のインスタンスです)。プロトタイプチェーンは次のようになります。

Function ---> Function.prototype ---> Object.prototype ---> null

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

したがって、Function()およびObject()コンストラクターは関数であることを覚えておいてください。これらは関数であるため、Function()コンストラクターのインスタンスであり、Function.prototypeから継承されます。 Function.prototypeはオブジェクトであるため、Function.prototypeはObjectのインスタンスであり、Object.prototypeから継承されます。

console.log(Object instance of Function)                    // true
console.log(Function instance of Function)                  // true
console.log(Function.prototype instanceof Object);          // true
6
misterd89

質問の混乱の原因は、JavaScript(ECMAScript)の関数*の本質的な二重の性質にあります。

Jsの関数は、通常の関数であると同時にオブジェクトでもあります。それらをアルゴリズムジキル博士とハイド氏と考えてください。それらは外側ではオブジェクトのように見えますが、内側では、すべての奇妙な点を備えた古き良きjs関数であるか、または逆になっている可能性があります。

JavaScriptは本当にトリッキーなビジネスです:)

質問に戻り、MDNに表示される構文を借ります。

object instanceof constructor

コードの最初のステートメントに適用します。

Object instanceof Function

ここにObjectがあります。これは、オブジェクト初期化子として使用されるコンストラクター関数ですが、関数はjsで二重の寿命をもたらすため、それにオブジェクト固有のプロップとメソッドがアタッチされ、効果的にオブジェクトをレンダリングします。

したがって、ステートメントの最初の条件が満たされました。他の条件またはオペランドを調査するために残ります。

お気づきかもしれませんが、Functionも関数コンストラクターですが、この特定のステートメントの実行中は、他のオブジェクト側は関係ありません。

つまり、「オブジェクト」と「コンストラクター」という構文条件の両方が満たされます。次に、それらの遺伝的関係と、それらの間に関係があるかどうかを調査します。

Objectはそれ自体が機能する関数なので、内部プロトタイププロップがFunction.prototypeオブジェクト参照を指していると仮定すると、js [〜#〜] all [〜#〜]関数継承その同じ場所Function.prototypeから小道具とメソッドを継承します。

trueは間違いなく[〜#〜] only [〜#〜]instanceof演算子によって実行されるこの比較の期待される結果です。

その他の場合:

Function instanceof Object

既に確立しているので、jsの関数にはオブジェクト側もあります。彼らがObject.prototypeからファンシーなオブジェクト固有のtoysを取得したことは理にかなっており、したがって、それらはインスタンスを構成しますオブジェクトコンストラクタ。

私の説明と寓話で混乱を増やさなかったことを願っています。 :)

*:jsで二重の生活を送る関数だけではありません。 jsのほとんどすべてのデータ型には、オブジェクトにダークサイドがあり、手間をかけずに操作や操作を完了できます。

3
Mr. X

最も悪いプロパティは、実際にはFunctionがそれ自体のインスタンスであることです。 Function instanceof Functionはtrueを返します。

http://web.archive.org/web/20140205182624/にあるKannanのThe驚くべきエレガントなJavascriptタイプモデル)でうまく説明されていますhttp://vijayan.ca/blog/2012/02/21/javascript-type-model

説明の最後に引用:

はい、これはFunctionがそれ自体のインスタンスであることを意味します(当然のことながら、これは関数であり、したがってFunctionのインスタンスです)。これは、長い間、私たち全員が長い間、故意かどうかに関わらず、扱ってきたものです。すべてのコンストラクター関数は通常の関数であり、したがって、Functionのインスタンスであり、Function自体は、他の関数を構築するためのコンストラクター関数ですFunctionのインスタンス。

enter image description here

1
Aurelien Ribon

非常に簡単な説明、すべての回答とは異なります

ObjectとFunctionはどちらもコンストラクター(両方の型で、 "Functionオブジェクト"を返す)で、どちらもFunction Constructorから作成されます。これら両方の__proto__プロパティは、「Function.prototype」オブジェクトを指します。

クイック説明:オブジェクトの__proto__プロパティ(Personタイプであるp1など)は、コンストラクターのプロトタイプ(Person.prototypeなど)を指します。プロトタイプチェーンの__proto__は、オブジェクト「Object.prototype」を指します。

印刷の詳細を読む前にCHROME CONSOLE console.dir(Object)、console.dir(Function))

KEEP IN MIND、関数コンストラクタ、さらにオブジェクトを使用すると、.prototypeプロパティと__proto__プロパティの両方が表示されます。すべてのインスタンスオブジェクト(p1など)では、__ proto__プロパティのみが見つかります。 __proto__は非表示になっているため、__ proto__は非表示プロパティ[[Prototye]]のアクセサーであり、取得する最良の方法はObject.getPrototypeOf(p1)です。

(p1 instanceof Person)ここの演算子は、コンストラクターPersonのプロトタイプがオブジェクトp1のプロトタイプチェーンにあるかどうかをチェックします。最初の値はインスタンスオブジェクト(p1)であり、2番目の値はコンストラクタ(Person)であることに注意してください。

=> Function instanceof Objectを分析しましょう。

関数オブジェクトの__proto __.__ proto__はObject.prototypeを指しているため、true

=> Object instanceof Functionを分析しましょう。

オブジェクトオブジェクトの__proto__はFunction.prototypeを指すため、true

お役に立てれば。

0
Sumer