摘要:繼承原型鏈原型鏈是實現繼承的主要方法。臨時的構造函數將傳入的對象作為這個構造函數的原型返回新實例以為原型創建一個新實例不僅屬于所有,而且也會被共享。上訴例子只調用了一次構造函數,因此避免了在上面創建不必要的多余的屬性。
繼承 1 原型鏈
原型鏈是實現繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。
構造函數、原型和實例的關系
每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; } function SubType(){ this.subproperty = false; } // 使SubType繼承SuperType的實例 本質是重寫原型對象 SubType.prototype = new SuperType(); SubType.prototype.getValue = function(){ return this.subproperty; } var instance = new SubType(); alert(instance.getSuperValue());//true
注意 :==instance.constructor現在指向的是SuperType,這是因為原來SubType的原型指向了另一個對象--SuperType的原型,而這個原型對象的constructor屬性指向的是SuperType==
默認的原型 Objec
確定原型與實例的關系
instanceof:用這個操作符來測試實例與原型鏈中出現過的構造函數,結果就會返回true
alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true
使用isPrototypeOf()方法,只要是原型鏈中出現的原型,都可以說是該原型鏈所派生的實例的原型
alert(Object.prototype isPrototypeOf(instance)); //true alert(SuperType.prototype isPrototypeOf(instance)); alert(SubType.prototype isPrototypeOf(instance));
通過原型鏈實現繼承時,不能使用對象字面量創建原型方法,因為這樣會重寫原型鏈
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; } function SubType(){ this.subproperty = false; } SubType.prototype = new SuperType(); // 使用對象字面量創建原型方法,會重寫原型鏈 SubType.prototype = { getSubValue: function(){ return this.subproperty; } }; var instance = new SubType(); alert(instance.getSuperValue());//error!!
原型鏈的問題
包含引用類型值的原型屬性會被所有實例共享,因此在構造函數中定義屬性而不是在原型對象中定義屬性。
在通過原型來實現繼承時,原型實際上會變成另一個類型的實例。于是,原先的實例屬性也就變成了現在的原型屬性了。
function SuperType(){ this.color = ["red", "blue", "green"]; this.name = "Nicholas"; } function SubType(){ } SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red","blue","green","black" instance1.name = "Tom"; alert(instance1.name); //"Tom" var instance2 = new SubType(); // colors是引用類型,會被所有實例共享 alert(instance2.colors); // "red","blue","green","black" alert(instance2.name); //"Nicholas"
沒有辦法在不影響所有對象實例的情況下,給超類型的構造函數傳遞參數
function SuperType(name){ this.name = name; } function SubType(name){ } // ??如何向超類型的構造函數傳遞參數?? SubType.prototype = new SuperType(); var instance2 = new SubType(name);
綜合以上情況,實踐中很少會多帶帶使用原型鏈
2.借用構造函數在子類型構造函數的內部調用超類型構造函數。
function SuperType(){ this.colors=["red","blue","green"]; } function SubType(){ // superType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); // "red,blue,green,black" bar instance2 = new SubType(); alert(instance2.colors); //"red,blue,green"
使用這種方式,可以在子類型構造函數中向超類型構造函數傳遞參數。
function SuperType(name){ this.name=name; } function SubType(){ Super.call(this,"Nicholas"); this.age = 29; } var instance = new SubType(); alert(instance.name);//"Nicholas"
相關問題:
會出現與構造函數模式相同的問題——方法都在構造函數中定義,函數復用就無從談起了。而且在超類型的原型中定義的方法,對于子類型也是不可見的。
3.組合繼承——JavaScrip最常用的繼承模式使用原型鏈實現對原型屬性和方法的繼承(通過在原型上定義方法實現了函數復用),通過借用構造函數來實現對實例屬性的繼承(保證每個實例都有自己的屬性,而且可以向超類的構造函數傳遞參數)。
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.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas",29); instance1.sayName(); //"Nicholas" instance1.sayAge(); //29 var instance2 = new SubType("Greg",27); instance2.sayName(); //"Greg" instance2.sayAge(); //274 原型式繼承
借助原型可以基于已有的對象創建新對象,同時還不必因此創建自定義類型。
function object(o){ // 臨時的構造函數 function F(){} // 將傳入的對象作為這個構造函數的原型 F.prototype = o; // 返回新實例 return new F(); } var person = { name:"Nicholas", friends:["shelby","court","van"] }; // 以person為原型創建一個新實例 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"
person.friends不僅屬于person所有,而且也會被anotherPerson、yetAnotherPerson共享。
ECMAScript5通過Object.create()方法規范化了原型式繼承,這個方法接受兩個參數:一個用作新對象原型的對象,另一個(可選的)為新對象定義額外屬性的對象。只傳入一個參數的情況,和object()方法相同。
// Object.create()只用一個參數 var person = { name: "Nicholas", friends: ["shelby","court","van"] }; var anotherPerson = Object.create(person);
// Object.create()用兩個參數 var person = { name: "Nicholas", friends: ["shelby","court","van"] }; var anotherPerson = Object.create(person, { name:{ value: "Greg" } }); alert(anotherPerson.name); //Greg var yetAnotherPerson = Object.create(person, { // 為新對象定義新的friends屬性,該屬性會覆蓋原型屬性中的friends friends:{ value: ["1","2","3"] } }); alert(yetAnotherPerson.friends); //"1,2,3" // 但是原型對象中的friends屬性仍然被共享 alert(person.friends); //"shelby,court,van" alert(anothorPerson.friends); //"shelby,court,van"5.寄生式繼承
function createAnother(original){ var clone = object(original); // 不能做到函數復用,導致效率降低 // 添加新函數,增強對象 clone.sayHi = function(){ alert("hi"); } return clone; } var person = { name: "Nicholas", friends: ["shelby","court","van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"6.寄生組合式繼承
組合繼承無論在什么情況下,都會調用兩次超類型構造函數:一次是在創建子類型原型的時候,另一次是在子類型構造函數內部。
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); //第二次調用SuperType() this.age=age; } SubType.prototype = new SuperType(); //第一次調用SuperType()
第一次調用SuperType構造函數時,SubType.prototype(SubType的原型)會得到兩個屬性:name和colors。當調用SubType構造函數時,又會調用一次SuperType構造函數,這一次又在新對象上創建了實例屬性name和colors。于是這兩個屬性就屏蔽了原型中的兩個同名屬性
為了避免創建兩次相同的屬性,解決方法——寄生組合式繼承。其基本思想是:不必為了指定子類型的原型而調用超類型的構造函數,我們所需要的無非就是超類型原型的一個副本而已。
function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = 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; } inheritPrototype(SubType,SuperType); SubType.prototype.sayAge=function(){ alert(this.age); };
上訴例子只調用了一次SuperType構造函數,因此避免了在SubType.prototype上面創建不必要的、多余的屬性。寄生組合式繼承是引用類型最理想的繼承范式
相比之下,第二段程序少了SubType.prototype = new SuperType();這使得SubType.prototype中沒有了name和colors屬性,實現了避免了在SubType.prototype上面創建不必要的、多余的屬性的目的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83389.html
摘要:這種方法也存在這樣的問題如果修改了構造函數的原型對象,之前創建的對象無法通過這種方式來確定類型修改構造函數的原型對象會導致之前創建的對象無法通過這種方式判斷類型判斷對象繼承自哪些父類型使用使用 判斷對象類型的方法 使用原型對象上的constructor屬性來判斷 每個對象的原型上都有一個constructor屬性,指向了其構造函數 注意:對象沒有constructor屬性(除非自己添加...
摘要:原型對象的問題省略了為構造函數傳遞參數,導致了所有實例在默認情況下都取得相同的屬性值。即使有其他代碼會給這個對象添加方法或數據成員,但也不可能有別的方法訪問傳入到構造函數中的原始數據。 創建對象 1.Object構造函數 創建一個Object的實例,然為其添加屬性和方法(早期創建對象的模式) var person = new Object(); person.name = Nichol...
摘要:私有變量任何在函數中定義的變量,都可以認為是私有變量,因為在不能再函數的外部訪問這些變量。我們把有權訪問私有變量和私有函數的公有方法稱為特權方法。模塊模式模塊模式是為單例創建私有變量和特權方法。 私有變量 任何在函數中定義的變量,都可以認為是私有變量,因為在不能再函數的外部訪問這些變量。私有變量包括函數的參數、函數中定義的變量和函數。我們把有權訪問私有變量和私有函數的公有方法稱為特權方...
摘要:這是因為子類沒有自己的對象,而是繼承父類的對象,然后對其進行加工。 溫馨提示:作者的爬坑記錄,對你等大神完全沒有價值,別在我這浪費生命溫馨提示-續:你們要非得看,我也攔不住,但是至少得準備個支持ES6的Chrome瀏覽器吧?溫馨提示-再續:ES6簡直了,放著不用簡直令人發指! 書接上回,即便是程序員,也還是能夠通過自己的努力辛辛苦苦找到合適對象的,見前文《javascript對象不完全...
摘要:對象重新認識面向對象面向對象從設計模式上看,對象是計算機抽象現實世界的一種方式。除了字面式聲明方式之外,允許通過構造器創建對象。每個構造器實際上是一個函數對象該函數對象含有一個屬性用于實現基于原型的繼承和共享屬性。 title: JS對象(1)重新認識面向對象 date: 2016-10-05 tags: JavaScript 0x00 面向對象 從設計模式上看,對象是...
閱讀 3769·2021-09-02 09:53
閱讀 2749·2021-07-30 14:57
閱讀 3492·2019-08-30 13:09
閱讀 1179·2019-08-29 13:25
閱讀 810·2019-08-29 12:28
閱讀 1453·2019-08-29 12:26
閱讀 1129·2019-08-28 17:58
閱讀 3305·2019-08-26 13:28