一. JavaScript面向?qū)ο笄把?/h1>
1.1 什么是對(duì)象?
Everything is object (萬(wàn)物皆對(duì)象)。
對(duì)象到底是什么侠讯,我們可以從兩個(gè)層次來(lái)理解。
-
對(duì)象是單個(gè)事物的抽象
一本書(shū)暑刃、一輛汽車(chē)厢漩、一個(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)行編程谷扣。
-
對(duì)象是一個(gè)容器土全,封裝了屬性(property)和方法(method)
屬性是對(duì)象的狀態(tài),方法是對(duì)象的行為(完成某種任務(wù))抑钟。比如涯曲,我們可以把動(dòng)物抽象為animal對(duì)象野哭,使用“屬性”記錄具體是那一種動(dòng)物在塔,使用“方法”表示動(dòng)物的某種行為(奔跑、捕獵拨黔、休息等等)
在實(shí)際開(kāi)發(fā)中蛔溃,對(duì)象是一個(gè)抽象的概念,可以將其簡(jiǎn)單理解為 : 數(shù)據(jù)集或功能集篱蝇。
1.2 什么是面向?qū)ο螅?/h2>
面向?qū)ο笾皇沁^(guò)程式代碼的一種高度封裝贺待,目的在于提高代碼的開(kāi)發(fā)效率和可維護(hù)性
面向?qū)ο笾皇沁^(guò)程式代碼的一種高度封裝贺待,目的在于提高代碼的開(kāi)發(fā)效率和可維護(hù)性
小編的理解:面向?qū)ο缶褪钦f(shuō),使用對(duì)象的時(shí)候零截,你可以直接使用它所提供的功能而忽略其內(nèi)部組成情況麸塞。面對(duì)對(duì)象不一定只有在編程界里才有,我們生活中無(wú)處不在涧衙;比如說(shuō)哪工,你家里的電視機(jī)奥此,你使用了遙控,就能操作電視機(jī)雁比,但是你實(shí)際上不知道這臺(tái)電視機(jī)里面是什么零件組成的稚虎,你只要知道,我拿到遙控就可以操作電視機(jī)就好了偎捎。這就是一種面向?qū)ο蟮乃枷搿?/p>
1.2.1 什么是面向?qū)ο缶幊蹋?/h3>
面向?qū)ο缶幊?—— Object Oriented Programming蠢终,簡(jiǎn)稱(chēng) OOP ,是一種編程開(kāi)發(fā)思想茴她。
它將真實(shí)世界各種復(fù)雜的關(guān)系寻拂,抽象為一個(gè)個(gè)對(duì)象,然后由對(duì)象之間的分工與合作丈牢,完成對(duì)真實(shí)世界的模擬侄榴。
在面向?qū)ο蟪绦蜷_(kāi)發(fā)思想中,每一個(gè)對(duì)象都是功能中心侵贵,具有明確分工娩怎,可以完成接受信息、處理數(shù)據(jù)泛粹、發(fā)出信息等任務(wù)遂铡。
因此,面向?qū)ο缶幊叹哂徐`活晶姊、代碼可復(fù)用扒接、高度模塊化等特點(diǎn),容易維護(hù)和開(kāi)發(fā)们衙,比起由一系列函數(shù)或指令組成的傳統(tǒng)的過(guò)程式編程(procedural programming)钾怔,更適合多人合作的大型軟件項(xiàng)目。
1.2.2 面向?qū)ο笈c面向過(guò)程
- 面向過(guò)程就是親力親為蒙挑,事無(wú)巨細(xì)宗侦,面面俱到,步步緊跟忆蚀,有條不紊
- 面向?qū)ο缶褪钦乙粋€(gè)對(duì)象矾利,指揮得結(jié)果
- 面向?qū)ο髮?zhí)行者轉(zhuǎn)變成指揮者
- 面向?qū)ο蟛皇敲嫦蜻^(guò)程的替代,而是面向過(guò)程的封裝
二. 對(duì)象定義的兩種方式
2.1 字面量的方式進(jìn)行定義
var obj = {
name: "Tom ",
sex: " man",
age:19,
run:function(){
console.log("一天跑一公里");
}
}
2.2 使用 new Object() 進(jìn)行定義
var obj = new Object();
obj.name = "Tom ";
obj.sex = " man";
obj.age = 19;
obj.run = function(){
console.log("一天跑一公里");
}
三. 類(lèi)和對(duì)象
3.1 什么是類(lèi)(Class)馋袜?
具有相同特性(數(shù)據(jù)元素)和行為(功能)的對(duì)象的抽象就是類(lèi)男旗。因此,對(duì)象的抽象是類(lèi)欣鳖,類(lèi)的具體化就是對(duì)象察皇,也可以說(shuō)類(lèi)的實(shí)例是對(duì)象,類(lèi)實(shí)際上就是一種數(shù)據(jù)類(lèi)型泽台。類(lèi)具有屬性什荣,它是對(duì)象狀態(tài)的抽象呀忧,用數(shù)據(jù)結(jié)構(gòu)來(lái)描述類(lèi)的屬性。類(lèi)具有操作溃睹,它是對(duì)象行為的抽象而账,用操作名和實(shí)現(xiàn)該操作的方法來(lái)描述。
3.2 類(lèi)和對(duì)象的區(qū)別
作為初學(xué)者因篇,容易混淆類(lèi)和對(duì)象的概念泞辐。類(lèi)(Class)是一個(gè)抽象的概念,對(duì)象則是類(lèi)的具體實(shí)例竞滓。比如:人是一個(gè)類(lèi)咐吼,司馬遷,李白商佑,杜甫都是對(duì)象锯茄;首都是一個(gè)類(lèi),則北京茶没,倫敦肌幽,華盛頓,莫斯科都是對(duì)象抓半;動(dòng)物貓是一個(gè)類(lèi)喂急,則Kitty、Grafield 和 Doraemon 都是對(duì)象笛求。
我們可以說(shuō) Kitty 貓的體重是 1.5kg廊移,而不能說(shuō)貓類(lèi)的體重是 1.5kg;可以說(shuō)李白是詩(shī)人探入,而不能說(shuō)人類(lèi)是詩(shī)人狡孔。狀態(tài)是描述具體對(duì)象而非描述類(lèi)的,行為是由具體對(duì)象發(fā)出的而非類(lèi)發(fā)出的蜂嗽。
3.3 類(lèi)和對(duì)象的關(guān)系
類(lèi)與對(duì)象的關(guān)系就如模具和鑄件的關(guān)系苗膝,類(lèi)實(shí)例化的結(jié)果就是對(duì)象,而對(duì)象的抽象就是類(lèi)徒爹,類(lèi)描述了一組有相同特性(屬性)和相同行為的對(duì)象荚醒。
class person{ }//這個(gè)是類(lèi)
$obj = new person();//類(lèi)的實(shí)例化就是對(duì)象
四.創(chuàng)建對(duì)象的三種方式
4.1 工廠模式芋类,使用簡(jiǎn)單的函數(shù)創(chuàng)建對(duì)象隆嗅,為對(duì)象添加屬性和方法,然后返回對(duì)象
// Class 模板
function Person(name,sex,age){
var obj = {};
obj.name = name;
obj.sex = sex;
obj.age = age;
obj.run = function(){
console.log("每天堅(jiān)持跑步");
}
return obj;
}
// 實(shí)例化
var person1 = Person("Tom","sex",19);
//操作
person1.run(); // 輸出結(jié)果:每天堅(jiān)持跑步
4.1.1 工廠模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1侯繁、 在工廠模式中胖喳,用戶(hù)只需要知道所要生產(chǎn)的具體東西,無(wú)須關(guān)心具體的創(chuàng)建過(guò)程贮竟,甚至不需要具體產(chǎn)品類(lèi)的類(lèi)名丽焊。
2较剃、 在系統(tǒng)增加新的產(chǎn)品時(shí),我們只需要添加一個(gè)具體產(chǎn)品類(lèi)和對(duì)應(yīng)的實(shí)現(xiàn)工廠技健,無(wú)需對(duì)原工廠進(jìn)行任何修改写穴,很好地符合了“開(kāi)閉原則”。
缺點(diǎn):
1雌贱、 每次增加一個(gè)產(chǎn)品時(shí)啊送,都需要增加一個(gè)具體類(lèi)和對(duì)象實(shí)現(xiàn)工廠,使得系統(tǒng)中類(lèi)的個(gè)數(shù)成倍增加欣孤,在一定程度上增加了系統(tǒng)的復(fù)雜度馋没,同時(shí)也增加了系統(tǒng)具體類(lèi)的依賴(lài)。這并不是什么好事降传。
4.2 構(gòu)造函數(shù)模式篷朵。創(chuàng)建自定義引用類(lèi)型,可以像創(chuàng)建內(nèi)置對(duì)象實(shí)例一樣使用new操作符婆排,這種方法的缺點(diǎn)是声旺,構(gòu)造函數(shù)的每個(gè)成員都無(wú)法復(fù)用,每次創(chuàng)建出的對(duì)象都只有私有變量和私有方法段只,不能實(shí)現(xiàn)共用
//構(gòu)造函數(shù)(這里就創(chuàng)建了一個(gè)Class模板)
function Person(name,sex,age){
this.name = name;
this.sex = sex;
this.age = age;
this.run = function(){
console.log("每天堅(jiān)持跑步");
}
}
// 實(shí)例化 ( new Object())
var person1 = new Person("Tom","man",19);
//操作
person1.run();// 每天堅(jiān)持跑步
4.2.1 構(gòu)造函數(shù)的改進(jìn)
// 構(gòu)造全局的對(duì)象
var action = {
run:function(){
console.log("每天堅(jiān)持跑步");
}
}
//構(gòu)造函數(shù)
funcction(name,sex,age){
this.name = name;
this.sex = sex;
this.age = age;
this.run = action.run;
}
//實(shí)例化
var person1 = new Person("Tom","man",19);
person1.run();
分析: 為什么要改進(jìn)構(gòu)造函數(shù)艾少?
我們都知道當(dāng)實(shí)例化一個(gè)對(duì)象后,那么這個(gè)對(duì)象就擁有了模板的屬性和方法翼悴,
當(dāng)我們使用方法時(shí)首先會(huì)在內(nèi)存中開(kāi)辟一份空間缚够,然后在執(zhí)行相應(yīng)的操作。假設(shè)我們實(shí)例化一萬(wàn)個(gè)對(duì)象鹦赎,那么這一萬(wàn)個(gè)對(duì)象在執(zhí)行方法時(shí)都要在內(nèi)存中開(kāi)辟空間谍椅,這樣只會(huì)浪費(fèi)內(nèi)存的空間,這時(shí)如果能用一個(gè)操作代替一萬(wàn)個(gè)操作那樣豈不是美哉古话,所以小編這里用了一個(gè)全局的對(duì)象雏吭,即使是一萬(wàn)個(gè)對(duì)象使用這個(gè)方法只會(huì)在內(nèi)存中開(kāi)辟一份空間。
但是這也有缺點(diǎn)陪踩,我們都知道全局變量和局部變量杖们,全局變量易造成污染并且聲明周期比較長(zhǎng),那么全局對(duì)象也是一樣的肩狂,有沒(méi)有一種方式可以不用全部對(duì)象呢摘完?當(dāng)然有,見(jiàn)下面的第三種創(chuàng)建模式
4.3 原型模式傻谁,使用構(gòu)造函數(shù)的prototype屬性來(lái)指定共享的屬性和方法孝治,即使用構(gòu)造函數(shù)定義實(shí)例屬性,使用原型定義共享的屬性和方法
//構(gòu)造函數(shù)
function Person(name,sex,age){
this.name = name;
this.sex = sex;
this.age = age;
}
//使用原型對(duì)象 Object.prototype
Person.prototype.run = function() {
console.log("每天堅(jiān)持跑步");
}
//實(shí)例化
var person1 = new Person("Tom","man",19);
person1.run();// 每天堅(jiān)持跑步
五.構(gòu)造函數(shù)、原型對(duì)象谈飒、實(shí)例化對(duì)象三則的關(guān)系
首先先明白幾個(gè)屬性:
proto: 對(duì)象的原型岂座。所有對(duì)象都有(包括函數(shù))
prototype:函數(shù)的原型對(duì)象。只有函數(shù)(準(zhǔn)確來(lái)說(shuō)是構(gòu)造函數(shù))才有
constructor:對(duì)象上的一個(gè)屬性杭措,默認(rèn)指向這個(gè)原型的構(gòu)造函數(shù)
[圖片上傳失敗...(image-6be22c-1573402646019)]
六.徹底理解js中this的指向
首先必須要說(shuō)的是费什,this的指向在函數(shù)定義的時(shí)候是確定不了的,只有函數(shù)執(zhí)行的時(shí)候才能確定this到底指向誰(shuí)手素,實(shí)際上this的最終指向的是那個(gè)調(diào)用它的對(duì)象(這句話(huà)有些問(wèn)題吕喘,后面會(huì)解釋為什么會(huì)有問(wèn)題,雖然網(wǎng)上大部分的文章都是這樣說(shuō)的刑桑,雖然在很多情況下那樣去理解不會(huì)出什么問(wèn)題氯质,但是實(shí)際上那樣理解是不準(zhǔn)確的,所以在你理解this的時(shí)候會(huì)有種琢磨不透的感覺(jué))祠斧,那么接下來(lái)我會(huì)深入的探討這個(gè)問(wèn)題闻察。
eg1:
function show(){
var user = "Tom";
console.log(this.user);//undefined
console.log(this);//window
}
show();
按照我們上面說(shuō)的this最終指向的是調(diào)用它的對(duì)象,這里的函數(shù)show()實(shí)際是被Window對(duì)象所調(diào)用出來(lái)的琢锋,下面的代碼就可以證明辕漂。
function show(){
var user = "Tom";
console.log(this.user);//undefined
console.log(this);//window
}
window.show();
和上面代碼結(jié)果一樣,所以此時(shí)this是window調(diào)用出來(lái)的
eg2:
var show = {
user:"Tom",
fn:function(){
console.log(this.user);//Tom
}
}
show.fn();
這里的this指向的是對(duì)象show吴超,因?yàn)槟阏{(diào)用這個(gè)fn是通過(guò)show.fn()執(zhí)行的钉嘹,那自然指向就是對(duì)象show,這里再次強(qiáng)調(diào)一點(diǎn)鲸阻,this的指向在函數(shù)創(chuàng)建的時(shí)候是決定不了的跋涣,在調(diào)用的時(shí)候才能決定,誰(shuí)調(diào)用的就指向誰(shuí)鸟悴,一定要搞清楚這個(gè)陈辱。
要想徹底搞懂this的指向請(qǐng)看下面的例子
eg3:
var show = {
user:"Tom",
fn:function(){
console.log(this.user);//Tom
}
}
window.show.fn();
這段代碼和上面的那段代碼幾乎是一樣的,但是這里的this為什么不是指向window细诸,如果按照上面的理論沛贪,最終this指向的是調(diào)用它的對(duì)象,這里先說(shuō)個(gè)而外話(huà)震贵,window是js中的全局對(duì)象利赋,我們創(chuàng)建的變量實(shí)際上是給window添加屬性,所以這里可以用window點(diǎn)show對(duì)象猩系。
這里先不解釋為什么上面的那段代碼this為什么沒(méi)有指向window媚送,我們?cè)賮?lái)看一段代碼
var show = {
a:20,
b:{
a:22,
fn:function(){
console.log(this.a);// 22
}
}
}
show.b.fn();
這里同樣也是對(duì)象show點(diǎn)出來(lái)的,但是同樣this并沒(méi)有執(zhí)行它蝙眶,那你肯定會(huì)說(shuō)我一開(kāi)始說(shuō)的那些不就都是錯(cuò)誤的嗎季希?其實(shí)也不是褪那,只是一開(kāi)始說(shuō)的不準(zhǔn)確幽纷,接下來(lái)我將補(bǔ)充一句話(huà)式塌,我相信你就可以徹底的理解this的指向的問(wèn)題。
情況1:如果一個(gè)函數(shù)中有this友浸,但是它沒(méi)有被上一級(jí)的對(duì)象所調(diào)用峰尝,那么this指向的就是window,這里需要說(shuō)明的是在js的嚴(yán)格版中this指向的不是window收恢,但是我們這里不探討嚴(yán)格版的問(wèn)題武学,你想了解可以自行上網(wǎng)查找。
情況2:如果一個(gè)函數(shù)中有this伦意,這個(gè)函數(shù)有被上一級(jí)的對(duì)象所調(diào)用火窒,那么this指向的就是上一級(jí)的對(duì)象。
情況3:如果一個(gè)函數(shù)中有this驮肉,這個(gè)函數(shù)中包含多個(gè)對(duì)象熏矿,盡管這個(gè)函數(shù)是被最外層的對(duì)象所調(diào)用,this指向的也只是它上一級(jí)的對(duì)象离钝,例子3可以證明票编,如果不相信,那么接下來(lái)我們繼續(xù)看幾個(gè)例子卵渴。
var show = {
a:20,
b:{
fn:function(){
console.log(this.a);// undefined
}
}
}
show.b.fn();
盡管對(duì)象b中沒(méi)有屬性a慧域,這個(gè)this指向的也是對(duì)象b,因?yàn)閠his只會(huì)指向它的上一級(jí)對(duì)象浪读,不管這個(gè)對(duì)象中有沒(méi)有this要的東西昔榴。
還有一種比較特殊的情況,eg4:
var show = {
a:20,
b:{
a:22,
fn:function(){
console.log(this.a);// undefined
console.log(this);//window
}
}
}
var d = show.b.fn;
d();
這里this指向的是window碘橘,是不是有些蒙了论泛?其實(shí)是因?yàn)槟銢](méi)有理解一句話(huà),這句話(huà)同樣至關(guān)重要蛹屿。
this永遠(yuǎn)指向的是最后調(diào)用它的對(duì)象屁奏,也就是看它執(zhí)行的時(shí)候是誰(shuí)調(diào)用的,例子4中雖然函數(shù)fn是被對(duì)象b所引用错负,但是在將fn賦值給變量d的時(shí)候并沒(méi)有執(zhí)行所以最終指向的是window坟瓢,這和例子3是不一樣的,例子3是直接執(zhí)行了fn犹撒。
構(gòu)造函數(shù)版this:
function Fn(){
this.user = "Tom";
}
var a = new Fn();
console.log(a.user); //Tom
這里之所以對(duì)象a可以點(diǎn)出函數(shù)Fn里面的user是因?yàn)閚ew關(guān)鍵字可以改變this的指向折联,將這個(gè)this指向?qū)ο骯,為什么我說(shuō)a是對(duì)象识颊,因?yàn)橛昧薾ew關(guān)鍵字就是創(chuàng)建一個(gè)對(duì)象實(shí)例诚镰,理解這句話(huà)可以想想我們的例子3奕坟,我們這里用變量a創(chuàng)建了一個(gè)Fn的實(shí)例(相當(dāng)于復(fù)制了一份Fn到對(duì)象a里面),此時(shí)僅僅只是創(chuàng)建清笨,并沒(méi)有執(zhí)行月杉,而調(diào)用這個(gè)函數(shù)Fn的是對(duì)象a,那么this指向的自然是對(duì)象a抠艾,那么為什么對(duì)象a中會(huì)有user苛萎,因?yàn)槟阋呀?jīng)復(fù)制了一份Fn函數(shù)到對(duì)象a中,用了new關(guān)鍵字就等同于復(fù)制了一份检号。
當(dāng)this碰到return:
function Fn(){
this.user = "Tom";
return {};
}
var a = new Fn();
console.log(a.user);//undefined
繼續(xù):
function Fn(){
this.user = "Tom";
return funciton(){};
}
var a = new Fn();
console.log(a.user);//undefined
goon:
function Fn(){
this.user = "Tom";
return 1;
}
var a = new Fn();
console.log(a.user);// Tom
再繼續(xù):
function Fn(){
this.user = "Tom";
return undefined;
}
var a = new Fn();
console.log(a.user);//Tom
如果返回值是一個(gè)對(duì)象腌歉,那么this指向的就是那個(gè)返回的對(duì)象,如果返回值不是一個(gè)對(duì)象那么this還是指向函數(shù)的實(shí)例
function Fn(){
this.user = "Tom";
return null;
}
var a = new Fn();
console.log(a.user);//Tom
雖然null也是對(duì)象齐苛,但是在這里this還是指向那個(gè)函數(shù)的實(shí)例翘盖,因?yàn)閚ull比較特殊