[Javascript]:JS數據類型、類型判斷、函數function與Function的使用

      因爲之前學過Java,在學習JS時,對JS的數據類型、function和Function的使用概念容易搞混淆,理解不怎麼深刻。遂將這些知識點和概念梳理一遍,以便自己和他人理解。如有錯誤,敬請諒解。

      知識點會涉及到:JS數據類型、JS的類型判斷方式、函數創建方式、function與Function的區別、匿名函數的使用、函數特點以及函數重載問題。

Javascript數據類型

  • 基本類型:undefined(未定義)、null(空)、number(數字)、string、boolean、symbol(新增的,沒用過)
  • 引用類型:object function
  • 基本類型即值類型,引用類型即對象類型,和Java概念差不多。

在許多JS學習資料中,都會看見數據類型是以Undefined、Null、String...這種形式,而不是我上面的那種小寫形式。關於這個問題,會進行解釋。

  • 這是因爲在JS中,一切基於對象,所有事物都可以看作對象。這意味着在JS中使用的一切都是對象。給一個簡單實例:給一個變量賦值,這個過程沒用到new操作符創建相應對象,但卻可以使用它的包裝對象方法。如下:
  <!-- 以字面量形式創建 -->
    <script>
       var a = 15;
       var b = "abcde";
       var len  = b.length;
       var c = b.charAt(0);
       document.write("長度:" + len + ", b索引字符:" + c);
       //結果:長度:5, b:a
    </script>
    <!--以對象形式來創建 -->
    <script>
       var a = new Number(15);
       var b = new String("abcde");
       document.write(a.length + " , " + b.charAt(0)); //結果和上面一樣,兩者使用方式相同
    </script> 

        上面的實例驗證了:在JS中,直接使用值類型給變量賦值,JS也會把基本類型包裝成相應對象,這時變量就是一個對象,所以才能直接使用方法。和Java相比,Java只有在需要值類型和包裝類需要轉換時纔會自動進行裝箱拆箱,否則嚴格按照本身定義的類型來使用,不會全部轉成對象。所以搞明白JS中的一切都是對象這個概念後,包括變量、函數什麼的都可以看作是對象。

       這時再來理解爲什麼數據類型都是大寫而不是小寫就很簡單了。關於上面的基本類型,因爲JS一切爲對象的原則,所以undefined、null、object等就沒有實際使用意義,上面寫出來也只是爲了搞明白它們之間的區別和相關概念。這些原始類型的作用僅僅是在使用 typeof 和 instanceof  用來判斷具體類型,或是作爲返回的字符串,用來表明該類型是什麼,是基本類型還是引用類型,其他地方就用不到了~


所以,數據類型用對象類型來會更符合JS的定義:

Undefined、Null、Number、String、Boolean、Symbol、Object、 Function

  • Undefined表示的是一個變量被聲明但是沒有初始化,即沒有賦值。值爲undefined。
  • Null則和Java中概念差不多,唯一值是null,表示對象的引用地址爲空。
  • 關於Object和Function後面會說到,至於其他沒什麼可說的。
       

typeof 和 instanceof 的用法

typeof和instanceof都是運算符,它們都可以用來判斷變量類型,但是它們各自的用法和操作對象又有所不同。


 typeof用法 

typeof 的返回值 和用於判斷的值都是JS的原始類型,就是最上面寫的那些類型。

typeof(var):獲取指定變量的具體類型。

  <!-- 使用typeof()來得到變量的原始類型,也可以直接 typeof var -->
    <script>
       var a = 15;
       var b = "abcde";
       var c = false;
       var d = function(){} //這是一個函數
       var e = new Function(); //這是一個函數對象
       var f; //沒賦值
      document.write("a:" + typeof(a) + "<br>");
      document.write("b:" + typeof(b) + "<br>");
      document.write("c:" + typeof(c) + "<br>");
      document.write("d:" + typeof(d) + "<br>");
      document.write("e:" + typeof(e) + "<br>");
      document.write("f:" + typeof f +",undef:"+typeof(undef)+ "<br>");
    </script>
    <!--結果
        a:number
        b:string
        c:boolean
        d:function
        e:function
        f:undefined,undef:undefined -->


