代碼複用一直是咱們程序員所追求的遠大目標,畢竟能夠少寫點代碼,何樂而不爲呢?當說到代碼複用的時候,最早想到的是繼承,JavaScript對象上有本身的屬性,也有一些屬性是從原型對象繼承來的,下面咱們來看看實現繼承的幾種方式: 程序員
1.傳統模式——原型鏈閉包
用原型鏈的繼承時,不只繼承了自身的屬性,並且繼承了原型上的屬性和方法。函數
看代碼:this
//原型鏈繼承 Grand.prototype.lastName = '魯班大師'; function Grand(){ //隱式連接 __proto__ } var grand = new Grand(); Father.prototype = grand; function Father(){ this.name = '小魯班'; //隱式連接 __proto__ } var father = new Father(); Son.prototype = father; function Son(){ this.age = 18 //隱式連接 __proto__ } var son = new Son();
咱們先看下這種繼承模式的工做原理: spa
當咱們用new Son()來建立一個對象時,會建立一個圖(3),裏面保存了age的屬性,若是咱們訪問lastName屬性時,儘管圖(3)並無lastName這個屬性,可是經過Son()的prototype原型上的隱式屬性__proto__能夠訪問到圖(1)中的lastName屬性,這個__proto__就是原型鏈,這個屬性只能系統內部去使用,開發者是用不了的。 prototype
咱們還要注意一點:若是咱們在控制檯執行,son.name = "長江七號",這個操做並不會改變圖(2)裏面的name,它會直接在圖(3)自身上建立一個屬性,以下: 代理
原型鏈繼承弊端:缺點在於同時繼承了兩個對象的屬性,可是在大多的時候咱們並不須要這些屬性,由於它們頗有可能指向一個特定的實例,而不是複用。 指針
2.繼承——借用構造函數 code
借用構造函數的方式並不能徹底說是繼承,就是借別人的方法來用下。對象
function Person(name,age,sex){ this.name = name; this.age = age; this.sex = sex; } function Student(name,age,sex,grande){ Person.call(this,name,age,sex); this.grande = grande; } var student = new Student();
當咱們要構造一個Student的構造函數,可是Person已經實現了咱們部分功能,因此在Student中咱們能夠用Person中的方法就能夠了。
借用構造函數弊端:
1.只能繼承在構造函數中的方法,卻不能繼承那些添加到原型中的方法。
2. 這種繼承每次要多調用一個函數,只是在視覺上省代碼,實際運行上還浪費效率了。
3.共享原型
共享原型的方式簡單粗暴,讓一個原型同時給兩個函數,從而達到繼承的目的,如圖:
看代碼:
Father.prototype.name = "長江七號" function Father(){ var age = 18; } function Son(){ var sex = 'famle'; } function inherit(Target,Origin){ Target.prototype = Origin.prototype; } inherit(Son,Father) var son = new Son(); var father = new Father();
儘管這兩個對象共享了同一個原型,訪問值的時候也很快,可是,這同時也是一個缺點,若是子對象修改了一個原型的屬性,那麼它會影響全部的祖對象。咱們沒有辦法給子對象的原型單獨給加屬性。
屬性修改過程:
以上三種繼承方法多多少少都存在點缺點,接着引出第四種完美方法:
4.聖盃模式
聖盃模式其實跟共享原型的思路差很少,它是經過剪斷父對象跟子對象的原型之間的直接關係,從而解決共享原型這一方法產生的問題,同時還能夠繼續共享原型上的屬性,但改變子對象上原型的屬性時,祖對象原型不受影響。如圖所示:
看代碼:
Father.prototype.name = "長江七號" function Father(){ var age = 18; } function Son(){ var sex = 'famle'; } function inherit(Target,Origin){ function F(){};//中間空白函數代理 F.prototype = Origin.prototype; Target.prototype = new F(); } inherit(Son,Father) var son = new Son(); var father = new Father();
⚠️注意:寫聖盃模式的時候,咱們必定要注意第10行和第11行,它們兩個位置必定不能寫反,必須在new以前改變原型指向,不然原型指向就指原來的自身原型上。**
接下來咱們試試看好很差使:
ok!好使沒問題,咱們的目的達到了,幾乎完美了,可是還差點,咱們還須要重置構造函數指針,,若是不重置,那麼全部子對象都會顯示Father()是它們的構造函數,形成指向紊亂了,因此咱們能夠歸下位:
Father.prototype.name = "長江七號" function Father(){ var age = 18; } function Son(){ var sex = 'famle'; } function inherit(Target,Origin){ function F(){}; F.prototype = Origin.prototype; Target.prototype = new F(); Target.prototype.constructor = Target;//重置構造函數 } inherit(Son,Father) var son = new Son(); var father = new Father();
若是有一天咱們想知道這個子對象真正繼承自誰,在上面的基礎上,咱們還能夠添加一個指向原始父對象的引用,加一個屬性uber,原本是能夠用"super"的,可是因爲super是保留字的關鍵字。改進後以下代碼:
Father.prototype.name = "長江七號" function Father(){ var age = 18; } function Son(){ var sex = 'famle'; } function inherit(Target,Origin){ function F(){}; F.prototype = Origin.prototype; Target.prototype = new F(); Target.prototype.constructor = Target;//重置構造函數 Target.prototype.uber = Origin.prototype;//訪問超類,真正繼承於誰 } inherit(Son,Father) var son = new Son(); var father = new Father();
其實還有一種寫法,在雅虎的YUI庫裏面有個inherit方法:利用閉包的私有化屬性
Father.prototype.name = "長江七號" function Father(){ var age = 18; } function Son(){ var sex = 'famle'; } // function inherit(Target,Origin){ // function F(){}; // F.prototype = Origin.prototype; // Target.prototype = new F(); // Target.prototype.constructor = Target; // } var inherit = (function(){ var F = function(){}; return function (Target,Origin){ F.prototype = Origin.prototype; Target.prototype = new F(); Target.prototype.constructor = Target; } }()); inherit(Son,Father) var son = new Son(); var father = new Father();
『 好啦,以上呢就給你們的分享啦,若是您以爲本篇內容有幫助到你,能夠轉載但記得要關注,要標明原文哦,謝謝支持~』
「歡迎各位大佬關注我,掃二維碼便可」