原型、原型對象的理解 及其原型鏈繼承


https://www.cnblogs.com/my-blog-yu/p/6627639.html

https://blog.csdn.net/u010298576/article/details/86165783

from:https://www.cnblogs.com/sarahwang/p/6870072.html

1、prototype的定義

javascript中的每個對象都有prototype屬性,Javascript中對象的prototype屬性的解釋是:返回對象類型原型的引用。

每一個構造函數都有一個屬性叫做原型。這個屬性非常有用:為一個特定類聲明通用的變量或者函數。

你不需要顯式地聲明一個prototype屬性,因為在每一個構造函數中都有它的存在。你可以看看下面的例子:

function Test(){}
alert(Test.prototype); // 輸出 "Object"

1.1、 原型法設計模式

原型法的主要思想是,現在有1個類A,我想要創建一個類B,這個類是以A為原型的,並且能進行擴展。我們稱B的原型為A。

1.2、javascript的方法可以分為三類:

a 類方法

b 對象方法

c 原型方法

復制代碼
function People(name)
{
  this.name=name;
  //對象方法
  this.Introduce=function(){
    alert("My name is "+this.name);
  }
}
//類方法
People.Run=function(){
  alert("I can run");
}
//原型方法
People.prototype.IntroduceChinese=function(){
  alert("我的名字是"+this.name);
}
 
//測試
var p1=new People("Windking");
p1.Introduce();
People.Run();
p1.IntroduceChinese();
復制代碼

1.3、給prototype添加屬性

就如你在上面所看到的,prototype是一個對象,因此,你能夠給它添加屬性。你添加給prototype的屬性將會成為使用這個構造函數創建的對象的通用屬性。

例如,我下面有一個數據類型Fish,我想讓所有的魚都有這些屬性:livesIn="water"和price=20;為了實現這個,我可以給構造函數Fish的prototype添加那些屬性。

復制代碼
function Fish(name, color){
   this.name=name;
   this.color=color;
}
Fish.prototype.livesIn="water";
Fish.prototype.price=20;
var fish1=new Fish("mackarel", "gray"); var fish2=new Fish("goldfish", "orange"); var fish3=new Fish("salmon", "white"); for (int i=1; i<=3; i++){ alert(fish.name+","+fish.color+","+fish.livesIn+","+fish.price); }
復制代碼

輸出應該是:

"mackarel, gray, water, 20"
"goldfish, orange, water, 20"
"salmon, white water, 20"

你看到所有的魚都有屬性livesIn和price,我們甚至都沒有為每一條不同的魚特別聲明這些屬性。這時因為當一個對象被創建時,這個構造函數 將會把它的屬性prototype賦給新對象的內部屬性__proto__。這個__proto__被這個對象用來查找它的屬性

你也可以通過prototype來給所有對象添加共用的函數。這有一個好處:你不需要每次在構造一個對象的時候創建並初始化這個函數。為了解釋這一點,讓我們重新來看Example DT9並使用prototype來重寫它。

1.4、用prototype給對象添加函數

復制代碼
function Employee(name, salary){
    this.name=name;               
    this.salary=salary;
}
Employee.prototype.getSalary=function getSalaryFunction(){
    return this.salary;
}
Employee.prototype.addSalary=function addSalaryFunction(addition){
    this.salary=this.salary+addition;
}
var boss1=new Employee("Joan", 200000);
var boss2=new Employee("Kim", 100000);
var boss3=new Employee("Sam", 150000);
復制代碼

輸出的結果為:

alert(boss1.getSalary());   // 輸出 200000
alert(boss2.getSalary());   // 輸出 100000
alert(boss3.getSalary());   // 輸出 150000

子類如何重寫父類的屬性或方法:

復制代碼
function AClass(){
    this.Property = 1;
    this.Method = function(){
        alert(1);
    }
}
function AClass2(){
   this.Property2 = 2;
   this.Method2 = function(){
        alert(2);
   }
}
AClass2.prototype = new AClass();
AClass2.prototype.Property = 3;
AClass2.prototype.Method = function(){
   alert(4);
}
var obj = new AClass2();
alert(obj.Property);
obj.Method();
復制代碼

輸出結果為:

//3
//4

可以在對象上增加屬性或方法

復制代碼
function Aclass(){
this.Property = 1;
this.Method = function(){
    alert(1);
}
}
var obj = new Aclass();
obj.Property2 = 2;
obj.Method2 = function(){
    alert(2);
}
alert(obj.Property2);
obj.Method2();
復制代碼

輸出結果為:

//2
//2
在外部不能通過prototype改變自定義類型的屬性或方法。該例子可以看到:調用的屬性和方法仍是最初定義的結果。即當原型方法和對象方法在調用相同的屬性和函數時,會執行對象方法里面的屬性和函數。
復制代碼
function Aclass(){
this.Property = 1;
this.Method = function(){
    alert(1);
}
}
Aclass.prototype.Property = 2;
Aclass.prototype.Method = function{
    alert(2);
}
var obj = new Aclass();
alert(obj.Property);
obj.Method();
復制代碼

