Function, Object, Array 與instanceof 連用時的問題

一. 不管是Function, Object, Array這些都是構造函數。

Function
ƒ Function() { [native code] }

Function.prototype
ƒ () { [native code] }

Function.__proto__
ƒ () { [native code] }
Object
ƒ Object() { [native code] }
Object.prototype
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ,}
Object.__proto__
ƒ () { [native code] }

console.log(Object.__proto__.__proto__)
VM884:1 {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ

Function.prototype.__proto__ === Object.prototype  // true

Function.prototype.__proto__ === Object.__proto__.__proto__ // true;

Function.prototype.__proto__
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ,}


Object.__proto__.__proto__
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ,}constructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()


Object.__proto__.__proto__.__proto__
null

使用instanceof 時 左邊的對象裏的__proto__對象包含右邊這個函數原型的話就是true;

Object instanceof Function 這裏function.prototype (函數原型) 的值爲 f() { native code } ,而Object.__proto__(對象原型)的值現在也爲 f () { natvie code } ;

也就是說f () { native code } 這個方法對象是創建 Object,Function, Array, 包括 RegExp, String Boolean 這些原始對象之父。 用new生成的對象。

而且 f () { natvie code } .__proto__ 指向了 { } 這個。 而Object.prototype (對象構造函數的原型) 直接指向了 { } 這個。

所有會有 Function instanceof Object // true ------> 拆解。 Function.__proto__.__proto__ === Object.prototype


instanceof判斷數組類型

  1. typeof用以獲取一個變量的類型,typeof一般只能返回如下幾個結果:number,boolean,string,function,object,undefined。我們可以使用typeof來獲取一個變量是否存在,如if(typeof a!="undefined"){},而不要去使用if(a)因爲如果a不存在(未聲明)則會出錯,對於Array,Null等特殊對象使用typeof一律返回object,這正是typeof的侷限性。

  2. 如果我們希望獲取一個對象是否是數組,或判斷某個變量是否是某個對象的實例則要選擇使用instanceofinstanceof用於判斷一個變量是否某個對象的實例,如var a=new Array();alert(a instanceof Array);會返回true,同時alert(a instanceof Object)也會返回true;這是因爲Arrayobject的子類。再如:function test(){};var a=new test();alert(a instanceof test)會返回true


js中的instanceof運算符

instanceof運算符用來判斷一個構造函數的prototype屬性所指向的對象是否存在另外一個要檢測對象的原型鏈上

語法

obj instanceof Object;//true 實例obj在不在Object構造函數中

描述

instanceof 運算符用來檢測 constructor.prototype 是否存在於參數 object 的原型鏈上。

實例

重點內容instanceof的普通的用法,obj instanceof Object 檢測Object.prototype是否存在於參數obj的原型鏈上。

Person的原型在p的原型鏈中

function Person(){};
var p =new Person();
console.log(p instanceof Person);//true

繼承中判斷實例是否屬於它的父類

StudentPerson都在s的原型鏈中

function Person(){};
function Student(){};
var p =new Person();
Student.prototype=p;//繼承原型
var s=new Student();
console.log(s instanceof Student);//true
console.log(s instanceof Person);//true

複雜用法

這裏的案例要有熟練的原型鏈的認識才能理解

function Person() {}
console.log(Object instanceof Object); //true
//第一個Object的原型鏈:Object=>
//Object.proto => Function.prototype=>Function.prototype.proto=>Object.prototype
//第二個Object的原型:Object=> Object.prototype

console.log(Function instanceof Function); //true
//第一個Function的原型鏈:Function=>Function.proto => Function.prototype
//第二個Function的原型:Function=>Function.prototype

console.log(Function instanceof Object); //true
//Function=>
//Function.proto=>Function.prototype=>Function.prototype.proto=>Object.prototype
//Object => Object.prototype

console.log(Person instanceof Function); //true
//Person=>Person.proto=>Function.prototype
//Function=>Function.prototype

console.log(String instanceof String); //false
//第一個String的原型鏈:String=>
//String.proto=>Function.prototype=>Function.prototype.proto=>Object.prototype
//第二個String的原型鏈:String=>String.prototype

console.log(Boolean instanceof Boolean); //false
//第一個Boolean的原型鏈:Boolean=>
//Boolean.proto=>Function.prototype=>Function.prototype.proto=>Object.prototype
//第二個Boolean的原型鏈:Boolean=>Boolean.prototype

console.log(Person instanceof Person); //false
//第一個Person的原型鏈:Person=>
//Person.proto=>Function.prototype=>Function.prototype.proto=>Object.prototype
//第二個Person的原型鏈:Person=>Person.prototype

總結

對應上述規範做個函數模擬A instanceof B

function _instanceof(A, B) {
var O = B.prototype;// 取B的顯示原型
A = A.proto;// 取A的隱式原型
while (true) {
//Object.prototype.proto === null
if (A === null)
return false;
if (O === A)// 這裏重點:當 O 嚴格等於 A 時,返回 true
return true;
A = A.proto;
}
}

