JavaScript對象與繼承教程之內置對象(上)

作者: cj205  發布時間: 2011-02-10 16:52  閱讀: 1267 次  推薦: 1   原文鏈接   [收藏]  

  一、 類與對象

  在 JavaScript 世界里,關于面向對象第一個要澄清的概念就是類。對象都是有類來定義的,通過類來創建對象就是我們所熟悉的實例化。然而,在 JavaScript 中別沒有真正的類,對象的定義就是對象自身。而 ECMA-262 干脆把這種妥協的方式稱作為對象的調和劑。為了方便理解,我通常把這個發揮類的作用的調和劑稱為類。

  二、 內置對象

  1、 Array類

  數組在 js 中是非常常用的一種數據結構,由于其靈活性和易用性,合理的使用數組可以幫助我們更好的實現相應的功能。

  讓我們先看 Array 對象的創建:

  第一種:var arr = new Array(10);

  該方法在實際的使用當中并不那么的實用,與很多編譯型語言不同, js 數組的長度是可變的,不但增強了靈活性,還給我們有了更多好的選擇。

  第二種:var arr = new Array("one","two","three");

  使用 new 方式創建數組的方法一般多為這兩者,當然也可以使用 new Array() 創建一個空的數組對象。通常情況下,我推薦如下的方法

  第三種:var arr = ["one","two","three"];

  使用數組的字面量方式創建一個數組對象,不但簡潔易讀,而且幾乎完全等價于使用 new 方式創建數組對象的效果。數組對象有很多好用的方法,接下來我們就一起看看這個數組對象的強大功能吧。

  首先要介紹的是 push 方法,學過數據結構的朋友都知道 push 意味著什么,沒錯,他的出現讓數組能夠實現棧的數據結構(同時需要配合 pop 方法)。 push 方法幫助我們緊湊的添加數組元素。前面提到js中的數組是長度是可變的,則我們可以添加元素。既然可以通過 arr[length] = newValue; 來給 arr 添加一個新元素并放置于數組尾。更好的辦法就是使用 push 方法。 arr.push(newValue); 怎么樣,使用他比你還要通過數組長度來賦新值方便多了吧。在這里有一點需要注意。請看下面的代碼:

 
var arr = [];
arr[
4] = 5;
alert(arr.length
== 5);
alert(arr);
//alert : ,,,,5

  當我們需要給指定數組位置賦予指定的值的時候,這種賦值就顯得十分有用了,比如在用于裝箱排序的時候。

  pop 方法則是實現與 push 相反的作用,返回數組的最后一個元素,并出棧。

 
var arr = [1,2,3,4,5];
var ele = arr.pop();
alert(ele
== 5);
alert(arr.length
== 4);

  數組對象的 toString 和 valueOf 方法比較人性化的重寫了,它的實現是把每一項都調用 toString 方法,然后用半角逗號(,)連接每一項。那么:

 
var arr = [1,2,3,4,5];
alert(arr);
//output:1,2,3,4,5

  toLocaleString 方法在這里不做詳細說明了,他的效果與 toString 方法類似,只是每項調用 toLocateString 方法。

  如果你想使用個性化的分隔符來顯示數組元素,那么 join 方法可能會更加的適合。比如:

 
var city = ["上海","北京","天津","重慶","深圳"];
alert(city.join(
"|"));//output:上海|北京|天津|重慶|深圳

  由此可見 join 是把數組元素轉換為一個字符串。在介紹字符串的時候我們將再次看到 join 方法的使用。

  concat 方法和 slice 方法是又一對好用的方法,這兩個方法的特殊之處在于 String 對象也擁有他們。當我們希望給一個數組添加多個數組元素的時候,使用 push 可能就顯得有些冗余和復雜了,而且也會讓 coding 變得不那么有意思了。好在我們有 concat 方法,該方法將其參數們按序加入數組元素中。如:

 