輸出結果為:

//1
//1

1.5、A.prototype = new B();

理解prototype不應把它和繼承混淆。A的prototype為B的一個實例,可以理解A將B中的方法和屬性全部克隆了一遍。A能使用B的方法和屬性。這里強調的是克隆而不是繼承。可以出現這種情況:A的prototype是B的實例,同時B的prototype也是A的實例。

先看一個實驗的例子:

復制代碼
function baseClass(){
  this.showMsg = function(){
     alert("baseClass::showMsg");   
  }
}
function extendClass(){}
extendClass.prototype = new baseClass();
var instance = new extendClass();
instance.showMsg(); // 顯示baseClass::showMsg
復制代碼

我們首先定義了baseClass類,然后我們要定義extentClass,但是我們打算以baseClass的一個實例為原型,來克隆的extendClass也同時包含showMsg這個對象方法。

extendClass.prototype = new baseClass()就可以閱讀為:extendClass是以baseClass的一個實例為原型克隆創建的。

那么就會有一個問題,如果extendClass中本身包含有一個與baseClass的方法同名的方法會怎么樣?

下面是擴展實驗2:

復制代碼
function baseClass(){
    this.showMsg = function(){
        alert("baseClass::showMsg");   
    }
}

function extendClass(){
    this.showMsg =function (){
        alert("extendClass::showMsg");
    }
}
extendClass.prototype = new baseClass();
var instance = new extendClass();

instance.showMsg();//顯示extendClass::showMsg
復制代碼

實驗證明:函數運行時會先去本體的函數中去找,如果找到則運行,找不到則去prototype中尋找函數。或者可以理解為prototype不會克隆同名函數

那么又會有一個新的問題:如果我想使用extendClass的一個實例instance調用baseClass的對象方法showMsg怎么辦?

答案是可以使用call:

extendClass.prototype = new baseClass();
var instance = new extendClass();

var baseinstance = new baseClass();
baseinstance.showMsg.call(instance);//顯示baseClass::showMsg

這里的baseinstance.showMsg.call(instance);閱讀為“將instance當做baseinstance來調用,調用它的對象方法showMsg”

好了,這里可能有人會問,為什么不用baseClass.showMsg.call(instance);

這就是對象方法和類方法的區別,我們想調用的是baseClass的對象方法

1.6、對象方法與通過new創建對象的重要區別

這個區別就是function定義的方法(對象方法)有一個prototype屬性,使用new生成的對象就沒有這個prototype屬性。也就是prototype屬性是對象方法或者構造方法的專有屬性。 prototype屬性又指向了一個prototype對象,注意prototype屬性與prototype對象是兩個不同的東西,要注意區別。在prototype對象中又有一個constructor屬性,這個constructor屬性同樣指向一個constructor對象,而這個constructor對象恰恰就是這個function函數本身。如下所示:

復制代碼
function Person(name)  
{  
   this.name=name;  
   this.showMe=function()  
        {  
           alert(this.name);  
        }  
};    
var one=new Person('js');    
alert(one.prototype)//undefined  
alert(typeof Person.prototype);//object  
alert(Person.prototype.constructor);//function Person(name) {...};  
復制代碼

最后,下面這個代碼如果理解清晰,那么這篇文章說的就已經理解了:

復制代碼
<script type="text/javascript">

function baseClass()
{
    this.showMsg = function()
    {
        alert("baseClass::showMsg");   
    }
   
    this.baseShowMsg = function()
    {
        alert("baseClass::baseShowMsg");
    }
}
baseClass.showMsg = function()
{
    alert("baseClass::showMsg static");
}

function extendClass()
{
    this.showMsg =function ()
    {
        alert("extendClass::showMsg");
    }
}
extendClass.showMsg = function()
{
    alert("extendClass::showMsg static")
}

extendClass.prototype = new baseClass();
var instance = new extendClass();

instance.showMsg(); //顯示extendClass::showMsg
instance.baseShowMsg(); //顯示baseClass::baseShowMsg
instance.showMsg(); //顯示extendClass::showMsg

baseClass.showMsg.call(instance);//顯示baseClass::showMsg static

var baseinstance = new baseClass();
baseinstance.showMsg.call(instance);//顯示baseClass::showMsg

</script>
 
復制代碼

總結:本文對在javascript中經常出現且難以理解的prototype屬性,在含義,使用方法(給prototype添加屬性,用prototype給對象添加函數),在添加函數且調用內容函數過程中遇到的一些問題提供了相應的解決方法等方面做了相關的解釋,希望對大家有幫助,喜歡的話,記得動動手指點點推薦哦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM