JS中的prototype和__proto__

在開始以前要明確一點,「在JS裏,萬物皆對象」,方法(Function)是對象,方法的原型(Function.prototype)也是對象。所以,它們都會具備對象共有的特色。chrome

 

 

1、prototype和__proto__分別是什麼?函數

 

prototype(顯式原型)是對象的一個屬性(每一個對象都有一個prototype屬性),這個屬性是一個指針,指向一個對象,經過它能夠向對象添加屬性和方法。this

 

__proto__(隱式原型)是對象的一個內置屬性,是JS內部使用尋找原型鏈的屬性,也就是說,當咱們訪問obj這個對象中的x屬性時,若是在obj中找不到,那麼就會沿着__proto__依次向上查找。spa

 

 注意:用chrome和FF均可以訪問到對象的__proto__屬性,IE不能夠。因此咱們建議避免使用__proto__,由於它存在兼容性問題。prototype

 

兩者關係:一個對象的隱式原型指向構造該對象的構造函數的原型指針

 

 

2、建立對象實例的new作了些什麼?code

 

var Person = function(){}; Person.prototype.sayName = function() { alert("My Name is Jacky"); }; Person.prototype.age = 27; var p = new Person();

console.log(p.age);  // 27 p.sayName();      //
My Name is Jacky
 

這段代碼你們都能看懂,可是有人思考過這個問題嗎:咱們並無顯式設置p這個實例的age屬性,爲何p.age能夠打印出27?是什麼幫咱們悄悄作了處理?答案就是__proto__。對象

 

當執行 var p =  new Person() 時 ,悄然發生瞭如下事情:blog

var p={};
p.__proto__
=
Person.prototype;  // 一個對象的隱式原型指向構造該對象的構造函數的原型


Person.call(p);

 

爲了證實關鍵的第二步代碼的正確性,咱們執行:原型鏈

alert(p.__proto__ === Person.prototype);  // true

有這層等價關係存在,就能夠保證明例化對象p能夠找到原型鏈上的屬性。

p是一個引用指向Person的實例化對象,咱們在Person的原型上定義了一個sayName方法和age屬性,當咱們執行p.age時,會先在this的內部查找(也就是構造函數內部),若是沒有找到,就沿着原型鏈向上追溯。

這裏的向上追溯是怎麼向上的呢?就是經過__proto__屬性來連接到原型(也就是Person.prototype)進行查找。最終在原型上找到了age屬性。

 

 

3、一個更復雜的示例

 

var Person = function() {}; Person.prototype.say = function() { console.log("Person say"); } Person.prototype.salary = 50000; var Programmer = function() {}; Programmer.prototype = new Person(); Programmer.prototype.writeCode = function() { console.log("Programmer writes code"); }; Programmer.prototype.salary = 500; var p = new Programmer(); p.say(); // Person say
p.writeCode(); // Programmer writes code
console.log(p.salary); // 500

你們能夠先本身思考一下,在new Programmer()時悄然發生了什麼?

 

 

 

 

------------------------------------思考分割線------------------------------------------

 

 

 

 

在var p = new Programmer()時發生瞭如下事情:

var p = {}; p.__proto__ = Programmer.prototype; p.__proto__ = new Person(); p.__proto__.__proto__ = Pserson.prototype; Person.call(p.__proto__); Programmer.call(p);

當咱們調用 p.say() 時,p 中是沒有 say 屬性,因而到 p 的 __proto__ 屬性中去找,也就是 Programmer.prototype,此時 Programmer.prototype 是等於 new Person(),但 new Person() 中也沒有 say 屬性,因而又到 new Person().__proto__ 中找,此時 new Person().__proto__ 等於 Pserson.prototype 的,咱們定義了 Person.prototype.say=function(){}; 因此,p 在 Person.prototype 中就找到了這個方法。 

 

4、一張粗糙的理解圖

 

 

 

 

1.構造函數Foo()
構造函數的原型屬性Foo.prototype指向了原型對象,在原型對象裏有共有的方法,全部構造函數聲明的實例(這裏是f1,f2)均可以共享這個方法。

 

2.原型對象Foo.prototype
Foo.prototype保存着實例共享的方法,有一個指針constructor指回構造函數。

 

3.實例
f1和f2是Foo這個對象的兩個實例,這兩個對象也有屬性__proto__,指向構造函數的原型對象,這樣就能夠訪問原型對象的全部方法。

 

另外:
構造函數Foo()除了是方法,也是對象(萬物皆對象),它也有__proto__屬性,指向誰呢?指向它的構造函數的原型對象,而函數的構造函數是Function,所以Foo.__proto__  =  Function.prototype。

Function(), Object()也是同樣的道理。Function是由Object構造出來的,因此Function的__proto__指向了Object.prototype

最後,Object.prototype的__proto__屬性指向null

相關文章
相關標籤/搜索