javascript原型物件prototype
“我們建立的每一個函式都有一個prototype(原型)屬性,這個屬性是一個指標,指向一個物件,而這個物件的用途是包含可以由特定型別的所有例項共享的屬性和方法。”
引用型別才具有prototype屬性,包含:
1.Object 2.Function 3.Array 4.Date 5.String 6.RegExp 比如: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指向它的建構函式(它的來源),和其他原型屬性方法。
Person.prototype就是原型 isPrototypeOf確定二者是否有關係,Object.getPrototypeOf獲取原型值
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:me
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
只會改變後面例項的物件,之前的不會改變。之前引用的不會被垃圾清理。
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=newSuperType();相當於:子類是父類的一個例項,子類自己擁有了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";//覆蓋父類
原文連結:https://www.cnblogs.com/ooooevan/p/5746708.html