領悟JavaScript

 Javascript是一門極易上手卻難於精通的語言,有人說javascript是一門基於對象的語言,並非一門面向對象的語言,可是這門語言的發明者明確告訴了咱們,這是一門面向對象的語言。javascript就像是面向對象中的90後,總不被大多數人所接受,它是基於原型的面向對象的語言。並非一般的基於類的。
         1、核心javascript
             一、數據類型    javascript做爲一門解釋型的腳本語言,它是弱類型的,它有三種基本數據類型——boolean,number,string。特殊的值:null,undefined。除了這些基本類型javascript還支持一種複合數據類型——object(對象)。
            布爾類型有2個值:true和false。
            數字是64位的浮點值,與Java的Double相似。它沒有整型。除法運算可能帶來小數位的結果。數字包括特殊值NaN(不是一個數字)和Infinity(無窮大)。
            字符串(string)是從零到多個Unicode字符組成的。沒有單獨的字節類型。一個字節被描繪成一個長度爲1的字符串。字符串被'符號或"符號引用到一塊兒,單引號和雙引號能夠替換使用,但必須先後匹配使用。
            函數是一種特殊的對象。由於它包含了一段能夠執行的代碼。

             二、對象是詞典    在 JavaScript 中,對象只是一組名稱/值對,就是說,將 JavaScript 對象視爲包含字符串關鍵字的詞典。咱們可使用熟悉的「.」(點)運算符或「[]」運算符,來得到和設置對象的屬性,這是在處理詞典時一般採用的方法。如下代碼段:
             var userObject = new Object();
            userObject.lastLoginTime = new Date();
            alert(userObject.lastLoginTime); 
的功能與下面的代碼段徹底相同:
             var userObject = {};     // equivalent to new Object()
            userObject[「lastLoginTime」] = new Date();
            alert(userObject[「lastLoginTime」]);
咱們還能夠直接在 userObject 的定義中定義 lastLoginTime 屬性,以下所示:
             var userObject = { 「lastLoginTime」: new Date(), "userName": "NanGe" };
            alert(userObject.lastLoginTime);
用for語句能夠實現對象一個枚舉的能力:
             for (var n in userObject ) {
                    if (userObject.hasOwnProperty(n)) {
                        document.writeln("<p>" + n + ": " + userObject + "</p>");
                    }
            }
            這個結果應該是:
