「咱們建立的每個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。」javascript
引用類型才具備prototype屬性,包含:java
1 var fn=new String("text"); 2 String.prototype.value="val"; 3 console.log(fn.value); //val
1 function Person(name){ 2 this.name=name; 3 } 4 Person.prototype.getName = function() { 5 return this.name; 6 }; 7 var p1 = new Person("Evan"); 8 9 console.log(p1.name); //Evan 10 console.log(p1.getName()); //Evan
這個prototype屬性也就至關於動態添加屬性或方法閉包
再看一個更詳細的例子:函數
1 function Person(){ 2 } 3 Person.prototype.name = "Nicholas"; 4 Person.prototype.age = 29; 5 Person.prototype.job = "Software Engineer"; 6 Person.prototype.sayName = function(){ 7 console.log(this.name) 8 }; 9 var person1 = new Person(); 10 var person2 = new Person(); 11 person2.sayName(); //Nicholas 12 console.log(person1.sayName == person2.sayName); //true
一張圖看各對象的關係:學習
Person裏面只有一個prototype屬性,指向原型對象。原型對象中constructor指向它的構造函數(它的來源),和其餘原型屬性方法。this
Person.prototype就是原型 isPrototypeOf肯定兩者是否有關係,Object.getPrototypeOf獲取原型值spa
1 console.log(Person.prototype); //Object {name: "Nicholas", age: 29, job: "Software Engineer"} (原型對象) 2 console.log(Person.prototype.isPrototypeOf(person1)) //true; 3 console.log(Object.getPrototypeOf(person1).name) //Nicholas; 4 console.log(Person.prototype.constructor == Person); //true
若將上例稍改一下,給實例化的person1添加name屬性並賦值: name:meprototype
1 ↑ 2 person1.name = "me"; 3 4 //先在實例中找,沒有再到原型中找 5 console.log(person1.name);//me
console.log(person1.constructor == Person);//true 6 7 //用hasOwnPrototy()檢測是否在實例中,爲false說明屬性來自原型 8 console.log(person1.hasOwnProperty("name")) ;//true 9 console.log(person2.hasOwnProperty("name")) ;//false 10 11 //用一個in,檢測是否有次屬性,不管在實例仍是原型中 12 console.log("name" in person1) ;//true 13 console.log("name" in person2) ;//true
若更改整個 Person.prototype: 指針
1 Person.prototype = {xx:"xx"}; 2 person1.xx; //undefined 3 var ppp=new Person(); 4 ppp.xx; //輸出:xx
只會改變後面實例的對象,以前的不會改變。以前引用的不會被垃圾清理。code
person1和person2是實例化的Person,也能訪問Person的原型對象,用指針[[Prototype]]來實現,咱們不可操做[[Prototype]],但能夠有另外一個__proto__來訪問。
接上例。
1 ↑ 2 console.log(person1.prototype);//undefined 3 console.log(Person.prototype);//Object {name: "Nicholas", age: 29, job: "Software Engineer"} 4 console.log(person1.__proto__);//Object {name: "Nicholas", age: 29, job: "Software Engineer"} 5 console.log(Person.__proto__);//function(){}
實例化對象調用原型對象,是__proto__指針,不是prototype屬性。它自己沒有原型對象,是去調用構造函數的原型對象。
當構造函數(Person)調用__proto__指針時,返回它自己。
__proto__與prototype的區別:(IE不支持__proto__)
__proto__:對象的內部原型的引用。
prototype :返回是對象的原型。
當咱們訪問一個對象的屬性 時,若是這個對象內部不存在這個屬性,那麼他就會去__proto__裏找這個屬性,這個__proto__又會有本身的__proto__,因而就這樣 一直找下去,也就是咱們平時所說的原型鏈的概 念。因此__proto__是聯繫各對象的橋樑。
1 var Person = function () { }; 2 var p = new Person(); 3 alert(p.__proto__ === Person.prototype); //true
這個例子很容易理解,實例p的來源就是Person的原型
看一個複雜的例子:
1 var Person = function () { }; 2 Person.prototype.Say = function () { 3 alert("Person say"); 4 } 5 Person.prototype.Salary = 50000; 6 7 var Programmer = function () { }; 8 Programmer.prototype = new Person(); 9 Programmer.prototype.WriteCode = function () { 10 alert("programmer writes code"); 11 }; 12 Programmer.prototype.Salary = 500; 13 14 var p = new Programmer(); 15 p.Say(); 16 p.WriteCode(); 17 alert(p.Salary);
咱們來作這樣的推導:
var p=new Programmer()能夠得出p.__proto__=Programmer.prototype;
而在上面咱們指定了Programmer.prototype=new Person();咱們來這樣拆分,var p1=new Person();Programmer.prototype=p1;那麼:
p1.__proto__=Person.prototype;
Programmer.prototype.__proto__=Person.prototype;
由根據上面獲得p.__proto__=Programmer.prototype。能夠獲得p.__proto__.__proto__=Person.prototype。
好,算清楚了以後咱們來看上面的結果,p.Say()。因爲p沒有Say這個屬性,因而去p.__proto__,也就是 Programmer.prototype,也就是p1中去找,因爲p1中也沒有Say,那就去p.__proto__.__proto__,也就是 Person.prototype中去找,因而就找到了alert(「Person say」)的方法。
(參考:http://rockyuse.iteye.com/blog/1426510)
1 var student = {name:'aaron'}; 2 console.log(student.__proto__);//Object {}
JS中的全部對象都是繼承自Object對象,因此這裏來源是Object {}
關於繼承
1 function Animal(){} 2 Animal.prototype.say=function(){return "www"}; 3 function Person(){} 4 Person.prototype.say=function(){return "hello"}; 5 Person.prototype = new Animal(); 6 var p2 = new Person(); 7 p2.say(); //www
當一個對象的prototype指向另外一對象時,就實現了繼承。如例子中Person繼承Animal
此時,要注意,父類和子類都有本身的prototype。繼承後,子類的實例對象構造函數就指向父類了。以下
1 ↑ 2 console.log(p2.constructor);//function Animal(){} 3 console.log(Object.getPrototypeOf(Person.prototype).constructor) ;//function Animal(){}
按道理這很正常,理應如此。但有時不是咱們想要的結果。如上例子,雖然繼承了父類,但 p2.say()我想讓它輸出子類的結果,那怎麼弄呢
只需在繼承後重寫便可
1 function Animal(){} 2 Animal.prototype.say=function(){return "www"}; 3 function Person(){} 4 Person.prototype.say=function(){return "hello"}; 5 Person.prototype = new Animal(); 6 Person.prototype.say=function(){return "i am people"} //這裏重寫父類函數 7 var p2 = new Person(); 8 9 p2.say(); //i am people
引用類型的問題:
1 function SuperType(){this.color = ["red","blue","green"]} 2 function SubType(){ 3 } 4 SubType.prototype=new SuperType(); 5 var s1=new SubType(); 6 s1.color.push("black"); 7 console.log(s1.color);//["red", "blue", "green", "black"] 8 var s2=new SubType(); 9 console.log(s2.color);//["red", "blue", "green", "black"]
s1和s2是子類的實例化,s1把繼承父類的color屬性進行添加,按理說只能s1本身添加。結果把子類實例化出的其餘對象的屬性都改了。
SubType.prototype=new SuperType();至關於:子類是父類的一個實例,子類本身擁有了color屬性。但實例化子類時,s1和s2共享color屬性,致使更改時一塊兒改了。
這樣確定不合常理。更改
1 function SuperType(){this.color = ["red","blue","green"]} 2 function SubType(){ 3 SuperType.call(this); 4 } 5 SubType.prototype=new SuperType(); 6 var s1=new SubType(); 7 s1.color.push("black"); 8 console.log(ss.color); //["red", "blue", "green", "black"] 9 var s2=new SubType(); 10 console.log(s2.color); //["red", "blue", "green"]
call()函數在閉包時有講,把某個函數綁定到某個對象中。在這裏,就至關於把父類函數拿過來,在本身做用域調用,借用構造函數,也至關於重寫了父類。
因此每次實例化子類,都要調用子類重寫的函數,進行一次分配,每一個實例擁有本身的color屬性。互不干擾。
或者這樣繼承:
1 function Person(){} 2 Person.prototype.language="chinese"; 3 Person.prototype.eat= function () { 4 console.log("eating"); 5 } 6 function Programmer(){} 7 Programmer.prototype=Object.create(Person.prototype);//js原生複製函數 8 Programmer.prototype.constructor=Programmer; //指回子類本身 9 Programmer.prototype.language="javascript";//覆蓋父類
此文章是學習小結,若有不正,望指正。