在第一章的時候,說過了單例模式、工廠模式、構造函數模式,你還記得構造模式是怎麼樣的嗎?瀏覽器
function CreateJsPerson(name, age) { this.name = name; this.age = age; this.writeJs = function() { console.log("my name is " + this.name + ", i can write js."); } } var p1 = new CreateJsPerson("p1", 18); var p2 = new CreateJsPerson("p2", 17); p1.writeJs(); p2.writeJs(); console.log(p1.writeJs() === p2.writeJs()); //-> false // 構造函數模式中擁有了類和實例的概念,而且實例和實例之間是相互獨立開的 // -> 叫作實例識別
上面的就是構造函數模式,你知道,爲何p一、p2的writeJs()方法爲何不相等嗎?這是由於正如上面所說的,兩個實例是相互獨立的,也就是說,兩個實例的屬性都是各自私有屬性。函數
-> 問題來了,兩個實例裏面的屬性都是私有的之外,是否是還得有公有的部分?學習
1、【基於構造函數的原型模式】this
使用基於構造函數模式的原型模式,可以實現把writeJs()方法變成公有的,代碼以下:spa
function CreateJsPerson(name, age) { this.name = name; this.age = age; // this.writeJs = function() { // console.log("my name is " + this.name + ", i can write js."); // } } CreateJsPerson.prototype.writeJsG = function() { console.log("my name is " + this.name + ", i can write js."); }; var p1 = new CreateJsPerson("p1", 18); var p2 = new CreateJsPerson("p2", 17); p1.writeJs(); p2.writeJs(); console.log(p1.writeJsG() === p2.writeJsG()); //->true
爲何這樣寫,就能把屬性變爲公有的呢?prototype
基於構造函數模式擴展出來的原型模式:對象
->它解決了方法或者屬性公有的問題blog
->即把實例之間公有的屬性和方法提出成公有的屬性和方法繼承
->想讓誰公有,就把它放在prototype上便可內存
2、【原型基礎3句話】
想要學習原型,要記住下面的三句話(不要問爲何~~):
1)每個函數數據類型(普通函數、類)都有一個天生自帶的屬性,prototype(原型),而且這個屬性是一個對象數據類型的值。
2)而且在prototype上瀏覽器會天生給它加上一個屬性contructor(構造函數),屬性值是當前函數(類)自己。
3)每個對象數據類型(普通對象,實例,prototype...)也天生自帶一個屬性:__proto__,屬性值是當前實例所屬類的原型(prototype)。
下面這段代碼,能夠說明一下上面的3句話:
function Fn() { this.x = 100; }; Fn.prototype.getX = function() { console.log(this.x); }; var f1 = new Fn; var f2 = new Fn; console.log(Fn.prototype.constructor === Fn); //->true // 堆內存:存儲 對象、函數裏面的代碼字符串
上面代碼的原型鏈以下圖所示,正方形表明棧內存(即函數做用域),橢圓正方形表明堆內存(即對象)
從畫圖能夠很清晰看到,整個基於構造函數擴展出來的原型鏈函數,類與實例的原型鏈,實例與JS基類Object的原型鏈關係,一覽無遺。
上圖+代碼,能夠總結出:
一、Object是JS中全部對象數據類型的基類(最頂層的類)
1) f1 instanceof Object -> true,這是由於f1經過__proto__能夠向上級查找,無論有多少級,最後總能找到Object
2)在Object.prototype上沒有__proto__這個屬性(由於本身指向本身沒意義)
二、原型鏈模式
舉個簡單的例子,f1.hasOwnProperty("x"); //->hasWwnProperty是f1的一個屬性,可是咱們發現f1的私有屬性上並無這個方法,那如何處理的呢?
經過 對象名.屬性名 的方式獲取屬性值的時候,首先在對象的私有的屬性上進行查找,若是私有中存在這個屬性,則獲取的是私有的屬性值;
->若是私有的沒有,則經過__proto__找到所屬類的原型(類的原型上定義的屬性和方法都是當前實例的公有的屬性和方法),原型上存在的話,獲取的是公有的屬性值;
-> 若是原型上也沒有,則繼續經過原型上的__proto__繼續向上查找,一直找到Object.prototype爲止...
->這種查找的機制就是咱們的原型鏈模式
3、【原型知識玩起來】
cconsole.log(f1.getX === f2.getX); //->true console.log(f1.__proto__.getX === f2.getX); //->true console.log(f1.getX === Fn.prototype.getX); //->true // f1.getX 跟 f1.__proto__.getX的區別 // 前者是瀏覽器先找私有做用域,找不到再找公有做用域 // 後者是瀏覽器直接查找公有做用域 // console.log(f1.hasOwnProperty === f1.__proto__.__proto__.hasOwnProperty); // 在IE瀏覽器中,咱們原型模式也是一樣的原理,可是IE瀏覽器怕你經過__proto__把公有的修改, // 禁止咱們使用__proto__,下面的例子就能夠很明顯的說明爲啥IE禁止了 f1.sum == function() { //修改本身私有的sum }; f1.__proto__.sum = function() { //修改所屬類原型上的sum }; // 因此修改公有的,IE只能經過prototype Fn.prototype.sum = function() { // 修改公有的sum };
4、【總結】
在這一章中,主要說的是原型模式,可是原型模式是經過構造函數擴展出來的,同時也經過代碼+圖的方式,把原型模式的實現原理給畫了出來。
既然原型這個知識點出來了,我會在後面介紹一下,使用原型來實現類的封裝、繼承、多態(裏面的重寫),而且會介紹使用原型來實現7中繼承方法,將會很是有趣。