var arr = [1,2,3,4,5];
arr
= arr.concat(6,7,8,9);
alert(arr.length
== 9);

  注意, concat 并不修改數組對象本身,而是將數組元素與 concat 方法的數組元素合并后返回。所以需要給數組元素進行賦值運算才行。

  slice 方法則是從數組對象中返回一個子數組。該子數組是從 slice 方法的第一個參數所指位置至第二個參數所指的位置。這是一個半開半閉區間 [a,b) 。如:

 
var arr = [1,2,3,4,5];
var arr1 = arr.slice(1,3);
alert(arr1);
//output:2,3
alert(arr); //output:1,2,3,4,5

  好了, slice 和 concat 方法一樣不是修改數組對象本身。同時參數1,3表示從位置1到位置3的半開半閉區間的子數組。

  剛才討論了后進先出的棧操作,現在我們來看看先進先出的隊列操作吧。進列使用 push 方法沒有問題,那么出列呢。是 shift ,他刪除數組對象的第一個元素并返回:

 
var arr = [1,2,3,4,5];
var ele = arr.shift();
alert(ele);
//output:1
alert(arr.length);//output:4

  另外一個還有一個方法,叫 unshift ,他將新元素插入數組對象的第一項,究其功能與 shift 是相反的操作。

  sort 方法很靈活,使用好了,他可以給數組元素以任意你想要的排序方式來進行排序。因為 sort 方法接收一個匿名函數(其實,它同樣可以接收一個非匿名的函數,但是通常不推薦為此而創建一個這樣的命名函數,除非該函數可重用)作為自己的排序的條件。比如:

 
Object.prototype.toString = function(){
var str = '';
for(var item in this) {
str
+= item + ":" + this[item] + ",";
}

return str.length?str.substr(0,str.length-1):str;
};

var arr = [{key:3,value:"three"},{key:1,value:"one"},{key:2,value:"two"}];
arr.sort(
function(a,b){
return a.key - b.key;
});
alert(arr);
//output:key:1,value:one,key:2,value:two,key:3,value:three

  我們先不去糾結 Object.prototype.toString 方法,他的左右就是將對象遍歷使之可以輸出為鍵值對格式字符串,在介紹原型鏈的時候會再次提到。我們可以看到 sort 方法通過這個匿名方法讓我們可以根據 key 屬性來進行排序。那就讓我們來看看這個匿名方法吧。

 