Object instanceof FunctionFunction instanceof Object

Object, Function, Array等等這些都被稱作是構造“函數”,他們都是函數。而所有的函數都是構造函數Function的實例。從原型鏈機制的的角度來說,那就是說所有的函數都能通過原型鏈找到創建他們的Function構造函數的構造原型Function.protorype對象,所以:

alert(Object instanceof Function);// return true 與此同時,又因爲Function.prototype是一個對象,所以他的構造函數是Object. 從原型鏈機制的的角度來說,那就是說所有的函數都能通過原型鏈找到創建他們的Object構造函數的構造原型Object.prototype對象,所以: alert(Function instanceof Object);// return true 有趣的是根據我們通過原型鏈機制對instanceof進行的分析,我們不難得出一個結論:Function instanceof Function 依然返回true, 原理是一樣的

  1. Function是構造函數,所以它是函數對象
  2. 函數對象都是由Function構造函數創建而來的,原型鏈機制解釋爲:函數對象的原型鏈中存在Function.prototype
  3. instanceof查找原型鏈中的每一個節點,如果Function.prototype的構造函數Function的原型鏈中被查到,返回true

因此下面代碼依然返回true

alert(Function instanceof Function);// still true

結論

  1. JavaScript語言中,一切的一切都是對象,它們全部繼承自Object.
    或者說所有對象的原型鏈的根節點都是Object.prototype
  2. 理解原型鏈機制在JavaScript中式如何工作的是非常重要的。掌握了它,不管一個對象多麼複雜,你總能夠輕而易舉地將它攻破。

Object instanceof Function 還是 Function instance of Object,是真是假,一一道來

如今的JavaScript再也不是以前被當做玩具的在網頁上運行的花哨的腳本了。JavaScript已經逐漸標準化,作爲一門真正的編程語言廣泛地應用在Web開發上。因此,越來越多的人開始重新認識這門腳本語言,並在不斷地探索關於JavaScript核心思想和實現原理,過程中遇到了一些非常混淆的問題。本文着重解釋一個比較常見但是非常容易使開發人員或者是初學JavaScript的人非常混淆的問題,那就是兩個核心構造函數ObjectFunction,他們之間到底有什麼關係?爲何instanceof運算符的返回結果會讓你感到混淆?本文將爲你一一道來。不過在這之前,我們需要先了解一些JavaScript中的概念和基本的運行機制。

JavaScript的對象體系結構

其實在JavaScript語言中,整個核心的體系結構都圍繞着兩個構造函數ObjectFunction來構建的。我將引用來自mollypages.org的一張JavaScript對象體系結構圖來說明。
在這裏插入圖片描述
圖1-1 The JavaScript Object Hierarchy

instanceof 運算符

instanceof是一個二元運算符,如:A instanceof B. 其中,A必須是一個合法的JavaScript對象,B必須是一個合法的JavaScript函數 (function). 判斷過程如下:

如果函數B在對象A的原型鏈 (prototype chain) 中被發現,那麼instanceof操作符將返回true,否則返回false.

例如下面的代碼會返回true.

// return true if specified function is found
// in the object's prototype chain as a constructor.
alert({} instanceof Object);

JavaScript中的原型鏈(prototype chain)機制

這裏簡單概括一下,因爲這個話題需要很大篇幅去討論,本文只是引用了這個概念,重點並非詳細討論該機制。

JavaScript中的原型(prototype)是和函數(function)緊密相連的,因爲每個函數默認都會有一個屬性叫prototype, 每一個通過函數和new操作符生成的對象都具有一個屬性__proto__, 這個屬性保存了創建它的構造函數的prototype屬性的引用。這個__proto__對象就是實現原型鏈的核心對象。JavaScript是一門面向對象的編程語言,它的繼承特性其實就是通過原型鏈機制來實現的。同時,instanceof運算符也需要在原型鏈的支持。我們舉例說明:

// create a custom constructor Foo
function Foo() {
}
// create an insatnce of Foo
var foo = new Foo();

// foo is an instance of Foo
alert(foo instanceof Foo);// true 
// foo is also an instance of Object because
// Foo.prototype is an instance of Object.
// the interpreter will find the constructor
// through the prototype chain.
alert(foo instanceof Object);// true

// Prototype chain of the object foo
//
//      __proto__                   __proto__                     __proto__
// foo -----------> Foo.prototype -----------> Object.prototype -----------> null

// But foo is not an instance of Function, because
// we could not find Function.prototype in foo's
// prototype chain.
alert(foo instanceof Function);// false

// However, its constructor Foo is an instance of 
// Function.
alert(Foo instanceof Function);// true
// it's also an instance of Object 
alert(Foo instanceof Object);// true

// Prototype chain of the constructor Foo
//
//      __proto__                        __proto__                      __proto__
// Foo -----------> Function.prototype -----------> Object.prototype -----------> null

