天天看點

JavaScript進階程式設計學習(四)之引用類型(續)

一、Date類型

其實引用類型和相關的操作方法,遠遠不止昨天的所說的那些,還有一部分今天繼續補充。

在java中日期Date,它所屬的包有sql包,也有util包。我個人比較喜歡用util包的。理由,順手習慣,個人也覺得比較好用。sql的Date類用的不算多。也用過。

下面就進入正題吧,不單單在java,在js中也存在日期函數,或換言之,日期對象。

var now = new Date();

通過now.getYear(),now.getMonth(),now.getMonth(),簡單的翻譯,可以了解為獲得目前的年月日。

now翻譯過來表示現在,之是以采取這樣的命名,還是那就話,規範起見。

js獲得的日期,通常情況要麼通過字元串拼接,要麼通過格式轉換,達到自己想要的那樣。

常用的格式轉換方法如下:

 toDateString()——以特定于實作的格式顯示星期幾、月、日和年;

 toTimeString()——以特定于實作的格式顯示時、分、秒和時區;

 toLocaleDateString()——以特定于地區的格式顯示星期幾、月、日和年;

 toLocaleTimeString()——以特定于實作的格式顯示時、分、秒;

 toUTCString()——以特定于實作的格式完整的 UTC日期。 與 toLocaleString()和 toString()方法一樣,以上這些字元串格式方法的輸出也是因浏覽器 而異的,是以沒有哪一個方法能夠用來在使用者界面中顯示一緻的日期資訊;

日期和時間元件方法如下(大家可以參考):

getTime() 傳回表示日期的毫秒數;與valueOf()方法傳回的值相同 setTime(毫秒) 以毫秒數設定日期,會改變整個日期

getFullYear() 取得4位數的年份(如2007而非僅07)

getUTCFullYear() 傳回UTC日期的4位數年份 setFullYear(年) 設定日期的年份。傳入的年份值必須是4位數字(如2007而非僅07)

setUTCFullYear(年) 設定UTC日期的年份。傳入的年份值必須是4位數字(如2007而非僅07)

getMonth() 傳回日期中的月份,其中0表示一月,11表示十二月 getUTCMonth() 傳回UTC日期中的月份,其中0表示一月,11表示十二月

setMonth(月) 設定日期的月份。傳入的月份值必須大于0,超過11則增加年份

setUTCMonth(月) 設定UTC日期的月份。傳入的月份值必須大于0,超過11則增加年份 getDate() 傳回日期月份中的天數(1到31)

getUTCDate() 傳回UTC日期月份中的天數(1到31)

setDate(日) 設定日期月份中的天數。如果傳入的值超過了該月中應有的天數,則增加月份

setUTCDate(日) 設定UTC日期月份中的天數。如果傳入的值超過了該月中應有的天數,則增加月份

getDay() 傳回日期中星期的星期幾(其中0表示星期日,6表示星期六) getUTCDay() 傳回UTC日期中星期的星期幾(其中0表示星期日,6表示星期六)

getHours() 傳回日期中的小時數(0到23)

getUTCHours() 傳回UTC日期中的小時數(0到23) setHours(時) 設定日期中的小時數。傳入的值超過了23則增加月份中的天數

setUTCHours(時) 設定UTC日期中的小時數。傳入的值超過了23則增加月份中的天數

getMinutes() 傳回日期中的分鐘數(0到59)

getUTCMinutes() 傳回UTC日期中的分鐘數(0到59)

setMinutes(分) 設定日期中的分鐘數。傳入的值超過59則增加小時數

setUTCMinutes(分) 設定UTC日期中的分鐘數。傳入的值超過59則增加小時數

getSeconds() 傳回日期中的秒數(0到59)

getUTCSeconds() 傳回UTC日期中的秒數(0到59)

setSeconds(秒) 設定日期中的秒數。傳入的值超過了59會增加分鐘數

setUTCSeconds(秒) 設定UTC日期中的秒數。傳入的值超過了59會增加分鐘數

getMilliseconds() 傳回日期中的毫秒數

getUTCMilliseconds() 傳回UTC日期中的毫秒數

