javascript筆記:深入分析javascript裏對象的創建(中)

  深入分析javascript裏對象的創建這個小系列是我整個博客裏最受歡迎的文章,有博友催我把下篇寫完,昨天和今天整理資料發現一篇文章還真講不完我下半部分的內容,所以把本來打算寫的下篇分成兩部分了。

  本主題的上篇裏我講到了三種對象創建的方式,最後通過類比java面向對象的思想反過來理解javascript對象的創建。如果根據標題的核心對象的創建,我所闡述的問題其實已經講完,但是我寫完上篇時候總覺得欠缺點啥,欠缺我的標題裏面的深入分析二字。小小的創建一個javascript對象它所包含的技術的延伸面是極其寬泛的,到了實際開發時候沒有一定發散的知識的積累我們想還會碰到難以理解的問題,中篇和下篇我想從我前一篇講到的知識拓展開這個問題。

  1.類的屬性和方法的另一種理解

  上篇裏面我寫到一句話:“一.屬於類的屬性和方法:用對象初始化的方式都可以當做是屬於類的屬性和方法,這種定義在jQuery裏面大量運用。”

  大家知道在java裏面類可以具有靜態屬性和方法,無需實例化該類的對象,就可以訪問這些屬性和方法,但是javascript裏面是不是隻有通過對象初始化方式纔會模擬出這樣的特點了?其實不然,在編程語言裏,類的方法和屬性比較標準的叫法是:靜態作用域定義的屬性和方法任何時候都能從同一個位置訪問。其實嚴格意義上說javascript是沒有靜態作用域,對象初始化可以產生這樣的效果,但是它太不直觀了,前面的博文裏我講到javascript語言設計時候省略的類這個定義,而是把類的定義賦予到了構造函數裏面,那麼我們可以這樣思考,要想讓javascript語言裏有屬於類的屬性和方法,最佳的展現形式就是讓構造函數本身具有屬性和方法,大家看下面代碼:

 

複製代碼
function JsObj()
{
    this.sayHello = function(){
        console.log('Hello World!!!');
    }
    
}

JsObj.whosay = function(){
    console.log('sharpxiajun say Hello World!!!');
}

JsObj.whosay();
var obj = new JsObj();//sharpxiajun say Hello World!!!
obj.sayHello();//Hello World!!!
複製代碼

  這種寫法體現類的靜態屬性和方法特點會更加清晰些,前不久有人告訴我javascript裏面的面向對象的做法現在已經成爲了一種實現面向對象的標準,有些新語言現在就借鑑javascript來設計自己的面向對象的機制,但是我另可不去相信這個說法,我還是願意把java的方式作爲面向對象的標準方式,而javascript只是用模擬方式來實現,如果真讓我把javascript面向對象做爲一個新標準來理解,慣性的思維可能很難讓我對javascript面向對象的做法有更加清晰的認識。如果說上面代碼所運用的原理無非是javascript裏面函數就是對象,同樣可以爲其賦值或者授予方法。這個問題很簡單,也很好理解,我這裏拿出這段代碼是想告訴大家,我在讀一些經典框架源碼時候,有些設計思想就是運用了這種寫法,但是我卻沒有把他們當作靜態變量來理解,導致有些代碼沒有讀懂。

  2.關於this指針的問題

  這是javascript最最最重要的一個概念,它的用法是掌握javascript精髓的關鍵。我們先看看下面這句話很關鍵,它道出了this用法的精髓:

  關鍵字this的用法:它用在對象的方法中,this總是指向調用該方法的對象。

  這句話道出了this是存在於對象的方法,裏面包含兩個內容:對象和方法,方法屬於對象,示例格式就是:

 

複製代碼
var obj = {};//或者var obj = new Object();二者等價
obj.nation = 'China';
obj.say1 = function()
{
    console.log(obj.nation);
}
obj.say2 = function()
{
    console.log(this.nation);
}

obj.say1();//China
obj.say2();//China
複製代碼

  結果一樣,這個正好體現了this使用在對象方法中,this總是指向調用該方法的對象。這是對this用法的標準定義,但是真的把這個概念理解透還真的下功夫,下面我拋開這個定義,列舉我所知道的this的用法。

  用法一:在函數中的使用

 

複製代碼
function JsObj()
{
    this.nation = 'China';
    console.log(this.nation);
}
JsObj();//China
複製代碼

  那麼這個this指向的是JsObj函數嗎?回答是NO,this指向的是window,看下面代碼:

 

複製代碼
function JsObj()
{
    this.nation = 'China';
    console.log(this.nation);
}
JsObj();//China

