一. 不管是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
判斷數組類型
-
typeof
用以獲取一個變量的類型,typeof
一般只能返回如下幾個結果:number,boolean,string,function,object,undefined
。我們可以使用typeof
來獲取一個變量是否存在,如if(typeof a!="undefined"){}
,而不要去使用if(a)
因爲如果a
不存在(未聲明)則會出錯,對於Array,Null
等特殊對象使用typeof
一律返回object
,這正是typeof
的侷限性。 -
如果我們希望獲取一個對象是否是數組,或判斷某個變量是否是某個對象的實例則要選擇使用
instanceof
。instanceof
用於判斷一個變量是否某個對象的實例,如var a=new Array();alert(a instanceof Array);
會返回true
,同時alert(a instanceof Object)
也會返回true
;這是因爲Array
是object
的子類。再如: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
繼承中判斷實例是否屬於它的父類
Student
和Person
都在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 Function
和Function 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
, 原理是一樣的
Function
是構造函數,所以它是函數對象- 函數對象都是由
Function
構造函數創建而來的,原型鏈機制解釋爲:函數對象的原型鏈中存在Function.prototype
instanceof
查找原型鏈中的每一個節點,如果Function.prototype
的構造函數Function
的原型鏈中被查到,返回true
因此下面代碼依然返回true
alert(Function instanceof Function);// still true
結論
- 在
JavaScript
語言中,一切的一切都是對象,它們全部繼承自Object
.
或者說所有對象的原型鏈的根節點都是Object.prototype
- 理解原型鏈機制在
JavaScript
中式如何工作的是非常重要的。掌握了它,不管一個對象多麼複雜,你總能夠輕而易舉地將它攻破。
Object instanceof Function
還是 Function instance of Object
,是真是假,一一道來
如今的JavaScript
再也不是以前被當做玩具的在網頁上運行的花哨的腳本了。JavaScript
已經逐漸標準化,作爲一門真正的編程語言廣泛地應用在Web開發上。因此,越來越多的人開始重新認識這門腳本語言,並在不斷地探索關於JavaScript
核心思想和實現原理,過程中遇到了一些非常混淆的問題。本文着重解釋一個比較常見但是非常容易使開發人員或者是初學JavaScript
的人非常混淆的問題,那就是兩個核心構造函數Object
和Function
,他們之間到底有什麼關係?爲何instanceof
運算符的返回結果會讓你感到混淆?本文將爲你一一道來。不過在這之前,我們需要先了解一些JavaScript
中的概念和基本的運行機制。
JavaScript
的對象體系結構
其實在JavaScript
語言中,整個核心的體系結構都圍繞着兩個構造函數Object
和Function
來構建的。我將引用來自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 Function
和Function 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
, 原理是一樣的
Function
是構造函數,所以它是函數對象- 函數對象都是由
Function
構造函數創建而來的,原型鏈機制解釋爲:函數對象的原型鏈中存在Function.prototype
instanceof
查找原型鏈中的每一個節點,如果Function.prototype
的構造函數Function
的原型鏈中被查到,返回true
因此下面代碼依然返回true
alert(Function instanceof Function);// still true
結論
-
在JavaScript語言中,一切的一切都是對象,它們全部繼承自
Object
. 或者說所有對象的原型鏈的根節點都是Object.prototype
-
理解原型鏈機制在JavaScript中式如何工作的是非常重要的。掌握了它,不管一個對象多麼複雜,你總能夠輕而易舉地將它攻破。
一張圖看懂Function
和Object
的關係及簡述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
原型。
二、圖解構造器Function
和Object
的關係
圖解構造器Function
和Object
的關係
我們再配合代碼來看一下就明白了:
//①構造器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
三、當構造器Object
和Function
遇到instanceof
我們回過頭來看第一部分那個“奇怪的現象”,從上面那個圖中我們可以看到:
Function.__proto__.__proto__ === Object.prototype;//true
Object.__proto__ === Function.prototype;//true
所以再看回第一點中我們說的instanceof
的運算規則,Function instanceof Object
和 Object instanceof Function
運算的結果當然都是true
啦!
如果看完以上,你還覺得上面的關係看暈了的話,只需要記住下面兩個最重要的關係,其他關係就可以推導出來了:
1、所有的構造器的constructor
都指向Function
2、Function
的prototype
指向一個特殊匿名函數,而這個特殊匿名函數的__proto__
指向Object.prototype
至於prototype
和__proto__
的關係如何推導,可以參考我寫的上一篇博客《三張圖搞懂JavaScript的原型對象與原型鏈》