js中的對象分為兩種:普通對象object和函數對象function。
function fn1(){}; var fn2 = function(){}; var fn3 = new Function(); var fn4 = Function(); var obj1 = new fn1(); var obj2 = {}; var obj3 = new Object(); var obj4 = Object(); console.log(typeof fn1);//function console.log(typeof fn2);//function console.log(typeof fn3);//function console.log(typeof fn4);//function console.log(typeof obj1);//object console.log(typeof obj2);//object console.log(typeof obj3);//object console.log(typeof obj4);//object
還有就是所有的構建函數比如Function、Object、Number等等都是函數對象,這個共知的。
//所有的構建函數都是function類型的 console.log(typeof Object);//function console.log(typeof Function);//function console.log(typeof Number);//function
所有的這類構建函數使用new或者直接調用方式都能構建出一個新的數據類型。但是構建出來的數據有區別的
var o = new Object(), o1 = Object(); console.log(o == o1);//false console.log(o === o1);//false var f = new Function(), f1 = Function(); console.log(f == f1);//false console.log(f === f1);//false var a = new Array(), a1 = Array(); console.log(a == a1);//false console.log(a === a1);//false var n = new Number(), n1 = Number(); console.log(n == n1);//true console.log(n === n1);//false var s = new String(), s1 = String(); console.log(s == s1);//true console.log(s === s1);//false var b = new Boolean(), b1 = Boolean(); console.log(b == b1);//true console.log(b === b1);//false //數據類型null/undefined是沒有構造函數的
上面的例子中Object/Function/Array的對比結果都好說,因為他們構建出來的都是新的對象,對象比較是要比較根源(數據是否是同一個)。無論是使用new還是直接調用生成一個新的對象是要開辟新的空間存儲的,不會和任何一個對象相等。
但是對於數值類型,比較符“==”只是比較值相等,比較符"==="除了比較值以外還要比較數據類型。
那么構建數值類型比較為什么呈現上面的樣子?
我們以Number為例。實際上new Number()構建出來的變量n是一個特殊的對象,chrome上的展示如下

只不過這個對象和數值類型比較的時候被當做數值類型來比較。當使用“===”的時候比較數值相等時再比較數據類型的時候是有別於其他數值類型的。
上面提到了和數值類型比較的時候才成立,如果這個對象和其他對象比較則使用對象比較的規則。
比如下面的例子
var num = new Number(0); var str = new String(0); var str1 = String(0); console.log(num == str);//false console.log(num == str1);//true console.log(num === str1);//false
num和str都是比較特殊的對象,str1為數值類型。num和str比較實用對象比較的規則來,num和str1比較實用數值比較的規則來。
上面分析了那么多,現在進入正題。
普通對象是沒有prototype屬性的,只有隱藏屬性__proto__(IE上也有該隱藏屬性,但是使用obj.__proto__不能輸出東西,所以建議不要使用__proto__屬性)。而函數對象則兩者兼有。prototype屬性指向的是函數對象的原型對象,對象的__proto__屬性是創建實例對象的時候對應的函數對象的原型對象。
a.函數對象的原型對象(fn.prototype)
這里我們需要理解原型對象的值是怎么來的。原型對象的值實際上就是在函數創建的時候,創建了一個它的實例對象並賦值給它的prototype。過程如下(以Function為例)
var temp = new Function(); Function.prototype = temp;
所以我們看一下熟知的函數的原型對象吧
//chrome下的顯示效果 Function.prototype;//function() {} Object.prototype;//Object {} Number.prototype;//Number {[[PrimitiveValue]]: 0} Boolean.prototype;//Boolean {[[PrimitiveValue]]: false} Array.prototype;//[] String.prototype;//String {length: 0, [[PrimitiveValue]]: ""}
說道這里,必須提的是所有函數對象的原型對象都繼承制原始對象,即fn.prototype.__proto__為原始對象(原始對象在繼承屬性__proto__中有定義)。這其中比較特別的是Object函數,他的原型對象就是原始對象,即Object.prototype。
var f1 = new Function(); var f2 = Function(); var fn3 = function(){} console.log(f1.prototype.__proto__ === Object.prototype);//true console.log(f2.prototype.__proto__ === Object.prototype);//true console.log(f2.prototype.__proto__ === Object.prototype);//true console.log(Number.prototype.__proto__ === Object.prototype);//true console.log(Boolean.prototype.__proto__ === Object.prototype);//true
b.繼承屬性__proto__
實際上js沒有繼承這個東東,但是__proto__卻起到了類似繼承的作用。我們所知的所有的對象起源都是一個空對象,我們把這個空對象叫做原始對象。所有的對象通過__proto__回溯最終都會指向(所謂的指向類似C中的指針,這個原始對象是唯一的,整個內存中只會存在一個原始對象)這個原始對象。用下面的例子佐證
var o = new Object(); o.__proto__;//Object {} o.prototype;//undefined Object.prototype;//Object {} Object.__proto__;//function(){} Object.__proto__.__proto__;//Object {} var f = new Function(); f.__proto__;//function(){} f.prototype;//Object {} Function.prototype;//function(){} Function.__proto__;//function(){} Function.__proto__.__proto__;//Object {}
原始對象的__proto__屬性為null,並且沒有原型對象。
所有的對象都繼承自原始對象;Object比較特殊,他的原型對象也就是原始對象;所以我們往往用Object.prototype表示原始對象。
//所有的對象都繼承自原始對象 //Object比較特殊,他的原型對象也就是原始對象 //所以我們往往用Object.prototype表示原始對象 Object.prototype === o.__proto__;//true Object.prototype === Object.__proto__.__proto__;//true Object.prototype === Function.__proto__.__proto__;//true
f.prototype的的值貌似也是原始對象?其實不是,我們在函數對象的原型對象這一段中不是說過嗎函數對象f的原型對象實際上是函數對象的一個實例。每一個實例都是一個新的單獨的對象。
new f();//Object {}
所有的函數對象都繼承制原始函數對象;Function比較特殊,他的原型對象也就是原始函數對象;所以我們往往用Function.prototype表示原始函數對象;而原始函數對象又繼承自原始對象。
//所有的函數對象都繼承制原始函數對象, //Function比較特殊,他的原型對象也就是原始函數對象 Function.prototype === f.__proto__ Function.prototype === Object.__proto__ ;//true Function.prototype === Function.__proto__;//true //所以我們往往用Function.prototype表示原始函數對象 //而原始函數對象又繼承自原始對象 Function.prototype.__proto__ === Object.prototype;
所以對象之間的繼承和原型對象結構如下圖(引用的別人的js object猜想圖)

看了上面的圖我們還知道函數對象的原型對象的構造函數就是函數對象本身。不難理解函數對象的原型對象就是函數對象的實例了吧。
c. 原型鏈
在使用New方法初始化函數的時候(詳細點擊查看new的深度理解)得到的新對象的__proto__屬性會指向函數對象的原型對象,而函數對象的原型對象又繼承至原始對象。所以呈現以下結構
function fn(){}; var test = new fn();

把這個有__proto__串起來的直到Object.prototype.__proto__為null的鏈叫做原型鏈。原型鏈實際上就是js中數據繼承的繼承鏈。