setMilliseconds(毫秒) 設定日期中的毫秒數

二、 RegExp 類型 

RegExp,簡單的說,就是正規表達式,正規表達式,在開發中用的也很多,比如表單校驗,手機号碼,郵箱,網關,昵稱,qq号,身份證等,當然在java中也有相關的方法和工具類可以完成驗證,但是那樣的話,全部請求就會走向伺服器,那麼對于伺服器負載也是很大的。是以這也是js的一大亮點,減少伺服器壓力,同時也給使用者比較好的體驗。試問,如果走伺服器的話,在過去網絡這玩意,是個新鮮貨,人們那時填寫表單點選送出,基本都是跑伺服器,但是有的時候不小心點錯了或者大意疏忽了,好不容易填完十幾個表單輸入框,最後告知你,有幾個必填項或者幾個不符合要求的項,要求你重新填寫,在那個時候,人們不得不接受,但是在這個使用者量即市場的網際網路時代,可以說使用者就是上帝,如果使用者不滿意相當于自取滅亡。

正規表達式在此就可以發揮作用了,當使用者填寫不合法,直接提示相關文字資訊,當必填項沒填寫直接表單被紅框标記,現在的html5表單校驗就是基于正則和一些相關原生js方法,這個我曾經在我開發的部落格中用過。挺好用的。

當然,正規表達式的作用遠遠不止這麼點,它可以根據字元比對,這樣就有個小小的比對功能,也可以說搜尋功能,當然這個搜尋肯定是不能和百度那樣的公司比的。這個搜尋隻不過是字元比對。

(1)RegExp執行個體屬性 

 global:布爾值,表示是否設定了 g 标志。

 ignoreCase:布爾值,表示是否設定了 i 标志。

 lastIndex:整數,表示開始搜尋下一個比對項的字元位置,從 0算起。

 multiline:布爾值,表示是否設定了 m 标志。

 source:正規表達式的字元串表示,按照字面量形式而非傳入構造函數中的字元串模式傳回。 

(2)RegExp執行個體方法 

RegExp 對象的主要方法是 exec(),該方法是專門為捕獲組而設計的。exec()接受一個參數,即 要應用模式的字元串,然後傳回包含第一個比對項資訊的數組;或者在沒有比對項的情況下傳回 null。 傳回的數組雖然是 Array 的執行個體,但包含兩個額外的屬性:index 和 input。其中,index 表示比對 項在字元串中的位置,而 input 表示應用正規表達式的字元串。在數組中,第一項是與整個模式比對 的字元串,其他項是與模式中的捕獲組比對的字元串(如果模式中沒有捕獲組,則該數組隻包含一項)。 

var text = "mom and dad and baby"; 
var pattern = /mom( and dad( and baby)?)?/gi; 
 
var matches = pattern.exec(text); 
alert(matches.index);     // 0 
alert(matches.input);   // "mom and dad andbaby" 
alert(matches[0]);        // "mom and dad and baby" 
alert(matches[1]);        // " and dad and baby"
alert(matches[2]);        // " and baby"       
var text = "this has been a short summer";
 var pattern = /(.)hort/g; 
 
/*  * 注意:Opera 不支援 input、lastMatch、lastParen 和 multiline 屬性  * Internet Explorer 不支援 multiline 屬性  */      
   if (pattern.test(text)){  
alert(RegExp.input);            // this has been a short summer  
alert(RegExp.leftContext);      // this has been a                 
alert(RegExp.rightContext);     // summer     
alert(RegExp.lastMatch);        // short    
alert(RegExp.lastParen);        // s     
alert(RegExp.multiline);        // false 
}       

以上代碼建立了一個模式,比對任何一個字元後跟 hort,而且把第一個字元放在了一個捕獲組中。 RegExp 構造函數的各個屬性傳回了下列值:  input 屬性傳回了原始字元串;  leftContext 屬性傳回了單詞 short 之前的字元串,而 rightContext 屬性則傳回了 short 之後的字元串;  lastMatch 屬性傳回近一次與整個正規表達式比對的字元串,即 short;  lastParen 屬性傳回近一次比對的捕獲組,即例子中的 s。