typeof var === 'type' 對變量類型進行判斷。

 var a = 150;
        var b = "abcde";
        var c = false;
        alert(typeof a === 'number');    //true
        alert(typeof b === 'number');    //fasle
        alert(typeof c === 'boolean');   //true
        alert(typeof d === 'undefined'); //true      

使用typeof判斷對象類型變量時,都是返回object。

 <script>
        //定義兩個構造函數
        function cons1(){}
        function cons2(){}
        var a = new cons1();
        var b = new cons2();
        alert(typeof a === cons1);//false
        alert(typeof b === cons2);//false
        alert(typeof a);//object
        alert(typeof b);//object
 </script>

正因爲typeof對於判斷對象類型具有侷限性,所以判斷對象類型應使用insatanceof運算符。



 instanceof用法 

object instanceof Object2,instanceof運算符會判斷指定對象類型的prototype

 //定義兩個構造函數
        function cons1(){}
        function cons2(){}
        var a = new cons1();
        var b = new cons2();
        var aa = new Function(); //這是一個函數對象
        var arr = []; //字面量創建數組
        var arr2 = new Array();//數組對象創建數組
        var obj = {name:"傲天", age:19}; //使用字面量創建對象
        alert(a instanceof cons1); //返回true
        alert(b instanceof cons2); //返回true
        alert(aa instanceof Function); //返回true
        alert(arr instanceof Object); //返回true
        alert(arr instanceof Array); //返回true
        alert(obj instanceof Object); //返回true
兩者區別:

typeof用於判斷基本類型,instanceof 用於判斷對象類型。



function和Function區別,創建函數、函數特點


Function與function的區別:      

  • Function是一個功能完整的對象,作爲JS的內置對象之一。而function只是一個關鍵字,用來創建一個普通函數或對象的構造函數。JS的普通函數都是Function對象的實例,所以函數本身也是一個對象,就像var一樣,只不過這個對象具有可調用特徵而已。


function創建對象和普通函數的區別:

  • 如果用function創建構造函數,例如:var a = new function{}() 或者  var b = new Person();  則a、b就不是普通函數,而是作爲一個真正對象,是Object類型。雖然普通函數也是對象,但一個是Function,作爲函數;一個是Object,作爲對象。 關於這個問題,可以使用instanceof運算符去驗證。
  • 本篇不會涉及使用function創建對象的相關知識,這裏僅作了解。

注意:關鍵字function和原始類型的function名稱是相同的,但就像object和Object一樣,兩者沒半毛錢關係。一個是作爲創建函數的關鍵字,一個是用來判斷一個對象(函數)是不是Funtion類型。別搞混了。  



 創建函數的方式 

        //  函數聲明:使用function聲明一個函數,再爲其指定一個函數名。
        function first(){
            alert("函數聲明方式");
        }
        //  函數表達式:使用function聲明一個函數,但沒有函數名,而是將這個函數賦給一個變量。也叫作匿名函數
        var second = function(arg1, arg2){
            alert(arg1+"|" + arg2+"\n匿名函數方式")
        }
        //  使用Function對象構造函數創建函數
        var third = new Function(
            "a1", "a2", "alert('函數對象,' + a1 + '|' + a2)"
        );

調用函數方式可以有如下幾種:

        //  1.直接執行方式或採用事件來執行函數
        first();
        second("ABC", "XYZ");
        third("火", "土");

        //  2.函數也是對象,所以像變量一樣.把函數名賦給另一個變量,那個變量就指向該函數地址
        //  相當於再進行一次封裝。動態添加對象方法時會用到。
        var a = first;
        var b = second; 
        a();
        b("ABC", "XYZ");

        //注意!這種方式是直接執行third()函數,而不是賦值函數名。
        var c = third("火", "土");

