您好,登錄后才能下訂單哦!
這篇文章主要介紹了JavaScript原型繼承和原型鏈原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
在討論原型繼承之前,先回顧一下關于創建自定義類型的方式,這里推薦將構造函數和原型模式組合使用,通過構造函數來定義實例自己的屬性,再通過原型來定義公共的方法和屬性。
這樣一來,每個實例都有自己的實例屬性副本,又能共享同一個方法,這樣的好處就是可以極大的節省內存空間。同時還可以向構造函數傳遞參數,十分的方便。
這里還要再講一下兩種特色的構造函數模式:
1.寄生構造函數
從形式上來看,這種模式和工廠模式并無區別:
function Person(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function() { alert(this.name); }; return o; } var friend = new Person("Nicholas", 29, "Software Engineer"); friend.sayName(); //"Nicholas"
都是在內部創建一個Object對象實例,再賦予屬性和方法,最后返回,這種構造模式的好處是,對于原生的引用類型創建的實例,例如Array,想為實例創建新的方法或者屬性時,因為不建議直接修改原生的Array引用類型的構造函數,所以可以利用寄生構造函數:
function SpecialArray() { //創建數組 var values = new Array(); //添加值 values.push.apply(values, arguments); //添加方法 values.toPipedString = function() { return this.join("|"); }; //返回數組 return values; } var colors = new SpecialArray("red", "blue", "green"); alert(colors.toPipedString()); //"red|blue|green"
通過在內部創建一個Array實例,并添加新的方法,最后將這個實例返回,既沒有修改原生的Array構造函數,又成功添加了自定義的方法和屬性。
缺點:使用寄生構造函數有一個缺點,那就是返回的實例與構造函數或構造函數原型屬性之間沒有關系,與在構造函數外創建實例沒有區別,也無法通過instanceof來確定對象類型,因此有其他更好選擇的時候,不推薦使用該方法。
2.穩妥構造函數
穩妥構造函數與寄生構造函數類似,但是并不使用new和this(某些環境下禁止使用),前面的函數可以改寫成這樣:
function Person(name, age, job) { //創建要返回的對象 var o = new Object(); //可以在這里定義私有變量和函數 //添加方法 o.sayName = function() { alert(name); }; //返回對象 return o; }
說完這些,現在來談談原型繼承和原型鏈,所謂繼承,基本思想是利用原型讓一個引用類型繼承另一個引用類型的方法和屬性。每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。所有引用類型默認都繼承了Object,而這個繼承也是通過原型鏈實現的。大家要記住,所有函數的默認原型都是Object 的實例,因此默認原型都會包含一個內部指針,指向Object.prototype。
借用構造函數(即在子類型構造函數的內部調用超類型構造函數)
如果僅僅是借用構造函數,那么也將無法避免構造函數模式存在的問題——方法都在構造函數中定
義,因此函數復用就無從談起了。而且,在超類型的原型中定義的方法,對子類型而言也是不可見的,結
果所有類型都只能使用構造函數模式。考慮到這些問題,借用構造函數的技術也是很少單獨使用的。
組合繼承(思路是使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承)
function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { alert(this.name); }; function SubType(name, age) { //繼承屬性 SuperType.call(this, name); this.age = age; } //繼承方法 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function() { alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27
SuperType 構造函數定義了兩個屬性:name 和colors。SuperType 的原型定義了一個方法sayName()。SubType 構造函數在調用SuperType 構造函數時傳入了name 參數,緊接著又定義了它自己的屬性age。然后,將SuperType 的實例賦值給SubType 的原型,然后又在該新原型上定義了方法sayAge()。這樣一來,就可以讓兩個不同的SubType 實例既分別擁有自己屬性——包括colors 屬性,又可以使用相同的方法了。
原型式繼承(借助原型可以基于已有的對象創建新對象,同時還不必因此創建自定義類型)
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
在沒有必要興師動眾地創建構造函數,而只想讓一個對象與另一個對象保持類似的情況下,原型式
繼承是完全可以勝任的。不過別忘了,包含引用類型值的屬性始終都會共享相應的值,就像使用原型模
式一樣。
寄生式繼承(寄生式繼承的思路與寄生構造函數和工廠模式類似,即創建一個僅用于封裝繼承過程的函數,該函數在內部以某種方式來增強對象,最后再像真地是它做了所有工作一樣返回對象)
function createAnother(original) { var clone = object(original); //通過調用函數創建一個新對象 clone.sayHi = function() { //以某種方式來增強這個對象 alert("hi"); }; return clone; //返回這個對象 }
寄生組合式繼承
通過借用構造函數來繼承屬性,通過原型鏈的混成形式來繼承方法。其背后的基本思路是:不必為了指定子類型的原型而調用超類型的構造函數,我們所需要的無非就是超類型原型的一個副本而已。本質上,就是使用寄生式繼承來繼承超類型的原型,然后再將結果指定給子類型的原型。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。