本文主要內容:
1.分析函數的四種調用形式
2.弄清楚函數中this的意義
3.明確構造函對象的過程
4.學會使用上下文調用函數
一、函數調用形式
函數調用形式是最常見的形式,也是最好理解的形式。所謂函數形式就是一般聲明函數後直接調用即是。例如:
複製代碼 代碼如下:
// 聲明一個函數,並調用
function func() {
alert("Hello World");
}
func();
或者:
複製代碼 代碼如下:
// 使用函數的Lambda表達式定義函數,然後調用
var func = function() {
alert("你好,程序員");
};
func();
這兩段代碼都會在瀏覽器中彈出一個對話框,顯示字符串中的文字,這個就是函數調用。
可以發現函數調用很簡單,就是平時學習的一樣,這裏的關鍵是,在函數調用模式中,函數裏的 this 關鍵字指全局對象,如果在瀏覽器中就是 window 對象。例如:
複製代碼 代碼如下:
var func = function() {
alert(this);
};
func();
此時,會彈出對話框,打印出 [object Window]。
二、方法調用模式
函數調用模式很簡單,是最基本的調用方式。但是同樣的是函數,將其賦值給一個對象的成員以後,就不一樣了。將函數賦值給對象的成員後,那麼這個就不在稱為函數,而應該叫做方法。例如:
複製代碼 代碼如下:
// 定義一個函數
var func = function() {
alert("我是一個函數麼?");
};
// 將其賦值給一個對象
var o = {};
= func; // 注意這裏不要加圓括號
// 調用
();
此時, 則是方法,不是函數了。實際上 fn 的方法體與 func 是一模一樣的,但是這裏有個微妙的不同。看下面的代碼:
複製代碼 代碼如下:
// 接上面的代碼
alert( === func);
打印結果是true,這個表明兩個函數是一樣的東西,但是修改一下函數的代碼:
// 修改函數體
var func = function() {
alert(this);
};
var o = {};
= func;
// 比較
alert( === func);
// 調用
func();
();
這裏的運行結果是,兩個函數是相同的,因此打印結果是 true。但是由於兩個函數的調用是不一樣的,func 的調用,打印的是 [object Window],而 的`打印結果是 [object Object]。
這裏便是函數調用與方法調用的區別,函數調用中,this 專指全局對象 window,而在方法中 this 專指當前對象,即 中的 this 指的就是對象o。
三、構造器調用模式
同樣是函數,在單純的函數模式下,this 表示 window;在對象方法模式下,this 指的是當前對象。除了這兩種情況,JavaScript 中函數還可以是構造器。將函數作為構造器來使用的語法就是在函數調用前面加上一個 new 關鍵字。如代碼:
複製代碼 代碼如下:
// 定義一個構造函數
var Person = function() {
= "程序員";
ello = function() {
alert("你好,這裏是" + );
};
};
// 調用構造器,創建對象
var p = new Person();
// 使用對象
ello();
上面的案例首先創建一個構造函數Person,然後使用構造函數創建對象p。這裏使用 new 語法。然後在使用對象調用sayHello()方法,這個使用構造函數創建對象的案例比較簡單。從案例可以看到,此時 this 指的是對象本身。除了上面簡單的使用以外,函數作為構造器還有幾個變化,分別為:
1、所有需要由對象使用的屬性,必須使用 this 引導;
2、函數的 return 語句意義被改寫,如果返回非對象,就返回this。
構造器中的 this
我們需要分析創建對象的過程,方能知道 this 的意義。如下面代碼:
複製代碼 代碼如下:
var Person = function() {
= "程序員";
};
var p = new Person();
這裏首先定義了函數 Person,下面分析一下整個執行:
1、程序在執行到這一句的時候,不會執行函數體,因此 JavaScript 的解釋器並不知道這個函數的內容。
2、接下來執行 new 關鍵字,創建對象,解釋器開闢內存,得到對象的引用,將新對象的引用交給函數。
3、緊接着執行函數,將傳過來的對象引用交給 this。也就是説,在構造方法中,this 就是剛剛被 new 創建出來的對象。
4、然後為 this 添加成員,也就是為對象添加成員。
5、最後函數結束,返回 this,將 this 交給左邊的變量。
分析過構造函數的執行以後,可以得到,構造函數中的 this 就是當前對象。
構造器中的 return
在構造函數中 return 的意義發生了變化,首先如果在構造函數中,如果返回的是一個對象,那麼就保留原意。如果返回的是非對象,比如數字、布爾和字符串,那麼就返回 this,如果沒有 return 語句,那麼也返回 this,看下面代碼:
複製代碼 代碼如下:
// 返回一個對象的 return
var ctr = function() {
= "趙曉虎";
return {
name:"牛亮亮"
};
};
// 創建對象
var p = new ctr();
// 訪問name屬性
alert();
執行代碼,這裏打印的結果是”牛亮亮”。因為構造方法中返回的是一個對象,那麼保留 return 的意義,返回內容為 return 後面的對象,再看下面代碼:
複製代碼 代碼如下:
// 定義返回非對象數據的構造器
var ctr = function() {
= "趙曉虎";
return "牛亮亮";
};
// 創建對象
var p = new ctr();
// 使用
alert(p);
alert();
代碼運行結果是,先彈窗打印[object Object],然後打印”趙曉虎”,因為這裏 return 的是一個字符串,屬於基本類型,那麼這裏的 return 語句無效,返回的是 this 對象,因此第一個打印的是[object Object]而第二個不會打印 undefined。
四、apply調用模式
除了上述三種調用模式以外,函數作為對象還有 apply 方法與 call 方法可以使用,這便是第四種調用模式,我稱其為 apply 模式。
首先介紹 apply 模式,首先這裏 apply 模式既可以像函數一樣使用,也可以像方法一樣使用,可以説是一個靈活的使用方法。首先看看語法:函數名y(對象, 參數數組);
這裏看語法比較晦澀,還是使用案例來説明:
1、新建兩個 js 文件,分別為””與””;
2、添加代碼
複製代碼 代碼如下:
// 文件中
var func1 = function() {
= "程序員";
};
y(null);
alert(name);
// 文件
var func2 = function() {
= "程序員";
};
var o = {};
y(o);
alert();
3、分別運行着兩段代碼,可以發現第一個文件中的 name 屬性已經加載到全局對象 window 中; 而第二個文件中的 name 屬性是在傳入的對象 o 中,即第一個相當於函數調用,第二個相當 於方法調用。
這裏的參數是方法本身所帶的參數,但是需要用數組的形式存儲在,比如代碼:
複製代碼 代碼如下:
// 一個數組的例子
var arr1 = [1,2,3,[4,5],[6,7,8]];
// 將其展開
var arr2 = y([], arr1);
然後介紹一下 call 模式,call 模式與 apply 模式最大的不同在於 call 中的參數不用數組,看下面代碼就清楚了:
// 定義方法
var func = function(name, age, sex) {
= name;
= age;
= sex;
};
// 創建對象
var o = {};
// 給對象添加成員
// apply 模式
var p1 = y(o, ["趙曉虎", 19, "男"]);
// call 模式
var p2 = (o, "趙曉虎", 19, "男");
上面的代碼,apply 模式與 call 模式的結果是一樣的。
實際上,使用 apply 模式和 call 模式,可以任意的操作控制 this 的意義,在函數 js 的設 計模式中使用廣泛。簡單小結一下,js 中的函數調用有四種模式,分別是:函數式、方法式、構造 器式和 apply 式. 而這些模式中,this 的含義分別為:在函數中 this 是全局對象 window,在方 法中 this 指當前對象,在構造函數中 this 是被創建的對象,在 apply 模式中 this 可以隨意的指定.。在 apply 模式中如果使用 null,就是函數模式,如果使用對象,就是方法模式。
五、綜合例子
下面通過一個案例結束本篇吧。案例説明:有一個div,id為dv,鼠標移到上面去高度增大2倍,鼠標離開恢復,下面直接上js代碼:
複製代碼 代碼如下:
var dv = lementById("dv");
var height = parseInt(ht || etHeight);
var intervalId;
useover = function() {
// 停止已經在執行的動畫
clearInterval(intervalId);
// 得到目標高度
var toHeight = height * 2;
// 獲得當前對象
var that = this;
// 開器計時器,緩慢變化
intervalId = setInterval(function() {
// 得到現在的高度
var height = parseInt(ht || etHeight);
// 記錄每次需要變化的步長
var h = ((height - toHeight) / 10);
// 判斷變化,如果步長為0就停止計時器
if( h > 0 ) {
// 這裏為什麼要用that呢?思考一下吧
ht = (height + h) + "px";
} else {
clearInterval(intervalId);
}
}, 20);
};
useout = function() {
// 原理和之前一樣
clearInterval(intervalId);
var toHeight = height;
var that = this;
intervalId = setInterval(function() {
var height = parseInt(ht || etHeight);
var h = ((height - toHeight) / 10);
if( h > 0 ) {
ht = (height - h) + "px";
} else {
clearInterval(intervalId);
}
}, 20);
};