1、函數定義
函數包含一組語句,它們是javascript的基礎模組單元,用於程式碼重複使用、資訊隱藏和組合呼叫。函數用於指定物件的行為
2、函數的四種呼叫模式及this的初始化
第一種:方法呼叫模式
下列事例證明透過方法呼叫模式呼叫時,this綁定到擁有此方法的物件。如:
var person = {
name: "defaultName",
setName : function(name){
this.name = name;
}
};
person.setName("zhangsan");
alert(person.name);
第二種:函數呼叫模式
下列事例證明透過函數呼叫模式呼叫時,this綁定到全域物件上。如:
var test = add(value1, value2);
var name = "defaultName";
var person = {
name: "zhangsan", // person中定義的name
getName : function(){
// 透過此方法可以將test函數的this改為person的this物件
var that = this; // 解
// getName中定義的name
var name = "lisi";
var test = function(){
// 透過that來存取person中的物件
// this指向Global物件
// this.name = defaultName
// that.name = zhangsan
alert([this.name, that.name]);
};
test(); // 函數呼叫模式
}
}
person.getName();
第三種:構造器呼叫模式
// 定義一個Person的建構器,在呼叫時一定要用new呼叫
var Person = function(name){
this.name = name;
}
// 新增一個方法到Person
Person.prototype.getName = function(){
return this.name;
};
// 建構一個Person物件
var person = new Person("zhangsan");
alert(person.getName()); // 呼叫getName取得person物件中name屬性的值
第四種:Apply呼叫模式
3、Apply和call的差別
// 定一個對象,包含一個add方法,回傳a、b的和
var Person = {
'add' : function(a, b){
return a b;
}
};
// 顯示a、b的和
function showInfo(a, b){
alert(this.add(a, b));
}
// 透過apply方法改變showInfo方法的this指向
//showInfo(1, 3); // 物件不支援次物件
showInfo.apply(Person, [1, 3]);
showInfo.call(Person, 1, 3);
// 從上面可以看出,apply和call的差別是apply接受一個陣列作為被調函數的參數,
// 而call是透過將被調函數的所有參數以逗號分隔的形式展開
4、函數參數(arguments)
arguments並不是一個數組,只是與數組相似。 arguments除了擁有length屬性,陣列的所有屬性和方法都不具備。用arguments來實作一個累加的函數。
function sum(){
var total = 0;
for(var i=0; i
total = arguments[i];
}
return total;
}
alert("sum: " sum(1, 3, 2, 4));
5、函數回傳值(return)
當函數被調用,通常會從函數的{開始執行到}結束。如果想要提前結束函數的執行可以使用return語句,此時,return語句後面的所有語句將永遠不會執行。如:
function test(){
alert("first");
return;
alert("second"); // 該語句永遠被不會執行
}
test();
// 一個函數總是會回傳值,如果沒有使用return回傳值,預設回傳undefined。如:
function test(){
alert("first");
}
alert(test()); // 輸出:undefined
// 如果函數前使用new方式調用,且傳回值不是一個對象,則傳回this(新對象)。如:
function test(){
alert("first");
}
var t = new test();
alert(typeof t); // 輸出:‘object'
alert(t instanceof test); // 輸出:true
6、異常(exception)
異常是乾擾程序正常流程的非正常事故(可能人為有意的)。當檢查出這樣的事故,應拋出異常。如:
function add(a, b){ // 定義一個加法函數
// 如果傳遞的參數不是數字類型,則拋出一個異常訊息
if(typeof a != 'number' || typeof b != 'number'){
throw {
'name' : "typeError", // 屬性是自訂的,名字可任意取
'message': "add方法必須使用數字作為參數"
};
}
return a b;
}
(function(){
// 捕捉add方法可能產生的異常
try{
add(10, "");
} catch(e){
// 一個try語句只有一個catch語句,如果要處理多個異常,則透過異常的name屬性來區別
// 判斷異常的類型
if(e.name === "typeError"){
alert(e.message);
}
}
})();
7、加入型別方法
javascript中允許給基本型別新增方法。如:boolean、string、Number
實例:在Function中加入一個method函數,該函數為Function加入其他自訂的函數(避免使用prototype),然後利用method函數想Function中加入一個add函數,最後測試add函數在Function中確實存在。此方法將func函數加入Function中,以name命名。然後,回傳Function的物件
Function.prototype.method = function(name, func){
// 避免涵蓋現有的方法
if(!this.prototype[name]){
this.prototype[name] = func;
}
return this;
};
// 透過Function.method方法加入一個加法函數到Function,該函數的名稱為「add」
Function.method("add", function(a, b){
if(typeof a != 'number' || typeof b != 'number'){
throw {
'name' : "typeError",
'message' : "add方法必須傳入數字"
};
}
return a b;
});
// 呼叫Function的add方法是否存在
(function(){
try{
alert(Function.add(1, 3)); // 輸出:4
} catch(e){
if(e.name === 'typeError'){
alert(e.message);
}
}
})();
// 去除字串兩端的空白
String.method("trim", function(){
return this.replace(/^s |s $/g, '');
});
alert('|' " hello world ".trim() '|'); // 輸出: '|hello world|'
// 新增數字的取整函數
Number.method("integer", function(){
// 可以透過此種方式呼叫函數,如:Math.random() == Math['random']() == Math["random"]()
return Math[this
});
alert((-10 / 3).integer()); // 輸出:-3
8、遞迴呼叫(arguments.callee)
遞歸呼叫就是自己呼叫自己。呼叫分為:直接呼叫和間接呼叫下面展示使用遞歸呼叫來計算指定值的斐波那契數列。
// 求i的階乘
function factorial(i){
if(i
return 1;
}
return i*factorial(i-1); // 遞迴呼叫
}
alert(factorial(5)); // 求5的階乘
// 以上方式有一個問題?如下:
var factorial = function(i){
if(i
return 1;
}
return i*factorial(i-1); // factorial還能被呼叫嗎?不能
}
var test = factorial;
factorial = null;
alert(test(2));
// 解決方案:
var factorial = function(i){
if(i
return 1;
}
return i*arguments.callee(i-1); // arguments.callee回傳正被執行的 Function 對象,也就是所指定的 Function 對象的正文
}
var test = factorial;
factorial = null;
alert(test(5));
9、作用域
// 在程式中,作用域控制變數的可見性與生命週期。
var name = "default"; // 全域作用域
function getName(){
var name = "getName"; // getName作用域下
for(var i=0; i
var inName = "inName";
}
alert(i "," inName); // 2,inName 注意:在js中沒有區塊級作用域,而if、for、while中宣告的變數是放在區塊所在的作用域下
return name;
}
alert(getName()); // getName 注意:js存在函數作用域,所以在函數內部定義的變數在外部是不可見的
alert(name); // default
注意:在現代的許多語言中,建議將變數盡可能的延遲聲明。如:java而在js中,卻不建議這樣做,因為js不支援區塊級作用域。推薦在函數的開始就將所有用到的變數進行宣告。
10、閉包
函數能夠存取它被創建時環境的上下文稱為閉包。作用域的好處是,內部函數可以存取外部函數的所有變數(除this和arguments)。
var myObject = {
value : 0,
increment : function(inc){
this.value = typeof inc === 'number' ? inc : 1;
},
getValue : function(){
return this.value;
}
};
myObject.increment(10);
alert(myObject.value);
alert(myObject.getValue());
// 上面使用字面常數方式定義了一個myObject物件。但是value變數可以被外部物件存取
var myObject = function(){
var value = 0;
return {
increment: function(inc){
value = typeof inc === 'number' ? inc : 1;
},
getValue : function(){
return value;
}
};
}();
myObject.increment(10);
alert(myObject.value); // 不能被外部物件存取
alert(myObject.getValue()); // 10
// 漸層body的背景色(黃色到白色)
var fade = function(node){
var level = 1;
var step = function(){
var hex = level.toString(16);
node.style.backgroundColor = '#FFFF' hex hex;
if(level
level = 1;
setTimeout(step, 500); // 若level小於15,則內部函數自我呼叫
}
};
setTimeout(step, 1); // 呼叫內部函數
};
fade(document.body);
// 下面是一個很糟糕的例子
點我... // 點選時顯示3
點我... // 點選時顯示3
點我... // 點選時顯示3
var add_the_handlers = function(nodes){
var i;
for(i = 0; i
nodes[i].onclick = function(e){ // 函數建構時的:i
alert(i);
};
}
};
var objs = document.getElementsByName("test");
add_the_handlers(objs);
// 造成上面的原因是:a標籤的事件函數綁定了變數i,則不是函數在建構時的i值。
// 解決方案如下:
var add_the_handlers = function(nodes){
var i;
for(i = 0; i
nodes[i].onclick = function(i){
return unction(e){
alert(i); // 輸出的i是建構子傳遞進來的i,且不是事件處理綁定的i。
};
}(i);
}
};
var objs = document.getElementsByName("test");
add_the_handlers(objs);
11、回呼(callbacks)
// data表示參數,而call_function則表示回呼函數
function sendRequest(data, call_function){
// setTimeout來模仿客戶端請求服務端中傳輸資料的時間。
// 當3秒鐘後就呼叫回呼函數(有客戶端實作回呼函數)
setTimeout(function(){
call_function(data); // 呼叫回呼函數
}, 3000);
}
// 測試sendRequest函數
sendRequest("參數", function(context){
alert("context=" context);
});
12、模組
模組是一個提供介面而隱藏狀態和實作的函數或物件。
一般形式:一個定義了私有變數和函數的函數;利用閉包創造可以存取私有變數和函數的特權函數;最後傳回這個特權函數,或是把他們存到一個可以被存取的地方。
Function.prototype.method = function(name,func){
this.prototype[name] = func;
return this;
};
String.method("deentityify",function(){
var entity = {
quot : '"',
lt : '
gt : '>'
};
return function(){
return this.replace(/&([^&;] );/g, function(a, b){ // 怎樣知道a、b的值,了解正規表示式
var r = entity[b];
return typeof r === "string" ? r : a;
});
};
}());
alert("".deentityify()); // 測試:
註:模組模式通常結合單例模式使用,JavaScript的單例模式就是用對象字面量方式創建的對象,對象的屬性值可以是數值或函數,並且屬性值在該對象的生命週期中不會發生變化。
13、級聯(鍊式操作)
對於一些不回傳值的方法,我們回傳this,而不是undefined,那麼我們就可以啟動以級聯(鍊式)去操作該物件。如下:
var $ = 函數(id){
var obj = document.getElementById(id);
obj.setColor = 函數(顏色){
this.style.color = 顏色;
回此;
};
obj.setBgColor = 函數(顏色){
this.style.backgroundColor = 顏色;
回傳這個; //返回this對象,啟動級聯
};
obj.setFontSize = function(size){
this.style.fontSize = 大小;
回此;
};
回傳物件;
};
$("測試").setColor("紅色")
.setFontSize("30px")
.setBgColor("藍色");
// 改進後的程式碼:
(函數(id){
var _$ = 函數(id){
this.element = document.getElementById(id);
};
_$.prototype = {
setColor : 函數(顏色){
this.element.style.color = 顏色;
回此;
},
setBgColor : 函數(顏色){
this.element.style.backgroundColor = 顏色;
回此;
},
setFontSize : 函數(大小){
this.element.style.fontSize = 大小;
回此;
}
};
// 加入到視窗原型鏈中
視窗.$ = 函數(id){
回新的_$(id);
};
})();
$("測試").setColor("紅色")
.setFontSize("30px")
.setBgColor("藍色");
14、套用
所謂套用就是將函數與傳遞給它的參數結合,產生一個新的函數。如:在下面程式碼中定義一個add()函數,該函數能夠傳回一個新的函數,並將參數值傳遞給這個新函數,從而實現連加操作。
// 第一種方式:
var add = function(a){
return function(b){
return a b;
}
};
alert(add(1)(2)); // 3
// 第二種方式:用arguments實作
var add = function(){
var arg = arguments;
return function(){
var sum = 0;
for(var i=0; i
sum = arg[i];
}
for(i=0; i
sum = arguments[i];
}
return sum;
}
};
alert(add(1,2,3)(4,5,6)); // 21
// 第三種方式:透過一個套用方法(curry)實作
var add = function(){
var sum = 0;
for(var i=0; i
sum = arguments[i];
}
return sum;
};
// 將方法加入Function的原型鏈上
Function.prototype.method = function(name, func){
this.prototype[name] = func;
return this;
};
// 應用方法
Function.method('curry', function(){
// 透過陣列Array的slice方法,使得arguments也具有concat方法
var slice = Array.prototype.slice,
args = slice.apply(arguments), that = this;
return function(){
return that.apply(null, args.concat(slice.apply(arguments)));
};
});
alert(add.curry(1,2)(3,4)); // 10
15、記憶
函數可以用物件去記住先前運算的結果,從而避免無謂的運算。這種最佳化被稱為記憶。
var fibonacci = function(){
var mome = [0,1]; // 存放計算後的資料
var fib = function(n){
var result = mome[n];
// 若不存在被計算過的數據,則直接計算。然後在將計算結果快取
if(typeof result !== 'number'){
result = fib(n-1) fib(n-2);
mome[n] = result;
}
return result;
};
return fib;
}();
for(var i=0; i
document.writeln("// " i ": " fibonacci(i) "
");
}
//==========================
// 建立一個有記憶的函數
//==========================
var memoizer = function(memo, fundamental){
var shell = function(n){
var result = memo[n];
if(typeof result !== "number"){
result = fundamental(shell, n);
memo[n] = result;
}
return result;
};
return shell;
};
// 透過記憶函數memoizer完成斐波那契數列
var fibonacci = memoizer([0,1], function(shell, n){
return shell(n-1) shell(n-2);
});
// 透過記憶函數memoizer完成階乘
var factorial = memoizer([1,1], function(shell, n){
return n * shell(n-1);
});
for(var i=0; i
document.writeln("// " i ": " factorial(i) "
");
}
小夥伴們看明白了沒,非常實用吧,如有遺漏的地方,還請大神們指點下,共同進步