關於javascript的原型和原型鏈,看我就夠了(二)

陌上寒發表於2018-11-03

溫故

建立物件的三種方式

  • 通過物件直接量
  • 通過new建立物件
  • 通過Object.create()

js中物件分為兩種

  • 函式物件
  • 普通物件 仔細觀察如下程式碼
function Foo(name) {
	this.name = name;
}
var foo = new Foo('陌上寒');
console.log(foo)// Foo{name: "陌上寒"}
//---------------
var foo1 = {name:'"陌上寒"'}
//等價於
var foo1 = new Object()
foo1.name = "陌上寒"
複製程式碼

綜合以上,得出結論==>普通物件都是通過函式建立的

prototype

每一個函式物件都有一個prototype屬性,但是普通物件是沒有的;

遺留問題

昨天留下了一些知識點,今天重點討論

  • constructor
  • _proto_

知新

constructor建構函式

我們昨天說建立物件的三種方式,第二種是通過new建立物件,

var  obj = new Object()//建立一個空物件等同於 var obj = {}
console.log(obj.constructor===Object)//true
複製程式碼

Object就是一個建構函式,是js內建的建構函式,上面的例子中Object就是obj的建構函式,這個例子似乎不太明顯,我們繼續看

function Foo(name){
    this.name = name
}
var foo = new Foo("陌上寒")
console.log(foo.constructor===Foo)//true
複製程式碼

我們自定義了一個建構函式Foo,Foo是foo的建構函式,foo的建構函式就是Foo 建構函式與其他函式的唯一區別,就在於呼叫它們的方式不同。不過,建構函式畢竟也是函式,不存在定義建構函式的特殊語法。任何函式,只要通過 new 操作符來呼叫,那它就可以作為建構函式;而任何函式,如果不通過 new 操作符來呼叫,那它跟普通函式也不會有什麼兩樣。 建構函式在建立時有一個約定,如果是建構函式,那麼首字母要大寫,普通函式首字母小寫

constructor和prototype

constructor和我們昨天討論的prototype有什麼聯絡嗎? 觀察如下程式碼的輸出

function Foo(name) {
    this.name = name;
}
var foo = new Foo('陌上寒');
console.log(Foo.prototype)
複製程式碼

通過昨天的討論我們得知只有函式物件才存在prototype 輸出

關於javascript的原型和原型鏈,看我就夠了(二)
Foo.prototype是Foo的原型物件 繼續觀察

function Foo(name) {
    this.name = name;
}
var foo = new Foo('陌上寒');
 console.log(Foo.prototype.constructor===Foo)//true
複製程式碼

在預設情況下,所有原型物件都會自動獲得一個 constructor(建構函式)屬性,這個屬性包含一個指向 prototype 屬性所在函式的指標。就拿前面的例子來說,Foo.prototype.constructor 指向 Foo。 我們得出以下結論 原型物件中的constructor屬性,指向該原型物件對應的建構函式 也就是說上面的例子,Foo的原型物件是Foo.prototype,原型物件(Foo.prototype)中有一個constructor屬性,這個constructor屬性指向原型物件(Foo.prototype)對應的建構函式Foo,用一行程式碼概括

 console.log(Foo.prototype.constructor===Foo)//true
複製程式碼

以上就是constructor和prototype的關係 我們注意到原型物件(Foo.prototype)中還存在一個屬性_proto_,這又是什麼?它和prototype,constructor又有什麼關聯呢?

隱式原型(_proto_)

那麼__proto__是什麼?每個物件都會在其內部初始化一個屬性,就是_proto_。 Firefox、Safari 和 Chrome 的每個物件上都有這個屬性 ,而在其他瀏覽器中是完全不可見的(為了確保瀏覽器相容性問題,不要直接使用 _proto_ 屬性,此處只為演示)。我們繼續看程式碼

var arr = new Array()
console.log(arr.__proto__===Array.prototype);//true
var str = new String()
console.log(str.__proto__===String.prototype);//true
var Fun = new Function()
console.log(Fun.__proto__===Function.prototype);//true
var bool = new Boolean
console.log(bool.__proto__===Boolean.prototype);//true
var obj = new Object()
console.log(obj.__proto__===Object.prototype);//true
function MyFun() {
	console.log("我是陌上寒");
}
var myfoo = new MyFun()
console.log(myfoo.__proto__===MyFun.prototype);//true
複製程式碼

再重複一次:Array,String,Function,Boolean,Object都是js內建的建構函式,MyFun是自定義的建構函式 只有函式物件才存在prototype 所有物件(除了Object.prototype)都存在_proto_ 剛才我們討論過,普通物件都是通過函式建立的 根據以上我們得出結論: 普通物件__proto__指向當前函式物件的原型, 你可能發現了,有一個矛盾的地方,所有物件都存在_proto_,只有普通物件的__proto__指向當前函式物件的原型,那函式物件的__proto__指向哪裡呢?繼續看程式碼

function MyFun() {
	console.log("我是陌上寒");
}
console.log(Boolean.__proto__);//ƒ () { [native code] }
console.log(Function.__proto__);//ƒ () { [native code] }
console.log(String.__proto__);//ƒ () { [native code] }
console.log(Array.__proto__);//ƒ () { [native code] }
console.log(Object.__proto__);//ƒ () { [native code] }
console.log(MyFun.__proto__);//ƒ () { [native code] }
複製程式碼

函式物件的__proto__輸出的都是ƒ () { [native code] } 函式內部是[native code],也就是系統編譯好的二進位制程式碼函式,我們不對此做研究 上面說到,所有物件都有_proto_,原型物件也是物件, 我們得出結論 原型物件也存在_proto_ 結合以上我門又一次得出結論 原型物件的__proto__指向當前函式物件的原型, 還是繼續看程式碼,便於理解*

console.log('陌上寒'.__proto__===String.prototype);//true
console.log(String.prototype.__proto__===Object.prototype);//true
//等量代換,得出一下結論
console.log('陌上寒'.__proto__.__proto__===Object.prototype);//true
//自此形成了一條鏈,===>原型鏈
複製程式碼

解釋一下如上程式碼, '陌上寒'是字串型別,'陌上寒'的建構函式是String(), 所以'陌上寒'的__proto__指向String的原型 String()是js的內建建構函式,繼承自Object,也就是說Object是頂端,是原型鏈的頂端,既然是頂端,所以:

console.log(Object.prototype.__proto__)//null
複製程式碼

Object的原型物件是不存在__proto__的

總結

所有物件(不包括Object.prototype)有__proto__屬性,函式物件有prototype屬性; 物件由函式生成; 生成物件時,物件的__proto__屬性指向當前函式的prototype屬性。 Object.prototyp處於原型鏈的頂端,不存在原型,不繼承任何屬性,其他原型物件都是普通物件,普通物件都具有原型,所有的內建建構函式(以及大部分自定義建構函式)都具有一個繼承自Object.prototype的原型,例如Date.prototype的 屬性繼承自Object.prototype,因此有new Date()建立的Date物件的屬性同時繼承自Date.prototype和Object.prototype,這一系列的原型物件就是所謂的原型鏈。

原文連結

關於javascript的原型和原型鏈,看我就夠了(一)

參考連結 《JavaScript 闖關記》之原型及原型鏈

JavaScript之原型與原型鏈

一篇文章帶你理解原型和原型鏈

徹底理解JavaScript原型鏈(一)—__proto__的預設指向

圖解prototype、proto和constructor的三角關係

Object.prototype 原型和原型鏈

三張圖搞懂JavaScript的原型物件與原型鏈

關於javascript的原型和原型鏈,看我就夠了(二)

相關文章