一步步學習javascript基礎篇(5):面向對象設計之對象繼承(原型鏈繼承)


上一篇介紹了對象創建的幾種基本方式,今天我們看分析下對象的繼承。

一、原型鏈繼承

1.通過設置prototype指向“父類”的實例來實現繼承。

function Obj1() {
    this.name1 = "張三";
}
function Obj2() {
}
Obj2.prototype = new Obj1();
var t2 = new Obj2();
alert(t2.name1);

這里有個明顯的缺點就是:(如果父類的屬性是引用類型,那么我們在對象實例修改屬性的時候會把原型中的屬性修改,這樣會在每個實例對象中改變數據,而這不是我們想要的效果)

function Obj1() {
    this.arr = ["張三"];
}
function Obj2() {
}
Obj2.prototype = new Obj1();
var t2 = new Obj2();
alert(t2.arr);//打印“張三”
t2.arr[t2.arr.length] = "李四";
var t1 = new Obj2();
alert(t1.arr);//打印“張三,李四”

例:

那我們怎樣規避這種問題呢?接着往下看。

2. 利用構造函數來實現繼承

function Obj1() {
    this.arr = ["張三"];
}
function Obj2() {
    Obj1.call(this);//【1.新增】
}
//Obj2.prototype = new Obj1();【2.注釋這行】
var t2 = new Obj2();
alert(t2.arr);//打印“張三”
t2.arr[t2.arr.length] = "李四";
var t1 = new Obj2();
alert(t1.arr);//打印“張三,李四”

我們看到上面代碼,就注釋了一行,新增了以后。打印出來的效果完全不一樣了。現在的arr屬性是每個實例對象獨有的了。(之前是定義到原型上的,而原型的屬性對每個實例都是共享的

 

例:

同樣,單純的這種方式也是有問題的。因為我們這樣就無法繼承對象的方法了。如:

function Obj1() {
    this.arr = ["張三"];
}
Obj1.prototype.sayHi = function () { alert(this.arr); }////【1.新增】
function Obj2() {
    Obj1.call(this);
}        
var t2 = new Obj2();
//t2里面是沒有sayHi方法的

我們可以使用原型和構造的混用來解決,如下:

3.通過原型和構造來實現繼承

function Obj1() {
    this.arr = ["張三"];
}
Obj1.prototype.sayHi = function () { alert(this.arr); }
function Obj2() {
    Obj1.call(this);
}
Obj2.prototype = new Obj1();//【1.新增】
var t2 = new Obj2();
t2.sayHi();

如上,通過構造函數中的  Obj1.call(this); 和設置原型屬性 Obj2.prototype = new Obj1(); 結合使用,完美解決問題。

這里需要注意一個地方,如果把 Obj2.prototype = new Obj1(); 改成 Obj2.prototype = Obj1.prototype ; 的話,會有你想不到的問題。如:

function Obj1() {
    this.arr = ["張三"];
}
Obj1.prototype.sayHi = function () { alert(this.arr); }
function Obj2() {
    Obj1.call(this);
}
Obj2.prototype = Obj1.prototype;//【1.新增】
var t2 = new Obj2();
t2.constructor.prototype.sayHi = function () { alert("test") };//修改Obj2中的原型的方法
var t1 = new Obj1();
t1.sayHi();
//影響到了Obj1中的原型的方法。因為 Obj2.prototype = Obj1.prototype;讓兩個對象的原型指向了同一處。
//所以還是只能用Obj2.prototype = new Obj1();

 例:

4.什么是原型鏈 

如:

//*************Obj1****
function Obj1() {
    this.arr = ["張三"];
}
Obj1.prototype.sayHi = function () { alert(this.arr); }

//*************Obj2****
function Obj2() {
    Obj1.call(this);
    this.name = "張三";
}
Obj2.prototype = new Obj1();
Obj2.prototype.sayHi2 = function () { alert(this.name); };

//*************Obj3****
function Obj3() {
    Obj2.call(this);
}
Obj3.prototype = new Obj2();
Obj3.prototype.sayHi3 = function () { };

//*******************
var t3 = new Obj3();
t3.sayHi();

 Obj3繼承Obj2,Obj2繼承Obj1。我們的Obj3的實例對象訪問sayHi的時候,會先去Obj3的實例對象中找sayHi方法(沒找到),然后去Obj3的原型中找(沒找到),然后去父類Obj2的原型中找(沒找到),然后去Obj1的原型中找(找到了)。這個找的路徑就是原型鏈。

 


 

補充分割線20151230) 

以上,我們在說繼承的時候,我們都是 obj2.prototype = new obj1(); 原型指向父類構造函數。其實這樣有一個問題。如:

function obj1() {
    this.name2 = "張三";
}
obj1.prototype.sayhi = function () {
    alert(this.name2);
}

function obj2() {
    obj1.call(this);//繼承屬性
}
obj2.prototype = new obj1();
var obj = new obj2();

我們看到name2這個屬性,並不是我們想要在prototype中的prototype.name2中繼承過來的。(感覺不是那么干凈

然而,我們可以:

function obj1() {
    this.name2 = "張三";
}
obj1.prototype.sayhi = function () {
    alert(this.name2);
}

function obj2() {
    obj1.call(this);//繼承屬性
}
//obj2.prototype = new obj1();
obj2.prototype = Object.create(obj1.prototype);//繼承原型中的方法【E5中才有的一種新的對象創建方式】
var obj = new obj2();

 

這是學習記錄,不是教程。文中錯誤難免,您可以指出錯誤,但請不要言辭刻薄。

原文鏈接:http://haojima.net/zhaopei/517.html

本文已同步至目錄索引:一步步學習javascript

歡迎上海“程序猿/媛”、"攻城獅"入群:【滬猿】229082941 入群須知

歡迎對個人博客感興趣的道友加入群:【嗨-博客】469075305 入群須知

如果您覺得文章對您有那么一點點幫助,那么麻煩您輕輕的點個贊,以資鼓勵。

 


免責聲明!

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



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