三者的不同和優缺點:

  • 函數聲明在使用之前,就會被加載到作用域中,隨時等待調用。而函數表達式(匿名函數)則是在代碼執行到那一行才被定義,提前使用則會出錯。
        first(); //可以執行
        function first(){
            alert("函數聲明方式");
        }
        second(15,25); //執行出錯
        var second = function(arg1, arg2){
            alert(arg1+"|" + arg2+"\n匿名函數方式")
        }
  • 匿名函數(函數表達式)與Function()構造函數用法很相似,它們的不同點:匿名函數在使用時只被解析一次。而作爲字符串傳遞給Function()構造函數的JS代碼在每次調用構造函數時都要被解析和編譯一次。Function對象的就是通過構造函數動態地創建和編譯一個函數,但最好不要過多使用,因爲用Function對象構造函數定義函數比其他兩種慢多了。


 匿名函數 

  • 匿名函數就是上面提到的函數表達式。匿名函數從字面意義來講,就是沒名字的函數,但它需要有一個依附體,要讓匿名函數能被找到,不然的話沒有意義。匿名函數看起來像Java的匿名內部類,但兩者的使用方式差別巨大。但可以去了解一下,對於使用匿名函數或許有不一樣的理解。
  • 匿名函數除了可以賦值給一個var,用變量來調用匿名函數;還能把匿名函數賦給一個事件,作爲事件處理程序,但別的函數方式也能達到這種效果。但匿名函數最特殊的地方是它的調用方式,下面會一一列舉。
實例

        // 定義一個匿名函數,依附於一個變量
        var first = function(a1, a2){
            alert(a1 + "|" + a2 + "\n變量名調用匿名函數")
        }
        //通過var名調用
        first(111, 222);

        //依附一個事件,隨着事件觸發而執行
        window.onload = function () {
            alert("頁面加載完成後觸發事件!");
        }


上述匿名函數需要觸發相關事件,或是主動執行。有沒有匿名函數自動執行的方式呢?如下:

立即執行的匿名函數

 //在匿名函數體後面加上(),表示立即執行該函數
        var second = function(){
            alert("代碼執行到此處,立即執行。無需var()");
        }(); //加分號表示執行完成,可加可不加。

        //匿名函數有參,則往裏面傳參數
        var second = function(a1, a2){
            alert(a1 + "|" + a2 + "\n用括號給匿名函數傳入參數,立即執行");
        }(555,666);

因爲函數本身也是對象,所以能賦給變量、事件,自然也能賦給一個對象。

//匿名函數賦給anyobj對象,然後anyobj再賦給second變量。這種方式沒啥意義,瞭解原理即可。
        var second = {
            anyobj:function(msg){
                alert(msg);
            }("依附一個對象的匿名函數")
        }

觀察上述調用方式,它們都需要一個依附體(變量、事件等),然後這些對象會通過()調用匿名函數,這就是立即執行的匿名函數。但能不能換成別的依附體呢?讓匿名函數看起來更像是匿名函數。如下:

  //匿名函數放在括號體內
        (
            function(msg){
                alert(msg + "\n匿名函數放在括號內,作爲函數表達式");
            }("傳入參數")
        );

這種調用方式是:外面括號包含匿名函數,然後調用()來立即執行函數。這種方式爲什麼能調用呢?這就要說到小括號的作用,外面的小括號相當於運算符,而此時匿名函數放在裏面就形成了函數表達式,然後通過()就能調用該匿名函數。


