prototype
- 每個函數(shù)都有prototype屬性熏兄,它指向函數(shù)的原型對象(Person.prototype)(從下面的例子中可以看出它默認(rèn)指向的是object空對象,而Date構(gòu)造函數(shù)會有很多方法是因為在原型中添加了方法)酥艳,通過函數(shù)創(chuàng)建的對象也將會擁有該原型對象。
function Person() {}
let p = new Person()
console.log(Date.prototype,'---',Person.prototype)
//{constructor: ? Date()
//getDate: ? getDate()
//getDay: ? getDay()
//getFullYear: ? getFullYear()
//getHours: ? getHours()……} --- {constructor: ?}
- 這樣說或許有點抽象,不如先來舉個例子
var arr1 = [2,4,6,4,3]
var arr2 = [3,5,3,6,4,8,3]
arr1.sort(function(num1,num2) {
return num1 - num2
})
arr2.sort(function(num1,num2) {
return num1 - num2
})
console.log(arr1) //[2, 3, 4, 4, 6]
console.log(arr2) //[3, 3, 3, 4, 5, 6, 8]
console.log(arr1 === arr2) //false
console.log(arr1.sort === arr2.sort) //true
上述代碼定義了兩個數(shù)組并調(diào)用了sort方法來講數(shù)組重新排序杖狼,在第三個輸出中很明顯兩個數(shù)組是不相等的,但是它們調(diào)用的sort方法卻是相等的术徊,這是為什么呢本刽?我們再看一個例子:
var arr1 = [2,4,6,4,3]
var arr2 = [3,5,3,6,4,8,3]
arr1.getSum = function() {
var sum = 0
for(var i = 0;i < this.length;i++) {
sum += this[i]
}
return sum
}
console.log(arr1.getSum()) //19
console.log(arr2.getSum()) //報錯Uncaught TypeError: arr2.getSum is not a function
這個例子可以看出getSum函數(shù)只存在于arr1中,那為什么sort方法卻能被兩個數(shù)組同時使用呢赠涮?結(jié)合上面的例子可以猜測出一定有什么東西存放著所有數(shù)組都能共享的方法sort子寓,那么就引出了原型(prototype)這個概念。通過原型的方法笋除,我們能夠做到讓getSum函數(shù)也能被所有數(shù)組共享斜友。
var arr1 = [2,4,6,4,3]
var arr2 = [3,5,3,6,4,8,3]
Array.prototype.getSum = function() {
var sum = 0
for(var i = 0;i < this.length;i++) {
sum += this[i]
}
return sum
}
console.log(arr1.getSum()) //19
console.log(arr2.getSum()) //32
console.log(arr1.getSum === arr2.getSum) //true
這個例子更能佐證了arr1和arr2是調(diào)用Array.prototype中的sort方法(當(dāng)然Array.prototype中也還有很多其它的方法),如果不放心垃它,大可打印出來看看:
console.log(arr1.sort === Array.prototype.sort) //true
- 添加給prototype的屬性將會成為使用這個構(gòu)造函數(shù)創(chuàng)建的對象的通用屬性,方法同理
function Person(name,age) {
this.name = name
this.age = age
}
Person.prototype.profession = 'programmer'
var p1 = new Person('Joe',20)
var p2 = new Person('Bob',21)
var p3 = new Person('Mike',22)
console.log(p1.name,p1.age,p1.profession) //Joe 20 programmer
console.log(p2.name,p2.age,p2.profession) //Bob 21 programmer
console.log(p3.name,p3.age,p3.profession) //Mike 22 programmer
- 在外部不能通過prototype改變自定義類型的屬性或方法
function Person() {
this.name = 'Joe'
this.say = function() {
console.log('hello')
}
}
Person.prototype.name = 'Bob'
Person.prototype.say = function() {
console.log('Hi!')
}
var p1 = new Person()
console.log(p1.name) //Joe
p1.say() //hello
- 函數(shù)中的prototype屬性指向一個prototype對象(注意區(qū)別prototype屬性與prototype對象是兩個不同的東西)鲜屏。在prototype對象中有一個constructor屬性烹看,這個constructor屬性同樣指向一個constructor對象,而這個constructor對象恰恰就是這個函數(shù)本身
function Person() {
this.name = name
}
console.log(Person.prototype.constructor === Person) //true
可以用圖片這樣子表示:
- prototype 是一個指向該實例所使用的原型對象的指針
- prototype 是函數(shù)的屬性洛史,而不是對象的屬性
function Person(name) {
this.name = name;
}
var p1 = new Person()
console.log(p1.prototype) //undefined
- prototype一般共享方法惯殊,不要共享數(shù)據(jù)
原型鏈
- 每個對象都會有原型,而該對象的原型指向原型對象(即父級對象)也殖,父級對象的原型又指向父級的父級土思,這種原型層層連接起來的就構(gòu)成了原型鏈
- 實例對象的
__proto__
(隱式原型)指向它構(gòu)造函數(shù)的prototype(顯式原型)
function Person(name) { //內(nèi)部語句:this.prototype = {}
this.name = name
}
var p1 = new Person() //內(nèi)部語句:this.__proto__ = Person.prototype
console.log(p1.__proto__ === Person.prototype) //true
實例對象與其構(gòu)造函數(shù)的關(guān)系可表示為:
- 可以強行改變某實例對象的父級
function Person(name) {
this.name = name;
}
function Workder () {
this.sayHello = function() {
console.log('worker')
}
}
Person.prototype = new Workder()
var son = new Person()
console.log(son.__proto__) //Workder
- 前面說過prototype是一個指向該實例所使用的原型對象的指針,也就是說原型對象也是一個對象忆嗜,那么它也可以由最原始創(chuàng)建對象的方法創(chuàng)建出來:
var p1 = new Object()
p1.name = 'Joe'
console.log(p1.name) // Joe
這個例子中己儒,p1實例對象是由Object構(gòu)造函數(shù)創(chuàng)建出來的,那么也符合p1.__proto__ === Object.prototype
捆毫,因為原型對象也是對象闪湾,我們假設(shè)p1本來就是某個對象的原型對象,那么Object.prototype也就是該原型對象__proto__
指向的原型對象绩卤,而所有對象都可以通過new Object()
來創(chuàng)建途样,所以原型鏈最終都會指向Object.prototype
。
function Person(name) {
this.name = name
}
var p1 = new Person()
console.log(p1.__proto__.__proto__ === Object.prototype) //true
該例再次說明了原型的原型確實會指向Object.prototype
省艳,而原型鏈這條“鏈”便是一個個__proto__
連起來的娘纷。
那么鏈的終點是什么呢?
console.log(Object.prototype.__proto__) //null
可見跋炕,原型鏈的終點是null
- 實例訪問屬性或者方法的時候赖晶,遵循以為原則:如果實例上面存在,就用實例本身的屬性和方法辐烂,否則遏插,就會順著
__proto__
的指向一直往上查找,查找就停止纠修。
var parent = {
say() {
console.log('Hello')
}
}
function Person(name) {
this.name = name
}
Person.prototype = parent
var p1 = new Person()
p1.say() //Hello
//p1.sayName() //報錯Uncaught TypeError: p1.sayName is not a function
console.log(p1.age) //undefined
var p2 = new Person()
p2.say = function() {
console.log('Hi')
}
p2.say() //Hi
總結(jié)
//構(gòu)造函數(shù)
function Foo() {}
//實例對象
let f1 = new Foo()
let o1 = new Object()
console.log(f1.__proto__ === Foo.prototype) //true
console.log(Foo.prototype.constructor === Foo) //true
console.log(Foo.prototype.__proto__ === Object.prototype) //true 沿著原型鏈往上找都會找到Object.prototype
console.log(o1.__proto__ === Object.prototype) //true
console.log(Object.prototype.__proto__) //null 原型鏈的終點指向null
console.log(Foo.prototype.constructor === Foo) //true
console.log(Function.prototype.constructor === Function) //true
console.log(Object.__proto__ === Function.prototype) //true 內(nèi)置的引用類型(包括Object)其實也是一個函數(shù)對象胳嘲,都是由Function創(chuàng)建的,所以原型會指向Function.prototype
console.log(Foo.__proto__ === Function.prototype) //true
console.log(Function.prototype.prototype === undefined) //true Function.prototype是個特例扣草,它是函數(shù)對象了牛,但是沒有prototype屬性。其他所有函數(shù)都有prototype屬性辰妙。
*由上圖可知:
- 函數(shù)的顯式原型指向的對象默認(rèn)是空Object實例對象(但Object不滿足)
console.log(Fn.prototype instanceof Object) //true
console.log(Object.prototype instanceof Object) //false
console.log(Function.prototype instanceof Object) //true
- 所有函數(shù)都是Function的實例(包括Function)
console.log(Function.__proto__ === Function.prototype) //true
注:插入的圖片來自于網(wǎng)絡(luò)鹰祸。