console.log(nation);//China
console.log(window.nation);//China
console.log(this.nation);//China
複製代碼

  看到效果了吧,指向的是window,我想有些童鞋可能不太理解爲什麼,沒關係,這個疑問會引出我下一個小節要講的內容。

  用法二:作爲對象方法的調用

  這個用法和我最開始講this指針的用法類似,代碼如下:

 

複製代碼
function say()
{
    console.log(this.nation);
}

var obj = {};
obj.nation = 'China';
obj.objSay = say;
obj.objSay();//China
複製代碼

  用法三:作爲構造函數的調用

  說道javascript構造函數,我就要反覆再強調一個基礎知識:在javascript裏的構造函數包含了類的特性。下面看我寫的代碼:

 

複製代碼
function JsObj()
{
    this.nation = 'China';
}
var obj = new JsObj();
console.log(obj.nation);//China
複製代碼

  這個nation絕對不屬於window了,大家信不信了?我們可以測試一下:

 

複製代碼
var nation = 'USA';
function JsObj()
{
    this.nation = 'China';
}
var obj = new JsObj();
console.log(obj.nation);//China
console.log(nation);//USA
複製代碼

  用法四:apply調用時候的this

  apply()是函數對象的一個方法,它的作用是改變函數的調用對象,它的第一個參數就表示改變後的調用這個函數的對象。因此,this指的就是這第一個參數。測試代碼如下:

 

複製代碼
var nation = 'USA';
function say()
{
    console.log(this.nation);
}
var obj = {};
obj.nation = 'China';
obj.objSay = say;
obj.objSay.apply();//USA
obj.objSay.apply(obj);//China
複製代碼

  apply()的參數爲空時,默認調用全局對象。因此,這時的運行結果爲USA,證明this指的是全局對象。apply傳入obj對象,this就指向了對象obj了,運行的結果是China。

  3.執行環境及作用域

  我在上面總結javascript裏this用法時候提出了一個問題,爲什麼this指向了window,解釋這個問題就會牽涉出javascript裏面有一個非常重要的概念執行環境及作用域

  首先說道的是執行環境,什麼是執行環境呢?在javascript裏面執行環境分爲兩類,一類是全局環境,一類是局部環境,整個頁面裏被共享的方法和屬性就是在全局環境,相對於全局環境,函數{}號裏的執行環境就是局部環境,執行環境定義了變量或函數有權訪問的其他數據,決定了它們各自的行爲,每個執行環境都定義了一個與之相關的變量對象,環境中定義的所有變量和函數都保存在這個對象裏,雖然我們自己編寫的代碼無法訪問這個對象,但解析器在處理數據時候後臺會使用到它

  全局執行環境另一種說法是最外圍的一個執行環境,在web瀏覽器的範圍中(actionscript也是施行了ECMAScript標準,它的全局範圍就和javascript的全局範圍不同),全局執行環境被認爲是window對象,因此全局變量和函數都是作爲window對象的方法和屬性來創建的,全局執行環境知道應用程序退出比如關閉網頁或瀏覽器纔會被銷燬。而局部環境則是以函數對象作爲關聯對象。

  javascript語言規定了全局執行環境和局部執行環境的概念,這就產生了一個極其重要的應用:作用域鏈。當代碼在一個環境裏面被執行的時候會創建變量的對象構成的作用域鏈,它的用途是保證對執行環境有權訪問所有變量和函數的有序訪問。作用域鏈的前端始終是當前執行代碼所在的環境的變量對象。如果這個環境是函數,則將其活動對象作爲變量的對象,活動對象在最開始時只包含一個變量,就是arguments對象,arguments在全局環境中是不存在的,作用域鏈的下一個對象來自包含前一個對象的外部環境,而再下一個變量對象則來自下一個包含環境,如此類推,一直延續到全局環境,全局環境永遠是作用域鏈最後一個對象。

  呵呵,看這個解釋是不是有點暈啊,我想換個角度思考可能好理解點,在javascript裏,所有的屬性和方法都是屬於某一個對象的,其實在javascript裏面,所有方法或屬性的調用都是obj.method(),obj.name樣式,如果程序代碼裏調用屬性或方法時候找不到這個調用對象,javascript解析器就會往函數作用域的上層作用域裏找,直到window全局環境,實在找不到的就默認授予給window對象。

  好了,今天內容就寫到這麼多吧,真沒想到javascript一個個看起來簡單的對象創建能延伸出這麼多知識,回味下這種點到面的研究方法還是蠻不錯的,最近做java太多(我最近想開起一個新系列,我一直想做的系列android,不過有人跟我說一個人精力有限,我還是先把沒寫完的東西寫完),寫javascrip的激情有點不夠了,真希望有一個全職做前端的環境,不過java也是很有趣的,最近和博友交流後我越加有動力把三套javaEE框架寫完,給自己打打氣,加油了。


發佈了2 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章