深入理解JavaScript的原型 原型鏈與繼承

2021-08-25 14:28:20 字數 4317 閱讀 4845

在介紹原型是什麼之前,首先需要知道原型是做什麼用的,在js高設書中,明顯可以看到介紹有關原型的知識是在介紹建立物件的方式時提出來的,即使用原型模式來建立物件,顯而易見,原型這個概念是與建立物件聯絡在一起的。當然,建立物件的方式有很多種,如工廠模式,建構函式模式,以及與原型模式有關的其他模式等。

我們建立的每乙個函式都有乙個prototype屬性,這個屬性是乙個指標,指向乙個物件,而這個物件的用途是包含可以由特定型別的所有例項共享的屬性和方法。簡單的說,prototype就是通過呼叫建構函式而建立的那個物件例項的原型物件。使用原型物件的好處是可以讓所有物件例項共享它所包含的屬性和方法。換句話說,就是不必在建構函式中定義物件例項的資訊,而是可以將這些資訊直接新增到原型物件中,如下面例子所示:

function person () 

person.prototype.name="jack";

person.prototype.age=20;

person.prototype.sayname = function () ;

var person1=new person();

person1.sayname(); //"jack"

var person2=new person();

person2.sayname(); //"jack"

alert(person1.sayname==person2.sayname);//true

在這個例子中,我們將sayname( )方法和屬性直接新增到了person的prototype屬性中,建構函式變成了空函式。即使如此,也仍然可以通過呼叫建構函式來建立新物件,而且新物件還會具有相同的屬性和方法。但與建構函式不同的是,新物件的這些屬性和方法是由所有例項共享的。換句話說,person1和person2訪問的都是同一組屬性和乙個sayname( )函式,要理解原型模式的工作原理,必須先理解ecsmscript中原型物件的性質。

無論什麼時候,只要建立了乙個新的函式,就會根據一種特定的規則為該函式建立乙個prototype屬性,這個屬性指向函式的原型物件。在預設的情況下,所有的原型物件都會自動獲得乙個叫做constructor的屬性,也是乙個指標,指向擁有prototype屬性的函式,即建構函式person。在上面的例子中,即person.prototype.constructor指向建構函式person。

建立了自定義的建構函式之後,其原型物件預設只會取得constructor屬性,至於其他方法,則都是從object上繼承而來的。當呼叫建構函式建立乙個新例項之後,該例項的內部將包含乙個指標_proto_,這個指標指向建構函式的原型物件。

以前面**為例,下圖展示了它們之間的關係

可以看出,雖然person的每個例項——person1和person2都不包含屬性和方法,但是仍然可以呼叫person1.sayname( ),這是通過查詢物件屬性的過程來實現的。

注意:當為物件例項新增乙個屬性時,這個屬性就會遮蔽原型物件中的同名屬性。換句話說,為例項新增乙個同名屬性,只會阻止我們訪問原型中的那個屬性,但不會修改那個屬性,即使將這個屬性設為null,也只會在例項中設定這個屬性,而不會恢復其指向原型的連線。不過,使用delete操作符則可以完全刪除例項屬性,從而讓我們能夠重新訪問原型中的屬性。

由於在原型中查詢值的過程是一次搜尋,因此我們對原型物件所做的任何修改都能夠立即從例項上反映出來——即使是先建立了例項後修改原型也是照樣如此。

function person ( )

var friend = new person();

person.prototype.sayhi= function () ;

friend.sayhi(); //"hi"

儘管可以隨時為原型新增屬性和方法,並且修改能夠立即在所有物件例項中反映出來,但如果是重寫整個原型物件,那麼情況就不一樣了。我們知道,呼叫建構函式時會為例項新增乙個指向最初原型的指標_proto_,而把原型修改給另外乙個物件就等於切斷了建構函式與最初原型之間的聯絡。

function person()

var person=new person();

person.prototype=

};console.log(person.name);//undefined

person.sayname();//報錯

由此可知,重寫原型物件切斷了現有原型與任何之前已經存在的物件例項之間的聯絡,它們引用的仍然是最初的原型。

原型模式也不是沒有缺點。首先,它省略了為建構函式傳遞初始化引數這一環節,結果所有例項在預設情況下都將取得相同的屬性值。雖然這會在某種程度上帶來一些不便,但這不是原型最大的問題。

