JavaScript函數(shù)詳解
1、函數(shù)定義
函數(shù)包含一組語句,它們是javascript的基礎(chǔ)模塊單元,用于代碼復(fù)用、信息隱藏和組合調(diào)用。函數(shù)用于指定對(duì)象的行為
2、函數(shù)的四種調(diào)用模式及this的初始化
第一種:方法調(diào)用模式
以下事例證明通過方法調(diào)用模式調(diào)用時(shí),this綁定到擁有該方法的對(duì)象。如:
var person = {
name: "defaultName",
setName : function(name){
this.name = name;
}
};
person.setName("zhangsan");
alert(person.name);
第二種:函數(shù)調(diào)用模式
以下事例證明通過函數(shù)調(diào)用模式調(diào)用時(shí),this綁定到全局對(duì)象上。如:
var test = add(value1, value2);
var name = "defaultName";
var person = {
name: "zhangsan", // person中定義的name
getName : function(){
// 通過此方法可以將test函數(shù)的this改變?yōu)閜erson的this對(duì)象
var that = this; // 解決方案
// getName中定義的name
var name = "lisi";
var test = function(){
// 通過that來訪問person中的對(duì)象
// this指向Global對(duì)象
// this.name = defaultName
// that.name = zhangsan
alert([this.name, that.name]);
};
test(); // 函數(shù)調(diào)用模式
}
}
person.getName();
第三種:構(gòu)造器調(diào)用模式
// 定義一個(gè)Person的構(gòu)造器,在調(diào)用時(shí)一定要用new調(diào)用
var Person = function(name){
this.name = name;
}
// 添加一個(gè)方法到Person
Person.prototype.getName = function(){
return this.name;
};
// 構(gòu)造一個(gè)Person對(duì)象
var person = new Person("zhangsan");
alert(person.getName()); // 調(diào)用getName獲取person對(duì)象中name屬性的值
第四種:Apply調(diào)用模式
<script type="text/javascript">
// 定一個(gè)累加方法。如sum(1,2,3,4...)
// 該方法位于window執(zhí)行環(huán)境中。
var displayName = function(){
alert("sum的執(zhí)行環(huán)境: " + typeof(this));
alert("Name: " + this.name); // 取出當(dāng)前執(zhí)行環(huán)境中name屬性
}
// 定一個(gè)Person對(duì)象
var Person = {
name: "zhangsan"
};
displayName.apply(Person);
</script>
3、Apply和call的區(qū)別
// 定一個(gè)對(duì)象,包含一個(gè)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); // 對(duì)象不支持次對(duì)象
showInfo.apply(Person, [1, 3]);
showInfo.call(Person, 1, 3);
// 從上面可以看出,apply和call的區(qū)別是apply接受一個(gè)數(shù)組作為被調(diào)函數(shù)的參數(shù),
// 而call是通過將被調(diào)函數(shù)的所有參數(shù)以逗號(hào)分隔的形式展開
4、函數(shù)參數(shù)(arguments)
arguments并不是一個(gè)數(shù)組,只是與數(shù)組相似。arguments除了擁有l(wèi)ength屬性,數(shù)組的所有屬性和方法都不具備。用arguments來實(shí)現(xiàn)一個(gè)累加的函數(shù)。
function sum(){
var total = 0;
for(var i=0; i<arguments.length; i++){ // arguments.length返回sum函數(shù)調(diào)用時(shí)傳遞參數(shù)的個(gè)數(shù)
total += arguments[i];
}
return total;
}
alert("sum: " + sum(1, 3, 2, 4));
5、函數(shù)返回值(return)
當(dāng)一個(gè)函數(shù)被調(diào)用,通常會(huì)從函數(shù)的{開始執(zhí)行到}結(jié)束。如果想提前結(jié)束該函數(shù)的執(zhí)行可以使用return語句,此時(shí),return語句后面的所有語句將永遠(yuǎn)不會(huì)執(zhí)行。如:
function test(){
alert("first");
return;
alert("second"); // 該語句永遠(yuǎn)被不會(huì)執(zhí)行
}
test();
// 一個(gè)函數(shù)總是會(huì)返回值,如果沒有使用return返回值,默認(rèn)返回undefined。如:
function test(){
alert("first");
}
alert(test()); // 輸出:undefined
// 如果函數(shù)前使用new方式調(diào)用,且返回值不是一個(gè)對(duì)象,則返回this(新對(duì)象)。如:
function test(){
alert("first");
}
var t = new test();
alert(typeof t); // 輸出:‘object'
alert(t instanceof test); // 輸出:true
6、異常(exception)
異常是干擾程序正常流程的非正常事故(可能人為有意的)。當(dāng)檢查出這樣的事故,應(yīng)當(dāng)拋出異常。如:
function add(a, b){ // 定義一個(gè)加法函數(shù)
// 如果傳遞的參數(shù)不是數(shù)字類型,則拋出一個(gè)異常信息
if(typeof a != 'number' || typeof b != 'number'){
throw {
'name' : "typeError", // 屬性是自定義的,名字可以任意取
'message': "add方法必須使用數(shù)字作為參數(shù)"
};
}
return a + b;
}
(function(){
// 捕獲add方法可能產(chǎn)生的異常
try{
add(10, "");
} catch(e){
// 一個(gè)try語句只有一個(gè)catch語句,如果要處理多個(gè)異常,則通過異常的name屬性來區(qū)別
// 判斷異常的類型
if(e.name === "typeError"){
alert(e.message);
}
}
})();
7、給類型添加方法
javascript中允許給基本類型添加方法。如:boolean、string、Number
實(shí)例:在Function中添加一個(gè)method函數(shù),該函數(shù)為Function添加其他自定義的函數(shù)(避免使用prototype),然后利用method函數(shù)想Function中添加一個(gè)add函數(shù),最后測(cè)試add函數(shù)在Function中確實(shí)存在。該方法將func函數(shù)添加到Function中,以name命名。然后,返回Function的對(duì)象
Function.prototype.method = function(name, func){
// 避免覆蓋已有的方法
if(!this.prototype[name]){
this.prototype[name] = func;
}
return this;
};
// 通過Function.method方法添加一個(gè)加法函數(shù)到Function,該函數(shù)的名稱為“add”
Function.method("add", function(a, b){
if(typeof a != 'number' || typeof b != 'number'){
throw {
'name' : "typeError",
'message' : "add方法必須傳入數(shù)字"
};
}
return a + b;
});
// 調(diào)用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|'
// 添加數(shù)字的取整函數(shù)
Number.method("integer", function(){
// 可以通過此種方式調(diào)用函數(shù),如:Math.random() == Math['random']() == Math["random"]()
return Math[this < 0 ? 'ceil' : 'floor'](this);
});
alert((-10 / 3).integer()); // 輸出:-3
8、遞歸調(diào)用(arguments.callee)
遞歸調(diào)用就是自己調(diào)用自己。調(diào)用分為:直接調(diào)用和間接調(diào)用下面展示使用遞歸調(diào)用來計(jì)算指定值的斐波那契數(shù)列。
// 求i的階乘
function factorial(i){
if(i < 2){
return 1;
}
return i*factorial(i-1); // 遞歸調(diào)用
}
alert(factorial(5)); // 求5的階乘
// 以上方式存在一個(gè)問題?如下:
var factorial = function(i){
if(i < 2){
return 1;
}
return i*factorial(i-1); // factorial還能被調(diào)用嗎?不能
}
var test = factorial;
factorial = null;
alert(test(2));
// 解決方案:
var factorial = function(i){
if(i < 2){
return 1;
}
return i*arguments.callee(i-1); // arguments.callee返回正被執(zhí)行的 Function 對(duì)象,也就是所指定的 Function 對(duì)象的正文
}
var test = factorial;
factorial = null;
alert(test(5));
9、作用域
// 在程序中,作用域控制著變量的可見性和生命周期。
var name = "default"; // 全局作用域
function getName(){
var name = "getName"; // getName作用域下
for(var i=0; i<2; i++){
var inName = "inName";
}
alert(i + "," + inName); // 2,inName 注意:在js中沒有塊級(jí)作用域,及if、for、while中聲明的變量是放在塊所在的作用域下
return name;
}
alert(getName()); // getName 注意:js存在函數(shù)作用域,所以在函數(shù)內(nèi)部定義的變量在外部是不可見的
alert(name); // default
注意:在現(xiàn)代的很多語言中,推薦將變量盡可能的延遲聲明。如:java而在js中,卻不推薦這樣做,因?yàn)閖s不支持塊級(jí)作用域。推薦在函數(shù)的開始就將所有用到的變量進(jìn)行聲明。
10、閉包
函數(shù)能夠訪問它被創(chuàng)建時(shí)環(huán)境的上下文稱為閉包。作用域的好處是,內(nèi)部函數(shù)可以訪問外部函數(shù)的所有變量(除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());
// 上面使用字面常量方式定義了一個(gè)myObject對(duì)象。但是value變量可以被外部對(duì)象訪問
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); // 不能被外部對(duì)象訪問
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 < 15){
level += 1;
setTimeout(step, 500); // 如果level小于15,則內(nèi)部函數(shù)自我調(diào)用
}
};
setTimeout(step, 1); // 調(diào)用內(nèi)部函數(shù)
};
fade(document.body);
// 下面是一個(gè)很糟糕的例子
<a href="#" name="test">點(diǎn)擊我...</a><br> // 點(diǎn)擊時(shí)顯示3
<a href="#" name="test">點(diǎn)擊我...</a><br> // 點(diǎn)擊時(shí)顯示3
<a href="#" name="test">點(diǎn)擊我...</a><br> // 點(diǎn)擊時(shí)顯示3
var add_the_handlers = function(nodes){
var i;
for(i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function(e){ // 函數(shù)構(gòu)造時(shí)的:i
alert(i);
};
}
};
var objs = document.getElementsByName("test");
add_the_handlers(objs);
// 造成上面的原因是:a標(biāo)簽的事件函數(shù)綁定了變量i,則不是函數(shù)在構(gòu)造時(shí)的i值。
// 解決方案如下:
var add_the_handlers = function(nodes){
var i;
for(i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function(i){
return function(e){
alert(i); // 輸出的i是構(gòu)造函數(shù)傳遞進(jìn)來的i,不是事件處理綁定的i。
};
}(i);
}
};
var objs = document.getElementsByName("test");
add_the_handlers(objs);
11、回調(diào)(callbacks)
// data表示參數(shù),而call_function則表示回調(diào)函數(shù)
function sendRequest(data, call_function){
// setTimeout來模仿客戶端請(qǐng)求服務(wù)端中傳輸數(shù)據(jù)的時(shí)間。
// 當(dāng)3秒鐘后就調(diào)用回調(diào)函數(shù)(有客戶端實(shí)現(xiàn)回調(diào)函數(shù))
setTimeout(function(){
call_function(data); // 調(diào)用回調(diào)函數(shù)
}, 3000);
}
// 測(cè)試sendRequest函數(shù)
sendRequest("參數(shù)", function(context){
alert("context=" + context);
});
12、模塊
模塊是一個(gè)提供接口而隱藏狀態(tài)和實(shí)現(xiàn)的函數(shù)或?qū)ο蟆?br />
一般形式:一個(gè)定義了私有變量和函數(shù)的函數(shù);利用閉包創(chuàng)建可以訪問私有變量和函數(shù)的特權(quán)函數(shù);最后返回這個(gè)特權(quán)函數(shù),或者把他們保存到一個(gè)可以被訪問到的地方。
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的值,了解正則表達(dá)式
var r = entity[b];
return typeof r === "string" ? r : a;
});
};
}());
alert("<">".deentityify()); // 測(cè)試:<">
注:模塊模式通常結(jié)合單例模式使用,JavaScript的單例模式就是用對(duì)象字面量方式創(chuàng)建的對(duì)象,對(duì)象的屬性值可以是數(shù)值或函數(shù),并且屬性值在該對(duì)象的生命周期中不會(huì)發(fā)生變化。
13、級(jí)聯(lián)(鏈?zhǔn)讲僮鳎?/strong>
對(duì)于一些不返回值的方法,我們返回this,而不是undefined,那么我們就可以啟動(dòng)以級(jí)聯(lián)(鏈?zhǔn)剑┤ゲ僮髟搶?duì)象。如下:
var $ = function(id){
var obj = document.getElementById(id);
obj.setColor = function(color){
this.style.color = color;
return this;
};
obj.setBgColor = function(color){
this.style.backgroundColor = color;
return this; // 返回this對(duì)象,啟動(dòng)級(jí)聯(lián)
};
obj.setFontSize = function(size){
this.style.fontSize = size;
return this;
};
return obj;
};
$("test").setColor("red")
.setFontSize("30px")
.setBgColor("blue");
// 改進(jìn)后的代碼:
(function(id){
var _$ = function(id){
this.element = document.getElementById(id);
};
_$.prototype = {
setColor : function(color){
this.element.style.color = color;
return this;
},
setBgColor : function(color){
this.element.style.backgroundColor = color;
return this;
},
setFontSize : function(size){
this.element.style.fontSize = size;
return this;
}
};
// 添加到window原型鏈中
window.$ = function(id){
return new _$(id);
};
})();
$("test").setColor("red")
.setFontSize("30px")
.setBgColor("blue");
14、套用
所謂套用就是將函數(shù)與傳遞給它的參數(shù)相結(jié)合,產(chǎn)生一個(gè)新的函數(shù)。如:下面代碼中定義一個(gè)add()函數(shù),該函數(shù)能夠返回一個(gè)新的函數(shù),并把參數(shù)值傳遞給這個(gè)新函數(shù),從而實(shí)現(xiàn)連加操作。
// 第一種方式:
var add = function(a){
return function(b){
return a + b;
}
};
alert(add(1)(2)); // 3
// 第二種方式:用arguments實(shí)現(xiàn)
var add = function(){
var arg = arguments;
return function(){
var sum = 0;
for(var i=0; i<arg.length; i++){
sum += arg[i];
}
for(i=0; i<arguments.length; i++){
sum += arguments[i];
}
return sum;
}
};
alert(add(1,2,3)(4,5,6)); // 21
// 第三種方式:通過一個(gè)套用方法(curry)實(shí)現(xiàn)
var add = function(){
var sum = 0;
for(var i=0; i<arguments.length; i++){
sum += arguments[i];
}
return sum;
};
// 添加方法到Function的原型鏈上
Function.prototype.method = function(name, func){
this.prototype[name] = func;
return this;
};
// 套用方法
Function.method('curry', function(){
// 通過數(shù)組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、記憶
函數(shù)可以用對(duì)象去記住先前操作的結(jié)果,從而能避免無謂的運(yùn)算。這種優(yōu)化被稱為記憶。
var fibonacci = function(){
var mome = [0,1]; // 存放計(jì)算后的數(shù)據(jù)
var fib = function(n){
var result = mome[n];
// 如果不存在被計(jì)算過的數(shù)據(jù),則直接計(jì)算。然后在將計(jì)算結(jié)果緩存
if(typeof result !== 'number'){
result = fib(n-1) + fib(n-2);
mome[n] = result;
}
return result;
};
return fib;
}();
for(var i=0; i<=10; i++){
document.writeln("http:// " + i + ": " + fibonacci(i) + "<br/>");
}
//==========================
// 創(chuàng)建一個(gè)具有記憶的函數(shù)
//==========================
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;
};
// 通過記憶函數(shù)memoizer完成斐波那契數(shù)列
var fibonacci = memoizer([0,1], function(shell, n){
return shell(n-1) + shell(n-2);
});
// 通過記憶函數(shù)memoizer完成階乘
var factorial = memoizer([1,1], function(shell, n){
return n * shell(n-1);
});
for(var i=0; i<=15; i++){
document.writeln("http:// " + i + ": " + factorial(i) + "<br/>");
}
小伙伴們看明白了沒,非常實(shí)用吧,如有遺漏的地方,還請(qǐng)大神們指點(diǎn)下,共同進(jìn)步
相關(guān)文章
JavaScript深拷貝的幾種實(shí)現(xiàn)方法實(shí)例
javascript深拷貝是初學(xué)者甚至有經(jīng)驗(yàn)的開發(fā)著,都會(huì)經(jīng)常遇到問題,下面這篇文章主要給大家介紹了關(guān)于JavaScript深拷貝的幾種實(shí)現(xiàn)方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05JavaScript預(yù)解析及相關(guān)技巧分析
這篇文章主要介紹了JavaScript預(yù)解析及相關(guān)技巧,結(jié)合實(shí)例形式分析了JavaScript與解析的原理,步驟與相關(guān)技巧,需要的朋友可以參考下2016-04-04利用進(jìn)制轉(zhuǎn)換壓縮數(shù)字函數(shù)分享
本文主要介紹了進(jìn)制轉(zhuǎn)換函數(shù),用于壓縮數(shù)字,比如Date.now()這樣的長(zhǎng)數(shù)字,用62進(jìn)制表示,就更短,大家參考使用吧2014-01-01JavaScript實(shí)現(xiàn)答題評(píng)分功能頁面
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)答題評(píng)分功能頁面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06javascript相等運(yùn)算符與等同運(yùn)算符詳細(xì)介紹
不管是java、c++、php都有相等運(yùn)算符與等同運(yùn)算符,當(dāng)然javasript也不例外,下面介紹一下2013-11-11Bootstrap開發(fā)實(shí)戰(zhàn)之響應(yīng)式輪播圖
這篇文章主要為大家詳細(xì)介紹了Bootstrap開發(fā)實(shí)戰(zhàn)之響應(yīng)式輪播圖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06javascript將json格式數(shù)組下載為excel表格的方法
下面小編就為大家分享一篇javascript將json格式數(shù)組下載為excel表格的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12