1 學(xué)習(xí)目標(biāo)
理解面向?qū)ο箝_發(fā)思想
掌握 JavaScript 面向?qū)ο箝_發(fā)相關(guān)模式
2.面向?qū)ο蠼榻B
2.1什么是對(duì)象
Everything is object (一切皆對(duì)象)
我們可以從兩個(gè)層次來理解對(duì)象:
(1) 對(duì)象是單個(gè)事物的抽象耳舅。
一本書碌上、一輛汽車倚评、一個(gè)人都可以是對(duì)象浦徊,一個(gè)數(shù)據(jù)庫(kù)、一張網(wǎng)頁(yè)天梧、一個(gè)與遠(yuǎn)程服務(wù)器的連接也可以是對(duì)象盔性。當(dāng)實(shí)物被抽象成對(duì)象,實(shí)物之間的關(guān)系就變成了對(duì)象之間的關(guān)系呢岗,從而就可以模擬現(xiàn)實(shí)情況冕香,針對(duì)對(duì)象進(jìn)行編程蛹尝。
(2) 對(duì)象是一個(gè)容器,封裝了屬性(property)和方法(method)悉尾。
屬性是對(duì)象的狀態(tài)突那,方法是對(duì)象的行為(完成某種任務(wù))。比如构眯,我們可以把動(dòng)物抽象為animal對(duì)象愕难,使用“屬性”記錄具體是那一種動(dòng)物,使用“方法”表示動(dòng)物的某種行為(奔跑惫霸、捕獵猫缭、休息等等)。
在實(shí)際開發(fā)中壹店,對(duì)象是一個(gè)抽象的概念猜丹,可以將其簡(jiǎn)單理解為:數(shù)據(jù)集或功能集。ECMAScript-262 把對(duì)象定義為:無序?qū)傩缘募瞎杪鋵傩钥梢园局瞪渲稀?duì)象或者函數(shù)。 嚴(yán)格來講将塑,這就相當(dāng)于說對(duì)象是一組沒有特定順序的值轮洋。對(duì)象的每個(gè)屬性或方法都有一個(gè)名字,而每個(gè)名字都 映射到一個(gè)值抬旺。
提示:每個(gè)對(duì)象都是基于一個(gè)引用類型創(chuàng)建的弊予,這些類型可以是系統(tǒng)內(nèi)置的原生類型,也可以是開發(fā)人員自定義的類型开财。
2.2什么是面向?qū)ο?/b>
面向?qū)ο蟛皇切碌臇|西汉柒,它只是過程式代碼的一種高度封裝,目的在于提高代碼的開發(fā)效率和可維護(hù)性责鳍。
面向?qū)ο缶幊?—— Object Oriented Programming碾褂,簡(jiǎn)稱 OOP ,是一種編程開發(fā)思想历葛。 它將真實(shí)世界各種復(fù)雜的關(guān)系正塌,抽象為一個(gè)個(gè)對(duì)象,然后由對(duì)象之間的分工與合作恤溶,完成對(duì)真實(shí)世界的模擬乓诽。
在面向?qū)ο蟪绦蜷_發(fā)思想中,每一個(gè)對(duì)象都是功能中心咒程,具有明確分工鸠天,可以完成接受信息、處理數(shù)據(jù)帐姻、發(fā)出信息等任務(wù)稠集。 因此奶段,面向?qū)ο缶幊叹哂徐`活、代碼可復(fù)用剥纷、高度模塊化等特點(diǎn)痹籍,容易維護(hù)和開發(fā),比起由一系列函數(shù)或指令組成的傳統(tǒng)的過程式編程(procedural programming)晦鞋,更適合多人合作的大型軟件項(xiàng)目词裤。
面向?qū)ο笈c面向過程區(qū)別:
面向過程就是親力親為,事無巨細(xì)鳖宾,面面俱到吼砂,步步緊跟,有條不紊鼎文。
面向?qū)ο缶褪钦乙粋€(gè)對(duì)象渔肩,指揮得結(jié)果巢株。
面向?qū)ο髮?zhí)行者轉(zhuǎn)變成指揮者脱盲。
面向?qū)ο蟛皇敲嫦蜻^程的替代阿弃,而是面向過程的封裝贴唇。
面向?qū)ο蟮奶匦裕?/b>
封裝性
繼承性
多態(tài)性
2.3JavaScript?中面向?qū)ο蟮幕倔w現(xiàn)
在 JavaScript 中,所有數(shù)據(jù)類型都可以視為對(duì)象豌拙,當(dāng)然也可以自定義對(duì)象黎烈。 自定義的對(duì)象數(shù)據(jù)類型就是面向?qū)ο笾械念悾?Class )的概念唠粥。
我們以一個(gè)例子來說明面向過程和面向?qū)ο笤诔绦蛄鞒躺系牟煌帯?/p>
假設(shè)我們要處理學(xué)生的成績(jī)表胡嘿,為了表示一個(gè)學(xué)生的成績(jī)蛉艾,面向過程的程序可以用一個(gè)對(duì)象表示:
varstd1 = {name:'張三',score:98}
varstd2 = {name:'李四',score:81}
而處理學(xué)生成績(jī)可以通過函數(shù)實(shí)現(xiàn),比如打印學(xué)生的成績(jī):
functionprintScore(student){
console.log('姓名:'+ student.name +'? '+'成績(jī):'+ student.score)
}
如果采用面向?qū)ο蟮某绦蛟O(shè)計(jì)思想衷敌,我們首選思考的不是程序的執(zhí)行流程勿侯, 而是?Student?這種數(shù)據(jù)類型應(yīng)該被視為一個(gè)對(duì)象,這個(gè)對(duì)象擁有?name?和?score?這兩個(gè)屬性(Property)缴罗。 如果要打印一個(gè)學(xué)生的成績(jī)助琐,首先必須創(chuàng)建出這個(gè)學(xué)生對(duì)應(yīng)的對(duì)象,然后面氓,給對(duì)象發(fā)一個(gè)?printScore?消息兵钮,讓對(duì)象自己把自己的數(shù)據(jù)打印出來。
抽象數(shù)據(jù)行為模板(Class):
functionStudent(name, score){
this.name = name
this.score = score
}
Student.prototype.printScore =function(){
console.log('姓名:'+this.name +'? '+'成績(jī):'+this.score)
}
根據(jù)模板創(chuàng)建具體實(shí)例對(duì)象(Instance):
varstd1 =newStudent('張三',98)
varstd2 =newStudent('李四',81)
實(shí)例對(duì)象具有自己的具體行為(給對(duì)象發(fā)消息):
std1.printScore()// => 姓名:張三? 成績(jī):98
std2.printScore()// => 姓名:李四? 成績(jī) 81
面向?qū)ο蟮脑O(shè)計(jì)思想是從自然界中來的舌界,因?yàn)樵谧匀唤缰芯蚱悾–lass)和實(shí)例(Instance)的概念是很自然的。 Class 是一種抽象概念禀横,比如我們定義的 Class——Student 屁药,是指學(xué)生這個(gè)概念粥血, 而實(shí)例(Instance)則是一個(gè)個(gè)具體的 Student 柏锄,比如酿箭, 張三 和 李四 是兩個(gè)具體的 Student 。
面向?qū)ο蟮脑O(shè)計(jì)思想是:
抽象出 Class
根據(jù) Class 創(chuàng)建 Instance
指揮 Instance 得結(jié)果
面向?qū)ο蟮某橄蟪潭缺群瘮?shù)要高趾娃,因?yàn)橐粋€(gè) Class 既包含數(shù)據(jù)缭嫡,又包含操作數(shù)據(jù)的方法。
3JavaScript如何創(chuàng)建對(duì)象
3.1字面量方式
我們可以直接通過?new Object()?創(chuàng)建:
varperson =newObject()
person.name ='張三'
person.age =18
?
person.sayName =function(){
console.log(this.name)
}
每次創(chuàng)建通過?new Object()?比較麻煩抬闷,所以可以通過它的簡(jiǎn)寫形式對(duì)象字面量來創(chuàng)建:
varperson = {
name:'張三',
age:18,
sayName:function(){
console.log(this.name)
? }
}
上面的寫法是沒有問題的妇蛀,但是假如我們要生成兩個(gè)?person?實(shí)例對(duì)象呢?
varperson1 = {
name:'張三',
age:18,
sayName:function(){
console.log(this.name)
? }
}
?
varperson2 = {
name:'李四',
age:16,
sayName:function(){
console.log(this.name)
? }
}
通過上面的代碼我們不難看出笤成,這樣寫的代碼太過冗余评架,重復(fù)性太高。
3.2簡(jiǎn)單方法的改進(jìn):工廠函數(shù)
我們可以寫一個(gè)函數(shù)炕泳,解決上邊代碼重復(fù)的問題:
functioncreatePerson(name, age){
return{
name: name,
age: age,
sayName:function(){
console.log(this.name)
?? }
? }
}
生成實(shí)例對(duì)象:
varp1 = createPerson('張三',18)
varp2 = createPerson('李四',18)
這樣封裝比上邊的方式好多了纵诞,通過工廠模式我們解決了創(chuàng)建多個(gè)相似對(duì)象代碼冗余的問題, 但卻沒有解決對(duì)象識(shí)別的問題(即怎樣知道一個(gè)對(duì)象的類型)培遵。
3.3更優(yōu)雅的工廠函數(shù):構(gòu)造函數(shù)
一種更優(yōu)雅的工廠函數(shù)就是下面這樣浙芙,構(gòu)造函數(shù):
functionPerson(name, age){
this.name = name
this.age = age
this.sayName =function(){
console.log(this.name)
? }
}
?
varp1 =newPerson('張三',18)
p1.sayName()// => 張三
?
varp2 =newPerson('李四',23)
p2.sayName()// => 李四
在上面的示例中,Person()?函數(shù)取代了?createPerson()?函數(shù)籽腕,但是實(shí)現(xiàn)效果是一樣的嗡呼。 這是為什么呢?
我們注意到皇耗,Person()?中的代碼與?createPerson()?有以下幾點(diǎn)不同之處:
沒有顯示的創(chuàng)建對(duì)象
直接將屬性和方法賦給了?this?對(duì)象
沒有?return?語(yǔ)句
函數(shù)名使用的是大寫的?Person
3.4構(gòu)造函數(shù)代碼執(zhí)行過程
要?jiǎng)?chuàng)建?Person?實(shí)例南窗,則必須使用?new?操作符。 以這種方式調(diào)用構(gòu)造函數(shù)會(huì)經(jīng)歷以下 4 個(gè)步驟:
創(chuàng)建一個(gè)新對(duì)象郎楼。
將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此 this 就指向了這個(gè)新對(duì)象)矾瘾。
執(zhí)行構(gòu)造函數(shù)中的代碼。
返回新對(duì)象箭启。
下面是具體的偽代碼:
functionPerson(name, age){
// 當(dāng)使用 new 操作符調(diào)用 Person() 的時(shí)候壕翩,實(shí)際上這里會(huì)先創(chuàng)建一個(gè)對(duì)象
// var instance = {}
// 然后讓內(nèi)部的 this 指向 instance 對(duì)象
// this = instance
// 接下來所有針對(duì) this 的操作實(shí)際上操作的就是 instance
?
this.name = name
this.age = age
this.sayName =function(){
console.log(this.name)
? }
?
// 在函數(shù)的結(jié)尾處會(huì)將 this 返回,也就是 instance
// return this
}
3.5構(gòu)造函數(shù)和實(shí)例對(duì)象的關(guān)系
使用構(gòu)造函數(shù)的好處不僅僅在于代碼的簡(jiǎn)潔性傅寡,更重要的是我們可以識(shí)別對(duì)象的具體類型了放妈。 在每一個(gè)實(shí)例對(duì)象中的_proto_中同時(shí)有一個(gè)?constructor?屬性,該屬性指向創(chuàng)建該實(shí)例的構(gòu)造函數(shù):
console.log(p1.constructor === Person)// => true
console.log(p2.constructor === Person)// => true
console.log(p1.constructor === p2.constructor)// => true
對(duì)象的?constructor?屬性最初是用來標(biāo)識(shí)對(duì)象類型的荐操, 但是芜抒,如果要檢測(cè)對(duì)象的類型,還是使用?instanceof?操作符更可靠一些:
console.log(p1instanceofPerson)// => true
console.log(p2instanceofPerson)// => true
總結(jié):
1 構(gòu)造函數(shù)是根據(jù)具體的事物抽象出來的抽象模板托启。
2?實(shí)例對(duì)象是根據(jù)抽象的構(gòu)造函數(shù)模板得到的具體實(shí)例對(duì)象宅倒。
3?每一個(gè)實(shí)例對(duì)象都具有一個(gè)?constructor?屬性,指向創(chuàng)建該實(shí)例的構(gòu)造函數(shù)屯耸。( 此處constructor?是實(shí)例的屬性的說法不嚴(yán)謹(jǐn)拐迁,具體后面的原型會(huì)講到)
4 可以通過實(shí)例的?constructor?屬性判斷實(shí)例和構(gòu)造函數(shù)之間的關(guān)系蹭劈。(這種方式不嚴(yán)謹(jǐn),推薦使用?instanceof?操作符线召,后面學(xué)原型會(huì)解釋為什么)
3.6構(gòu)造函數(shù)的問題
使用構(gòu)造函數(shù)帶來的最大的好處就是創(chuàng)建對(duì)象更方便了铺韧,但是其本身也存在一個(gè)浪費(fèi)內(nèi)存的問題:
functionPerson(name, age){
this.name = name
this.age = age
this.type ='學(xué)生'
this.sayHello =function(){
console.log('hello '+this.name)
? }
}
?
varp1 =newPerson('王五',18)
varp2 =newPerson('李四',16)
上邊的代碼,從表面看上好像沒什么問題缓淹,但是實(shí)際上這樣做哈打,有一個(gè)很大的弊端。 那就是對(duì)于每一個(gè)實(shí)例對(duì)象讯壶,type?和?sayHello?都是一模一樣的內(nèi)容料仗, 每一次生成一個(gè)實(shí)例,都必須為重復(fù)的內(nèi)容伏蚊,多占用一些內(nèi)存罢维,如果實(shí)例對(duì)象很多,會(huì)造成極大的內(nèi)存浪費(fèi)丙挽。
console.log(p1.sayHello === p2.sayHello)// => false
對(duì)于這種問題我們可以把需要共享的函數(shù)定義到構(gòu)造函數(shù)外部:
functionsayHello=function(){
console.log('hello '+this.name)
}
?
functionPerson(name, age){
this.name = name
this.age = age
this.type ='學(xué)生'
this.sayHello = sayHello
}
?
varp1 =newPerson('王五',18)
varp2 =newPerson('李四',16)
?
console.log(p1.sayHello === p2.sayHello)// => true
這樣確實(shí)可以了肺孵,但是如果有多個(gè)需要共享的函數(shù)的話就會(huì)造成全局命名空間沖突的問題。如何解決這個(gè)問題呢颜阐?你肯定想到了可以把多個(gè)函數(shù)放到一個(gè)對(duì)象中用來避免全局命名空間沖突的問題:
varfns = {
sayHello:function(){
console.log('hello '+this.name)
? },
sayAge:function(){
console.log(this.age)
? }
}
?
functionPerson(name, age){
this.name = name
this.age = age
this.type ='學(xué)生'
this.sayHello = fns.sayHello
this.sayAge = fns.sayAge
}
?
varp1 =newPerson('王五',18)
varp2 =newPerson('李四',16)
?
console.log(p1.sayHello === p2.sayHello)// => true
console.log(p1.sayAge === p2.sayAge)// => true
至此平窘,我們利用自己的方式基本上解決了構(gòu)造函數(shù)的內(nèi)存浪費(fèi)問題。 但是代碼看起來還是那么的格格不入凳怨,那有沒有更好的方式呢瑰艘?
4原型
4.1更好的解決方案:prototype
Javascript 規(guī)定,每一個(gè)構(gòu)造函數(shù)都有一個(gè)?prototype?屬性肤舞,指向另一個(gè)對(duì)象紫新。 這個(gè)對(duì)象的所有屬性和方法,都會(huì)被構(gòu)造函數(shù)的實(shí)例繼承李剖。
這也就意味著芒率,我們可以把所有對(duì)象實(shí)例需要共享的屬性和方法直接定義在?prototype?對(duì)象上。
functionPerson(name, age){
this.name = name
this.age = age
}
?
console.log(Person.prototype)
?
Person.prototype.type ='學(xué)生'
?
Person.prototype.sayName =function(){
console.log(this.name)
}
?
varp1 =newPerson(...)
varp2 =newPerson(...)
?
console.log(p1.sayName === p2.sayName)// => true
這時(shí)所有實(shí)例的?type?屬性和?sayName()?方法篙顺, 其實(shí)都是同一個(gè)內(nèi)存地址偶芍,指向?prototype?對(duì)象,因此就提高了運(yùn)行效率德玫。
構(gòu)造函數(shù)匪蟀、實(shí)例、原型三者之間的關(guān)系:
任何函數(shù)都有一個(gè)?prototype?屬性宰僧,該屬性是一個(gè)對(duì)象材彪。
functionF(){}
console.log(F.prototype)// => object
?
F.prototype.sayHi =function(){
console.log('hi!')
}
構(gòu)造函數(shù)的?prototype?對(duì)象默認(rèn)都有一個(gè)?constructor?屬性,指向?prototype?對(duì)象所在函數(shù)。
console.log(F.constructor === F)// => true
通過構(gòu)造函數(shù)得到的實(shí)例對(duì)象內(nèi)部會(huì)包含一個(gè)指向構(gòu)造函數(shù)的?prototype?對(duì)象的指針?__proto__段化。
varinstance =newF()
console.log(instance.__proto__ === F.prototype)// => true
`__proto__` 是非標(biāo)準(zhǔn)屬性嘁捷。
實(shí)例對(duì)象可以直接訪問原型對(duì)象成員:
instance.sayHi()// => hi!
總結(jié):
任何函數(shù)都具有一個(gè)?prototype?屬性,該屬性是一個(gè)對(duì)象穗泵。
構(gòu)造函數(shù)的?prototype?對(duì)象默認(rèn)都有一個(gè)?constructor?屬性普气,指向?prototype?對(duì)象所在函數(shù)谜疤。
通過構(gòu)造函數(shù)得到的實(shí)例對(duì)象內(nèi)部會(huì)包含一個(gè)指向構(gòu)造函數(shù)的?prototype?對(duì)象的指針?__proto__佃延。
所有實(shí)例都直接或間接繼承了原型對(duì)象的成員。
4.2屬性成員的搜索原則:原型鏈
了解了?構(gòu)造函數(shù)-實(shí)例-原型對(duì)象?三者之間的關(guān)系后夷磕,接下來我們來解釋一下為什么實(shí)例對(duì)象可以訪問原型對(duì)象中的成員履肃。
每當(dāng)代碼讀取某個(gè)對(duì)象的某個(gè)屬性時(shí),都會(huì)執(zhí)行一次搜索坐桩,目標(biāo)是具有給定名字的屬性尺棋。
搜索首先從對(duì)象實(shí)例本身開始。
如果在實(shí)例中找到了具有給定名字的屬性绵跷,則返回該屬性的值膘螟。
如果沒有找到,則繼續(xù)搜索指針指向的原型對(duì)象碾局,在原型對(duì)象中查找具有給定名字的屬性荆残。
如果在原型對(duì)象中找到了這個(gè)屬性,則返回該屬性的值净当。
也就是說内斯,在我們調(diào)用?person1.sayName()?的時(shí)候,會(huì)先后執(zhí)行兩次搜索:
首先像啼,解析器會(huì)問:“實(shí)例 person1 有 sayName 屬性嗎俘闯?”答:“沒有。
然后忽冻,它繼續(xù)搜索真朗,再問:“ person1 的原型有 sayName 屬性嗎?”答:“有僧诚。
于是蜜猾,它就讀取那個(gè)保存在原型對(duì)象中的函數(shù)。
當(dāng)我們調(diào)用 person2.sayName() 時(shí)振诬,將會(huì)重現(xiàn)相同的搜索過程蹭睡,得到相同的結(jié)果。
這就是多個(gè)對(duì)象實(shí)例共享原型所保存的屬性和方法的基本原理赶么。
總結(jié):
先在自己身上找肩豁,找到即返回。
自己身上找不到,則沿著原型鏈向上查找清钥,找到即返回琼锋。
如果一直到原型鏈的末端還沒有找到,則返回?undefined祟昭。
4.3實(shí)例對(duì)象讀寫原型對(duì)象成員
讀嚷瓶病:
先在自己身上找,找到即返回篡悟。
自己身上找不到谜叹,則沿著原型鏈向上查找,找到即返回搬葬。
如果一直到原型鏈的末端還沒有找到荷腊,則返回?undefined。
值類型成員寫入(實(shí)例對(duì)象.值類型成員 = xx):
當(dāng)實(shí)例期望重寫原型對(duì)象中的某個(gè)普通數(shù)據(jù)成員時(shí)實(shí)際上會(huì)把該成員添加到自己身上急凰。
也就是說該行為實(shí)際上會(huì)屏蔽掉對(duì)原型對(duì)象成員的訪問女仰。
引用類型成員寫入(實(shí)例對(duì)象.引用類型成員 = xx):同上。
復(fù)雜類型修改(實(shí)例對(duì)象.成員.xx = xx):
同樣會(huì)先在自己身上找該成員抡锈,如果自己身上找到則直接修改疾忍。
如果自己身上找不到,則沿著原型鏈繼續(xù)查找床三,如果找到則修改一罩。
如果一直到原型鏈的末端還沒有找到該成員,則報(bào)錯(cuò)(實(shí)例對(duì)象.undefined.xx = xx)勿璃。
4.4更簡(jiǎn)單的原型語(yǔ)法
我們注意到擒抛,前面例子中每添加一個(gè)屬性和方法就要敲一遍?Person.prototype?。?為減少不必要的輸入补疑,更常見的做法是用一個(gè)包含所有屬性和方法的對(duì)象字面量來重寫整個(gè)原型對(duì)象:
functionPerson(name, age){
this.name = name
this.age = age
}
?
Person.prototype = {
type:'學(xué)生',
sayHello:function(){
console.log('我叫'+this.name +'歧沪,我今年'+this.age +'歲了')
? }
}
在該示例中,我們將?Person.prototype?重置到了一個(gè)新的對(duì)象莲组。 這樣做的好處就是為?Person.prototype?添加成員簡(jiǎn)單了诊胞,但是也會(huì)帶來一個(gè)問題,那就是原型對(duì)象丟失了?constructor?成員锹杈。
所以撵孤,我們?yōu)榱吮3?constructor?的指向正確,建議的寫法是:
functionPerson(name, age){
this.name = name
this.age = age
}
Person.prototype = {
constructor: Person,// => 手動(dòng)將 constructor 指向正確的構(gòu)造函數(shù)
type:'學(xué)生',
sayHello:function(){
console.log('我叫'+this.name +'竭望,我今年'+this.age +'歲了')
? }
}
4.5原生對(duì)象的原型
所有函數(shù)都有 prototype 屬性對(duì)象邪码。
Object.prototype
Function.prototype
Array.prototype
String.prototype
Number.prototype
Date.prototype
...
為數(shù)組對(duì)象和字符串對(duì)象擴(kuò)展原型方法:
//為內(nèi)置對(duì)象添加原型方法
//我們?cè)谙到y(tǒng)的對(duì)象的原型中添加方法,相當(dāng)于在改變?cè)创a
//我希望字符串中有一個(gè)倒序字符串的方法
String.prototype.myReverse =function(){
for(vari =this.length -1; i >=0; i--) {
console.log(this[i]);
}
};
varstr ="abcdefg";
str.myReverse();
//為Array內(nèi)置對(duì)象的原型對(duì)象中添加方法
Array.prototype.mySort =function(){
for(vari =0; i
for(varj =0; j
if(this[j]
vartemp =this[j];
this[j] =this[j +1];
this[j +1] = temp;
}//end if
}// end for
}//end for
};
vararr = [100,3,56,78,23,10];
arr.mySort();
console.log(arr);
String.prototype.sayHi =function(){
console.log(this+"哈哈,我又變帥了");
};
//字符串就有了打招呼的方法
varstr2 ="小楊";
str2.sayHi();
4.6原型對(duì)象的一些問題
共享數(shù)組
共享對(duì)象
如果真的希望可以被實(shí)例對(duì)象之間共享和修改這些共享數(shù)據(jù)那就不是問題。但是如果不希望實(shí)例之間共享和修改這些共享數(shù)據(jù)則會(huì)出現(xiàn)問題咬清。一個(gè)更好的建議是闭专,最好不要讓實(shí)例之間互相共享數(shù)組或者對(duì)象成員奴潘,一旦修改的話會(huì)導(dǎo)致數(shù)據(jù)的走向很不明確而且難以維護(hù)。
原型對(duì)象使用建議:
私有成員(一般就是非函數(shù)成員)放到構(gòu)造函數(shù)中影钉。
共享成員(一般就是函數(shù))放到原型對(duì)象中画髓。
如果重置了?prototype?記得修正?constructor?的指向。