其實(shí)MDN上寫的非常清晰了,我這里只是為了自己做筆記。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
繼承時(shí),JavaScript 只有一種結(jié)構(gòu):對(duì)象。每個(gè)實(shí)例對(duì)象( object )都有一個(gè)私有屬性(稱之為 proto )指向它的構(gòu)造函數(shù)的原型對(duì)象(prototype )。該原型對(duì)象也有一個(gè)自己的原型對(duì)象( proto ) ,層層向上直到一個(gè)對(duì)象的原型對(duì)象為 null。根據(jù)定義,null 沒有原型,并作為這個(gè)原型鏈中的最后一個(gè)環(huán)節(jié)。
幾乎所有 JavaScript 中的對(duì)象都是位于原型鏈頂端的 Object 的實(shí)例。
基于原型鏈的繼承
繼承屬性
JavaScript 對(duì)象是動(dòng)態(tài)的屬性“包”(指其自己的屬性)。JavaScript 對(duì)象有一個(gè)指向一個(gè)原型對(duì)象的鏈。當(dāng)試圖訪問一個(gè)對(duì)象的屬性時(shí),它不僅僅在該對(duì)象上搜尋,還會(huì)搜尋該對(duì)象的原型,以及該對(duì)象的原型的原型,依次層層向上搜索,直到找到一個(gè)名字匹配的屬性或到達(dá)原型鏈的末尾。
遵循ECMAScript標(biāo)準(zhǔn),
someObject.[[Prototype]]符號(hào)是用于指向someObject的原型。從 ECMAScript 6 開始,[[Prototype]]可以通過Object.getPrototypeOf()和Object.setPrototypeOf()訪問器來訪問。這個(gè)等同于 JavaScript 的非標(biāo)準(zhǔn)但許多瀏覽器實(shí)現(xiàn)的屬性__proto__。
但它不應(yīng)該與構(gòu)造函數(shù) func 的 prototype 屬性相混淆。被構(gòu)造函數(shù)創(chuàng)建的實(shí)例對(duì)象的 [[prototype]] 指向 func 的 prototype 屬性。**Object.prototype **屬性表示 Object 的原型對(duì)象。
// 讓我們從一個(gè)自身擁有屬性a和b的函數(shù)里創(chuàng)建一個(gè)對(duì)象o:
let f = function () {
this.a = 1;
this.b = 2;
}
/* 這么寫也一樣
function f() {
this.a = 1;
this.b = 2;
}
*/
let o = new f(); // {a: 1, b: 2}
// 在f函數(shù)的原型上定義屬性
f.prototype.b = 3;
f.prototype.c = 4;
// 不要在 f 函數(shù)的原型上直接定義 f.prototype = {b:3,c:4};這樣會(huì)直接打破原型鏈
// o.[[Prototype]] 有屬性 b 和 c
// (其實(shí)就是 o.__proto__ 或者 o.constructor.prototype)
// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
// 最后o.[[Prototype]].[[Prototype]].[[Prototype]]是null
// 這就是原型鏈的末尾,即 null,
// 根據(jù)定義,null 就是沒有 [[Prototype]]。
// 綜上,整個(gè)原型鏈如下:
// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null
console.log(o.a); // 1
// a是o的自身屬性嗎?是的,該屬性的值為 1
console.log(o.b); // 2
// b是o的自身屬性嗎?是的,該屬性的值為 2
// 原型上也有一個(gè)'b'屬性,但是它不會(huì)被訪問到。
// 這種情況被稱為"屬性遮蔽 (property shadowing)"
console.log(o.c); // 4
// c是o的自身屬性嗎?不是,那看看它的原型上有沒有
// c是o.[[Prototype]]的屬性嗎?是的,該屬性的值為 4
console.log(o.d); // undefined
// d 是 o 的自身屬性嗎?不是,那看看它的原型上有沒有
// d 是 o.[[Prototype]] 的屬性嗎?不是,那看看它的原型上有沒有
// o.[[Prototype]].[[Prototype]] 為 null,停止搜索
// 找不到 d 屬性,返回 undefined
給對(duì)象設(shè)置屬性會(huì)創(chuàng)建自有屬性。獲取和設(shè)置屬性的唯一限制是內(nèi)置 getter 或 setter 的屬性。
繼承方法
在 JavaScript 里,任何函數(shù)都可以添加到對(duì)象上作為對(duì)象的屬性。
當(dāng)繼承的函數(shù)被調(diào)用時(shí),this 指向的是當(dāng)前繼承的對(duì)象,而不是繼承的函數(shù)所在的原型對(duì)象。
var o = {
a: 2,
m: function(){
return this.a + 1;
}
};
console.log(o.m()); // 3
// 當(dāng)調(diào)用 o.m 時(shí),'this' 指向了 o.
var p = Object.create(o);
// p是一個(gè)繼承自 o 的對(duì)象
p.a = 4; // 創(chuàng)建 p 的自身屬性 'a'
console.log(p.m()); // 5
// 調(diào)用 p.m 時(shí),'this' 指向了 p
// 又因?yàn)?p 繼承了 o 的 m 函數(shù)
// 所以,此時(shí)的 'this.a' 即 p.a,就是 p 的自身屬性 'a'
在 JavaScript 中使用原型
在 JavaScript 中,函數(shù)(function)是允許擁有屬性的。所有的函數(shù)會(huì)有一個(gè)特別的屬性 —— prototype 。
運(yùn)行如下代碼
function doSomething(){}
console.log( doSomething.prototype );
// 和聲明函數(shù)的方式無關(guān),
// JavaScript 中的函數(shù)永遠(yuǎn)有一個(gè)默認(rèn)原型屬性。
var doSomething = function(){}; // doSomething從Object.prototype繼承對(duì)象的方法。
console.log( doSomething.prototype );


