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框架寫完,給自己打打氣,加油瞭。

 

 

作者 夏天的森林

發佈留言