原型模式(prototype)是指用原型實例指向建立對象的種類,而且經過拷貝這些原型建立新的對象。--引自JavaScript設計模式javascript
咱們建立的每個函數都有一個prototype屬性,這個屬性是一個指針,指向一個對象,而這個對象包含了全部由指向他的構造函數所生成的實例的共享屬性和方法。說的通俗點,就是一個對象包含了一些屬性和方法,而全部prototype爲這個對象的構造函數所建立的實例均可以共享這個對象的屬性和方法。直接上代碼:java
```javascript function Animal(type){ this.type = type; } Animal.prototype.sayType = function(){ console.log("我是一隻"+this.type); }; var tom = new Animal("Cat"); var jerry = new Animal("Mouse"); tom.sayType(); // "我是一隻Cat" jerry.sayType(); // "我是一隻Mouse" console.log(tom.sayType === jerry.sayType); //true ```
以上代碼說明了每一個實例都擁有了原型上的方法,這也就是Javascript基於原型的繼承設計模式
我在上一篇中提過,構造函數模式一個缺點是每一個實例的方法都是一個新的Function實例(雖然能夠解決,可是也讓咱們的代碼絲毫沒有封裝性,詳細請看上一篇),而原型模式正好解決了咱們的問題,並且原型模式更符合咱們封裝的需求。markdown
不管何時,只有咱們建立了一個新的函數,JS就會給這個新函數建立一個prototype屬性,該屬性指向這個新函數的原型對象。在默認狀況下,全部的原型對象都會自動得到一個constructor屬性,該屬性包含一個指向這個函數自己的指針,上代碼:ide
```javascript function Animal(type){ this.type = type; } console.log(Animal.prototype.constructor == Animal); //true ```
咱們要注意的是prototype只會取得constructor屬性,至於其餘方法和屬性則都是從Object繼承而來的,示例以下:函數
```javascript function Animal(type){ this.type = type; } var obj = new Object(); console.dir(Animal.prototype); console.dir(obj); ```
運行結果以下圖所示:this
而一樣的咱們能夠給prototype添加其餘自定義的方法和屬性(原型模式的共享),當調用構造函數建立一個新實例後,該實例內部將包含一個指針,指向構造函數的原型對象。ECMA-262第五版管這個指針叫作[[prototype]],須要注意的在JS中是沒有標準的方式訪問[[prototype]],但Firefox,Safari,Chrome在每一個對象上都支持一個屬性__proto__,該屬性指向建立這個實例的構造方法的原型對象,須要注意的是即使咱們在之後改變了prototype,也不會改變這個屬性。(詳細請看上一篇)。在其餘的實現中,這個屬性對腳本是徹底不可見的。prototype
雖然咱們沒有標準的方式去訪問到實例的[[prototype]],但能夠經過isPrototypeOf()方法來肯定實例直接是否存在這種關係。從本質上講,若是[[prototype]]指向調用isPrototypeOf()的實例,那麼這個方法就會返回true,代碼以下:設計
```javascript function Animal(type){ this.type = type; } var tom = new Animal("Cat"); Animal.prototype = {}; var jerry = new Animal("Mouse"); console.log(Animal.prototype.isPrototypeOf(tom)); //false console.log(Animal.prototype.isPrototypeOf(jerry)); //true ```
以上代碼說明,isPrototypeOf()可以正確的檢測出實例的[[prototype]]指向。指針
ECMAScript5 增長了一個新的方法Object.getPrototypeOf(),在全部支持的實現中,這個方法返回[[prototype]]的值。(只支持IE9+,FF3.5+,Safari5+,Opera12+,Chrome)
每當咱們讀取一個對象的屬性時,都會執行一次搜索,他會如今對象自己查看有木有符合給定名字的屬性,若是沒有他會去找該對象的原型對象,一直找下去,直到找到爲止,若是到了Object.prototype還沒找到則會返回undefined,代碼以下:
```javascript function Man(name){ this.name = name; this.sayName = function(){ console.log(this.name); }; } function Person(){ this.type = "人類" this.sayType = function(){ console.log("我是人"); }; } function X(){ this.say = function(){ console.log("XXXXX"); } } Object.prototype.sayHello = function(){ console.log("Hello Moto!"); } Person.prototype = new X(); Man.prototype = new Person(); var wr = new Man("WeiRan"); wr.sayName(); //WeiRan wr.sayType(); //我是人 wr.say(); //XXXXX wr.sayHello();//Hello Moto wr.sayLast(); //Uncaught TypeError: Object #<X> has no method 'sayLast' console.log(wr.xxx); //undefined ```
而咱們新建另一個實例,也會依次執行這樣的步驟,而這正是多個實例共享原型所保存的屬性和方法的基本原理。
廢話很少說,直接上代碼:
```javascript function Animal(type,name){ this.type = type; this.name = name; } Animal.prototype.say = function(){ console.log("我是一隻叫作"+this.name+"的"+this.type); }; var tom = new Animal("Cat","tom"); var jerry = new Animal("Mouse","jerry"); tom.say = function(){ console.log("我偏不說~~~~"); }; tom.say(); //我偏不說~~~~ jerry.say(); //我是一隻叫作jerry的Mouse ```
在這個例子中,咱們發如今tom實例中,say給咱們override的方法給替代了,而在jerry實例中,咱們發現也能正常的訪問prototype。這是由於,當爲一個對象添加一個屬性,這個屬性會屏蔽原型對象中保存的同名屬性。須要注意的是屏蔽而非覆蓋,咱們override的方法或則屬性這是阻止咱們訪問原型中的同名屬性,而並無修改原型對象中同名屬性的值。即便咱們在實例中設置這個屬性爲null,undefined,也不會恢復其指向原型的連接,從而讓咱們從新訪問原型中的屬性。
使用hasOwnProperty()方法能夠檢測一個屬性是否存在與實例中,直接上代碼:
```javascript function Animal(type,name){ this.type = type; this.name = name; } Animal.prototype.say = function(){ console.log("我是一隻叫作"+this.name+"的"+this.type); }; var tom = new Animal("Cat","tom"); console.log(tom.hasOwnProperty("name")); //true console.log(tom.hasOwnProperty("say")); //false ```
這這是原型模式的第一部分,估計會分三部分。因爲這段時間工做需求比較緊,可能沒辦法保證一天一篇,可是一週我對本身的要求是最少三篇。文中若是發現有錯或則個人理解不對,請告訴我,共同進步。