(3)模式的局限性 

盡管 ECMAScript中的正規表達式功能還是比較完備的,但仍然缺少某些語言(特别是 Perl)所支 持的進階正規表達式特性。下面列出了 ECMAScript正規表達式不支援的特性。

 比對字元串開始和結尾的\A 和\Z 錨①

 向後查找(lookbehind)②

 并集和交集類

 原子組(atomic grouping)

 Unicode支援(單個字元除外,如\uFFFF)

 命名的捕獲組③

 s(single,單行)和 x(free-spacing,無間隔)比對模式

 條件比對

 正規表達式注釋 

三、Function

說起來 ECMAScript中什麼有意思,我想那莫過于函數了——而有意思的根源,則在于函數實際 上是對象。每個函數都是 Function 類型的執行個體,而且都與其他引用類型一樣具有屬性和方法。由于函 數是對象,是以函數名實際上也是一個指向函數對象的指針,不會與某個函數綁定。函數通常是使用函 數聲明文法定義的。

function sum (num1, num2) {     

return num1 + num2; 

}      

與下面這個并沒有什麼差異

var sum = function(num1, num2){   
  return num1 + num2; 
}; 
       

以上代碼定義了變量 sum 并将其初始化為一個函數。有讀者可能會注意到,function 關鍵字後面 沒有函數名。這是因為在使用函數表達式定義函數時,沒有必要使用函數名——通過變量 sum 即可以引 用函數。另外,還要注意函數末尾有一個分号,就像聲明其他變量時一樣。 

下面示例不推薦使用:

var sum = new Function("num1", "num2", "return num1 + num2"); // 不推薦       
function sum(num1, num2){  
   return num1 + num2; 
} 
alert(sum(10,10));        //20 
 
var anotherSum = sum; 
alert(anotherSum(10,10)); //20 
 
sum = null; 
alert(anotherSum(10,10)); //20       

以上代碼首先定義了一個名為 sum()的函數,用于求兩個值的和。然後,又聲明了變量 anotherSum, 并将其設定為與 sum 相等(将 sum 的值賦給 anotherSum)。注意,使用不帶圓括号的函數名是通路函 數指針,而非調用函數。此時,anotherSum 和 sum 就都指向了同一個函數,是以 anotherSum()也 可以被調用并傳回結果。即使将 sum 設定為 null,讓它與函數“斷絕關系”,但仍然可以正常調用 anotherSum()。

var addSomeNumber = function (num){  
   return num + 100; 
}; 
 
addSomeNumber = function (num) {   
  return num + 200; 
}; 
 
var result = addSomeNumber(100); //300       

通過觀察重寫之後的代碼,很容易看清楚到底是怎麼回事兒——在建立第二個函數時,實際上覆寫 了引用第一個函數的變量 addSomeNumber。 

(1)函數聲明和函數表達式

我們一直沒有對函數聲明和函數表達式加以差別。而實際上,解析器在向執行環 境中加載資料時,對函數聲明和函數表達式并非一視同仁。解析器會率先讀取函數聲明,并使其在執行 任何代碼之前可用(可以通路);至于函數表達式,則必須等到解析器執行到它所在的代碼行,才會真 正被解釋執行。

示例:

alert(sum(10,10));
 function sum(num1, num2){ 
    return num1 + num2; 
} 
       

因為在代碼開始執行之前,解析器就已經通過一個名為函數聲明提升 (function declaration hoisting)的過程,讀取并将函數聲明添加到執行環境中。對代碼求值時,JavaScript 引擎在第一遍會聲明函數并将它們放到源代碼樹的頂部。是以,即使聲明函數的代碼在調用它的代碼後 面,JavaScript 引擎也能把函數聲明提升到頂部。

如果像下面例子所示的,把上面的函數聲明改為等價 的函數表達式,就會在執行期間導緻錯誤。

alert(sum(10,10)); 
var sum = function(num1, num2){    
 return num1 + num2;
 };       

這個就會導緻常見js錯誤,函數未定義錯誤。

(2)作為值函數

