標籤:javascript proto prototype 原形 原形鏈
在 javascript 中我們會約定俗成,如果一個方法是被 new 出來使用的,那麼該方法名首字母通常會大寫,例如下面代碼塊中的 Person。(我們也可以把 Person 看成 java 或 c# 中的類)
var Person = function(name) { this.name = name;}
在 javascript 中一個類被 new 出來的具體過程如下:
// 初始化一個對象 pvar p = new Person();
// 初始化一個對象 pvar p = {};p.__proto__ = Person.prototype;Person.call(p);
以上兩段代碼的作用完全一樣,關鍵在第2段代碼的第2行,我們來證明一下:
var p = new Person();console.log(p.__proto__ === Person.prototype); // true
上面這段代碼返回的true,說明我們第2段代碼是正確的。
那麼__proto__是什嗎?
簡單來說,在 javascript 中每個對象都會有一個 __proto__ 屬性,當我們訪問一個對象的屬性時,如果這個對象內部不存在這個屬性,那麼他就會去 __proto__ 裡找這個屬性,這個 __proto__ 又會有自己的 __proto__,於是就這樣一直找下去,也就是我們平時所說的原型鏈的概念。
按照標準,__proto__ 是不對外公開的,但是 chrome 的引擎卻將他暴露了出來成為了一個公有屬性,我們可以對其進行訪問和賦值。但 ie 瀏覽器是不能訪問這個屬性的,所以不推薦大家直接操作這個屬性,以免造成瀏覽器安全色問題。
概念說清楚了,讓我們看段代碼:
var Person = function() {};Person.prototype.say = function() { alert("Person say");}var p = new Person();p.say(); // Person say
這段代碼很簡單,相信每個人都這樣寫過,那就讓我們看下為什麼 p 可以訪問 Person 的 say。它等同於下面這段代碼:
var Person = function() {};Person.prototype.say = function() { alert("Person say");}var p = {};p.__proto__ = Person.prototype;Person.call(p);p.say(); // Person say
當我們調用 p.say() 時,p 中是沒有 say 屬性,於是到 p 的 __proto__ 屬性中去找,也就是 Person.prototype,而我們定義了 Person.prototype.say=function(){}; 於是,p 在 Person.prototype 中就找到了這個方法。
過程:p –> p.__proto__ === Person.prototype
接下來,讓我們看個更複雜的代碼。
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 sayp.writeCode(); // Programmer writes codeconsole.log(p.salary); // 500
大家先不要看下面的推導過程,自己試著推導一下。
var Person = function() {};Person.prototype.say = function() { console.log("Person say");}Person.prototype.salary = 50000;var Programmer = function() {};Programmer.prototype = new Person();// 推導過程 --> // Programmer.prototype = {};// Programmer.prototype.__proto__ = Person.prototype;// Person.call(Programmer.prototype);Programmer.prototype.writeCode = function() { console.log("Programmer writes code");};Programmer.prototype.salary = 500;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(); // Person sayp.writeCode(); // Programmer writes codeconsole.log(p.salary); // 500
當我們調用 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 中就找到了這個方法。
過程:
p –>
p.__proto__ === Programmer.prototype === new Person() –>
p.__proto__.__proto__ === Programmer.prototype.__proto__ === new Person().__proto__ === Pserson.prototype
調用 p.writeCode() 和 p.salary 也都是同樣的原理,大家自己推導。
重中之重,只要大家理解了下面段代碼,就能輕而易舉掌握 javascript 的原型鏈知識。
var Person = function(name) { this.name = name;}// 初始化一個對象 pvar p = new Person();// 推導過程 --> // var p = {};// p.__proto__ = Person.prototype;// Person.call(p);
參考資料:關於__proto__和prototype的一些理解
簡單介紹 javascript 中 __proto__ 屬性的原理