JavaScriptで両方を実行する理由Object instanceof Function
およびFunction instanceof Object
戻るtrue
?
Safari WebInspectorで試してみました。
私が理解するのにしばらく時間がかかりましたが、費やす時間は本当に価値があります。まず、instanceof
の動作を見てみましょう。
[〜#〜] mdn [〜#〜] からの引用
instanceof
演算子は、オブジェクトのプロトタイプチェーンにコンストラクタのprototype
プロパティがあるかどうかをテストします。
[instanceof]
_instanceof
がECMA 5.1仕様でどのように定義されているかを見てみましょう。
プロダクション_
RelationalExpression: RelationalExpression instanceof ShiftExpression
_は次のように評価されます。
lref
をRelationalExpression
の評価結果とする。lval
をGetValue(lref)
とします。rref
をShiftExpression
の評価結果とする。rval
をGetValue(rref)
とします。Type(rval)
がObjectでない場合は、TypeError
例外をスローします。rval
に_[[HasInstance]]
_内部メソッドがない場合は、TypeError
例外をスローします。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
で呼び出されると、次の手順が実行されます。
V
がオブジェクトでない場合は、false
を返します。O
を、F
の_[[Get]]
_内部メソッドをプロパティ名_"prototype"
_で呼び出した結果とします。Type(O)
がObjectでない場合は、TypeError
例外をスローします。- 繰り返す
V
をV
の_[[Prototype]]
_内部プロパティの値とします。V
がnull
の場合、false
を返します。O
とV
が同じオブジェクトを参照している場合は、true
を返します。
とても簡単です。 prototype
のF
プロパティを取得し、O
またはnull
のprototype
がF
と同じになるまで、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
を返す理由です。
から [〜#〜] mdn [〜#〜] :
Instanceof演算子は、オブジェクトのプロトタイプチェーンにコンストラクタのプロトタイププロパティがあるかどうかをテストします。
基本的に、Object
(Object
のインスタンスではなく、コンストラクタ自体)がFunction.constructor
のインスタンスとしてプロトタイプチェーンのどこかにあるかどうかをチェックしています。
本当に:
> Function.__proto__.__proto__ === Object.prototype
true
> Object.__proto__ === Function.prototype
true
これは、Object instanceof Function
とその逆の理由を説明しています。
すべてのオブジェクトには、[[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
質問の混乱の原因は、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のほとんどすべてのデータ型には、オブジェクトにダークサイドがあり、手間をかけずに操作や操作を完了できます。
最も悪いプロパティは、実際にはFunctionがそれ自体のインスタンスであることです。 Function instanceof Function
はtrueを返します。
説明の最後に引用:
はい、これはFunctionがそれ自体のインスタンスであることを意味します(当然のことながら、これは関数であり、したがってFunctionのインスタンスです)。これは、長い間、私たち全員が長い間、故意かどうかに関わらず、扱ってきたものです。すべてのコンストラクター関数は通常の関数であり、したがって、Functionのインスタンスであり、Function自体は、他の関数を構築するためのコンストラクター関数ですFunctionのインスタンス。
非常に簡単な説明、すべての回答とは異なります
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
お役に立てれば。