因為 ECMAScript中的函數名本身就是變量,是以函數也可以作為值來使用。也就是說,不僅可以 像傳遞參數一樣把一個函數傳遞給另一個函數,而且可以将一個函數作為另一個函數的結果傳回。

function callSomeFunction(someFunction, someArgument){     

return someFunction(someArgument); 

} 
       

這個函數接受兩個參數。第一個參數應該是一個函數,第二個參數應該是要傳遞給該函數的一個值。 然後,就可以像下面的例子一樣傳遞函數了。

function add10(num){    
 return num + 10;
 } 
 
var result1 = callSomeFunction(add10, 10);
 alert(result1);   //20 
 
function getGreeting(name){  
   return "Hello, " + name; 
} 
 
var result2 = callSomeFunction(getGreeting, "Nicholas");
 alert(result2);   //"Hello, Nicholas"       

這裡的 callSomeFunction()函數是通用的,即無論第一個參數中傳遞進來的是什麼函數,它都 會傳回執行第一個參數後的結果。還記得吧,要通路函數的指針而不執行函數的話,必須去掉函數名後 面的那對圓括号。是以上面例子中傳遞給 callSomeFunction()的是 add10 和 getGreeting,而不 是執行它們之後的結果。

當然,可以從一個函數中傳回另一個函數,而且這也是極為有用的一種技術。例如,假設有一個 對象數組,我們想要根據某個對象屬性對數組進行排序。而傳遞給數組 sort()方法的比較函數要接收 兩個參數,即要比較的值。可是,我們需要一種方式來指明按照哪個屬性來排序。要解決這個問題, 可以定義一個函數,它接收一個屬性名,然後根據這個屬性名來建立一個比較函數,下面就是這個函 數的定義。

function createComparisonFunction(propertyName) { 
 
    return function(object1, object2){       
      var value1 = object1[propertyName];       
      var value2 = object2[propertyName]; 
 
      if (value1 < value2){        

        return -1;      
      } else if (value1 > value2){        
        return 1;       
      } else {     
        return 0;   
      }     
}; 
}       

這個函數定義看起來有點複雜,但實際上無非就是在一個函數中嵌套了另一個函數,而且内部函數 前面加了一個 return 操作符。在内部函數接收到 propertyName 參數後,它會使用方括号表示法來 取得給定屬性的值。取得了想要的屬性值之後,定義比較函數就非常簡單了。

var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}]; 
 
data.sort(createComparisonFunction("name")); alert(data[0].name);  //Nicholas 
 
data.sort(createComparisonFunction("age")); alert(data[0].name);  //Zachary         

這裡,我們建立了一個包含兩個對象的數組 data。其中,每個對象都包含一個 name 屬性和一個 age 屬性。在預設情況下,sort()方法會調用每個對象的 toString()方法以确定它們的次序;但得 到的結果往往并不符合人類的思維習慣。是以,我們調用 createComparisonFunction("name")方 法建立了一個比較函數,以便按照每個對象的 name 屬性值進行排序。而結果排在前面的第一項是 name 為"Nicholas",age 是 29的對象。然後,我們又使用了 createComparisonFunction("age")傳回 的比較函數,這次是按照對象的 age 屬性排序。得到的結果是 name 值為"Zachary",age 值是 28的 對象排在了第一位。 

(3)函數内部屬性 

在函數内部,有兩個特殊的對象:arguments 和 this。其中,arguments 在第 3章曾經介紹過, 它是一個類數組對象,包含着傳入函數中的所有參數。雖然 arguments 的主要用途是儲存函數參數, 但這個對象還有一個名叫 callee 的屬性,該屬性是一個指針,指向擁有這個 arguments 對象的函數。 請看下面這個非常經典的階乘函數:

function factorial(num){   
  if (num <=1) { 
        return 1;     
} else {       
  return num * factorial(num-1)  
} 
}        

IE、Firefox、Chrome和 Safari的所有版本以及 Opera 9.6都支援 caller 屬性。 當函數在嚴格模式下運作時,通路 arguments.callee 會導緻錯誤。ECMAScript 5 還定義了 arguments.caller 屬性,但在嚴格模式下通路它也會導緻錯誤,而在非嚴格模式下這個屬性始終是 undefined。定義這個屬性是為了厘清 arguments.caller 和函數的 caller 屬性。以上變化都是為 了加強這門語言的安全性,這樣第三方代碼就不能在相同的環境裡窺視其他代碼了。 嚴格模式還有一個限制:不能為函數的 caller 屬性指派,否則會導緻錯誤。 