function(a,b) {
return a.key - b.key;
};

  可以看到,這個方法接收2個參數,然后對參數的自身或某個屬性進行比較,然后返回比較結果,他們的比較結果與排序對應關系如下:

  如果 paramA - paramB > 0,return 正數 ,則 b 排在 a 的前面

  如果 paramA - paramB < 0,return 負數 ,則 b 排在 a 的后面

  如果 paramA - paramB = 0,return 0 ,則順序不變。

  上面的實現是順序排序,那么 倒序 呢?對, return paramB - paramA;

  reverse 方法可以將數組對象反轉。他和 sort 方法一樣是修改數組對象內部元素順序的。

  最后我們看看 splice 方法,他是替換和刪除數組對象元素的方法。根據參數的改變而擁有不同的實現結果。 splice(pos,count[,insertParams]);pos 參數是刪除元素的第一個項的位置, count 參數是刪除元素的個數,當為0時則不刪除(不刪除還要這個方法干嘛,別著急,往下看), insertParams 則是參數列表,這些參數是即將插入的元素集合。插入的位置為 pos 。那么就出現了以下幾種情況了。

  1、 insertParams 忽略時,該方法就是刪除數組元素

  2、 當 count 參數為0時,該方法將只是將 insertParams 插入到 pos 位置

  3、 當 count 參數不為0且 insertParams 不忽略時,該方法就是刪除 pos 位置開始的 count 個元素,并替換 insertParams 參數集合。

  2、 Math類

  我們花了很大的篇幅來介紹數組類,我要再次強調一點,這個類只是為了介紹方便強加于它的一個名字,實際上他們也只是對象。而非真正的類。

  Math 類的使用范圍相對狹窄,因為他作為一個數學計算的類,而非一個數據結構類,但是我們也看到了 Math.random 以及各種取整等常用方法。因此我們不妨花些時間來看看他們,但是如果你對此興趣不大,那么看完 random 方法之后就可以跳到下一節去,以后用到的時候再翻手冊就可以了。

  Math 通常是一個“靜態”類,因為沒有人會實例化一個 Math 對象,而是直接使用其“靜態”方法,有些資料直接稱它為 Math 對象,在這里我們不妨稱它為“靜態”類吧。

  首先我必須介紹 random 方法,因為他常用且太有用了。在制造隨機事件的時候他總是不可或缺,同樣在防止緩存上他也顯得很有用處。 Math.random 方法返回的是一個 0到1 之間的開區間浮點數,即 (0,1) ,他的使用非常簡單,唯一需要注意的是,當我們取整的時候對 floor 和 ceil 方法的篩選時需要謹慎,前者使得 random 間接轉換為前閉后開區間,而后者則是前開后閉區間。假如我們現在需要一個取1-100的隨機數,那么有如下的兩種常用解決方案

  方法一:Math.ceil(Math.random*100);

  方法二:Math.floor(Math.random*100)+1;

  ceil 方法和 floor 方法都是用來取整的數學方法,根據單詞含義我們可以理解,前者是向上取整,而后者則是向下取整。

  當我們從一個連續的數組對象中隨機選擇一個數組元素時,我們可以借助 random 輕松的來幫我們挑選:

  ["ipad","iphone","ipod touch","ipod nano","macbook"][Math.ceil(Math.random()*4)];

  這個例子中,我直接使用的是數組字面量,一開始你可能會覺得費解或者不易理解,當你深入以后你會發現原來 JavaScript 是如此的方便、簡潔、靈活。

  前面我們介紹了 ceil 和 floor 方法的取整,那么當我們想要接近舍入時呢,我們可以使用 Math.round 方法,他在取整時根據數值進行靠近取整。比如 Math.round(5.4) 返回的是 5 。那么如果 Math.round(5.5) 呢,答案是 6 而不是 5 。關于這點需要有所了解。好吧我承認我較真了,但是知道了他有什么壞處呢。

  當我們想從 2 個數中取得較小或者較大的數的時候怎么做呢?

 
if(a>b) {
return a;
}
else {
return b;
}

  我們多慮了。 Math 提供了 Math.max 和 Math.min 方法來幫助我們解決這個問題。

  Math 還有一大堆的“靜態”方法和屬性。在這里我就不一一列舉了,當我們要進行數學計算的時候不妨去查查手冊。

  3、 String類

  字符串對象的使用頻率恐怕比數組對象有過之而無不及,為什么我要放到后面來說呢,其實是因為對于字符串,我們要說的更多,而可擴展的方法和工具函數也更加豐富。我們一起先來看看 String 類本身吧。

  創建一個字符串對象有以下幾種方法:

  方法一:var str = new String("Hello World");

  方法二:var str = String("Hello World");

  方法三:var str = "Hello World";

  和數組對象一樣,我推薦大家使用最后一種方法,及字符串字面量。關于是否有 new 的區別,周愛民老師的博客中有過很詳細的解釋,同時,如果你一直讀下去,在介紹自定義對象的時候也會提到 new 運算符。

  String 對象有且只有一個屬性 length ,他返回字符串的長度,在這里我們必須要弄明白 JavaScript 是 unicode 編碼,那么漢字和英文都當作一個字符長度來處理,之所以提到這個是因為曾經遇到不止一位朋友在論壇提問這個問題,呵呵,動手的必要性啊。那么如果我們非要把漢字當作 2 個字符長度來計算呢?這就帶來了我們第一個自定義方法。

 