從這個角度來看,我們還能使用其他運算符來執行匿名函數。

 //使用一元運算符和匿名函數形成函數表達式
        +function(msg){
            alert(msg);
        }("+")
        -function(msg){
            alert(msg);
        }("-")
        !function(msg){
            alert(msg);
        }("!")
        ~function(msg){
            alert(msg);
        }("~")
        *function(msg){
            alert(msg);
        }("*")

      

      關於匿名函數的調用方式就說到這裏,當然還有許多細節沒說到,這裏也僅僅是作爲參考,至於還有沒有其他運算符來執行匿名函數就不過多瞭解,最常用的也就是以上幾種。最後想說,瞭解基本概念後,一定要多寫,寫得多自然也就理解了。


 JS函數的特點 

  1. 因爲JS的弱類型特性,函數形參無法指定具體類型,返回類型自然也沒有,return可以返回任何類型。
  2. JS函數不限制傳遞的參數數量。這表明就算函數的參數列表已確定,照樣可以傳入多個參數。
  3. JS函數沒有重載,如果有同名函數,後一個會覆蓋前一個。
實例
  <script>
        // 定義函數,返回傳入的參數值
        function getVal(val){
            return val;
        }
        var a = getVal("傳入val值", 200); //傳入2個參數
        //只定義了兩個參數a,b 可以是任何類型
       function method1(arg1, arg2){
            console.log("method1函數值:" + arg1 + "," + arg2);
            console.log("a值:" + a);
       }
    </script>
    <!-- 往method1函數中傳入4個參數 -->
    <button onclick="method1(1, 'abc', 100, true)">點擊</button>
    <!-- 控制檯輸出
        method1函數值:1,abc
        a值:傳入val值 
    -->
     通過上面,可以看到儘管定義函數時參數數量已確定,但依然可以往裏面傳入多個參數,JS不會對其進行檢查。看起來無參函數和有參函數似乎沒啥區別,反正參數可以隨便傳。但只有定義了形參,函數內部才能方便地使用定義好的形參。如果函數沒有定義形參,你傳入一堆參數給無參函數,函數內部無法直接拿到傳入的參數值。這時候就只能通過函數內部的arguments對象來獲取實參。

關於參數傳入數量的問題:
  • 如果傳遞的參數數量少於函數本身定義的形參數量時,之後的參數值都是undefined,因爲沒對其傳值,就和定義了變量卻沒賦值一樣。
  • 如果是在強類型語言中,這種問題是沒有的,所以要注意JS的特性。

 關於arguments對象 

  • arguments是每個函數內部都有的內部對象。函數所有接收的實參都由它來存儲管理。取值方式和數組差不多。
 <script>
        //返回所有傳入的參數
        function getArgs(){
            var all = "";
            for(var i = 0; i < arguments.length; i++){
                all += arguments[i] + " ";
            }
            return all;
        }
        function show(){
            var allArgs = getArgs("我", 150, new Date(), "ABC", 555);
            console.log(allArgs);
            // 我 150 Wed Sep 13 2017 21:19:53 GMT+0800 (中國標準時間) ABC 555 
        }       
 </script>
 <button onclick="show()">點擊</button>
      通過上面實例,可以看到arguments對象能拿到函數實參,無參函數通過arguments[0] 、argument[1]就能拿到傳入的第一個實參和第二個實參。但這種獲取實參方式不能亂用。如果規定了形參就使用形參,並且不要多傳無用參數。而無參函數就別傳入參數,一是無用,二是容易誤導,代碼寫規範點總是沒錯的。

 JS函數爲什麼沒重載 

  • 首先,先了解重載需要具備的條件:方法名相同,但只要形參類型不相同或者形參數量不相同,都會導致方法重載。至於有無返回值則和重載條件無關。這樣就會出現同名但方法不同。
  • 在JS中,函數參數數量沒法確定,而且參數類型可以是任意的,如果函數同名,後面的會覆蓋前面的。所以JS函數沒法實現重載。反過來想,如果JS函數可以重載,那就需要確定參數類型或者參數個數,那這樣JS的動態類型就沒實際意義了。但可以通過arguments得到參數長度來實現另類重載。

  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章