<p>lastLoginTime: new Date()</p>
<p>userName: NanGe</p>
            JavaScript的對象模式是JSON的數據交換格式的基礎。 新的成員能夠在任什麼時候候被附着增長到任何對象上。如:
             userObject .password = "666666";

             三、數組    JavaScript中數組也是哈希表對象,當你聲明瞭一個數組,你不須要聲明它的大小,數組會自增加,這很像Java的向量(vectors)或者是ArrayList。有2種方式建立一個新數組:
             var myArray = [ ];
            var myArray = new Array();
            數組不是強類型,它能夠包括數字、字符串、布爾值、對象、函數和數組,你能夠在一個數組中混合使用字符串、數字和對象。數組的第一個索引是0。

             四、靈活的函數    在大部分編程語言中,函數和對象一般是不一樣的東西,在javascript中,函數即對象,只是它有點特別——它是具備與它關聯的可執行代碼的對象。先來看一個普通函數:
             function func(x) {
                alert(x);
            }
            func(「blah」);
            這是一般的建立函數的方法;可是,你還能夠經過建立一個匿名函數對象,而後將它賦值給一個變量。如:
             var func = function(x) {
                alert(x);
            };
            func("blah");
            甚至你還能夠這樣,使用Function構造函數:
             var func = new Function("x", "alert(x);");
            func("blah");
            此示例代表函數實際上只是支持函數調用操做的對象。最後一個使用 Function 構造函數來定義函數的方法並不經常使用,但它展現的可能性很是有趣,由於你可能注意到,該函數的主體正是 Function 構造函數的 String 參數,而且最後一個參數纔是函數的主體,以前的參數全是函數的參數。這意味着,您能夠在運行時構造任意函數。
            爲了能進一步說明函數即對象,你能夠像其餘任何javascript對象同樣,對函數設置或者添加屬性。好比:
             functiion func(x) {
                alert(x + func.text);
            }
            func.text = "Hello World!";
            func["text2"] = "Hello World .... again!";
            alert(func["text"]);    //display "Hello World!"
            alert(func.text2);    //display "Hello World .... again!"
            func("Hello ");        //display "Hello Hello World!"

            做爲對象,函數還能夠賦給變量、做爲參數傳遞給其餘函數、做爲其餘函數的值返回,並能夠做爲對象的屬性或數組的元素進行存儲等等。下面的例子將對其進行說明:
             // assign an anonmyous function to a variable 
            var greet = function(x) {
                alert("Hello " + x);
            }
            greet("nange");

            // passing a function as an argument to another
            function square(x) {
                return x * x;
            }
            function operateOn(num, func) {
                return func(num);
            }
            alert(operateOn(16, square));    // display 256

            // function as return value
            function makeIncrement() {
                return function(x) { return x + 1;};
            }
            var inc = makeIncrement();
            alert(inc(7));       // display 8

            // function stored as array element
            var arr = [];
            arr[0] = function(x) { return x * x; };
            arr[1] = arr[0](2);
            arr[2] = arr[0](arr[1]);
            arr[3] = arr[0](arr[2]);
            alert(arr[3]);    // display 256

            // function as object propertie
            var obj = {"toString": function() { return "this is an object!"; }};
            alert(obj);    // display "this is an object!" 

            向對象中添加方法是一件很是容易的事情,只須要記住名稱,而後將函數賦值給該名稱便可,咱們能夠方便的使用匿名函數來完成這件事(固然了,你也能夠不使用匿名函數)。以下所示:
             var myDog = {
                "name" ; "HuaHua",
                "bark" : function() { alert("Woof !"); },
                "displayFullName" : function() {
                    alert(this.name + "the beautiful dog !");
                },
                "otherMethod" ; function() {
                    // implementation beyond the scope of this aticle
                }
            };
             myDog.displayFullName();    // display "HuaHua the beautiful dog !"
            myDog.bark();    // display "Woof !"
            上面的例子中,displayFullName方法中的「this」值的就是myDog對象,由於它位於myDog對象中,那麼當前的對象就是myDog。可是"this"的值不是靜態的,經過不一樣對象調用"this"時,它的值也會更改,以便指向相應的對象。以下所示:
             function callWhichName() {
                alert(this.name);
            }
            var nange = {
                "name" : "nange",
                "sayIt" : callWhichName
            };
            var zhangsan = {
                 "name" : "zhangsan",
                 "sayIt" : callWhichName
            };
            var lisi = {
                "name" : "lisi",
            };
            nange.sayIt();    // display "nange"
            zhangsan.sayIt();     // display "zhangsan"
            callWhichName.call(lisi);    // display "lisi" 

            上面的例子中,最後一段代碼表示的是,將函數做爲對象的方法進行調用的另一種方式。爲何函數"callWhichName"能夠調用"call"方法?請記住,JavaScript 中的函數是對象。每一個函數對象都有一個名爲 call 的方法,它將函數做爲第一個參數的方法進行調用。就是說,做爲函數第一個參數傳遞給 call 的任何對象都將在函數調用中成爲「this」的值。
            有一點須要注意的是,必定不要直接調用包含"this"(卻沒有所屬對象,即全局對象以外的對象)關鍵字的函數,不然將可能影響全局命名空間,由於此時的"this"指的是全局對象(在瀏覽器中即window),這極可能會產生災難性的後果。例如,下面的代碼將改變javascript全局函數isNaN的行爲。必定不要這樣作!
             alert("NaN is NaN:" + isNaN(NaN));    // dispaly true
            function x() {
                this.isNaN = function() {
                    return "not any more!";
                };
            }
            x();
            alert("NaN is NaN:" + isNaN(NaN));    // dispaly "not any more!"  

            到這裏,咱們已經介紹瞭如何建立對象,包括它的屬性和方法。但若是注意上面的全部代碼段,你會發現屬性和方法是在對象定義自己中進行硬編碼的。但若是須要更好地控制對象的建立,該怎麼作呢?例如,您可能須要根據某些參數來計算對象的屬性值。或者,可能須要將對象的屬性初始化爲僅在運行時才能得到的值。也可能須要建立對象的多個實例(此要求很是常見)。
            在Java中,咱們一般是採用類來實例化對象實例,但在javascript中有所不一樣,javascript沒有類,這時,函數在與"new"關鍵字一塊兒使用時,函數就變成了構造函數。下一節將對其進行說明。

             五、構造函數    前面提到過,javascript的OOP就像OOP中的90後,它比較異類,由於沒有類的概念。當一個函數用於初始化對象的時候就被叫作構造函數。一個函數構造函數帶着"new"前綴被調用。如:
             new Constructor(parameters...)
            根據習慣,咱們將構造函數的首字母大寫。
            調用構造函數時,將會返回一個新的對象(除非明確的使用了return語句進行替換),new前綴改變了this的含義,this將引用這個返回的對象。來看一個例子:
             function Dog(name) {
                this.name = name;
                this.respondTo = function(name) {
                    if (this.name == name) {
                        alert("Woof !");
                    }
                }
            }
            var spot = new Dog("Spot");
            spot.respondTo("Rover");    // display none
            spot.respondTo("Spot");    // display "Woof !"

            咱們觀察上面的例子能夠發現,每個Dog的實例對象,都有本身的name實例副本,這是咱們所但願的,由於每一個實例都須要一個name屬性來表示其狀態,但同時,每一個實例對象也都有一個respondTo方法的實例副本,這就是一個浪費,由於每一個實例其實均可以共享同一個方法,這應該怎麼辦呢? javascript爲咱們提供了原型對象,就能夠解決這個問題。下一節對其進行介紹。

             六、原型(Porototype)    以前咱們提到過,javascript是基於原型的面向對象的編程語言。因此在javascript的面向對象編程中,原型對象是個核心概念。在 JavaScript 中,每一個函數都有名爲「prototype」的屬性,用於引用原型對象。此原型對象又有名爲「constructor」的屬性,它反過來引用函數自己。這是一種循環引用。下圖就描述了這種循環引用的關係:

 

