溫故
我們先回顧一下前兩天討論的內容
建立物件的三種方式
- 通過物件直接量
- 通過new建立物件
- 通過Object.create()
js中物件分為兩種
- 函式物件
- 普通物件
原型物件prototype
- 每一個函式物件都有一個prototype屬性,但是普通物件是沒有的;
- 普通物件都是通過函式建立的
在 ECMAScript 核心所定義的全部屬性中,最耐人尋味的就要數 prototype 屬性了。對於 ECMAScript 中的引用型別而言,prototype 是儲存著它們所有例項方法的真正所在。換句話所說,諸如 toString()和 valuseOf() 等方法實際上都儲存在 prototype 名下,只不過是通過各自物件的例項訪問罷了。----《JavaScript 高階程式設計》
建構函式constructor
function Foo(name) {
this.name = name;
}
var foo = new Foo('陌上寒');
console.log(Foo.prototype.constructor===Foo)//true
console.log(foo.constructor===Foo);//true
複製程式碼
原型物件有一個constructor屬性,指向該原型物件對應的建構函式 foo 為什麼有 constructor 屬性?那是因為 foo 是 Foo 的例項。 那 Foo.prototype 為什麼有 constructor 屬性??同理, Foo.prototype Foo 的例項。 也就是在 Foo 建立的時候,建立了一個它的例項物件並賦值給它的 prototype
隱式原型_proto_
在Firefox、Safari 和 Chrome 的每個物件上都有這個_proto_,屬性 ,而在其他瀏覽器中是完全不可見的為了確保瀏覽器相容性問題,不要直接使用 proto 屬性)
// 普通物件的\__proto\__指向當前函式物件的原型,
console.log('陌上寒'.__proto__===String.prototype);//true
//原型物件(也屬於普通物件)的__proto__指向當前函式物件的原型
console.log(String.prototype.__proto__===Object.prototype);//true
//內建函式物件的\__proto\__指向的都是ƒ () { [native code] }
console.log(Object.__proto__);//ƒ () { [native code] }
//Object的原型物件的\__proto\__為null
console.log(Object.prototype.__proto__)//null
複製程式碼
- 普通物件的__proto__指向當前函式物件的原型,
- 原型物件(也屬於普通物件)的__proto__指向當前函式物件的原型
- 內建函式物件的__proto__指向的都是ƒ () { [native code] }
- 所有物件(除了Object.prototype)都存在_proto_
- Object的原型物件的__proto__為null 好好消化上面的知識點,有助於我麼討論新的內容==>原型鏈
知新
原型鏈
- 原型物件prototype
- 建構函式constructor
- 隱式原型_proto_ 通過三者之間的聯絡,形成了原型鏈 繼續看一下我門昨天討論過的程式碼
console.log('陌上寒'.__proto__===String.prototype);//true
console.log(String.prototype.__proto__===Object.prototype);//true
//等量代換,得出以下結論
console.log('陌上寒'.__proto__.__proto__===Object.prototype);//true
複製程式碼
我們剛才說過,普通物件的__proto__指向當前函式物件的原型 我們剛才還說過,普通物件都是通過函式建立的 根據以上兩個結論我們分析一下上面的程式碼 ‘陌上寒’是字串型別,’陌上寒’的建構函式是String(), 所以’陌上寒’的__proto__指向String的原型 String是js的內建建構函式,內建建構函式繼承自Object String的原型物件String.prototype也是一個普通物件,它的__proto__指向Object的原型,即Object.prototype 所以
console.log('陌上寒'.__proto__.__proto__===Object.prototype);//true
複製程式碼
這就是原型鏈 我們繼續品讀以下程式碼
//我們建立一個建構函式Foo(要記得,建構函式命名,首字母要大寫)
function Foo() {}
//通過我們自定義的建構函式。通過new操作符,我們例項化出來一個物件foo
const foo = new Foo()
//物件的__proto__指向當前函式物件(foo是同構Foo例項化出來的,所以foo的函式物件是Foo)的原型
console.log(foo.__proto__===Foo.prototype);
//原型物件也存在__proto__,指向該原型物件(Foo.prototype)所對應的函式物件(Object)的原型(好像有點繞,看程式碼就沒那麼繞了)
console.log(Foo.prototype.__proto__===Object.prototype);//true
//上面的如果懂了,這行程式碼就簡單了,一個數學的等量代換,就得出了結論
console.log(foo.__proto__.__proto__===Object.prototype);//true
console.log('---我是分割線----');
//我們通過字面量建立了一個物件,等同於 const obj = new Object()
const obj = {}
//obj 是通過內建建構函式Object建立的,所以,obj的__proto__指向它的函式物件(Object)的原型(prototype)即:Object.prototype
console.log(obj.__proto__===Object.prototype);//true
console.log('---我是分割線----');//true
//建立一個物件b
const b = {}
//我們之前說過建立物件有三種方式,我們使用第三種方式建立一個物件b1,物件b1繼承自物件b,也就是說,物件b是物件b1的__proto__
const b1 = Object.create(b)
//物件b是物件b1的__proto__
console.log(b1.__proto__===b);//true
//前面已經證實過,b.__proto__==Object.prototype,不再贅述
console.log(b.__proto__==Object.prototype);//true
//等量代換就得出以下結論
console.log(b1.__proto__.__proto__===b.__proto__);//true
console.log(b1.__proto__.__proto__==Object.prototype);//true
複製程式碼
啊,好像很簡單,我們看一下
//自定義建構函式A(建構函式命名首字母大寫)建立方式等同於var A = new Function()
var A = function(){}//等同於var A = new Function()
//通過建構函式A,使用new操作符例項化一個物件a(A是a的建構函式,a是A建立出來的)
var a = new A()
//物件的的__proto__指向它所物件函式物件(A)的原型(A.prototype)
console.log(a.__proto__===A.prototype)//true
//A是通過js內建建構函式Function建立出來的,然而
console.log(A.prototype.__proto__===Function.prototype);//false
console.log(A.prototype.__proto__==Object.prototype);//true
console.log(Function.prototype)//ƒ () { [native code] }
複製程式碼
Function特例
我們重新修改一下上面的程式碼
var A = new Function()
var a = new A()
複製程式碼
A.prototype是個函式物件,理論上他的__proto__應該指向 Function.prototype,就是他自己,自己指向自己,沒有意義。 js一直強調萬物皆物件,函式物件也是物件,給他認個祖宗,指向Object.prototype。Object.prototype._proto_ === null,保證原型鏈能夠正常結束。 事實驗證了這樣一個道理
console.log(a.__proto__===A.prototype)//true
console.log(A.prototype.__proto__===Function.prototype);//false
console.log(A.prototype.__proto__==Object.prototype);//true
console.log(a.__proto__.__proto__==Object.prototype);//true
複製程式碼
A的原型物件的__proto__指向的是Object的原型 再看一張圖
圖片來源- 原型物件是建構函式的prototype屬性,是所有例項化物件共享屬性和方法的原型物件。
- 例項化物件通過new建構函式得到,都繼承了原型物件的屬性和方法。
- 原型物件中有個隱式的constructor,指向了建構函式本身。
總結
今天先討論到這裡,我們一起對今天的內容做一個總結 所有函式物件的\ _proto_ 都指向 Function.prototype,它是一個空函式(Empty function)ƒ () { [native code] }
var obj = {name: '陌上寒'}
var arr = [1,2,3]
var reg = /hello/g
var date = new Date
var err = new Error('exception')
console.log(obj.__proto__ === Object.prototype) // true
console.log(arr.__proto__ === Array.prototype) // true
console.log(reg.__proto__ === RegExp.prototype) // true
console.log(date.__proto__ === Date.prototype) // true
console.log(err.__proto__ === Error.prototype) // true
複製程式碼
所有物件的 _proto_ 都指向其構造器的 prototype 原型鏈 原型鏈是實現繼承的主要方法。
自己是由自己建立的,好像不符合邏輯,但仔細想想,現實世界也有些類似,你是怎麼來的,你媽生的,你媽怎麼來的,你姥姥生的,……類人猿進化來的,那類人猿從哪來,一直追溯下去……,就是無,(NULL生萬物) 正如《道德經》裡所說“無,名天地之始”。
function Person(){}
var person1 = new Person();
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype) //true
console.log(Object.prototype.__proto__) //null
Person.__proto__ === Function.prototype; //true
console.log(Function.prototype)// function(){} (空函式)
var num = new Array()
console.log(num.__proto__ === Array.prototype) // true
console.log( Array.prototype.__proto__ === Object.prototype) // true
console.log(Array.prototype) // [] (空陣列)
console.log(Object.prototype.__proto__) //null
console.log(Array.__proto__ === Function.prototype)// true
複製程式碼
我們一起連續討論了那麼多,相信你一定對js原型和原型鏈有了些認識,但是光有認識還是不夠的,原型和原型鏈在實際場景中是怎麼發揮作用的呢?我們如何將原型鏈的相關技能投入到開發中呢?我們明天繼續討論javascript的原型和原型鏈,不見不散
相關文章
關於javascript的Object. hasOwnProperty,看我就夠了
參考連結
圖解prototype、proto和constructor的三角關係