(4)函數屬性和方法 

前面曾經提到過,ECMAScript 中的函數是對象,是以函數也有屬性和方法。每個函數都包含兩個 屬性:length 和 prototype。其中,length 屬性表示函數希望接收的命名參數的個數。

如下所示:

function sayName(name){     
alert(name); 
}       
 
function sum(num1, num2){  
   return num1 + num2; 
} 
 
function sayHi(){     
alert("hi"); 
} 
 
alert(sayName.length);      //1 
alert(sum.length);          //2 
alert(sayHi.length);        //0 
       

在 ECMAScript 核心所定義的全部屬性中,耐人尋味的就要數 prototype 屬性了。對于 ECMAScript 中的引用類型而言,prototype 是儲存它們所有執行個體方法的真正所在。換句話說,諸如 toString()和 valueOf()等方法實際上都儲存在 prototype 名下,隻不過是通過各自對象的執行個體訪 問罷了。在建立自定義引用類型以及實作繼承時,prototype 屬性的作用是極為重要的。在 ECMAScript 5中,prototype 屬性是不可枚舉的,是以使用 for-in 無法發現。 

每個函數都包含兩個非繼承而來的方法:apply()和 call()。這兩個方法的用途都是在特定的作 用域中調用函數,實際上等于設定函數體内 this 對象的值。首先,apply()方法接收兩個參數:一個 是在其中運作函數的作用域,另一個是參數數組。其中,第二個參數可以是 Array 的執行個體,也可以是 arguments 對象。

function sum(num1, num2){    
 return num1 + num2;
 } 
 
function callSum1(num1, num2){   
  return sum.apply(this, arguments);   
     // 傳入 arguments 對象 
} 
 
function callSum2(num1, num2){    
 return sum.apply(this, [num1, num2]);   
 // 傳入數組 
} 
 
alert(callSum1(10,10));   //20 
alert(callSum2(10,10));   //20       

注意:

在嚴格模式下,未指定環境對象而調用函數,則 this 值不會轉型為 window。 除非明确把函數添加到某個對象或者調用 apply()或 call(),否則 this 值将是 undefined。

四、基本包裝類型

說到包裝類型,在Java中不得不提基本資料類型的包裝類。

在此溫習下

Java基本資料類型及其對應的包裝類

int - Integer

byte - Byte

long - Long

short - Short

char - Char

float - Float

double - Double

boolean - Boolean

在js中包裝類型屬于引用類型,Java同樣如此。

ECMAScript提供了三個包裝類,String,Boolean,Number,分别對應的類型,字元類型,布爾類型,數字類型。

對于包裝類型不做太多講解,學過Java的人容易上手,沒有學過Java的人了解也不存在困難。

這裡講解是包裝類有什麼用,最直接直白的作用就是,類型轉換非常簡單。不需要什麼parseInt,toString,parseFloat等等進行調用轉換方法。直接var b = new String(“1”),即可将其轉為字元串類型。

其他也同理。

String,Boolean,Number我就在此不提太多了。

有一個要提Global,這個要提的原因主要是設計URL編碼和解碼問題,這個在實際應用中非常廣,比如郵件驗證,url加密。

編碼和解碼,可了解為加密和解密的過程,代碼如下:

<html>
<meta charset="utf-8">
<head>
<script>
var uri = "http://www.baidu.com/illegal?userId=1"; 
 

alert(encodeURI(uri)); 
 

alert(encodeURIComponent(uri)); 
</script>
</head>
<body>
</body>
</html>      

使用 encodeURI()編碼後的結果是除了空格之外的其他字元都原封不動,隻有空格被替換成了 %20。而 encodeURIComponent()方法則會使用對應的編碼替換所有非字母數字字元。這也正是可以 對整個URI使用encodeURI(),而隻能對附加在現有URI後面的字元串使用encodeURIComponent() 的原因所在。 

