javaScript中的繼承
函數預編譯的過程
函數預編譯發生在函數執行之前。
只有函數表達式纔可以執行,函數聲明並不可以執行。
預編譯時函數會生成自己的執行上下文。每個執行上下文都是獨一無二的,在函數執行完成時,銷燬執行上下文。
預編譯的過程可以分爲以下4個步驟:
- 形成了自己的執行上下文,變量提升,這時執行上下文裏存儲所有作用域中的變量。
- 若有形參,找出所有形式參數,存到形成的執行上下文裏中。若形參與變量名重複,則形參覆蓋變量。
- 給形參賦值;
- 函數提升,若函數內部還聲明瞭其它函數,則將函數存到執行上下文中;
Star.prototype = {};
var Star = function() {};
var star1 = new Star();
在javaScript 中我們通常並不會關心構造函數(Star)的原型指向誰(這裏Star的原型應指向Function.prototype)。
javascript 中並沒有真正的類,而我們所說的繼承可以理解成委託。
當new的時候發生了什麼
var Star = function() {};
var star1 = new Star();
new Star()時也會執行Star函數,所以會觸發預編譯過程。
Star函數在預編譯時,在自己的函數作用域裏聲明瞭一個變量this
,此時this的值爲{}。
函數都具有返回值所以在構造函數內部會 return this
。
當在構造函數中訪問原型上的屬性a時,實際上是將a屬性複製了一份,當你在構造函數中改變a屬性的值時,原型上a屬性的值並不會改變。
__proto__和constructor
所有的對象上都有_prop_屬性。
- 一般通過
Object.getPrototypeOf(對象)
來獲取一個對象的_prop_. - 一般通過
Object.prototype.isPrototypeOf(對象)
判斷當前對象是否在指定對象的原型鏈上。 - 一般通過
對象 instanof 函數
判斷函數的原型是否在對象的原型鏈上。在有iframe時不建議使用這種方法,因爲數組的指向會發生改變。
舉例:
//判斷一個值是否爲數組 現在可以用Array.isArray()判斷。
Array.prototype.isPrototypeOf(值);
//第二種法
[] instanceof Array
- 一般通過
Object.prototype.hasOwnProperty(屬性名)
判斷一個對象自身是否擁有某個屬性。
Object.create() 創建一個對象,該對象的_prop_屬性指向傳入該函數的參數。
所有的函數都有prototype屬性,值爲對象。
在每個對象的原型上都有兩個私有屬性_proto_
和constructor
,這兩個屬性會隨着原型鏈一直繼承。
_proto_
:會沿着原型鏈向上查找,一直找到原型鏈的最頂端。
constructor
:找到原型對應的構造函數。
繼承的幾種方式
function Origin () {
};
function Target () {
}
下文我們會用Origin
和Target
兩個函數做示例,默認 是Target
繼承Origin
。
1 傳統模式- 原型鏈繼承
function Father (name,age) {
this.name = name;
this.age = age;
}
Origin.propotype = new Father();
Target.propotype = new Origin();
var target = new target();
弊端:會繼承原型鏈上許多多餘的東西,比如我們只需要Target函數
2 通過call和apply的方式
function Target (){
Origin.call(this);//只在了new的時候纔有效
}
弊端:每 new
一次都會call
一次,都會多執行一次Origin
函數,造成性能與效率的浪費。並且不能繼承原型,繼承的是構造函數。
3 共享模式
Target.propotype = Origin.propotype;
弊端:修改Target.propotype
的同時也會修改Origin.propotype
。
所有的_prop_
最終都會指向Object.propotype
,因此Target.propotype
和Origin.propotype
都是引用值。
比如:
Target.propotype.lastName = 'li';
此時打印Origin.propotype.lastName的值:
console.log(Origin.propotype.lastName);
返回值爲:
"li"
通常在我們開發過程中較爲理想的狀態是:Origin的原型 可以繼承 Target原型 的方法和屬性,並且修改其中任何一個原型都不會互相影響,即上述操作的理想狀態爲: console.log(Origin.propotype.lastName)
,返回值爲‘undefined’
。
因爲,我們構建了新的模式 聖盃模式 。
聖盃模式
var Star = function (){
}
var inhreit = (function (Target,Origin){
var F = function(){} ;
retutn function () {
F.prototype = Origin.propotype;
Target.prototype = new F();
Target.prototype.constructor = Target;
}
})
var obj = {};
Object.creat(obj);
Object.creat()方法將會創建一個對象,這個對象的prototype指向傳入的參數(這裏是
obj);
工廠模式
每個構造函數都相當於一個工廠,所有通過 new
被創造的對象都擁有其構造函數的方法和屬性,我們又可以對每個對象擴展新的屬性,並且這些對象之間並不會互相影響,這樣就加工出許多相同的部件。當需要創造多個相同的實例時,可以利用構造函數的特性來開發。這種開發模式被稱爲“工廠模式”。