Javascript原型和原型鏈

JavaScript在ES6之前沒有類似class,extend的繼承機制,JavaScript的繼承主要是通過原型和原型鏈實現的。

在說原型和原型鏈之前 ,我們首先要了解以下幾個概念

一.私有變量和私有函數

在函數內部定義的變量和函數,如果不對外提供接口,那么外部是無法訪問到的。這個被定義的變量和函數就叫做該函數的私有變量和私有函數。

function Foo() {

var name = "yiMu";  //私有變量

var fn = function() {   //私有函數

console.log("hello word");

};

}

var bar = new Foo();

console.log(bar.name);  //undefined

console.log(bar.fn);    //undefined

因為變量name和函數fn都是在Foo函數內部定義局部變量,所以在函數外部新創建的實例無法訪問;

二、靜態變量和靜態函數

當定義一個函數后通過"."的方式為其添加屬性和函數,通過對象本身可以訪問到,但是其實例卻無法訪問到,這樣的變量和函數叫做靜態變量和靜態函數。

function Foo(){}

Foo.num = 10;  //靜態變量

Foo.fn = function() {   //靜態函數

console.log("hello word");

};

console.log(Foo.num);  //10

console.log(typeof Foo.fn); //function

var bar = new Foo();

console.log(bar.num);  //undefined

console.log(typeof bar.fn); //undefined
三、實例屬性和實例方法

在面向對象編程中除了一些庫函數,我們還是希望在定義一個對象的時候同時定義一些屬性和方法并在實例化后能夠訪問,這些添加的屬性和方法就叫做實例屬性和實例方法。

function Foo() {

this.num = [];  //實例屬性

this.fn = function() {  //實例方法

console.log("hello word");

};

}

console.log(Foo.num);   //undefined

console.log(typeof Foo.fn); //undefined

var bar = new Foo();

console.log(bar.num);   //[]

console.log(typeof bar.fn); //function

我們也可以為實例屬性和實例方法添加屬性和方法:

function Foo() {

this.num = [];  //實例屬性

this.fn = function() {  //實例方法

console.log("hello word");

}

}

var oneBar = new Foo();

oneBar.num.push(1);

oneBar.fn = {};

console.log(oneBar.num);    //[1]

console.log(typeof oneBar.fn);  //Object

var twoBar = new Foo();

console.log(twoBar.num);   //[]

console.log(typeof twoBar.fn);  //function

從上面的代碼可以看到,當我們在oneBar中修改了num的值和fn的類型,但是在twoBar中卻沒有發生改變,這是由于數組和函數都是對象,屬于引用類型。oneBar和twoBar中的屬性和方法名稱雖然相同但是卻不是同一個引用,它們只是對Foo對象定義的屬性和方法的一個復制。

如果一個構造函數對象有上千的實例方法,那么它的每個實例都要對這個構造函數的上千個實例方法進行復制,這顯然是不科學的,那么這種情況下我們就必須使用prototype了。好,接下來我們正式說一下什么是原型,什么是原型鏈?

首先,什么是原型?

JavaScript 中,萬物皆對象!但對象也是有區別的。分為普通對象和函數對象,Object ,Function 是JS自帶的函數對象。每個對象都有原型(null和undefined除外),你可以把它理解為對象的默認屬性和方法。
普通對象和函數對象
var o1 = {}; 
var o2 =new Object();
var o3 = new f1();

function f1(){}; 
var f2 = function(){};
var f3 = new Function('str','console.log(str)');

console.log(typeof Object); //function 
console.log(typeof Function); //function  

console.log(typeof f1); //function 
console.log(typeof f2); //function 
console.log(typeof f3); //function   

console.log(typeof o1); //object 
console.log(typeof o2); //object 
console.log(typeof o3); //object

