構造函數(shù)創(chuàng)建對象
function Person() {
}
let person1 = new Person()
let person2 = new Person()
person1.name = 'james'
person2.name = 'kobe'
console.log(person1);
console.log(person2);
我們通過new來創(chuàng)建一個person實例垃帅,我們可以看到不同的實例擁有自己的屬性漫蛔。
proto
我們可以看到每個對象下都會有proto的屬性,這個屬性會指向該對象的原型
function Person() {
}
Person.prototype.name = 'chris'
let person = new Person()
let person1 = new Person()
let person2 = new Person()
person1.name = 'james'
person2.name = 'kobe'
console.log(person);
console.log(person1);
console.log(person2);
我們看到proto下會出現(xiàn)prototype的name屬性访得,那么proto與prototype關系又是什么呢州既?
prototype
每個函數(shù)都有具有 prototype屬性,就是我們經(jīng)常在用到的prototype
Person.prototype.name = 'chris'
那么問題來了羔巢,那這個函數(shù)的 prototype 屬性到底指向的是什么呢望忆?是這個函數(shù)的原型嗎?
其實竿秆,函數(shù)的 prototype 屬性指向了一個對象启摄,這個對象正是調(diào)用該構造函數(shù)而創(chuàng)建的實例的原型,也就是這個例子中的 person1 和 person2 的原型幽钢。
那原型是什么呢歉备?可以這樣理解:每一個JavaScript對象(null除外)在創(chuàng)建的時候就會與之關聯(lián)另一個對象,這個對象就是我們所說的原型匪燕,每一個對象都會從原型所謂的繼承屬性蕾羊。
通過實例的proto和構造函數(shù)的prototype的對比谎懦,我們不難發(fā)現(xiàn)person 和 Person.prototype 的關系
person.__proto__ === Person.prototype //true
既然實例對象和構造函數(shù)都可以指向原型,那么原型是否有屬性指向構造函數(shù)或者實例呢界拦?
constructor
不難發(fā)現(xiàn),每個構造函數(shù)都有 constructor這個屬性享甸, 通過控制臺我們會發(fā)現(xiàn)constructor 屬性指向關聯(lián)的構造函數(shù)
這樣我們就了解了構造函數(shù)截碴、實例原型蛉威、和實例之間的關系,接下來我們講講實例和原型的關系:
實例下的原型
我們知道如果讀取不到實例的屬性時蚯嫌,就會查找與對象關聯(lián)的原型中的屬性哲虾,如果還查不到丙躏,就去找原型的原型,一直找到最頂層為止束凑。
function Person() {
}
console.log(Person.prototype.name); //underfind
Person.prototype.name = 'chris';
var person = new Person();
person.name = 'james';
console.log(person.name) // james 拿到實例的name屬性
delete person.name;
console.log(person.name) // chris 拿到原型的name屬性
但是萬一還沒有讀取到呢?原型下的原型又是什么呢废恋?
原型下的原型
通過上面的知識我們知道person.proto與Person.protype相等扒寄,那么Person.prototype.proto下又是什么呢?很顯然就是對象實例的proto
function Person() { }
let person = new Person()
let obj = new Object()
console.log(Person.prototype.__proto__ === obj.__proto__) //true
console.log(Person.prototype.__proto__ === Object.prototype) //true
console.log(obj.__proto__.__proto__) //null
let obj = new Object();
obj.__proto__.name = 'chris'
obj.name = 'Kevin'
console.log(obj.name) // Kevin
delete obj.name
console.log(obj.name) // chris
原型鏈
既然我們知道Object的原型迄本,那 Object.prototype的原型呢?
我們可以看到返回一個null岸梨,表達的就是已經(jīng)沒有原型了稠氮。
最終就是原型和原型鏈的結構
一些補充
關于Funtion
中的原型 我們可以會發(fā)現(xiàn)Function.prototype
有些特殊
Function.prototype === Function.__proto__ //true
這樣看上去實例的原型和原型的原型是相等的隔披,即是雞也是蛋寂拆。 我們可以參考MDN關于proto的解釋:
proto的讀取器(getter)暴露了一個對象的內(nèi)部
[[Prototype]]
。對于使用對象字面量創(chuàng)建的對象纠永,這個值是Object.prototype
。對于使用數(shù)組字面量創(chuàng)建的對象涉波,這個值是Array.prototype
炭序。對于functions,這個值是Function.prototype
惭聂。對于使用 new fun 創(chuàng)建的對象,其中fun是由js提供的內(nèi)建構造器函數(shù)之一(Array
,Boolean
,Date
,Number
,Object
,String
等等)辜纲,這個值總是fun.prototype拦耐。對于用js定義的其他js構造器函數(shù)創(chuàng)建的對象见剩,這個值就是該構造器函數(shù)的prototype屬性。
Object.__proto__ === Function.prototype //true
Object.__proto__ === Function.__proto__ //true
引用冴羽的理解
至于為什么Function.proto === Function.prototype火脉,我認為有兩種可能:一是為了保持與其他函數(shù)一致柒啤,二是就是表明一種關系而已。
簡單的說方援,就是先有的Function涛癌,然后實現(xiàn)上把原型指向了Function.prototype,但是我們不能倒過來推測因為Function.proto === Function.prototype拳话,所以Function調(diào)用了自己生成了自己。
總結
1弃衍、實例對象的proto始終指向構造函數(shù)的prototype
2、只有構造函數(shù)才擁有prototype屬性岸裙,對象(除了null)都擁有proto屬性
3速缆、每一個原型對象都有一個constructor屬性指向它們的構造函數(shù)
4、要讀取屬性時艺糜,先讀取實例上的屬性,讀取不到會在原型鏈上尋找相應屬性
5送滞、原型鏈按照proto的指向下一級對象
6辱挥、原型鏈的盡頭始終是null
7、構造函數(shù)實例化以后晤碘,既是構造函數(shù)函數(shù)功蜓,也是對象
function Foo() {
}
const obj = new Foo()
Foo.prototype === obj.__proto__ //true
obj.constructor === Foo //true
Foo.prototype.__proto__ === Object.prototype //true
Object.prototype.__proto__ === null //true
Object.constructor === Function //true