首先,先認識下什麼是原型?javascript
原型是一個對象,其餘對象能夠經過它實現屬性繼承,並且任何一個對象均可以成爲原型。這是爲何呢?請繼續看。html
首先,要弄明白什麼是對象,在JavaScript中能夠說一切都是對象,除了(undefined, number, string, boolean)這四種值類型,咱們能夠經過 typeof() 這個函數和instanceof進行檢測,如何判斷一個變量是否是對象,值類型的類型判斷用typeof,引用類型的類型判斷用instanceof。以下:java
1 function show(x) { 2 3 console.log(typeof(x)); // undefined 4 console.log(typeof(10)); // number 5 console.log(typeof('abc')); // string 6 console.log(typeof(true)); // boolean 7
8 console.log(typeof(function () { })); //function 9 console.log((function () { }) instanceof Object); // true 10 11 console.log(typeof([1, 'a', true])); //object 12 console.log(typeof ({ a: 10, b: 20 })); //object 13 console.log(typeof (null)); //object 14 console.log(typeof (new Number(10))); //object 15 } 16 show();
其中上面的四種(undefined, number, string, boolean)屬於簡單的值類型,不是對象。剩下的幾種狀況——函數、數組、對象、null、new Number(10)都是對象。對象是屬性的集合。git
弄明白什麼是對象後,咱們再來理解下函數與對象的關係。對象都是經過函數建立的,爲何這麼說呢,請看代碼: github
1 var obj = { a: 10, b: 20 }; 2 var arr = [5, 'x', true]; 3 //上面看起來不像是經過函數建立出一個對象,其實其本質以下 4 5 var obj = new Object(); 6 obj.a = 10; 7 obj.b = 20; 8 9 var arr = new Array(); 10 arr[0] = 5; 11 arr[1] = 'x'; 12 arr[2] = true; 13 14 console.log(typeof (Object)); // function 15 console.log(typeof (Array)); // function
經過上述代碼理解仍是很是的迷惑吧。對象是函數建立的,而函數卻又是一種對象,函數和對象究竟是什麼關係啊?咱們就經過prototyoe原型來加以理解。chrome
每一個函數都有一個屬性叫作prototype(原型)。這個prototype的屬性值是一個對象(屬性的集合),默認的只有一個叫作constructor的屬性,指向這個函數自己。如圖(SuperType是一個函數,右側的方框就是它的原型)json
固然,原型做爲一個對象,不可能只含有consrtuctor這一個屬性而已,咱們能夠在其中添加一些咱們自定義的屬性,例如:數組
1 function Fn() { }; 2 Fn.prototype.name = 'hello'; 3 Fn.prototype.getYear = function () { 4 return 2015; 5 };
1 function Fn() { }; 2 Fn.prototype.name = 'hello'; 3 Fn.prototype.getYear = function () { 4 return 2015; 5 }; 6 7 var fn = new Fn(); 8 console.log(fn.name); //hello 9 console.log(fn.getYear()); //2015
Fn是一個函數,fn對象是從Fn函數new出來的,這樣fn對象就能夠調用Fn.prototype中的屬性。由於每一個對象都有一個隱藏的屬性 ----「__proto__」,這個屬性引用了建立這個對象的函數的prototype。即:fn.__proto__ === Fn.prototype.這裏的"__proto__"成爲「隱式原型」。瀏覽器
prototype與__proto__的區別app
二者都是對象類型的屬性,並不是全部的對象類型都有prototype屬性,通常只有function對象纔有prototype屬性(除非主動賦值),它指向的是一個對象,未來會被多個該function的實例所繼承(或者說該對象處於多個實例的原型鏈上);__proto__纔是真正的原型鏈的實際指針,然而許多瀏覽器並不對外公開這個屬性,Firefox暴露出這一屬性,僅供開發人員理解,但不推薦開發中使用,目前chrome也能夠支持了。
小插曲:instanceof表示的就是一種繼承關係,或者原型鏈的結構
typeof在判斷到引用類型的時候,返回值只有object/function,你不知道它究竟是一個object對象,仍是數組,仍是new Number等等。這個時候就須要用到instanceof.
instanceof的判斷隊則是:沿着A的__proto__這條線來找,同時沿着B的prototype這條線來找,若是兩條線能找到同一個引用,即同一個對象,那麼就返回true。若是找到終點還未重合,則返回false。以下圖:
全部構造器/函數的__proto__都指向Function.prototype,它是一個空函數(Empty function),JavaScript中有內置(build-in)構造器/對象共計12個(ES5中新加了JSON),這裏列舉了可訪問的8個構造器。剩下如Global不能直接訪問,Arguments僅在函數調用時由JS引擎建立,Math,JSON是以對象形式存在的,無需new。它們的__proto__是Object.prototype。以下:
1 Number.__proto__ === Function.prototype // true 2 Boolean.__proto__ === Function.prototype // true 3 String.__proto__ === Function.prototype // true 4 Object.__proto__ === Function.prototype // true 5 Function.__proto__ === Function.prototype // true 6 Array.__proto__ === Function.prototype // true 7 RegExp.__proto__ === Function.prototype // true 8 Error.__proto__ === Function.prototype // true 9 Date.__proto__ === Function.prototype // true 10 11 Math.__proto__ === Object.prototype // true 12 JSON.__proto__ === Object.prototype // true
注:上述代碼中__proto__目前在IE6/7/8/9中都不支持,IE9中可使用Object.getPrototypeOf(ES5)獲取對象的內部原型。除了IE(IE11開始支持),其餘的瀏覽器支持非標準的訪問器__proto__。那麼那些不支持__proto__屬性的能夠經過constructor間接獲得,constructor屬性不是對象本身的屬性,而是順着原型鏈向上從原型對象中獲取的。這個屬性指向的是這個原型對象所對應的構造函數。而構造函數的prototype屬性指向了原型對象, 因此這樣咱們就能夠間接獲得了,例:
function Foo(){}; var foo = new Foo(); alert(foo.constructor.prototype == Foo.prototype); // true
每一個對象都有一個__proto__屬性,原型鏈上的對象正是依靠這個__proto__屬性連結在一塊兒的,__proto__是否指向實例對象的原型prototype對象的引用。什麼是原型鏈呢?簡單來講,訪問一個對象的屬性時,先在基本屬性中查找,若是沒有,再沿着__proto__這條鏈向上找,這就是原型鏈。全部對象都繼承於Object,原型鏈的頂端就是Object.prototype,Object.prototype.__proto__ = null;看圖理解下:
對象的原型鏈是沿着__proto__這條線走的,所以在查找f1.hasOwnProperty屬性時,就會順着原型鏈一直查找到Object.prototype。因爲全部的對象的原型鏈都會找到Object.prototype,所以全部的對象都會有Object.prototype的方法。這也是所謂的「繼承」。 說一個函數的例子吧,咱們都知道每一個函數都有call,apply方法,都有length,arguments,caller等屬性。爲何每一個函數都有?這確定是「繼承」的。函數由Function函數建立,所以繼承的Function.prototype中的方法。
理解了原型與原型鏈後,那麼用原型有什麼好處呢?
1.對象屬性能夠隨時改動,對象或者函數,剛開始new出來以後,可能啥屬性都沒有。可是你能夠根據你的須要繼續添加,很是靈活。
2.若是繼承的方法不合適,能夠作出修改。例如在json2.js源碼中,爲Date、String、Number、Boolean方法添加一個toJSON的屬性。
3.能夠繼續建立新的方法,不過若是你要添加內置方法的原型屬性,最好作一步判斷,若是該屬性不存在,則添加。若是原本就存在,就不必再添加了。
記:博文參考了網上不少關於原型的介紹,概括得不足之處,請指正。