javaScript複習(八)物件、閉包
Function物件
Function物件:一切(引用型別)都是物件,物件是屬性的集合
undefined, number, string, boolean屬於簡單的值型別,不是物件。剩下的一切都是物件,包括函式、陣列、物件、null、new Number(10)都是物件。他們都是引用型別。
判斷一個變數是不是物件非常簡單。值型別的型別判斷用typeof,引用型別的型別判斷用instanceof。
1、arguments物件:
①過載概念:程式中可定義多個相同函式名,不同引數列表的函式,呼叫者不必區分每個函式的引數,在執行時,程式根據傳入的引數個數,自動判斷選擇哪個函式執行。
②js語法不支援過載!但可用arguments物件模擬過載效果*
arguments物件:函式物件內,自動建立的專門接收所有引數值得類陣列物件。
arguments[i]: 獲得傳入的下標為i的引數值
arguments.length: 獲得傳入的引數個數!
即使定義了引數變數,arguments物件同樣會收到所有引數值
③例:function a(){
if(arguments.length==1){
//如果傳入一個引數,求平方
alert(arguments[0]*arguments[0]);
}else if(arguments.length==2){
//如果傳入兩個引數,求和
alert(arguments[0]+arguments[1])
}}a(3);a(5,5); //輸出:9 10
2、函式物件
建立函式物件時:同時建立2個物件:
函式物件:函式的定義
作用域鏈物件:儲存了函式物件可用變數的位置的物件(棧)
預設第一項指向window物件
呼叫函式時:又會建立1個新物件:
活動物件:專門儲存區域性變數的物件
在作用域鏈物件中追加指向活動物件的引用
呼叫後:作用域鏈中活動物件的引用出棧
活動物件因無人引用而釋放
3、匿名函式:定義時,不指定函式名的函式
何時使用:2大用途:
① 匿名函式自調:定義完,立刻執行
執行完立刻釋放
何時使用:只有確定函式只執行一次時!
如何自調:(function(引數){
函式體
})(引數值);
自調:定義在哪兒,就在哪兒執行,不提前
② 匿名函式回撥:將函式作為物件傳遞給另一個函式
由另一個函式自主決定在需要時呼叫
何時使用:只要將一個函式物件傳遞給其他方法呼叫時
如何回撥:直接將匿名函式的宣告傳入另一個函式中。
4、閉包:
問題:全域性變數和區域性變數的缺陷
全域性變數:容易全域性汙染
區域性變數:無法共享,不能長久儲存
閉包好處:即反覆使用區域性變數,又避免全域性汙染, 就要用閉包
解決:3步:閉包三特點:
① 定義外層函式,封裝被保護的區域性變數
② 定義內層函式,執行對外層函式區域性變數的操作
③ 外層函式返回內層函式的物件,並且外層函式被呼叫,結果被儲存在全域性變數中
標準的閉包寫法
function outer(){ //定義外層函式
var n=1; //封裝被儲存的區域性變數
function inner(){ //定義內層函式
return n++;} //執行對外層函式區域性變數的操作
return inner; //外層函式返回內層函式的物件
}
var getNum=outer(); //外層函式被呼叫
getNum();
例:
function add() { //定義外層函式
var x = 1; //封裝被儲存的區域性變數
return function() { //外層函式返回內層函式的物件,定義內層函式,兩步一起寫
console.log(++x); //執行對外層函式區域性變數的操作
};
}
var num1 = add(); //外層函式返回內層函式的物件
var num2 = add(); //外層函式返回內層函式的物件
num1(); //輸出2,
num1(); //輸出3,
num2(); //輸出2,
例:下面這段程式碼想要迴圈延時輸出結果 0 1 2 3 4,請問輸出結果是否正確,如果不正確請說明為什麼,並修改迴圈內的程式碼使其輸出正確結果
for (var i = 0; i < 5; ++i) {
setTimeout(function() {
console.log(i + " ");
}, 100);
}
原因: js 執行環境為單執行緒,setTimeout 註冊的函式需要等到執行緒空閒時才能執行,此時 for 迴圈已經結束,i 值為 5,又因為迴圈中 setTimeout 接受的引數函式通過閉包訪問變數 i,所以 5 個定時輸出都是 5。
修改方法:將 setTimeout 放在立即執行函式中,將 i 值作為引數傳遞給包裹函式,建立新閉包。就會保護每個不同的i值
for (var i = 0; i < 5; ++i) {
(function(i) { //定義外層函式
setTimeout(function() { //定義內層函式,並且引數i就是需要被儲存的區域性變數
console.log(i + " "); //對外層區域性變數進行操作
}, 100); //setTimeout()本身就是回撥函式,相當於外層函式返回內層函式的物件
})(i); //立即執行函式,屬於自己呼叫自己,就是外層函式被呼叫
}
輸出結果為: 0 1 2 3 4
5、 面向物件:在程式中都是用一個物件來描述現實中一個具體的東西。
現實中的一個東西都包含屬性和功能:
屬性:描述一個東西特點的變數,一個值
功能:東西可以執行的操作
什麼是物件:封裝多個數據和方法的儲存空間
什麼是自定義物件:封裝現實中一個東西的屬性和功能的儲存空間。
現實中東西的屬性會成為物件中的屬性變數。
現實中東西的功能,會成為物件中的方法(函式)
6、 建立物件
①物件直接量
var obj={"屬性名":值,
... : ...,
"方法名" : function(){ ...this.屬性名... }
}
② var obj=new Object(); //建立一個空物件
obj.屬性名=值;
obj.方法名=function(){...this.屬性名...}
③ 利用建構函式*反覆*建立*相同結構*的物件共2步:
建構函式:描述一類物件結構的特殊函式
第一步: 定義建構函式
function 建構函式名|型別名(屬性引數1,.....){
this.屬性名=屬性引數1;
//在當前正在建立的物件中新增一個屬性名
//賦值為屬性引數1的值
...
this.方法名=function(){
...this.屬性名...
}
}
第二步: 利用建構函式建立物件:
var obj=new 建構函式名|型別名(屬性值1,...);
7、訪問物件的屬性與方法
屬性:如何訪問屬性:2種:obj.屬性名 obj["屬性名"]
訪問物件中不存在的屬性(訪問陣列中不存在的下標): 不會出錯,返回undefined
強行給不存在屬性賦值,不報錯!js會自動建立同名屬性
如何判斷某個物件是否包含指定成員:3種
①obj.hasOwnProperty("成員名")
② "屬性名" in 物件
如果找到,返回true,否則返回false!
③ 直接使用obj.屬性名作為條件:
arr.indexOf!==undefined
如果不包含,返回undefind-->false
如果包含,返回值或function-->true
何時省略:判斷方法是否存在時,可省略!==
如果確定屬性值一定不是null,0,"",NaN
也可省略
方法:如何在方法中,訪問當前物件自己:
this關鍵字:執行時,指代正在*呼叫*方法的物件
(.前的物件)
this本質是window下唯一的一個指標,指向當前正在呼叫方法的物件
在方法內訪問當前物件自己的屬性,必須用this.屬性名
this和定義在哪兒無關!僅和呼叫時使用的當前物件有關
如果無主的呼叫或賦值,預設this都是window!
改變this的指向有call()、apple()和 bind(),想詳細瞭解請看我轉載的另一篇文章
8、JavaScript原型與原型鏈
知識較多較雜,梳理不清楚,推薦大家看我轉載的一篇文章《詳解JavaScript原型鏈》
這裡面說的提別清楚