JavaScript淺析 -- 原型和原型鏈

一、原型

上回講到量没,生成一個對象我們可以通過new構(gòu)造函數(shù)來實現(xiàn)玉转,如下:

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.getAge = function() {
        return this.age;
    }
}
var p = new Person('peter', 18);

但是,上面這樣也有個缺陷殴蹄,比如每個person對象實際上都有g(shù)etAge方法而且都一樣究抓,但每次new的時候都要重新生成未免太浪費。那有沒有辦法把公共的方法找個地方存起來大家都去讀袭灯,然后每次只要生成自己私有的屬性和方法而不再生成共有的就可以了刺下?

因為這點,原型就產(chǎn)生了稽荧,原型就是這個存公共方法的地方橘茉。一般我們把公共函數(shù)放在原型里,私有屬性和方法放在構(gòu)造函數(shù)里姨丈,通過構(gòu)造函數(shù)的prototype可以訪問到其對應的原型畅卓。

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.getAge = function() {
    return this.age;
}
var p = new Person('peter', 18);

上面的代碼將公共的getAge挪到了Person的原型上,這樣每次new生成實例的時候跑Person函數(shù)里的代碼的時候就不會重復生成getAge方法了蟋恬,而每個實例對象又能訪問到getAge翁潘。
其對應的原型圖如下,構(gòu)造函數(shù)有個prototype指向其原型筋现,每個原型又有個constructor指向其構(gòu)造函數(shù)唐础,實例中的__proto__指向原型,這樣就能一一對應關(guān)系和互相訪問了矾飞。

原型圖

二一膨、原型鏈

上面的例子我們講的只是簡單畫了原型圖作為示例,實際上他的原型圖不止這三個洒沦。其實豹绪,所有的構(gòu)造函數(shù)都有prototype指向原型,而所有的原型都有constructor指向其構(gòu)造函數(shù)申眼,而每個實例對象都有一個__proto__指向生成它構(gòu)造函數(shù)的原型瞒津。而原型其實也是個對象實例,通過new Object生成括尸;構(gòu)造函數(shù)也是個函數(shù)實例巷蚪,通過new Function生成。所以上面的圖可以進一步完善成下面的樣子濒翻。

原型鏈圖

實際上屁柏,根據(jù)上面說的啦膜,原型其實也是個對象實例,通過new Object生成淌喻;構(gòu)造函數(shù)也是個函數(shù)實例僧家,通過new Function生成。那么裸删,F(xiàn)unction.prototype也是個對象實例八拱,而Object和Function也是函數(shù)實例,所以有

Function.prototype.__proto__ === Object.prototype
Object.__proto__ === Function.prototype
Function.__proto__ === Function.prototype

這其中要特別注意涯塔,Object.prototype實際上也是個對象實例肌稻,但js規(guī)定它的__proto__是指向null。所以有Object.prototype.__proto__ === null伤塌。于是上面的原型最后變成如下圖所示:

終極原型鏈

所以所謂的原型鏈灯萍,指的是每個實例對象都有自己的原型,而原型實際上也是個對象實例每聪,他也有自己的原型旦棉,層層往上,從而形成一條原型鏈药薯。所有的原型即prototype最終都會追溯到Object.prototype绑洛,而Object.prototype的原型是null,所以原型鏈的末端就是null童本。

// 從上圖的鏈真屯,按著箭頭指向我們可以得到
p.__proto__.constructor.__proto__.__proto__.__proto__ === null // true

而當我們在一個實例里尋找某個屬性或方法時,js引擎會先在當前的實例上去找穷娱,如果找不到就往上一個原型里找绑蔫,找到則返回,找不到再沿著原型鏈往上上個原型找泵额,一直到末端的null配深,還沒有的話則返回undefined。

還是上面的例子:

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.getAge = function() {
    return this.age;
}
var p = new Person('peter', 18);
p.getAge(); // 18
p.toString(); // [object Object]
  • (1) 調(diào)用實例p.getAge方法嫁盲,但是實例p上只有name和age屬性篓叶,沒有g(shù)etAge;
    (2) 于是往實例p的原型Person.prototype上去找羞秤,找到getAge方法返回缸托,停止尋找。
  • (1) 當調(diào)用p.toString方法時瘾蛋,實例p上只有name和age屬性找不到toString方法俐镐;
    (2) 于是往p的原型Person.prototype上去找,但Person.prototype的原型只有g(shù)etAge方法沒有toString哺哼;
    (3) 于是繼續(xù)往Person.prototype的原型Obect.prototype里找佩抹,而Object.prototype默認定義了toString方法奇唤,所以返回該方法,停止尋找匹摇。

這就是為啥我們生成實例對象時,雖然沒有定義toString或valueOf方法甲葬,但仍能使用的原因廊勃,因為所有的原型鏈最終都能追溯到Object.prototype上,而Object.prototype默認定義了toString和valueOf方法经窖。同理坡垫,其他原型也定義了一些公共方法給實例使用,比如生成數(shù)組實例時画侣,雖然沒有定義pop冰悠、push等數(shù)組方法,卻能使用的原因配乱,是因為在Array.prototype上已經(jīng)默認定義了這些方法溉卓。

三、原型相關(guān)方法

1. 獲得原型的三種方法