String.prototype.getLength = function(isSplit){
if(isSplit) {
return this.replace(/[^\u0000-\u00FF]/g,"tt").length;
}

else {
return this.length;
}
};

  該方法通過傳遞一個 Boolean 類型的參數,如果為真則將非半角字符、數字、英文字母分割為 2 個長度來求長度,不用擔心這個分割,他并不會修改字符串對象本身。如果為假,則直接返回 length 屬性值。由于介紹方法不是根據方法的字符排列順序而來,如果作為字典,我想還是 w3school 更合適,因為我是根據作用的不同和關聯性來進行介紹。 ok ,介紹完了 length 屬性,我們看看字符串查找吧。

  indexOf 和 lastIndexOf 方法。

  這兩個方法從是從字符串中查找一個字符或字符子串,區別在于查找方向,前者是從位置 0 處開始查找,并返回第一個查找到的位置,后者從位置 length-1 處開始查找,并返回第一個查找到的位置。如果查找不到呢,返回 -1 。例:

 
var str = "了解面向對象編程和基于對象編程是一個基礎理論";
alert(str.indexOf(
"對象")); //output:4
alert(str.lastIndexOf("對象"));//output:11
alert(str.indexOf("過程"));//output:-1

  從輸出的結果我可以得到以下結論:

  1、 字符位置是從 0 開始索引

  2、 即使是從后往前查找,返回位置時也還是位置 0 開始計算

  3、 當在字符串中索引不到該子串時,返回 -1 值。

  charAt 和 charCodeAt 方法根據一個位置索引來返回字符,其中前者是返回字符本身,后者返回字符編碼。我們簡單的看個例子后結束他們:

 
var str = "了解面向對象編程和基于對象編程是一個基礎理論";
alert(str.charAt(
5)); //output:象
alert(str.charCodeAt(5));//output:35937

  接下來輪到 slice , substr 和 substring 方法,說實話很多熟悉 JavaScript 的程序員也經常會混淆兩者的用法,并非是我夸張,而是 substring 和很多后臺語言的 substring 方法區別很大的哦。先看看 slice 方法。

  slice(start[,end]) 方法需要提供至少一個整數參數,作用是返回從 start 的位置開始到 end 位置的字符子串。接下來幾句話請仔細看清楚了,以防造成曲解,當參數 start 為負數的時候他將從字符串尾部開始計算,當 end 沒有指定時, end 即為字符串的結尾。如果為負數呢,他也要從字符串尾部開始計算。所以當我們需要一個字符串的之后 3 個字符時只需 slice(-3); 由此可見,合理的使用負數讓我們的程序變得簡單。但是在此之前,請確保自己了解了他的作用。

  據我所知的編程語言中,有很大一部分的 substring 方法設計為 substring(beginposition,length) ,而在 JavaScript 中正好也有這么一個方法,可惜真正與之對應的是 substr 方法。 substr(pos[,length]) 方法中,如果 pos 為負數,則與 slice 的負數解釋相同, length 省略時與 slice 的 end 省略也相同。

  到了 substring 方法, substring(from[,to]); 從定義上就可以看到,后一個參數是一個位置,而非長度,因此他更像 slice ,但是與之有一點重要的區別,那就是 substring 方法不包含 to 位置。即是一個半開半閉區間。另一個區別是 substring 不支持負向位置,如果第一個參數為負數,那么就是從位置 0 開始。后一個位置如果是負數,則返回空串,如果第二個參數小于第一個參數,那么同樣返回空串,但是如果相等呢,還是空串,因為這是一個半開半閉區間 [from,to) 。

  另外幾個查找的方法 :match 、 search 將在后面介紹正則表達式和 RegExp 類的時候詳細介紹。替換方法 replace 也將在介紹正則表達式時介紹。另外一對有用的方法是 toLowerCase 和 toUpperCase 。他們就是將字符串進行大小寫轉換的。

  字符串操作的另一個領悟的連接字符串。字符串對象是長度不可變的,仔細回顧下之前所有的方法中沒有一個是修改對象本身的方法。關于這點將于稍后一個思考題單獨會展開來介紹。現在你要做的就是知道這點。字符串對象的連接方法常見的三種,第一種是使用 concat 方法,在介紹 Array 類的時候我們也見過這個方法,他們其實是一樣的東西,連接字符串為一個新串,另外字符串對象重載了 + 號運算符,用來簡化連接操作,因此 "abc"+"de" == "abcde"; 還有一個方法是借助 Array 對象中的 push 和 join 方法連接字符串。

 