原型中的所有屬性是被很多例項共享的,這種共享對於函式非常適合。對於那些包含基本型別的屬性高倒也說得過去,畢竟我們可以通過在例項上新增乙個同名屬性,可以隱藏原型中的對應屬性。然而,對於引用型別值的屬性來說,問題就非常突出。

因為引用型別在引用時候並不是建立乙個副本,而是通過指標直接在原值上操作,如果通過在某個例項上修改了原型物件的某個屬性,就相當於這個屬性本身被修改了,在其他原型訪問這個屬性時,已經是修改後的屬性了,顯然這帶來了很多麻煩。

因此,實際上很少有人單獨使用原型模式來建立物件。一般都會採取組合使用原型模式和建構函式模式,寄生建構函式模式,穩妥都在函式模式等來建立物件。

原型與 in 操作符 :in操作在單獨使用時,如果物件能夠訪問給定的屬性,就會返回true,無論該屬性存在於例項中還是原型中。 

console.log("name" in person1);
組合使用in操作符和hasownproperty( ),就可以確定該屬性到底存在於物件中還是存在於原型中。

function hasprototypeproperty(object,name)
其基本思想是利用原型讓乙個引用型別繼承另乙個引用型別的屬性和方法。

由建構函式,原型,例項的關係可知,每個建構函式都有乙個原型物件,原型物件都包含乙個指向建構函式的指標,呼叫建構函式例項化的物件例項,都乙個可以指向原型物件的內部指標。那麼假如我們讓原型物件等於另乙個型別的例項,結果會怎麼樣呢?顯然,此時的原型物件都將包含乙個指向另乙個原型的指標,相應的,另乙個原型中也包含著乙個指向另乙個建構函式的指標。假如另乙個原型又是另一種型別的例項,那麼上述關係依然成立,如此層層遞進,就構成了例項與原型的鏈條。而互相關聯的原型組成的鏈狀結構,就是所謂的原型鏈。

function supertype ( )

supertype.prototype.getsupervalue = function () ;

function subtype ( );

subtype.prototype = new supertype(); //實現了subtype繼承supertype

subtype.prototype.getsubvalue = function () ;

var instance=new subtype();

alert(instance.getsupervalue()); //true

在這個例子中,建立了兩個建構函式supertype( )和subtype( );給每個建構函式新增了屬性,利用原型物件新增屬性的方法為其新增了兩個不同的方法。然後subtype.prototype = new supertype(),這行**實現了繼承,其原理是subtype.prototype作為建構函式supertype()的乙個例項,所以subtype.prototype就可以訪問到supertype.prototype的屬性和方法,然後再將乙個新的變數instance作為建構函式subtype( )的例項物件,所以instance可以訪問到其建構函式原型subtype.prototype的屬性和方法。如果要在instance上訪問名為getsupervalue()的方法,會執行三次搜尋,如下:

圖中紅色的即為原型鏈。

前面例子中所展示的原型鏈還少一環。我們知道,所有引用型別預設都繼承了object,而這個繼承也是通過原型鏈實現的。需要注意,所有函式的預設原型都是object的例項,因此預設原型都會包含乙個內部指標,指向object.prototype。

深入理解JavaScript箭頭函式

箭頭函式就是個簡寫形式的函式表示式 並且它擁有詞法作用域的this 值 即不會新產生自己作用域下的this,arguments super 和new.target 等物件 此外,箭頭函式總是匿名的 語法 基礎語法 param1,param2,paramn param1,param2,paramn e...

深入理解JavaScript閉包

一 什麼是閉包 多個 兩個或兩個以上 函式巢狀,當內部函式被儲存到外部時,將會生成閉包。內部函式在外面執行的時候一定能夠呼叫的了原來它在的那個函式環境裡的變數。閉包會導致原有作用域鏈不釋放,造成記憶體洩露。functiona var aaa 123 return b var glob 100 var...

深入理解javascript 基礎筆記1

通過var宣告 或未宣告的變數都會在全域性window,this上產生全域性變數。對於 var a b 0 來說,全域性變數a是不能被刪除的,隱式全域性變數b是可以刪除的 對於乙個系統的不同組成部分,如果都有相同命名的全域性變數名,如parta,partb都有乙個全域性變數result,後者會替換前...