您好,登錄后才能下訂單哦!
今天小編給大家分享一下javascript繼承的作用是什么的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
javascript繼承的作用:1、可以不調用“父類”的構造方法就創造新的實例;2、修改“父類”的prototype可以動態修改所有已經創造的實例;3、可以動態修改一個對象的原型。
本教程操作環境:windows7系統、javascript1.8.5版、Dell G3電腦。
JavaScript 是以對象為基礎,以函數為模型,以原型為繼承的面向對象開發模式。
javascript繼承的作用:
1、可以不調用「父類」的構造方法就創造新的實例
2、修改「父類」的 prototype 可以動態修改所有已經創造的實例
3、可以動態修改一個對象的原型,也就是說爹媽可以換
下面介紹定義 JavaScript 類型的方法,以及實現繼承的常用模式。
構造原型
直接使用 prototype 原型設計類的繼承存在兩個問題。
由于構造函數事先聲明,而原型屬性在類結構聲明之后才被定義,因此無法通過構造函數向原型動態傳遞參數。這樣實例化對象都是一個模樣,沒有個性。要改變原型屬性值,則所有實例都會受到干擾。
當原型屬性的值為引用類型數據時,如果在一個對象實例中修改該屬性值,將會影響所有的實例。
示例1
簡單定義 Book 類型,然后實例化。
function Book () {}; //聲明構造函數 Book.prototype.o = {x : 1, y : 2}; //構造函數的原型屬性o是一個對象 var book1 = new Book (); //實例化對象book1 var book2 = new Book (); //實例化對象book2 console.log(book1.o.x); //返回1 console.log(book2.o.x); //返回1 book2.o.x = 3; //修改實例化對象book2中的屬性x的值 console.log(book1.o.x); //返回3 console.log(book2.o.x); //返回3
由于原型屬性 o 為一個引用型的值,所以所有實例的屬性 o 的值都是同一個對象的引用,一旦 o 的值發生變化,將會影響所有實例。
構造原型正是為了解決原型模式而誕生的一種混合設計模式,它把構造函數模式與原型模式混合使用,從而避免了上述問題的發生。
實現方法:對于可能會相互影響的原型屬性,并且希望動態傳遞參數的屬性,可以把它們獨立出來使用構造函數模式進行設計。對于不需要個性設計、具有共性的方法或屬性,則可以使用原型模式來設計。
示例2
遵循上述設計原則,把其中兩個屬性設計為構造函數模式,設計方法為原型模式。
function Book (title, pages) { //構造函數模式設計 this.title = title; this.pages = pages; } Book.prototype.what = function () { //原型模式設計 console.log(this.title + this.pages); }; var book1 = new Book("JavaScript 程序設計", 160); var book2 = new Book("C語言程序設計", 240); console.log(book1.title); console.log(book2.title);
構造原型模式是 ECMAScript 定義類的推薦標準。一般建議使用構造函數模式定義所有屬性,使用原型模式定義所有方法。這樣所有方法都只創建一次,而每個實例都能夠根據需要設置屬性值。這也是使用最廣的一種設計模式。
動態原型
根據面向對象的設計原則,類型的所有成員應該都被封裝在類結構體內。例如:
function Book (title, pages) { //構造函數模式設計 this.title = title; this.pages = pages; Book.prototype.what = function () { //原型模式設計,位于類的內部 console.log(this.title + this.pages); }; }
但當每次實例化時,類 Book 中包含的原型方法就會被重復創建,生成大量的原型方法,浪費系統資源。可以使用 if 判斷原型方法是否存在,如果存在就不再創建該方法,否則就創建方法。
function Book (title, pages) { this.title = title; this.pages = pages; if (typeof Book.isLock == "undefined") { //創建原型方法的鎖,如果不存在則創建 Book.prototype.what = function () { console.log(this.title + this.pages); }; Book.isLock = true; //創建原型方法后,把鎖鎖上,避免重復創建 } } var book1 = new Book("JavaScript 程序設計", 160); var book2 = new Book("C語言程序設計", 240); console.log(book1.title); console.log(book2.title);
typeof Book.isLock 表達式能夠檢測該屬性值的類型,如果返回為 undefined 字符串,則不存在該屬性值,說明沒有創建原型方法,并允許創建原型方法,設置該屬性的值為 true,這樣就不用重復創建原型方法。這里使用類名 Book,而沒有使用 this,這是因為原型是屬于類本身的,而不是對象實例的。
動態原型模式與構造原型模式在性能上是等價的,用戶可以自由選擇,不過構造原型模式應用比較廣泛。
工廠模式
工廠模式是定義類型的最基本方法,也是 JavaScript 最常用的一種開發模式。它把對象實例化簡單封裝在一個函數中,然后通過調用函數,實現快速、批量生產實例對象。
示例1
下面示例設計一個 Car 類型:包含汽車顏色、驅動輪數、百公里油耗 3 個屬性,同時定義一個方法,用來顯示汽車顏色。
function Car (color, drive, oil) { //汽車類 var _car = new Object(); //臨時對象 _car.color = color; //初始化顏色 _car.drive = drive; //初始化驅動輪數 _car.oil = oil; //初始化百公里油耗 _car.showColor = function () { //方法,提示汽車顏色 console.log(this.color); }; return _car; //返回實例 } var car1 = Car("red", 4, 8); var car2 = Car("blue", 2, 6); car1.showColor(); //輸出“red” car2.showColor(); //輸出“blue”
上面代碼是一個簡單的工廠模式類型,使用 Car 類可以快速創建多個汽車實例,它們的結構相同,但是屬性不同,可以初始化不同的顏色、驅動輪數和百公里油耗。
示例2
在類型中,方法就是一種行為或操作,它能夠根據初始化參數完成特定任務,具有共性。因此,可以考慮把方法置于 Car() 函數外面,避免每次實例化時都要創建一次函數,讓每個實例共享同一個函數。
function showColor () { //公共方法,提示汽車顏色 console.log(this.color); }; function Car (color, drive, oil) { //汽車類 var _car = new Object(); //臨時對象 _car.color = color; //初始化顏色 _car.drive = drive; //初始化驅動輪數 _car.oil = oil; //初始化百公里油耗 _car.showColor = showColor; //引用外部函數 return _car; //返回實例 }
在上面這段重寫的代碼中,在函數 Car() 之前定義了函數 showColor()。在 Car() 內部,通過引用外部 showColor() 函數,避免了每次實例化時都要創建一個新的函數。從功能上講,這樣解決了重復創建函數的問題;但是從語義上講,該函數不太像是對象的方法。
類繼承
類繼承的設計方法:在子類中調用父類構造函數。
在 JavaScript 中實現類繼承,需要注意以下 3 個技術問題。
在子類中,使用 apply 調用父類,把子類構造函數的參數傳遞給父類父類構造函數。讓子類繼承父類的私有屬性,即 Parent.apply(this, arguments); 代碼行。
在父類和子類之間建立原型鏈,即 Sub.prototype = new Parent(); 代碼行。通過這種方式保證父類和子類是原型鏈上的上下級關系,即子類的 prototype 指向父類的一個實例。
恢復子類的原型對象的構造函數,即 Sub.prototype.constructor=Sub;語句行。當改動 prototype 原型時,就會破壞原來的 constructor 指針,所以必須重置 constructor。
示例1
下面示例演示了一個三重繼承的案例,包括基類、父類和子類,它們逐級繼承。
//基類Base function Base (x) { //構造函數Base this.get = function () { //私有方法,獲取參數值 return x; } } Base.prototype.has = function () { //原型方法,判斷get()方法返回值是否為0 return ! (this.get() == 0); } //父類Parent function Parent () { //構造函數Parent var a = []; //私有數組a a = Array.apply(a, arguments); //把參數轉換為數組 Base.call(this, a.length); //調用Base類,并把參數數組長度傳遞給它 this.add = function () { //私有方法,把參數數組補加到數組a中并返回 return a.push.apply(a, arguments) } this.geta = function () { //私有方法,返回數組a return a; } } Parent.prototype = new Base(); //設置Parent原型為Base的實例,建立原型鏈 Parent.prototype.constructor = Parent; //恢復Parent類原型對象的構造器 Parent.prototype.str = function (){ //原型方法,把數組轉換為字符串并返回 return this.geta().toString(); } //子類Sub function Sub () { //構造函數 Parent.apply(this, arguments); //調用Parent類,并把參數數組長度傳遞給它 this.sort = function () { //私有方法,以字符順序對數組進行排序 var a = this.geta(); //獲取數組的值 a.sort.apply(a, arguments); //調用數組排序方法 sort()對數組進行排序 } } Sub.prototype = new Parent(); //設置Sub原型為Parent實例,建立原型鏈 Sub.prototype.constructor = Sub; //恢復Sub類原型對象的構造器 //父類Parent的實例繼承類Base的成員 var parent = new Parent (1, 2, 3, 4); //實例化Parent類 console.log(parent.get()); //返回4,調用Base類的方法get() console.log(parent.has()); //返回true,調用Base類的方法has() //子類Sub的實例繼承類Parent和類Base的成員 var sub = new Sub (30, 10, 20, 40); //實例化Sub類 sub.add(6, 5); //調用Parent類方法add(),補加數組 console.log(sub.geta()); //返回數組30,10,20,40,6,5 sub.sort(); //排序數組 console.log(sub.geta()); //返回數組10,20,30,40,5,6 console.log(sub.get()); //返回4,調用Base類的方法get() console.log(sub.has()); //返回true,調用Base類的方法has() console.log(sub.str()); //返回10,20,30,40,5,6
【設計思路】
設計子類 Sub 繼承父類 Parent,而父類 Parent 又繼承基類 Base。Base、Parent、Sub 三個類之間的繼承關系是通過在子類中調用的構造函數來維護的。
例如,在 Sub 類中,Parent.apply(this, arguments); 能夠在子類中調用父類,并把子類的參數傳遞給父類,從而使子類擁有父類的所有屬性。
同理,在父類中,Base.call(this, a.length); 把父類的參數長度作為值傳遞給基類,并進行調用,從而實現父類擁有基類的所有成員。
從繼承關系上看,父類繼承了基類的私有方法 get(),為了確保能夠繼承基類的原型方法,還需要為它們建立原型鏈,從而實現原型對象的繼承關系,方法是添加語句行 Parent.prototype=new Base();。
同理,在子類中添加語句 Sub.prototype=new Parent();,這樣通過原型鏈就可以把基類、父類和子類串連在一起,從而實現子類能夠繼承父類屬性,還可以繼承基類的屬性。
示例2
下面嘗試把類繼承模式封裝起來,以便規范代碼應用。
function extend (Sub, Sup) { //類繼承封裝函數 var F = function () {}; //定義一個空函數 F.prototype = Sup.prototype; //設置空函數的原型為父類的原型 Sub.prototype = new F (); //實例化空函數,并把父類原型引用傳給給子類 Sub.prototype.constructor = Sub; //恢復子類原型的構造器為子類自身 Sub.sup = Sup.prototype; //在子類定義一個私有屬性存儲父類原型 //檢測父類原型構造器是否為自身 if (Sup.prototype.constructor == Object.prototype.constructor) { Sup.prototype.constructor = Sup; //類繼承封裝函數 } }
【操作步驟】
1) 定義一個封裝函數。設計入口為子類和父類對象,函數功能是子類能夠繼承父類的所有原型成員,不涉及出口。
function extend (Sub, Sup) { //類繼承封裝函數 //其中參數Sub表示子類,Sup表示父類 }
2) 在函數體內,首先定義一個空函數 F,用來實現功能中轉。設計它的原型為父類的原型,然后把空函數的實例傳遞給子類的原型,這樣就避免了直接實例化父類可能帶來的系統負荷。因為在實際開發中,父類的規模可能會很大,如果實例化,會占用大量內存。
3) 恢復子類原型的構造器為子類自己。同時,檢測父類原型構造器是否與 Object 的原型構造器發生耦合。如果是,則恢復它的構造器為父類自身。
下面定義兩個類,嘗試把它們綁定為繼承關系。
function A (x) { //構造函數A this.x = x; //私有屬性x this.get = function () { //私有方法get() return this.x; } } A.prototype.add = function () { //原型方法add() return this.x + this.x; } A.prototype.mul = function () { //原型方法mul() return this.x * this.x; } function B (x) { //構造函數B A.call (this.x); //在函數體內調用構造函數A,實現內部數據綁定 } extend (B, A); //調用封裝函數,把A和B的原型捆綁在一起 var f = new B (5); //實例化類B console.log(f.get()); //繼承類A的方法get(),返回5 console.log(f.add()); //繼承類A的方法add(),返回10 console.log(f.mul()); //繼承類A的方法mul(),返回25
在函數類封裝函數中,有這么一句 Sub.sup=Sup.prototype;,在上面代碼中沒有被利用,那么它有什么作用呢?為了解答這個問題,先看下面的代碼。
extend (B, A); B.prototype.add = function () { //為B類定義一個原型方法 return this.x + "" + this.x; }
上面的代碼是在調用封裝函數之后,再為 B 類定義了一個原型方法,該方法名與基類中原型方法 add() 同名,但是功能不同。如果此時測試程序,會發現子類 B 定義的原型方法 add() 將會覆蓋父類 A 的原型方法 add()。
console.log(f.add()); //返回字符串55,而不是數值10
如果在 B 類的原型方法 add() 中調用父類的原型方法 add(),避免代碼耦合現象發生。
B.prototype.add = function () { //定義子類B的原型方法add() return B.sup.add.call(this); //在函數內部調用父類方法add() }
以上就是“javascript繼承的作用是什么”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。