從上面的代碼來分析,我們不難得出這樣一個結論:任何對象的原型鏈最後都能追溯到Object.prototype. 這也就是我們爲什麼說JavaScript中所有的對象都繼承自Object的原因了。

爲何Object instanceof FunctionFunction instanceof Object都返回true

Object, Function, Array等等這些都被稱作是構造“函數”,他們都是函數。而所有的函數都是構造函數Function的實例。從原型鏈機制的的角度來說,那就是說所有的函數都能通過原型鏈找到創建他們的Function構造函數的構造原型Function.protorype對象,所以:

alert(Object instanceof Function);// return true

與此同時,又因爲Function.prototype是一個對象,所以他的構造函數是Object. 從原型鏈機制的的角度來說,那就是說所有的函數都能通過原型鏈找到創建他們的Object構造函數的構造原型Object.prototype對象,所以:

alert(Function instanceof Object);// return true

有趣的是根據我們通過原型鏈機制對instanceof進行的分析,我們不難得出一個結論:Function instanceof Function 依然返回true, 原理是一樣的

  1. Function是構造函數,所以它是函數對象
  2. 函數對象都是由Function構造函數創建而來的,原型鏈機制解釋爲:函數對象的原型鏈中存在Function.prototype
  3. instanceof查找原型鏈中的每一個節點,如果Function.prototype的構造函數Function的原型鏈中被查到,返回true

因此下面代碼依然返回true

alert(Function instanceof Function);// still true

結論

  1. 在JavaScript語言中,一切的一切都是對象,它們全部繼承自Object. 或者說所有對象的原型鏈的根節點都是Object.prototype

  2. 理解原型鏈機制在JavaScript中式如何工作的是非常重要的。掌握了它,不管一個對象多麼複雜,你總能夠輕而易舉地將它攻破。


一張圖看懂FunctionObject的關係及簡述instanceof運算符

我在寫一篇圖解prototype__proto__的區別時,搜資料搜到了一個有意思的現象,下面這兩個運算返回的結果是一樣的:

Function instanceof Object;//true
Object instanceof Function;//true

這個是怎麼一回事呢?要從運算符instanceof說起。

一、instanceof究竟是運算什麼的?

我曾經簡單理解instanceof只是檢測一個對象是否是另個對象new出來的實例(例如var a = new Object()a instanceof Object返回true),但實際instanceof的運算規則上比這個更復雜。

首先w3c上有官方解釋(傳送門,有興趣的同學可以去看看),但是一如既往地讓人無法一目瞭然地看懂……

知乎上有同學把這個解釋翻譯成人能讀懂的語言(傳送門),看起來似乎明白一些了:

//假設instanceof運算符左邊是L,右邊是R
L instanceof R 
//instanceof運算時,通過判斷L的原型鏈上是否存在R.prototype
L.__proto__.__proto__ ..... === R.prototype ?
//如果存在返回true 否則返回false

注意:instanceof運算時會遞歸查找L的原型鏈,即L.__proto__.__proto__.__proto__.__proto__…直到找到了或者找到頂層爲止。

所以一句話理解instanceof的運算規則爲:

instanceof檢測左側的__proto__原型鏈上,是否存在右側的prototype原型。

二、圖解構造器FunctionObject的關係

在這裏插入圖片描述圖解構造器FunctionObject的關係

我們再配合代碼來看一下就明白了:

//①構造器Function的構造器是它自身
Function.constructor=== Function;//true

//②構造器Object的構造器是Function(由此可知所有構造器的constructor都指向Function)
Object.constructor === Function;//true



//③構造器Function的__proto__是一個特殊的匿名函數function() {}
console.log(Function.__proto__);//function() {}

//④這個特殊的匿名函數的__proto__指向Object的prototype原型。
Function.__proto__.__proto__ === Object.prototype//true

//⑤Object的__proto__指向Function的prototype,也就是上面③中所述的特殊匿名函數
Object.__proto__ === Function.prototype;//true
Function.prototype === Function.__proto__;//true

三、當構造器ObjectFunction遇到instanceof

我們回過頭來看第一部分那個“奇怪的現象”,從上面那個圖中我們可以看到:

Function.__proto__.__proto__ === Object.prototype;//true
Object.__proto__ === Function.prototype;//true

所以再看回第一點中我們說的instanceof的運算規則,Function instanceof ObjectObject instanceof Function運算的結果當然都是true啦!

如果看完以上,你還覺得上面的關係看暈了的話,只需要記住下面兩個最重要的關係,其他關係就可以推導出來了:

1、所有的構造器的constructor都指向Function

2、Functionprototype指向一個特殊匿名函數,而這個特殊匿名函數的__proto__指向Object.prototype

至於prototype__proto__的關係如何推導,可以參考我寫的上一篇博客《三張圖搞懂JavaScript的原型對象與原型鏈》

發佈了323 篇原創文章 · 獲贊 549 · 訪問量 363萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章