一、原型
JavaScript
?常被描述為一種基于原型的語言——每個對象擁有一個原型對象
當試圖訪問一個對象的屬性時,它不僅僅在該對象上搜尋,還會搜尋該對象的原型,以及該對象的原型的原型,依次層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾
準確地說,這些屬性和方法定義在Object的構造器函數(constructor functions)之上的prototype
屬性上,而非實例對象本身
下面舉個例子:
函數可以有屬性。 每個函數都有一個特殊的屬性叫作原型prototype
function doSomething(){}
console.log( doSomething.prototype );
控制臺輸出
{constructor: ? doSomething(),__proto__: {constructor: ? Object(),hasOwnProperty: ? hasOwnProperty(),isPrototypeOf: ? isPrototypeOf(),propertyIsEnumerable: ? propertyIsEnumerable(),toLocaleString: ? toLocaleString(),toString: ? toString(),valueOf: ? valueOf()}
}
上面這個對象,就是大家常說的原型對象
可以看到,原型對象有一個自有屬性constructor
,這個屬性指向該函數,如下圖關系展示
二、原型鏈
原型對象也可能擁有原型,并從中繼承方法和屬性,一層一層、以此類推。這種關系常被稱為原型鏈 (prototype chain),它解釋了為何一個對象會擁有定義在其他對象中的屬性和方法
在對象實例和它的構造器之間建立一個鏈接(它是__proto__
屬性,是從構造函數的prototype
屬性派生的),之后通過上溯原型鏈,在構造器中找到這些屬性和方法
下面舉個例子:
function Person(name) {this.name = name;this.age = 18;this.sayName = function() {console.log(this.name);}
}
// 第二步 創建實例
var person = new Person('person')
下面分析一下:
-
構造函數
Person
存在原型對象Person.prototype
-
構造函數生成實例對象
person
,person
的__proto__
指向構造函數Person
原型對象 -
Person.prototype.__proto__
?指向內置對象,因為?Person.prototype
?是個對象,默認是由?Object
函數作為類創建的,而?Object.prototype
?為內置對象 -
Person.__proto__
?指向內置匿名函數?anonymous
,因為 Person 是個函數對象,默認由 Function 作為類創建 -
Function.prototype
?和?Function.__proto__
同時指向內置匿名函數?anonymous
,這樣原型鏈的終點就是?null
三、總結
下面首先要看幾個概念:
__proto__
作為不同對象之間的橋梁,用來指向創建它的構造函數的原型對象的
每個對象的__proto__
都是指向它的構造函數的原型對象prototype
的
person1.__proto__ === Person.prototype
構造函數是一個函數對象,是通過?Function
構造器產生的
Person.__proto__ === Function.prototype
原型對象本身是一個普通對象,而普通對象的構造函數都是Object
Person.prototype.__proto__ === Object.prototype
剛剛上面說了,所有的構造器都是函數對象,函數對象都是?Function
構造產生的
Object.__proto__ === Function.prototype
Object
的原型對象也有__proto__
屬性指向null
,null
是原型鏈的頂端
Object.prototype.__proto__ === null
下面作出總結:
-
一切對象都是繼承自
Object
對象,Object
?對象直接繼承根源對象null
-
一切的函數對象(包括?
Object
?對象),都是繼承自?Function
?對象 -
Object
?對象直接繼承自?Function
?對象 -
Function
對象的__proto__
會指向自己的原型對象,最終還是繼承自Object
對象