JavaScript知識點滴

  • JavaScript的函數參數僅僅具有指示作用, 在實際調用中, 你可以給一個函數傳任意個參數. 當然, 如果函數體內需要某個參數, 而你沒有傳, 調用可能就不能按預期的方式進行; 反之, 傳遞多餘的參數, 函數在執行過程中根本不會管他. 但是如果函數定義的時候加進了參數檢測機制, 意味着, 傳遞任意個參數可能都是有意義的. 在C裏面, 這叫做變參函數. 當然JavaScript的函數參數遠比C靈活, 可以用以下方式取得函數的參數.

    function param_test(){
    	alert("參數個數:"+arguments.length);
    	for(i=0;i<arguments.length;i++){
    		alert("參數"+i+"的值:"+arguments[i]);
    	}
    }
    param_test("first","second","third");
  • JavaScript不要求變量聲明, 但是它使用了一個非常雷人的規則, 就是在函數內部使用var聲明的變量是局部變量, 而沒有聲明的變量是全局變量. 在函數內部不使用任何關鍵字(也就是沒有任何指示)的情況下, 變量默認是全局變量, 只能說, JavaScript太喜歡全局變量了. 但是實際當中, 我們使用的局部變量遠比全局變量多. 這個規則改成局部使用一個global關鍵字聲明全局變量比較好, 還有, 既然無需聲明, 局部使用全局變量的時候應該指示一下, 以免不小心和一個全局變量同名了, PHP就是這樣做的. 當然了, 一個流行的語言, 是無法更改規則的, 不得不考慮人們已經形成的習慣, 和兼容性.

  • 小經驗     服務器對於死循環會給予斷開處理, 但是對於遞歸調用導致的堆棧溢出, 在非調試模式下沒有明確指示, 而結果是不可預知的, 也就是你可能遇到各種無法解釋的現象, 因爲堆棧溢出導致變量可能失效, 這時候調試信息是不能作準的.

  • 使用JavaScript動態加載另一個JS腳本, 有三種方式:

    //1.
    <script type="javascript"> 
        document.write("<script src='test.js'><\/script>"); 
    </script>
    //2.
    <script src='' id="s1"></script> 
    <script type="javascript"> 
        s1.src="test.js" //重新設置源JS文件.
    </script>
    //3. 
    <script language="javascript"> 
     var oHead = document.getElementsByTagName('HEAD').item(0); 
        var oScript= document.createElement("script"); 
        oScript.type = "text/javascript"; 
        oScript.src="test.js"; 
        oHead.appendChild( oScript); 
    </script>

    但是這些加載都是動態的, 也就是說, 腳本執行完後, 不一定保證JS已經真的加載完成, 畢竟需要從服務器下載腳本數據. 可以使用XMLHttpRequest方法, 把需要加載的JS下載下來, 再使用方法3把下載的文本設置爲script對象的text屬性. 因爲XMLHttpRequest對象在下載完成後會觸發一個事件, 我們需要把後續執行放在這個事件回調裏, 而不是原來的腳本執行序列裏. XMLHttpRequest的用法, 另行介紹.

    網頁儘量使用一個JS文件和CSS文件, 且在頭部加載, 瀏覽器加載網頁時, 遇到腳本, 會等待腳本加載, 因爲腳本完全可能導致頁面非常大的行爲差異, 所以乾脆優先處理腳本. 腳本文本一般即使有幾十K大小(這一般很大了), 加載是很快的. 網頁主要的延時在於不同的文件請求和圖像視頻flash等數據大戶, 而且少用外部鏈接. 所以動態加載不同的CSS和JS腳本儘量不要使用, 因爲寫在一個文件裏, 反倒更節省資源.

  • JavaScript的面向對象和繼承, 這一點另寫了一篇文章:論JavaScript和C/C++的想通之處

  • 對象拷貝 JavaScript沒有提供複製一個對象的方法, 事實上, 你確實很少需要這麼做, C++要特別小心的使用對象複製操作, 因爲對象可能很複雜, 簡單的複製成員是不行的. 雖然如此, 也不是沒有這種需求, JavaScript的很多內置對象提供了複製和克隆函數, 自定義對象, 也可以定義給一個克隆函數, 需要的話, 就調用它. 

  • 閉包    在理解了JavaScript的函數其實就是C++的類之後, 閉包就非常簡單了. 閉包就是一個定義在函數內部的函數, 和定義在外部不同, 它們不是全局的, 即使這個函數被執行, 定義在它內部的函數也不能在外部被直接從函數名調用它. 但是可以通過把它賦值給外部能訪問的變量或者父函數返回它這種方式來訪問它. 閉包特殊的地方在於, 閉包一旦創建完成, 和父函數就沒有任何關係了, 但是每調用一次父函數(包括直接調用和 new 創建實例的調用) 閉包就會創建一分父函數的局部變量, 這就導致父函數成爲了一個類似C++類定義的東西, 閉包使用的變量是保存在自己內部的, 而不是父函數內部. 

    我們知道, 一個對象的屬性既可以是數據對象, 也可以是函數, 而對象的函數可以通過obj.func這種方式調用, 而func內部通過this指針可以訪問對象的任何屬性, 這本質上已經完成了類的功能. 閉包實際上做的是類似的事情, 只不過使用了另一種規則和方式, 但是本質是相同的, 而且閉包不允許外部訪問它保存的對象, 所以又類似於C++ private的功能.

    function A(){
    	var i = 0;
    	this.f1 = function(){
    		return ++i;
    	}
    	this.f2 = function(){
    		return ++i;
    	}
    }
    var a = new A();
    alert(a.f1());
    alert(a.f2());

    如果把function A() 換成class A, 並且把f1,f2前面的this去掉, 你會發現, 這就是C++的類定義, 連調用方式都一樣!!!相同的功能用屬性來實現:
    var a = new Object();
    a.i = 0;
    a.f1 = function(){
    	return ++this.i;
    }
    a.f2 = function(){
    	return ++this.i;
    }
    alert(a.f1());
    alert(a.f2());
    //把上述定義語句合成一個A函數.
    function A(){
    	this.i = 0;
    	this.f1 = function(){
    		return ++this.i;
    	}
    	this.f2 = function(){
    		return ++this.i;
    	}
    }
    a = new A();
    alert(a.f1());
    alert(a.f2());
    上述方式和閉包的區別在於, 閉包沒有向實例添加屬性, 那麼外部就無法訪問那些變量. 閉包的實質就是允許在函數內部定義函數, 這個特性的結果就是能夠構造一個封閉特性的隱藏對象, 訪問這些數據, 只能通過閉包對應的函數, 這也是閉包名稱的由來.
發佈了63 篇原創文章 · 獲贊 7 · 訪問量 42萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章