JavaScript原型與原型鏈

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

可以用圖片這樣子表示:


QQ圖片20210305193109.png
  • 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)系可表示為:


QQ圖片1.png
  • 可以強行改變某實例對象的父級
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屬性辰妙。 
QQ圖片.png

*由上圖可知:

  • 函數(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ò)鹰祸。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市密浑,隨后出現(xiàn)的幾起案子蛙婴,更是在濱河造成了極大的恐慌,老刑警劉巖尔破,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件街图,死亡現(xiàn)場離奇詭異浇衬,居然都是意外死亡,警方通過查閱死者的電腦和手機餐济,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門耘擂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颤介,你說我怎么就攤上這事梳星≡蘩担” “怎么了滚朵?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長前域。 經(jīng)常有香客問我辕近,道長,這世上最難降的妖魔是什么匿垄? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任移宅,我火速辦了婚禮,結(jié)果婚禮上椿疗,老公的妹妹穿的比我還像新娘漏峰。我一直安慰自己,他們只是感情好届榄,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布浅乔。 她就那樣靜靜地躺著,像睡著了一般铝条。 火紅的嫁衣襯著肌膚如雪靖苇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天班缰,我揣著相機與錄音贤壁,去河邊找鬼。 笑死埠忘,一個胖子當(dāng)著我的面吹牛脾拆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播莹妒,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼名船,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了动羽?” 一聲冷哼從身側(cè)響起包帚,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎运吓,沒想到半個月后渴邦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疯趟,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年谋梭,在試婚紗的時候發(fā)現(xiàn)自己被綠了信峻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓮床,死狀恐怖盹舞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情隘庄,我是刑警寧澤踢步,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站丑掺,受9級特大地震影響获印,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜街州,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一兼丰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唆缴,春花似錦鳍征、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至斗忌,卻和暖如春质礼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背织阳。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工眶蕉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唧躲。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓造挽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親弄痹。 傳聞我的和親對象是個殘疾皇子饭入,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容