摘要:原型之所以被稱(chēng)為原型,可能正是因?yàn)檫@種不可重載的特質(zhì)。而一旦實(shí)例化,那么將指向?qū)嵗膶?duì)象。首先是,我使用了,直接利用貓咪的年齡進(jìn)行計(jì)算得出體重返回給屬性。
和java比起來(lái),javascript真的是松散的無(wú)以復(fù)加,不過(guò)這也讓我們?cè)跓o(wú)聊之余,有精力去探討一些復(fù)雜的應(yīng)用,從而在開(kāi)發(fā)之路上,獲得一些新的想法。
javascript中的類(lèi)的構(gòu)造javascript中有對(duì)象的概念,卻沒(méi)有類(lèi)的概念。對(duì)于基礎(chǔ)不牢的同學(xué),很難在類(lèi)和對(duì)象之間加以區(qū)分,這里簡(jiǎn)單的將它們的關(guān)系概況為:類(lèi)是一種抽象的概念,例如瓶子、人、笨蛋;而對(duì)象,則是指這種概念中的實(shí)體,比如“那個(gè)美女手中的那只瓶子”“村長(zhǎng)是一個(gè)地道的農(nóng)民”“她的男朋友是個(gè)笨蛋”;實(shí)例化,就是指以類(lèi)為基礎(chǔ)構(gòu)建一個(gè)實(shí)體。類(lèi)所擁有的特征,其實(shí)例化對(duì)象,也一定擁有這些特征,而且實(shí)例化后可能擁有更多特征。
javascript在用到對(duì)象時(shí),完全沒(méi)有類(lèi)的概念,但是編程的世界里,無(wú)奇不有,我們卻可以通過(guò)function構(gòu)造出一種假想的類(lèi),從而實(shí)現(xiàn)javascript中類(lèi)的構(gòu)造。
比如,我們通過(guò)下面的方法來(lái)構(gòu)造一個(gè)類(lèi):
//java class Book { private String name; private double price; public Book(name,price) {this.name=name;this.price=price;} public void setName(String name) { this.name = name;} public void setPrice(double price) {this.price = price;} public String getInfo() {...} } Book book1 = new Book("java",13.3); //javascript function Book(name,price) { this.name = name; this.price = price; this.setName = function(name) {this.name = name;}; this.setPrice = function(price) {this.price = price}; this.getInfo = function() {return this.name + " " + this.price;}; } var book1 = new Book("java",13.3);
這是很好玩兒的一種嘗試。這也是本文要討論的所有問(wèn)題的起源。
function(){}()讓變量快速初始化結(jié)果在《javascript立即執(zhí)行某個(gè)函數(shù):插件中function(){}()再思考》一文中,我詳細(xì)闡述了function(){}()的作用及理解思路。這里不再贅述,現(xiàn)在,我們面臨的新問(wèn)題是,知道了它的作用,我們?nèi)绾问褂盟?/p>
讓我們來(lái)看一段代碼:
var timestamp = function(){ var timestamp = Date.parse(new Date()); return timestamp/1000; }();
當(dāng)我們要使用一個(gè)變量時(shí),我們希望這個(gè)變量在一個(gè)環(huán)節(jié)完成我們的賦值,使用上面的這種方法,可以減少代碼上下文執(zhí)行邏輯,如果按照我們以前的方法,代碼可能會(huì)寫(xiě)成:
var timestamp = Date.parse(new Data()); timestamp = timestamp/1000;
看上去好像比上面的操作簡(jiǎn)潔多了,只需要兩行代碼。但是我們仔細(xì)去觀(guān)察,就會(huì)發(fā)現(xiàn)第一段代碼其實(shí)本身僅是一個(gè)賦值操作,在function中完成的所有動(dòng)作將會(huì)在function執(zhí)行完后全部釋放,整個(gè)代碼看上去好像只執(zhí)行了一條語(yǔ)句一樣。
而實(shí)際上更重要的意義在于它可以讓一個(gè)變量在初始化時(shí),就具備了運(yùn)算結(jié)果的效果。
使用new function初始化一個(gè)可操作對(duì)象第一部分講到了javascript中的類(lèi),而使用new function就可以實(shí)例化這個(gè)類(lèi)。但是我們實(shí)際上有的時(shí)候在為一個(gè)變量賦值的時(shí)候,希望直接將它初始化為一個(gè)可操作的對(duì)象,比如像這樣:
// 這里的數(shù)據(jù)庫(kù)操作是我虛擬出來(lái)的一種數(shù)據(jù)庫(kù)操作形式 var $db = new function(){ var $db = db_connect("127.0.0.1","root",""); $db.use("database"); this.select = function(table,where) { var result = $db.query("select from " + table + " where " + where); return $db.fetchAll(result); } };
而當(dāng)我們要對(duì)數(shù)據(jù)庫(kù)database進(jìn)行查詢(xún)時(shí),只需要通過(guò)var list = $db.select("table","1=1");進(jìn)行操作即可,數(shù)據(jù)庫(kù)的初始化結(jié)果已經(jīng)在$db這個(gè)變量中了。
Function是由function關(guān)鍵字定義的函數(shù)對(duì)象的原型在javascript中,多出了一個(gè)原型的概念。所謂原型,其實(shí)就是一個(gè)對(duì)象的本質(zhì),但復(fù)雜就復(fù)雜在,原型本身也是對(duì)象,因此,任何一個(gè)對(duì)象又可以作為其他對(duì)象的原型。Function就相當(dāng)于一個(gè)系統(tǒng)原型,可以把它理解為一種“基本對(duì)象類(lèi)型”,是“對(duì)象”這個(gè)概念范疇類(lèi)的基本數(shù)據(jù)類(lèi)型。除了Function之外,其實(shí)還有很多類(lèi)似的首字母大寫(xiě)的對(duì)象原型,例如Object, Array, Image等等。有一種說(shuō)法是:javascript中所有的一切都是對(duì)象(除了基本數(shù)據(jù)類(lèi)型,其他的一切全是對(duì)象),所有的對(duì)象都是Object衍生出來(lái)的。(按照這種說(shuō)法,我們應(yīng)該返回去再思考,上面說(shuō)的類(lèi)的假設(shè)是否成立。)
極其重要的prototype概念prototype的概念在javascript中極其重要,它是javascript中完成上面說(shuō)的“一切皆對(duì)象”的關(guān)鍵。有了prototype,才有了原型,有了原型,才有了javascript五彩繽紛的世界(當(dāng)然,也有人說(shuō)是雜亂的)。我們可以這樣去理解prototype:世界上本沒(méi)有javascript,上帝說(shuō)要有Object,于是有了Object,可是要有Function怎么辦?只需要對(duì)Object進(jìn)行擴(kuò)展,可是如何擴(kuò)展?只需要用prototype……當(dāng)然,這是亂扯的,不過(guò)在javascript中,只要是function,就一定會(huì)有一個(gè)prototype屬性。實(shí)際上確實(shí)是這樣
Function.prototype.show = function() {...}
在原型的基礎(chǔ)上通過(guò)prototype新增屬性或方法,則以該對(duì)象為原型的實(shí)例化對(duì)象中,必然存在新增的屬性或方法,而且它的內(nèi)容是靜態(tài)不可重載的。原型之所以被稱(chēng)為原型,可能正是因?yàn)檫@種不可重載的特質(zhì)。
比如上面的這段代碼,會(huì)導(dǎo)致每一個(gè)實(shí)例化的function,都會(huì)具備一個(gè)show方法。而如果我們自己創(chuàng)建了一個(gè)類(lèi),則可以通過(guò)prototype將之轉(zhuǎn)化為原型:
function Cat() {...} Cat.prototype.run = function() {}; var cat1 = new Cat();
這時(shí),對(duì)于cat1而言,Cat就是原型,而該原型擁有一個(gè)run的原始方法,所以無(wú)論實(shí)例化多少個(gè)Cat,每一個(gè)實(shí)例化對(duì)象都有run方法,而且該方法是不能被重載的,通過(guò)cat1.run = function(){}是無(wú)效的。
為了和其他語(yǔ)言的類(lèi)的定義方法統(tǒng)一,我們可以將這種原型屬性在定義類(lèi)的時(shí)候,寫(xiě)在類(lèi)的構(gòu)造里面:
function Cat() { .... Cat.prototype.run = function() {}; }new Function()是函數(shù)原型的一個(gè)實(shí)例化
在理解了Function原型的概念之后,再來(lái)看new Function()就顯得很容易了。首先來(lái)看下我們是怎么使用這種奇特的寫(xiě)法的:
var message = new Function("msg","alert(msg)"); // 等價(jià)于: function message(msg) { alert(msg); }
new Function(參數(shù)1,參數(shù)2,...,參數(shù)n,函數(shù)體),它的本意其實(shí)是通過(guò)實(shí)例化一個(gè)Function原型,得到一個(gè)數(shù)據(jù)類(lèi)型為function的對(duì)象,也就是一個(gè)函數(shù),而該變量就是函數(shù)名。
this在這類(lèi)function中的指向this在javascript中真的是無(wú)法讓我們捉摸透徹。但是有一個(gè)小竅門(mén),就是:一般情況下,this指向的是當(dāng)前實(shí)例化對(duì)象,如果沒(méi)有找到該對(duì)象,則是指向window。從使用上來(lái)講,我們應(yīng)該排除new Function的討論,因?yàn)樗臀覀兂S玫暮瘮?shù)聲明是一致的。
普通的函數(shù)中this的指向
函數(shù)聲明的時(shí)候,如果使用了this,那么就要看是把該函數(shù)當(dāng)做一個(gè)對(duì)象加以返回,還是以?xún)H執(zhí)行函數(shù)體。普通函數(shù)執(zhí)行時(shí),我們完全沒(méi)有引入對(duì)象、類(lèi)這些概念,因此,this指向window。通過(guò)代碼來(lái)看下:
var msg; function message(msg) { this.msg = msg; } message("ok"); alert(msg);
首先是聲明一個(gè)函數(shù)message,在函數(shù)中this.msg實(shí)際上就是window.msg,也實(shí)際上就是代碼開(kāi)頭的msg。因此,當(dāng)執(zhí)行完message("ok")的時(shí)候,開(kāi)頭的全局變量msg也被賦值為ok。
通過(guò)function構(gòu)造類(lèi)時(shí)this的指向
如果function被構(gòu)造為一個(gè)類(lèi),那么必然存在該類(lèi)被實(shí)例化的一個(gè)過(guò)程,如果沒(méi)有實(shí)例化,那么該類(lèi)實(shí)際上并沒(méi)有在程序中被使用。而一旦實(shí)例化,那么this將指向?qū)嵗膶?duì)象。
var age = 3; var cat1 = new function() { this.name = "Tom"; this.age = 2; this.weight = function(age) { var age = age * 2; var _age = this.age * 2; return "weight by age:" + age + "; weight by this.age:" + _age; }(this.age); this.eye = new function() { this.size = "1.5cm"; this.color = "red"; }; this.catching = function(mouse) { return this.name + " is catching " + mouse; }; }; alert(cat1.weight); alert(cat1.eye.color); alert(cat1.catching("Jerry"));
上面代碼中標(biāo)記了4處紅色的this的使用。根據(jù)我們的原則,this指向?qū)嵗瘜?duì)象,我們來(lái)對(duì)每一個(gè)this進(jìn)行分解。
首先是cat1.weight,我使用了function(){}(),直接利用貓咪的年齡進(jìn)行計(jì)算得出體重返回給weight屬性。
第一個(gè)this.age出現(xiàn)在function(){}(this.age),這個(gè)this.age實(shí)際上是一個(gè)傳值過(guò)程,如果你對(duì)我之前分析function(){}()比較了解的話(huà),應(yīng)該知道,this.age實(shí)際上是和前面this.age = 2指同一個(gè),這里的this.age的this,首先要去找它所在的function,然后看這個(gè)function是否被實(shí)例化,最后確認(rèn),確實(shí)被實(shí)例化為cat1,因此this=cat1。
第二個(gè)this.age出現(xiàn)在function(){this.age}()。同樣,你先需要對(duì)function(){}()再次深入了解,實(shí)際上,function(){}()就是執(zhí)行一個(gè)函數(shù)而已,我們前面提到了,普通函數(shù)執(zhí)行中this=window,所以,這里的this.age實(shí)際上是var age = 3。
第三個(gè)this.color出現(xiàn)在new function(){this.color},這里就比較好玩,由于有一個(gè)new,實(shí)際上也被實(shí)例化了,只不過(guò)是對(duì)匿名類(lèi)的實(shí)例化,沒(méi)有類(lèi)名,而且實(shí)例化僅可能出現(xiàn)這一次。因此,this.color的this要去找new function的主人,也就是this.eye,而this.eye的this=cat1,所以cat1.eye.color="red"。
第四個(gè)this.name出現(xiàn)在function(){this.name},它出現(xiàn)在cacthing方法中,它既不是普通的函數(shù)執(zhí)行,也不是實(shí)例化為對(duì)象,而是正常的類(lèi)中的方法的聲明,因此this指向要去找它所在的function被實(shí)例化的對(duì)象,也就是cat1。
小結(jié)本文雖然講了很多,但核心點(diǎn)實(shí)際上還是落在javascript的面向?qū)ο筮@個(gè)點(diǎn)上。javascript中雖然沒(méi)有明確的class的概念,那是因?yàn)樗紫然趖ype這樣的基礎(chǔ)概念,并強(qiáng)調(diào)一切皆對(duì)象的這種思想。如果從哲學(xué)上講,javascript真正符合道生一,一生二,二生萬(wàn)物的原則。不過(guò)本文沒(méi)有提到和繼承相關(guān)的內(nèi)容,所有解釋也是我自己的一種理解方式,不代表真正的原理,讀者請(qǐng)自己通過(guò)其他途徑進(jìn)行學(xué)習(xí)。
本文發(fā)布在我的個(gè)人博客,請(qǐng)到原地址交流:http://www.tangshuang.net/2280.html
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/78950.html
摘要:這幾天因?yàn)閷?duì)于中的作用域鏈和原型鏈有點(diǎn)混淆,當(dāng)訪(fǎng)問(wèn)一個(gè)不帶有修飾的變量時(shí),我想知道它的搜索順序,因?yàn)樽饔糜蜴湹逆溄Y(jié)點(diǎn)也是一個(gè)變量對(duì)象,那么當(dāng)在這個(gè)變量對(duì)象中查找變量時(shí)會(huì)不會(huì)沿著它的原型鏈查找呢這樣就有兩種可能先查找作用域鏈前端的變量對(duì)象,然 這幾天因?yàn)閷?duì)于JavaScript中的作用域鏈和原型鏈有點(diǎn)混淆,當(dāng)訪(fǎng)問(wèn)一個(gè)不帶有this修飾的變量時(shí),我想知道它的搜索順序,因?yàn)樽饔糜蜴湹逆溄Y(jié)點(diǎn)也...
摘要:上面的例子應(yīng)用了匿名函數(shù)這個(gè)特性,還可以使用構(gòu)造函數(shù)或者閉包來(lái)添加事件監(jiān)聽(tīng)器另一個(gè)重要特性,則是上面這段代碼中最后一行的最后一個(gè)參數(shù),用來(lái)控制監(jiān)聽(tīng)器對(duì)于冒泡事件的響應(yīng)。在這里你不能使用閉包或者匿名函數(shù),并且控制域也是有限的。 原文出處:addEventListener vs onclick 之所以會(huì)想到這個(gè)話(huà)題,是因?yàn)樵诨仡欁约褐皩?xiě)的為 button 動(dòng)態(tài)綁定事件的函數(shù)時(shí),腦海里忽...
摘要:事件的響應(yīng)分區(qū)為三個(gè)階段捕獲目標(biāo)冒泡階段。綁定的多個(gè)事件會(huì)被覆蓋,后者覆蓋前者。再用轉(zhuǎn)換成數(shù)值表示。如實(shí)際數(shù)量為,則展示為項(xiàng)目中使用過(guò)濾器做的處理可以抽取方法的,調(diào)整相關(guān),可以獲取指定位數(shù)的縮寫(xiě)。 CSS html5中a的download屬性 定義和用法download 屬性定義下載鏈接的地址或指定下載文件的名稱(chēng)。文件名稱(chēng)沒(méi)有限定值,瀏覽器會(huì)自動(dòng)在文件名稱(chēng)末尾添加該下載文件的后綴 (...
摘要:事件的響應(yīng)分區(qū)為三個(gè)階段捕獲目標(biāo)冒泡階段。綁定的多個(gè)事件會(huì)被覆蓋,后者覆蓋前者。再用轉(zhuǎn)換成數(shù)值表示。如實(shí)際數(shù)量為,則展示為項(xiàng)目中使用過(guò)濾器做的處理可以抽取方法的,調(diào)整相關(guān),可以獲取指定位數(shù)的縮寫(xiě)。 CSS html5中a的download屬性 定義和用法download 屬性定義下載鏈接的地址或指定下載文件的名稱(chēng)。文件名稱(chēng)沒(méi)有限定值,瀏覽器會(huì)自動(dòng)在文件名稱(chēng)末尾添加該下載文件的后綴 (...
摘要:使用實(shí)現(xiàn)計(jì)算器,開(kāi)啟你的計(jì)算之旅吧效果圖代碼如下,復(fù)制即可使用主體顯示框微軟雅黑功能區(qū)?使用html+css+js實(shí)現(xiàn)計(jì)算器,開(kāi)啟你的計(jì)算之旅吧 ? 效果圖: ?? ?代碼如下,復(fù)制即可使用: ? ?? /* 主體 */ .counter{ width: 396px; ...
閱讀 3256·2021-09-10 10:51
閱讀 3448·2021-08-31 09:38
閱讀 1764·2019-08-30 15:54
閱讀 3194·2019-08-29 17:22
閱讀 3284·2019-08-26 13:53
閱讀 2036·2019-08-26 11:59
閱讀 3343·2019-08-26 11:37
閱讀 3373·2019-08-26 10:47