var arr = [];
for(var i = 0; i < 10; i++) {
arr.push(
"<img alt=\"test\" src=\"pic" + i + ".jpg\" />");
}
arr
= arr.join("");

  這種方法很類似與 C# 中的 StringBuilder 的 append 和 ToString 方法,而好處也很類似。不要忘了在 join 的方法中加上參數""哦,否則的話,多出的逗號可能就有點事與愿違了。為了方便操作,我們通常還會擴展一個方法來模擬 printf(in c) 和 format(in c#):

 
function formatString() {
var args = arguments;
if(args.length > 1) {
return args[0].replace(new RegExp("\{([0-" + (args.length-2).toString() + "])\}","g"),function(s,m){
return args[+m+1];
});
}
}

  首先我要說明,這個方法與原來的方法相比丑陋了許多,但是更容易理解,其次,這個方法有自身的 bug 存在,即當參數過多(>10)時,正則表達式將不能正確運轉。因此我們換個解決方案來解決這個 bug (感謝zswang 的仔細,下面的解決方案也是仿照zswang的。

 
function formatString(str,params) {
return str.replace(/\{(\d+)\}/g,function(s,m){
return params[+m];
});
}
alert(formatString(
"{0} is {1} old",['JeeChang',25]));
JavaScript 中沒有一個 trim 方法,讓我們很是苦惱,沒有例外,我們自己寫一個 trim 方法吧
String.prototype.trim
= function(which){
var pattern = {
"left":"^\\s*",
"right":"\\s*$",
"both":"^\\s*|\\s*$"
}
return this.replace(new RegExp(pattern[which],'g'),"");
};

  字符串的操作一直都不僅限于此,比如字符串截取往往還有這樣的需求,即我們在瀏覽器顯示的時候往往需要根據全半角來截斷字符串,但是最后一個字符是全角的話則需要全部截斷。看上去一定很復雜吧。沒關系我們來添加這個方法。

 
var i = 0; i < signs.length; i++) {
if(str.indexOf(signs[i]) < 0) {
sign
= signs[i];
break;
}
}
str
= str.replace(/[^\u0000-\u00FF]/g,sign + sign);
var ig = count;
for(var i = 0; i < count; i++) {
if(str[i] == sign)
ig
-=0.5;
}

return this.substr(0,Math.floor(ig));
};

  這個方法很可惜,替換字符不夠全面,如果恰巧這些字符都存在于其中,那么結果就很可悲了。所以這個方法不是那么的可靠。其實可以通過初步判斷總字符長度來截取掉后面的字符串然后再查找是否有替換字符。這樣概率就相對小些。

  介紹完了數組對象和字符串對象之后,我們看下面一個例子:

 