圖片

 

            下面的代碼能進一步說明問題:
             var spot = new Dog("Spot");
            alert(Dog.prototype.isPrototypeOf(spot));    // display true
            alert(spot.constructor == Dog.prototype.constructor);    // display true
            alert(spot.constructor == Dog);    // display true

            alert(spot.hasOwnProperty("constructor"));    // display false why?? because constructor property doesn't belong to spot.
            alert(Dog.prototype.hasOwnProperty("constructor"));    // display true

             從上面的例子中,你可能注意到:對 hasOwnProperty 和 isPrototypeOf 方法的調用。這些方法是從哪裏來的呢?它們不是來自 Dog.prototype。實際上,在 Dog.prototype 和 Dog 實例中還能夠調用其餘方法,好比 toString、toLocaleString 和 valueOf,但它們都不來自 Dog.prototype。就像在Java中,全部類都繼承自Object類同樣,在javascript中,Object.prototype 是全部原型的最終基礎原型。(Object.prototype 的原型是 null。)在此示例中,請記住 Dog.prototype 是對象。它是經過調用 Object 構造函數建立的(儘管它不可見)。
             Dog.prototype = new Object();

            所以,正如 Dog 實例繼承 Dog.prototype 同樣,Dog.prototype 繼承 Object.prototype。這使得全部 Dog 實例也繼承了 Object.prototype 的方法和屬性。
            每一個 JavaScript 對象都繼承一個原型鏈,而全部原型都終止於 Object.prototype。注意,迄今爲止你看到的這種繼承是活動對象之間的繼承。它不一樣於繼承的常見概念,後者是指在聲明類時類之間的發生的繼承。所以,JavaScript 繼承動態性更強。它使用簡單算法實現這一點,以下所示:當你嘗試訪問對象的屬性/方法時,JavaScript 將檢查該屬性/方法是不是在該對象中定義的。若是不是,則檢查對象的原型。若是還不是,則檢查該對象的原型的原型,如此繼續,一直檢查到 Object.prototype。以下圖所示:

 

圖片

 

            JavaScript 動態地解析屬性訪問和方法調用的方式產生了一些特殊效果:
  • 繼承原型對象的對象上能夠當即呈現對原型所作的更改,即便是在建立這些對象以後。
  • 若是在對象中定義了屬性/方法 X,則該對象的原型中將隱藏同名的屬性/方法。例如,經過在 Dog.prototype 中定義 toString 方法,能夠改寫 Object.prototype 的 toString 方法。
  • 更改只沿一個方向傳遞,即從原型到它的派生對象,但不能沿相反方向傳遞。
            下面的代碼將說明上面這些效果:
             function Dog() { }
            var redDog = new Dog();
            var greenDog = new Dog();
            Dog.prototype.greet = function() {
                return "Hello My Host";
            };
            alert(redDog.greet());    // display "Hello My Host"
            greenDog.greet = function() {
                return "Hello My Host .... again";
            };
            alert(greenDog.greet());    // display "Hello My Host .... again"
            alert(redDog.greet());    // display "Hello My Host"  

             七、變量做用域   JavaScript的變量做用域有三個特色:做用域是基於其特有的做用域鏈的、沒有塊級做用域、函數中聲明的變量在整個函數中都有定義。看下面一些例子:
  •   javascript做用域鏈    
             var rain = 1;
            function rainman(){
                var man = 2;
                function inner(){
                    var innerVar = 4;
                    alert(rain);
                }
                inner();    
            }
            rainman();    // display 1
            做用域鏈:JavaScript須要查詢一個變量x時,首先會查找做用域鏈的第一個對象,若是以第一個對象沒有定義x變量,JavaScript會繼續查找第二個對象有沒有定義x變量,若是第二個對象沒有定義則會繼續查找,以此類推。
            上面的代碼涉及到了三個做用域鏈對象,依次是:inner、rainman、window。
  • 函數內部,局部變量的優先級比同名的全局變量優先級高
             var rain = 1;    // assign global variable rain
            function check(){
                var rain = 100;    // assign local variable rain
                alert( rain );       // display 100
            }
            check();
            alert( rain ); 
  • JavaScript沒有塊級做用域
             這一點也是JavaScript相比其它語言較靈活的部分。仔細觀察下面的代碼,你會發現變量i、j、k做用域是相同的,他們在整個rain函數體內都是全局的。
             function rainman(){
                //  i, j, k as three local variable in function rainman
                var i = 0;
                if ( 1 ) {
                    var j = 0;
                    for(var k = 0; k < 3; k++) {
                        alert( k );    // display 0 1 2
                    }
                    alert( k );        // display 3
                }
                alert( j );            // display 0
            }
  • 函數中聲明的變量在整個函數中都有定義
             function rain(){
                var x = 1;
                function man(){
                    x = 100;
                }
                man();        
                alert( x );    // display 100
            }
            rain();    

            上面得代碼說明了,變量x在整個rain函數體內均可以使用,並能夠從新賦值。因爲這條規則,會產生「匪夷所思」的結果,觀察下面的代碼:
             var x = 1;
            function rain(){
                  alert( x );        // display 'undefined',not 1
                  var x = 'rain-man';
                  alert( x );        // display  'rain-man'
             }
            rain();

            這是因爲在函數rain內局部變量x在整個函數體內都有定義( var x= 'rain-man',進行了聲明),因此在整個rain函數體內隱藏了同名的全局變量x。這裏之因此會彈出'undefined'是由於,第一個執行alert(x)時,局部變量x仍未被初始化。 
            因此上面的rain函數等同於下面的函數:
             function rain(){
                var x;
                alert( x );
                x = 'rain-man';
                alert( x );
            }
  • 未使用var關鍵字聲明的變量都是全局變量
             function rain(){
                x = 100;    // assigned a global variable x and initailize it to 100
            }
            rain();
            alert( x );    // display 100
  • 全局變量都是window的屬性
             var x = 100 ;
            alert( window.x );//彈出100
            alert(x);

             八、理解閉包(高手的必經階段)    閉包是當內部函數綁定到它的外部函數的本地變量時所發生的運行時現象。很明顯,除非此內部函數以某種方式可被外部函數訪問,不然它沒有多少意義。示例能夠更好說明這一點。
            假設須要根據一個簡單條件篩選一個數字序列,這個條件是:只有大於 100 的數字才能經過篩選,並忽略其他數字。那麼咱們一般能夠編寫以下代碼:
             function filter(pred, arr) {
                var len = arr.length;
                var filtered = []; // shorter version of new Array();
                // iterate through every element in the array...
                for(var i = 0; i < len; i++) {
                    var val = arr[i];
                    // if the element satisfies the predicate let it through
                    if(pred(val)) {
                        filtered.push(val);
                    }
                }
                return filtered;
            }

            var someRandomNumbers = [12, 32, 1, 3, 2, 2, 234, 236, 632,7, 8];
            var numbersGreaterThan100 = filter(
            function(x) { return (x > 100) ? true : false; }, 
            someRandomNumbers);

            // displays 234, 236, 632
            alert(numbersGreaterThan100);
            可是,如今要建立不一樣的篩選條件,假設此次只有大於 300 的數字才能經過篩選,則能夠編寫下面這樣的函數:
             var greaterThan300 = filter(  function(x) { return (x > 300) ? true : false; }, someRandomNumbers);
            而後,也許須要篩選大於 50、2五、十、600 如此等等的數字,但做爲一個聰明人,你會發現它們所有都有相同的謂詞「greater than」,只有數字不一樣。所以,能夠用相似下面的函數分開各個數字:
             function makeGreaterThanPredicate(lowerBound) {
                return function(numberToCheck) {
                    return (numberToCheck > lowerBound) ? true : false;
                };
            }
            這樣,您就能夠編寫如下代碼:
             var greaterThan10 = makeGreaterThanPredicate(10);
            var greaterThan100 = makeGreaterThanPredicate(100);
            alert(filter(greaterThan10, someRandomNumbers));
            alert(filter(greaterThan100, someRandomNumbers));

            經過觀察函數 makeGreaterThanPredicate 返回的內部匿名函數,能夠發現,該匿名內部函數使用 lowerBound,後者是傳遞給 makeGreaterThanPredicate 的參數。按照做用域的通常規則,當 makeGreaterThanPredicate 退出時,lowerBound 超出了做用域!但在這裏,內部匿名函數仍然攜帶 lowerBound,甚至在 makeGreaterThanPredicate 退出以後的很長時間內仍然如此。這就是咱們所說的閉包:由於內部函數關閉了定義它的環境(即外部函數的參數和本地變量)。
            開始可能感受不到閉包的功能很強大。但若是應用恰當,它們就能夠很是有創造性地幫您將想法轉換成代碼,這個過程很是有趣。在 JavaScript 中,閉包最有趣的用途之一是模擬類的私有變量。
             function Person(name, age) {
                this.getName = function() { return name; };
                this.setName = function(newName) { name = newName; };
                this.getAge = function() { return age; };
                this.setAge = function(newAge) { age = newAge; };
            }
            參數 name 和 age 是構造函數 Person 的本地變量。Person 返回時,name 和 age 應當永遠消失。可是,它們被做爲 Person 實例的方法而分配的四個內部函數捕獲,實際上這會使 name 和 age 繼續存在,但只能嚴格地經過這四個方法訪問它們。以下:
             var nange = new Person("nange", 23);
            alert(nange.getName());    // display "nange"
            alert(nange.getAge());    // display 23
            nange.setName("nange2");
            nange.setAge(24);
            alert("now person is :" + nange.getName() + ", " + nange.getAge());    // display "now person is : nange2, 24"

             九、返回值    JavaScript沒有void類型,因此每一個函數都必須返回一個值,除了構造器以外默認值是undefined,構造器的默認返回值是this。

             十、語句    語句包括 var, if, switch, for, while, do, break, continue, return, try, throw, and with,它們大多數與C類的語言相同。
            var語句是一個或多個變量名稱的列表,它們以逗號隔開,帶着可選的初始化表達式。
            var a, b = window.document.body;
            若是var語句出如今全部的函數以外那麼就聲明瞭一個全局的變量。若是出如今一個函數內,就聲明瞭一個局部的變量。
            在if語句,while語句,do語句, 和符合邏輯的操做符中,JavaScript視爲false,null,undefined, ""(空字符串),和數字0做爲假(false)。其它全部值被視爲真(true)。
            在switch語句中的case標記能夠是表達式,它們沒必要非得是常數,也能夠是字符串。
            有2種形式的for語句,第一種像這樣 (init; test; inc)形式。第二種是對象重複器。
             for (name in object) {
                if (object.hasOwnProperty(name)) {
                    value = object[name];
                }
            }
            上面執行在object中的每個name,名字產生的次序是不肯定的。
            with語句不該該被使用。


             2、瀏覽器中的Javascript
            待續。。。
相關文章
相關標籤/搜索