在上面的例子中 o1 o2 o3 為普通對象,f1 f2 f3 為函數對象。怎么區分,其實很簡單,凡是通過 new Function() 創建的對象都是函數對象,其他的都是普通對象。f1,f2,歸根結底都是通過 new Function()的方式進行創建的。Function Object 也都是通過 New Function()創建的。

  • Object:Object是一個函數對象,Object的原型就是一個Object對象,它里面存在著一些對象的方法和屬性,例如最常見的toString方法。
  • 新建對象:用new Object或者{}建的對象是普通對象,它沒有prototype屬性,只有proto屬性,它指向Object.prototype。
  • Array Array也是一個函數對象,它的原型就是Array.prototype,它里面存在著一些數組的方法和屬性,例如常見的push,pop等方法。
  • Function:Function也是一個函數對象,但它有點特殊,它的原型就是一個function空函數。
  • 自定義函數:它的原型就是你給它指定的那個東西。如果你不指定,那它的原型就是一個Object.prototype。

實例就是通過構造函數創建的。實例一創建出來就具有constructor屬性(指向構造函數)和proto屬性(指向原型對象)。構造函數中有一個prototype屬性,這個屬性是一個指針,指向它的原型對象。原型對象內部也有一個指針(constructor屬性),指向構造函數Person.prototype.constructor = Person。實例可以訪問原型對象上定義的屬性和方法。

普通對象的proto指向這個對象(this)的構造函數的prototype;
函數對象的proto全部都是指向Function的prototype。這是一個空函數
然后無論是Function的prototype還是構造器的prototype的proto都指向object.prototype,然后最終object.prototype指向null,原型鏈結束;

總的來說:

在Javascript中,萬物皆對象,然而對象又分為普通對象和函數對象,至于怎么區分普通對象和函數對象,很簡單,沒有通過new Function()創建的都為普通對象,Object ,Function 是JS自帶的函數對象,每個對象都具有原型屬性prototype指向它的原型對象,每個實例都是通過構造函數創建的,一旦創建就擁有constructor屬性,指向它的構造函數,和_ proto _屬性,指向原型對象,也就是說

實例對象._ proto _屬性===構造函數.prototype

所有普通對象的_ proto 屬性等于它構造器的prototype;
所有函數對象的
proto 屬性等于Function的prototype;
無論是FUnction還是構造器,構造函數的prototype的
proto _都等于object.prototype;

Function.prototype._ proto _ === Object.prototype
Object.prototype._ proto _===null
則原型鏈完成

function Person(name) {
    this.name = name
}
// 重寫原型
Person.prototype = {
    getName: function() {}
}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // false
這里直接重寫了 Person.prototype。輸出結果可以看出p._ proto 仍然指向的是Person.prototype,而不是p.constructor.prototype。這也很好理解,給Person.prototype賦值的是一個對象直接量{getName: function(){}},使用對象直接量方式定義的對象其構造器(constructor)指向的是根構造器Object_,Object.prototype是一個空對象{},{}自然與{getName: function(){}}不等。

什么是原型鏈

在JavaScript 中,每個對象都有一個指向它的原型(prototype)對象的內部鏈接。這個原型對象又有自己的原型,直到某個對象的原型為 null 為止(也就是不再有原型指向),組成這條鏈的最后一環。這種一級一級的鏈結構就稱為原型鏈(prototype chain)。
JavaScript 對象是動態的屬性“包”(指其自己的屬性)。JavaScript 對象有一個指向一個原型對象的鏈。當試圖訪問一個對象的屬性時,它不僅僅在該對象上搜尋,還會搜尋該對象的原型,以及該對象的原型的原型,依此層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾。


原型鏈.png
var A = function(){};

var a = new A();

console.log(a.__proto__);   //A{}(即構造器function A 的原型對象)

console.log(a.__proto__.__proto__); //Object{}(即構造器function Object 的原型對象)

console.log(a.__proto__.__proto__.__proto__);   //null
參考:

作者:Yi罐可樂
最詳盡的 JS 原型與原型鏈終極詳解,沒有「可能是」。(一)
最詳盡的 JS 原型與原型鏈終極詳解,沒有「可能是」。(二)
最詳盡的 JS 原型與原型鏈終極詳解,沒有「可能是」。(三)

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容