(1) 通過實例的__proto__來獲取搬泥,如obj.__proto__桑寨。但一般__proto__是瀏覽器為了實現(xiàn)原型鏈而增加的內(nèi)在屬性,不是官方規(guī)定的方法不推薦忿檩。
(2) 通過實例的constructor.prototype來獲取尉尾,如obj.constructor.prototype。因為實例上找constructor屬性時會找到原型的constructor燥透,可以獲得產(chǎn)生該實例的構(gòu)造函數(shù)沙咏,構(gòu)造函數(shù)的prototype又會指向原型,所以間接獲得了原型班套。
(3) 通過Object.getPrototypeOf(obj)來獲取肢藐。此法是官方定義的方法,推薦用此法孽尽。

function Person(name, age) {
    this.name = name;
    this.age = age;
}
var p = new Person('peter', 18);
// 下面三種方法都可以獲得實例p的原型
p.__proto__;
p.constructor.prototype;
Object.getPrototypeOf(p);
2. 修改設(shè)置原型的三種方法

(1) 通過實例的__proto__來修改窖壕。該屬性可讀可寫,通過此法設(shè)置效果跟第三點一樣杉女,不過不推薦用此法瞻讽。
(2) 通過給構(gòu)造函數(shù)的prototype直接賦值實現(xiàn)修改。注意用此法修改的時候要相應的修改prototype的constructor熏挎,否則會出現(xiàn)引用錯誤速勇,詳見下面的例子。
(3) 通過Object.setPrototypeOf(obj, proObj)設(shè)置坎拐。官方定義的方法烦磁,推薦此法养匈,不需要考慮constructor。

function Person(name) {
  this.name = name;
}
Person.prototype.constructor === Person // true
// 法一
Person.prototype = {
  //constructor: Person, // 應該加上這句都伪,否則constructor不再指向?qū)臉?gòu)造函數(shù)
  method: function () {}
};
console.log(Person.prototype.constructor === Person) // false
console.log(Person.prototype.constructor === Object) // true
// 法二
Object.setPrototypeOf(Person, {
  method: function () {}
});
console.log(Person.prototype.constructor === Person) // true
console.log(Person.prototype.constructor === Object) // false
3. 判斷原型

通過a.isPrototypeOf(b)可以判斷a是否是b的原型呕乎。

  • 注意前面的getPrototypeOf()和setPrototypeOf()都是定義在Obejct上的,而此法是定義在Object.prototype上的陨晶,所以通過實例可以直接調(diào)用猬仁。
  • 只要a在參數(shù)對象b的原型鏈上,該函數(shù)都會返回true先誉。由于Object.prototype處于原型鏈的最頂端湿刽,所以對各種實例都返回true,只有直接繼承自null的對象除外褐耳。
function Person(name, age) {
    this.name = name;
    this.age = age;
}
var p = new Person('peter', 18);
Person.prototype.isPrototypeOf(p); // true
Object.prototype.isPrototypeOf(p); // true
Object.prototype.isPrototypeOf(Object.create(null)); // false诈闺,括號里面是以null為原型生成的實例
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市铃芦,隨后出現(xiàn)的幾起案子雅镊,更是在濱河造成了極大的恐慌,老刑警劉巖杨帽,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漓穿,死亡現(xiàn)場離奇詭異,居然都是意外死亡注盈,警方通過查閱死者的電腦和手機晃危,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來老客,“玉大人僚饭,你說我怎么就攤上這事‰逝椋” “怎么了鳍鸵?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長尉间。 經(jīng)常有香客問我偿乖,道長,這世上最難降的妖魔是什么哲嘲? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任贪薪,我火速辦了婚禮,結(jié)果婚禮上眠副,老公的妹妹穿的比我還像新娘画切。我一直安慰自己,他們只是感情好囱怕,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布霍弹。 她就那樣靜靜地躺著毫别,像睡著了一般。 火紅的嫁衣襯著肌膚如雪典格。 梳的紋絲不亂的頭發(fā)上岛宦,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音耍缴,去河邊找鬼恋博。 笑死,一個胖子當著我的面吹牛私恬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼圃验!你這毒婦竟也來了变逃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤凡蚜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涮瞻,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年假褪,在試婚紗的時候發(fā)現(xiàn)自己被綠了署咽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡生音,死狀恐怖宁否,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缀遍,我是刑警寧澤慕匠,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站域醇,受9級特大地震影響台谊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜譬挚,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一锅铅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧殴瘦,春花似錦狠角、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姨蟋。三九已至,卻和暖如春立帖,著一層夾襖步出監(jiān)牢的瞬間眼溶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工晓勇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留堂飞,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓绑咱,卻偏偏與公主長得像绰筛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子描融,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • JS中原型鏈铝噩,說簡單也簡單。 首先明確: 函數(shù)(Function)才有prototype屬性窿克,對象(除Object...
    前小白閱讀 3,907評論 0 9
  • 原型鏈是一種機制骏庸,指的是 JavaScript 每個對象都有一個內(nèi)置的 __proto__ 屬性指向創(chuàng)建它的構(gòu)造函...
    劼哥stone閱讀 4,402評論 15 80
  • 第3章 基本概念 3.1 語法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,097評論 0 21
  • 2017.2.6寫作第三天,雨天 我是一個最怕做抉擇的人年叮,然而每天都需要抉擇具被,人生路上其實是一直在做選擇的過程。譬...
    80后女諸葛閱讀 240評論 6 0
  • 炎炎夏日只损, 海水清涼一姿。 我在海中, 洗一個澡跃惫。 口銜玫瑰啸蜜, 我有準備, 你為我來辈挂, 我為你狂衬横!
    簡村小吹閱讀 89評論 2 0