BATJ前端高級大師筆記首次分享之JS原型與原型鏈的剖析

BATJ前端高級大師筆記首次分享之JS原型與原型鏈的剖析

JavaScript中萬物皆對象,但對象之間也是有區別的。分為函數對象和普通對象

普通對象和函數對象

凡是通過new Function創建的對象都是函數對象,其他都是普通對象(通常通過Object創建),可以通過typeof來判斷。

由function創造出來的函數,比如

function f1(){};

typeof f1 //"function"

var f2 = function() {}

typeof f2 // "function"

var o1 = new f1();

typeof o1 //"object"

var o2 = {};

typeof o2 //"object"

可能有人會問,不是通過new Function創建的對象才是函數對象嗎?注意下面這兩種寫法是一樣的

function f1(){}; 等價於 var f1 = new Function();

寫了這麼多,有些人可能不展示了,這跟原型有什麼關係呢?

下面兩句話很重要

1、每一個函數對象都有一個prototype屬性,但是普通對象是沒有的;

  prototype下面又有個construetor,指向這個函數。

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

普通對象

var o = {};

1、o的確沒有prototype屬性

2、o是Object的實例

console.log(o.prototype); //undefined

console.log(o instanceof Object); //true

函數對象

function Fun(){};

var f1 = new Fun ();

1、Fun是函數對象,f1還是普通對象

2、f1是Fun的實例

console.log(f1.prototype); //undefined

console.log(f1 instanceof Fun); //true

原型對象

說到原型對象,我們先了解下構造函數是什麼?構造函數與其他函數唯一的區別在於調用方式不同。任何函數只要通過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("狗");

dog.species = '食肉動物';

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

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

但是,我們希望構造函數中的species屬性是一個共有屬性,那麼此時用這樣的方法,每個實例中都有一個相同的species屬性,會造成資源極大的浪費!

那麼原型對象就即將登場了!給每一個構造函數都設置一個prototype屬性,這個屬性就指向原型對象。其實原型對象就只是個普通對象,裡面存放著所有實例對象需要共享的屬性和方法!所以,我們把需要共享的放到原型對象裡,把那些不需要共享的屬性和方法存在構造函數里!

那麼上面的代碼怎麼修改呢?

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

繼承實現方式:

為了實現繼承,__proto__會指向上一層的原型對象,而上一層的結構依然類似,那麼就利用__proto__一直指向Object的原型對象上!Object.prototype.__proto__ = null;表示到達最頂端。如此形成了原型鏈繼承。

下面有個圖解非常經典,可以手畫幾遍去理解,非常有效~


分享到:


相關文章: