摘要:通過同一個(gè)構(gòu)造函數(shù)實(shí)例化的多個(gè)實(shí)例對(duì)象具有同一個(gè)原型對(duì)象。所以當(dāng)給原型對(duì)象賦值一個(gè)新對(duì)象時(shí),切記將原型對(duì)象的指回原構(gòu)造函數(shù)以上就是本次分享的內(nèi)容,關(guān)于原型對(duì)象的其他知識(shí),下一篇基礎(chǔ)原型對(duì)象的那些事二會(huì)講到。
談起js的基礎(chǔ),繞不過去的坎就是:原型鏈、作用域鏈、this(em...好吧,還有閉包),今天總結(jié)一下關(guān)于原型對(duì)象的一些知識(shí),供自己和大家復(fù)習(xí)。
概念理解什么是原型對(duì)象呢?有以下幾點(diǎn):
1.構(gòu)造函數(shù)有一個(gè)prototype屬性,指向構(gòu)造函數(shù)的原型對(duì)象。而實(shí)例有一個(gè)__proto__屬性,也指向原型對(duì)象。
PS: 準(zhǔn)確的說,實(shí)例指向原型對(duì)象的是[[Prototype]]屬性,但這是一個(gè)隱式屬性,腳本不可訪問。因此瀏覽器廠商提供了一個(gè)屬性__proto__,用來顯式指向原型對(duì)象,但它并不是ECMA規(guī)范。
注意函數(shù)的是prototype屬性,實(shí)例的是__proto__屬性,不要弄錯(cuò)。
舉個(gè)栗子,我們有一個(gè)構(gòu)造函數(shù)Person:
function Person(name) { this.name = name }
這時(shí),我們創(chuàng)建一個(gè)Person的實(shí)例person:
var person = new Person("張三")
按照上邊的理論,就可以表示為:
Person.prototype === person.__proto__
他們指向的都是原型對(duì)象。
2.通過同一個(gè)構(gòu)造函數(shù)實(shí)例化的多個(gè)實(shí)例對(duì)象具有同一個(gè)原型對(duì)象。
var person1 = new Person("張三") var person2 = new Person("李四")
person1.__proto__、person2.__proto__ 、Person.prototype 他們是兩兩相等的。
3.原型對(duì)象有一個(gè)constructor屬性,指向該原型對(duì)象對(duì)應(yīng)的構(gòu)造函數(shù)。
Person.prototype.constructor === Person person.__proto__.constructor === Person
4.實(shí)例對(duì)象本身并沒有constructor屬性,但它可以繼承原型對(duì)象的constructor屬性。
person1.constructor === Person person2.constructor === Person作用
OK,說清楚了什么是原型,就要說一下這玩意是干嘛用的,為啥要在構(gòu)造函數(shù)里加這么個(gè)東西。
還是以構(gòu)造函數(shù)Person為例,稍微改一下:
function Person(name) { this.name = name this.sayName = function() { console.log(this.name) } } var person1 = new Person("張三") var person2 = new Person("李四")
我們?cè)跇?gòu)造函數(shù)Person中增加了一個(gè)方法sayName,這樣Person的實(shí)例person1、person2就各自都有了一個(gè)sayName方法。
注意,我說的是各自,什么意思呢?就是說每次創(chuàng)建一個(gè)實(shí)例,就要在內(nèi)存中創(chuàng)建一個(gè)sayName方法,這些sayName并不是同一個(gè)sayName。
person1.sayName === person2.sayName -> false
多個(gè)實(shí)例重復(fù)創(chuàng)建相同的方法,這顯然是浪費(fèi)資源的。這個(gè)時(shí)候,我們的原型對(duì)象登場(chǎng)了。假如構(gòu)造函數(shù)中的方法我們這樣寫:
function Person(name) { this.name = name } Person.prototype.sayName = function() { console.log(this.name) } var person1 = new Person("張三") var person2 = new Person("李四")
和之前的區(qū)別是,我們將sayName方法寫到了構(gòu)造函數(shù)的原型對(duì)象上,而不是寫在構(gòu)造函數(shù)里。
這里要先提一個(gè)概念,就是當(dāng)對(duì)象找屬性或者方法時(shí),先在自己身上找,找到就調(diào)用。在自己身上找不到時(shí),就會(huì)去他的原型對(duì)象上找。這就是原型鏈的概念,先點(diǎn)到這,大家知道這件事就可以了。
還記得之前說的嗎:
通過同一個(gè)構(gòu)造函數(shù)實(shí)例化的多個(gè)實(shí)例對(duì)象具有同一個(gè)原型對(duì)象
person1和person2上顯然是沒有sayName方法的,但是他們的原型對(duì)象有啊。
所以這里的person1.sayName和person2.sayName,實(shí)際上都是繼承自他原型對(duì)象上的sayName方法,既然原型對(duì)象是同一個(gè),那sayName方法自然也是同一個(gè)了,所以此時(shí):
person1.sayName === person2.sayName -> true
將需要共享的方法和屬性放到原型對(duì)象上,實(shí)例在調(diào)用這些屬性和方法時(shí),不用每次都創(chuàng)建,從而節(jié)約資源,這就是原型對(duì)象的作用。
共享帶來的“煩惱”但是,既然是共享,就有一點(diǎn)問題了,還是Person構(gòu)造函數(shù),我們?cè)俑脑煲幌隆?/p>
function Person(name) { this.name = name } Person.prototype.ageList = [12, 16, 18] var person1 = new Person("張三") var person2 = new Person("李四")
這個(gè)時(shí)候,我們?cè)?b>person1上做一些操作:
person1.ageList.push(30)
看一下此時(shí)person2.ageList是什么:
person2.ageList -> [12, 16, 18, 30]
有點(diǎn)意思,person2上的ageList也多了30。
原因其實(shí)還是因?yàn)楣蚕怼?/p>
共享不好的地方就是:一個(gè)實(shí)例對(duì)引用類型(數(shù)組、對(duì)象、函數(shù))的屬性進(jìn)行修改,會(huì)導(dǎo)致原型對(duì)象上的屬性修改(其實(shí)修改的就是原型對(duì)象上的屬性,實(shí)例是沒有這個(gè)屬性的),進(jìn)而導(dǎo)致所有的實(shí)例中,這個(gè)屬性都改了!
很顯然,大部分時(shí)候,我們喜歡共享,可以節(jié)約資源。但是不喜歡每一個(gè)實(shí)例都受影響,要不還創(chuàng)建不同的實(shí)例干嘛,用一個(gè)不就好了(攤手)。
所以,我們需要把那些需要共享的屬性和方法,寫在原型對(duì)象上,而每個(gè)實(shí)例多帶帶用的、不希望互相影響的屬性,就寫在構(gòu)造函數(shù)里邊。類似這樣:
function Person(name) { this.name = name this.ageList = [12, 16, 18] } var person1 = new Person("張三") var person2 = new Person("李四") person1.ageList.push(30) person1.ageList -> [12, 16, 18, 30] person2.ageList -> [12, 16, 18]此處有坑
關(guān)于原型對(duì)象,還有兩個(gè)坑,需要和大家說一下。
1.
function Person(name) { this.name = name } Person.prototype.ageList = [12, 16, 18] var person1 = new Person("張三") var person2 = new Person("李四") person1.ageList.push(30) person2.ageList -> [12, 16, 18, 30]
這個(gè)沒毛病,但是假如我把操作
person1.ageList.push(30)
改為
person1.ageList = [1, 2, 3],結(jié)果會(huì)怎樣呢?
person2.ageList -> [12, 16, 18]
這里就奇怪了,都是對(duì)person1.ageList進(jìn)行操作,怎么就不一樣呢?
其實(shí)原因在于,person1.ageList = [1, 2, 3]是一個(gè)賦值操作。
我們說過,person1本身是沒有ageList屬性的,而賦值操作,會(huì)給person1增加自己的ageList屬性。既然自己有了,也就不用去原型對(duì)象上找了。這個(gè)時(shí)候,原型對(duì)象的ageList其實(shí)是沒有變化的。而person2沒有自己的ageList屬性,所以person2.ageList還是繼承自原型,就是[12, 16, 18]。
2.
function Person(name) { this.name = name } Person.prototype = { ageList : [12, 16, 18] } var person1 = new Person("張三") var person2 = new Person("李四") person1.ageList.push(30) person2.ageList -> [12, 16, 18, 30]
這里依然沒毛病,但是寫法上有一個(gè)變化:我們不再采用Person.prototype.ageList = [12, 16, 18]的形式賦值,而是給Person.prototype賦值了一個(gè)對(duì)象,對(duì)象中有ageList。
這樣看貌似沒有問題,用起來也都一樣:改變person1.ageList,person2.ageList也變化了,說明person1.ageList和person2.ageList還是繼承自同一個(gè)原型對(duì)象。
但是,這里有一個(gè)問題,之前我們說過:
實(shí)例對(duì)象本身并沒有constructor屬性,但它可以繼承原型對(duì)象的constructor屬性
但是此時(shí)
person1.constructor === Person -> false person2.constructor === Person -> false
為什么呢?
因?yàn)橥ㄟ^給Person.prototype賦值一個(gè)對(duì)象,就修改了原型對(duì)象的指向,此時(shí)原型對(duì)象的constructor指向內(nèi)置構(gòu)造函數(shù)Object了,使用Person.prototype.ageList = [12, 16, 18]的形式賦值,就不會(huì)造成這樣的問題。
所以當(dāng)給原型對(duì)象賦值一個(gè)新對(duì)象時(shí),切記將原型對(duì)象的constructor指回原構(gòu)造函數(shù):
Person.prototype.constructor = Person
以上就是本次分享的內(nèi)容,關(guān)于原型對(duì)象的其他知識(shí),下一篇JS基礎(chǔ)—原型對(duì)象的那些事(二)會(huì)講到。
ps:寫的啰啰嗦嗦的,也不知道大家會(huì)不會(huì)看著煩。。。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/84900.html
摘要:只是構(gòu)造函數(shù)上的一個(gè)屬性,它是一個(gè)指針,指向原型對(duì)象,并不表示就是原型對(duì)象。在上一個(gè)例子中,就是一個(gè)對(duì)象,這個(gè)對(duì)象可以說是原生構(gòu)造函數(shù)的實(shí)例,所以也是一個(gè)對(duì)象,所以它也有屬性,不過它的指向也就是原型鏈的頂端,再往上就沒有了。 上一篇講了①原型對(duì)象是什么;②__proto__、prototype、constructor的關(guān)系;③原型對(duì)象的作用;④原型對(duì)象帶來的一些需要注意的問題; 沒理解...
摘要:的擴(kuò)展知識(shí)對(duì)于哈希表來說,最重要的莫過于生成哈希串的哈希算法和處理沖突的策略了。由于鏈表的查找需要遍歷,如果我們將鏈表換成樹或者哈希表結(jié)構(gòu),那么就能大幅提高沖突元素的查找效率。 最近在整理數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的知識(shí),小茄專門在github上開了個(gè)repo https://github.com/qieguo2016...,后續(xù)內(nèi)容也會(huì)更新到這里,歡迎圍觀加星星! js對(duì)象 js中的對(duì)象是基...
摘要:有需要還可以修改指向謙龍寄生組合式繼承思路是通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混合形式來繼承方法改變執(zhí)行環(huán)境實(shí)現(xiàn)繼承有需要還可以修改指向謙龍謙龍拷貝繼承該方法思路是將另外一個(gè)對(duì)象的屬性和方法拷貝至另一個(gè)對(duì)象使用遞歸 前言 js中實(shí)現(xiàn)繼承的方式只支持實(shí)現(xiàn)繼承,即繼承實(shí)際的方法,而實(shí)現(xiàn)繼承主要是依靠原型鏈來完成的。 原型鏈?zhǔn)嚼^承 該方式實(shí)現(xiàn)的本質(zhì)是重寫原型對(duì)象,代之以一個(gè)新類型的實(shí)例...
摘要:委托上面的代碼結(jié)合了構(gòu)造函數(shù)和原型兩種方式去創(chuàng)建對(duì)象,首先聊聊構(gòu)造函數(shù)構(gòu)造函數(shù)構(gòu)造函數(shù)本質(zhì)上還是函數(shù),只不過為了區(qū)分將其首字母大寫了而已。注意注釋掉的代碼是自動(dòng)執(zhí)行的,但這并不是構(gòu)造函數(shù)獨(dú)有的,每個(gè)函數(shù)在聲明時(shí)都會(huì)自動(dòng)生成。 首先看看下面兩個(gè)1+1=2的問題: 問題一:為什么改變length的值,數(shù)組的內(nèi)容會(huì)變化? var arr = [1]; arr.length = 3; aler...
摘要:如圖遍歷數(shù)組遍歷數(shù)組元素并以某種方式處理每個(gè)元素是一個(gè)常見的操作。如圖不過,里的功能比較強(qiáng)大,可以遍歷而且返回值是的則被省略掉總結(jié)遍歷對(duì)象,遍歷出來的是鍵名,而不是鍵值,參數(shù)必須是對(duì)象。 可能是由于職業(yè)的關(guān)系,下班之后完全不想Open PC,但很多知識(shí)點(diǎn)有必要自己做個(gè)小小總結(jié)。本人之前對(duì)原生Array和Object完全沒概念。 遍歷對(duì)象的方法: Object.keys(Object)...
閱讀 2340·2023-04-25 14:17
閱讀 1532·2021-11-23 10:02
閱讀 2177·2021-11-23 09:51
閱讀 890·2021-10-14 09:49
閱讀 3392·2021-10-11 10:57
閱讀 2930·2021-09-24 09:47
閱讀 3058·2021-08-24 10:00
閱讀 2307·2019-08-29 18:46