Function類型
ECMAScript中的函數實際上也是對象。每個函數都是Function類型的實例,並且與其它引用類型一樣也具有屬性和方法。所以,函數名稱實際上也是一個指向函數對象的指針。
這也就不難理解函數的另一種定義方法,實際就是在聲明一個變量:
var sum = function(num1, num2) {
return num1 + num2;
}
實際上,函數也能使用構造方法來定義,不過這種方法是不推薦使用的:
// 最後一個參數會被當成函數體
var sum = new Function("num1", "num2", "return num1 + num2");
沒有重載(深入理解)
將函數名想象為指針,也就可以理解為什麼ECMAScript當中沒有函數重載的概念。
function addSomeNumber(num) { return num + 100; }
function addSomeNumber(num) { return num + 200; }
var result = addSomeNumber(100); //
// --> 等價於 <--
var addSomeNumber = function(num) { return num + 100; }
addSomeNumber = function(num) { return num + 200; }
var result = addSomeNumber(100); // 300
可以看到,當聲明兩個同名函數時,實際上是第一個函數的變量被後面的所覆蓋了,也就無法構成重載。
函數聲明與函數表達式
在解析器向執行環境加載數據時,對待函數聲明和函數表達式是有區別的。解析器會率先讀取函數聲明,並使其可以在執行任何代碼之前可用,而對於函數表達式,則必須等到解析器執行到它所在的代碼行,才會真正被執行。
// 運行正確
alert(sum(10,10));
function sum(num1, num2){
return num1 + num2;
}
// 運行出錯
alert(sum(10,10));
var sum = function(num1, num2){
return num1 + num2;
};
由於第一段代碼使用了函數聲明,所以解析器會在運行代碼之前將其添加到執行環境中,所以可以正確運行。而第二段代碼因為是函數表達式,所以在執行第一句的時候,實際上sum函數還未被定義,導致運行出錯。
作為值的函數
因為ECMAScript當中的函數名本身就是變量,所以函數也可以被當成參數來使用,也可以作為返回值從一個函數中返回。
函數內部屬性
在函數的內部,有兩個特殊的屬性:arguments和this,這兩個屬性本身也都是引用類型。
arguments除了保存函數參數的作用,還有一個callee的屬性,該屬性是一個指針,指向擁有這個arguments對象的函數。
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * factorial(num-1)
}
}
// <-- 等價於 -->
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * arguments.callee(num-1)
}
}
this引用的是執行該函數的環境對象。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //"red"
o.sayColor = sayColor;
o.sayColor(); //"blue"
當在全局作用域中調用函數時,this引用的是全局對象window。而使用對象調用時,則this指向的是該對象本身。
關於this以後還會進行詳細的討論。
ECMAScript 5中還定義了另一個函數屬性的對象:caller,這個屬性保存了調用當前函數的函數的引用,如果是在全局使用域內,則它的值為null。
函數屬性和方法
因為函數也是對象,所以函數也有屬性和方法。每個函數都有兩個屬性: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
prototype是ECMAScript中面向對象一個十分重要的屬性,關於這個屬性之後會更加詳細地討論。
除了上面兩個屬性,每個函數還有兩個非繼承而來的方法: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
call()方法與apply()方法類似,區別只在於第二個參數不同,對於call()方法,所以參數都是直接指定,而不是傳遞數組。
apply()和call()方法真正強大的地方在於他們能夠擴充函數運行的作用域。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue
ECMAScript 5還定義了一個bind()方法,這個方法會創建一個函數的實例,其this值會被綁定到傳給bind()函數的值。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue
而對於toString()、toLocaleString()和valueOf()方法,都會返回函數的代碼。
基本包裝類型
我們之前說過,只有引用類型的數據才能添加屬性,但是我們卻經常對字符串調用各種方法。這是怎麼回事呢?實際上,每當我們讀取一個基本類型值的時候,後台就會創建一個對應的基本包裝類型的對象,從而我們能夠調用一些方法來操作這些數據。
基本包裝類型只存在於一行代碼的執行瞬間,然後立即被銷毀。這也就解釋了為什麼我們不能為基本類型添加屬性和方法,因為對基本包裝類型添加的屬性,在下一行代碼執行的時候就被銷毀了。
我們應該避免顯式地創建基本包裝類型對象。
Boolean類型
Boolean類型是與布爾值對應的引用類型。Boolean類型的實例重寫了valueOf()方法,返回基本類型值的true或false。重寫了toString()方法,返回字符串true和false。
Number類型
Number是與數字值對應的引用類型。Number的valueOf()方法也會返回基本類型的數值,而toString()和toLocaleString()方法會返回數值對應的字符串。
Number類型還提供了一些用於數值格式化為字符串的方法。
toFixed()方法會按照指定的小數返回數值的字符串表示:
var num = 10;
alert(num.toFixed(2)); //"10.00"
toExponential()方法會返回以指數表示法表示的數值的字符串形式。
var num = 10;
alert(num.toExponential(1)); //"1.0e+1"
對於一個數值來說,toPrecision()方法可能會返回固定大小(fixed)格式,也可能返回指數格式(exponential),具體看哪種格式最合適。
var num = 99;
alert(num.toPrecision(1)); //"1e+2"
alert(num.toPrecision(2)); //"99"
alert(num.toPrecision(3)); //"99.0"
String類型
String類型是字符串的對象包裝類型。對於String類型的對象,三個繼承的方法都直接返回對象所表示的基本字符串值。String類型每個實例都有一個length屬性,表示字符串中的字符個數。要注意的是,即使字符串中包含雙字節字符,每個字符也仍然算一個字符。
String類型提供了很多方法,以提供字符串的操作和解析。
字符方法
charAt()和charCodeAt()用於訪問字符串中的特定字符。這兩個方法都接收一個參數,即基於0的字符位置。charAt()以字符形式返回給定位置的字符,而charCodeAt()會以字符編碼的形式返回。
var stringValue = "hello world";
alert(stringValue.charAt(1)); //"e"
alert(stringValue.charCodeAt(1)); // 輸出"101"
在ECMAScript 5當中,還可以使用方括號加數字索引來訪問字符串,與數組的取值方法類似。
字符串操作方法
concat()方法用於將一或多個字符串拼接起來,返回拼接得到的新字符串。
var stringValue = "hello ";
var result = stringValue.concat("world", "!");
alert(result); //"hello world!"
alert(stringValue); //"hello"
實際上,在大多數情況下,字符串拼接使用更多的還是加號操作符(+)。
ECMAScript還提供了三個用於獲取子字符串的方法:slice()、substr()和substring()。這三個方法可以接受一個或兩個參數,第一個參數為指定開始位置,對於slice()和substring(),第二個參數是指定子字符串的結束位置,而substr()的第二個參數則是指定字符的個數。
var stringValue = "hello world";
alert(stringValue.slice(3)); //"lo world"
alert(stringValue.substring(3)); //"lo world"
alert(stringValue.substr(3)); //"lo world"
alert(stringValue.slice(3, 7)); //"lo w"
alert(stringValue.substring(3,7)); //"lo w"
alert(stringValue.substr(3, 7)); //"lo worl"
在傳遞參數為負值的情況下,slice() 方法會將傳入的負值與字符串的長度相加, substr() 方法將負的第一個參數加上字符串的長度,而將負的第二個參數轉換為 0。最後, substring() 方法會把所有負值參數都轉換為 0。
var stringValue = "hello world";
alert(stringValue.slice(-3)); //"rld"
alert(stringValue.substring(-3)); //"hello world"
alert(stringValue.substr(-3)); //"rld"
alert(stringValue.slice(3, -4)); //"lo w"
alert(stringValue.substring(3, -4)); //"hel"
alert(stringValue.substr(3, -4)); //"" (空字符串)
字符串位置方法
indexOf()和lastIndexOf()可以用於從字符串中查找子字符串,並返回子字符串的位置(如果沒有找到,則返回-1)。兩個方法區別只在於搜索的起始位置。並且兩個方法都可以接受第二個參數,用於指定從哪個位置可以搜索。
trim()方法
ECMAScript 5為所有字符串字義了trim()方法。這個方法會創建一個字符串副本,刪除前置和後綴的所有空格,然後返回結果。
var stringValue = " hello world ";
var trimmedStringValue = stringValue.trim();
alert(stringValue); //" hello world "
alert(trimmedStringValue); //"hello world"
字符串大小寫轉換方法
toLowerCase()、toLocalceLowerCase()、toUpperCase()和toLocaleUpperCase()用於轉換字符串的大小寫。
字符串模式匹配方法
String類型定義了幾個用於在字符串中匹配模式的方法。
match()與RegExp的exec()方法本質上是一樣的。match()方法接收一個參數,要麼是正則字面量,或一個RegExp對象。
var text = "cat, bat, sat, fat";
var pattern = /.at/;
//與 pattern.exec(text)相同
var matches = text.match(pattern);
alert(matches.index); //0
alert(matches[0]); //"cat"
alert(pattern.lastIndex); //0
search()方法用於在字符串中查找符合特定模式的子字符串,並返回子字符串的位置,它接收的參數與match()一樣。
var text = "cat, bat, sat, fat";
var pos = text.search(/at/);
alert(pos); //1
String類型還提供了一個用於替換子字符串。這個方法可以接收兩個參數:第一個參數可以是一個RegExp對象或者一個字符串(不會被轉換為正則表達式),第二個參數可以是一個字符串或者一個函數。如果第一個參數是字符串,則只會替換第一個匹配的子字符串。
var text = "cat, bat, sat, fat";
var result = text.replace("at", "ond");
alert(result); //"cond, bat, sat, fat"
result = text.replace(/at/g, "ond");
alert(result); //"cond, bond, sond, fond"
第二個參數如果指定一個函數,傳遞給函數的參數依次是模式的匹配項、各個捕獲組的匹配項、匹配項的位置和原始字符串。
最後一個與模式匹配有關的方法是split(),這個方法可以基本指定的分隔符將一個字符串分割成多個子字符串,並將結果放在一個數組中。分隔符可以是一個字符串(不會被轉化為正則表達式),也可以是一個RegExp對象。這個方法也可以接收第二個參數,用於指定數組的大小。
var colorText = "red,blue,green,yellow";
var colors1 = colorText.split(","); //["red", "blue", "green", "yellow"]
var colors2 = colorText.split(",", 2); //["red", "blue"]
var colors3 = colorText.split(/[^\,]+/); //["", ",", ",", ",", ""]
localeCompare()方法
這個方法比較兩個字符串,根據字符編碼進行比較返回正數、負數或者0。
fromCharCode()方法
String構造函數本身還有一個靜態方法:fromCharCode(),這個方法接收一個或多個字符編碼,然後將它們轉換成一個字符串。
alert(String.fromCharCode(104, 101, 108, 108, 111)); //"hello"
單體內置對象
除了上面介紹的內置對象,ECMA-262還定義了兩個單體內置對象:Global和Math。
Global對象
所以在全局作用域中定義的屬性和函數,都是Global對象的屬性。在web浏覽器當中,這個對象被當作window對象的一部分來實現。Global對象還包含其他一些方法:
URI編碼方法
encodeURI()和encodeURIComponent()方法可以對URI進行編碼,它們會以UTF-8編碼替換掉無效的字符。其中,encodeURI()用於整個URI,而encodeURIComponent()只用於URI中某一段進行編碼。與其對應的兩個方法是decodeURI()和decodeURIComponent()。
eval()方法
eval()方法可以將傳入的字符串當作ECMAScript語句來執行。通過eval()執行的代碼被認為是包含該次調用的執行環境的一部分,因為被執行的代碼具有與該執行環境相同的作用域鏈。
eval("function sayHi() { alert('hi'); }");
sayHi();
eval("var msg = 'hello world'; ");
alert(msg); //"hello world"
Math對象
ECMAScript對保存數學公式和信息提供了一個公共位置,即Math對象。
Math對象的屬性
Math對象包含的屬性大都是數學中可能用到的特殊值:
屬性 說明
Math.E 自然對數的底數,即常量 e 的值
Math.LN10 10的自然對數
Math.LN2 2的自然對數
Math.LOG2E 以2為底 e 的對數
Math.LOG10E 以10為底 e 的對數
Math.PI π的值
Math.SQRT1_2 1/2的平方根(即2的平方根的倒數)
Math.SQRT2 2的平方根
min()和max()方法
這兩個方法可以接收任意多個數值參數,並返回所有值的最大值或最小值。
var max = Math.max(3, 54, 32, 16);
alert(max); //54
var min = Math.min(3, 54, 32, 16);
alert(min); //3
捨入方法
Math.ceil()向上取整,Math.floor()向下取整,Math.round()執行四捨五入方法。
alert(Math.ceil(25.9)); //26
alert(Math.ceil(25.5)); //26
alert(Math.ceil(25.1)); //26
alert(Math.round(25.9)); //26
alert(Math.round(25.5)); //26
alert(Math.round(25.1)); //25
alert(Math.floor(25.9)); //25
alert(Math.floor(25.5)); //25
alert(Math.floor(25.1)); //25
random()方法
Math.random()方法返回大於等於0小於1的一個隨機數。可以利用這個方法從某個整數范圍內隨機選擇一個值。
值 = Math.floor(Math.random() * 可能值的總數 + 第一個可能的值)
// 通用函數
function selectFrom(lowerValue, upperValue) {
var choices = upperValue - lowerValue + 1;
return Math.floor(Math.random() * choices + lowerValue);
}
其他方法
Math對象中還包含了其他一些與計算相關的方法,在此就不詳細記錄,遇到的時候再查閱資料就可以了。