與 encodeURI()和 encodeURIComponent()方法對應的兩個方法分别是 decodeURI()和 decodeURIComponent()。其中,decodeURI()隻能對使用 encodeURI()替換的字元進行解碼。例如, 它可将%20 替換成一個空格,但不會對%23 作任何處理,因為%23 表示井字号(#),而井字号不是使用 encodeURI()替換的。同樣地,decodeURIComponent()能夠解碼使用 encodeURIComponent()編碼,的所有字元,即它可以解碼任何特殊字元的編碼。

示例如下所示:

<html>
<meta charset="utf-8">
<head>
<script>
var uri = "http%3A%2F%2Fwww.baidu.com%2Fillegal%20value.htm%23start"; 
 

alert(decodeURI(uri)); 

alert(decodeURIComponent(uri)); 
 
</script>
</head>
<body>
</body>
</html>      

這裡,變量 uri 包含着一個由 encodeURIComponent()編碼的字元串。在第一次調用 decodeURI() 輸出的結果中,隻有%20 被替換成了空格。而在第二次調用 decodeURIComponent()輸出的結果中, 所有特殊字元的編碼都被替換成了原來的字元,得到了一個未經轉義的字元串(但這個字元串并不是一 個有效的 URI)。

URI方法 encodeURI()、encodeURIComponent()、decodeURI()和 decode- URIComponent()用于替代已經被ECMA-262第3版廢棄的escape()和unescape() 方法。URI方法能夠編碼所有 Unicode字元,而原來的方法隻能正确地編碼 ASCII字元。 是以在開發實踐中,特别是在産品級的代碼中,一定要使用URI方法,不要使用 escape() 和unescape()方法。

eval這個方法我常用,主要是将json格式資料解析出來。

另外再提一個Math對象,該對象如其名"數學",主要涉及數學相關的

JavaScript進階程式設計學習(四)之引用類型(續)

同時還有min和max方法,最小值和最大值

直接var num = Math.max(3,2,1,0)

var num2 = Math.min(1,2,3,4)

就可以得出最大值和最小值。

加入購物車進行計算涉及該方法

 Math.ceil()執行向上舍入,即它總是将數值向上舍入為接近的整數;

 Math.floor()執行向下舍入,即它總是将數值向下舍入為接近的整數;

對象在 JavaScript 中被稱為引用類型的值,而且有一些内置的引用類型可以用來建立特定的對象, 現簡要總結如下:

 引用類型與傳統面向對象程式設計中的類相似,但實作不同;

 Object 是一個基礎類型,其他所有類型都從 Object 繼承了基本的行為;

 Array 類型是一組值的有序清單,同時還提供了操作和轉換這些值的功能;

 Date 類型提供了有關日期和時間的資訊,包括目前日期和時間以及相關的計算功能;

 RegExp 類型是 ECMAScript支援正規表達式的一個接口,提供了基本的和一些進階的正則表 達式功能。 函數實際上是 Function 類型的執行個體,是以函數也是對象;而這一點正是 JavaScript有特色的地 方。由于函數是對象,是以函數也擁有方法,可以用來增強其行為。 因為有了基本包裝類型,是以 JavaScript 中的基本類型值可以被當作對象來通路。三種基本包裝類 型分别是:Boolean、Number 和 String。以下是它們共同的特征:

 每個包裝類型都映射到同名的基本類型;

 在讀取模式下通路基本類型值時,就會建立對應的基本包裝類型的一個對象,進而友善了資料 操作;

 操作基本類型值的語句一經執行完畢,就會立即銷毀新建立的包裝對象。 在所有代碼執行之前,作用域中就已經存在兩個内置對象:Global 和 Math。在大多數ECMAScript 實作中都不能直接通路 Global 對象;不過,Web 浏覽器實作了承擔該角色的 window 對象。全局變 量和函數都是 Globa

 Math.round()執行标準舍入,即它總是将數值四舍五入為接近的整數(這也是我們在數學課 上學到的舍入規則)。 

繼續昨天沒總結完了,該篇是昨天和今天的内容補充。