1. 程式人生 > >js原型鏈詳解

js原型鏈詳解

在講原型之前,我們先要了解一些東西。在JavaScript語言中,萬物皆物件,物件分為兩種,函式物件和普通物件。

function f1() {}; console.log(typeof f1) //"function"

var f2 = function() {} console.log(typeof f2) // "function"

var o1 = new f1(); console.log(typeof o1) //"object"

var o2 = {}; console.log(typeof o2) //"object"

1、每一個函式物件都有一個prototype屬性,但是普通物件是沒有的;prototype下面又有個construetor,指向這個函式。

2、每個物件都有一個名為_proto_的內部屬性,指向它所對應的建構函式的原型物件,原型鏈基於_proto_;

function Fun(){}; console.log(Fun.prototype);    //{constructor: ƒ}      constructor:ƒ Fun()     __proto__:Object

大家自己執行一下,可以看到 constructor和__proto__。

原型物件

原型物件就只是個普通物件,裡面存放著所有例項物件需要共享的屬性和方法。說到原型物件,我們先了解下建構函式是什麼?建構函式與其他函式唯一的區別在於呼叫方式不同。任何函式只要通過new來呼叫就可以作為建構函式,它是用來建立特定型別的物件。

下面定義一個建構函式Animal:

function Animal (name){

     this.name = name;

     this.species = '動物';  

 }

通過new命令來生成一個Animal例項:

var cat = new Animal ("貓")

這裡,建構函式Animal就是例項物件cat的原型。Animal裡的this關鍵字就指的是cat這個物件。new出來的cat物件此時已經和Animal再無聯絡了,也就是說每一個new出來的例項都有自己的屬性和方法的副本,是獨立的!修改其中一個不會影響另一個。

var dog = new Animal("狗");

console.log(cat.species)      // 動物

console.log(dog.species)      // 動物

但是,我們希望建構函式中的species屬性是一個共有屬性。上面的方法中,每個例項中都有一個相同的species屬性,會造成資源的浪費,因為每個例項中的這個共有屬性都要佔用記憶體,建立100個例項同樣也建立了100個species屬性。我們是否可以建立一次species屬性,讓所有例項化的物件都可以用呢?

那麼原型物件就即將登場了!我們把需要共享的放到原型物件裡,把那些不需要共享的屬性和方法存在建構函式裡!

那麼上面的程式碼怎麼修改呢?

function Animal(name) {

    this.name = name;

}

Animal.prototype.species = '動物';

var cat = new Animal("貓");

var dog = new Animal("狗");

console.log(cat.species) // 動物

console.log(dog.species) // 動物

Animal.prototype.species = '食肉動物';

console.log(cat.species) // 食肉動物

console.log(dog.species) // 食肉動物

可以看出,修改prototype屬性會影響它的所有例項的species的值。

例項一旦創建出來就會自動引用prototype物件的屬性和方法!所以例項物件的屬性和方法一般分為兩種:一種是自身的,一種是引用自prototype的。

具體實現是這樣的:

每當程式碼讀取某個物件的某個屬性的時候,都會執行一次搜尋。首先從物件例項本身開始,如果在例項中找到了該屬性,則返回該屬性的值,如果沒有找到,則順著原型鏈指標向上,到原型物件中去找,如果找到就返回該屬性值。

原型鏈

事實上,js裡完全依靠"原型鏈"(prototype chain)模式來實現繼承。

上面說完原型物件。下面要扒一扒__proto__、prototype、constructor

__proto__:事實上就是原型鏈指標

prototype:上面說到這個是指向原型物件的

constructor:每一個原型物件都包含一個指向建構函式的指標,就是constructor

js中是如何通過原型來實現繼承的呢?來看下面的程式碼:

function AA(){     this.name="xxx";     this.age=18 } function BB(){     this.sex="man"; } BB.prototype=new AA();  //實現繼承 var gzb=new BB(); console.log(gzb.name); //xxx console.log(gzb.age); //18 console.log(gzb.sex); //man

關鍵在於BB.prototype=new AA();這行程式碼中以new的方式呼叫了AA(),使BB函式的原型成為了AA()的一個例項物件,重寫了BB函式的原型物件,實現了繼承。

如果我們也在BB函式中也新增一個name屬性,那獲取gzb.name時會輸出什麼呢?

function BB(){     this.sex="man";

    this.name="zzz"; }

gzb.name  //"zzz"

我們在訪問name屬性時,首先會查詢例項物件中是否存在這個屬性,如果存在直接返回,如果沒有會沿著原型鏈向上尋找,檢視原型物件中是否存在這個屬性,如果有則返回,如果沒有則返回undefined。