面向?qū)ο笾蓄愔傅氖峭活愋蛯ο蟮某橄笾ё樱鬃帜复髮懘锸妫热缜捌械男螤頢hape 類,三角形是通過Shape擴(kuò)展而來昨登,則也是一個類贯底,Shape稱之為它的父類丈甸,它是Shape的子類,同理Rect也是Shape的一個子類睦擂。類的具體抽象稱之為實例顿仇,通常為小寫
摆马,創(chuàng)建實例的過程稱之為實例化。上文中triangle就是一個Triangle三角形的實例述呐,指具體畫出的那個三角形蕉毯。關(guān)于父類,子類的實例
- 父類 Animal
- 子類 Cat 實例 cat1_tom
- 子類 Dog 實例 dog1
Animal 指所有動物进肯,Cat 指所有貓 繼承Animal 是動物的一個子類江掩,cat1_tom 指的具體一個叫 tom 的貓。有了類我們就需要給類加一些標(biāo)識环形,以區(qū)分類之間的區(qū)別抬吟、即屬性和方法。
1. JS原型
弄清楚了類是什么任洞,而JavaScript沒有類的概念发侵,是通過原型來實現(xiàn)面向?qū)ο笕婿T谝灶悶橹行牡拿嫦驅(qū)ο缶幊陶Z言中,類和對象的關(guān)系可以想象成鑄模和鑄件的關(guān)系叔锐,對象總是從類中創(chuàng)建而來愉烙。而在原型編程的思想中,類并不是必需的返顺,對象未必需要從類中創(chuàng)建而來蔓肯,一個對象是通過克隆另外一個對象所得到的。
ES6為了在繼承上與傳統(tǒng)面向?qū)ο笳Z言更加類似秉扑,引入了class调限。
ES5 的繼承,實質(zhì)是先創(chuàng)造子類的實例對象 this吨娜,然后再將父類的方法添加到 this 上面(Parent.apply(this))宦赠。ES6 的繼承機(jī)制完全不同,實質(zhì)是先將父類實例對象的屬性和方法勾扭,加到 this 上面(所以在 constructor 里必須先調(diào)用super方法)妙色,然后再用子類的構(gòu)造函數(shù)修改this。
從設(shè)計模式的角度講丐谋,原型模式是用于創(chuàng)建對象的一種模式煌珊,如果我們想要創(chuàng)建一個對象定庵,一種方法是先指定它的類型,然后通過類來創(chuàng)建這個對象猪落。原型模式選擇了另外一種方式畴博,我們不再關(guān)心對象的具體類型,而是找到一個對象蜜唾,然后通過克隆來創(chuàng)建一個一模一樣的對象
。而克隆出來的這個對象會記住他的原型庶艾,由誰克隆而來,同時也會共享原型的屬性和方法擎勘。這樣一個一個對象克隆而來咱揍,則形成了一條原型鏈。對上文中的例子而言棚饵,三角形的原型是形狀煤裙,貓和狗的原型是動物掩完。
2. 構(gòu)造函數(shù)
在js中類之后跟的是一個構(gòu)造函數(shù)。
function Shape(name) {
this.val = 1;
this.name = name;
this.all = '圖形';
return this.name
}
let a = Shape('a'); // 'a'
let shape1 = new Shape('triangle');
let shape2 = new Shape('rect');
構(gòu)造函數(shù)的定義與一般函數(shù)的定義相同硼砰,注意首字母大寫且蓬。構(gòu)造函數(shù)本質(zhì)上還是一個函數(shù),可以傳參可以有返回值题翰,只是內(nèi)部使用了this變量,函數(shù)存在調(diào)用問題:
直接調(diào)用:在瀏覽器環(huán)境中相當(dāng)于在window上掛在了val這個屬性豹障,值為1冯事。請注意這個特點,如果Shape.call(obj) 即相當(dāng)于設(shè)定obj對象的val為1血公。
new 調(diào)用:生成一個實例昵仅,即生成一個新對象,這個this指向當(dāng)前新生成的對象累魔。
constructor和prototype
實例/構(gòu)造函數(shù)(構(gòu)造器)
的關(guān)系是
A為B的構(gòu)造函數(shù) 則 B為A的一個實例摔笤。
首先創(chuàng)建一個Cat的構(gòu)造函數(shù),希望say是Cat的實例共享屬性垦写,
function Cat(name) {
this.name = name;
this.say = function() {console.log(this.name)};
}
let cat1 = new Cat('tom');
let cat2 = new Cat('bob');
cat1.say === cat2.say // false
但是發(fā)現(xiàn)cat1 cat2的共有方法all并沒有共享吕世,每一個實例對象,都有自己的屬性和方法的副本梯澜。這不僅無法做到數(shù)據(jù)共享寞冯,也是極大的資源浪費(fèi), 那么引入prototype對象:
function Cat(name) {
this.name = name;
}
Cat.prototype.say = function() {
console.log(this.name);
}
let cat1 = new Cat('tom');
let cat2 = new Cat('bob');
cat1.say === cat2.say
cat1.say === Cat.prototype.say; // true
cat1.prototype; // undefined
cat1.hasOwnProperty('say');// false
實例對象的constructor屬性指向其構(gòu)造函數(shù)(1)晚伙,這樣看起來實例對象好像“繼承”了prototype對象一樣吮龄。實例沒有prototype,上文最后一行代碼通過hasOwnPropertyk可以判斷say這個方法并不是cat1自己的方法咆疗,如果一個方法沒有在實例對象自身找到漓帚,則向其構(gòu)造函數(shù)prototype中開始尋找(2)。
既然實例是繼承自構(gòu)造器的prototype午磁,那么有沒有一個屬性可以直接表示對象的繼承關(guān)系呢尝抖?答案是有的proto,很多瀏覽器都實現(xiàn)了這個屬性迅皇,如下所示昧辽。
cat1.__proto__ === Cat.prototype // true
Cat.__proto__ === Function.prototype; // true
Function.prototype.__proto__ === Object.prototype; // true
從上我們可以發(fā)現(xiàn) Cat 構(gòu)造器的原型為Function.prototype ,Cat.prototype的原型為Object.prototype登颓,所以當(dāng)cat1調(diào)toString時 Cat.prototype上沒有找到 就去Function.prototype上尋找搅荞,這就構(gòu)成了原型鏈。但是對象的原型鏈查找和構(gòu)造函數(shù)的原型查找又有一點小區(qū)別(不查Function),構(gòu)造器生成的實例對象原型鏈的查找過程可以如下表示:
cat1
=> cat1.__proto__(Cat.prototype)
=> cat1.__proto__.__proto__(Function.prototype)
=> cat1.__proto__.__proto__.__proto__ (Object.prototype)
還有通過對象字面量創(chuàng)建的對象的原型鏈查找方式
let obj = {};
obj => obj.__proto__(Object.prototype) ;
這里根據(jù)上文加粗(2)的語言可以得到Function.prototype 的構(gòu)造函數(shù)是Object(3)咕痛。
都有constructor
上文的兩個實例對象cat1 cat2痢甘,他們都具有一個屬性constructor,指向?qū)嵗臉?gòu)建函數(shù)Cat茉贡,意思是他們由Cat創(chuàng)建而來塞栅。實例有一個constructor屬性,指向其構(gòu)造函數(shù)(4)
cat1.constructor === Cat; // true
cat1.constructor === Cat; // true
Cat.constructor === Function; // true
Cat.prototype.constructor === Cat; // true
Object.constructor === Function;// true
構(gòu)造函數(shù)同樣具有construtor腔丧,指向Function放椰,Cat.prototype同樣具有construtor,指向他自身悔据,構(gòu)造函數(shù)的prototype對象的constructor指向該構(gòu)造函數(shù)(5)庄敛。
根據(jù)上文最后一行代碼 可以判斷Object 的構(gòu)造函數(shù) 是Function。則我們可以得到Object是Function的一個實例科汗。如下Object 與 Function的關(guān)系是
??Object是Function的一個實例藻烤。
??Function.prototype 是 Object 的 一個實例。
根據(jù)上文總結(jié)如下:
??實例對象的constructor指向其構(gòu)造器头滔。
??實例對象沒有prototype怖亭。
??實例對象可以通過構(gòu)造函數(shù)的prototype對象實現(xiàn)屬性方法共享±ぜ欤’
??實例對象的proto 原型指向其構(gòu)造函數(shù)的prototype對象
構(gòu)造器的constructor指向 Function兴猩。
構(gòu)造函數(shù)的prototype可以掛在公共屬性方法,prototype的constructor屬性指向該構(gòu)造函數(shù)早歇。
構(gòu)造函數(shù) 的proto 原型指向 Function.prototype倾芝。
構(gòu)造函數(shù)prototype對象的 proto 原型指向Object.prototype。
對象原型指的是對象的 proto 屬性箭跳。