var a = "abc";
var b = "abc";
alert(a.valueOf()
=== b.valueOf);
alert(a.toString()
=== b.toString());
alert(a.valueOf()
== b.valueOf());
var arr1 = ['a','b','c'];
var arr2 = ['a','b','c'];
alert(arr1.toString()
=== arr2.toString());
alert(arr1.valueOf()
=== arr2.valueOf());

  請問輸出結果是什么呢?

  4、 Date類

  日期類恐怕是我見過最無奈的JavaScript內置類(對象)。因為他的瀏覽器不同表現,時間日期的操作處理都顯得那么的不友好。幸好,JavaScript固有的機制可以讓我們自己來解決這些問題。不過在此之前我們還是先大致了解下他的一些方法和屬性。

  創建一個日期對象可以有這些方法

  方法一:var d = new Date(ms);//ms代表從1970.1.1凌晨0點的毫秒數

  方法二:var d = new Date(year,month[,day,hour,minute,second,millisecond]);

  方法三:var d = new Date("localDateString");//這里不是那么的通用。2011/5/5格式相對通用

  如果我們需要創建一個當前時間的日期對象。直接new Date()用無參數的構造函數即可。當然我們不能忽略這個new,前面提到String和Array可以省略,然而這里千萬不能這樣做。因為Date()的結果是瀏覽器實現的一個日期對象的toString返回的表示日期的字符串。故此,這里兩者不能混用。

  日期對象的方法大致分為獲取和設置日期時間的某一(幾)個部分。獲取方法相對好用一些,然后設置方法則顯得不那么夠用。這里有幾個需要拿出來先說說。

  getDate/setDate。該方法操作的是天數,而非日期值。這個還是有點不大直觀的。

  getDay/setDay。該方法操作的是周數,序數從0開始,即周日的值是0。

  getMonth/setMonth。該方法操作的是月數沒有疑問,但是月數是從0開始。

  getFullYear/setFullYear。我通常建議用這組方法來代替直觀的getYear/setYear。

  toDateString/toTimeString==輸出日期的方法。但是并不是那么的好用。

  接下來,讓我們一步一步來打造更適合自己的一套方法。

  首先,我們來格式化輸出:

 
var conf = {
syslang :
"cn" //設置系統語言
};
Date.prototype.toFormatString
= function(format) {
var weeks = {};
weeks[
'cn'] = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
weeks[
'en'] = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
var self = this;
var fix = {
'yyyy':self.getFullYear(),
'MM':self.getMonth()+1,
'dd':self.getDate(),
'wk':weeks[conf.syslang][self.getDay()],
'hh':self.getHours(),
'min':self.getMinutes(),
'ss':self.getSeconds()
};

return format.replace(/[a-zA-Z]+/g,function(m){
return fix[m];
});
};

  嗯,這個方法多了個全局的配置對象,但是這個不是必須的,只是在這里提示大家如果實現個性化定制,但是如果是自己的項目使用,我更建議減少代碼(把en或cn去掉)來打造適合自己項目的精簡代碼。該方法的使用一目了然,在這里也不多解釋了。

  接下來是日期的操作,第一組是日期的加減。熟悉.net的朋友都知道AddXXX的一組方法,因此我們也可以打造一組這樣的代碼,在此我只列舉一個,有需要的可以自己實現其他的。其實這套方法可以使用偽泛型的方式將Add方法組并到一個方法。但是我更愿意用一目了然的方法名來提供。

 
Date.prototype.addMonth = function(n){
var month = this.getMonth();
this.setMonth(month+n);
};

  怎么樣,很簡單吧。不用擔心溢出(13月)或者負月份(-1月)會造成什么不良后果,日期對象會自己為了通過調整年數來得到合適的結果。

  接下來是日期比較。純粹的日期比較不是問題,因為getTime獲取毫秒數之后進行加減操作即可。然而如果要是比較相差的天數怎么辦呢。其實也簡單。那就是相差的毫秒數換算到天數即可

 
Date.prototype.compareTime = function(time) {
var ticks = time.getTime() - this.getTime();
return Math.floor(ticks/(1000*60*60*24));
}

  至于比較周數同理,但是月數和年數呢?對不起,考慮到閏年和大小月等問題,這個方法比較復雜,在這里就不貼出來了,如果您有興趣不妨嘗試著自己寫寫看。

  第一部分就到這里,下一篇將是正則表達式類和全局對象方法。而自定義對象將在這些之后。預計這個禮拜會完成這幾篇。

  本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/cj205/archive/2011/01/23/6159709.aspx

1
0
 
標簽:JavaScript
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()