1. 程式人生 > >簡單介紹 javascript 中 __proto__ 屬性的原理

簡單介紹 javascript 中 __proto__ 屬性的原理

重要說明:本博已遷移到 石佳劼的部落格,有疑問請到 文章新地址 留言!!!

在 javascript 中我們會約定俗成,如果一個方法是被 new 出來使用的,那麼該方法名首字母通常會大寫,例如下面程式碼塊中的 Person。(我們也可以把 Person 看成 java 或 c# 中的類)

var Person = function(name) {
    this.name = name;
}

在 javascript 中一個類被 new 出來的具體過程如下:

// 初始化一個物件 p
var p = new Person();
// 初始化一個物件 p
var 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 say
p.writeCode(); // Programmer writes code
console.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 say
p.writeCode(); // Programmer writes code
console.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;
}

// 初始化一個物件 p
var p = new Person();

// 推導過程 --> 
// var p = {};
// p.__proto__ =  Person.prototype;
// Person.call(p);

歡迎來到 石佳劼的部落格,如有疑問,請在「原文」評論區 留言,我會盡量為您解答。