我們可以給doSomething函數(shù)的原型對(duì)象添加新屬性,如下:
function doSomething(){}
doSomething.prototype.foo = "bar";
console.log( doSomething.prototype );

通過new操作符來創(chuàng)建基于這個(gè)原型對(duì)象的doSomething實(shí)例。
function doSomething(){}
doSomething.prototype.foo = "bar"; // add a property onto the prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // add a property onto the object
console.log( doSomeInstancing );

接下來這段看似話很多,但是非常清晰,不繞??赐昃湍芎芡笍氐亩玩?層層向上。
如上所示, doSomeInstancing 中的_ _ proto _ _是 doSomething.prototype. 但這是做什么的呢?當(dāng)你訪問doSomeInstancing 中的一個(gè)屬性,瀏覽器首先會(huì)查看doSomeInstancing 中是否存在這個(gè)屬性。
如果 doSomeInstancing 不包含屬性信息, 那么瀏覽器會(huì)在 doSomeInstancing 的 _ _ proto _ _ 中進(jìn)行查找(同 doSomething.prototype). 如屬性在 doSomeInstancing 的 _ _ proto _ _ 中查找到,則使用 doSomeInstancing 中 _ _ proto _ _ 的屬性。
否則,如果 doSomeInstancing 中 _ _ proto _ _ 不具有該屬性,則檢查doSomeInstancing 的 _ _ proto _ _ 的 _ _proto _ _ 是否具有該屬性。默認(rèn)情況下,任何函數(shù)的原型屬性 _ _ proto _ _ 都是 window.Object.prototype. 因此, 通過doSomeInstancing 的 _ _ proto _ _ 的 _ _ proto _ _ ( 同 doSomething.prototype 的 _ _ proto _ _ (同 Object.prototype)) 來查找要搜索的屬性。
如果屬性不存在 doSomeInstancing 的 _ _ proto _ _ 的 _ _ proto _ _ 中, 那么就會(huì)在doSomeInstancing 的 _ _ proto _ _ 的 _ _ proto _ _ 的 _ _ proto _ _ 中查找。然而, 這里存在個(gè)問題:doSomeInstancing 的 _ _ proto _ _ 的 _ _ proto _ _ 的 _ _ proto _ _ 其實(shí)不存在。因此,只有這樣,在 _ _ proto _ _ 的整個(gè)原型鏈被查看之后,這里沒有更多的 _ _ proto _ _ , 瀏覽器斷言該屬性不存在,并給出屬性值為 undefined 的結(jié)論。
在控制臺(tái)窗口中輸入更多的代碼,如下:
function doSomething(){}
doSomething.prototype.foo = "bar";
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log("doSomeInstancing.prop: " + doSomeInstancing.prop);
console.log("doSomeInstancing.foo: " + doSomeInstancing.foo);
console.log("doSomething.prop: " + doSomething.prop);
console.log("doSomething.foo: " + doSomething.foo);
console.log("doSomething.prototype.prop: " + doSomething.prototype.prop);
console.log("doSomething.prototype.foo: " + doSomething.prototype.foo);
結(jié)果如下:

其實(shí)我在這里一直一直想不通,我總覺得
doSomething.foo的結(jié)果應(yīng)該是bar。直到讀了犀牛書的第六章--對(duì)象,
通過new創(chuàng)建對(duì)象
new運(yùn)算符創(chuàng)建并初始化一個(gè)新對(duì)象。關(guān)鍵字new后跟隨一個(gè)函數(shù)調(diào)用。這里的函數(shù)稱做構(gòu)造函數(shù)(constructor),構(gòu)造函數(shù)用以初始化一個(gè)新創(chuàng)建的對(duì)象。
再翻上去看引用中的那段話,才能明白。function doSomething(){}是定義一個(gè)空構(gòu)造函數(shù)。doSomething.prototype指向其原型對(duì)象。doSomeInstancing()是doSomething()的實(shí)例,是doSomething原型的繼承對(duì)象,也因此,doSomething.anything的答案都是undefined。doSomething.prototype才訪問到了它的原型對(duì)象。
掘金中有一篇文章講得非常非常好!鏈接 --> JS原型鏈與繼承別再被問倒了
我的個(gè)人感覺是:如果你看的不是很理解的話,八成是代碼寫少了,多從例子中體會(huì)。